Merge branch 'MDL-62414-master' of git://github.com/junpataleta/moodle
authorDavid Monllao <davidm@moodle.com>
Mon, 14 May 2018 09:44:13 +0000 (11:44 +0200)
committerDavid Monllao <davidm@moodle.com>
Mon, 14 May 2018 09:44:13 +0000 (11:44 +0200)
676 files changed:
admin/roles/classes/privacy/provider.php [new file with mode: 0644]
admin/roles/tests/privacy_test.php [new file with mode: 0644]
admin/settings/users.php
admin/tool/analytics/version.php
admin/tool/assignmentupgrade/version.php
admin/tool/availabilityconditions/version.php
admin/tool/behat/version.php
admin/tool/capability/version.php
admin/tool/cohortroles/version.php
admin/tool/customlang/version.php
admin/tool/dataprivacy/classes/api.php
admin/tool/dataprivacy/classes/expired_user_contexts.php
admin/tool/dataprivacy/classes/external.php
admin/tool/dataprivacy/classes/form/context_instance.php
admin/tool/dataprivacy/classes/form/defaults.php
admin/tool/dataprivacy/classes/local/helper.php
admin/tool/dataprivacy/classes/metadata_registry.php
admin/tool/dataprivacy/classes/output/data_registry_page.php
admin/tool/dataprivacy/classes/output/data_requests_page.php
admin/tool/dataprivacy/classes/output/my_data_requests_page.php
admin/tool/dataprivacy/classes/output/renderer.php
admin/tool/dataprivacy/classes/task/initiate_data_request_task.php
admin/tool/dataprivacy/classes/task/process_data_request_task.php
admin/tool/dataprivacy/createdatarequest_form.php
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
admin/tool/dataprivacy/styles.css
admin/tool/dataprivacy/templates/contact_dpo.mustache
admin/tool/dataprivacy/templates/data_registry.mustache
admin/tool/dataprivacy/templates/data_requests.mustache
admin/tool/dataprivacy/templates/my_data_requests.mustache
admin/tool/dataprivacy/templates/purposes.mustache
admin/tool/dataprivacy/tests/api_test.php
admin/tool/dataprivacy/tests/expired_contexts_test.php
admin/tool/dataprivacy/tests/metadata_registry_test.php
admin/tool/dataprivacy/version.php
admin/tool/dbtransfer/version.php
admin/tool/filetypes/version.php
admin/tool/generator/version.php
admin/tool/health/version.php
admin/tool/httpsreplace/version.php
admin/tool/innodb/version.php
admin/tool/installaddon/version.php
admin/tool/langimport/version.php
admin/tool/log/store/database/version.php
admin/tool/log/store/legacy/version.php
admin/tool/log/store/standard/classes/privacy/provider.php
admin/tool/log/store/standard/version.php
admin/tool/log/version.php
admin/tool/lp/version.php
admin/tool/lpimportcsv/version.php
admin/tool/lpmigrate/version.php
admin/tool/messageinbound/version.php
admin/tool/mobile/version.php
admin/tool/monitor/classes/privacy/provider.php
admin/tool/monitor/version.php
admin/tool/multilangupgrade/version.php
admin/tool/oauth2/version.php
admin/tool/phpunit/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/phpunit/version.php
admin/tool/policy/classes/output/page_agreedocs.php
admin/tool/policy/classes/output/page_viewdoc.php
admin/tool/policy/index.php
admin/tool/policy/lang/en/tool_policy.php
admin/tool/policy/tests/behat/acceptances.feature
admin/tool/policy/tests/behat/managepolicies.feature
admin/tool/policy/tests/privacy_provider_test.php
admin/tool/policy/user.php
admin/tool/policy/version.php
admin/tool/profiling/version.php
admin/tool/recyclebin/version.php
admin/tool/replace/version.php
admin/tool/spamcleaner/version.php
admin/tool/task/version.php
admin/tool/templatelibrary/version.php
admin/tool/unsuproles/version.php
admin/tool/uploadcourse/version.php
admin/tool/uploaduser/version.php
admin/tool/usertours/version.php
admin/tool/xmldb/version.php
auth/cas/version.php
auth/db/version.php
auth/email/version.php
auth/ldap/version.php
auth/lti/version.php
auth/manual/version.php
auth/mnet/classes/privacy/provider.php
auth/mnet/lang/en/auth_mnet.php
auth/mnet/tests/privacy_provider_test.php [new file with mode: 0644]
auth/mnet/version.php
auth/nologin/version.php
auth/none/version.php
auth/oauth2/classes/privacy/provider.php
auth/oauth2/lang/en/auth_oauth2.php
auth/oauth2/version.php
auth/shibboleth/version.php
auth/webservice/version.php
availability/condition/completion/version.php
availability/condition/date/version.php
availability/condition/grade/version.php
availability/condition/group/version.php
availability/condition/grouping/version.php
availability/condition/profile/version.php
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js
availability/yui/src/form/js/form.js
backup/backup.class.php
backup/converter/convertlib.php
backup/tests/privacy_provider_test.php [new file with mode: 0644]
backup/util/ui/classes/privacy/provider.php [new file with mode: 0644]
badges/criteria/award_criteria.php
blocks/activity_modules/version.php
blocks/activity_results/version.php
blocks/admin_bookmarks/version.php
blocks/badges/version.php
blocks/blog_menu/version.php
blocks/blog_recent/version.php
blocks/blog_tags/version.php
blocks/calendar_month/version.php
blocks/calendar_upcoming/version.php
blocks/comments/version.php
blocks/community/version.php
blocks/completionstatus/version.php
blocks/course_list/version.php
blocks/course_summary/version.php
blocks/edit_form.php
blocks/feedback/version.php
blocks/globalsearch/version.php
blocks/glossary_random/version.php
blocks/html/version.php
blocks/login/version.php
blocks/lp/version.php
blocks/mentees/version.php
blocks/mnet_hosts/version.php
blocks/myoverview/version.php
blocks/myprofile/version.php
blocks/navigation/version.php
blocks/news_items/version.php
blocks/online_users/version.php
blocks/participants/version.php
blocks/private_files/version.php
blocks/quiz_results/version.php
blocks/recent_activity/version.php
blocks/rss_client/version.php
blocks/search_forums/version.php
blocks/section_links/version.php
blocks/selfcompletion/version.php
blocks/settings/version.php
blocks/site_main_menu/version.php
blocks/social_activities/version.php
blocks/tag_flickr/classes/privacy/provider.php
blocks/tag_flickr/lang/en/block_tag_flickr.php
blocks/tag_flickr/version.php
blocks/tag_youtube/version.php
blocks/tags/version.php
blog/classes/privacy/provider.php
cache/locks/file/version.php
cache/stores/apcu/version.php
cache/stores/file/version.php
cache/stores/memcache/version.php
cache/stores/memcached/version.php
cache/stores/mongodb/version.php
cache/stores/redis/version.php
cache/stores/session/version.php
cache/stores/static/version.php
calendar/type/gregorian/version.php
competency/classes/privacy/provider.php
completion/classes/privacy/provider.php
completion/tests/fixtures/completion_creation.php
completion/tests/privacy_test.php
course/classes/privacy/provider.php [new file with mode: 0644]
course/format/singleactivity/version.php
course/format/social/version.php
course/format/topics/version.php
course/format/weeks/version.php
course/tests/privacy_test.php [new file with mode: 0644]
dataformat/csv/version.php
dataformat/excel/version.php
dataformat/html/version.php
dataformat/json/version.php
dataformat/ods/version.php
enrol/category/version.php
enrol/classes/privacy/provider.php [new file with mode: 0644]
enrol/cohort/lang/en/enrol_cohort.php
enrol/cohort/version.php
enrol/database/lang/en/enrol_database.php
enrol/database/version.php
enrol/flatfile/lang/en/enrol_flatfile.php
enrol/flatfile/version.php
enrol/guest/lang/en/enrol_guest.php
enrol/guest/version.php
enrol/imsenterprise/lang/en/enrol_imsenterprise.php
enrol/imsenterprise/version.php
enrol/ldap/version.php
enrol/lti/classes/privacy/provider.php [new file with mode: 0644]
enrol/lti/lang/en/enrol_lti.php
enrol/lti/tests/privacy_provider_test.php [new file with mode: 0644]
enrol/lti/version.php
enrol/manual/version.php
enrol/meta/lang/en/enrol_meta.php
enrol/meta/version.php
enrol/mnet/version.php
enrol/paypal/classes/privacy/provider.php [new file with mode: 0644]
enrol/paypal/lang/en/enrol_paypal.php
enrol/paypal/tests/privacy_provider_test.php [new file with mode: 0644]
enrol/paypal/version.php
enrol/self/lang/en/enrol_self.php
enrol/self/version.php
enrol/tests/privacy_test.php [new file with mode: 0644]
files/converter/googledrive/version.php
files/converter/unoconv/version.php
filter/activitynames/version.php
filter/algebra/version.php
filter/censor/version.php
filter/data/version.php
filter/emailprotect/version.php
filter/emoticon/version.php
filter/glossary/version.php
filter/mathjaxloader/version.php
filter/mediaplugin/version.php
filter/multilang/version.php
filter/tex/version.php
filter/tidy/version.php
filter/urltolink/version.php
grade/classes/privacy/provider.php
grade/export/ods/version.php
grade/export/txt/version.php
grade/export/xls/version.php
grade/export/xml/version.php
grade/grading/classes/privacy/gradingform_legacy_polyfill.php [new file with mode: 0644]
grade/grading/classes/privacy/gradingform_provider.php [new file with mode: 0644]
grade/grading/classes/privacy/provider.php [new file with mode: 0644]
grade/grading/form/guide/classes/privacy/provider.php [new file with mode: 0644]
grade/grading/form/guide/lang/en/gradingform_guide.php
grade/grading/form/guide/tests/privacy_test.php [new file with mode: 0644]
grade/grading/form/guide/version.php
grade/grading/form/rubric/classes/privacy/provider.php [new file with mode: 0644]
grade/grading/form/rubric/lang/en/gradingform_rubric.php
grade/grading/form/rubric/version.php
grade/grading/tests/privacy_legacy_polyfill_test.php [new file with mode: 0644]
grade/grading/tests/privacy_test.php [new file with mode: 0644]
grade/import/csv/version.php
grade/import/direct/version.php
grade/import/xml/version.php
grade/report/grader/version.php
grade/report/history/version.php
grade/report/outcomes/version.php
grade/report/overview/version.php
grade/report/singleview/version.php
grade/report/user/version.php
group/classes/privacy/provider.php [new file with mode: 0644]
group/tests/privacy_provider_test.php [new file with mode: 0644]
index.php
lang/en/backup.php
lang/en/badges.php
lang/en/completion.php
lang/en/course.php [new file with mode: 0644]
lang/en/enrol.php
lang/en/files.php
lang/en/grading.php
lang/en/group.php
lang/en/moodle.php
lang/en/notes.php
lang/en/portfolio.php
lang/en/privacy.php
lang/en/role.php
lang/en/search.php
lang/en/user.php [new file with mode: 0644]
lib/antivirus/clamav/version.php
lib/classes/event/notification_sent.php
lib/classes/output/icon_system_fontawesome.php
lib/classes/privacy/provider.php [new file with mode: 0644]
lib/classes/task/file_temp_cleanup_task.php
lib/db/upgrade.php
lib/editor/atto/db/upgrade.php
lib/editor/atto/plugins/accessibilitychecker/tests/behat/accessibilitychecker.feature
lib/editor/atto/plugins/accessibilitychecker/version.php
lib/editor/atto/plugins/accessibilityhelper/version.php
lib/editor/atto/plugins/align/version.php
lib/editor/atto/plugins/backcolor/version.php
lib/editor/atto/plugins/bold/version.php
lib/editor/atto/plugins/charmap/version.php
lib/editor/atto/plugins/clear/version.php
lib/editor/atto/plugins/collapse/version.php
lib/editor/atto/plugins/emoticon/version.php
lib/editor/atto/plugins/equation/version.php
lib/editor/atto/plugins/fontcolor/version.php
lib/editor/atto/plugins/html/version.php
lib/editor/atto/plugins/image/lang/en/atto_image.php
lib/editor/atto/plugins/image/tests/behat/image.feature
lib/editor/atto/plugins/image/version.php
lib/editor/atto/plugins/indent/version.php
lib/editor/atto/plugins/italic/version.php
lib/editor/atto/plugins/link/version.php
lib/editor/atto/plugins/managefiles/version.php
lib/editor/atto/plugins/media/lang/en/atto_media.php
lib/editor/atto/plugins/media/tests/behat/media.feature
lib/editor/atto/plugins/media/version.php
lib/editor/atto/plugins/noautolink/version.php
lib/editor/atto/plugins/orderedlist/version.php
lib/editor/atto/plugins/recordrtc/lang/en/atto_recordrtc.php
lib/editor/atto/plugins/recordrtc/lib.php
lib/editor/atto/plugins/recordrtc/pix/i/audiortc.png
lib/editor/atto/plugins/recordrtc/pix/i/videortc.png
lib/editor/atto/plugins/recordrtc/version.php
lib/editor/atto/plugins/rtl/version.php
lib/editor/atto/plugins/strike/version.php
lib/editor/atto/plugins/subscript/version.php
lib/editor/atto/plugins/superscript/version.php
lib/editor/atto/plugins/table/version.php
lib/editor/atto/plugins/title/version.php
lib/editor/atto/plugins/underline/version.php
lib/editor/atto/plugins/undo/version.php
lib/editor/atto/plugins/unorderedlist/version.php
lib/editor/atto/settings.php
lib/editor/atto/styles.css
lib/editor/atto/tests/behat/autosave.feature
lib/editor/atto/version.php
lib/editor/textarea/version.php
lib/editor/tinymce/plugins/ctrlhelp/version.php
lib/editor/tinymce/plugins/managefiles/version.php
lib/editor/tinymce/plugins/moodleemoticon/version.php
lib/editor/tinymce/plugins/moodleimage/version.php
lib/editor/tinymce/plugins/moodlemedia/version.php
lib/editor/tinymce/plugins/moodlenolink/version.php
lib/editor/tinymce/plugins/pdw/version.php
lib/editor/tinymce/plugins/spellchecker/version.php
lib/editor/tinymce/plugins/wrap/version.php
lib/editor/tinymce/version.php
lib/mlbackend/php/version.php
lib/mlbackend/python/version.php
lib/moodlelib.php
lib/portfolio/exporter.php
lib/portfoliolib.php
lib/tests/cronlib_test.php
lib/upgrade.txt
media/player/html5audio/version.php
media/player/html5video/version.php
media/player/swf/version.php
media/player/videojs/version.php
media/player/vimeo/version.php
media/player/youtube/version.php
message/output/airnotifier/version.php
message/output/email/version.php
message/output/jabber/version.php
message/output/popup/version.php
message/tests/events_test.php
message/tests/privacy_provider_test.php
mnet/service/enrol/classes/privacy/provider.php [new file with mode: 0644]
mnet/service/enrol/lang/en/mnetservice_enrol.php
mnet/service/enrol/tests/privacy_test.php [new file with mode: 0644]
mnet/service/enrol/version.php
mod/assign/classes/privacy/provider.php
mod/assign/feedback/comments/version.php
mod/assign/feedback/editpdf/classes/privacy/provider.php
mod/assign/feedback/editpdf/version.php
mod/assign/feedback/file/version.php
mod/assign/feedback/offline/version.php
mod/assign/locallib.php
mod/assign/submission/comments/version.php
mod/assign/submission/file/version.php
mod/assign/submission/onlinetext/version.php
mod/assign/tests/portfolio_caller_test.php [new file with mode: 0644]
mod/assign/tests/privacy_test.php
mod/assign/version.php
mod/assignment/type/offline/version.php
mod/assignment/type/online/version.php
mod/assignment/type/upload/version.php
mod/assignment/type/uploadsingle/version.php
mod/assignment/version.php
mod/book/tool/exportimscp/version.php
mod/book/tool/importhtml/version.php
mod/book/tool/print/version.php
mod/book/version.php
mod/chat/lang/en/chat.php
mod/chat/version.php
mod/choice/version.php
mod/data/classes/privacy/provider.php
mod/data/field/checkbox/version.php
mod/data/field/date/version.php
mod/data/field/file/version.php
mod/data/field/latlong/version.php
mod/data/field/menu/version.php
mod/data/field/multimenu/version.php
mod/data/field/number/version.php
mod/data/field/picture/version.php
mod/data/field/radiobutton/version.php
mod/data/field/text/version.php
mod/data/field/textarea/version.php
mod/data/field/url/version.php
mod/data/preset/imagegallery/version.php
mod/data/version.php
mod/feedback/version.php
mod/folder/version.php
mod/forum/classes/message/inbound/reply_handler.php
mod/forum/classes/privacy/provider.php [new file with mode: 0644]
mod/forum/classes/privacy/subcontext_info.php [new file with mode: 0644]
mod/forum/db/install.xml
mod/forum/db/upgrade.php
mod/forum/discuss.php
mod/forum/externallib.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/locallib.php
mod/forum/post.php
mod/forum/rsslib.php
mod/forum/tests/externallib_test.php
mod/forum/tests/helper.php [new file with mode: 0644]
mod/forum/tests/mail_test.php
mod/forum/tests/portfolio_caller_test.php [new file with mode: 0644]
mod/forum/tests/privacy_provider_test.php [new file with mode: 0644]
mod/forum/tests/subscriptions_test.php
mod/forum/version.php
mod/glossary/classes/privacy/provider.php [new file with mode: 0644]
mod/glossary/lang/en/glossary.php
mod/glossary/tests/privacy_provider_test.php [new file with mode: 0644]
mod/glossary/version.php
mod/imscp/version.php
mod/label/version.php
mod/lesson/classes/privacy/provider.php
mod/lesson/lang/en/lesson.php
mod/lesson/report.php
mod/lesson/tests/behat/duplicate_lesson_page.feature
mod/lesson/tests/behat/questions_images.feature
mod/lesson/version.php
mod/lti/service/gradebookservices/version.php
mod/lti/service/memberships/version.php
mod/lti/service/profile/version.php
mod/lti/service/toolproxy/version.php
mod/lti/service/toolsettings/version.php
mod/lti/version.php
mod/page/version.php
mod/quiz/accessrule/delaybetweenattempts/version.php
mod/quiz/accessrule/ipaddress/version.php
mod/quiz/accessrule/numattempts/version.php
mod/quiz/accessrule/offlineattempts/version.php
mod/quiz/accessrule/openclosedate/version.php
mod/quiz/accessrule/password/version.php
mod/quiz/accessrule/safebrowser/version.php
mod/quiz/accessrule/securewindow/version.php
mod/quiz/accessrule/timelimit/version.php
mod/quiz/addrandomform.php
mod/quiz/amd/build/add_random_form.min.js
mod/quiz/amd/src/add_random_form.js
mod/quiz/classes/privacy/provider.php
mod/quiz/report/grading/version.php
mod/quiz/report/overview/version.php
mod/quiz/report/responses/version.php
mod/quiz/report/statistics/version.php
mod/quiz/tests/behat/manually_mark_question.feature
mod/quiz/version.php
mod/resource/version.php
mod/scorm/report/basic/version.php
mod/scorm/report/graphs/version.php
mod/scorm/report/interactions/version.php
mod/scorm/report/objectives/version.php
mod/scorm/version.php
mod/survey/version.php
mod/upgrade.txt
mod/url/version.php
mod/wiki/classes/privacy/provider.php [new file with mode: 0644]
mod/wiki/lang/en/wiki.php
mod/wiki/tests/generator/lib.php
mod/wiki/tests/privacy_test.php [new file with mode: 0644]
mod/wiki/version.php
mod/workshop/allocation/manual/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/allocation/manual/lang/en/workshopallocation_manual.php
mod/workshop/allocation/manual/tests/privacy_provider_test.php [new file with mode: 0644]
mod/workshop/allocation/manual/version.php
mod/workshop/allocation/random/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/allocation/random/lang/en/workshopallocation_random.php
mod/workshop/allocation/random/version.php
mod/workshop/allocation/scheduled/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/allocation/scheduled/lang/en/workshopallocation_scheduled.php
mod/workshop/allocation/scheduled/version.php
mod/workshop/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/classes/privacy/workshopform_legacy_polyfill.php [new file with mode: 0644]
mod/workshop/classes/privacy/workshopform_provider.php [new file with mode: 0644]
mod/workshop/db/install.xml
mod/workshop/db/upgrade.php
mod/workshop/eval/best/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/eval/best/lang/en/workshopeval_best.php
mod/workshop/eval/best/version.php
mod/workshop/form/accumulative/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/form/accumulative/lang/en/workshopform_accumulative.php
mod/workshop/form/accumulative/tests/privacy_provider_test.php [new file with mode: 0644]
mod/workshop/form/accumulative/version.php
mod/workshop/form/comments/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/form/comments/lang/en/workshopform_comments.php
mod/workshop/form/comments/tests/privacy_provider_test.php [new file with mode: 0644]
mod/workshop/form/comments/version.php
mod/workshop/form/numerrors/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/form/numerrors/lang/en/workshopform_numerrors.php
mod/workshop/form/numerrors/tests/privacy_provider_test.php [new file with mode: 0644]
mod/workshop/form/numerrors/version.php
mod/workshop/form/rubric/classes/privacy/provider.php [new file with mode: 0644]
mod/workshop/form/rubric/lang/en/workshopform_rubric.php
mod/workshop/form/rubric/tests/privacy_provider_test.php [new file with mode: 0644]
mod/workshop/form/rubric/version.php
mod/workshop/lang/en/workshop.php
mod/workshop/tests/behat/embedded_images.feature
mod/workshop/tests/generator/lib.php
mod/workshop/tests/privacy_provider_test.php [new file with mode: 0644]
mod/workshop/version.php
phpunit.xml.dist
pix/e/insert_edit_video.png
pix/e/insert_edit_video.svg
portfolio/boxnet/classes/privacy/provider.php
portfolio/boxnet/version.php
portfolio/classes/privacy/provider.php
portfolio/download/version.php
portfolio/flickr/classes/privacy/provider.php
portfolio/flickr/version.php
portfolio/googledocs/classes/privacy/provider.php
portfolio/googledocs/version.php
portfolio/mahara/classes/privacy/provider.php
portfolio/mahara/version.php
portfolio/picasa/classes/privacy/provider.php
portfolio/picasa/version.php
portfolio/tests/privacy_provider_test.php
privacy/classes/local/request/context_aware_provider.php [new file with mode: 0644]
privacy/classes/local/request/contextlist.php
privacy/classes/local/request/moodle_content_writer.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/tests_content_writer_test.php
question/behaviour/adaptive/version.php
question/behaviour/adaptivenopenalty/version.php
question/behaviour/deferredcbm/version.php
question/behaviour/deferredfeedback/version.php
question/behaviour/immediatecbm/version.php
question/behaviour/immediatefeedback/version.php
question/behaviour/informationitem/version.php
question/behaviour/interactive/version.php
question/behaviour/interactivecountback/version.php
question/behaviour/manualgraded/version.php
question/behaviour/missing/version.php
question/format/aiken/version.php
question/format/blackboard_six/version.php
question/format/examview/version.php
question/format/gift/version.php
question/format/missingword/version.php
question/format/multianswer/version.php
question/format/webct/version.php
question/format/xhtml/version.php
question/format/xml/version.php
question/type/calculated/datasetitems_form.php
question/type/calculated/edit_calculated_form.php
question/type/calculated/question.php
question/type/calculated/questiontype.php
question/type/calculated/tests/formula_validation_test.php
question/type/calculated/tests/questiontype_test.php
question/type/calculated/version.php
question/type/calculatedmulti/db/upgradelib.php
question/type/calculatedmulti/edit_calculatedmulti_form.php
question/type/calculatedmulti/questiontype.php
question/type/calculatedmulti/version.php
question/type/calculatedsimple/version.php
question/type/ddimageortext/lang/en/qtype_ddimageortext.php
question/type/ddimageortext/version.php
question/type/ddmarker/lang/en/qtype_ddmarker.php
question/type/ddmarker/version.php
question/type/ddwtos/lang/en/qtype_ddwtos.php
question/type/ddwtos/version.php
question/type/description/version.php
question/type/essay/lang/en/qtype_essay.php
question/type/essay/version.php
question/type/gapselect/version.php
question/type/match/lang/en/qtype_match.php
question/type/match/version.php
question/type/missingtype/lang/en/qtype_missingtype.php
question/type/missingtype/version.php
question/type/multianswer/version.php
question/type/multichoice/lang/en/qtype_multichoice.php
question/type/multichoice/version.php
question/type/numerical/version.php
question/type/random/version.php
question/type/randomsamatch/lang/en/qtype_randomsamatch.php
question/type/randomsamatch/version.php
question/type/shortanswer/lang/en/qtype_shortanswer.php
question/type/shortanswer/version.php
question/type/truefalse/version.php
rating/classes/privacy/provider.php
report/backups/version.php
report/competency/version.php
report/completion/version.php
report/configlog/version.php
report/courseoverview/version.php
report/eventlist/version.php
report/insights/version.php
report/log/version.php
report/loglive/version.php
report/outline/version.php
report/participation/version.php
report/performance/version.php
report/progress/version.php
report/questioninstances/version.php
report/security/version.php
report/stats/classes/privacy/provider.php
report/stats/lang/en/report_stats.php
report/stats/tests/privacy_test.php [new file with mode: 0644]
report/stats/version.php
report/usersessions/version.php
repository/areafiles/version.php
repository/boxnet/version.php
repository/coursefiles/version.php
repository/dropbox/version.php
repository/equella/version.php
repository/filesystem/version.php
repository/flickr/version.php
repository/flickr_public/version.php
repository/googledocs/version.php
repository/local/version.php
repository/merlot/version.php
repository/onedrive/version.php
repository/picasa/version.php
repository/recent/version.php
repository/s3/version.php
repository/skydrive/version.php
repository/upload/version.php
repository/url/version.php
repository/user/version.php
repository/webdav/version.php
repository/wikimedia/version.php
repository/youtube/version.php
search/classes/privacy/provider.php [new file with mode: 0644]
search/engine/simpledb/classes/privacy/provider.php [new file with mode: 0644]
search/engine/simpledb/lang/en/search_simpledb.php
search/engine/simpledb/tests/privacy_test.php [new file with mode: 0644]
search/engine/simpledb/version.php
search/engine/solr/classes/privacy/provider.php [new file with mode: 0644]
search/engine/solr/lang/en/search_solr.php
search/engine/solr/tests/privacy_test.php [new file with mode: 0644]
search/engine/solr/version.php
search/tests/fixtures/mock_search_area.php
tag/classes/tests/privacy_helper.php [new file with mode: 0644]
theme/boost/classes/output/core_course/management/renderer.php
theme/boost/classes/output/core_renderer.php
theme/boost/scss/moodle/bs4alphacompat.scss
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/undo.scss
theme/boost/templates/columns2.mustache
theme/boost/templates/core/auth_digital_minor_page.mustache
theme/boost/templates/core/auth_verify_age_location_page.mustache
theme/boost/templates/core/paging_bar.mustache
theme/boost/templates/core_form/element-advcheckbox-inline.mustache
theme/boost/templates/core_form/element-advcheckbox.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_selector-inline.mustache
theme/boost/templates/core_form/element-date_selector.mustache
theme/boost/templates/core_form/element-radio-inline.mustache
theme/boost/templates/core_form/element-radio.mustache
theme/boost/templates/core_form/element-template-inline.mustache
theme/boost/templates/footer.mustache
theme/boost/version.php
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/version.php
theme/clean/version.php
theme/more/version.php
user/classes/privacy/provider.php [new file with mode: 0644]
user/profile/field/checkbox/version.php
user/profile/field/datetime/version.php
user/profile/field/menu/version.php
user/profile/field/text/version.php
user/profile/field/textarea/version.php
user/tests/privacy_test.php [new file with mode: 0644]
version.php
webservice/rest/version.php
webservice/soap/version.php
webservice/xmlrpc/version.php

