Merge branch 'MDL-63028-master' of git://github.com/bmbrands/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 21 Aug 2018 17:23:37 +0000 (19:23 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 21 Aug 2018 17:23:37 +0000 (19:23 +0200)
593 files changed:
Gruntfile.js
admin/modules.php
admin/registration/index.php
admin/tests/behat/enable_multiple_accounts_use_same_email.feature
admin/tool/assignmentupgrade/batchupgrade.php [deleted file]
admin/tool/assignmentupgrade/classes/privacy/provider.php [deleted file]
admin/tool/assignmentupgrade/index.php [deleted file]
admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php [deleted file]
admin/tool/assignmentupgrade/listnotupgraded.php [deleted file]
admin/tool/assignmentupgrade/locallib.php [deleted file]
admin/tool/assignmentupgrade/module.js [deleted file]
admin/tool/assignmentupgrade/paginationform.php [deleted file]
admin/tool/assignmentupgrade/renderer.php [deleted file]
admin/tool/assignmentupgrade/settings.php [deleted file]
admin/tool/assignmentupgrade/styles.css [deleted file]
admin/tool/assignmentupgrade/tests/privacy_test.php [deleted file]
admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php [deleted file]
admin/tool/assignmentupgrade/upgradableassignmentstable.php [deleted file]
admin/tool/assignmentupgrade/upgradesingle.php [deleted file]
admin/tool/assignmentupgrade/upgradesingleconfirm.php [deleted file]
admin/tool/availabilityconditions/tests/behat/manage_conditions.feature
admin/tool/behat/tests/behat/data_generators.feature
admin/tool/behat/tests/behat/get_and_set_fields.feature
admin/tool/behat/tests/behat/list_steps.feature
admin/tool/behat/tests/manager_test.php [deleted file]
admin/tool/dataprivacy/classes/api.php
admin/tool/dataprivacy/classes/data_registry.php
admin/tool/dataprivacy/classes/data_request.php
admin/tool/dataprivacy/classes/external/data_request_exporter.php
admin/tool/dataprivacy/classes/local/helper.php
admin/tool/dataprivacy/classes/metadata_registry.php
admin/tool/dataprivacy/classes/output/data_requests_table.php
admin/tool/dataprivacy/classes/output/my_data_requests_page.php
admin/tool/dataprivacy/classes/task/delete_expired_requests.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/task/process_data_request_task.php
admin/tool/dataprivacy/datadeletion.php
admin/tool/dataprivacy/dataregistry.php
admin/tool/dataprivacy/datarequests.php
admin/tool/dataprivacy/db/access.php
admin/tool/dataprivacy/db/tasks.php
admin/tool/dataprivacy/db/upgrade.php [new file with mode: 0644]
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
admin/tool/dataprivacy/lib.php
admin/tool/dataprivacy/pluginregistry.php
admin/tool/dataprivacy/settings.php
admin/tool/dataprivacy/styles.css
admin/tool/dataprivacy/templates/my_data_requests.mustache
admin/tool/dataprivacy/tests/api_test.php
admin/tool/dataprivacy/tests/behat/datadelete.feature [new file with mode: 0644]
admin/tool/dataprivacy/tests/behat/dataexport.feature [new file with mode: 0644]
admin/tool/dataprivacy/tests/data_privacy_testcase.php [new file with mode: 0644]
admin/tool/dataprivacy/tests/expired_data_requests_test.php [new file with mode: 0644]
admin/tool/dataprivacy/tests/manager_observer_test.php
admin/tool/dataprivacy/version.php
admin/tool/filetypes/tests/behat/add_filetypes.feature
admin/tool/httpsreplace/tests/behat/httpsreplace.feature
admin/tool/langimport/tests/behat/manage_langpacks.feature
admin/tool/monitor/tests/behat/disabled.feature
admin/tool/monitor/tests/behat/rule.feature
admin/tool/monitor/tests/behat/subscription.feature
admin/tool/policy/classes/output/page_viewalldoc.php
admin/tool/policy/lang/en/tool_policy.php
admin/tool/policy/lib.php
admin/tool/policy/templates/page_viewalldoc.mustache
admin/tool/policy/tests/behat/consent.feature
admin/tool/policy/tests/behat/managepolicies.feature
admin/tool/policy/viewall.php
admin/tool/recyclebin/tests/behat/backup_user_data.feature
admin/tool/recyclebin/tests/behat/basic_functionality.feature
admin/tool/task/tests/behat/clear_fail_delay.feature
admin/tool/task/tests/behat/manage_tasks.feature
admin/tool/task/tests/behat/run_task_now.feature
admin/tool/upgrade.txt
admin/tool/uploadcourse/tests/behat/create.feature
admin/tool/uploadcourse/tests/behat/update.feature
admin/tool/uploaduser/tests/behat/upload_users.feature
admin/tool/usertours/tests/behat/behat_tool_usertours.php
auth/shibboleth/auth.php
auth/shibboleth/index_form.html
auth/shibboleth/lang/en/auth_shibboleth.php
auth/shibboleth/lib.php [new file with mode: 0644]
auth/shibboleth/login.php
auth/shibboleth/settings.php
auth/tests/behat/displayloginfailures.feature
auth/tests/behat/validateagedigitalconsentmap.feature
availability/condition/completion/tests/behat/conditional_bug.feature
availability/condition/profile/tests/behat/availability_profile.feature
backup/backup.php
backup/moodle2/tests/behat/import_multiple_times.feature
backup/util/dbops/restore_dbops.class.php
backup/util/ui/base_moodleform.class.php
backup/util/ui/renderer.php
backup/util/ui/tests/behat/import_groups.feature
backup/util/ui/tests/behat/restore_moodle2_courses.feature
backup/util/ui/tests/behat/restore_moodle2_courses_settings.feature
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js
backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js
badges/tests/behat/add_badge.feature
badges/tests/behat/award_badge.feature
badges/tests/behat/criteria_cohort.feature
badges/tests/behat/criteria_profile.feature
badges/tests/behat/role_visibility.feature
blocks/admin_bookmarks/tests/behat/bookmark_admin_pages.feature
blocks/badges/tests/behat/block_badges_course.feature
blocks/badges/tests/behat/block_badges_dashboard.feature
blocks/badges/tests/behat/block_badges_frontpage.feature
blocks/blog_menu/tests/behat/block_blog_menu_frontpage.feature
blocks/blog_recent/tests/behat/block_blog_recent_frontpage.feature
blocks/calendar_month/tests/behat/block_calendar_month.feature
blocks/calendar_upcoming/tests/behat/block_calendar_upcoming_frontpage.feature
blocks/comments/tests/behat/block_comment_frontpage.feature
blocks/completionstatus/tests/behat/block_completionstatus.feature
blocks/completionstatus/tests/behat/block_completionstatus_activity_completion.feature
blocks/completionstatus/tests/behat/block_completionstatus_manual_other.feature
blocks/completionstatus/tests/behat/block_completionstatus_manual_self.feature
blocks/course_list/tests/behat/block_course_list_frontpage.feature
blocks/course_summary/tests/behat/block_course_summary_frontpage.feature
blocks/login/tests/behat/login_block.feature
blocks/myoverview/amd/build/event_list.min.js
blocks/myoverview/amd/src/event_list.js
blocks/myoverview/templates/timeline-view-courses.mustache
blocks/myprofile/lang/en/block_myprofile.php
blocks/myprofile/lang/en/deprecated.txt
blocks/myprofile/tests/behat/block_myprofile_frontpage.feature
blocks/navigation/tests/behat/expand_courses_node.feature
blocks/online_users/tests/behat/block_online_users_frontpage.feature
blocks/participants/tests/behat/block_participants_frontpage.feature
blocks/private_files/tests/behat/block_private_files_frontpage.feature
blocks/recent_activity/block_recent_activity.php
blocks/recent_activity/classes/task/cleanup.php [new file with mode: 0644]
blocks/recent_activity/db/tasks.php [moved from mod/quiz/db/renamedclasses.php with 60% similarity]
blocks/recent_activity/lang/en/block_recent_activity.php
blocks/recent_activity/version.php
blocks/rss_client/block_rss_client.php
blocks/rss_client/classes/task/refreshfeeds.php [new file with mode: 0644]
blocks/rss_client/db/tasks.php [moved from admin/tool/assignmentupgrade/version.php with 59% similarity]
blocks/rss_client/lang/en/block_rss_client.php
blocks/rss_client/tests/cron_test.php
blocks/rss_client/version.php
blocks/search_forums/tests/behat/block_search_forums_course.feature
blocks/search_forums/tests/behat/block_search_forums_frontpage.feature
blocks/site_main_menu/tests/behat/add_url.feature
blocks/site_main_menu/tests/behat/edit_activities.feature
blocks/tag_flickr/tests/behat/configuring_tag_flickr_block.feature
blocks/tests/behat/configure_block_throughout_site.feature
blog/classes/privacy/provider.php
blog/tests/behat/blog_visibility.feature
blog/tests/behat/comment.feature
blog/tests/behat/delete.feature
cache/stores/redis/lib.php
calendar/amd/build/event_form.min.js
calendar/amd/build/repository.min.js
calendar/amd/src/event_form.js
calendar/amd/src/repository.js
calendar/classes/local/event/container.php
calendar/classes/local/event/forms/create.php
calendar/classes/local/event/forms/eventtype.php
calendar/classes/local/event/forms/managesubscriptions.php
calendar/classes/local/event/mappers/create_update_form_mapper.php
calendar/classes/local/event/strategies/raw_event_retrieval_strategy.php
calendar/externallib.php
calendar/lib.php
calendar/managesubscriptions.php
calendar/tests/behat/calendar.feature
calendar/tests/behat/category_events.feature
calendar/tests/externallib_test.php
calendar/tests/lib_test.php
calendar/tests/raw_event_retrieval_strategy_test.php
calendar/upgrade.txt
cohort/edit.php
cohort/index.php
cohort/tests/behat/access_visible_cohorts.feature
cohort/tests/behat/add_cohort.feature
cohort/tests/behat/behat_cohort.php
cohort/tests/behat/upload_cohort_users.feature
cohort/tests/behat/upload_cohorts.feature
cohort/tests/behat/view_cohorts.feature
comment/lib.php
competency/tests/privacy_test.php
completion/tests/behat/behat_completion.php
completion/tests/behat/enable_manual_complete_mark.feature
config-dist.php
course/ajax/management.php
course/classes/management_renderer.php
course/classes/search/mycourse.php
course/classes/search/section.php
course/format/topics/db/upgradelib.php
course/format/topics/tests/format_topics_upgrade_test.php
course/format/upgrade.txt
course/format/weeks/db/upgradelib.php
course/format/weeks/tests/format_weeks_upgrade_test.php
course/renderer.php
course/tests/behat/behat_course.php
course/tests/behat/coursetags.feature
course/tests/behat/keyholder.feature
course/tests/behat/max_number_sections.feature
course/tests/behat/role_renaming.feature
course/tests/search_test.php
course/upgrade.txt
enrol/guest/tests/behat/guest_access.feature
enrol/lti/classes/manage_table.php
enrol/lti/lang/en/enrol_lti.php
enrol/lti/tests/behat/basic_settings.feature
enrol/lti/tests/behat/index_page.feature
enrol/manual/amd/build/form-potential-user-selector.min.js
enrol/manual/amd/src/form-potential-user-selector.js
enrol/manual/tests/behat/quickenrolment.feature [new file with mode: 0644]
enrol/meta/tests/behat/enrol_meta.feature
enrol/paypal/ipn.php
enrol/self/lang/en/enrol_self.php
enrol/self/lib.php
enrol/self/settings.php
enrol/self/tests/behat/key_holder.feature
enrol/tests/behat/behat_enrol.php
enrol/tests/behat/enrol_user.feature
enrol/tests/behat/role_visibility.feature
error/index.php
files/classes/privacy/provider.php
files/tests/behat/add_custom_file_type.feature
files/tests/behat/course_files.feature
grade/edit/letter/index.php
grade/edit/tree/lib.php
grade/report/history/index.php
grade/tests/behat/grade_UI_settings.feature
grade/tests/behat/grade_average.feature
grade/tests/behat/grade_calculated_weights.feature
grade/tests/behat/grade_category_validation.feature
grade/tests/behat/grade_hidden_items.feature
grade/tests/behat/grade_item_validation.feature
grade/tests/behat/grade_letter_logging.feature
grade/tests/behat/grade_natural_normalisation.feature
grade/tests/behat/grade_point_maximum.feature
grade/tests/behat/grade_scales.feature
grade/tests/behat/grade_scales_aggregation.feature
grade/tests/behat/grade_scales_logging.feature
grade/tests/behat/grade_single_item_scales.feature
group/tests/behat/overview.feature
group/tests/behat/role_visibility.feature
install/lang/el/admin.php
install/lang/el/error.php
install/lang/el/install.php
lang/en/admin.php
lang/en/backup.php
lang/en/calendar.php
lang/en/competency.php
lang/en/deprecated.txt
lang/en/files.php
lang/en/form.php
lang/en/hub.php
lang/en/media.php
lang/en/message.php
lang/en/moodle.php
lang/en/webservice.php
lib/accesslib.php
lib/adminlib.php
lib/amd/build/ajax.min.js
lib/amd/build/form-autocomplete.min.js
lib/amd/src/ajax.js
lib/amd/src/form-autocomplete.js
lib/behat/classes/behat_config_manager.php
lib/classes/event/base.php
lib/classes/event/message_sent.php
lib/classes/hub/api.php
lib/classes/hub/registration.php
lib/classes/hub/site_unregistration_form.php
lib/classes/message/manager.php
lib/classes/plugin_manager.php
lib/classes/session/manager.php
lib/classes/session/redis.php
lib/coursecatlib.php
lib/datalib.php
lib/db/caches.php
lib/db/renamedclasses.php
lib/db/services.php
lib/db/tasks.php
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/atto/tests/behat/autosave.feature
lib/editor/atto/tests/behat/disablecontrol.feature [new file with mode: 0644]
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
lib/editor/atto/yui/src/editor/js/editor.js
lib/editor/tests/fixtures/disable_control_example.php [new file with mode: 0644]
lib/editor/tests/fixtures/editor_form.php [new file with mode: 0644]
lib/editor/textarea/tests/behat/disablecontrol.feature [new file with mode: 0644]
lib/editor/tinymce/module.js
lib/editor/tinymce/tests/behat/disablecontrol.feature [new file with mode: 0644]
lib/eventslib.php [deleted file]
lib/externallib.php
lib/filebrowser/file_info_context_course.php
lib/filelib.php
lib/form/form.js
lib/form/htmleditor.php
lib/form/submitlink.php [deleted file]
lib/form/tests/behat/modgrade_validation.feature
lib/formslib.php
lib/grade/grade_category.php
lib/grade/grade_object.php
lib/grade/grade_outcome.php
lib/gradelib.php
lib/grouplib.php
lib/medialib.php [deleted file]
lib/messagelib.php
lib/moodlelib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/password_compat/lib/password.php [deleted file]
lib/phpunit/classes/advanced_testcase.php
lib/phpunit/classes/util.php
lib/questionlib.php
lib/setup.php
lib/tests/admintree_test.php
lib/tests/behat/alpha_chooser.feature
lib/tests/behat/behat_deprecated.php
lib/tests/behat/behat_filters.php
lib/tests/behat/behat_general.php
lib/tests/behat/behat_navigation.php
lib/tests/behat/behat_permissions.php
lib/tests/behat/behat_transformations.php
lib/tests/behat/timezone.feature
lib/tests/coursecatlib_test.php
lib/tests/csslib_test.php
lib/tests/event_test.php
lib/tests/eventslib_test.php [deleted file]
lib/tests/filelib_test.php
lib/tests/gradelib_test.php
lib/tests/grouplib_test.php
lib/tests/medialib_test.php
lib/tests/moodle_url_test.php [new file with mode: 0644]
lib/tests/moodlelib_test.php
lib/tests/plugin_manager_test.php
lib/tests/questionlib_test.php
lib/tests/string_manager_standard_test.php
lib/tests/weblib_test.php
lib/upgrade.txt
lib/upgradelib.php
lib/weblib.php
lib/xhprof/xhprof_moodle.php
media/classes/player.php
media/player/videojs/tests/behat/modules.feature
media/upgrade.txt
message/classes/search/base_message.php
message/lib.php
message/tests/behat/update_messaging_preferences.feature
message/tests/events_test.php
message/tests/search_received_test.php
message/tests/search_sent_test.php
message/upgrade.txt
mod/assign/backup/moodle2/backup_assign_stepslib.php
mod/assign/backup/moodle2/restore_assign_stepslib.php
mod/assign/feedback/editpdf/tests/behat/annotate_pdf.feature
mod/assign/lang/en/assign.php
mod/assign/lang/en/deprecated.txt
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/tests/behat/assign_course_reset.feature
mod/assign/tests/behat/outcome_grading.feature
mod/assign/tests/behat/quickgrading.feature
mod/assign/tests/lib_test.php
mod/assign/tests/locallib_test.php
mod/assign/upgrade.txt
mod/assignment/lang/en/assignment.php
mod/assignment/view.php
mod/chat/tests/behat/chat_calendar_events.feature
mod/chat/tests/behat/chat_course_reset.feature
mod/chat/tests/lib_test.php
mod/data/field/radiobutton/field.class.php
mod/data/lang/en/data.php
mod/data/lang/en/deprecated.txt
mod/data/tests/behat/behat_mod_data.php
mod/feedback/classes/responses_table.php
mod/feedback/lang/en/deprecated.txt
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/feedback/show_nonrespondents.php
mod/forum/classes/output/email/renderer.php
mod/forum/classes/output/emaildigestbasic/renderer_textemail.php
mod/forum/classes/output/emaildigestfull/renderer_textemail.php
mod/forum/classes/output/forum_post.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/tests/behat/advanced_search.feature
mod/forum/tests/mail_test.php
mod/glossary/lib.php
mod/label/lib.php
mod/label/tests/behat/label_idnumber.feature [new file with mode: 0644]
mod/label/version.php
mod/lesson/essay.php
mod/lesson/index.php
mod/lesson/locallib.php
mod/lesson/tests/behat/lesson_course_reset.feature
mod/lesson/tests/locallib_test.php
mod/lti/locallib.php
mod/lti/service/memberships/classes/local/resources/linkmemberships.php
mod/lti/service/memberships/classes/local/service/memberships.php
mod/lti/tests/behat/addtool.feature
mod/lti/tests/behat/backup_restore.feature
mod/lti/tests/behat/contentitem.feature
mod/lti/tests/behat/contentitemregistration.feature
mod/lti/tests/behat/toolconfigure.feature
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/tests/behat/backup.feature
mod/quiz/tests/behat/behat_mod_quiz.php
mod/quiz/tests/behat/completion_condition_attempts_used.feature
mod/quiz/tests/behat/completion_condition_passing_grade.feature
mod/quiz/tests/behat/editing_add.feature
mod/quiz/tests/behat/editing_add_from_question_bank.feature
mod/quiz/tests/behat/editing_add_random.feature
mod/quiz/tests/behat/quiz_reset.feature
mod/quiz/upgrade.txt
mod/scorm/db/renamedclasses.php [deleted file]
mod/scorm/report/basic/db/renamedclasses.php [deleted file]
mod/scorm/report/basic/upgrade.txt [new file with mode: 0644]
mod/scorm/upgrade.txt
mod/scorm/view.php
mod/upgrade.txt
mod/wiki/editors/html.php [deleted file]
mod/wiki/editors/wiki_editor.php
mod/wiki/instancecomments.php
mod/wiki/lib.php
mod/wiki/locallib.php
mod/wiki/pagelib.php
mod/wiki/tests/behat/reset_wiki_comments_tags_files.feature
my/tests/behat/reset_all_pages.feature
pix/i/empty.png [moved from pix/i/emtpy.png with 100% similarity]
pix/i/empty.svg [moved from pix/i/emtpy.svg with 100% similarity]
pluginfile.php
privacy/classes/local/request/contextlist.php
privacy/classes/local/request/writer.php
privacy/tests/contextlist_test.php
question/format/gift/tests/behat/import_export.feature
question/format/webct/tests/behat/import.feature
question/format/webct/tests/behat/importcalculated.feature
question/format/xml/tests/behat/import_export.feature
question/templates/tag_condition.mustache
question/tests/behat/copy_questions.feature
question/tests/behat/delete_questions.feature
question/tests/behat/edit_questions.feature
question/tests/behat/edit_questions_standard_tags.feature
question/tests/behat/filter_questions_by_tag.feature
question/tests/behat/move_question_categories.feature
question/tests/behat/preview_question.feature
question/tests/behat/question_categories.feature
question/tests/behat/sort_questions.feature
question/type/ddimageortext/tests/behat/add.feature
question/type/ddimageortext/tests/behat/backup_and_restore.feature
question/type/ddimageortext/tests/behat/edit.feature
question/type/ddimageortext/tests/behat/export.feature
question/type/ddimageortext/tests/behat/import.feature
question/type/ddimageortext/tests/behat/preview.feature
question/type/ddmarker/tests/behat/add.feature
question/type/ddmarker/tests/behat/backup_and_restore.feature
question/type/ddmarker/tests/behat/edit.feature
question/type/ddmarker/tests/behat/export.feature
question/type/ddmarker/tests/behat/import.feature
question/type/ddmarker/tests/behat/preview.feature
question/type/ddwtos/tests/behat/add.feature
question/type/ddwtos/tests/behat/backup_and_restore.feature
question/type/ddwtos/tests/behat/edit.feature
question/type/ddwtos/tests/behat/export.feature
question/type/ddwtos/tests/behat/import.feature
question/type/ddwtos/tests/behat/preview.feature
question/type/ddwtos/tests/edit_form_test.php [new file with mode: 0644]
question/type/description/tests/behat/add.feature
question/type/description/tests/behat/backup_and_restore.feature
question/type/description/tests/behat/edit.feature
question/type/description/tests/behat/export.feature
question/type/description/tests/behat/import.feature
question/type/description/tests/behat/preview.feature
question/type/essay/tests/behat/add.feature
question/type/essay/tests/behat/backup_and_restore.feature
question/type/essay/tests/behat/edit.feature
question/type/essay/tests/behat/export.feature
question/type/essay/tests/behat/import.feature
question/type/essay/tests/behat/preview.feature
question/type/gapselect/edit_form_base.php
question/type/gapselect/edit_gapselect_form.php
question/type/gapselect/tests/behat/basic_test.feature
question/type/gapselect/tests/behat/import_test.feature
question/type/gapselect/tests/edit_form_test.php
question/type/match/tests/behat/add.feature
question/type/match/tests/behat/backup_and_restore.feature
question/type/match/tests/behat/edit.feature
question/type/match/tests/behat/export.feature
question/type/match/tests/behat/import.feature
question/type/match/tests/behat/preview.feature
question/type/multichoice/tests/behat/add.feature
question/type/multichoice/tests/behat/backup_and_restore.feature
question/type/multichoice/tests/behat/edit.feature
question/type/multichoice/tests/behat/export.feature
question/type/multichoice/tests/behat/import.feature
question/type/multichoice/tests/behat/preview.feature
question/type/shortanswer/tests/behat/add.feature
question/type/shortanswer/tests/behat/backup_and_restore.feature
question/type/shortanswer/tests/behat/edit.feature
question/type/shortanswer/tests/behat/export.feature
question/type/shortanswer/tests/behat/import.feature
question/type/shortanswer/tests/behat/preview.feature
question/type/truefalse/tests/behat/add.feature
question/type/truefalse/tests/behat/backup_and_restore.feature
question/type/truefalse/tests/behat/edit.feature
question/type/truefalse/tests/behat/export.feature
question/type/truefalse/tests/behat/import.feature
question/type/truefalse/tests/behat/preview.feature
report/eventlist/tests/behat/mainsection.feature
report/log/tests/behat/filter_log.feature
report/log/tests/behat/filter_log_actions.feature
report/log/tests/behat/user_log.feature
report/loglive/tests/behat/loglive_report.feature
report/outline/tests/behat/filter.feature
report/outline/tests/behat/outline.feature
report/outline/tests/behat/user.feature
report/participation/tests/behat/filter_participation.feature
report/participation/tests/behat/message_participants.feature
report/progress/tests/behat/activity_completion_report.feature
report/upgrade.txt
repository/coursefiles/pix/icon.svg [new file with mode: 0644]
repository/dropbox/classes/dropbox.php
repository/dropbox/tests/api_test.php
repository/local/pix/icon.svg [new file with mode: 0644]
repository/recent/pix/icon.svg [new file with mode: 0644]
repository/user/pix/icon.svg [new file with mode: 0644]
search/classes/base.php
search/classes/base_block.php
search/classes/base_mod.php
search/classes/document.php
search/classes/document_icon.php [new file with mode: 0644]
search/classes/engine.php
search/templates/result.mustache
search/tests/base_activity_test.php
search/tests/base_block_test.php
search/tests/base_test.php
search/tests/document_icon_test.php [new file with mode: 0644]
search/tests/document_test.php
search/tests/engine_test.php
tag/tests/behat/collections.feature
tag/tests/behat/delete_tag.feature
tag/tests/behat/edit_tag.feature
tag/tests/behat/flag_tags.feature
tag/tests/behat/standard_tags.feature
tag/tests/behat/tagindex.feature
tag/tests/events_test.php
tag/tests/taglib_test.php
tag/upgrade.txt
theme/boost/classes/output/core_course/management/renderer.php
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/style/moodle.css
theme/boost/templates/core_form/element-radio.mustache
theme/boost/tests/behat/behat_theme_boost_behat_deprecated.php [new file with mode: 0644]
theme/boost/tests/behat/behat_theme_boost_behat_mod_quiz.php
theme/boost/tests/behat/behat_theme_boost_behat_navigation.php
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/style/moodle.css
theme/upgrade.txt
tokenpluginfile.php [moved from lib/classes/task/events_cron_task.php with 51% similarity]
user/classes/search/user.php
user/editlib.php
user/index.php
user/lib.php
user/message.html [deleted file]
user/messageselect.php [deleted file]
user/profile/lib.php
user/selector/lib.php
user/tests/behat/addnewuser.feature [new file with mode: 0644]
user/tests/behat/custom_profile_fields.feature
user/tests/behat/delete_users.feature
user/tests/behat/edit_user_enrolment.feature
user/tests/behat/edituserpassword.feature
user/tests/behat/enrol_cohort_list.feature
user/tests/behat/filter_participants_showall.feature
user/tests/behat/name_fields.feature
user/tests/behat/table_sorting.feature
user/tests/behat/user_grade_navigation.feature
user/tests/behat/view_full_profile.feature
user/tests/behat/view_participants.feature
user/tests/behat/view_preferences_page.feature
user/tests/fixtures/testable_user_selector.php [new file with mode: 0644]
user/tests/search_test.php
user/tests/userselector_test.php [new file with mode: 0644]
user/upgrade.txt [new file with mode: 0644]
version.php
webservice/lib.php

index 4501fc6..78583d0 100644 (file)
@@ -37,7 +37,7 @@ module.exports = function(grunt) {
     var expected = semver.validRange(grunt.file.readJSON('package.json').engines.node);
     var actual = semver.valid(process.version);
     if (!semver.satisfies(actual, expected)) {
-        grunt.fail.fatal('Node version too old. Require ' + expected + ', version installed: ' + actual);
+        grunt.fail.fatal('Node version not satisfied. Require ' + expected + ', version installed: ' + actual);
     }
 
     // Windows users can't run grunt in a subdirectory, so allow them to set
index fb112e7..01f67a1 100644 (file)
     $table->set_attribute('class', 'admintable generaltable');
     $table->setup();
 
+    $pluginmanager = core_plugin_manager::instance();
+
     foreach ($modules as $module) {
+        $plugininfo = $pluginmanager->get_plugin_info('mod_'.$module->name);
+        $status = $plugininfo->get_status();
 
-        if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
+        if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
             $strmodulename = '<span class="notifyproblem">'.$module->name.' ('.get_string('missingfromdisk').')</span>';
             $missing = true;
         } else {
index 57d65a3..62f251a 100644 (file)
@@ -40,8 +40,9 @@ if ($unregistration && \core\hub\registration::is_registered()) {
     if ($siteunregistrationform->is_cancelled()) {
         redirect(new moodle_url('/admin/registration/index.php'));
     } else if ($data = $siteunregistrationform->get_data()) {
-        if (\core\hub\registration::unregister($data->unpublishalladvertisedcourses,
-            $data->unpublishalluploadedcourses)) {
+        \core\hub\registration::unregister($data->unpublishalladvertisedcourses,
+            $data->unpublishalluploadedcourses);
+        if (!\core\hub\registration::is_registered()) {
             redirect(new moodle_url('/admin/registration/index.php'));
         }
     }
index 30d21b9..65dffa3 100644 (file)
@@ -10,7 +10,7 @@ Feature: Enable multiple accounts to have the same email address
   Scenario: Enable registration of multiple accounts with the same email address
     Given the following config values are set as admin:
       | allowaccountssameemail | 1 |
-    When I navigate to "Add a new user" node in "Site administration>Users>Accounts"
+    When I navigate to "Users > Accounts > Add a new user" in site administration
     And I set the following fields to these values:
       | Username                        | testmultiemailuser1             |
       | Choose an authentication method | Manual accounts                 |
@@ -35,7 +35,7 @@ Feature: Enable multiple accounts to have the same email address
   Scenario: Disable registration of multiple accounts with the same email address
     Given the following config values are set as admin:
       | allowaccountssameemail | 0 |
-    When I navigate to "Add a new user" node in "Site administration>Users>Accounts"
+    When I navigate to "Users > Accounts > Add a new user" in site administration
     And I set the following fields to these values:
       | Username                        | testmultiemailuser1             |
       | Choose an authentication method | Manual accounts                 |
diff --git a/admin/tool/assignmentupgrade/batchupgrade.php b/admin/tool/assignmentupgrade/batchupgrade.php
deleted file mode 100644 (file)
index 7744063..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?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/>.
-
-/**
- * Script to show all the assignments that have not been upgraded after the main upgrade.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-define('NO_OUTPUT_BUFFERING', true);
-
-require_once(__DIR__ . '/../../../config.php');
-require_once($CFG->libdir . '/adminlib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/locallib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/upgradableassignmentstable.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/upgradableassignmentsbatchform.php');
-
-require_sesskey();
-
-// This calls require_login and checks moodle/site:config.
-admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('batchupgrade'));
-
-$PAGE->set_pagelayout('maintenance');
-$PAGE->navbar->add(get_string('batchupgrade', 'tool_assignmentupgrade'));
-
-$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
-
-$confirm = required_param('confirm', PARAM_BOOL);
-if (!$confirm) {
-    print_error('invalidrequest');
-    die();
-}
-raise_memory_limit(MEMORY_EXTRA);
-// Release session.
-\core\session\manager::write_close();
-
-echo $renderer->header();
-echo $renderer->heading(get_string('batchupgrade', 'tool_assignmentupgrade'));
-
-$current = 0;
-if (optional_param('upgradeall', false, PARAM_BOOL)) {
-    $assignmentids = tool_assignmentupgrade_load_all_upgradable_assignmentids();
-} else {
-    $assignmentids = explode(',', optional_param('selected', '', PARAM_TEXT));
-}
-$total = count($assignmentids);
-
-foreach ($assignmentids as $assignmentid) {
-    list($summary, $success, $log) = tool_assignmentupgrade_upgrade_assignment($assignmentid);
-    $current += 1;
-    $params = array('current'=>$current, 'total'=>$total);
-    echo $renderer->heading(get_string('upgradeprogress', 'tool_assignmentupgrade', $params), 3);
-    echo $renderer->convert_assignment_result($summary, $success, $log);
-}
-
-echo $renderer->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
-echo $renderer->footer();
diff --git a/admin/tool/assignmentupgrade/classes/privacy/provider.php b/admin/tool/assignmentupgrade/classes/privacy/provider.php
deleted file mode 100644 (file)
index 452cc15..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Privacy Subsystem implementation for tool_assignmentupgrade.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2018 Zig Tan <zig@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace tool_assignmentupgrade\privacy;
-
-use core_privacy\local\metadata\collection;
-use core_privacy\local\request\writer;
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Privacy Subsystem for tool_assignmentupgrade implementing metadata, plugin, and user_preference providers.
- *
- * @copyright  2018 Zig Tan <zig@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class provider implements
-    \core_privacy\local\metadata\provider,
-    \core_privacy\local\request\user_preference_provider {
-
-    /**
-     * Returns meta data about this system.
-     *
-     * @param   collection $collection The initialised collection to add items to.
-     * @return  collection     A listing of user data stored through this system.
-     */
-    public static function get_metadata(collection $collection) : collection {
-        $collection->add_user_preference(
-            'tool_assignmentupgrade_perpage',
-            'privacy:metadata:preference:perpage'
-        );
-        return $collection;
-    }
-
-    /**
-     * Export all user preferences for the plugin.
-     *
-     * @param   int $userid The userid of the user whose data is to be exported.
-     */
-    public static function export_user_preferences(int $userid) {
-        $perpage = get_user_preferences('tool_assignmentupgrade_perpage', null, $userid);
-        if ($perpage !== null) {
-            writer::export_user_preference(
-                'tool_assignmentupgrade',
-                'perpage',
-                $perpage,
-                get_string('privacy:metadata:preference:perpage', 'tool_assignmentupgrade')
-            );
-        }
-    }
-
-}
diff --git a/admin/tool/assignmentupgrade/index.php b/admin/tool/assignmentupgrade/index.php
deleted file mode 100644 (file)
index 7720ac7..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This tool can upgrade old assignment activities to the new assignment activity type
- *
- * The upgrade can be done on any old assignment instance providing it is using one of the core
- * assignment subtypes (online text, single upload, etc).
- * The new assignment module was introduced in Moodle 2.3 and although it completely reproduces
- * the features of the existing assignment type it wasn't designed to replace it entirely as there
- * are many custom assignment types people use and it wouldn't be practical to try to convert them.
- * Instead the existing assignment type will be left in core and people will be encouraged to
- * use the new assignment type.
- *
- * This screen is the main entry-point to the plugin, it gives the admin a list
- * of options available to them.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require_once(__DIR__ . '/../../../config.php');
-require_once($CFG->libdir . '/adminlib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/locallib.php');
-
-// This calls require_login and checks moodle/site:config.
-admin_externalpage_setup('assignmentupgrade');
-
-$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
-
-$actions = array();
-
-$header = get_string('pluginname', 'tool_assignmentupgrade');
-$actions[] = tool_assignmentupgrade_action::make('listnotupgraded');
-
-echo $renderer->index_page($header, $actions);
diff --git a/admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php b/admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php
deleted file mode 100644 (file)
index c7e7d26..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?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/>.
-
-/**
- * Strings for the assignment upgrade tool
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-$string['areyousure'] = 'Are you sure?';
-$string['areyousuremessage'] = 'Are you sure you want to upgrade the assignment "{$a->name}"?';
-$string['assignmentid'] = 'Assignment ID';
-$string['assignmentnotfound'] = 'Assignment could not be found (id={$a})';
-$string['assignmentsperpage'] = 'Assignments per page';
-$string['assignmenttype'] = 'Assignment type';
-$string['backtoindex'] = 'Back to index';
-$string['batchoperations'] = 'Batch operations';
-$string['batchupgrade'] = 'Upgrade multiple assignments';
-$string['confirmbatchupgrade'] = 'Confirm batch upgrade assignments';
-$string['conversioncomplete'] = 'Assignment converted';
-$string['conversionfailed'] = 'The assignment conversion was not successful. The log from the upgrade was: <br />{$a}';
-$string['listnotupgraded'] = 'List assignments that have not been upgraded';
-$string['listnotupgraded_desc'] = 'You can upgrade individual assignments from here';
-$string['noassignmentsselected'] = 'No assignments selected';
-$string['noassignmentstoupgrade'] = 'There are no assignments that require upgrading';
-$string['notsupported'] = '';
-$string['notupgradedintro'] = 'This page lists the assignments created with an older version of Moodle that have not been upgraded to the new assignment module in Moodle 2.3. Not all assignments can be upgraded - if they were created with a custom assignment subtype, then that subtype will need to be upgraded to the new assignment plugin format in order to complete the upgrade.';
-$string['notupgradedtitle'] = 'Assignments not upgraded';
-$string['pluginname'] = 'Assignment upgrade helper';
-$string['select'] = 'Select';
-$string['submissions'] = 'Submissions';
-$string['supported'] = 'Upgrade';
-$string['updatetable'] = 'Update table';
-$string['unknown'] = 'Unknown';
-$string['upgradeassignmentsummary'] = 'Upgrade assignment: {$a->name} (Course: {$a->shortname})';
-$string['upgradeassignmentsuccess'] = 'Result: Upgrade successful';
-$string['upgradeassignmentfailed'] = 'Result: Upgrade failed. The log from the upgrade was: <br/><div class="tool_assignmentupgrade_upgradelog">{$a->log}</div>';
-$string['upgradable'] = 'Upgradable';
-$string['upgradeselected'] = 'Upgrade selected assignments';
-$string['upgradeselectedcount'] = 'Upgrade {$a} selected assignments?';
-$string['upgradeall'] = 'Upgrade all assignments';
-$string['upgradeallconfirm'] = 'Upgrade all assignments?';
-$string['upgradeprogress'] = 'Upgrade assignment {$a->current} of {$a->total}';
-$string['upgradesingle'] = 'Upgrade single assignment';
-$string['viewcourse'] = 'View the course with the converted assignment';
-$string['privacy:metadata:preference:perpage'] = 'The assignment upgrade records per page preference set for the user.';
diff --git a/admin/tool/assignmentupgrade/listnotupgraded.php b/admin/tool/assignmentupgrade/listnotupgraded.php
deleted file mode 100644 (file)
index 0836076..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<?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/>.
-
-/**
- * Script to show all the assignments that have not been upgraded after the main upgrade.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require_once(__DIR__ . '/../../../config.php');
-require_once($CFG->libdir . '/adminlib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/locallib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/upgradableassignmentstable.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/upgradableassignmentsbatchform.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/paginationform.php');
-
-// This calls require_login and checks moodle/site:config.
-admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('listnotupgraded'));
-$PAGE->navbar->add(get_string('listnotupgraded', 'tool_assignmentupgrade'));
-
-$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
-
-$perpage = optional_param('perpage', 0, PARAM_INT);
-if (!$perpage) {
-    $perpage = get_user_preferences('tool_assignmentupgrade_perpage', 100);
-} else {
-    set_user_preference('tool_assignmentupgrade_perpage', $perpage);
-}
-$assignments = new tool_assignmentupgrade_assignments_table($perpage);
-
-$batchform = new tool_assignmentupgrade_batchoperations_form();
-$data = $batchform->get_data();
-
-if ($data && $data->selectedassignments != '' || $data && isset($data->upgradeall)) {
-    require_sesskey();
-    echo $renderer->confirm_batch_operation_page($data);
-} else {
-    $paginationform = new tool_assignmentupgrade_pagination_form();
-    $pagedata = new stdClass();
-    $pagedata->perpage = $perpage;
-    $paginationform->set_data($pagedata);
-    echo $renderer->assignment_list_page($assignments, $batchform, $paginationform);
-}
-
diff --git a/admin/tool/assignmentupgrade/locallib.php b/admin/tool/assignmentupgrade/locallib.php
deleted file mode 100644 (file)
index 27db509..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-<?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/>.
-
-/**
- * Assignment upgrade tool library functions
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Get the URL of a script within this plugin.
- * @param string $script the script name, without .php. E.g. 'index'
- * @param array $params URL parameters (optional)
- * @return moodle_url
- */
-function tool_assignmentupgrade_url($script, $params = array()) {
-    return new moodle_url('/admin/tool/assignmentupgrade/' . $script . '.php', $params);
-}
-
-/**
- * Class to encapsulate the continue / cancel for batch operations
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_batchoperationconfirm implements renderable {
-    /** @var string $continuemessage The message to show above the continue cancel buttons */
-    public $continuemessage = '';
-    /** @var string $continueurl The url to load if the user clicks continue */
-    public $continueurl;
-
-    /**
-     * Constructor for this class
-     * @param stdClass $data - The data from the previous batch form
-     */
-    public function __construct($data) {
-        if (isset($data->upgradeselected)) {
-            $this->continuemessage = get_string('upgradeselectedcount',
-                                                'tool_assignmentupgrade',
-                                                count(explode(',', $data->selectedassignments)));
-            $urlparams = array('upgradeselected'=>'1',
-                               'confirm'=>'1',
-                               'sesskey'=>sesskey(),
-                               'selected'=>$data->selectedassignments);
-            $this->continueurl = new moodle_url('/admin/tool/assignmentupgrade/batchupgrade.php', $urlparams);
-        } else if (isset($data->upgradeall)) {
-            if (!tool_assignmentupgrade_any_upgradable_assignments()) {
-                $this->continuemessage = get_string('noassignmentstoupgrade', 'tool_assignmentupgrade');
-                $this->continueurl = '';
-            } else {
-                $this->continuemessage = get_string('upgradeallconfirm', 'tool_assignmentupgrade');
-                $urlparams = array('upgradeall'=>'1', 'confirm'=>'1', 'sesskey'=>sesskey());
-                $this->continueurl = new moodle_url('/admin/tool/assignmentupgrade/batchupgrade.php', $urlparams);
-            }
-        }
-    }
-}
-
-
-/**
- * Class to encapsulate one of the functionalities that this plugin offers.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_action {
-    /** @var string the name of this action. */
-    public $name;
-    /** @var moodle_url the URL to launch this action. */
-    public $url;
-    /** @var string a description of this aciton. */
-    public $description;
-
-    /**
-     * Constructor to set the fields.
-     *
-     * In order to create a new tool_assignmentupgrade_action instance you must use
-     * the tool_assignmentupgrade_action::make
-     * method.
-     *
-     * @param string $name the name of this action.
-     * @param moodle_url $url the URL to launch this action.
-     * @param string $description a description of this aciton.
-     */
-    protected function __construct($name, moodle_url $url, $description) {
-        $this->name = $name;
-        $this->url = $url;
-        $this->description = $description;
-    }
-
-    /**
-     * Make an action with standard values.
-     * @param string $shortname internal name of the action. Used to get strings and build a URL.
-     * @param array $params any URL params required.
-     * @return tool_assignmentupgrade_action
-     */
-    public static function make($shortname, $params = array()) {
-        return new self(
-                get_string($shortname, 'tool_assignmentupgrade'),
-                tool_assignmentupgrade_url($shortname, $params),
-                get_string($shortname . '_desc', 'tool_assignmentupgrade'));
-    }
-}
-
-/**
- * Determine if there are any assignments that can be upgraded
- * @return boolean - Are there any assignments that can be upgraded
- */
-function tool_assignmentupgrade_any_upgradable_assignments() {
-    global $DB, $CFG;
-    require_once($CFG->dirroot . '/mod/assign/locallib.php');
-    // First find all the unique assignment types.
-    $types = $DB->get_records_sql('SELECT plugin AS assignmenttype,
-                                          value AS version
-                                   FROM {config_plugins}
-                                   WHERE
-                                       name = ? AND
-                                       plugin LIKE ?', array('version', 'assignment_%'));
-
-    $upgradabletypes = array();
-
-    foreach ($types as $assignment) {
-        $shorttype = substr($assignment->assignmenttype, strlen('assignment_'));
-        if (assign::can_upgrade_assignment($shorttype, $assignment->version)) {
-            $upgradabletypes[] = $shorttype;
-        }
-    }
-    list($sql, $params) = $DB->get_in_or_equal($upgradabletypes);
-
-    $count = $DB->count_records_sql('SELECT COUNT(id) FROM {assignment} WHERE assignmenttype ' . $sql, $params);
-
-    return $count > 0;
-}
-
-/**
- * Load a list of all the assignmentids that can be upgraded
- * @return array of assignment ids
- */
-function tool_assignmentupgrade_load_all_upgradable_assignmentids() {
-    global $DB, $CFG;
-    require_once($CFG->dirroot . '/mod/assign/locallib.php');
-    // First find all the unique assignment types.
-    $types = $DB->get_records_sql('SELECT
-                                       plugin AS assignmenttype,
-                                       value AS version
-                                   FROM {config_plugins}
-                                   WHERE
-                                       name = ? AND
-                                       plugin LIKE ?', array('version', 'assignment_%'));
-
-    $upgradabletypes = array();
-
-    foreach ($types as $assignment) {
-        $shorttype = substr($assignment->assignmenttype, strlen('assignment_'));
-        if (assign::can_upgrade_assignment($shorttype, $assignment->version)) {
-            $upgradabletypes[] = $shorttype;
-        }
-    }
-
-    list($sql, $params) = $DB->get_in_or_equal($upgradabletypes);
-
-    $records = $DB->get_records_sql('SELECT id from {assignment} where assignmenttype ' . $sql, $params);
-    $ids = array();
-    foreach ($records as $record) {
-        $ids[] = $record->id;
-    }
-
-    return $ids;
-}
-
-
-/**
- * Upgrade a single assignment. This is used by both upgrade single and upgrade batch
- *
- * @param int $assignmentid - The assignment id to upgrade
- * @return array(string, boolean, string) -
- *                  The array contains
- *                      - the assignment summary (returned by tool_assignmentupgrade_get_assignment)
- *                      - success
- *                      - the upgrade log
- */
-function tool_assignmentupgrade_upgrade_assignment($assignmentid) {
-    global $CFG;
-    require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
-
-    $assignment_upgrader = new assign_upgrade_manager();
-    $info = tool_assignmentupgrade_get_assignment($assignmentid);
-    if ($info) {
-        $log = '';
-        $success = $assignment_upgrader->upgrade_assignment($assignmentid, $log);
-    } else {
-        $success = false;
-        $log = get_string('assignmentnotfound', 'tool_assignmentupgrade', $assignmentid);
-        $info = new stdClass();
-        $info->name = get_string('unknown', 'tool_assignmentupgrade');
-        $info->shortname = get_string('unknown', 'tool_assignmentupgrade');
-    }
-
-    return array($info, $success, $log);
-}
-
-/**
- * Get the information about a assignment to be upgraded.
- * @param int $assignmentid the assignment id.
- * @return stdClass the information about that assignment.
- */
-function tool_assignmentupgrade_get_assignment($assignmentid) {
-    global $DB;
-    return $DB->get_record_sql("
-            SELECT a.id, a.name, c.shortname, c.id AS courseid
-            FROM {assignment} a
-            JOIN {course} c ON c.id = a.course
-            WHERE a.id = ?", array($assignmentid));
-}
-
diff --git a/admin/tool/assignmentupgrade/module.js b/admin/tool/assignmentupgrade/module.js
deleted file mode 100644 (file)
index ab7f670..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-M.tool_assignmentupgrade = {
-    init_upgrade_table: function(Y) {
-
-        Y.use('node', function(Y) {
-            checkboxes = Y.all('td.c0 input');
-            checkboxes.each(function(node) {
-                node.on('change', function(e) {
-                    rowelement = e.currentTarget.get('parentNode').get('parentNode');
-                    if (e.currentTarget.get('checked')) {
-                        rowelement.setAttribute('class', 'selectedrow');
-                    } else {
-                        rowelement.setAttribute('class', 'unselectedrow');
-                    }
-                });
-
-                rowelement = node.get('parentNode').get('parentNode');
-                if (node.get('checked')) {
-                    rowelement.setAttribute('class', 'selectedrow');
-                } else {
-                    rowelement.setAttribute('class', 'unselectedrow');
-                }
-            });
-        });
-
-        var selectall = Y.one('th.c0 input');
-        selectall.on('change', function(e) {
-            if (e.currentTarget.get('checked')) {
-                checkboxes = Y.all('td.c0 input');
-                checkboxes.each(function(node) {
-                    rowelement = node.get('parentNode').get('parentNode');
-                    node.set('checked', true);
-                    rowelement.setAttribute('class', 'selectedrow');
-                });
-            } else {
-                checkboxes = Y.all('td.c0 input');
-                checkboxes.each(function(node) {
-                    rowelement = node.get('parentNode').get('parentNode');
-                    node.set('checked', false);
-                    rowelement.setAttribute('class', 'unselectedrow');
-                });
-            }
-        });
-
-        var upgradeselectedbutton = Y.one('#id_upgradeselected');
-        upgradeselectedbutton.on('click', function(e) {
-            checkboxes = Y.all('td.c0 input');
-            var selectedassignments = [];
-            checkboxes.each(function(node) {
-                if (node.get('checked')) {
-                    selectedassignments[selectedassignments.length] = node.get('value');
-                }
-            });
-
-            operation = Y.one('#id_operation');
-            assignmentsinput = Y.one('input.selectedassignments');
-            assignmentsinput.set('value', selectedassignments.join(','));
-            if (selectedassignments.length == 0) {
-                alert(M.util.get_string('noassignmentsselected', 'tool_assignmentupgrade'));
-                e.preventDefault();
-            }
-        });
-
-        var perpage = Y.one('#id_perpage');
-        perpage.on('change', function(e) {
-            window.onbeforeunload = null;
-            Y.one('.tool_assignmentupgrade_paginationform form').submit();
-        });
-
-    }
-}
diff --git a/admin/tool/assignmentupgrade/paginationform.php b/admin/tool/assignmentupgrade/paginationform.php
deleted file mode 100644 (file)
index e4c9028..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file contains the forms to create and edit an instance of this module
- *
- * @package   tool_assignmentupgrade
- * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
-
-require_once($CFG->libdir.'/formslib.php');
-
-/**
- * Assignment upgrade table display options
- *
- * @package   tool_assignmentupgrade
- * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_pagination_form extends moodleform {
-    /**
-     * Define this form - called from the parent constructor
-     */
-    public function definition() {
-        $mform = $this->_form;
-        $instance = $this->_customdata;
-
-        $mform->addElement('header', 'general', get_string('assignmentsperpage', 'tool_assignmentupgrade'));
-        // Visible elements.
-        $options = array(10=>'10', 20=>'20', 50=>'50', 100=>'100');
-        $mform->addElement('select', 'perpage', get_string('assignmentsperpage', 'assign'), $options);
-
-        // Hidden params.
-        $mform->addElement('hidden', 'action', 'saveoptions');
-        $mform->setType('action', PARAM_ALPHA);
-
-        // Buttons.
-        $this->add_action_buttons(false, get_string('updatetable', 'tool_assignmentupgrade'));
-    }
-}
-
diff --git a/admin/tool/assignmentupgrade/renderer.php b/admin/tool/assignmentupgrade/renderer.php
deleted file mode 100644 (file)
index 2811232..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-<?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/>.
-
-/**
- * Defines the renderer for the assignment upgrade helper plugin.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Renderer for the assignment upgrade helper plugin.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_renderer extends plugin_renderer_base {
-
-    /**
-     * Render the index page.
-     * @param string $detected information about what sort of site was detected.
-     * @param array $actions list of actions to show on this page.
-     * @return string html to output.
-     */
-    public function index_page($detected, array $actions) {
-        $output = '';
-        $output .= $this->header();
-        $output .= $this->heading(get_string('pluginname', 'tool_assignmentupgrade'));
-        $output .= $this->box($detected);
-        $output .= html_writer::start_tag('ul');
-        foreach ($actions as $action) {
-            $output .= html_writer::tag('li',
-                    html_writer::link($action->url, $action->name) . ' - ' .
-                    $action->description);
-        }
-        $output .= html_writer::end_tag('ul');
-        $output .= $this->footer();
-        return $output;
-    }
-
-    /**
-     * Render a page that is just a simple message.
-     * @param string $message the message to display.
-     * @return string html to output.
-     */
-    public function simple_message_page($message) {
-        $output = '';
-        $output .= $this->header();
-        $output .= $this->heading($message);
-        $output .= $this->back_to_index();
-        $output .= $this->footer();
-        return $output;
-    }
-
-    /**
-     * Render the confirm batch operation page
-     * @param stdClass $data Submitted form data with list of assignments to upgrade
-     * @return string html to output.
-     */
-    public function confirm_batch_operation_page(stdClass $data) {
-        $output = '';
-        $output .= $this->header();
-
-        $output .= $this->heading(get_string('confirmbatchupgrade', 'tool_assignmentupgrade'));
-        $output .= $this->output->spacer(array(), true);
-
-        $output .= $this->container_start('tool_assignmentupgrade_confirmbatch');
-
-        $output .= $this->render(new tool_assignmentupgrade_batchoperationconfirm($data));
-        $output .= $this->container_end();
-
-        $output .= $this->back_to_index();
-        $output .= $this->footer();
-        return $output;
-    }
-
-    /**
-     * Render the confirm batch continue / cancel links
-     * @param tool_assignmentupgrade_batchoperationconfirm $confirm Wrapper class to determine the continue message and url
-     * @return string html to output.
-     */
-    public function render_tool_assignmentupgrade_batchoperationconfirm(tool_assignmentupgrade_batchoperationconfirm $confirm) {
-        $output = '';
-
-        if ($confirm->continueurl) {
-            $output .= $this->output->confirm($confirm->continuemessage,
-                                              $confirm->continueurl,
-                                              tool_assignmentupgrade_url('listnotupgraded'));
-        } else {
-            $output .= $this->output->box($confirm->continuemessage);
-            $output .= $this->output->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
-        }
-        return $output;
-    }
-
-    /**
-     * Render the list of assignments that still need to be upgraded page.
-     * @param tool_assignmentupgrade_assignments_table $assignments of data about assignments.
-     * @param tool_assignmentupgrade_batchoperations_form $batchform Submitted form with list of assignments to upgrade
-     * @param tool_assignmentupgrade_pagination_form $paginationform Form which contains the preferences for paginating the table
-     * @return string html to output.
-     */
-    public function assignment_list_page(tool_assignmentupgrade_assignments_table $assignments,
-                                         tool_assignmentupgrade_batchoperations_form $batchform,
-                                         tool_assignmentupgrade_pagination_form $paginationform) {
-        $output = '';
-        $output .= $this->header();
-        $this->page->requires->js_init_call('M.tool_assignmentupgrade.init_upgrade_table', array());
-        $this->page->requires->string_for_js('noassignmentsselected', 'tool_assignmentupgrade');
-
-        $output .= $this->heading(get_string('notupgradedtitle', 'tool_assignmentupgrade'));
-        $output .= $this->box(get_string('notupgradedintro', 'tool_assignmentupgrade'));
-        $output .= $this->output->spacer(array(), true);
-
-        $output .= $this->container_start('tool_assignmentupgrade_upgradetable');
-
-        $output .= $this->container_start('tool_assignmentupgrade_paginationform');
-        $output .= $this->moodleform($paginationform);
-        $output .= $this->container_end();
-
-        $output .= $this->flexible_table($assignments, $assignments->get_rows_per_page(), true);
-        $output .= $this->container_end();
-
-        if ($assignments->anyupgradableassignments) {
-            $output .= $this->container_start('tool_assignmentupgrade_batchform');
-            $output .= $this->moodleform($batchform);
-            $output .= $this->container_end();
-        }
-
-        $output .= $this->back_to_index();
-        $output .= $this->footer();
-        return $output;
-    }
-
-    /**
-     * Render the result of an assignment conversion
-     * @param stdClass $assignmentsummary data about the assignment to upgrade.
-     * @param bool $success Set to true if the outcome of the conversion was a success
-     * @param string $log The log from the conversion
-     * @return string html to output.
-     */
-    public function convert_assignment_result($assignmentsummary, $success, $log) {
-        $output = '';
-
-        $output .= $this->container_start('tool_assignmentupgrade_result');
-        $output .= $this->container(get_string('upgradeassignmentsummary', 'tool_assignmentupgrade', $assignmentsummary));
-        if (!$success) {
-            $output .= $this->container(get_string('conversionfailed', 'tool_assignmentupgrade', $log));
-        } else {
-            $output .= $this->container(get_string('upgradeassignmentsuccess', 'tool_assignmentupgrade'));
-            $url = new moodle_url('/course/view.php', array('id'=>$assignmentsummary->courseid));
-            $output .= $this->container(html_writer::link($url, get_string('viewcourse', 'tool_assignmentupgrade')));
-        }
-        $output .= $this->container_end();
-
-        return $output;
-    }
-
-    /**
-     * Render the are-you-sure page to confirm a manual upgrade.
-     * @param stdClass $assignmentsummary data about the assignment to upgrade.
-     * @return string html to output.
-     */
-    public function convert_assignment_are_you_sure($assignmentsummary) {
-        $output = '';
-        $output .= $this->header();
-        $output .= $this->heading(get_string('areyousure', 'tool_assignmentupgrade'));
-
-        $params = array('id' => $assignmentsummary->id, 'confirmed' => 1, 'sesskey' => sesskey());
-        $output .= $this->confirm(get_string('areyousuremessage', 'tool_assignmentupgrade', $assignmentsummary),
-                new single_button(tool_assignmentupgrade_url('upgradesingle', $params), get_string('yes')),
-                tool_assignmentupgrade_url('listnotupgraded'));
-
-        $output .= $this->footer();
-        return $output;
-    }
-
-    /**
-     * Helper method dealing with the fact we can not just fetch the output of flexible_table
-     *
-     * @param flexible_table $table
-     * @param int $rowsperpage
-     * @param bool $displaylinks Show links in the table
-     * @return string HTML
-     */
-    protected function flexible_table(flexible_table $table, $rowsperpage, $displaylinks) {
-
-        $o = '';
-        ob_start();
-        $table->out($rowsperpage, $displaylinks);
-        $o = ob_get_contents();
-        ob_end_clean();
-
-        return $o;
-    }
-
-    /**
-     * Helper method dealing with the fact we can not just fetch the output of moodleforms
-     *
-     * @param moodleform $mform
-     * @return string HTML
-     */
-    protected function moodleform(moodleform $mform) {
-
-        $o = '';
-        ob_start();
-        $mform->display();
-        $o = ob_get_contents();
-        ob_end_clean();
-
-        return $o;
-    }
-
-
-    /**
-     * Render a link in a div, such as the 'Back to plugin main page' link.
-     * @param string|moodle_url $url the link URL.
-     * @param string $text the link text.
-     * @return string html to output.
-     */
-    public function end_of_page_link($url, $text) {
-        return html_writer::tag('div', html_writer::link($url, $text), array('class' => 'mdl-align'));
-    }
-
-    /**
-     * Output a link back to the plugin index page.
-     * @return string html to output.
-     */
-    public function back_to_index() {
-        return $this->end_of_page_link(tool_assignmentupgrade_url('index'), get_string('backtoindex', 'tool_assignmentupgrade'));
-    }
-}
diff --git a/admin/tool/assignmentupgrade/settings.php b/admin/tool/assignmentupgrade/settings.php
deleted file mode 100644 (file)
index 1a91922..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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/>.
-
-/**
- * Adds this plugin to the admin menu.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die;
-
-if ($hassiteconfig) {
-    // Needs this condition or there is error on login page.
-    $ADMIN->add('root', new admin_externalpage('assignmentupgrade',
-            get_string('pluginname', 'tool_assignmentupgrade'),
-            new moodle_url('/admin/tool/assignmentupgrade/index.php')));
-}
diff --git a/admin/tool/assignmentupgrade/styles.css b/admin/tool/assignmentupgrade/styles.css
deleted file mode 100644 (file)
index bdb9fa5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable .c0 {
-    display: none;
-}
-
-#page-admin-tool-assignmentupgrade-listnotupgraded.jsenabled .tool_assignmentupgrade_upgradetable .c0 {
-    display: table-cell;
-}
-/*
-.gradingbatchoperationsform { display: none; }
-.jsenabled .gradingbatchoperationsform { display: block; }
-*/
-
-#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable tr.selectedrow td {
-    background-color: #fec;
-}
-
-#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable tr.unselectedrow td {
-    background-color: white;
-}
-
-#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_paginationform .hidden {
-    display: none;
-}
diff --git a/admin/tool/assignmentupgrade/tests/privacy_test.php b/admin/tool/assignmentupgrade/tests/privacy_test.php
deleted file mode 100644 (file)
index d22770c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Privacy tests for tool_assignmentupgrade.
- *
- * @package    tool_assignmentupgrade
- * @category   test
- * @copyright  2018 Zig Tan <zig@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-use \core_privacy\tests\provider_testcase;
-use \core_privacy\local\request\writer;
-use \tool_assignmentupgrade\privacy\provider;
-
-/**
- * Unit tests for tool_assignmentupgrade/classes/privacy/policy
- *
- * @copyright  2018 Zig Tan <zig@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_privacy_testcase extends provider_testcase {
-
-    /**
-     * Overriding setUp() function to always reset after tests.
-     */
-    public function setUp() {
-        $this->resetAfterTest(true);
-    }
-
-    /**
-     * Test for provider::test_export_user_preferences().
-     */
-    public function test_export_user_preferences() {
-        // Test setup.
-        $user = $this->getDataGenerator()->create_user();
-        $this->setUser($user);
-
-        // Add a user home page preference for the User.
-        set_user_preference('tool_assignmentupgrade_perpage', '100', $user);
-
-        // Test the user preference exists.
-        $params = [
-            'userid' => $user->id,
-            'name' => 'tool_assignmentupgrade_perpage'
-        ];
-
-        // Test the user preferences export contains 1 user preference record for the User.
-        provider::export_user_preferences($user->id);
-        $contextuser = context_user::instance($user->id);
-        $writer = writer::with_context($contextuser);
-        $this->assertTrue($writer->has_any_data());
-
-        $exportedpreferences = $writer->get_user_preferences('tool_assignmentupgrade');
-        $this->assertCount(1, (array) $exportedpreferences);
-        $this->assertEquals('100', $exportedpreferences->perpage->value);
-    }
-
-}
diff --git a/admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php b/admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php
deleted file mode 100644 (file)
index 291329d..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file contains the forms to create and edit an instance of this module
- *
- * @package   tool_assignmentupgrade
- * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
-
-require_once($CFG->libdir.'/formslib.php');
-
-/**
- * Assignment upgrade batch operations form.
- *
- * @package   tool_assignmentupgrade
- * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_batchoperations_form extends moodleform {
-    /**
-     * Define this form - is called from parent constructor.
-     */
-    public function definition() {
-        $mform = $this->_form;
-        $instance = $this->_customdata;
-
-        $mform->addElement('header', 'general', get_string('batchoperations', 'tool_assignmentupgrade'));
-        // Visible elements.
-        $mform->addElement('hidden', 'selectedassignments', '', array('class'=>'selectedassignments'));
-        $mform->setType('selectedassignments', PARAM_SEQUENCE);
-
-        $mform->addElement('submit', 'upgradeselected', get_string('upgradeselected', 'tool_assignmentupgrade'));
-        $mform->addElement('submit', 'upgradeall', get_string('upgradeall', 'tool_assignmentupgrade'));
-    }
-
-}
-
diff --git a/admin/tool/assignmentupgrade/upgradableassignmentstable.php b/admin/tool/assignmentupgrade/upgradableassignmentstable.php
deleted file mode 100644 (file)
index a7851b9..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file contains the definition for the grading table which subclassses easy_table
- *
- * @package   tool_assignmentupgrade
- * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-require_once($CFG->libdir.'/tablelib.php');
-require_once($CFG->libdir.'/gradelib.php');
-require_once($CFG->dirroot.'/mod/assign/locallib.php');
-
-/**
- * Extends table_sql to provide a table of assignment submissions
- *
- * @package   tool_assignmentupgrade
- * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_assignmentupgrade_assignments_table extends table_sql implements renderable {
-
-    /** @var int $perpage */
-    private $perpage = 10;
-    /** @var int $rownum (global index of current row in table) */
-    private $rownum = -1;
-    /** @var renderer_base for getting output */
-    private $output = null;
-    /** @var boolean anyupgradableassignments - True if there is one or more assignments that can upgraded */
-    public $anyupgradableassignments = false;
-
-    /**
-     * This table loads a list of the old assignment instances and tests them to see
-     * if they can be upgraded
-     *
-     * @param int $perpage How many per page
-     * @param int $rowoffset The starting row for pagination
-     */
-    public function __construct($perpage, $rowoffset=0) {
-        global $PAGE;
-        parent::__construct('tool_assignmentupgrade_assignments');
-        $this->perpage = $perpage;
-        $this->output = $PAGE->get_renderer('tool_assignmentupgrade');
-
-        $this->define_baseurl(new moodle_url('/admin/tool/assignmentupgrade/listnotupgraded.php'));
-
-        $this->anyupgradableassignments = tool_assignmentupgrade_any_upgradable_assignments();
-
-        // Do some business - then set the sql.
-        if ($rowoffset) {
-            $this->rownum = $rowoffset - 1;
-        }
-
-        $fields = 'a.id as id,
-                   a.name as name,
-                   a.assignmenttype as type,
-                   c.shortname as courseshortname,
-                   c.id as courseid,
-                   COUNT(s.id) as submissioncount';
-        $from = '{assignment} a JOIN {course} c ON a.course = c.id ' .
-                        ' LEFT JOIN {assignment_submissions} s ON a.id = s.assignment';
-
-        $where = '1 = 1';
-        $where .= ' GROUP BY a.id, a.name, a.assignmenttype, c.shortname, c.id ';
-
-        $this->set_sql($fields, $from, $where, array());
-        $this->set_count_sql('SELECT COUNT(*) FROM {assignment} a JOIN {course} c ON a.course = c.id', array());
-
-        $columns = array();
-        $headers = array();
-
-        $columns[] = 'select';
-        $headers[] = get_string('select', 'tool_assignmentupgrade') .
-                     '<div class="selectall">' .
-                     '<input type="checkbox" name="selectall" title="' . get_string('selectall') . '"/>' .
-                     '</div>';
-        $columns[] = 'upgradable';
-        $headers[] = get_string('upgradable', 'tool_assignmentupgrade');
-        $columns[] = 'id';
-        $headers[] = get_string('assignmentid', 'tool_assignmentupgrade');
-        $columns[] = 'courseshortname';
-        $headers[] = get_string('course');
-        $columns[] = 'name';
-        $headers[] = get_string('name');
-        $columns[] = 'type';
-        $headers[] = get_string('assignmenttype', 'tool_assignmentupgrade');
-        $columns[] = 'submissioncount';
-        $headers[] = get_string('submissions', 'tool_assignmentupgrade');
-
-        // Set the columns.
-        $this->define_columns($columns);
-        $this->define_headers($headers);
-        $this->no_sorting('upgradable');
-        $this->no_sorting('select');
-    }
-
-    /**
-     * Return the number of rows to display on a single page
-     *
-     * @return int The number of rows per page
-     */
-    public function get_rows_per_page() {
-        return $this->perpage;
-    }
-
-    /**
-     * Format a link to the assignment instance
-     *
-     * @param stdClass $row
-     * @return string
-     */
-    public function col_name(stdClass $row) {
-        $url = new moodle_url('/mod/assignment/view.php', array('a' => $row->id));
-        return html_writer::link($url, $row->name);
-    }
-
-
-    /**
-     * Format a link to the upgrade single tool
-     *
-     * @param stdClass $row (contains cached result from previous upgradable check)
-     * @return string
-     */
-    public function col_upgradable(stdClass $row) {
-        if ($row->upgradable) {
-            $urlparams = array('id' => $row->id, 'sesskey' => sesskey());
-            $url = new moodle_url('/admin/tool/assignmentupgrade/upgradesingleconfirm.php', $urlparams);
-            return html_writer::link($url, get_string('supported', 'tool_assignmentupgrade'));
-        } else {
-            return get_string('notsupported', 'tool_assignmentupgrade');
-        }
-    }
-
-    /**
-     * Insert a checkbox for selecting the current row for batch operations
-     *
-     * @param stdClass $row
-     * @return string
-     */
-    public function col_select(stdClass $row) {
-        global $CFG;
-        $version = get_config('assignment_' . $row->type, 'version');
-        require_once($CFG->dirroot . '/mod/assign/locallib.php');
-        if (assign::can_upgrade_assignment($row->type, $version)) {
-            $row->upgradable = true;
-            return '<input type="checkbox" name="selectedassignment" value="' . $row->id . '"/>';
-        }
-        $row->upgradable = false;
-        return '';
-    }
-
-    /**
-     * Override the table show_hide_link to not show for select column
-     *
-     * @param string $column the column name, index into various names.
-     * @param int $index numerical index of the column.
-     * @return string HTML fragment.
-     */
-    protected function show_hide_link($column, $index) {
-        if ($index > 0) {
-            return parent::show_hide_link($column, $index);
-        }
-        return '';
-    }
-}
diff --git a/admin/tool/assignmentupgrade/upgradesingle.php b/admin/tool/assignmentupgrade/upgradesingle.php
deleted file mode 100644 (file)
index 8a1f78d..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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/>.
-
-/**
- * Script to show all the assignments that have not been upgraded after the main upgrade.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require_once(__DIR__ . '/../../../config.php');
-require_once($CFG->libdir . '/adminlib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/locallib.php');
-
-require_sesskey();
-
-$assignmentid = required_param('id', PARAM_INT);
-
-// This calls require_login and checks moodle/site:config.
-admin_externalpage_setup('assignmentupgrade',
-                         '',
-                         array(),
-                         tool_assignmentupgrade_url('upgradesingle', array('id' => $assignmentid)));
-
-$PAGE->navbar->add(get_string('upgradesingle', 'tool_assignmentupgrade'));
-$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
-
-$log = '';
-list($summary, $success, $log) = tool_assignmentupgrade_upgrade_assignment($assignmentid);
-
-echo $renderer->header();
-echo $renderer->heading(get_string('conversioncomplete', 'tool_assignmentupgrade'));
-echo $renderer->convert_assignment_result($summary, $success, $log);
-echo $renderer->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
-echo $renderer->footer();
diff --git a/admin/tool/assignmentupgrade/upgradesingleconfirm.php b/admin/tool/assignmentupgrade/upgradesingleconfirm.php
deleted file mode 100644 (file)
index 3c41fd9..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?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/>.
-
-/**
- * Script to show all the assignments that have not been upgraded after the main upgrade.
- *
- * @package    tool_assignmentupgrade
- * @copyright  2012 NetSpot
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require_once(__DIR__ . '/../../../config.php');
-require_once($CFG->libdir . '/adminlib.php');
-require_once($CFG->dirroot . '/'.$CFG->admin.'/tool/assignmentupgrade/locallib.php');
-
-require_sesskey();
-
-$assignmentid = required_param('id', PARAM_INT);
-
-// This calls require_login and checks moodle/site:config.
-admin_externalpage_setup('assignmentupgrade',
-                         '',
-                         array(),
-                         tool_assignmentupgrade_url('upgradesingle', array('id' => $assignmentid)));
-
-$PAGE->navbar->add(get_string('upgradesingle', 'tool_assignmentupgrade'));
-$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
-
-$assignmentinfo = tool_assignmentupgrade_get_assignment($assignmentid);
-
-echo $renderer->convert_assignment_are_you_sure($assignmentinfo);
index 97b5b20..8fef761 100644 (file)
@@ -20,7 +20,7 @@ Feature: Manage availability conditions
     And the following config values are set as admin:
       | enableavailability | 1 |
     And I am on homepage
-    And I navigate to "Manage restrictions" node in "Site administration > Plugins > Availability restrictions"
+    And I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration
 
     # Having clicked on it, I should also see the list of plugins.
     And I should see "Restriction by date"
@@ -34,7 +34,7 @@ Feature: Manage availability conditions
       | Course 1 | C1        | topics |
     And I log in as "admin"
     And I am on site homepage
-    When I navigate to "Manage restrictions" node in "Site administration > Plugins > Availability restrictions"
+    When I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration
 
     # Check the icon is there (it should be a Hide icon, meaning is currently visible).
     Then "Hide" "icon" should exist in the "Restriction by date" "table_row"
index 0e62104..f078fab 100644 (file)
@@ -286,7 +286,7 @@ Feature: Set up contextual data for tests
       | student1 | CHSB   |
       | student1 | CHC    |
     When I log in as "admin"
-    And I navigate to "Cohorts" node in "Site administration > Users > Accounts"
+    And I navigate to "Users > Accounts > Cohorts" in site administration
     Then the following should exist in the "cohorts" table:
       | Name            | Cohort size |
       | System cohort A | 1           |
index 62a6fe8..9ade5e0 100644 (file)
@@ -34,7 +34,7 @@ Feature: Verify that all form fields values can be get and set
       | wiki | C1 | wiki1 | Test this one | Test this one | Test this one | collaborative | 0 |
     And I log in as "admin"
     And I am on "Course 1" course homepage
-    And I navigate to "Reset" node in "Course administration"
+    And I navigate to "Reset" in current page administration
     # Select (multi-select) - Checking "the select box should contain".
     And I expand all fieldsets
     And the "Unenrol users" select box should contain "No roles"
@@ -131,12 +131,12 @@ Feature: Verify that all form fields values can be get and set
     And the field "two" matches value ""
     # Check if field xpath set/match works.
     And I am on "Course 1" course homepage
-    And I navigate to "Edit settings" node in "Course administration"
+    And I navigate to "Edit settings" in current page administration
     And I set the field with xpath "//input[@id='id_idnumber']" to "Course id number"
     And the field with xpath "//input[@name='idnumber']" matches value "Course id number"
     And the field with xpath "//input[@name='idnumber']" does not match value ""
     And I press "Save and display"
-    And I navigate to "Edit settings" node in "Course administration"
+    And I navigate to "Edit settings" in current page administration
     And the field "Course ID number" matches value "Course id number"
 
   Scenario: with JS disabled all form fields getters and setters works as expected
index 708a0e7..2df5363 100644 (file)
@@ -7,7 +7,7 @@ Feature: List the system steps definitions
   Background:
     Given I am on homepage
     And I log in as "admin"
-    And I navigate to "Acceptance testing" node in "Site administration > Development"
+    And I navigate to "Development > Acceptance testing" in site administration
 
   @javascript
   Scenario: Accessing the list
diff --git a/admin/tool/behat/tests/manager_test.php b/admin/tool/behat/tests/manager_test.php
deleted file mode 100644 (file)
index 7b88c80..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-<?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/>.
-
-/**
- * Unit tests for behat manager.
- *
- * @package   tool_behat
- * @copyright  2012 David Monllaó
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-global $CFG;
-require_once($CFG->dirroot . '/' . $CFG->admin .'/tool/behat/locallib.php');
-require_once($CFG->libdir . '/behat/classes/util.php');
-require_once($CFG->libdir . '/behat/classes/behat_config_manager.php');
-
-/**
- * Behat manager tests.
- *
- * @package    tool_behat
- * @copyright  2012 David Monllaó
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class tool_behat_manager_testcase extends advanced_testcase {
-
-    /**
-     * behat_config_manager tests.
-     */
-    public function test_merge_configs() {
-
-        // Simple default config.
-        $array1 = array(
-            'the' => 'same',
-            'simple' => 'value',
-            'array' => array(
-                'one' => 'arrayvalue1',
-                'two' => 'arrayvalue2'
-            )
-        );
-
-        // Simple override.
-        $array2 = array(
-            'simple' => 'OVERRIDDEN1',
-            'array' => array(
-                'one' => 'OVERRIDDEN2'
-            ),
-            'newprofile' => array(
-                'anotherlevel' => array(
-                    'andanotherone' => array(
-                        'list1',
-                        'list2'
-                    )
-                )
-            )
-        );
-
-        $array = testable_behat_config_manager::merge_config($array1, $array2);
-        $this->assertDebuggingCalled("Use of merge_config is deprecated, please see behat_config_util");
-
-        // Overrides are applied.
-        $this->assertEquals('OVERRIDDEN1', $array['simple']);
-        $this->assertEquals('OVERRIDDEN2', $array['array']['one']);
-
-        // Other values are respected.
-        $this->assertNotEmpty($array['array']['two']);
-
-        // Completely new nodes are added.
-        $this->assertNotEmpty($array['newprofile']);
-        $this->assertNotEmpty($array['newprofile']['anotherlevel']['andanotherone']);
-        $this->assertEquals('list1', $array['newprofile']['anotherlevel']['andanotherone'][0]);
-        $this->assertEquals('list2', $array['newprofile']['anotherlevel']['andanotherone'][1]);
-
-        // Complex override changing vectors to scalars and scalars to vectors.
-        $array2 = array(
-            'simple' => array(
-                'simple' => 'should',
-                'be' => 'overridden',
-                'by' => 'this-array'
-            ),
-            'array' => 'one'
-        );
-
-        $array = testable_behat_config_manager::merge_config($array1, $array2);
-        $this->assertDebuggingCalled("Use of merge_config is deprecated, please see behat_config_util");
-
-        // Overrides applied.
-        $this->assertNotEmpty($array['simple']);
-        $this->assertNotEmpty($array['array']);
-        $this->assertTrue(is_array($array['simple']));
-        $this->assertFalse(is_array($array['array']));
-
-        // Other values are maintained.
-        $this->assertEquals('same', $array['the']);
-    }
-
-    /**
-     * behat_config_manager tests.
-     */
-    public function test_config_file_contents() {
-        global $CFG;
-
-        $this->resetAfterTest(true);
-
-        // Skip tests if behat is not installed.
-        $vendorpath = $CFG->dirroot . '/vendor';
-        if (!file_exists($vendorpath . '/autoload.php') || !is_dir($vendorpath . '/behat')) {
-            $this->markTestSkipped('Behat not installed.');
-        }
-
-        // Add some fake test url.
-        $CFG->behat_wwwroot = 'http://example.com/behat';
-
-        // To avoid user value at config.php level.
-        unset($CFG->behat_config);
-
-        // List.
-        $features = array(
-            'feature1',
-            'feature2',
-            'feature3'
-        );
-
-        // Associative array.
-        $stepsdefinitions = array(
-            'micarro' => '/me/lo/robaron',
-            'anoche' => '/cuando/yo/dormia'
-        );
-
-        $contents = testable_behat_config_manager::get_config_file_contents($features, $stepsdefinitions);
-        $this->assertDebuggingCalled("Use of get_config_file_contents is deprecated, please see behat_config_util");
-
-        // YAML decides when is is necessary to wrap strings between single quotes, so not controlled
-        // values like paths should not be asserted including the key name as they would depend on the
-        // directories values.
-        $this->assertContains($CFG->dirroot,
-            $contents['default']['extensions']['Moodle\BehatExtension']['moodledirroot']);
-
-        // Not quoted strings.
-        $this->assertEquals('/me/lo/robaron',
-            $contents['default']['extensions']['Moodle\BehatExtension']['steps_definitions']['micarro']);
-
-        // YAML uses single quotes to wrap URL strings.
-        $this->assertEquals($CFG->behat_wwwroot, $contents['default']['extensions']['Behat\MinkExtension']['base_url']);
-
-        // Lists.
-        $this->assertEquals('feature1', $contents['default']['suites']['default']['paths'][0]);
-        $this->assertEquals('feature3', $contents['default']['suites']['default']['paths'][2]);
-
-        unset($CFG->behat_wwwroot);
-    }
-
-}
-
-/**
- * Allows access to internal methods without exposing them.
- *
- * @package    tool_behat
- * @copyright  2012 David Monllaó
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class testable_behat_config_manager extends behat_config_manager {
-
-    /**
-     * Allow access to protected method
-     * @see parent::merge_config()
-     * @param mixed $config
-     * @param mixed $localconfig
-     * @return mixed
-     */
-    public static function merge_config($config, $localconfig) {
-        return parent::merge_config($config, $localconfig);
-    }
-
-    /**
-     * Allow access to protected method
-     * @see parent::get_config_file_contents()
-     * @param array $features
-     * @param array $stepsdefinitions
-     * @return string
-     */
-    public static function get_config_file_contents($features, $stepsdefinitions) {
-        return parent::get_config_file_contents($features, $stepsdefinitions);
-    }
-}
index 9aaea28..6ee9707 100644 (file)
@@ -76,7 +76,7 @@ class api {
     /** The request is now being processed. */
     const DATAREQUEST_STATUS_PROCESSING = 4;
 
-    /** Data request completed. */
+    /** Information/other request completed. */
     const DATAREQUEST_STATUS_COMPLETE = 5;
 
     /** Data request cancelled by the user. */
@@ -85,6 +85,15 @@ class api {
     /** Data request rejected by the DPO. */
     const DATAREQUEST_STATUS_REJECTED = 7;
 
+    /** Data request download ready. */
+    const DATAREQUEST_STATUS_DOWNLOAD_READY = 8;
+
+    /** Data request expired. */
+    const DATAREQUEST_STATUS_EXPIRED = 9;
+
+    /** Data delete request completed, account is removed. */
+    const DATAREQUEST_STATUS_DELETED = 10;
+
     /**
      * Determines whether the user can contact the site's Data Protection Officer via Moodle.
      *
@@ -127,6 +136,25 @@ class api {
         require_capability('tool/dataprivacy:managedataregistry', $context);
     }
 
+    /**
+     * Fetches the role shortnames of Data Protection Officer roles.
+     *
+     * @return array An array of the DPO role shortnames
+     */
+    public static function get_dpo_role_names() : array {
+        global $DB;
+
+        $dporoleids = explode(',', str_replace(' ', '', get_config('tool_dataprivacy', 'dporoles')));
+        $dponames = array();
+
+        if (!empty($dporoleids)) {
+            list($insql, $inparams) = $DB->get_in_or_equal($dporoleids);
+            $dponames = $DB->get_fieldset_select('role', 'shortname', "id {$insql}", $inparams);
+        }
+
+        return $dponames;
+    }
+
     /**
      * Fetches the list of users with the Data Protection Officer role.
      *
@@ -300,6 +328,18 @@ class api {
             }
         }
 
+        // If any are due to expire, expire them and re-fetch updated data.
+        if (empty($statuses)
+                || in_array(self::DATAREQUEST_STATUS_DOWNLOAD_READY, $statuses)
+                || in_array(self::DATAREQUEST_STATUS_EXPIRED, $statuses)) {
+            $expiredrequests = data_request::get_expired_requests($userid);
+
+            if (!empty($expiredrequests)) {
+                data_request::expire($expiredrequests);
+                $results = self::get_data_requests($userid, $statuses, $types, $sort, $offset, $limit);
+            }
+        }
+
         return $results;
     }
 
@@ -381,6 +421,9 @@ class api {
             self::DATAREQUEST_STATUS_COMPLETE,
             self::DATAREQUEST_STATUS_CANCELLED,
             self::DATAREQUEST_STATUS_REJECTED,
+            self::DATAREQUEST_STATUS_DOWNLOAD_READY,
+            self::DATAREQUEST_STATUS_EXPIRED,
+            self::DATAREQUEST_STATUS_DELETED,
         ];
         list($insql, $inparams) = $DB->get_in_or_equal($nonpendingstatuses, SQL_PARAMS_NAMED);
         $select = 'type = :type AND userid = :userid AND status NOT ' . $insql;
@@ -404,6 +447,9 @@ class api {
             self::DATAREQUEST_STATUS_COMPLETE,
             self::DATAREQUEST_STATUS_CANCELLED,
             self::DATAREQUEST_STATUS_REJECTED,
+            self::DATAREQUEST_STATUS_DOWNLOAD_READY,
+            self::DATAREQUEST_STATUS_EXPIRED,
+            self::DATAREQUEST_STATUS_DELETED,
         ];
 
         return !in_array($status, $finalstatuses);
@@ -608,6 +654,54 @@ class api {
         return has_capability('tool/dataprivacy:makedatarequestsforchildren', $usercontext, $requester);
     }
 
+    /**
+     * Checks whether a user can download a data request.
+     *
+     * @param int $userid Target user id (subject of data request)
+     * @param int $requesterid Requester user id (person who requsted it)
+     * @param int|null $downloaderid Person who wants to download user id (default current)
+     * @return bool
+     * @throws coding_exception
+     */
+    public static function can_download_data_request_for_user($userid, $requesterid, $downloaderid = null) {
+        global $USER;
+
+        if (!$downloaderid) {
+            $downloaderid = $USER->id;
+        }
+
+        $usercontext = \context_user::instance($userid);
+        // If it's your own and you have the right capability, you can download it.
+        if ($userid == $downloaderid && has_capability('tool/dataprivacy:downloadownrequest', $usercontext, $downloaderid)) {
+            return true;
+        }
+        // If you can download anyone's in that context, you can download it.
+        if (has_capability('tool/dataprivacy:downloadallrequests', $usercontext, $downloaderid)) {
+            return true;
+        }
+        // If you can have the 'child access' ability to request in that context, and you are the one
+        // who requested it, then you can download it.
+        if ($requesterid == $downloaderid && self::can_create_data_request_for_user($userid, $requesterid)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Gets an action menu link to download a data request.
+     *
+     * @param \context_user $usercontext User context (of user who the data is for)
+     * @param int $requestid Request id
+     * @return \action_menu_link_secondary Action menu link
+     * @throws coding_exception
+     */
+    public static function get_download_link(\context_user $usercontext, $requestid) {
+        $downloadurl = moodle_url::make_pluginfile_url($usercontext->id,
+                'tool_dataprivacy', 'export', $requestid, '/', 'export.zip', true);
+        $downloadtext = get_string('download', 'tool_dataprivacy');
+        return new \action_menu_link_secondary($downloadurl, null, $downloadtext);
+    }
+
     /**
      * Creates a new data purpose.
      *
index 1435fcd..dbc8f32 100644 (file)
@@ -147,8 +147,8 @@ class data_registry {
 
         if ($contextcourse = $context->get_course_context(false)) {
             // Below course level we look at module or block level roles + course-assigned roles.
-            $courseroles = get_roles_with_assignment_on_context($contextcourse);
-            $roles = $courseroles + get_roles_with_assignment_on_context($context);
+            $courseroles = get_roles_used_in_context($contextcourse, false);
+            $roles = $courseroles + get_roles_used_in_context($context, false);
         } else {
             // We list category + system for others (we don't work with user instances so no need to work about them).
             $roles = get_roles_used_in_context($context);
index d5ab218..92c8c1f 100644 (file)
@@ -85,6 +85,9 @@ class data_request extends persistent {
                     api::DATAREQUEST_STATUS_COMPLETE,
                     api::DATAREQUEST_STATUS_CANCELLED,
                     api::DATAREQUEST_STATUS_REJECTED,
+                    api::DATAREQUEST_STATUS_DOWNLOAD_READY,
+                    api::DATAREQUEST_STATUS_EXPIRED,
+                    api::DATAREQUEST_STATUS_DELETED,
                 ],
                 'type' => PARAM_INT
             ],
@@ -110,4 +113,101 @@ class data_request extends persistent {
             ],
         ];
     }
+
+    /**
+     * Determines whether a completed data export request has expired.
+     * The response will be valid regardless of the expiry scheduled task having run.
+     *
+     * @param data_request $request the data request object whose expiry will be checked.
+     * @return bool true if the request has expired.
+     */
+    public static function is_expired(data_request $request) {
+        $result = false;
+
+        // Only export requests expire.
+        if ($request->get('type') == api::DATAREQUEST_TYPE_EXPORT) {
+            switch ($request->get('status')) {
+                // Expired requests are obviously expired.
+                case api::DATAREQUEST_STATUS_EXPIRED:
+                    $result = true;
+                    break;
+                // Complete requests are expired if the expiry time has elapsed.
+                case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+                    $expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry');
+                    if ($expiryseconds > 0 && time() >= ($request->get('timemodified') + $expiryseconds)) {
+                        $result = true;
+                    }
+                    break;
+            }
+        }
+
+        return $result;
+    }
+
+
+
+    /**
+     * Fetch completed data requests which are due to expire.
+     *
+     * @param int $userid Optional user ID to filter by.
+     *
+     * @return array Details of completed requests which are due to expire.
+     */
+    public static function get_expired_requests($userid = 0) {
+        global $DB;
+
+        $expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry');
+        $expirytime = strtotime("-{$expiryseconds} second");
+        $table = self::TABLE;
+        $sqlwhere = 'type = :export_type AND status = :completestatus AND timemodified <= :expirytime';
+        $params = array(
+            'export_type' => api::DATAREQUEST_TYPE_EXPORT,
+            'completestatus' => api::DATAREQUEST_STATUS_DOWNLOAD_READY,
+            'expirytime' => $expirytime,
+        );
+        $sort = 'id';
+        $fields = 'id, userid';
+
+        // Filter by user ID if specified.
+        if ($userid > 0) {
+            $sqlwhere .= ' AND (userid = :userid OR requestedby = :requestedby)';
+            $params['userid'] = $userid;
+            $params['requestedby'] = $userid;
+        }
+
+        return $DB->get_records_select_menu($table, $sqlwhere, $params, $sort, $fields, 0, 2000);
+    }
+
+    /**
+     * Expire a given set of data requests.
+     * Update request status and delete the files.
+     *
+     * @param array $expiredrequests [requestid => userid]
+     *
+     * @return void
+     */
+    public static function expire($expiredrequests) {
+        global $DB;
+
+        $ids = array_keys($expiredrequests);
+
+        if (count($ids) > 0) {
+            list($insql, $inparams) = $DB->get_in_or_equal($ids);
+            $initialparams = array(api::DATAREQUEST_STATUS_EXPIRED, time());
+            $params = array_merge($initialparams, $inparams);
+
+            $update = "UPDATE {" . self::TABLE . "}
+                          SET status = ?, timemodified = ?
+                        WHERE id $insql";
+
+            if ($DB->execute($update, $params)) {
+                $fs = get_file_storage();
+
+                foreach ($expiredrequests as $id => $userid) {
+                    $usercontext = \context_user::instance($userid);
+                    $fs->delete_area_files($usercontext->id, 'tool_dataprivacy', 'export', $id);
+                }
+            }
+        }
+    }
 }
index 93b33e3..b7d483c 100644 (file)
@@ -160,7 +160,7 @@ class data_request_exporter extends persistent_exporter {
 
         switch ($this->persistent->get('status')) {
             case api::DATAREQUEST_STATUS_PENDING:
-                $values['statuslabelclass'] = 'label-default';
+                $values['statuslabelclass'] = 'label-info';
                 // Request can be manually completed for general enquiry requests.
                 $values['canmarkcomplete'] = $requesttype == api::DATAREQUEST_TYPE_OTHERS;
                 break;
@@ -181,6 +181,8 @@ class data_request_exporter extends persistent_exporter {
                 $values['statuslabelclass'] = 'label-info';
                 break;
             case api::DATAREQUEST_STATUS_COMPLETE:
+            case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+            case api::DATAREQUEST_STATUS_DELETED:
                 $values['statuslabelclass'] = 'label-success';
                 break;
             case api::DATAREQUEST_STATUS_CANCELLED:
@@ -189,6 +191,9 @@ class data_request_exporter extends persistent_exporter {
             case api::DATAREQUEST_STATUS_REJECTED:
                 $values['statuslabelclass'] = 'label-important';
                 break;
+            case api::DATAREQUEST_STATUS_EXPIRED:
+                $values['statuslabelclass'] = 'label-default';
+                break;
         }
 
         return $values;
index f98362d..36dd93a 100644 (file)
@@ -117,6 +117,7 @@ class helper {
         if (!isset($statuses[$status])) {
             throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
         }
+
         return $statuses[$status];
     }
 
@@ -133,8 +134,11 @@ class helper {
             api::DATAREQUEST_STATUS_APPROVED => get_string('statusapproved', 'tool_dataprivacy'),
             api::DATAREQUEST_STATUS_PROCESSING => get_string('statusprocessing', 'tool_dataprivacy'),
             api::DATAREQUEST_STATUS_COMPLETE => get_string('statuscomplete', 'tool_dataprivacy'),
+            api::DATAREQUEST_STATUS_DOWNLOAD_READY => get_string('statusready', 'tool_dataprivacy'),
+            api::DATAREQUEST_STATUS_EXPIRED => get_string('statusexpired', 'tool_dataprivacy'),
             api::DATAREQUEST_STATUS_CANCELLED => get_string('statuscancelled', 'tool_dataprivacy'),
             api::DATAREQUEST_STATUS_REJECTED => get_string('statusrejected', 'tool_dataprivacy'),
+            api::DATAREQUEST_STATUS_DELETED => get_string('statusdeleted', 'tool_dataprivacy'),
         ];
     }
 
index 6282cc4..ca50157 100644 (file)
@@ -70,7 +70,8 @@ class metadata_registry {
                     $internaldata['compliant'] = false;
                 }
                 // Check to see if we are an external plugin.
-                $componentshortname = explode('_', $component);
+                // Plugin names can contain _ characters, limit to 2 to just remove initial plugintype.
+                $componentshortname = explode('_', $component, 2);
                 $shortname = array_pop($componentshortname);
                 if (isset($contributedplugins[$plugintype][$shortname])) {
                     $internaldata['external'] = true;
index d8b0644..477e503 100644 (file)
@@ -59,7 +59,7 @@ class data_requests_table extends table_sql {
     /** @var bool Whether this table is being rendered for managing data requests. */
     protected $manage = false;
 
-    /** @var stdClass[] Array of data request persistents. */
+    /** @var \tool_dataprivacy\data_request[] Array of data request persistents. */
     protected $datarequests = [];
 
     /**
@@ -206,12 +206,21 @@ class data_requests_table extends table_sql {
                 $actiontext = get_string('denyrequest', 'tool_dataprivacy');
                 $actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
                 break;
+            case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+                $userid = $data->foruser->id;
+                $usercontext = \context_user::instance($userid, IGNORE_MISSING);
+                // If user has permission to view download link, show relevant action item.
+                if ($usercontext && api::can_download_data_request_for_user($userid, $data->requestedbyuser->id)) {
+                    $actions[] = api::get_download_link($usercontext, $requestid);
+                }
+                break;
         }
 
         $actionsmenu = new action_menu($actions);
         $actionsmenu->set_menu_trigger(get_string('actions'));
         $actionsmenu->set_owner_selector('request-actions-' . $requestid);
         $actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
+        $actionsmenu->set_constraint('[data-region=data-requests-table] > .no-overflow');
 
         return $OUTPUT->render($actionsmenu);
     }
@@ -227,19 +236,25 @@ class data_requests_table extends table_sql {
     public function query_db($pagesize, $useinitialsbar = true) {
         global $PAGE;
 
-        // Count data requests from the given conditions.
-        $total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
-        $this->pagesize($pagesize, $total);
+        // Set dummy page total until we fetch full result set.
+        $this->pagesize($pagesize, $pagesize + 1);
 
         $sort = $this->get_sql_sort();
 
         // Get data requests from the given conditions.
         $datarequests = api::get_data_requests($this->userid, $this->statuses, $this->types, $sort,
                 $this->get_page_start(), $this->get_page_size());
+
+        // Count data requests from the given conditions.
+        $total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
+        $this->pagesize($pagesize, $total);
+
         $this->rawdata = [];
         $context = \context_system::instance();
         $renderer = $PAGE->get_renderer('tool_dataprivacy');
+
         foreach ($datarequests as $persistent) {
+            $this->datarequests[$persistent->get('id')] = $persistent;
             $exporter = new data_request_exporter($persistent, ['context' => $context]);
             $this->rawdata[] = $exporter->export($renderer);
         }
index c5e18a1..729a7fe 100644 (file)
@@ -95,7 +95,8 @@ class my_data_requests_page implements renderable, templatable {
             $requestexporter = new data_request_exporter($request, ['context' => $outputcontext]);
             $item = $requestexporter->export($output);
 
-            if ($request->get('userid') != $USER->id) {
+            $self = $request->get('userid') == $USER->id;
+            if (!$self) {
                 // 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);
@@ -108,8 +109,28 @@ class my_data_requests_page implements renderable, templatable {
                     $item->statuslabelclass = 'label-success';
                     $item->statuslabel = get_string('statuscomplete', 'tool_dataprivacy');
                     $cancancel = false;
-                    // Show download links only for export-type data requests.
-                    $candownload = $type == api::DATAREQUEST_TYPE_EXPORT;
+                    break;
+                case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+                    $item->statuslabelclass = 'label-success';
+                    $item->statuslabel = get_string('statusready', 'tool_dataprivacy');
+                    $cancancel = false;
+                    $candownload = true;
+
+                    if ($usercontext) {
+                        $candownload = api::can_download_data_request_for_user(
+                                $request->get('userid'), $request->get('requestedby'));
+                    }
+                    break;
+                case api::DATAREQUEST_STATUS_DELETED:
+                    $item->statuslabelclass = 'label-success';
+                    $item->statuslabel = get_string('statusdeleted', 'tool_dataprivacy');
+                    $cancancel = false;
+                    break;
+                case api::DATAREQUEST_STATUS_EXPIRED:
+                    $item->statuslabelclass = 'label-default';
+                    $item->statuslabel = get_string('statusexpired', 'tool_dataprivacy');
+                    $item->statuslabeltitle = get_string('downloadexpireduser', 'tool_dataprivacy');
+                    $cancancel = false;
                     break;
                 case api::DATAREQUEST_STATUS_CANCELLED:
                 case api::DATAREQUEST_STATUS_REJECTED:
@@ -126,10 +147,7 @@ class my_data_requests_page implements renderable, templatable {
                 $actions[] = new action_menu_link_secondary($cancelurl, null, $canceltext, $canceldata);
             }
             if ($candownload && $usercontext) {
-                $downloadurl = moodle_url::make_pluginfile_url($usercontext->id, 'tool_dataprivacy', 'export', $requestid, '/',
-                        'export.zip', true);
-                $downloadtext = get_string('download', 'tool_dataprivacy');
-                $actions[] = new action_menu_link_secondary($downloadurl, null, $downloadtext);
+                $actions[] = api::get_download_link($usercontext, $requestid);
             }
             if (!empty($actions)) {
                 $actionsmenu = new action_menu($actions);
diff --git a/admin/tool/dataprivacy/classes/task/delete_expired_requests.php b/admin/tool/dataprivacy/classes/task/delete_expired_requests.php
new file mode 100644 (file)
index 0000000..1ed3ac8
--- /dev/null
@@ -0,0 +1,67 @@
+<?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/>.
+
+/**
+ * Scheduled task to delete files and update statuses of expired data requests.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Michael Hawkins
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_dataprivacy\task;
+
+use coding_exception;
+use core\task\scheduled_task;
+use tool_dataprivacy\api;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
+
+/**
+ * Scheduled task to delete files and update request statuses once they expire.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Michael Hawkins
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class delete_expired_requests extends scheduled_task {
+
+    /**
+     * Returns the task name.
+     *
+     * @return string
+     */
+    public function get_name() {
+        return get_string('deleteexpireddatarequeststask', 'tool_dataprivacy');
+    }
+
+    /**
+     * Run the task to delete expired data request files and update request statuses.
+     *
+     */
+    public function execute() {
+        $expiredrequests = \tool_dataprivacy\data_request::get_expired_requests();
+        $deletecount = count($expiredrequests);
+
+        if ($deletecount > 0) {
+            \tool_dataprivacy\data_request::expire($expiredrequests);
+
+            mtrace($deletecount . ' expired completed data requests have been deleted');
+        }
+    }
+}
index c58f574..c44b661 100644 (file)
@@ -81,6 +81,7 @@ class process_data_request_task extends adhoc_task {
         // Update the status of this request as pre-processing.
         mtrace('Processing request...');
         api::update_request_status($requestid, api::DATAREQUEST_STATUS_PROCESSING);
+        $completestatus = api::DATAREQUEST_STATUS_COMPLETE;
 
         if ($request->type == api::DATAREQUEST_TYPE_EXPORT) {
             // Get the collection of approved_contextlist objects needed for core_privacy data export.
@@ -105,7 +106,7 @@ class process_data_request_task extends adhoc_task {
             $filerecord->author    = fullname($foruser);
             // Save somewhere.
             $thing = $fs->create_file_from_pathname($filerecord, $exportedcontent);
-
+            $completestatus = api::DATAREQUEST_STATUS_DOWNLOAD_READY;
         } else if ($request->type == api::DATAREQUEST_TYPE_DELETE) {
             // Get the collection of approved_contextlist objects needed for core_privacy data deletion.
             $approvedclcollection = api::get_approved_contextlist_collection_for_request($requestpersistent);
@@ -115,10 +116,11 @@ class process_data_request_task extends adhoc_task {
             $manager->set_observer(new \tool_dataprivacy\manager_observer());
 
             $manager->delete_data_for_user($approvedclcollection);
+            $completestatus = api::DATAREQUEST_STATUS_DELETED;
         }
 
         // When the preparation of the metadata finishes, update the request status to awaiting approval.
-        api::update_request_status($requestid, api::DATAREQUEST_STATUS_COMPLETE);
+        api::update_request_status($requestid, $completestatus);
         mtrace('The processing of the user data request has been completed...');
 
         // Create message to notify the user regarding the processing results.
@@ -139,8 +141,17 @@ class process_data_request_task extends adhoc_task {
 
         $output = $PAGE->get_renderer('tool_dataprivacy');
         $emailonly = false;
+        $notifyuser = true;
         switch ($request->type) {
             case api::DATAREQUEST_TYPE_EXPORT:
+                // Check if the user is allowed to download their own export. (This is for
+                // institutions which centrally co-ordinate subject access request across many
+                // systems, not just one Moodle instance, so we don't want every instance emailing
+                // the user.)
+                if (!api::can_download_data_request_for_user($request->userid, $request->requestedby, $request->userid)) {
+                    $notifyuser = false;
+                }
+
                 $typetext = get_string('requesttypeexport', 'tool_dataprivacy');
                 // We want to notify the user in Moodle about the processing results.
                 $message->notification = 1;
@@ -179,18 +190,40 @@ class process_data_request_task extends adhoc_task {
         $message->fullmessagehtml = $messagehtml;
 
         // Send message to the user involved.
-        if ($emailonly) {
-            email_to_user($foruser, $dpo, $subject, $message->fullmessage, $messagehtml);
-        } else {
-            message_send($message);
+        if ($notifyuser) {
+            if ($emailonly) {
+                email_to_user($foruser, $dpo, $subject, $message->fullmessage, $messagehtml);
+            } else {
+                message_send($message);
+            }
+            mtrace('Message sent to user: ' . $messagetextdata['username']);
         }
-        mtrace('Message sent to user: ' . $messagetextdata['username']);
 
-        // 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) {
+        // Send to requester as well in some circumstances.
+        if ($foruser->id != $request->requestedby) {
+            $sendtorequester = false;
+            switch ($request->type) {
+                case api::DATAREQUEST_TYPE_EXPORT:
+                    // Send to the requester as well if they can download it, unless they are the
+                    // DPO. If we didn't notify the user themselves (because they can't download)
+                    // then send to requester even if it is the DPO, as in that case the requester
+                    // needs to take some action.
+                    if (api::can_download_data_request_for_user($request->userid, $request->requestedby, $request->requestedby)) {
+                        $sendtorequester = !$notifyuser || !api::is_site_dpo($request->requestedby);
+                    }
+                    break;
+                case api::DATAREQUEST_TYPE_DELETE:
+                    // Send to the requester if they are not the DPO and if they are allowed to
+                    // create data requests for the user (e.g. Parent).
+                    $sendtorequester = !api::is_site_dpo($request->requestedby) &&
+                            api::can_create_data_request_for_user($request->userid, $request->requestedby);
+                    break;
+                default:
+                    throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
+            }
+
             // 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)) {
+            if ($sendtorequester) {
                 $requestedby = core_user::get_user($request->requestedby);
                 $message->userto = $requestedby;
                 $messagetextdata['username'] = fullname($requestedby);
index f622e5b..c92c676 100644 (file)
@@ -37,13 +37,19 @@ $title = get_string('datadeletion', 'tool_dataprivacy');
 
 echo $OUTPUT->header();
 
-$table = new \tool_dataprivacy\output\expired_contexts_table($filter);
-$table->baseurl = $url;
-$table->baseurl->param('filter', $filter);
-
-$datadeletionpage = new \tool_dataprivacy\output\data_deletion_page($filter, $table);
-
-$output = $PAGE->get_renderer('tool_dataprivacy');
-echo $output->render($datadeletionpage);
+if (\tool_dataprivacy\api::is_site_dpo($USER->id)) {
+    $table = new \tool_dataprivacy\output\expired_contexts_table($filter);
+    $table->baseurl = $url;
+    $table->baseurl->param('filter', $filter);
+
+    $datadeletionpage = new \tool_dataprivacy\output\data_deletion_page($filter, $table);
+
+    $output = $PAGE->get_renderer('tool_dataprivacy');
+    echo $output->render($datadeletionpage);
+} else {
+    $dponamestring = implode (',', tool_dataprivacy\api::get_dpo_role_names());
+    $message = get_string('privacyofficeronly', 'tool_dataprivacy', $dponamestring);
+    echo $OUTPUT->notification($message, 'error');
+}
 
 echo $OUTPUT->footer();
index 52eff0a..50c0ab9 100644 (file)
@@ -38,7 +38,12 @@ $title = get_string('dataregistry', 'tool_dataprivacy');
 $output = $PAGE->get_renderer('tool_dataprivacy');
 echo $output->header();
 
-$dataregistry = new tool_dataprivacy\output\data_registry_page($contextlevel, $contextid);
-
-echo $output->render($dataregistry);
+if (\tool_dataprivacy\api::is_site_dpo($USER->id)) {
+    $dataregistry = new tool_dataprivacy\output\data_registry_page($contextlevel, $contextid);
+    echo $output->render($dataregistry);
+} else {
+    $dponamestring = implode (', ', tool_dataprivacy\api::get_dpo_role_names());
+    $message = get_string('privacyofficeronly', 'tool_dataprivacy', $dponamestring);
+    echo $OUTPUT->notification($message, 'error');
+}
 echo $OUTPUT->footer();
index 0887134..6a8140d 100644 (file)
@@ -36,39 +36,45 @@ $title = get_string('datarequests', 'tool_dataprivacy');
 echo $OUTPUT->header();
 echo $OUTPUT->heading($title);
 
-$filtersapplied = optional_param_array('request-filters', [-1], PARAM_NOTAGS);
-$filterscleared = optional_param('filters-cleared', 0, PARAM_INT);
-if ($filtersapplied === [-1]) {
-    // If there are no filters submitted, check if there is a saved filters from the user preferences.
-    $filterprefs = get_user_preferences(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, null);
-    if ($filterprefs && empty($filterscleared)) {
-        $filtersapplied = json_decode($filterprefs);
-    } else {
-        $filtersapplied = [];
+if (\tool_dataprivacy\api::is_site_dpo($USER->id)) {
+    $filtersapplied = optional_param_array('request-filters', [-1], PARAM_NOTAGS);
+    $filterscleared = optional_param('filters-cleared', 0, PARAM_INT);
+    if ($filtersapplied === [-1]) {
+        // If there are no filters submitted, check if there is a saved filters from the user preferences.
+        $filterprefs = get_user_preferences(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, null);
+        if ($filterprefs && empty($filterscleared)) {
+            $filtersapplied = json_decode($filterprefs);
+        } else {
+            $filtersapplied = [];
+        }
     }
-}
-// Save the current applied filters to the user preferences.
-set_user_preference(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, json_encode($filtersapplied));
+    // Save the current applied filters to the user preferences.
+    set_user_preference(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, json_encode($filtersapplied));
 
-$types = [];
-$statuses = [];
-foreach ($filtersapplied as $filter) {
-    list($category, $value) = explode(':', $filter);
-    switch($category) {
-        case \tool_dataprivacy\local\helper::FILTER_TYPE:
-            $types[] = $value;
-            break;
-        case \tool_dataprivacy\local\helper::FILTER_STATUS:
-            $statuses[] = $value;
-            break;
+    $types = [];
+    $statuses = [];
+    foreach ($filtersapplied as $filter) {
+        list($category, $value) = explode(':', $filter);
+        switch($category) {
+            case \tool_dataprivacy\local\helper::FILTER_TYPE:
+                $types[] = $value;
+                break;
+            case \tool_dataprivacy\local\helper::FILTER_STATUS:
+                $statuses[] = $value;
+                break;
+        }
     }
-}
 
-$table = new \tool_dataprivacy\output\data_requests_table(0, $statuses, $types, true);
-$table->baseurl = $url;
+    $table = new \tool_dataprivacy\output\data_requests_table(0, $statuses, $types, true);
+    $table->baseurl = $url;
 
-$requestlist = new tool_dataprivacy\output\data_requests_page($table, $filtersapplied);
-$requestlistoutput = $PAGE->get_renderer('tool_dataprivacy');
-echo $requestlistoutput->render($requestlist);
+    $requestlist = new tool_dataprivacy\output\data_requests_page($table, $filtersapplied);
+    $requestlistoutput = $PAGE->get_renderer('tool_dataprivacy');
+    echo $requestlistoutput->render($requestlist);
+} else {
+    $dponamestring = implode (', ', tool_dataprivacy\api::get_dpo_role_names());
+    $message = get_string('privacyofficeronly', 'tool_dataprivacy', $dponamestring);
+    echo $OUTPUT->notification($message, 'error');
+}
 
 echo $OUTPUT->footer();
index ecc2ec3..ad31867 100644 (file)
@@ -49,4 +49,22 @@ $capabilities = [
         'contextlevel' => CONTEXT_USER,
         'archetypes' => []
     ],
+
+    // Capability for users to download the results of their own data request.
+    'tool/dataprivacy:downloadownrequest' => [
+        'riskbitmask' => 0,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_USER,
+        'archetypes' => [
+            'user' => CAP_ALLOW
+        ]
+    ],
+
+    // Capability for administrators to download other people's data requests.
+    'tool/dataprivacy:downloadallrequests' => [
+        'riskbitmask' => RISK_PERSONAL,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_USER,
+        'archetypes' => []
+    ],
 ];
index 3a4915b..5ee3a19 100644 (file)
@@ -42,5 +42,13 @@ $tasks = array(
         'day' => '*',
         'dayofweek' => '*',
         'month' => '*'
+    ), array(
+        'classname' => 'tool_dataprivacy\task\delete_expired_requests',
+        'blocking' => 0,
+        'minute' => 'R',
+        'hour' => 'R',
+        'day' => '*',
+        'dayofweek' => '*',
+        'month' => '*'
     ),
 );
diff --git a/admin/tool/dataprivacy/db/upgrade.php b/admin/tool/dataprivacy/db/upgrade.php
new file mode 100644 (file)
index 0000000..6ad876c
--- /dev/null
@@ -0,0 +1,175 @@
+<?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/>.
+
+/**
+ * tool_dataprivacy plugin upgrade code
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Function to upgrade tool_dataprivacy.
+ *
+ * @param int $oldversion the version we are upgrading from
+ * @return bool result
+ */
+function xmldb_tool_dataprivacy_upgrade($oldversion) {
+    global $CFG, $DB;
+
+    $dbman = $DB->get_manager();
+
+    if ($oldversion < 2018051405) {
+        // Define table tool_dataprivacy_ctxexpired to be created.
+        $table = new xmldb_table('tool_dataprivacy_ctxexpired');
+
+        // Adding fields to table tool_dataprivacy_ctxexpired.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('status', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
+        $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+
+        // Adding keys to table tool_dataprivacy_ctxexpired.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('contextid', XMLDB_KEY_FOREIGN_UNIQUE, array('contextid'), 'context', array('id'));
+
+        // Conditionally launch create table for tool_dataprivacy_ctxexpired.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Define table tool_dataprivacy_contextlist to be created.
+        $table = new xmldb_table('tool_dataprivacy_contextlist');
+
+        // Adding fields to table tool_dataprivacy_contextlist.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('component', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+
+        // Adding keys to table tool_dataprivacy_contextlist.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+        // Conditionally launch create table for tool_dataprivacy_contextlist.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Define table tool_dataprivacy_ctxlst_ctx to be created.
+        $table = new xmldb_table('tool_dataprivacy_ctxlst_ctx');
+
+        // Adding fields to table tool_dataprivacy_ctxlst_ctx.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('contextlistid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('status', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+
+        // Adding keys to table tool_dataprivacy_ctxlst_ctx.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('contextlistid', XMLDB_KEY_FOREIGN, array('contextlistid'), 'tool_dataprivacy_contextlist', array('id'));
+
+        // Conditionally launch create table for tool_dataprivacy_ctxlst_ctx.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Define table tool_dataprivacy_rqst_ctxlst to be created.
+        $table = new xmldb_table('tool_dataprivacy_rqst_ctxlst');
+
+        // Adding fields to table tool_dataprivacy_rqst_ctxlst.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('requestid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('contextlistid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+
+        // Adding keys to table tool_dataprivacy_rqst_ctxlst.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('requestid', XMLDB_KEY_FOREIGN, array('requestid'), 'tool_dataprivacy_request', array('id'));
+        $table->add_key('contextlistid', XMLDB_KEY_FOREIGN, array('contextlistid'), 'tool_dataprivacy_contextlist', array('id'));
+        $table->add_key('request_contextlist', XMLDB_KEY_UNIQUE, array('requestid', 'contextlistid'));
+
+        // Conditionally launch create table for tool_dataprivacy_rqst_ctxlst.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Define field lawfulbases to be added to tool_dataprivacy_purpose.
+        $table = new xmldb_table('tool_dataprivacy_purpose');
+
+        // It is a required field. We initially define and add it as null and later update it to XMLDB_NOTNULL.
+        $field = new xmldb_field('lawfulbases', XMLDB_TYPE_TEXT, null, null, null, null, null, 'descriptionformat');
+
+        // Conditionally launch add field lawfulbases.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+
+            // Set a kind-of-random value to lawfulbasis field.
+            $DB->set_field('tool_dataprivacy_purpose', 'lawfulbases', 'gdpr_art_6_1_a');
+
+            // We redefine it now as not null.
+            $field = new xmldb_field('lawfulbases', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null, 'descriptionformat');
+
+            // Launch change of nullability for field lawfulbases.
+            $dbman->change_field_notnull($table, $field);
+        }
+
+        // Define field sensitivedatareasons to be added to tool_dataprivacy_purpose.
+        $table = new xmldb_table('tool_dataprivacy_purpose');
+        $field = new xmldb_field('sensitivedatareasons', XMLDB_TYPE_TEXT, null, null, null, null, null, 'lawfulbases');
+
+        // Conditionally launch add field sensitivedatareasons.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Dataprivacy savepoint reached.
+        upgrade_plugin_savepoint(true, 2018051405, 'tool', 'dataprivacy');
+    }
+
+    if ($oldversion < 2018051406) {
+        // Update completed delete requests to new delete status.
+        $query = "UPDATE {tool_dataprivacy_request}
+                     SET status = :setstatus
+                   WHERE type = :type
+                         AND status = :wherestatus";
+        $params = array(
+            'setstatus' => 10, // Request deleted.
+            'type' => 2, // Delete type.
+            'wherestatus' => 5, // Request completed.
+        );
+
+        $DB->execute($query, $params);
+
+        // Update completed data export requests to new download ready status.
+        $params = array(
+            'setstatus' => 8, // Request download ready.
+            'type' => 1, // export type.
+            'wherestatus' => 5, // Request completed.
+        );
+
+        $DB->execute($query, $params);
+
+        upgrade_plugin_savepoint(true, 2018051406, 'tool', 'dataprivacy');
+    }
+
+    return true;
+}
index b01a0b6..a50a271 100644 (file)
@@ -66,6 +66,8 @@ $string['datadeletionpagehelp'] = 'Data for which the retention period has expir
 $string['dataprivacy:makedatarequestsforchildren'] = 'Make data requests for minors';
 $string['dataprivacy:managedatarequests'] = 'Manage data requests';
 $string['dataprivacy:managedataregistry'] = 'Manage data registry';
+$string['dataprivacy:downloadownrequest'] = 'Download your own exported data';
+$string['dataprivacy:downloadallrequests'] = 'Download exported data for everyone';
 $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}';
@@ -78,12 +80,14 @@ $string['defaultsinfo'] = 'Default categories and purposes are applied to all ne
 $string['deletecategory'] = 'Delete "{$a}" category';
 $string['deletecategorytext'] = 'Are you sure you want to delete "{$a}" category?';
 $string['deleteexpiredcontextstask'] = 'Delete expired contexts';
+$string['deleteexpireddatarequeststask'] = 'Delete files from completed data requests that have expired';
 $string['deletepurpose'] = 'Delete "{$a}" purpose';
 $string['deletepurposetext'] = 'Are you sure you want to delete "{$a}" purpose?';
 $string['defaultssaved'] = 'Defaults saved';
 $string['deny'] = 'Deny';
 $string['denyrequest'] = 'Deny request';
 $string['download'] = 'Download';
+$string['downloadexpireduser'] = 'Download has expired. Submit a new request if you wish to export your personal data.';
 $string['dporolemapping'] = 'Privacy officer role mapping';
 $string['dporolemapping_desc'] = 'The privacy officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a privacy officer role mapping option.';
 $string['editcategories'] = 'Edit categories';
@@ -182,6 +186,7 @@ $string['notset'] = 'Not set (use the default value)';
 $string['pluginregistry'] = 'Plugin privacy registry';
 $string['pluginregistrytitle'] = 'Plugin privacy compliance registry';
 $string['privacy'] = 'Privacy';
+$string['privacyofficeronly'] = 'Only users who are assigned a privacy officer role ({$a}) have access to this content';
 $string['privacy:metadata:preference:tool_dataprivacy_request-filters'] = 'The filters currently applied to the data requests page.';
 $string['privacy:metadata:request'] = 'Information from personal data requests (subject access and deletion requests) made for this site.';
 $string['privacy:metadata:request:comments'] = 'Any user comments accompanying the request.';
@@ -189,6 +194,8 @@ $string['privacy:metadata:request:userid'] = 'The ID of the user to whom the req
 $string['privacy:metadata:request:requestedby'] = 'The ID of the user making the request, if made on behalf of another user.';
 $string['privacy:metadata:request:dpocomment'] = 'Any comments made by the site\'s privacy officer regarding the request.';
 $string['privacy:metadata:request:timecreated'] = 'The timestamp indicating when the request was made by the user.';
+$string['privacyrequestexpiry'] = 'Data request expiry';
+$string['privacyrequestexpiry_desc'] = 'The amount of time that approved data requests will be available for download before expiring. 0 means no time limit.';
 $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';
@@ -238,7 +245,10 @@ $string['statusapproved'] = 'Approved';
 $string['statusawaitingapproval'] = 'Awaiting approval';
 $string['statuscancelled'] = 'Cancelled';
 $string['statuscomplete'] = 'Complete';
+$string['statusready'] = 'Download ready';
+$string['statusdeleted'] = 'Deleted';
 $string['statusdetail'] = 'Status:';
+$string['statusexpired'] = 'Expired';
 $string['statuspreprocessing'] = 'Pre-processing';
 $string['statusprocessing'] = 'Processing';
 $string['statuspending'] = 'Pending';
index 3c66485..fbeb61d 100644 (file)
@@ -185,26 +185,23 @@ function tool_dataprivacy_output_fragment_contextlevel_form($args) {
  * @return bool Returns false if we don't find a file.
  */
 function tool_dataprivacy_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
-    global $USER;
-
     if ($context->contextlevel == CONTEXT_USER) {
         // Make sure the user is logged in.
         require_login(null, false);
 
-        // Validate the user downloading this archive.
-        $usercontext = context_user::instance($USER->id);
-        // The user downloading this is not the user the archive has been prepared for. Check if it's the requester (e.g. parent).
-        if ($usercontext->instanceid !== $context->instanceid) {
-            // Get the data request ID. This should be the first element of the $args array.
-            $itemid = $args[0];
-            // Fetch the data request object. An invalid ID will throw an exception.
-            $datarequest = new \tool_dataprivacy\data_request($itemid);
-
-            // Check if the user is the requester and has the capability to make data requests for the target user.
-            $candownloadforuser = has_capability('tool/dataprivacy:makedatarequestsforchildren', $context);
-            if ($USER->id != $datarequest->get('requestedby') || !$candownloadforuser) {
-                return false;
-            }
+        // Get the data request ID. This should be the first element of the $args array.
+        $itemid = $args[0];
+        // Fetch the data request object. An invalid ID will throw an exception.
+        $datarequest = new \tool_dataprivacy\data_request($itemid);
+
+        // Check if user is allowed to download it.
+        if (!\tool_dataprivacy\api::can_download_data_request_for_user($context->instanceid, $datarequest->get('requestedby'))) {
+            return false;
+        }
+
+        // Make the file unavailable if it has expired.
+        if (\tool_dataprivacy\data_request::is_expired($datarequest)) {
+            send_file_not_found();
         }
 
         // All good. Serve the exported data.
index 3b3f1c4..5ae8839 100644 (file)
@@ -38,11 +38,18 @@ $title = get_string('pluginregistry', 'tool_dataprivacy');
 $output = $PAGE->get_renderer('tool_dataprivacy');
 echo $output->header();
 
-// Get data!
-$metadatatool = new \tool_dataprivacy\metadata_registry();
-$metadata = $metadatatool->get_registry_metadata();
+if (\tool_dataprivacy\api::is_site_dpo($USER->id)) {
+    // Get data!
+    $metadatatool = new \tool_dataprivacy\metadata_registry();
+    $metadata = $metadatatool->get_registry_metadata();
 
-$dataregistry = new tool_dataprivacy\output\data_registry_compliance_page($metadata);
+    $dataregistry = new tool_dataprivacy\output\data_registry_compliance_page($metadata);
+
+    echo $output->render($dataregistry);
+} else {
+    $dponamestring = implode (', ', tool_dataprivacy\api::get_dpo_role_names());
+    $message = get_string('privacyofficeronly', 'tool_dataprivacy', $dponamestring);
+    echo $OUTPUT->notification($message, 'error');
+}
 
-echo $output->render($dataregistry);
 echo $OUTPUT->footer();
index f04fc10..b902d52 100644 (file)
@@ -34,6 +34,12 @@ if ($hassiteconfig) {
                 new lang_string('contactdataprotectionofficer_desc', 'tool_dataprivacy'), 0)
         );
 
+        // Set days approved data requests will be accessible. 1 week default.
+        $privacysettings->add(new admin_setting_configduration('tool_dataprivacy/privacyrequestexpiry',
+                new lang_string('privacyrequestexpiry', 'tool_dataprivacy'),
+                new lang_string('privacyrequestexpiry_desc', 'tool_dataprivacy'),
+                WEEKSECS, 1));
+
         // Fetch roles that are assignable.
         $assignableroles = get_assignable_roles(context_system::instance());
 
@@ -57,22 +63,25 @@ if ($hassiteconfig) {
     }
 }
 
-// Link that leads to the data requests management page.
-$ADMIN->add('privacy', new admin_externalpage('datarequests', get_string('datarequests', 'tool_dataprivacy'),
-    new moodle_url('/admin/tool/dataprivacy/datarequests.php'), 'tool/dataprivacy:managedatarequests')
-);
+// Restrict config links to the DPO.
+if (tool_dataprivacy\api::is_site_dpo($USER->id)) {
+    // Link that leads to the data requests management page.
+    $ADMIN->add('privacy', new admin_externalpage('datarequests', get_string('datarequests', 'tool_dataprivacy'),
+        new moodle_url('/admin/tool/dataprivacy/datarequests.php'), 'tool/dataprivacy:managedatarequests')
+    );
 
-// Link that leads to the data registry management page.
-$ADMIN->add('privacy', new admin_externalpage('dataregistry', get_string('dataregistry', 'tool_dataprivacy'),
-    new moodle_url('/admin/tool/dataprivacy/dataregistry.php'), 'tool/dataprivacy:managedataregistry')
-);
+    // Link that leads to the data registry management page.
+    $ADMIN->add('privacy', new admin_externalpage('dataregistry', get_string('dataregistry', 'tool_dataprivacy'),
+        new moodle_url('/admin/tool/dataprivacy/dataregistry.php'), 'tool/dataprivacy:managedataregistry')
+    );
 
-// Link that leads to the review page of expired contexts that are up for deletion.
-$ADMIN->add('privacy', new admin_externalpage('datadeletion', get_string('datadeletion', 'tool_dataprivacy'),
-        new moodle_url('/admin/tool/dataprivacy/datadeletion.php'), 'tool/dataprivacy:managedataregistry')
-);
+    // Link that leads to the review page of expired contexts that are up for deletion.
+    $ADMIN->add('privacy', new admin_externalpage('datadeletion', get_string('datadeletion', 'tool_dataprivacy'),
+            new moodle_url('/admin/tool/dataprivacy/datadeletion.php'), 'tool/dataprivacy:managedataregistry')
+    );
 
-// Link that leads to the other data registry management page.
-$ADMIN->add('privacy', new admin_externalpage('pluginregistry', get_string('pluginregistry', 'tool_dataprivacy'),
-    new moodle_url('/admin/tool/dataprivacy/pluginregistry.php'), 'tool/dataprivacy:managedataregistry')
-);
+    // Link that leads to the other data registry management page.
+    $ADMIN->add('privacy', new admin_externalpage('pluginregistry', get_string('pluginregistry', 'tool_dataprivacy'),
+        new moodle_url('/admin/tool/dataprivacy/pluginregistry.php'), 'tool/dataprivacy:managedataregistry')
+    );
+}
index a351052..e6ddf93 100644 (file)
@@ -24,3 +24,7 @@ dd a.contactdpo {
     /* Reverting dd's left margin */
     margin-left: inherit;
 }
+
+[data-region="data-requests-table"] .moodle-actionmenu {
+    min-width: 150px;
+}
index 2b654b2..6f00965 100644 (file)
@@ -60,7 +60,7 @@
                 "typename" : "Data deletion",
                 "comments": "Please delete all of my son's personal data.",
                 "statuslabelclass": "label-success",
-                "statuslabel": "Complete",
+                "statuslabel": "Deleted",
                 "timecreated" : 1517902087,
                 "requestedbyuser" : {
                     "fullname": "Martha Smith",
                     "fullname": "Martha Smith",
                     "profileurl": "#"
                 }
+            },
+            {
+                "id": 6,
+                "typename" : "Data export",
+                "comments": "Please let me download my data",
+                "statuslabelclass": "label",
+                "statuslabel": "Expired",
+                "statuslabeltitle": "Download has expired. Submit a new request if you wish to export your personal data.",
+                "timecreated" : 1517902087,
+                "requestedbyuser" : {
+                    "fullname": "Martha Smith",
+                    "profileurl": "#"
+                }
             }
         ]
     }
                 <td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
                 <td><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></td>
                 <td>
-                    <span class="label {{statuslabelclass}}">{{statuslabel}}</span>
+                    <span class="label {{statuslabelclass}}" title="{{statuslabeltitle}}">{{statuslabel}}</span>
                 </td>
                 <td>{{comments}}</td>
                 <td>
index f4a7a66..9feebcf 100644 (file)
@@ -66,12 +66,12 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         $requestid = $datarequest->get('id');
 
         // Update with a valid status.
-        $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_COMPLETE);
+        $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_DOWNLOAD_READY);
         $this->assertTrue($result);
 
         // Fetch the request record again.
         $datarequest = new data_request($requestid);
-        $this->assertEquals(api::DATAREQUEST_STATUS_COMPLETE, $datarequest->get('status'));
+        $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $datarequest->get('status'));
 
         // Update with an invalid status.
         $this->expectException(invalid_persistent_exception::class);
@@ -276,6 +276,57 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
         $this->assertFalse(api::can_manage_data_requests($nondpoincapable->id));
     }
 
+    /**
+     * Test for api::can_download_data_request_for_user()
+     */
+    public function test_can_download_data_request_for_user() {
+        $generator = $this->getDataGenerator();
+
+        // Three victims.
+        $victim1 = $generator->create_user();
+        $victim2 = $generator->create_user();
+        $victim3 = $generator->create_user();
+
+        // Assign a user as victim 1's parent.
+        $systemcontext = \context_system::instance();
+        $parentrole = $generator->create_role();
+        assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
+        $parent = $generator->create_user();
+        role_assign($parentrole, $parent->id, \context_user::instance($victim1->id));
+
+        // Assign another user as data access wonder woman.
+        $wonderrole = $generator->create_role();
+        assign_capability('tool/dataprivacy:downloadallrequests', CAP_ALLOW, $wonderrole, $systemcontext);
+        $staff = $generator->create_user();
+        role_assign($wonderrole, $staff->id, $systemcontext);
+
+        // Finally, victim 3 has been naughty; stop them accessing their own data.
+        $naughtyrole = $generator->create_role();
+        assign_capability('tool/dataprivacy:downloadownrequest', CAP_PROHIBIT, $naughtyrole, $systemcontext);
+        role_assign($naughtyrole, $victim3->id, $systemcontext);
+
+        // Victims 1 and 2 can access their own data, regardless of who requested it.
+        $this->assertTrue(api::can_download_data_request_for_user($victim1->id, $victim1->id, $victim1->id));
+        $this->assertTrue(api::can_download_data_request_for_user($victim2->id, $staff->id, $victim2->id));
+
+        // Victim 3 cannot access his own data.
+        $this->assertFalse(api::can_download_data_request_for_user($victim3->id, $victim3->id, $victim3->id));
+
+        // Victims 1 and 2 cannot access another victim's data.
+        $this->assertFalse(api::can_download_data_request_for_user($victim2->id, $victim1->id, $victim1->id));
+        $this->assertFalse(api::can_download_data_request_for_user($victim1->id, $staff->id, $victim2->id));
+
+        // Staff can access everyone's data.
+        $this->assertTrue(api::can_download_data_request_for_user($victim1->id, $victim1->id, $staff->id));
+        $this->assertTrue(api::can_download_data_request_for_user($victim2->id, $staff->id, $staff->id));
+        $this->assertTrue(api::can_download_data_request_for_user($victim3->id, $staff->id, $staff->id));
+
+        // Parent can access victim 1's data only if they requested it.
+        $this->assertTrue(api::can_download_data_request_for_user($victim1->id, $parent->id, $parent->id));
+        $this->assertFalse(api::can_download_data_request_for_user($victim1->id, $staff->id, $parent->id));
+        $this->assertFalse(api::can_download_data_request_for_user($victim2->id, $parent->id, $parent->id));
+    }
+
     /**
      * Test for api::create_data_request()
      */
@@ -417,27 +468,20 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
      * @return array
      */
     public function get_data_requests_provider() {
-        $generator = new testing_data_generator();
-        $user1 = $generator->create_user();
-        $user2 = $generator->create_user();
-        $user3 = $generator->create_user();
-        $user4 = $generator->create_user();
-        $user5 = $generator->create_user();
-        $users = [$user1, $user2, $user3, $user4, $user5];
-        $completeonly = [api::DATAREQUEST_STATUS_COMPLETE];
-        $completeandcancelled = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_CANCELLED];
+        $completeonly = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_DOWNLOAD_READY, api::DATAREQUEST_STATUS_DELETED];
+        $completeandcancelled = array_merge($completeonly, [api::DATAREQUEST_STATUS_CANCELLED]);
 
         return [
             // Own data requests.
-            [$users, $user1, false, $completeonly],
+            ['user', false, $completeonly],
             // Non-DPO fetching all requets.
-            [$users, $user2, true, $completeonly],
+            ['user', true, $completeonly],
             // Admin fetching all completed and cancelled requests.
-            [$users, get_admin(), true, $completeandcancelled],
+            ['dpo', true, $completeandcancelled],
             // Admin fetching all completed requests.
-            [$users, get_admin(), true, $completeonly],
+            ['dpo', true, $completeonly],
             // Guest fetching all requests.
-            [$users, guest_user(), true, $completeonly],
+            ['guest', true, $completeonly],
         ];
     }
 
@@ -445,12 +489,31 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
      * Test for api::get_data_requests()
      *
      * @dataProvider get_data_requests_provider
-     * @param stdClass[] $users Array of users to create data requests for.
-     * @param stdClass $loggeduser The user logging in.
+     * @param string $usertype The type of the user logging in.
      * @param boolean $fetchall Whether to fetch all records.
      * @param int[] $statuses Status filters.
      */
-    public function test_get_data_requests($users, $loggeduser, $fetchall, $statuses) {
+    public function test_get_data_requests($usertype, $fetchall, $statuses) {
+        $generator = new testing_data_generator();
+        $user1 = $generator->create_user();
+        $user2 = $generator->create_user();
+        $user3 = $generator->create_user();
+        $user4 = $generator->create_user();
+        $user5 = $generator->create_user();
+        $users = [$user1, $user2, $user3, $user4, $user5];
+
+        switch ($usertype) {
+            case 'user':
+                $loggeduser = $user1;
+                break;
+            case 'dpo':
+                $loggeduser = get_admin();
+                break;
+            case 'guest':
+                $loggeduser = guest_user();
+                break;
+        }
+
         $comment = 'Data %s request comment by user %d';
         $exportstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_EXPORT);
         $deletionstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_DELETE);
@@ -549,6 +612,9 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
             [api::DATAREQUEST_STATUS_COMPLETE, false],
             [api::DATAREQUEST_STATUS_CANCELLED, false],
             [api::DATAREQUEST_STATUS_REJECTED, false],
+            [api::DATAREQUEST_STATUS_DOWNLOAD_READY, false],
+            [api::DATAREQUEST_STATUS_EXPIRED, false],
+            [api::DATAREQUEST_STATUS_DELETED, false],
         ];
     }
 
diff --git a/admin/tool/dataprivacy/tests/behat/datadelete.feature b/admin/tool/dataprivacy/tests/behat/datadelete.feature
new file mode 100644 (file)
index 0000000..c6454bd
--- /dev/null
@@ -0,0 +1,119 @@
+@tool @tool_dataprivacy
+Feature: Data delete from the privacy API
+  In order to delete data for users and meet legal requirements
+  As an admin, user, or parent
+  I need to be able to request a user and their data data be deleted
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname      | lastname |
+      | victim   | Victim User    | 1        |
+      | parent   | Long-suffering | Parent   |
+    And the following "roles" exist:
+      | shortname | name  | archetype |
+      | tired     | Tired |           |
+    And the following "permission overrides" exist:
+      | capability                                   | permission | role  | contextlevel | reference |
+      | tool/dataprivacy:makedatarequestsforchildren | Allow      | tired | System       |           |
+    And the following "role assigns" exist:
+      | user   | role  | contextlevel | reference |
+      | parent | tired | User         | victim    |
+    And the following config values are set as admin:
+      | contactdataprotectionofficer | 1  | tool_dataprivacy |
+
+  @javascript
+  Scenario: As admin, delete a user and their data
+    Given I log in as "victim"
+    And I should see "Victim User 1"
+    And I log out
+
+    And I log in as "admin"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "New request"
+    And I set the field "Requesting for" to "Victim User 1"
+    And I set the field "Type" to "Delete all of my personal data"
+    And I press "Save changes"
+    Then I should see "Victim User 1"
+    And I should see "Pending" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Awaiting approval" in the "Victim User 1" "table_row"
+    And I follow "Actions"
+    And I follow "Approve request"
+    And I press "Approve request"
+    And I should see "Approved" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Deleted" in the "Victim User 1" "table_row"
+
+    And I log out
+    And I log in as "victim"
+    And I should see "Invalid login"
+
+  @javascript
+  Scenario: As a student, request deletion of account and data
+    Given I log in as "victim"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I follow "New request"
+    And I set the field "Type" to "Delete all of my personal data"
+    And I press "Save changes"
+    Then I should see "Delete all of my personal data"
+    And I should see "Pending" in the "Delete all of my personal data" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Awaiting approval" in the "Delete all of my personal data" "table_row"
+
+    And I log out
+    And I log in as "admin"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "Actions"
+    And I follow "Approve request"
+    And I press "Approve request"
+
+    And I log out
+    And I log in as "victim"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I should see "Approved" in the "Delete all of my personal data" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Your session has timed out"
+    And I log in as "victim"
+    And I should see "Invalid login"
+
+    And I log in as "admin"
+    And I am on site homepage
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I should see "Deleted"
+
+  @javascript
+  Scenario: As a parent, request account and data deletion for my child
+    Given I log in as "parent"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I follow "New request"
+    And I set the field "Requesting for" to "Victim User 1"
+    And I set the field "Type" to "Delete all of my personal data"
+    And I press "Save changes"
+    Then I should see "Victim User 1"
+    And I should see "Pending" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Awaiting approval" in the "Victim User 1" "table_row"
+
+    And I log out
+    And I log in as "admin"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "Actions"
+    And I follow "Approve request"
+    And I press "Approve request"
+
+    And I log out
+    And I log in as "parent"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I should see "Approved" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "You don't have any personal data requests"
diff --git a/admin/tool/dataprivacy/tests/behat/dataexport.feature b/admin/tool/dataprivacy/tests/behat/dataexport.feature
new file mode 100644 (file)
index 0000000..50c58bc
--- /dev/null
@@ -0,0 +1,131 @@
+@tool @tool_dataprivacy
+Feature: Data export from the privacy API
+  In order to export data for users and meet legal requirements
+  As an admin, user, or parent
+  I need to be able to export data for a user
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname      | lastname |
+      | victim   | Victim User    | 1        |
+      | parent   | Long-suffering | Parent   |
+    And the following "roles" exist:
+      | shortname | name  | archetype |
+      | tired     | Tired |           |
+    And the following "permission overrides" exist:
+      | capability                                   | permission | role  | contextlevel | reference |
+      | tool/dataprivacy:makedatarequestsforchildren | Allow      | tired | System       |           |
+    And the following "role assigns" exist:
+      | user   | role  | contextlevel | reference |
+      | parent | tired | User         | victim    |
+    And the following config values are set as admin:
+      | contactdataprotectionofficer | 1  | tool_dataprivacy |
+      | privacyrequestexpiry         | 55 | tool_dataprivacy |
+
+  @javascript
+  Scenario: As admin, export data for a user and download it, unless it has expired
+    Given I log in as "admin"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "New request"
+    And I set the field "Requesting for" to "Victim User 1"
+    And I press "Save changes"
+    Then I should see "Victim User 1"
+    And I should see "Pending" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Awaiting approval" in the "Victim User 1" "table_row"
+    And I follow "Actions"
+    And I follow "Approve request"
+    And I press "Approve request"
+    And I should see "Approved" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Download ready" in the "Victim User 1" "table_row"
+    And I follow "Actions"
+    And following "Download" should download between "1" and "100000" bytes
+    And the following config values are set as admin:
+      | privacyrequestexpiry | 1 | tool_dataprivacy |
+    And I wait "1" seconds
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I should see "Expired" in the "Victim User 1" "table_row"
+    And I follow "Actions"
+    And I should not see "Download"
+
+  @javascript
+  Scenario: As a student, request data export and then download it when approved, unless it has expired
+    Given I log in as "victim"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I follow "New request"
+    And I press "Save changes"
+    Then I should see "Export all of my personal data"
+    And I should see "Pending" in the "Export all of my personal data" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Awaiting approval" in the "Export all of my personal data" "table_row"
+
+    And I log out
+    And I log in as "admin"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "Actions"
+    And I follow "Approve request"
+    And I press "Approve request"
+
+    And I log out
+    And I log in as "victim"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I should see "Approved" in the "Export all of my personal data" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Download ready" in the "Export all of my personal data" "table_row"
+    And I follow "Actions"
+    And following "Download" should download between "1" and "100000" bytes
+
+    And the following config values are set as admin:
+      | privacyrequestexpiry | 1 | tool_dataprivacy |
+    And I wait "1" seconds
+    And I reload the page
+
+    And I should see "Expired" in the "Export all of my personal data" "table_row"
+    And I should not see "Actions"
+
+  @javascript
+  Scenario: As a parent, request data export for my child because I don't trust the little blighter
+    Given I log in as "parent"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I follow "New request"
+    And I set the field "Requesting for" to "Victim User 1"
+    And I press "Save changes"
+    Then I should see "Victim User 1"
+    And I should see "Pending" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Awaiting approval" in the "Victim User 1" "table_row"
+
+    And I log out
+    And I log in as "admin"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "Actions"
+    And I follow "Approve request"
+    And I press "Approve request"
+
+    And I log out
+    And I log in as "parent"
+    And I follow "Profile" in the user menu
+    And I follow "Data requests"
+    And I should see "Approved" in the "Victim User 1" "table_row"
+    And I run all adhoc tasks
+    And I reload the page
+    And I should see "Download ready" in the "Victim User 1" "table_row"
+    And I follow "Actions"
+    And following "Download" should download between "1" and "100000" bytes
+
+    And the following config values are set as admin:
+      | privacyrequestexpiry | 1 | tool_dataprivacy |
+    And I wait "1" seconds
+    And I reload the page
+
+    And I should see "Expired" in the "Victim User 1" "table_row"
+    And I should not see "Actions"
diff --git a/admin/tool/dataprivacy/tests/data_privacy_testcase.php b/admin/tool/dataprivacy/tests/data_privacy_testcase.php
new file mode 100644 (file)
index 0000000..16b48de
--- /dev/null
@@ -0,0 +1,64 @@
+<?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/>.
+
+/**
+ * Parent class for tests which need data privacy functionality.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Michael Hawkins
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Parent class for tests which need data privacy functionality.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Michael Hawkins
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class data_privacy_testcase extends advanced_testcase {
+
+    /**
+     * Assign one or more user IDs as site DPO
+     *
+     * @param stdClass|array $users User ID or array of user IDs to be assigned as site DPO
+     * @return void
+     */
+    protected function assign_site_dpo($users) {
+        global $DB;
+        $this->resetAfterTest();
+
+        if (!is_array($users)) {
+            $users = array($users);
+        }
+
+        $context = context_system::instance();
+
+        // Give the manager role with the capability to manage data requests.
+        $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
+        assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
+
+        // Assign user(s) as manager.
+        foreach ($users as $user) {
+            role_assign($managerroleid, $user->id, $context->id);
+        }
+
+        // Only map the manager role to the DPO role.
+        set_config('dporoles', $managerroleid, 'tool_dataprivacy');
+    }
+}
diff --git a/admin/tool/dataprivacy/tests/expired_data_requests_test.php b/admin/tool/dataprivacy/tests/expired_data_requests_test.php
new file mode 100644 (file)
index 0000000..662d9ab
--- /dev/null
@@ -0,0 +1,173 @@
+<?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/>.
+
+/**
+ * Expired data requests tests.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Michael Hawkins
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use tool_dataprivacy\api;
+use tool_dataprivacy\data_request;
+
+defined('MOODLE_INTERNAL') || die();
+global $CFG;
+
+require_once('data_privacy_testcase.php');
+
+/**
+ * Expired data requests tests.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Michael Hawkins
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_dataprivacy_expired_data_requests_testcase extends data_privacy_testcase {
+
+    /**
+     * Test tearDown.
+     */
+    public function tearDown() {
+        \core_privacy\local\request\writer::reset();
+    }
+
+    /**
+     * Test finding and deleting expired data requests
+     */
+    public function test_data_request_expiry() {
+        global $DB;
+        $this->resetAfterTest();
+        \core_privacy\local\request\writer::setup_real_writer_instance();
+
+        // Set up test users.
+        $this->setAdminUser();
+        $studentuser = $this->getDataGenerator()->create_user();
+        $studentusercontext = context_user::instance($studentuser->id);
+
+        $dpouser = $this->getDataGenerator()->create_user();
+        $this->assign_site_dpo($dpouser);
+
+        // Set request expiry to 5 minutes.
+        set_config('privacyrequestexpiry', 300, 'tool_dataprivacy');
+
+        // Create and approve data request.
+        $this->setUser($studentuser->id);
+        $datarequest = api::create_data_request($studentuser->id, api::DATAREQUEST_TYPE_EXPORT);
+        $this->setAdminUser();
+        ob_start();
+        $this->runAdhocTasks('\tool_dataprivacy\task\initiate_data_request_task');
+        $requestid = $datarequest->get('id');
+        $this->setUser($dpouser->id);
+        api::approve_data_request($requestid);
+        $this->setAdminUser();
+        $this->runAdhocTasks('\tool_dataprivacy\task\process_data_request_task');
+        ob_end_clean();
+
+        // Confirm approved and exported.
+        $request = new data_request($requestid);
+        $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+        $fileconditions = array(
+            'userid' => $studentuser->id,
+            'component' => 'tool_dataprivacy',
+            'filearea' => 'export',
+            'itemid' => $requestid,
+            'contextid' => $studentusercontext->id,
+        );
+        $this->assertEquals(2, $DB->count_records('files', $fileconditions));
+
+        // Run expiry deletion - should not affect test export.
+        $expiredrequests = data_request::get_expired_requests();
+        $this->assertEquals(0, count($expiredrequests));
+        data_request::expire($expiredrequests);
+
+        // Confirm test export was not deleted.
+        $request = new data_request($requestid);
+        $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+        $this->assertEquals(2, $DB->count_records('files', $fileconditions));
+
+        // Change request expiry to 1 second and allow it to elapse.
+        set_config('privacyrequestexpiry', 1, 'tool_dataprivacy');
+        $this->waitForSecond();
+
+        // Re-run expiry deletion, confirm the request expires and export is deleted.
+        $expiredrequests = data_request::get_expired_requests();
+        $this->assertEquals(1, count($expiredrequests));
+        data_request::expire($expiredrequests);
+
+        $request = new data_request($requestid);
+        $this->assertEquals(api::DATAREQUEST_STATUS_EXPIRED, $request->get('status'));
+        $this->assertEquals(0, $DB->count_records('files', $fileconditions));
+    }
+
+
+    /**
+     * Test for \tool_dataprivacy\data_request::is_expired()
+     * Tests for the expected request status to protect from false positive/negative,
+     * then tests is_expired() is returning the expected response.
+     */
+    public function test_is_expired() {
+        $this->resetAfterTest();
+        \core_privacy\local\request\writer::setup_real_writer_instance();
+
+        // Set request expiry beyond this test.
+        set_config('privacyrequestexpiry', 20, 'tool_dataprivacy');
+
+        $admin = get_admin();
+        $this->setAdminUser();
+
+        // Create export request.
+        $datarequest = api::create_data_request($admin->id, api::DATAREQUEST_TYPE_EXPORT);
+        $requestid = $datarequest->get('id');
+
+        // Approve the request.
+        ob_start();
+        $this->runAdhocTasks('\tool_dataprivacy\task\initiate_data_request_task');
+        $this->setAdminUser();
+        api::approve_data_request($requestid);
+        $this->runAdhocTasks('\tool_dataprivacy\task\process_data_request_task');
+        ob_end_clean();
+
+        // Test Download ready (not expired) response.
+        $request = new data_request($requestid);
+        $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+        $result = data_request::is_expired($request);
+        $this->assertFalse($result);
+
+        // Let request expiry time lapse.
+        set_config('privacyrequestexpiry', 1, 'tool_dataprivacy');
+        $this->waitForSecond();
+
+        // Test Download ready (time expired) response.
+        $request = new data_request($requestid);
+        $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+        $result = data_request::is_expired($request);
+        $this->assertTrue($result);
+
+        // Run the expiry task to properly expire the request.
+        ob_start();
+        $task = \core\task\manager::get_scheduled_task('\tool_dataprivacy\task\delete_expired_requests');
+        $task->execute();
+        ob_end_clean();
+
+        // Test Expired response status response.
+        $request = new data_request($requestid);
+        $this->assertEquals(api::DATAREQUEST_STATUS_EXPIRED, $request->get('status'));
+        $result = data_request::is_expired($request);
+        $this->assertTrue($result);
+    }
+}