diff --git a/admin/roles/classes/privacy/provider.php b/admin/roles/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..ee8bcea
--- /dev/null
@@ -0,0 +1,382 @@
+<?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_role.
+ *
+ * @package    core_role
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_role\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\local\metadata\collection;
+use \core_privacy\local\request\contextlist;
+use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\transform;
+use \core_privacy\local\request\writer;
+
+/**
+ * Privacy provider for core_role.
+ *
+ * @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\provider,
+    \core_privacy\local\request\subsystem\provider,
+    \core_privacy\local\request\subsystem\plugin_provider,
+    \core_privacy\local\request\user_preference_provider {
+
+    /**
+     * Get information about the user data stored by this plugin.
+     *
+     * @param  collection $collection An object for storing metadata.
+     * @return collection The metadata.
+     */
+    public static function get_metadata(collection $collection) : collection {
+        $rolecapabilities = [
+            'roleid' => 'privacy:metadata:role_capabilities:roleid',
+            'capability' => 'privacy:metadata:role_capabilities:capability',
+            'permission' => 'privacy:metadata:role_capabilities:permission',
+            'timemodified' => 'privacy:metadata:role_capabilities:timemodified',
+            'modifierid' => 'privacy:metadata:role_capabilities:modifierid'
+        ];
+        $roleassignments = [
+            'roleid' => 'privacy:metadata:role_assignments:roleid',
+            'userid' => 'privacy:metadata:role_assignments:userid',
+            'timemodified' => 'privacy:metadata:role_assignments:timemodified',
+            'modifierid' => 'privacy:metadata:role_assignments:modifierid',
+            'component' => 'privacy:metadata:role_assignments:component',
+            'itemid' => 'privacy:metadata:role_assignments:itemid'
+        ];
+        $collection->add_database_table('role_capabilities', $rolecapabilities,
+            'privacy:metadata:role_capabilities:tableexplanation');
+        $collection->add_database_table('role_assignments', $roleassignments,
+            'privacy:metadata:role_assignments:tableexplanation');
+
+        $collection->add_user_preference('definerole_showadvanced',
+            'privacy:metadata:preference:showadvanced');
+
+        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) {
+        $showadvanced = get_user_preferences('definerole_showadvanced', null, $userid);
+        if ($showadvanced !== null) {
+            writer::export_user_preference('core_role',
+                'definerole_showadvanced',
+                transform::yesno($showadvanced),
+                get_string('privacy:metadata:preference:showadvanced', 'core_role')
+            );
+        }
+    }
+    /**
+     * Return all contexts for this userid.
+     *
+     * @param  int $userid The user ID.
+     * @return contextlist The list of context IDs.
+     */
+    public static function get_contexts_for_userid(int $userid) : contextlist {
+        global $DB;
+
+        $contextlist = new contextlist();
+
+        // The role_capabilities table contains user data.
+        $contexts = [
+            CONTEXT_SYSTEM,
+            CONTEXT_USER,
+            CONTEXT_COURSECAT,
+            CONTEXT_COURSE,
+            CONTEXT_MODULE,
+            CONTEXT_BLOCK
+        ];
+        list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+        $sql = "SELECT ctx.id
+                  FROM {context} ctx
+                  JOIN {role_capabilities} rc
+                    ON rc.contextid = ctx.id
+                   AND ((ctx.contextlevel {$insql} AND rc.modifierid = :modifierid)
+                    OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))";
+        $params = [
+            'modifierid' => $userid,
+            'contextlevel' => CONTEXT_USER,
+            'userid' => $userid
+         ];
+        $params += $inparams;
+
+        $contextlist->add_from_sql($sql, $params);
+
+        // The role_assignments table contains user data.
+        $contexts = [
+            CONTEXT_SYSTEM,
+            CONTEXT_USER,
+            CONTEXT_COURSECAT,
+            CONTEXT_COURSE,
+            CONTEXT_MODULE,
+            CONTEXT_BLOCK
+        ];
+        list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+        $params = [
+            'userid' => $userid,
+            'modifierid' => $userid
+         ];
+        $params += $inparams;
+        $sql = "SELECT ctx.id
+                  FROM {role_assignments} ra
+                  JOIN {context} ctx
+                    ON ctx.id = ra.contextid
+                   AND ctx.contextlevel {$insql}
+                 WHERE (ra.userid = :userid
+                    OR ra.modifierid = :modifierid)
+                   AND ra.component != 'tool_cohortroles'";
+        $contextlist->add_from_sql($sql, $params);
+
+        return $contextlist;
+    }
+    /**
+     * Export all user data for the specified user, in the specified contexts.
+     *
+     * @param  approved_contextlist $contextlist The list of approved contexts for a user.
+     */
+    public static function export_user_data(approved_contextlist $contextlist) {
+        global $DB;
+
+        if (empty($contextlist)) {
+             return;
+        }
+
+        $rolesnames = self::get_roles_name();
+        $userid = $contextlist->get_user()->id;
+        $ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
+        list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
+
+        // Role Assignments export data.
+        $contexts = [
+            CONTEXT_SYSTEM,
+            CONTEXT_USER,
+            CONTEXT_COURSECAT,
+            CONTEXT_COURSE,
+            CONTEXT_MODULE,
+            CONTEXT_BLOCK
+        ];
+        list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+        $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, $ctxfields
+                  FROM {role_assignments} ra
+                  JOIN {context} ctx
+                    ON ctx.id = ra.contextid
+                   AND ctx.contextlevel {$inctxsql}
+                   AND (ra.userid = :userid OR ra.modifierid = :modifierid)
+                   AND ra.component != 'tool_cohortroles'
+                  JOIN {role} r
+                    ON r.id = ra.roleid
+                 WHERE ctx.id {$insql}";
+        $params = ['userid' => $userid, 'modifierid' => $userid];
+        $params += $inparams;
+        $params += $ctxparams;
+        $assignments = $DB->get_recordset_sql($sql, $params);
+        foreach ($assignments as $assignment) {
+            \context_helper::preload_from_record($assignment);
+            $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
+                'timemodified' => transform::datetime($assignment->timemodified),
+                'userid' => transform::user($assignment->userid),
+                'modifierid' => transform::user($assignment->modifierid)
+            ];
+        }
+        $assignments->close();
+        if (!empty($alldata)) {
+            array_walk($alldata, function($roledata, $contextid) {
+                $context = \context::instance_by_id($contextid);
+                array_walk($roledata, function($data, $rolename) use ($context) {
+                    writer::with_context($context)->export_data(
+                            [get_string('privacy:metadata:role_assignments', 'core_role'), $rolename],
+                            (object)$data);
+                });
+            });
+            unset($alldata);
+        }
+
+        // Role Capabilities export data.
+        $strpermissions = self::get_permissions_name();
+        $contexts = [
+            CONTEXT_SYSTEM,
+            CONTEXT_USER,
+            CONTEXT_COURSECAT,
+            CONTEXT_COURSE,
+            CONTEXT_MODULE,
+            CONTEXT_BLOCK
+        ];
+        list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+        $sql = "SELECT rc.id, rc.contextid, rc.capability, rc.permission, rc.timemodified, rc.roleid, $ctxfields
+                  FROM {context} ctx
+                  JOIN {role_capabilities} rc
+                    ON rc.contextid = ctx.id
+                   AND ((ctx.contextlevel {$inctxsql} AND rc.modifierid = :modifierid)
+                    OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))
+                 WHERE ctx.id {$insql}";
+        $params = [
+            'modifierid' => $userid,
+            'contextlevel' => CONTEXT_USER,
+            'userid' => $userid
+         ];
+        $params += $inparams;
+        $params += $ctxparams;
+        $capabilities = $DB->get_recordset_sql($sql, $params);
+        foreach ($capabilities as $capability) {
+            \context_helper::preload_from_record($capability);
+            $alldata[$capability->contextid][$rolesnames[$capability->roleid]][] = (object)[
+                'timemodified' => transform::datetime($capability->timemodified),
+                'capability' => $capability->capability,
+                'permission' => $strpermissions[$capability->permission]
+            ];
+        }
+        $capabilities->close();
+        if (!empty($alldata)) {
+            array_walk($alldata, function($capdata, $contextid) {
+                $context = \context::instance_by_id($contextid);
+                array_walk($capdata, function($data, $rolename) use ($context) {
+                    writer::with_context($context)->export_data(
+                            [get_string('privacy:metadata:role_capabilities', 'core_role'), $rolename],
+                            (object)$data);
+                });
+            });
+        }
+    }
+    /**
+     * Exports the data relating to tool_cohortroles component on role assignments by
+     * Assign user roles to cohort feature.
+     *
+     * @param  int $userid The user ID.
+     */
+    public static function export_user_role_to_cohort(int $userid) {
+        global $DB;
+
+        $rolesnames = self::get_roles_name();
+        $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, r.id as roleid
+                  FROM {role_assignments} ra
+                  JOIN {context} ctx
+                    ON ctx.id = ra.contextid
+                   AND ctx.contextlevel = :contextlevel
+                   AND ra.component = 'tool_cohortroles'
+                  JOIN {role} r
+                    ON r.id = ra.roleid
+                 WHERE ctx.instanceid = :instanceid
+                    OR ra.userid = :userid";
+        $params = ['userid' => $userid, 'instanceid' => $userid, 'contextlevel' => CONTEXT_USER];
+        $assignments = $DB->get_recordset_sql($sql, $params);
+        foreach ($assignments as $assignment) {
+            $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
+                'timemodified' => transform::datetime($assignment->timemodified),
+                'userid' => transform::user($assignment->userid),
+                'modifierid' => transform::user($assignment->modifierid)
+            ];
+        }
+        $assignments->close();
+        if (!empty($alldata)) {
+            array_walk($alldata, function($roledata, $contextid) {
+                $context = \context::instance_by_id($contextid);
+                array_walk($roledata, function($data, $rolename) use ($context) {
+                    writer::with_context($context)->export_related_data(
+                            [get_string('privacy:metadata:role_cohortroles', 'core_role'), $rolename], 'cohortroles',
+                            (object)$data);
+                });
+            });
+        }
+    }
+    /**
+     * Delete all user data for this context.
+     *
+     * @param  \context $context The context to delete data for.
+     */
+    public static function delete_data_for_all_users_in_context(\context $context) {
+        global $DB;
+
+        // Don't remove data from role_capabilities.
+        // Because this data affects the whole Moodle, there are override capabilities.
+        // Don't belong to the modifier user.
+
+        // Remove data from role_assignments.
+        if (empty($context)) {
+            return;
+        }
+        $DB->delete_records('role_assignments', ['contextid' => $context->id]);
+    }
+    /**
+     * Delete all user data for this user only.
+     *
+     * @param  approved_contextlist $contextlist The list of approved contexts for a user.
+     */
+    public static function delete_data_for_user(approved_contextlist $contextlist) {
+        global $DB;
+
+        // Don't remove data from role_capabilities.
+        // Because this data affects the whole Moodle, there are override capabilities.
+        // Don't belong to the modifier user.
+
+        // Remove data from role_assignments.
+        if (empty($contextlist->count())) {
+            return;
+        }
+        $userid = $contextlist->get_user()->id;
+        foreach ($contextlist->get_contexts() as $context) {
+            // Only delete the roles assignments where the user is assigned in all contexts.
+            $DB->delete_records('role_assignments', ['userid' => $userid, 'contextid' => $context->id]);
+        }
+    }
+    /**
+     * Delete user entries in role_assignments related to the feature
+     * Assign user roles to cohort feature.
+     *
+     * @param  int $userid The user ID.
+     */
+    public static function delete_user_role_to_cohort(int $userid) {
+        global $DB;
+
+        // Delete entries where userid is a mentor by tool_cohortroles.
+        $DB->delete_records('role_assignments', ['userid' => $userid, 'component' => 'tool_cohortroles']);
+    }
+    /**
+     * Get all the localised roles name in a simple array.
+     *
+     * @return array Array of name of the roles by roleid.
+     */
+    protected static function get_roles_name() {
+        $roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
+        $rolesnames = array();
+        foreach ($roles as $role) {
+            $rolesnames[$role->id] = $role->localname;
+        }
+        return $rolesnames;
+    }
+    /**
+     * Get all the permissions name in a simple array.
+     *
+     * @return array Array of permissions name.
+     */
+    protected static function get_permissions_name() {
+        $strpermissions = array(
+            CAP_INHERIT => get_string('inherit', 'role'),
+            CAP_ALLOW => get_string('allow', 'role'),
+            CAP_PREVENT => get_string('prevent', 'role'),
+            CAP_PROHIBIT => get_string('prohibit', 'role')
+        );
+        return $strpermissions;
+    }
+}
\ No newline at end of file
diff --git a/admin/roles/tests/privacy_test.php b/admin/roles/tests/privacy_test.php
new file mode 100644 (file)
index 0000000..bffc919
--- /dev/null
@@ -0,0 +1,477 @@
+<?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 test for core_role
+ *
+ * @package    core_role
+ * @category   test
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+defined('MOODLE_INTERNAL') || die();
+use \core_role\privacy\provider;
+use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\writer;
+use \core_privacy\tests\provider_testcase;
+use \core_privacy\local\request\transform;
+use \tool_cohortroles\api;
+
+/**
+ * Privacy test for core_role
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_role_privacy_testcase extends provider_testcase {
+    /**
+     * Test to check export_user_preferences.
+     * returns user preferences data.
+     */
+    public function test_export_user_preferences() {
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $user = $this->getDataGenerator()->create_user();
+        $this->setUser($user);
+        $showadvanced = 1;
+        set_user_preference('definerole_showadvanced', $showadvanced);
+        provider::export_user_preferences($user->id);
+        $writer = writer::with_context(\context_system::instance());
+        $prefs = $writer->get_user_preferences('core_role');
+        $this->assertEquals(transform::yesno($showadvanced), transform::yesno($prefs->definerole_showadvanced->value));
+        $this->assertEquals(get_string('privacy:metadata:preference:showadvanced', 'core_role'),
+            $prefs->definerole_showadvanced->description);
+    }
+    /**
+     * Check all contexts are returned if there is any user data for this user.
+     */
+    public function test_get_contexts_for_userid() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $user = $this->getDataGenerator()->create_user();
+        $this->assertEmpty(provider::get_contexts_for_userid($user->id));
+
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext2 = \context_user::instance($user2->id);
+        $course = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $coursecat = $this->getDataGenerator()->create_category();
+        $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+        $cmcontext = \context_module::instance($cm->cmid);
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+        $cmcontext2 = \context_module::instance($page->cmid);
+        $coursecontext = \context_course::instance($course->id);
+        $coursecontext2 = \context_course::instance($course2->id);
+        $coursecatcontext = \context_coursecat::instance($coursecat->id);
+        $systemcontext = \context_system::instance();
+        $block = $this->getDataGenerator()->create_block('online_users');
+        $blockcontext = \context_block::instance($block->id);
+
+        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+        $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+
+        // Role assignments, where the user is assigned.
+        role_assign($student->id, $user->id, $cmcontext2->id);
+        role_assign($student->id, $user->id, $coursecontext2->id);
+        role_assign($student->id, $user->id, $blockcontext->id);
+        role_assign($manager->id, $user->id, $usercontext2->id);
+        // Role assignments, where the user makes assignments.
+        $this->setUser($user);
+        role_assign($student->id, $user2->id, $coursecontext->id);
+        role_assign($manager->id, $user2->id, $coursecatcontext->id);
+        role_assign($manager->id, $user2->id, $systemcontext->id);
+
+        // Role capabilities.
+        $this->setUser($user);
+        $result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $cmcontext->id);
+
+        $contextlist = provider::get_contexts_for_userid($user->id)->get_contextids();
+        $this->assertCount(8, $contextlist);
+        $this->assertTrue(in_array($cmcontext->id, $contextlist));
+    }
+
+    /**
+     * Test that user data is exported correctly.
+     */
+    public function test_export_user_data() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext2 = \context_user::instance($user2->id);
+        $course = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $coursecat = $this->getDataGenerator()->create_category();
+        $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+        $cmcontext = \context_module::instance($cm->cmid);
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+        $cmcontext2 = \context_module::instance($page->cmid);
+        $coursecontext = \context_course::instance($course->id);
+        $coursecontext2 = \context_course::instance($course2->id);
+        $coursecatcontext = \context_coursecat::instance($coursecat->id);
+        $systemcontext = \context_system::instance();
+        $block = $this->getDataGenerator()->create_block('online_users');
+        $blockcontext = \context_block::instance($block->id);
+
+        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+        $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+        $rolesnames = self::get_roles_name();
+
+        $subcontextstudent = [
+            get_string('privacy:metadata:role_assignments', 'core_role'),
+            $rolesnames[$student->id]
+        ];
+        $subcontextmanager = [
+            get_string('privacy:metadata:role_assignments', 'core_role'),
+            $rolesnames[$manager->id]
+        ];
+        $subcontextrc = [
+            get_string('privacy:metadata:role_capabilities', 'core_role'),
+            $rolesnames[$student->id]
+        ];
+
+        // Test over role assignments.
+        // Where the user is assigned.
+        role_assign($student->id, $user->id, $cmcontext2->id);
+        role_assign($student->id, $user->id, $coursecontext2->id);
+        role_assign($student->id, $user->id, $blockcontext->id);
+        role_assign($manager->id, $user->id, $usercontext2->id);
+        // Where the user makes assignments.
+        $this->setUser($user);
+        role_assign($manager->id, $user2->id, $coursecatcontext->id);
+        role_assign($manager->id, $user2->id, $systemcontext->id);
+
+        // Test overridable roles in module, course, category, user, system and block.
+        assign_capability('moodle/backup:backupactivity', CAP_ALLOW, $student->id, $cmcontext->id, true);
+        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $coursecontext->id, true);
+        assign_capability('moodle/category:manage', CAP_ALLOW, $student->id, $coursecatcontext->id, true);
+        assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $systemcontext->id, true);
+        assign_capability('moodle/block:edit', CAP_ALLOW, $student->id, $blockcontext->id, true);
+        assign_capability('moodle/competency:evidencedelete', CAP_ALLOW, $student->id, $usercontext2->id, true);
+
+        // Retrieve the user's context ids.
+        $contextlist = provider::get_contexts_for_userid($user->id);
+        $approvedcontextlist = new approved_contextlist($user, 'core_role', $contextlist->get_contextids());
+
+        $strpermissions = array(
+            CAP_INHERIT => get_string('inherit', 'role'),
+            CAP_ALLOW => get_string('allow', 'role'),
+            CAP_PREVENT => get_string('prevent', 'role'),
+            CAP_PROHIBIT => get_string('prohibit', 'role')
+        );
+        // Retrieve role capabilities and role assignments.
+        provider::export_user_data($approvedcontextlist);
+        foreach ($contextlist as $context) {
+            $writer = writer::with_context($context);
+            $this->assertTrue($writer->has_any_data());
+            if ($context->contextlevel == CONTEXT_MODULE) {
+                if ($data = $writer->get_data($subcontextstudent)) {
+                    $this->assertEquals($user->id, reset($data)->userid);
+                }
+                if ($data = $writer->get_data($subcontextrc)) {
+                    $this->assertEquals('moodle/backup:backupactivity', reset($data)->capability);
+                    $this->assertEquals($strpermissions[CAP_ALLOW], reset($data)->permission);
+                }
+            }
+            if ($context->contextlevel == CONTEXT_COURSE) {
+                if ($data = $writer->get_data($subcontextstudent)) {
+                    $this->assertEquals($user->id, reset($data)->userid);
+                }
+                if ($data = $writer->get_data($subcontextrc)) {
+                    $this->assertEquals('moodle/backup:backupcourse', reset($data)->capability);
+                }
+            }
+            if ($context->contextlevel == CONTEXT_COURSECAT) {
+                if ($data = $writer->get_data($subcontextmanager)) {
+                    $this->assertEquals($user->id, reset($data)->modifierid);
+                }
+                if ($data = $writer->get_data($subcontextrc)) {
+                    $this->assertEquals('moodle/category:manage', reset($data)->capability);
+                }
+            }
+            if ($context->contextlevel == CONTEXT_SYSTEM) {
+                if ($data = $writer->get_data($subcontextmanager)) {
+                    $this->assertEquals($user->id, reset($data)->modifierid);
+                }
+                if ($data = $writer->get_data($subcontextrc)) {
+                    $this->assertEquals('moodle/backup:backupcourse', reset($data)->capability);
+                }
+            }
+            if ($context->contextlevel == CONTEXT_BLOCK) {
+                if ($data = $writer->get_data($subcontextstudent)) {
+                    $this->assertEquals($user->id, reset($data)->userid);
+                }
+                if ($data = $writer->get_data($subcontextrc)) {
+                    $this->assertEquals('moodle/block:edit', reset($data)->capability);
+                }
+            }
+            if ($context->contextlevel == CONTEXT_USER) {
+                if ($data = $writer->get_data($subcontextmanager)) {
+                    $this->assertEquals($user->id, reset($data)->userid);
+                }
+                if ($data = $writer->get_data($subcontextrc)) {
+                    $this->assertEquals('moodle/competency:evidencedelete', reset($data)->capability);
+                }
+            }
+        }
+    }
+    /**
+     * Test for provider::delete_data_for_all_users_in_context().
+     */
+    public function test_delete_data_for_all_users_in_context() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext2 = \context_user::instance($user2->id);
+        $user3 = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = \context_course::instance($course->id);
+        $coursecat = $this->getDataGenerator()->create_category();
+        $coursecatcontext = \context_coursecat::instance($coursecat->id);
+        $systemcontext = \context_system::instance();
+        $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+        $cmcontext = \context_module::instance($cm->cmid);
+        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+        $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+        $block = $this->getDataGenerator()->create_block('online_users');
+        $blockcontext = \context_block::instance($block->id);
+
+        // Role assignments CONTEXT_COURSE.
+        role_assign($student->id, $user->id, $coursecontext->id);
+        role_assign($student->id, $user2->id, $coursecontext->id);
+        role_assign($student->id, $user3->id, $coursecontext->id);
+        $count = $DB->count_records('role_assignments', ['contextid' => $coursecontext->id]);
+        $this->assertEquals(3, $count);
+        // Role assignments CONTEXT_COURSECAT.
+        role_assign($student->id, $user2->id, $coursecatcontext->id);
+        role_assign($student->id, $user3->id, $coursecatcontext->id);
+        $count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
+        $this->assertEquals(2, $count);
+        // Role assignments CONTEXT_SYSTEM.
+        role_assign($student->id, $user->id, $systemcontext->id);
+        $count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
+        $this->assertEquals(1, $count);
+        // Role assignments CONTEXT_MODULE.
+        role_assign($student->id, $user->id, $cmcontext->id);
+        $count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
+        $this->assertEquals(1, $count);
+        // Role assigments CONTEXT_BLOCK.
+        role_assign($student->id, $user->id, $blockcontext->id);
+        $count = $DB->count_records('role_assignments', ['contextid' => $blockcontext->id]);
+        $this->assertEquals(1, $count);
+        // Role assigments CONTEXT_USER.
+        role_assign($manager->id, $user->id, $usercontext2->id);
+        $count = $DB->count_records('role_assignments', ['contextid' => $usercontext2->id]);
+        $this->assertEquals(1, $count);
+
+        // Delete data based on CONTEXT_COURSE context.
+        provider::delete_data_for_all_users_in_context($coursecontext);
+        // After deletion, the role_assignments entries for this context should have been deleted.
+        $count = $DB->count_records('role_assignments', ['contextid' => $coursecontext->id]);
+        $this->assertEquals(0, $count);
+        // Check it is not removing data on other contexts.
+        $count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
+        $this->assertEquals(2, $count);
+        $count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
+        $this->assertEquals(1, $count);
+        $count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
+        $this->assertEquals(1, $count);
+        // Delete data based on CONTEXT_COURSECAT context.
+        provider::delete_data_for_all_users_in_context($coursecatcontext);
+        // After deletion, the role_assignments entries for this context should have been deleted.
+        $count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
+        $this->assertEquals(0, $count);
+        // Delete data based on CONTEXT_SYSTEM context.
+        provider::delete_data_for_all_users_in_context($systemcontext);
+        // After deletion, the role_assignments entries for this context should have been deleted.
+        $count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
+        $this->assertEquals(0, $count);
+        // Delete data based on CONTEXT_MODULE context.
+        provider::delete_data_for_all_users_in_context($cmcontext);
+        // After deletion, the role_assignments entries for this context should have been deleted.
+        $count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
+        $this->assertEquals(0, $count);
+        // Delete data based on CONTEXT_BLOCK context.
+        provider::delete_data_for_all_users_in_context($usercontext2);
+        // After deletion, the role_assignments entries for this context should have been deleted.
+        $count = $DB->count_records('role_assignments', ['contextid' => $usercontext2->id]);
+        $this->assertEquals(0, $count);
+    }
+    /**
+     * Test for provider::delete_data_for_user().
+     */
+    public function test_delete_data_for_user() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext2 = \context_user::instance($user2->id);
+        $user3 = $this->getDataGenerator()->create_user();
+        $usercontext3 = \context_user::instance($user3->id);
+        $course = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $course3 = $this->getDataGenerator()->create_course();
+        $coursecontext = \context_course::instance($course->id);
+        $coursecontext2 = \context_course::instance($course2->id);
+        $coursecontext3 = \context_course::instance($course3->id);
+        $coursecat = $this->getDataGenerator()->create_category();
+        $coursecatcontext = \context_coursecat::instance($coursecat->id);
+        $systemcontext = \context_system::instance();
+        $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+        $cmcontext = \context_module::instance($cm->cmid);
+        $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+        $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+        $block = $this->getDataGenerator()->create_block('online_users');
+        $blockcontext = \context_block::instance($block->id);
+
+        // Role assignments, Where the user is assigned.
+        role_assign($student->id, $user->id, $coursecontext->id);
+        role_assign($student->id, $user->id, $coursecontext2->id);
+        role_assign($student->id, $user->id, $coursecatcontext->id);
+        role_assign($student->id, $user->id, $cmcontext->id);
+        role_assign($student->id, $user->id, $systemcontext->id);
+        role_assign($student->id, $user->id, $blockcontext->id);
+        role_assign($manager->id, $user->id, $usercontext2->id);
+        role_assign($manager->id, $user->id, $usercontext3->id);
+        $count = $DB->count_records('role_assignments', ['userid' => $user->id]);
+        $this->assertEquals(8, $count);
+        // Role assignments, where the user makes assignments.
+        $this->setUser($user);
+        role_assign($student->id, $user2->id, $coursecontext3->id);
+        role_assign($student->id, $user3->id, $coursecontext3->id);
+        $count = $DB->count_records('role_assignments', ['modifierid' => $user->id]);
+        $this->assertEquals(2, $count);
+
+        $contextlist = provider::get_contexts_for_userid($user->id);
+        $approvedcontextlist = new approved_contextlist($user, 'core_role', $contextlist->get_contextids());
+        provider::delete_data_for_user($approvedcontextlist);
+        // After deletion, the role_assignments assigned to the user should have been deleted.
+        $count = $DB->count_records('role_assignments', ['userid' => $user->id]);
+        $this->assertEquals(0, $count);
+        // After deletion, the role_assignments assigned by the user should not have been deleted.
+        $count = $DB->count_records('role_assignments', ['modifierid' => $user->id]);
+        $this->assertEquals(2, $count);
+    }
+    /**
+     * Export for a user with a key against a script where no instance is specified.
+     */
+    public function test_export_user_role_to_cohort() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        // Assign user roles to cohort.
+        $user = $this->getDataGenerator()->create_user();
+        $contextuser = \context_user::instance($user->id);
+        $teacher = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
+        $cohort = $this->getDataGenerator()->create_cohort();
+        $userassignover = $this->getDataGenerator()->create_user();
+        $contextuserassignover = \context_user::instance($userassignover->id);
+        cohort_add_member($cohort->id, $userassignover->id);
+        $this->setAdminUser();
+        $params = (object) array(
+            'userid' => $user->id,
+            'roleid' => $teacher->id,
+            'cohortid' => $cohort->id
+        );
+        api::create_cohort_role_assignment($params);
+        api::sync_all_cohort_roles();
+        $rolesnames = self::get_roles_name();
+        $subcontextteacher = [
+            get_string('privacy:metadata:role_cohortroles', 'core_role'),
+            $rolesnames[$teacher->id]
+        ];
+        // Test User is assigned role teacher to cohort.
+        provider::export_user_role_to_cohort($user->id);
+        $writer = writer::with_context($contextuserassignover);
+        $this->assertTrue($writer->has_any_data());
+        $exported = $writer->get_related_data($subcontextteacher, 'cohortroles');
+        $this->assertEquals($user->id, reset($exported)->userid);
+
+        // Test User is member of a cohort which User2 is assigned to role to this cohort.
+        $user2 = $this->getDataGenerator()->create_user();
+        $cohort2 = $this->getDataGenerator()->create_cohort();
+        cohort_add_member($cohort2->id, $user->id);
+        $params = (object) array(
+            'userid' => $user2->id,
+            'roleid' => $teacher->id,
+            'cohortid' => $cohort2->id
+        );
+        api::create_cohort_role_assignment($params);
+        api::sync_all_cohort_roles();
+        provider::export_user_role_to_cohort($user->id);
+        $writer = writer::with_context($contextuser);
+        $this->assertTrue($writer->has_any_data());
+        $exported = $writer->get_related_data($subcontextteacher, 'cohortroles');
+        $this->assertEquals($user2->id, reset($exported)->userid);
+    }
+    /**
+     * Test for provider::delete_user_role_to_cohort().
+     */
+    public function test_delete_user_role_to_cohort() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        // Assign user roles to cohort.
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
+        $teacher = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
+        $cohort = $this->getDataGenerator()->create_cohort();
+        cohort_add_member($cohort->id, $user2->id);
+        cohort_add_member($cohort->id, $user3->id);
+        cohort_add_member($cohort->id, $user4->id);
+        $this->setAdminUser();
+        $params = (object) array(
+            'userid' => $user->id,
+            'roleid' => $teacher->id,
+            'cohortid' => $cohort->id
+        );
+        api::create_cohort_role_assignment($params);
+        api::sync_all_cohort_roles();
+
+        $count = $DB->count_records('role_assignments', ['userid' => $user->id, 'component' => 'tool_cohortroles']);
+        $this->assertEquals(3, $count);
+
+        provider::delete_user_role_to_cohort($user->id);
+        $count = $DB->count_records('role_assignments', ['userid' => $user->id, 'component' => 'tool_cohortroles']);
+        $this->assertEquals(0, $count);
+    }
+    /**
+     * Supoort function to get all the localised roles name
+     * in a simple array for testing.
+     *
+     * @return array Array of name of the roles by roleid.
+     */
+    protected static function get_roles_name() {
+        $roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
+        $rolesnames = array();
+        foreach ($roles as $role) {
+            $rolesnames[$role->id] = $role->localname;
+        }
+        return $rolesnames;
+    }
+}
\ No newline at end of file
index 465a9d7..3adbf0b 100644 (file)
@@ -228,27 +228,17 @@ if ($hassiteconfig) {
     $setting->set_force_ltr(true);
     $temp->add($setting);
 
+    // See {@link https://gdpr-info.eu/art-8-gdpr/}.
+    $ageofdigitalconsentmap = implode(PHP_EOL, [
+        '*, 16',
+        'AT, 14',
+        'ES, 14',
+        'US, 13'
+    ]);
     $setting = new admin_setting_agedigitalconsentmap('agedigitalconsentmap',
         new lang_string('ageofdigitalconsentmap', 'admin'),
         new lang_string('ageofdigitalconsentmap_desc', 'admin'),
-        // See {@link https://gdpr-info.eu/art-8-gdpr/}.
-        implode(PHP_EOL, [
-            '*, 16',
-            'AT, 14',
-            'CZ, 13',
-            'DE, 14',
-            'DK, 13',
-            'ES, 13',
-            'FI, 15',
-            'GB, 13',
-            'HU, 14',
-            'IE, 13',
-            'LT, 16',
-            'LU, 16',
-            'NL, 16',
-            'PL, 13',
-            'SE, 13',
-        ]),
+        $ageofdigitalconsentmap,
         PARAM_RAW
     );
     $temp->add($setting);
index 162e89c..42e81f0 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_analytics'; // Full name of the plugin (used for diagnostics).
index 20d06f1..63fd29d 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300;
-$plugin->requires  = 2017110800;
+$plugin->version   = 2018051400;
+$plugin->requires  = 2018050800;
 $plugin->component = 'tool_assignmentupgrade';
-$plugin->dependencies = array('mod_assign' => 2017110800);
+$plugin->dependencies = array('mod_assign' => 2018050800);
index 0ed3384..a30d705 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
 $plugin->component = 'tool_availabilityconditions';
index 8066574..7771e76 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300;   // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2017110800;   // Requires this Moodle version
+$plugin->version   = 2018051400;   // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2018050800;   // Requires this Moodle version
 $plugin->component = 'tool_behat'; // Full name of the plugin (used for diagnostics)
index d8980e4..9cfa8f7 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_capability'; // Full name of the plugin (used for diagnostics).
index 4e7de4a..663c515 100644 (file)
@@ -25,8 +25,8 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_cohortroles'; // Full name of the plugin (used for diagnostics).
 
 $plugin->dependencies = array(
index 4dfe9c2..400e4b9 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300;
-$plugin->requires  = 2017110800;
+$plugin->version   = 2018051400;
+$plugin->requires  = 2018050800;
 $plugin->component = 'tool_customlang'; // Full name of the plugin (used for diagnostics)
index 09583b2..7bffc9f 100644 (file)
@@ -37,6 +37,7 @@ use moodle_url;
 use required_capability_exception;
 use stdClass;
 use tool_dataprivacy\external\data_request_exporter;
+use tool_dataprivacy\local\helper;
 use tool_dataprivacy\task\initiate_data_request_task;
 use tool_dataprivacy\task\process_data_request_task;
 
@@ -186,8 +187,25 @@ class api {
         $datarequest = new data_request();
         // The user the request is being made for.
         $datarequest->set('userid', $foruser);
+
+        $requestinguser = $USER->id;
+        // Check when the user is making a request on behalf of another.
+        if ($requestinguser != $foruser) {
+            if (self::is_site_dpo($requestinguser)) {
+                // The user making the request is a DPO. Should be fine.
+                $datarequest->set('dpo', $requestinguser);
+            } else {
+                // If not a DPO, only users with the capability to make data requests for the user should be allowed.
+                // (e.g. users with the Parent role, etc).
+                if (!api::can_create_data_request_for_user($foruser)) {
+                    $forusercontext = \context_user::instance($foruser);
+                    throw new required_capability_exception($forusercontext,
+                            'tool/dataprivacy:makedatarequestsforchildren', 'nopermissions', '');
+                }
+            }
+        }
         // The user making the request.
-        $datarequest->set('requestedby', $USER->id);
+        $datarequest->set('requestedby', $requestinguser);
         // Set status.
         $datarequest->set('status', self::DATAREQUEST_STATUS_PENDING);
         // Set request type.
@@ -218,16 +236,29 @@ class api {
      * @throws dml_exception
      */
     public static function get_data_requests($userid = 0) {
-        global $USER;
+        global $DB, $USER;
         $results = [];
         $sort = 'status ASC, timemodified ASC';
         if ($userid) {
             // Get the data requests for the user or data requests made by the user.
-            $select = "userid = :userid OR requestedby = :requestedby";
+            $select = "(userid = :userid OR requestedby = :requestedby)";
             $params = [
                 'userid' => $userid,
                 'requestedby' => $userid
             ];
+
+            // Build a list of user IDs that the user is allowed to make data requests for.
+            // Of course, the user should be included in this list.
+            $alloweduserids = [$userid];
+            // Get any users that the user can make data requests for.
+            if ($children = helper::get_children_of_user($userid)) {
+                // Get the list of user IDs of the children and merge to the allowed user IDs.
+                $alloweduserids = array_merge($alloweduserids, array_keys($children));
+            }
+            list($insql, $inparams) = $DB->get_in_or_equal($alloweduserids, SQL_PARAMS_NAMED);
+            $select .= " AND userid $insql";
+            $params = array_merge($params, $inparams);
+
             $results = data_request::get_records_select($select, $params, $sort);
         } else {
             // If the current user is one of the site's Data Protection Officers, then fetch all data requests.
@@ -290,17 +321,19 @@ class api {
      * @param int $requestid The request identifier.
      * @param int $status The request status.
      * @param int $dpoid The user ID of the Data Protection Officer
+     * @param string $comment The comment about the status update.
      * @return bool
      * @throws invalid_persistent_exception
      * @throws coding_exception
      */
-    public static function update_request_status($requestid, $status, $dpoid = 0) {
+    public static function update_request_status($requestid, $status, $dpoid = 0, $comment = '') {
         // Update the request.
         $datarequest = new data_request($requestid);
         $datarequest->set('status', $status);
         if ($dpoid) {
             $datarequest->set('dpo', $dpoid);
         }
+        $datarequest->set('dpocomment', $comment);
         return $datarequest->update();
     }
 
@@ -454,6 +487,19 @@ class api {
         return message_send($message);
     }
 
+    /**
+     * Checks whether a non-DPO user can make a data request for another user.
+     *
+     * @param int $user The user ID of the target user.
+     * @param int $requester The user ID of the user making the request.
+     * @return bool
+     * @throws coding_exception
+     */
+    public static function can_create_data_request_for_user($user, $requester = null) {
+        $usercontext = \context_user::instance($user);
+        return has_capability('tool/dataprivacy:makedatarequestsforchildren', $usercontext, $requester);
+    }
+
     /**
      * Creates a new data purpose.
      *
@@ -478,6 +524,10 @@ class api {
     public static function update_purpose(stdClass $record) {
         self::check_can_manage_data_registry();
 
+        if (!isset($record->sensitivedatareasons)) {
+            $record->sensitivedatareasons = '';
+        }
+
         $purpose = new purpose($record->id);
         $purpose->from_record($record);
 
@@ -765,6 +815,7 @@ class api {
      * @param int $status the status to set the contexts to.
      */
     public static function add_request_contexts_with_status(contextlist_collection $clcollection, int $requestid, int $status) {
+        $request = new data_request($requestid);
         foreach ($clcollection as $contextlist) {
             // Convert the \core_privacy\local\request\contextlist into a contextlist persistent and store it.
             $clp = \tool_dataprivacy\contextlist::from_contextlist($contextlist);
@@ -773,6 +824,12 @@ class api {
 
             // Store the associated contexts in the contextlist.
             foreach ($contextlist->get_contextids() as $contextid) {
+                if ($request->get('type') == static::DATAREQUEST_TYPE_DELETE) {
+                    $context = \context::instance_by_id($contextid);
+                    if (($purpose = static::get_effective_context_purpose($context)) && !empty($purpose->get('protected'))) {
+                        continue;
+                    }
+                }
                 $context = new contextlist_context();
                 $context->set('contextid', $contextid)
                     ->set('contextlistid', $contextlistid)
@@ -869,6 +926,7 @@ class api {
                 }
                 $contexts = [];
             }
+
             $contexts[] = $record->contextid;
             $lastcomponent = $record->component;
         }
index 62d9485..e4b40e8 100644 (file)
@@ -115,12 +115,35 @@ class expired_user_contexts extends \tool_dataprivacy\expired_contexts_manager {
      * @return \context|false
      */
     protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
-        if (!$context = parent::delete_expired_context($privacymanager, $expiredctx)) {
+        $context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
+        if (!$context) {
+            api::delete_expired_context($expiredctx->get('contextid'));
             return false;
         }
 
-        // Delete the user.
+        if (!PHPUNIT_TEST) {
+            mtrace('Deleting context ' . $context->id . ' - ' .
+                shorten_text($context->get_context_name(true, true)));
+        }
+
+        // To ensure that all user data is deleted, instead of deleting by context, we run through and collect any stray
+        // contexts for the user that may still exist and call delete_data_for_user().
         $user = \core_user::get_user($context->instanceid, '*', MUST_EXIST);
+        $approvedlistcollection = new \core_privacy\local\request\contextlist_collection($user->id);
+        $contextlistcollection = $privacymanager->get_contexts_for_userid($user->id);
+
+        foreach ($contextlistcollection as $contextlist) {
+            $approvedlistcollection->add_contextlist(new \core_privacy\local\request\approved_contextlist(
+                $user,
+                $contextlist->get_component(),
+                $contextlist->get_contextids()
+            ));
+        }
+
+        $privacymanager->delete_data_for_user($approvedlistcollection);
+        api::set_expired_context_status($expiredctx, expired_context::STATUS_CLEANED);
+
+        // Delete the user.
         delete_user($user);
 
         return $context;
index 63f8ecb..6587c40 100644 (file)
@@ -96,8 +96,8 @@ class external extends external_api {
         self::validate_context($context);
 
         // Ensure the request exists.
-        $select = 'id = :id AND requestedby = :requestedby';
-        $params = ['id' => $requestid, 'requestedby' => $USER->id];
+        $select = 'id = :id AND (userid = :userid OR requestedby = :requestedby)';
+        $params = ['id' => $requestid, 'userid' => $USER->id, 'requestedby' => $USER->id];
         $requestexists = data_request::record_exists_select($select, $params);
 
         $result = false;
index 707b290..14790b5 100644 (file)
@@ -55,6 +55,7 @@ class context_instance extends \core\form\persistent {
             $subjectscope = get_string('noassignedroles', 'tool_dataprivacy');
         }
         $this->_form->addElement('static', 'subjectscope', get_string('subjectscope', 'tool_dataprivacy'), $subjectscope);
+        $this->_form->addHelpButton('subjectscope', 'subjectscope', 'tool_dataprivacy');
 
         $this->add_purpose_category($this->_customdata['context']->contextlevel);
 
@@ -79,6 +80,7 @@ class context_instance extends \core\form\persistent {
         $addcategory = $mform->createElement('button', 'addcategory', $addcategorytext, ['data-add-element' => 'category']);
         $mform->addElement('group', 'categorygroup', get_string('category', 'tool_dataprivacy'),
             [$categoryselect, $addcategory], null, false);
+        $mform->addHelpButton('categorygroup', 'category', 'tool_dataprivacy');
         $mform->setType('categoryid', PARAM_INT);
         $mform->setDefault('categoryid', 0);
 
@@ -87,12 +89,14 @@ class context_instance extends \core\form\persistent {
         $addpurpose = $mform->createElement('button', 'addpurpose', $addpurposetext, ['data-add-element' => 'purpose']);
         $mform->addElement('group', 'purposegroup', get_string('purpose', 'tool_dataprivacy'),
             [$purposeselect, $addpurpose], null, false);
+        $mform->addHelpButton('purposegroup', 'purpose', 'tool_dataprivacy');
         $mform->setType('purposeid', PARAM_INT);
         $mform->setDefault('purposeid', 0);
 
         if (!empty($this->_customdata['currentretentionperiod'])) {
             $mform->addElement('static', 'retention_current', get_string('retentionperiod', 'tool_dataprivacy'),
                 $this->_customdata['currentretentionperiod']);
+            $mform->addHelpButton('retention_current', 'retentionperiod', 'tool_dataprivacy');
         }
     }
 
index 7476548..4e599c3 100644 (file)
@@ -68,9 +68,11 @@ class defaults extends \moodleform {
             $purposeoptions = data_registry_page::category_options($this->_customdata['purposes'], false, $includeinherit);
 
             $mform->addElement('select', $categoryvar, get_string('category', 'tool_dataprivacy'), $categoryoptions);
+            $mform->addHelpButton($categoryvar, 'categorydefault', 'tool_dataprivacy');
             $mform->setType($categoryvar, PARAM_INT);
 
             $mform->addElement('select', $purposevar, get_string('purpose', 'tool_dataprivacy'), $purposeoptions);
+            $mform->addHelpButton($purposevar, 'purposedefault', 'tool_dataprivacy');
             $mform->setType($purposevar, PARAM_INT);
         }
 
index 77664b8..d7c3436 100644 (file)
@@ -108,4 +108,42 @@ class helper {
                 throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
         }
     }
+
+    /**
+     * Get the users that a user can make data request for.
+     *
+     * E.g. User having a parent role and has the 'tool/dataprivacy:makedatarequestsforchildren' capability.
+     * @param int $userid The user's ID.
+     * @return array
+     */
+    public static function get_children_of_user($userid) {
+        global $DB;
+
+        // Get users that the user has role assignments to.
+        $allusernames = get_all_user_name_fields(true, 'u');
+        $sql = "SELECT u.id, $allusernames
+                  FROM {role_assignments} ra, {context} c, {user} u
+                 WHERE ra.userid = :userid
+                       AND ra.contextid = c.id
+                       AND c.instanceid = u.id
+                       AND c.contextlevel = :contextlevel";
+        $params = [
+            'userid' => $userid,
+            'contextlevel' => CONTEXT_USER
+        ];
+
+        // The final list of users that we will return;
+        $finalresults = [];
+
+        // Our prospective list of users.
+        if ($candidates = $DB->get_records_sql($sql, $params)) {
+            foreach ($candidates as $key => $child) {
+                $childcontext = \context_user::instance($child->id);
+                if (has_capability('tool/dataprivacy:makedatarequestsforchildren', $childcontext, $userid)) {
+                    $finalresults[$key] = $child;
+                }
+            }
+        }
+        return $finalresults;
+    }
 }
index a9457ad..e91844b 100644 (file)
@@ -122,11 +122,15 @@ class metadata_registry {
      * @return array An array of plugin types which contain plugin data.
      */
     protected function get_full_component_list() {
+        global $CFG;
+
         $list = \core_component::get_component_list();
+        $list['core']['core'] = "{$CFG->dirroot}/lib";
         $formattedlist = [];
         foreach ($list as $plugintype => $plugin) {
             $formattedlist[] = ['plugin_type' => $plugintype, 'plugins' => array_keys($plugin)];
         }
+
         return $formattedlist;
     }
 
index b16bfac..cfdad43 100644 (file)
@@ -101,6 +101,10 @@ class data_registry_page implements renderable, templatable {
         $data->actions = $actionmenu->export_for_template($output);
 
         if (!data_registry::defaults_set()) {
+            $data->info = (object)[
+                    'message' => get_string('dataregistryinfo', 'tool_dataprivacy'),
+                    'announce' => 1
+            ];
             $data->nosystemdefaults = (object)[
                 'message' => get_string('nosystemdefaults', 'tool_dataprivacy'),
                 'announce' => 1
@@ -131,7 +135,7 @@ class data_registry_page implements renderable, templatable {
                     'text' => get_string('user'),
                     'contextlevel' => CONTEXT_USER,
                 ], [
-                    'text' => get_string('categories', 'tool_dataprivacy'),
+                    'text' => get_string('categories'),
                     'branches' => $categorybranches,
                     'expandelement' => 'category',
                 ], [
@@ -312,29 +316,23 @@ class data_registry_page implements renderable, templatable {
 
         $branches = [];
 
-        $blockinstances = \core_block_external::get_course_blocks($coursecontext->instanceid);
-        if (empty($blockinstances['blocks'])) {
-            return $branches;
-        }
+        $children = $coursecontext->get_child_contexts();
+        foreach ($children as $childcontext) {
 
-        foreach ($blockinstances['blocks'] as $bi) {
-            if (function_exists('block_instance_by_id')) {
-                $blockinstance = block_instance_by_id($bi['instanceid']);
-            } else {
-                // TODO To be removed when MDL-61621 gets integrated.
-                $blockinstance = $DB->get_record('block_instances', ['id' => $bi['instanceid']]);
-                $blockinstance = block_instance($blockinstance->blockname, $blockinstance);
+            if ($childcontext->contextlevel !== CONTEXT_BLOCK) {
+                continue;
             }
-            $blockcontext = \context_block::instance($bi['instanceid']);
-            $displayname = shorten_text(format_string($blockinstance->get_title(), true, ['context' => $blockcontext->id]));
+
+            $blockinstance = block_instance_by_id($childcontext->instanceid);
+            $displayname = shorten_text(format_string($blockinstance->get_title(), true, ['context' => $childcontext]));
             $branches[] = self::complete([
                 'text' => $displayname,
-                'contextid' => $blockcontext->id,
+                'contextid' => $childcontext->id,
             ]);
+
         }
 
         return $branches;
-
     }
 
     /**
index 572617c..c1b2861 100644 (file)
@@ -73,6 +73,11 @@ class data_requests_page implements renderable, templatable {
         $data->newdatarequesturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php');
         $data->newdatarequesturl->param('manage', true);
 
+        if (!is_https()) {
+            $httpwarningmessage = get_string('httpwarning', 'tool_dataprivacy');
+            $data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
+        }
+
         $requests = [];
         foreach ($this->requests as $request) {
             $requestid = $request->get('id');
index 0e0da54..25229f0 100644 (file)
@@ -67,18 +67,39 @@ class my_data_requests_page implements renderable, templatable {
      * @throws moodle_exception
      */
     public function export_for_template(renderer_base $output) {
+        global $USER;
+
         $data = new stdClass();
         $data->newdatarequesturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php');
 
+        if (!is_https()) {
+            $httpwarningmessage = get_string('httpwarning', 'tool_dataprivacy');
+            $data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
+        }
+
         $requests = [];
         foreach ($this->requests as $request) {
             $requestid = $request->get('id');
             $status = $request->get('status');
             $userid = $request->get('userid');
-            $usercontext = context_user::instance($userid);
-            $requestexporter = new data_request_exporter($request, ['context' => $usercontext]);
+
+            $usercontext = context_user::instance($userid, IGNORE_MISSING);
+            if (!$usercontext) {
+                // Use the context system.
+                $outputcontext = \context_system::instance();
+            } else {
+                $outputcontext = $usercontext;
+            }
+
+            $requestexporter = new data_request_exporter($request, ['context' => $outputcontext]);
             $item = $requestexporter->export($output);
 
+            if ($request->get('userid') != $USER->id) {
+                // Append user name if it differs from $USER.
+                $a = (object)['typename' => $item->typename, 'user' => $item->foruser->fullname];
+                $item->typename = get_string('requesttypeuser', 'tool_dataprivacy', $a);
+            }
+
             $candownload = false;
             $cancancel = true;
             switch ($status) {
@@ -102,7 +123,7 @@ class my_data_requests_page implements renderable, templatable {
                 $canceltext = get_string('cancelrequest', 'tool_dataprivacy');
                 $actions[] = new action_menu_link_secondary($cancelurl, null, $canceltext, $canceldata);
             }
-            if ($candownload) {
+            if ($candownload && $usercontext) {
                 $downloadurl = moodle_url::make_pluginfile_url($usercontext->id, 'tool_dataprivacy', 'export', $requestid, '/',
                         'export.zip', true);
                 $downloadtext = get_string('download', 'tool_dataprivacy');
index 3199777..09bcb1c 100644 (file)
@@ -62,7 +62,8 @@ class renderer extends plugin_renderer_base {
     public function render_contact_dpo_link($replytoemail) {
         $params = [
             'data-action' => 'contactdpo',
-            'data-replytoemail' => $replytoemail
+            'data-replytoemail' => $replytoemail,
+            'class' => 'contactdpo'
         ];
         return html_writer::link('#', get_string('contactdataprotectionofficer', 'tool_dataprivacy'), $params);
     }
index 4e62b74..0cebeb0 100644 (file)
@@ -70,6 +70,27 @@ class initiate_data_request_task extends adhoc_task {
             return;
         }
 
+        $requestedby = $datarequest->get('requestedby');
+        $valid = true;
+        $comment = '';
+        $foruser = $datarequest->get('userid');
+        if ($foruser != $requestedby) {
+            if (!$valid = api::can_create_data_request_for_user($foruser, $requestedby)) {
+                $params = (object)[
+                    'requestedby' => $requestedby,
+                    'userid' => $foruser
+                ];
+                $comment = get_string('errornocapabilitytorequestforothers', 'tool_dataprivacy', $params);
+                mtrace($comment);
+            }
+        }
+        // Reject the request outright if it's invalid.
+        if (!$valid) {
+            $dpo = $datarequest->get('dpo');
+            api::update_request_status($requestid, api::DATAREQUEST_STATUS_REJECTED, $dpo, $comment);
+            return;
+        }
+
         // Update the status of this request as pre-processing.
         mtrace('Generating the contexts containing personal data for the user...');
         api::update_request_status($requestid, api::DATAREQUEST_STATUS_PREPROCESSING);
index 6148f01..589ef01 100644 (file)
@@ -68,8 +68,9 @@ class process_data_request_task extends adhoc_task {
         $request = $requestpersistent->to_record();
 
         // Check if this request still needs to be processed. e.g. The user might have cancelled it before this task has run.
-        if ($request->status != api::DATAREQUEST_STATUS_APPROVED) {
-            mtrace("Request {$request->id} hasn\'t been approved yet or it has already been processed. Skipping...");
+        $status = $requestpersistent->get('status');
+        if (!api::is_active($status)) {
+            mtrace("Request {$requestid} with status {$status} doesn't need to be processed. Skipping...");
             return;
         }
 
@@ -181,23 +182,27 @@ class process_data_request_task extends adhoc_task {
         }
         mtrace('Message sent to user: ' . $messagetextdata['username']);
 
-        // Send to the requester as well. requestedby is 0 if the request was made on behalf of the user by a DPO.
-        if (!empty($request->requestedby) && $foruser->id != $request->requestedby) {
-            $requestedby = core_user::get_user($request->requestedby);
-            $message->userto = $requestedby;
-            $messagetextdata['username'] = fullname($requestedby);
-            // Render message email body.
-            $messagehtml = $output->render_from_template('tool_dataprivacy/data_request_results_email', $messagetextdata);
-            $message->fullmessage = html_to_text($messagehtml);
-            $message->fullmessagehtml = $messagehtml;
-
-            // Send message.
-            if ($emailonly) {
-                email_to_user($requestedby, $dpo, $subject, $message->fullmessage, $messagehtml);
-            } else {
-                message_send($message);
+        // Send to requester as well if this request was made on behalf of another user who's not a DPO,
+        // and has the capability to make data requests for the user (e.g. Parent).
+        if (!api::is_site_dpo($request->requestedby) && $foruser->id != $request->requestedby) {
+            // Ensure the requester has the capability to make data requests for this user.
+            if (api::can_create_data_request_for_user($request->userid, $request->requestedby)) {
+                $requestedby = core_user::get_user($request->requestedby);
+                $message->userto = $requestedby;
+                $messagetextdata['username'] = fullname($requestedby);
+                // Render message email body.
+                $messagehtml = $output->render_from_template('tool_dataprivacy/data_request_results_email', $messagetextdata);
+                $message->fullmessage = html_to_text($messagehtml);
+                $message->fullmessagehtml = $messagehtml;
+
+                // Send message.
+                if ($emailonly) {
+                    email_to_user($requestedby, $dpo, $subject, $message->fullmessage, $messagehtml);
+                } else {
+                    message_send($message);
+                }
+                mtrace('Message sent to requester: ' . $messagetextdata['username']);
             }
-            mtrace('Message sent to requester: ' . $messagetextdata['username']);
         }
 
         if ($request->type == api::DATAREQUEST_TYPE_DELETE) {
index 306461c..680b93a 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use tool_dataprivacy\api;
+use tool_dataprivacy\local\helper;
 
 defined('MOODLE_INTERNAL') || die();
 
@@ -62,27 +63,12 @@ class tool_dataprivacy_data_request_form extends moodleform {
 
         } else {
             // Get users whom you are being a guardian to if your role has the capability to make data requests for children.
-            $allusernames = get_all_user_name_fields(true, 'u');
-            $sql = "SELECT u.id, $allusernames
-                      FROM {role_assignments} ra, {context} c, {user} u
-                     WHERE ra.userid = :userid
-                           AND ra.contextid = c.id
-                           AND c.instanceid = u.id
-                           AND c.contextlevel = :contextlevel";
-            $params = [
-                'userid' => $USER->id,
-                'contextlevel' => CONTEXT_USER
-            ];
-            $children = $DB->get_records_sql($sql, $params);
-
-            if ($children) {
-                $useroptions = [];
-                $useroptions[$USER->id] = fullname($USER);
-                foreach ($children as $child) {
-                    $childcontext = context_user::instance($child->id);
-                    if (has_capability('tool/dataprivacy:makedatarequestsforchildren', $childcontext)) {
-                        $useroptions[$child->id] = fullname($child);
-                    }
+            if ($children = helper::get_children_of_user($USER->id)) {
+                $useroptions = [
+                    $USER->id => fullname($USER)
+                ];
+                foreach ($children as $key => $child) {
+                    $useroptions[$key] = fullname($child);
                 }
                 $mform->addElement('autocomplete', 'userid', get_string('requestfor', 'tool_dataprivacy'), $useroptions);
                 $mform->addRule('userid', null, 'required', null, 'client');
index 4e81f01..8b089c7 100644 (file)
@@ -37,7 +37,10 @@ $string['cancelrequest'] = 'Cancel request';
 $string['cancelrequestconfirmation'] = 'Do you really want cancel this data request?';
 $string['categories'] = 'Categories';
 $string['category'] = 'Category';
+$string['category_help'] = 'A category in the data registry describes a type of data. A new category may be added, or if Inherit is selected, the data category from a higher context is applied. Contexts are (from low to high): Blocks > Activity modules > Courses > Course categories > Site.';
 $string['categorycreated'] = 'Category created';
+$string['categorydefault'] = 'Default category';
+$string['categorydefault_help'] = 'The default category is the data category applied to any new instances. If Inherit is selected, the data category from a higher context is applied. Contexts are (from low to high): Blocks > Activity modules > Courses > Course categories > User > Site.';
 $string['categorieslist'] = 'List of data categories';
 $string['categoryupdated'] = 'Category updated';
 $string['close'] = 'Close';
@@ -46,7 +49,7 @@ $string['confirmapproval'] = 'Do you really want to approve this data request?';
 $string['confirmcontextdeletion'] = 'Do you really want to confirm the deletion of the selected contexts? This will also delete all of the user data for their respective sub-contexts.';
 $string['confirmdenial'] = 'Do you really want deny this data request?';
 $string['contactdataprotectionofficer'] = 'Contact Data Protection Officer';
-$string['contactdataprotectionofficer_desc'] = 'Enabling this feature will provide a link for users to contact the site\'s Data Protection Officer through this site. This link will be shown on their profile page, and on the site\'s privacy policy page, as well. The link leads to a form in which the user can make a data request to the Data Protection Officer.';
+$string['contactdataprotectionofficer_desc'] = 'If enabled, users will be able to contact the Data Protection Officer and make a data request via a link on their profile page.';
 $string['contextlevelname10'] = 'Site';
 $string['contextlevelname30'] = 'Users';
 $string['contextlevelname40'] = 'Course categories';
@@ -54,15 +57,16 @@ $string['contextlevelname50'] = 'Courses';
 $string['contextlevelname70'] = 'Activity modules';
 $string['contextlevelname80'] = 'Blocks';
 $string['contextpurposecategorysaved'] = 'Purpose and category saved.';
-$string['contactdpoviaprivacypolicy'] = 'Please contact the site\'s Data Protection Officer as described in the Privacy Policy';
+$string['contactdpoviaprivacypolicy'] = 'Please contact the Data Protection Officer as described in the privacy policy.';
 $string['createcategory'] = 'Create data category';
 $string['createpurpose'] = 'Create data purpose';
 $string['datadeletion'] = 'Data deletion';
-$string['datadeletionpagehelp'] = 'This page lists the contexts that are already past their retention period and need to be confirmed for user data deletion. Once the selected contexts have been confirmed for deletion, the user data related to these contexts will be deleted on the next execution of the "Delete expired contexts" scheduled task.';
-$string['dataprivacy:makedatarequestsforchildren'] = 'Make data requests for children';
+$string['datadeletionpagehelp'] = 'Data for which the retention period has expired are listed here. Please review and confirm data deletion, which will then be executed by the "Delete expired contexts" scheduled task.';
+$string['dataprivacy:makedatarequestsforchildren'] = 'Make data requests for minors';
 $string['dataprivacy:managedatarequests'] = 'Manage data requests';
 $string['dataprivacy:managedataregistry'] = 'Manage data registry';
 $string['dataregistry'] = 'Data registry';
+$string['dataregistryinfo'] = 'The data registry enables categories (types of data) and purposes (the reasons for processing data) to be set for all content on the site - from users and courses down to activities and blocks. For each purpose, a retention period may be set. When a retention period has expired, the data is flagged and listed for deletion, awaiting admin confirmation.';
 $string['datarequestcreatedforuser'] = 'Data request created for {$a}';
 $string['datarequestemailsubject'] = 'Data request: {$a}';
 $string['datarequests'] = 'Data requests';
@@ -79,7 +83,7 @@ $string['deny'] = 'Deny';
 $string['denyrequest'] = 'Deny request';
 $string['download'] = 'Download';
 $string['dporolemapping'] = 'Data Protection Officer role mapping';
-$string['dporolemapping_desc'] = 'Select one or more roles that map to the Data Protection Officer role. Users with these roles will be able to manage data requests. This requires the selected role(s) to have the capability \'tool/dataprivacy:managedatarequests\'';
+$string['dporolemapping_desc'] = 'The Data Protection Officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a Data Protection Officer role mapping option.';
 $string['editcategories'] = 'Edit categories';
 $string['editcategory'] = 'Edit category';
 $string['editcategories'] = 'Edit categories';
@@ -90,6 +94,7 @@ $string['effectiveretentionperioduser'] = '{$a} (since the last time the user ac
 $string['emailsalutation'] = 'Dear {$a},';
 $string['errorinvalidrequeststatus'] = 'Invalid request status!';
 $string['errorinvalidrequesttype'] = 'Invalid request type!';
+$string['errornocapabilitytorequestforothers'] = 'User {$a->requestedby} doesn\'t have the capability to make a data request on behalf of user {$a->userid}';
 $string['errornoexpiredcontexts'] = 'There are no expired contexts to process';
 $string['errorcontexthasunexpiredchildren'] = 'The context "{$a}" still has sub-contexts that have not yet expired. No contexts have been flagged for deletion.';
 $string['errorrequestalreadyexists'] = 'You already have an ongoing request.';
@@ -137,6 +142,7 @@ $string['gdpr_art_9_2_i_name'] = 'Public health (GDPR Art. 9.2(i))';
 $string['gdpr_art_9_2_j_description'] = 'Processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject';
 $string['gdpr_art_9_2_j_name'] = 'Public interest, or scientific/historical/statistical research (GDPR Art. 9.2(j))';
 $string['hide'] = 'Collapse all';
+$string['httpwarning'] = 'Any data downloaded from this site may not be encrypted. Please contact your system administrator and request that they install SSL on this site.';
 $string['inherit'] = 'Inherit';
 $string['lawfulbases'] = 'Lawful bases';
 $string['lawfulbases_help'] = 'Select at least one option that will serve as the lawful basis for processing personal data. For details on these lawful bases, please see <a href="https://gdpr-info.eu/art-6-gdpr/" target="_blank">GDPR Art. 6.1</a>';
@@ -156,7 +162,7 @@ $string['noassignedroles'] = 'No assigned roles in this context';
 $string['noblockstoload'] = 'No blocks';
 $string['nocategories'] = 'There are no categories yet';
 $string['nocoursestoload'] = 'No activities';
-$string['noexpiredcontexts'] = 'There are no expired contexts in this level that need to be confirmed for deletion.';
+$string['noexpiredcontexts'] = 'This context level has no data for which the retention period has expired.';
 $string['nopersonaldatarequests'] = 'You don\'t have any personal data requests';
 $string['nopurposes'] = 'There are no purposes yet';
 $string['nosubjectaccessrequests'] = 'There are no data requests that you need to act on';
@@ -174,7 +180,10 @@ $string['privacy:metadata:request:timecreated'] = 'The timestamp indicating when
 $string['protected'] = 'Protected';
 $string['protectedlabel'] = 'The retention of this data has a higher legal precedent over a user\'s request to be forgotten. This data will only be deleted after the retention period has expired.';
 $string['purpose'] = 'Purpose';
+$string['purpose_help'] = 'The purpose describes the reason for processing the data. A new purpose may be added, or if Inherit is selected, the purpose from a higher context is applied. Contexts are (from low to high): Blocks > Activity modules > Courses > Course categories > User > Site.';
 $string['purposecreated'] = 'Purpose created';
+$string['purposedefault'] = 'Default purpose';
+$string['purposedefault_help'] = 'The default purpose is the purpose which is applied to any new instances. If Inherit is selected, the purpose from a higher context is applied. Contexts are (from low to high): Blocks > Activity modules > Courses > Course categories > User > Site.';
 $string['purposes'] = 'Purposes';
 $string['purposeslist'] = 'List of data purposes';
 $string['purposeupdated'] = 'Purpose updated';
@@ -182,12 +191,13 @@ $string['replyto'] = 'Reply to';
 $string['requestactions'] = 'Actions';
 $string['requestby'] = 'Requested by';
 $string['requestcomments'] = 'Comments';
-$string['requestcomments_help'] = 'Please feel free to provide more details about your request';
+$string['requestcomments_help'] = 'This box enables you to enter any further details about your data request.';
 $string['requestemailintro'] = 'You have received a data request:';
 $string['requestfor'] = 'Requesting for';
 $string['requeststatus'] = 'Status';
 $string['requestsubmitted'] = 'Your request has been submitted to the site\'s Data Protection Officer';
 $string['requesttype'] = 'Type';
+$string['requesttypeuser'] = '{$a->typename} ({$a->user})';
 $string['requesttype_help'] = 'Select the reason why you would like to contact the site\'s Data Protection Officer';
 $string['requesttypedelete'] = 'Delete all of my personal data';
 $string['requesttypedeleteshort'] = 'Delete';
@@ -201,6 +211,7 @@ $string['resultdeleted'] = 'You recently requested to have your account and pers
 $string['resultdownloadready'] = 'Your copy of your personal data in {$a} that you recently requested is now available for download. Please click on the link below to go to the download page.';
 $string['reviewdata'] = 'Review data';
 $string['retentionperiod'] = 'Retention period';
+$string['retentionperiod_help'] = 'The retention period specifies the length of time that data should be kept for. When the retention period has expired, the data is flagged and listed for deletion, awaiting admin confirmation.';
 $string['retentionperiodnotdefined'] = 'No retention period was defined';
 $string['retentionperiodzero'] = 'No retention period';
 $string['send'] = 'Send';
@@ -217,6 +228,7 @@ $string['statusprocessing'] = 'Processing';
 $string['statuspending'] = 'Pending';
 $string['statusrejected'] = 'Rejected';
 $string['subjectscope'] = 'Subject scope';
+$string['subjectscope_help'] = 'The subject scope lists the roles which may be assigned in this context.';
 $string['user'] = 'User';
 $string['viewrequest'] = 'View the request';
 $string['visible'] = 'Expand all';
index bff49a7..a351052 100644 (file)
@@ -1,7 +1,9 @@
 .nav-pills .nav-pills {
     margin-left: 1rem;
 }
-
+.data-registry > .top-nav > * {
+    margin-right: 0.5rem;
+}
 /*Extra attribute selection to have preference over bs2's .moodle-actionmenu[data-enhance] */
 .data-registry > .top-nav > .singlebutton,
 .data-registry > .top-nav > .moodle-actionmenu[data-owner='dataregistry-actions'] {
     height: 70vh;
     overflow-y: scroll;
 }
+
+dd a.contactdpo {
+    /* Reverting dd's left margin */
+    margin-left: -10px;
+}
+
+.card dd a.contactdpo {
+    /* Reverting dd's left margin */
+    margin-left: inherit;
+}
index 886bf45..fa84f14 100644 (file)
@@ -36,7 +36,7 @@
     }
 }}
 <div class="container">
-    <div class="row">
+    <div class="row m-b-2">
         <label class="col-md-3 span3 col-form-label">{{#str}}replyto, tool_dataprivacy{{/str}}</label>
         <div class="col-md-9 span9 col-form-label">{{replytoemail}}</div>
     </div>
index 203510b..fb86ae9 100644 (file)
@@ -33,7 +33,7 @@
     }
 }}
 <div class="data-registry">
-    <div class="top-nav">
+    <div class="top-nav d-flex">
         {{#defaultsbutton}}
             {{> core/action_link}}
         {{/defaultsbutton}}
             {{> core/action_menu}}
         {{/actions}}
     </div>
+    {{#info}}
+        <div class="m-t-1">
+            {{> core/notification_info}}
+        </div>
+    {{/info}}
     {{#nosystemdefaults}}
         <div class="m-t-1">
             {{> core/notification_warning}}
index db2f2ae..beed5ac 100644 (file)
     }
 }}
 
+{{#httpsite}}
+    {{> core/notification_warning}}
+{{/httpsite}}
+
 <div data-region="datarequests">
     <div class="m-t-1 m-b-1">
         <a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
index c43ea97..1bdcbdb 100644 (file)
     }
 }}
 
+{{#httpsite}}
+    {{> core/notification_warning}}
+{{/httpsite}}
+
 <div data-region="datarequests">
     <div class="m-t-1 m-b-1">
         <a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
index ebdc10f..6e6c855 100644 (file)
@@ -67,7 +67,7 @@
     <table class="generaltable fullwidth">
         <caption class="accesshide">{{#str}}purposeslist, tool_dataprivacy{{/str}}</caption>
         <thead>
-            <tr style="display: flex;">
+            <tr>
                 <th scope="col" class="col-md-2">{{#str}}name{{/str}}</th>
                 <th scope="col" class="col-md-3">{{#str}}description{{/str}}</th>
                 <th scope="col" class="col-md-2">{{#str}}lawfulbases, tool_dataprivacy{{/str}}</th>
index 62d29c1..6e06478 100644 (file)
@@ -57,6 +57,7 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
     public function test_update_request_status() {
         $generator = new testing_data_generator();
         $s1 = $generator->create_user();
+        $this->setUser($s1);
 
         // Create the sample data request.
         $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT);
@@ -145,6 +146,7 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         set_config('dporoles', $managerroleid, 'tool_dataprivacy');
 
         // Create the sample data request.
+        $this->setUser($s1);
         $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT);
         $requestid = $datarequest->get('id');
 
@@ -186,6 +188,7 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         set_config('dporoles', $managerroleid, 'tool_dataprivacy');
 
         // Create the sample data request.
+        $this->setUser($s1);
         $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT);
         $requestid = $datarequest->get('id');
 
@@ -203,6 +206,7 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         $teacher = $generator->create_user();
 
         // Create the sample data request.
+        $this->setUser($student);
         $datarequest = api::create_data_request($student->id, api::DATAREQUEST_TYPE_EXPORT);
 
         $requestid = $datarequest->get('id');
@@ -286,6 +290,71 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
         $this->assertEquals($user->id, $datarequest->get('userid'));
         $this->assertEquals($user->id, $datarequest->get('requestedby'));
+        $this->assertEquals(0, $datarequest->get('dpo'));
+        $this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $datarequest->get('type'));
+        $this->assertEquals(api::DATAREQUEST_STATUS_PENDING, $datarequest->get('status'));
+        $this->assertEquals($comment, $datarequest->get('comments'));
+
+        // Test adhoc task creation.
+        $adhoctasks = manager::get_adhoc_tasks(initiate_data_request_task::class);
+        $this->assertCount(1, $adhoctasks);
+    }
+
+    /**
+     * Test for api::create_data_request() made by DPO.
+     */
+    public function test_create_data_request_by_dpo() {
+        global $USER;
+
+        $generator = new testing_data_generator();
+        $user = $generator->create_user();
+        $comment = 'sample comment';
+
+        // Login as DPO (Admin is DPO by default).
+        $this->setAdminUser();
+
+        // Test data request creation.
+        $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
+        $this->assertEquals($user->id, $datarequest->get('userid'));
+        $this->assertEquals($USER->id, $datarequest->get('requestedby'));
+        $this->assertEquals($USER->id, $datarequest->get('dpo'));
+        $this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $datarequest->get('type'));
+        $this->assertEquals(api::DATAREQUEST_STATUS_PENDING, $datarequest->get('status'));
+        $this->assertEquals($comment, $datarequest->get('comments'));
+
+        // Test adhoc task creation.
+        $adhoctasks = manager::get_adhoc_tasks(initiate_data_request_task::class);
+        $this->assertCount(1, $adhoctasks);
+    }
+
+    /**
+     * Test for api::create_data_request() made by a parent.
+     */
+    public function test_create_data_request_by_parent() {
+        global $DB;
+
+        $generator = new testing_data_generator();
+        $user = $generator->create_user();
+        $parent = $generator->create_user();
+        $comment = 'sample comment';
+
+        // Get the teacher role pretend it's the parent roles ;).
+        $systemcontext = context_system::instance();
+        $usercontext = context_user::instance($user->id);
+        $parentroleid = $DB->get_field('role', 'id', array('shortname' => 'teacher'));
+        // Give the manager role with the capability to manage data requests.
+        assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentroleid, $systemcontext->id, true);
+        // Assign the parent to user.
+        role_assign($parentroleid, $parent->id, $usercontext->id);
+
+        // Login as the user's parent.
+        $this->setUser($parent);
+
+        // Test data request creation.
+        $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
+        $this->assertEquals($user->id, $datarequest->get('userid'));
+        $this->assertEquals($parent->id, $datarequest->get('requestedby'));
+        $this->assertEquals(0, $datarequest->get('dpo'));
         $this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $datarequest->get('type'));
         $this->assertEquals(api::DATAREQUEST_STATUS_PENDING, $datarequest->get('status'));
         $this->assertEquals($comment, $datarequest->get('comments'));
@@ -351,8 +420,10 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         $comment = 'sample comment';
 
         // Make a data request as user 1.
+        $this->setUser($user1);
         $d1 = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
         // Make a data request as user 2.
+        $this->setUser($user2);
         $d2 = api::create_data_request($user2->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 
         // Fetching data requests of specific users.
@@ -406,6 +477,7 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         $user1 = $generator->create_user();
 
         // Make a data request as user 1.
+        $this->setUser($user1);
         $request = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT);
         // Set the status.
         api::update_request_status($request->get('id'), $status);
@@ -1073,4 +1145,126 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
             [$module1, $module2]
         ];
     }
+
+    /**
+     * Test that delete requests filter out protected purpose contexts.
+     */
+    public function test_add_request_contexts_with_status_delete() {
+        $data = $this->setup_test_add_request_contexts_with_status(api::DATAREQUEST_TYPE_DELETE);
+        $contextids = $data->list->get_contextids();
+
+        $this->assertCount(1, $contextids);
+        $this->assertEquals($data->contexts->unprotected, $contextids);
+    }
+
+    /**
+     * Test that export requests don't filter out protected purpose contexts.
+     */
+    public function test_add_request_contexts_with_status_export() {
+        $data = $this->setup_test_add_request_contexts_with_status(api::DATAREQUEST_TYPE_EXPORT);
+        $contextids = $data->list->get_contextids();
+
+        $this->assertCount(2, $contextids);
+        $this->assertEquals($data->contexts->used, $contextids, '', 0.0, 10, true);
+    }
+
+    /**
+     * Perform setup for the test_add_request_contexts_with_status_xxxxx tests.
+     *
+     * @param       int $type The type of request to create
+     * @return      \stdClass
+     */
+    protected function setup_test_add_request_contexts_with_status($type) {
+        $this->setAdminUser();
+
+        // User under test.
+        $s1 = $this->getDataGenerator()->create_user();
+
+        // Create three sample contexts.
+        // 1 which should not be returned; and
+        // 1 which will be returned and is not protected; and
+        // 1 which will be returned and is protected.
+
+        $c1 = $this->getDataGenerator()->create_course();
+        $c2 = $this->getDataGenerator()->create_course();
+        $c3 = $this->getDataGenerator()->create_course();
+
+        $ctx1 = \context_course::instance($c1->id);
+        $ctx2 = \context_course::instance($c2->id);
+        $ctx3 = \context_course::instance($c3->id);
+
+        $unprotected = api::create_purpose((object)[
+            'name' => 'Unprotected', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a']);
+        $protected = api::create_purpose((object) [
+            'name' => 'Protected', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a', 'protected' => true]);
+
+        $cat1 = api::create_category((object)['name' => 'a']);
+
+        // Set the defaults.
+        list($purposevar, $categoryvar) = data_registry::var_names_from_context(
+            \context_helper::get_class_for_level(CONTEXT_SYSTEM)
+        );
+        set_config($purposevar, $unprotected->get('id'), 'tool_dataprivacy');
+        set_config($categoryvar, $cat1->get('id'), 'tool_dataprivacy');
+
+        $contextinstance1 = api::set_context_instance((object) [
+                'contextid' => $ctx1->id,
+                'purposeid' => $unprotected->get('id'),
+                'categoryid' => $cat1->get('id'),
+            ]);
+
+        $contextinstance2 = api::set_context_instance((object) [
+                'contextid' => $ctx2->id,
+                'purposeid' => $unprotected->get('id'),
+                'categoryid' => $cat1->get('id'),
+            ]);
+
+        $contextinstance3 = api::set_context_instance((object) [
+                'contextid' => $ctx3->id,
+                'purposeid' => $protected->get('id'),
+                'categoryid' => $cat1->get('id'),
+            ]);
+
+        $collection = new \core_privacy\local\request\contextlist_collection($s1->id);
+        $contextlist = new \core_privacy\local\request\contextlist();
+        $contextlist->set_component('tool_dataprivacy');
+        $contextlist->add_from_sql('SELECT id FROM {context} WHERE id IN(:ctx2, :ctx3)', [
+                'ctx2' => $ctx2->id,
+                'ctx3' => $ctx3->id,
+            ]);
+
+        $collection->add_contextlist($contextlist);
+
+        // Create the sample data request.
+        $datarequest = api::create_data_request($s1->id, $type);
+        $requestid = $datarequest->get('id');
+
+        // Add the full collection with contexts 2, and 3.
+        api::add_request_contexts_with_status($collection, $requestid, \tool_dataprivacy\contextlist_context::STATUS_PENDING);
+
+        // Mark it as approved.
+        api::update_request_contexts_with_status($requestid, \tool_dataprivacy\contextlist_context::STATUS_APPROVED);
+
+        // Fetch the list.
+        $approvedcollection = api::get_approved_contextlist_collection_for_request($datarequest);
+
+        return (object) [
+            'contexts' => (object) [
+                'unused' => [
+                    $ctx1->id,
+                ],
+                'used' => [
+                    $ctx2->id,
+                    $ctx3->id,
+                ],
+                'unprotected' => [
+                    $ctx2->id,
+                ],
+                'protected' => [
+                    $ctx3->id,
+                ],
+            ],
+            'list' => $approvedcollection->get_contextlist_for_component('tool_dataprivacy'),
+        ];
+    }
 }
index 7e62cc4..1e434a7 100644 (file)
@@ -84,6 +84,22 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
         $this->getDataGenerator()->enrol_user($user3->id, $course2->id, 'student');
         $this->getDataGenerator()->enrol_user($user4->id, $course3->id, 'student');
 
+        // Add an activity and some data for user 2.
+        $assignmod = $this->getDataGenerator()->create_module('assign', ['course' => $course2->id]);
+        $data = (object) [
+            'assignment' => $assignmod->id,
+            'userid' => $user2->id,
+            'timecreated' => time(),
+            'timemodified' => time(),
+            'status' => 'new',
+            'groupid' => 0,
+            'attemptnumber' => 0,
+            'latest' => 1,
+        ];
+        $DB->insert_record('assign_submission', $data);
+        // We should have one record in the assign submission table.
+        $this->assertEquals(1, $DB->count_records('assign_submission'));
+
         // Users without lastaccess are skipped as well as users enroled in courses with no end date.
         $expired = new \tool_dataprivacy\expired_user_contexts();
         $numexpired = $expired->flag_expired();
@@ -109,6 +125,9 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
         $deleted = $expired->delete();
         $this->assertEquals(0, $deleted);
 
+        // No user data left in mod_assign.
+        $this->assertEquals(0, $DB->count_records('assign_submission'));
+
         // The user is deleted.
         $deleteduser = \core_user::get_user($user2->id, 'id, deleted', IGNORE_MISSING);
         $this->assertEquals(1, $deleteduser->deleted);
index 21f067b..2fc88a4 100644 (file)
@@ -72,8 +72,9 @@ class tool_dataprivacy_metadata_registry_testcase extends advanced_testcase {
         }
 
         // Let's check core subsystems.
+        // The Privacy API adds an extra component in the form of 'core'.
         $coresubsystems = \core_component::get_core_subsystems();
-        $this->assertEquals(count($coresubsystems), count($data['core']));
+        $this->assertEquals(count($coresubsystems) + 1, count($data['core']));
     }
 
     /**
index 9d73fe1..3561d57 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2018040500;
-$plugin->requires  = 2018040500;        // Moodle 3.5dev (Build 2018031600) and upwards.
+$plugin->version   = 2018051400;
+$plugin->requires  = 2018050800;        // Moodle 3.5dev (Build 2018031600) and upwards.
 $plugin->component = 'tool_dataprivacy';
index 26baf54..9fa2f1e 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_dbtransfer'; // Full name of the plugin (used for diagnostics).
index 8910e8d..550536d 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
 $plugin->component = 'tool_filetypes';
index e27c676..4049283 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
 $plugin->component = 'tool_generator';
index c2e4db6..a4ef47c 100644 (file)
@@ -25,8 +25,8 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2017110800; // Requires this Moodle version
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2018050800; // Requires this Moodle version
 $plugin->component = 'tool_health'; // Full name of the plugin (used for diagnostics)
 
 $plugin->maturity  = MATURITY_ALPHA; // this version's maturity level
index f0a1df0..4a2e8cf 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_httpsreplace'; // Full name of the plugin (used for diagnostics).
index 5e80b83..cbc2912 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2017110800; // Requires this Moodle version
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2018050800; // Requires this Moodle version
 $plugin->component = 'tool_innodb'; // Full name of the plugin (used for diagnostics)
index ff1f7ea..b0ba3ac 100644 (file)
@@ -24,6 +24,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component  = 'tool_installaddon';
-$plugin->version    = 2017111300;
-$plugin->requires   = 2017110800;
+$plugin->version    = 2018051400;
+$plugin->requires   = 2018050800;
 $plugin->maturity   = MATURITY_STABLE;
index fafa6ed..c34440f 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2017110800; // Requires this Moodle version
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2018050800; // Requires this Moodle version
 $plugin->component = 'tool_langimport'; // Full name of the plugin (used for diagnostics)
index 27596c0..f3841d4 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
 $plugin->component = 'logstore_database'; // Full name of the plugin (used for diagnostics).
index 8eb6ff7..8d5b532 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
 $plugin->component = 'logstore_legacy'; // Full name of the plugin (used for diagnostics).
index 1994e29..2761ca2 100644 (file)
@@ -74,10 +74,8 @@ class provider implements
      */
     public static function add_contexts_for_userid(contextlist $contextlist, $userid) {
         $sql = "
-            SELECT ctx.id
-              FROM {context} ctx
-              JOIN {logstore_standard_log} l
-                ON l.contextid = ctx.id
+            SELECT l.contextid
+              FROM {logstore_standard_log} l
              WHERE l.userid = :userid1
                 OR l.relateduserid = :userid2
                 OR l.realuserid = :userid3";
index dc08ba9..b509968 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
 $plugin->component = 'logstore_standard'; // Full name of the plugin (used for diagnostics).
index 041c70e..b5a7d90 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_log'; // Full name of the plugin (used for diagnostics).
index 7477548..4436def 100644 (file)
@@ -25,6 +25,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_lp'; // Full name of the plugin (used for diagnostics).
index 1cae087..47fc25c 100644 (file)
@@ -25,8 +25,8 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_lpimportcsv'; // Full name of the plugin (used for diagnostics).
-$plugin->dependencies = array('tool_lp' => 2017110800);
+$plugin->dependencies = array('tool_lp' => 2018050800);
 
index a293e26..7ef6bd0 100644 (file)
@@ -24,8 +24,8 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_lpmigrate'; // Full name of the plugin (used for diagnostics).
 $plugin->dependencies = array(
     'tool_lp' => ANY_VERSION
index 0bf7da9..a13983f 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300;
-$plugin->requires  = 2017110800;
+$plugin->version   = 2018051400;
+$plugin->requires  = 2018050800;
 $plugin->component = 'tool_messageinbound';
index 438a6d5..2457ecf 100644 (file)
@@ -23,9 +23,9 @@
  */
 
 defined('MOODLE_INTERNAL') || die();
-$plugin->version   = 2017111301; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
 $plugin->dependencies = array(
-    'webservice_rest' => 2017110800
+    'webservice_rest' => 2018050800
 );
index 9b481d3..b2174ef 100644 (file)
@@ -91,8 +91,10 @@ class provider implements \core_privacy\local\metadata\provider, \core_privacy\l
         $sql = "SELECT DISTINCT ctx.id
                   FROM {context} ctx
              LEFT JOIN {tool_monitor_rules} mr ON ctx.instanceid = mr.userid AND ctx.contextlevel = :contextuserrule
+                       AND mr.userid = :useridsubscriptions
              LEFT JOIN {tool_monitor_subscriptions} ms ON ctx.instanceid = ms.userid AND ctx.contextlevel = :contextusersub
-                 WHERE (ms.userid = :useridrules OR mr.userid = :useridsubscriptions)";
+                       AND ms.userid = :useridrules
+                 WHERE ms.id IS NOT NULL OR mr.id IS NOT NULL";
 
         $contextlist = new contextlist();
         $contextlist->add_from_sql($sql, $params);
index c5239d8..5601b72 100644 (file)
@@ -26,6 +26,6 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2017111300;     // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800;     // Requires this Moodle version.
+$plugin->version   = 2018051400;     // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800;     // Requires this Moodle version.
 $plugin->component = 'tool_monitor'; // Full name of the plugin (used for diagnostics).
index 09000e0..6efabad 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2017110800; // Requires this Moodle version
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2018050800; // Requires this Moodle version
 $plugin->component = 'tool_multilangupgrade'; // Full name of the plugin (used for diagnostics)
 
index fe05f0e..4f78e33 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires  = 2017110800; // Requires this Moodle version.
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2018050800; // Requires this Moodle version.
 $plugin->component = 'tool_oauth2'; // Full name of the plugin (used for diagnostics).
 
diff --git a/admin/tool/phpunit/classes/privacy/provider.php b/admin/tool/phpunit/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..ab9c4a8
--- /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_phpunit.
+ *
+ * @package    tool_phpunit
+ * @copyright  2018 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_phpunit\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_phpunit implementing null_provider.
+ *
+ * @copyright  2018 Mark Nelson <markn@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';
+    }
+}
index b0741a2..dbc7baf 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2017110800; // Requires this Moodle version
+$plugin->version   = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2018050800; // Requires this Moodle version
 $plugin->component = 'tool_phpunit'; // Full name of the plugin (used for diagnostics)
 
index 6fb5330..5ede161 100644 (file)
@@ -278,9 +278,7 @@ class page_agreedocs implements renderable, templatable {
         // and $SESSION->wantsurl is defined, redirect to the return page.
         $hasagreedsignupuser = empty($USER->id) && $this->signupuserpolicyagreed;
         $hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
-        $canrevoke = api::can_revoke_policies($USER->id);
-        if (!is_siteadmin() && ($hasagreedsignupuser ||
-            ($hasagreedloggeduser && !$canrevoke))) {
+        if (!is_siteadmin() && ($hasagreedsignupuser || $hasagreedloggeduser)) {
             $this->redirect_to_previous_url();
         }
 
@@ -295,7 +293,6 @@ class page_agreedocs implements renderable, templatable {
 
         // Page setup.
         $PAGE->set_context(context_system::instance());
-        $PAGE->set_pagelayout('standard');
         $PAGE->set_url($myurl);
         $PAGE->set_heading($SITE->fullname);
         $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
index b16a4e7..95613a7 100644 (file)
@@ -155,12 +155,13 @@ class page_viewdoc implements renderable, templatable {
         $data = (object) [
             'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
             'returnurl' => $this->returnurl ? (new moodle_url($this->returnurl))->out(false) : null,
-            'editurl' => ($this->manage && $this->policy->status != policy_version::STATUS_ARCHIVED) ?
-                (new moodle_url('/admin/tool/policy/editpolicydoc.php',
-                ['policyid' => $this->policy->policyid, 'versionid' => $this->policy->id]))->out(false) : null,
             'numpolicy' => $this->numpolicy ? : null,
             'totalpolicies' => $this->totalpolicies ? : null,
         ];
+        if ($this->manage && $this->policy->status != policy_version::STATUS_ARCHIVED) {
+            $paramsurl = ['policyid' => $this->policy->policyid, 'versionid' => $this->policy->id];
+            $data->editurl = (new moodle_url('/admin/tool/policy/editpolicydoc.php', $paramsurl))->out(false);
+        }
 
         $data->policy = clone($this->policy);
 
index c420c5f..4be8554 100644 (file)
@@ -41,6 +41,7 @@ $agreedocs = optional_param_array('agreedoc', null, PARAM_INT);
 $behalfid = optional_param('userid', null, PARAM_INT);
 
 $PAGE->set_context(context_system::instance());
+$PAGE->set_pagelayout('standard');
 $PAGE->set_url('/admin/tool/policy/index.php');
 $PAGE->set_popup_notification_allowed(false);
 
index 32be8f9..efd944d 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['acceptanceacknowledgement'] = 'I acknowledge that I have received the user\'s request to consent on the abovementioned policy on behalf of the user.';
+$string['acceptanceacknowledgement'] = 'I acknowledge that I have received a request to give consent on behalf of user(s).';
 $string['acceptancecount'] = '{$a->agreedcount} of {$a->policiescount}';
 $string['acceptancenote'] = 'Remarks';
 $string['acceptancepolicies'] = 'Policies';
-$string['acceptancessavedsucessfully'] = 'The agreements has been saved successfully.';
+$string['acceptancessavedsucessfully'] = 'The agreements have been saved successfully.';
 $string['acceptancestatusoverall'] = 'Overall';
 $string['acceptanceusers'] = 'Users';
 $string['actions'] = 'Actions';
 $string['activate'] = 'Set status to "Active"';
 $string['activating'] = 'Activating a policy';
-$string['activateconfirm'] = '<p>You are about to activate policy <em>\'{$a->name}\'</em> and make the version <em>\'{$a->revision}\'</em> the current one.</p><p>All users will be required to accept this new policy version to be able to use the site.</p>';
+$string['activateconfirm'] = '<p>You are about to activate policy <em>\'{$a->name}\'</em> and make the version <em>\'{$a->revision}\'</em> the current one.</p><p>All users will be required to agree to this new policy version to be able to use the site.</p>';
 $string['activateconfirmyes'] = 'Activate';
 $string['agreed'] = 'Agreed';
 $string['agreedby'] = 'Agreed by';
-$string['agreedno'] = 'Not agreed';
-$string['agreednowithlink'] = 'Not agreed, click to agree to "{$a}"';
-$string['agreednowithlinkall'] = 'Not agreed, click to agree to all policies';
+$string['agreedno'] = 'Consent not given';
+$string['agreednowithlink'] = 'Consent not given; click to give consent on behalf of user for {$a}';
+$string['agreednowithlinkall'] = 'Consent not given; click to give consent on behalf of user for all policies';
 $string['agreedon'] = 'Agreed on';
 $string['agreedyes'] = 'Agreed';
-$string['agreedyesonbehalf'] = 'Agreed on behalf of';
-$string['agreedyesonbehalfwithlink'] = 'Agreed on behalf of, click to withdraw consent to "{$a}"';
-$string['agreedyesonbehalfwithlinkall'] = 'Agreed on behalf of, click to withdraw consent to all policies"';
-$string['agreedyeswithlink'] = 'Agreed, click to withdraw consent to "{$a}"';
-$string['agreedyeswithlinkall'] = 'Agreed, click to withdraw consent to all policies';
+$string['agreedyesonbehalf'] = 'Consent given on behalf of user';
+$string['agreedyesonbehalfwithlink'] = 'Consent given on behalf of user; click to withdraw user consent for {$a}';
+$string['agreedyesonbehalfwithlinkall'] = 'Consent given on behalf of user; click to withdraw user consent for all policies';
+$string['agreedyeswithlink'] = 'Consent given; click to withdraw user consent for {$a}';
+$string['agreedyeswithlinkall'] = 'Consent given; click to withdraw user consent for all policies';
 $string['agreepolicies'] = 'Please agree to the following policies';
 $string['backtotop'] = 'Back to top';
 $string['consentbulk'] = 'Consent';
-$string['consentdetails'] = 'Agree on behalf of the user';
+$string['consentdetails'] = 'Give consent on behalf of user';
 $string['consentpagetitle'] = 'Consent';
 $string['contactdpo'] = 'For questions regarding the policies please contact the Data Protection Officer.';
 $string['dataproc'] = 'Personal data processing';
@@ -61,7 +61,7 @@ $string['deleteconfirm'] = '<p>Are you sure you want to delete policy <em>\'{$a-
 $string['editingpolicydocument'] = 'Editing policy';
 $string['errorpolicyversionnotfound'] = 'There isn\'t any policy version with this identifier.';
 $string['errorsaveasdraft'] = 'Minor change can not be saved as draft';
-$string['errorusercantviewpolicyversion'] = 'The user hasn\'t access to this policy version.';
+$string['errorusercantviewpolicyversion'] = 'The user doesn\'t have access to this policy version.';
 $string['event_acceptance_created'] = 'User policy agreement created';
 $string['event_acceptance_updated'] = 'User policy agreement updated';
 $string['filtercapabilityno'] = 'Permission: Can not agree';
@@ -77,15 +77,15 @@ $string['filterpolicy'] = 'Policy: {$a}';
 $string['guestconsent:continue'] = 'Continue';
 $string['guestconsentmessage'] = 'If you continue browsing this website, you agree to our policies:';
 $string['iagree'] = 'I agree to the {$a}';
-$string['iagreetothepolicy'] = 'Agree';
+$string['iagreetothepolicy'] = 'Give consent on behalf of user';
 $string['inactivate'] = 'Set status to "Inactive"';
 $string['inactivating'] = 'Inactivating a policy';
-$string['inactivatingconfirm'] = '<p>You are about to inactivate policy <em>\'{$a->name}\'</em> version <em>\'{$a->revision}\'</em>.</p><p>The policy will not apply until some version is made the current one.</p>';
+$string['inactivatingconfirm'] = '<p>You are about to inactivate policy <em>\'{$a->name}\'</em> version <em>\'{$a->revision}\'</em>.</p>';
 $string['inactivatingconfirmyes'] = 'Inactivate';
 $string['invalidversionid'] = 'There is no policy with this identifier!';
-$string['irevokethepolicy'] = 'Withdraw consent';
+$string['irevokethepolicy'] = 'Withdraw user consent';
 $string['minorchange'] = 'Minor change';
-$string['minorchangeinfo'] = 'Minor changes do not amend the meaning of the policy text, terms or conditions. Users do not need to reconfirm their consent.';
+$string['minorchangeinfo'] = 'A minor change does not alter the meaning of the policy. Users are not required to agree to the policy again if the edit is marked as a minor change.';
 $string['managepolicies'] = 'Manage policies';
 $string['movedown'] = 'Move down';
 $string['moveup'] = 'Move up';
@@ -97,7 +97,7 @@ $string['nopermissiontoagreedocs'] = 'No permission to agree to the policies';
 $string['nopermissiontoagreedocs_desc'] = 'Sorry, you do not have the required permissions to agree to the policies.<br />You will not be able to use this site until the following policies are agreed:';
 $string['nopermissiontoagreedocsbehalf'] = 'No permission to agree to the policies on behalf of this user';
 $string['nopermissiontoagreedocsbehalf_desc'] = 'Sorry, you do not have the required permission to agree to the following policies on behalf of {$a}:';
-$string['nopermissiontoagreedocscontact'] = 'For further assistance, please contact the following person:';
+$string['nopermissiontoagreedocscontact'] = 'For further assistance, please contact';
 $string['nopermissiontoviewpolicyversion'] = 'You do not have permissions to view this policy version.';
 $string['nopolicies'] = 'There are no policies for registered users with an active version.';
 $string['selectpolicyandversion'] = 'Use the filter above to select policy and/or version';
@@ -105,7 +105,7 @@ $string['steppolicies'] = 'Policy {$a->numpolicy} out of {$a->totalpolicies}';
 $string['pluginname'] = 'Policies';
 $string['policiesagreements'] = 'Policies and agreements';
 $string['policy:accept'] = 'Agree to the policies';
-$string['policy:acceptbehalf'] = 'Agree to the policies on someone else\'s behalf';
+$string['policy:acceptbehalf'] = 'Give consent for policies on someone else\'s behalf';
 $string['policy:managedocs'] = 'Manage policies';
 $string['policy:viewacceptances'] = 'View user agreement reports';
 $string['policydocaudience'] = 'User consent';
@@ -124,30 +124,30 @@ $string['policydoctype0'] = 'Site policy';
 $string['policydoctype1'] = 'Privacy policy';
 $string['policydoctype2'] = 'Third parties policy';
 $string['policydoctype99'] = 'Other policy';
-$string['policyversionacceptedinbehalf'] = 'This policy version has been agreed to by another user on behalf of you.';
-$string['policyversionacceptedinotherlang'] = 'This policy version has been agreed to in a different language.';
+$string['policyversionacceptedinbehalf'] = 'Consent for this policy has been given on your behalf.';
+$string['policyversionacceptedinotherlang'] = 'Consent for this policy version has been given in a different language.';
 $string['previousversions'] = '{$a} previous versions';
-$string['privacy:metadata:acceptances'] = 'Information from policies agreements made by the users of this site.';
+$string['privacy:metadata:acceptances'] = 'Information from policy agreements made by site users';
 $string['privacy:metadata:acceptances:policyversionid'] = 'The ID of the accepted version policy.';
 $string['privacy:metadata:acceptances:userid'] = &#