Merge branch 'MDL-61326-master' of git://github.com/peterRd/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 11 Feb 2019 23:19:07 +0000 (00:19 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 11 Feb 2019 23:19:07 +0000 (00:19 +0100)
816 files changed:
.travis.yml
admin/classes/task_log_table.php [new file with mode: 0644]
admin/cli/uninstall_plugins.php [new file with mode: 0644]
admin/customfields.php [new file with mode: 0644]
admin/environment.xml
admin/index.php
admin/renderer.php
admin/searchareas.php
admin/settings/courses.php
admin/settings/plugins.php
admin/settings/server.php
admin/settings/subsystems.php
admin/tasklogs.php [new file with mode: 0644]
admin/templates/tasklogs.mustache [new file with mode: 0644]
admin/tool/behat/cli/util.php
admin/tool/behat/cli/util_single_run.php
admin/tool/customlang/db/upgrade.php
admin/tool/dataprivacy/amd/build/effective_retention_period.min.js
admin/tool/dataprivacy/amd/src/effective_retention_period.js
admin/tool/dataprivacy/classes/expired_contexts_manager.php
admin/tool/dataprivacy/classes/external.php
admin/tool/dataprivacy/db/upgrade.php
admin/tool/dataprivacy/styles.css
admin/tool/dataprivacy/templates/form-user-selector-suggestion.mustache
admin/tool/dataprivacy/tests/behat/dataexport.feature
admin/tool/dataprivacy/tests/expired_contexts_test.php
admin/tool/dataprivacy/tests/external_test.php
admin/tool/dataprivacy/version.php
admin/tool/log/db/upgrade.php
admin/tool/log/store/database/db/upgrade.php
admin/tool/log/store/standard/db/upgrade.php
admin/tool/monitor/db/upgrade.php
admin/tool/policy/db/upgrade.php
admin/tool/task/cli/schedule_task.php
admin/tool/task/lang/en/tool_task.php
admin/tool/task/renderer.php
admin/tool/task/settings.php
admin/tool/usertours/db/upgrade.php
admin/tool/xmldb/lang/en/tool_xmldb.php
analytics/classes/local/indicator/base.php
analytics/classes/local/indicator/binary.php
analytics/classes/local/indicator/discrete.php
analytics/tests/fixtures/test_indicator_discrete.php [new file with mode: 0644]
analytics/tests/fixtures/test_indicator_random.php
analytics/tests/indicator_test.php [new file with mode: 0644]
analytics/tests/prediction_test.php
auth/cas/auth.php
auth/cas/cas_form.html [deleted file]
auth/cas/db/upgrade.php
auth/cas/lang/en/auth_cas.php
auth/cas/lang/en/deprecated.txt [new file with mode: 0644]
auth/cas/lib.php [new file with mode: 0644]
auth/cas/settings.php
auth/cas/version.php
auth/db/db/upgrade.php
auth/email/db/upgrade.php
auth/ldap/auth.php
auth/ldap/db/upgrade.php
auth/ldap/lang/en/auth_ldap.php
auth/manual/db/upgrade.php
auth/mnet/classes/privacy/provider.php
auth/mnet/db/upgrade.php
auth/mnet/lang/en/auth_mnet.php
auth/none/db/upgrade.php
auth/oauth2/classes/auth.php
auth/oauth2/classes/privacy/provider.php
auth/oauth2/db/upgrade.php
auth/shibboleth/auth.php
auth/shibboleth/db/upgrade.php
auth/shibboleth/index_form.html [deleted file]
auth/shibboleth/lang/en/auth_shibboleth.php
auth/shibboleth/login.php
auth/shibboleth/templates/login_form.mustache [new file with mode: 0644]
backup/backup.class.php
backup/converter/imscc1/lib.php
backup/converter/imscc11/lib.php
backup/moodle2/backup_root_task.class.php
backup/moodle2/backup_settingslib.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_root_task.class.php
backup/moodle2/restore_settingslib.php
backup/moodle2/restore_stepslib.php
backup/util/ui/backup_ui_setting.class.php
badges/backpack_form.php
badges/criteria/award_criteria_activity.php
badges/renderer.php
badges/tests/behat/criteria_activity.feature [new file with mode: 0644]
blocks/admin_bookmarks/block_admin_bookmarks.php
blocks/admin_bookmarks/create.php
blocks/admin_bookmarks/delete.php
blocks/badges/db/upgrade.php
blocks/badges/lang/en/block_badges.php
blocks/calendar_month/db/upgrade.php
blocks/calendar_upcoming/db/upgrade.php
blocks/community/classes/privacy/provider.php
blocks/community/db/upgrade.php
blocks/completionstatus/db/upgrade.php
blocks/course_list/lang/en/block_course_list.php
blocks/course_summary/db/upgrade.php
blocks/html/classes/privacy/provider.php
blocks/html/db/upgrade.php
blocks/lp/db/access.php
blocks/lp/lang/en/block_lp.php
blocks/lp/upgrade.txt [new file with mode: 0644]
blocks/lp/version.php
blocks/myoverview/amd/build/view.min.js
blocks/myoverview/amd/src/view.js
blocks/myoverview/lang/en/block_myoverview.php
blocks/myoverview/lang/en/deprecated.txt
blocks/myoverview/templates/placeholders.mustache
blocks/myoverview/templates/view-cards.mustache
blocks/myoverview/templates/view-list.mustache
blocks/myoverview/templates/view-summary.mustache
blocks/myoverview/tests/behat/block_myoverview_dashboard.feature
blocks/navigation/block_navigation.php
blocks/navigation/db/upgrade.php
blocks/private_files/module.js
blocks/quiz_results/db/upgrade.php
blocks/recent_activity/db/upgrade.php
blocks/recentlyaccessedcourses/amd/build/main.min.js
blocks/recentlyaccessedcourses/amd/src/main.js
blocks/recentlyaccessedcourses/classes/output/main.php
blocks/recentlyaccessedcourses/lang/en/block_recentlyaccessedcourses.php
blocks/recentlyaccessedcourses/templates/main.mustache
blocks/recentlyaccessedcourses/templates/no-courses.mustache
blocks/recentlyaccessedcourses/templates/recentlyaccessedcourses-view.mustache
blocks/recentlyaccesseditems/classes/helper.php
blocks/recentlyaccesseditems/version.php
blocks/rss_client/classes/privacy/provider.php
blocks/rss_client/db/upgrade.php
blocks/rss_client/lang/en/block_rss_client.php
blocks/section_links/db/upgrade.php
blocks/selfcompletion/db/upgrade.php
blocks/settings/db/upgrade.php
blocks/starredcourses/amd/build/main.min.js
blocks/starredcourses/amd/src/main.js
blocks/starredcourses/lang/en/block_starredcourses.php
blocks/starredcourses/templates/no-courses.mustache
blocks/starredcourses/templates/placeholder-course.mustache [deleted file]
blocks/starredcourses/templates/view-cards.mustache [deleted file]
blocks/starredcourses/templates/view.mustache
blocks/timeline/amd/build/event_list.min.js
blocks/timeline/amd/src/event_list.js
cache/stores/memcached/lang/en/cachestore_memcached.php
cohort/tests/behat/upload_cohort_users.feature
comment/locallib.php
competency/tests/api_test.php
competency/tests/generator/lib.php
completion/classes/external.php
completion/classes/privacy/provider.php
completion/tests/externallib_test.php
completion/tests/privacy_test.php
completion/upgrade.txt
composer.json
composer.lock
config-dist.php
course/classes/category.php
course/classes/customfield/course_handler.php [new file with mode: 0644]
course/classes/external/course_summary_exporter.php
course/classes/list_element.php
course/classes/search/customfield.php [new file with mode: 0644]
course/classes/search/mycourse.php
course/classes/search/section.php
course/customfield.php [new file with mode: 0644]
course/dndupload.js
course/dnduploadlib.php
course/edit_form.php
course/externallib.php
course/format/lib.php
course/format/topics/db/upgrade.php
course/format/weeks/db/upgrade.php
course/lib.php
course/moodleform_mod.php
course/renderer.php
course/templates/coursecards.mustache
course/templates/no-courses.mustache [moved from blocks/myoverview/templates/no-courses.mustache with 68% similarity]
course/templates/placeholder-course.mustache [moved from blocks/recentlyaccessedcourses/templates/placeholder-course.mustache with 91% similarity]
course/templates/view-cards.mustache [moved from blocks/recentlyaccessedcourses/templates/view-cards.mustache with 91% similarity]
course/tests/behat/customfields_locked.feature [new file with mode: 0644]
course/tests/behat/customfields_visibility.feature [new file with mode: 0644]
course/tests/courselib_test.php
course/tests/customfield_test.php [new file with mode: 0644]
course/tests/externallib_test.php
course/tests/search_test.php
course/togglecompletion.php
course/upgrade.txt
customfield/amd/build/form.min.js [new file with mode: 0644]
customfield/amd/src/form.js [new file with mode: 0644]
customfield/classes/api.php [new file with mode: 0644]
customfield/classes/category.php [new file with mode: 0644]
customfield/classes/category_controller.php [new file with mode: 0644]
customfield/classes/data.php [new file with mode: 0644]
customfield/classes/data_controller.php [new file with mode: 0644]
customfield/classes/event/category_created.php [new file with mode: 0644]
customfield/classes/event/category_deleted.php [new file with mode: 0644]
customfield/classes/event/category_updated.php [new file with mode: 0644]
customfield/classes/event/field_created.php [new file with mode: 0644]
customfield/classes/event/field_deleted.php [new file with mode: 0644]
customfield/classes/event/field_updated.php [new file with mode: 0644]
customfield/classes/field.php [new file with mode: 0644]
customfield/classes/field_config_form.php [new file with mode: 0644]
customfield/classes/field_controller.php [new file with mode: 0644]
customfield/classes/handler.php [new file with mode: 0644]
customfield/classes/output/field_data.php [new file with mode: 0644]
customfield/classes/output/management.php [new file with mode: 0644]
customfield/classes/output/renderer.php [new file with mode: 0644]
customfield/classes/privacy/customfield_provider.php [new file with mode: 0644]
customfield/classes/privacy/provider.php [new file with mode: 0644]
customfield/edit.php [new file with mode: 0644]
customfield/externallib.php [new file with mode: 0644]
customfield/field/checkbox/classes/data_controller.php [new file with mode: 0644]
customfield/field/checkbox/classes/field_controller.php [new file with mode: 0644]
customfield/field/checkbox/classes/privacy/provider.php [new file with mode: 0644]
customfield/field/checkbox/lang/en/customfield_checkbox.php [new file with mode: 0644]
customfield/field/checkbox/tests/behat/field.feature [new file with mode: 0644]
customfield/field/checkbox/tests/plugin_test.php [new file with mode: 0644]
customfield/field/checkbox/version.php [new file with mode: 0644]
customfield/field/date/classes/data_controller.php [new file with mode: 0644]
customfield/field/date/classes/field_controller.php [new file with mode: 0644]
customfield/field/date/classes/privacy/provider.php [new file with mode: 0644]
customfield/field/date/lang/en/customfield_date.php [new file with mode: 0644]
customfield/field/date/lib.php [new file with mode: 0644]
customfield/field/date/tests/behat/field.feature [new file with mode: 0644]
customfield/field/date/tests/plugin_test.php [new file with mode: 0644]
customfield/field/date/version.php [new file with mode: 0644]
customfield/field/select/classes/data_controller.php [new file with mode: 0644]
customfield/field/select/classes/field_controller.php [new file with mode: 0644]
customfield/field/select/classes/privacy/provider.php [new file with mode: 0644]
customfield/field/select/lang/en/customfield_select.php [new file with mode: 0644]
customfield/field/select/tests/behat/field.feature [new file with mode: 0644]
customfield/field/select/tests/plugin_test.php [new file with mode: 0644]
customfield/field/select/version.php [new file with mode: 0644]
customfield/field/text/classes/data_controller.php [new file with mode: 0644]
customfield/field/text/classes/field_controller.php [new file with mode: 0644]
customfield/field/text/classes/privacy/provider.php [new file with mode: 0644]
customfield/field/text/lang/en/customfield_text.php [new file with mode: 0644]
customfield/field/text/tests/behat/field.feature [new file with mode: 0644]
customfield/field/text/tests/plugin_test.php [new file with mode: 0644]
customfield/field/text/version.php [new file with mode: 0644]
customfield/field/textarea/classes/data_controller.php [new file with mode: 0644]
customfield/field/textarea/classes/field_controller.php [new file with mode: 0644]
customfield/field/textarea/classes/privacy/provider.php [new file with mode: 0644]
customfield/field/textarea/lang/en/customfield_textarea.php [new file with mode: 0644]
customfield/field/textarea/lib.php [new file with mode: 0644]
customfield/field/textarea/tests/behat/default_value.feature [new file with mode: 0644]
customfield/field/textarea/tests/behat/field.feature [new file with mode: 0644]
customfield/field/textarea/tests/plugin_test.php [new file with mode: 0644]
customfield/field/textarea/version.php [new file with mode: 0644]
customfield/lib.php [new file with mode: 0644]
customfield/templates/field_data.mustache [new file with mode: 0644]
customfield/templates/list.mustache [new file with mode: 0644]
customfield/tests/api_test.php [new file with mode: 0644]
customfield/tests/behat/edit_categories.feature [new file with mode: 0644]
customfield/tests/behat/edit_fields_settings.feature [new file with mode: 0644]
customfield/tests/behat/required_field.feature [new file with mode: 0644]
customfield/tests/behat/unique_field.feature [new file with mode: 0644]
customfield/tests/category_controller_test.php [new file with mode: 0644]
customfield/tests/data_controller_test.php [new file with mode: 0644]
customfield/tests/field_controller_test.php [new file with mode: 0644]
customfield/tests/fixtures/test_instance_form.php [new file with mode: 0644]
customfield/tests/generator/lib.php [new file with mode: 0644]
customfield/tests/generator_test.php [new file with mode: 0644]
customfield/tests/privacy_test.php [new file with mode: 0644]
enrol/classes/privacy/provider.php
enrol/database/classes/task/sync_enrolments.php [new file with mode: 0644]
enrol/database/cli/sync.php
enrol/database/db/tasks.php [new file with mode: 0644]
enrol/database/db/upgrade.php
enrol/database/lang/en/enrol_database.php
enrol/database/upgrade.txt [new file with mode: 0644]
enrol/database/version.php
enrol/flatfile/db/upgrade.php
enrol/guest/db/upgrade.php
enrol/imsenterprise/db/upgrade.php
enrol/ldap/lang/en/enrol_ldap.php
enrol/lti/db/upgrade.php
enrol/manual/amd/build/form-potential-user-selector.min.js
enrol/manual/amd/src/form-potential-user-selector.js
enrol/manual/classes/enrol_users_form.php
enrol/manual/db/upgrade.php
enrol/manual/tests/behat/quickenrolment.feature
enrol/mnet/db/upgrade.php
enrol/paypal/db/upgrade.php
enrol/self/db/upgrade.php
files/converter/unoconv/lang/en/fileconverter_unoconv.php
filter/mathjaxloader/db/upgrade.php
filter/mediaplugin/db/upgrade.php
filter/tex/db/upgrade.php
grade/classes/privacy/provider.php
grade/edit/outcome/course_form.html
grade/edit/tree/calculation.php
grade/export/txt/tests/behat/export.feature
grade/export/xml/tests/behat/export.feature
grade/grading/form/guide/db/upgrade.php
grade/grading/form/rubric/db/upgrade.php
grade/grading/form/rubric/styles.css
grade/report/grader/lang/en/gradereport_grader.php
grade/report/overview/db/upgrade.php
grade/report/user/db/upgrade.php
group/autogroup.php
group/autogroup_form.php
group/externallib.php
group/import.php
group/overview.php
group/tests/behat/auto_creation.feature
group/tests/behat/groups_import.feature
group/tests/externallib_test.php
group/tests/fixtures/groups_import.csv
install/lang/el/admin.php
install/lang/el/langconfig.php
install/lang/eu/admin.php
install/lang/eu/error.php
install/lang/eu/install.php
install/lang/hr/error.php
install/lang/pl/moodle.php
install/lang/pt/admin.php
install/lang/ro/install.php
lang/en/admin.php
lang/en/backup.php
lang/en/course.php
lang/en/customfield.php [new file with mode: 0644]
lang/en/deprecated.txt
lang/en/error.php
lang/en/grades.php
lang/en/message.php
lang/en/moodle.php
lang/en/plugin.php
lang/en/role.php
lang/en/search.php
lib/accesslib.php
lib/adminlib.php
lib/amd/build/form-autocomplete.min.js
lib/amd/build/icon_system_fontawesome.min.js
lib/amd/build/inplace_editable.min.js
lib/amd/build/modal.min.js
lib/amd/build/str.min.js
lib/amd/build/templates.min.js
lib/amd/src/form-autocomplete.js
lib/amd/src/icon_system_fontawesome.js
lib/amd/src/inplace_editable.js
lib/amd/src/modal.js
lib/amd/src/str.js
lib/amd/src/templates.js
lib/antivirus/clamav/db/upgrade.php
lib/behat/behat_base.php
lib/behat/form_field/behat_form_autocomplete.php
lib/behat/form_field/behat_form_field.php
lib/behat/lib.php
lib/blocklib.php
lib/classes/analytics/analyser/courses.php
lib/classes/analytics/analyser/site_courses.php
lib/classes/component.php
lib/classes/filetypes.php
lib/classes/oauth2/api.php
lib/classes/output/external.php
lib/classes/output/icon_system_fontawesome.php
lib/classes/output/mustache_template_source_loader.php [new file with mode: 0644]
lib/classes/plugin_manager.php
lib/classes/plugininfo/customfield.php [new file with mode: 0644]
lib/classes/privacy/provider.php
lib/classes/shutdown_manager.php
lib/classes/task/database_logger.php [new file with mode: 0644]
lib/classes/task/logging_trait.php [new file with mode: 0644]
lib/classes/task/logmanager.php [new file with mode: 0644]
lib/classes/task/manager.php
lib/classes/task/messaging_cleanup_task.php
lib/classes/task/task_log_cleanup_task.php [new file with mode: 0644]
lib/classes/task/task_logger.php [new file with mode: 0644]
lib/classes/user.php
lib/cronlib.php
lib/db/access.php
lib/db/caches.php
lib/db/install.xml
lib/db/services.php
lib/db/tasks.php
lib/db/upgrade.php
lib/db/upgradelib.php
lib/editor/atto/classes/privacy/provider.php
lib/editor/atto/db/upgrade.php
lib/editor/atto/plugins/equation/db/upgrade.php
lib/editor/atto/tests/privacy_provider_test.php [moved from lib/editor/atto/tests/privacy_provider.php with 100% similarity]
lib/editor/tinymce/db/upgrade.php
lib/editor/tinymce/plugins/spellchecker/db/upgrade.php
lib/evalmath/evalmath.class.php
lib/evalmath/readme_moodle.txt
lib/filebrowser/file_info_context_course.php
lib/formslib.php
lib/gradelib.php
lib/grouplib.php
lib/modinfolib.php
lib/moodlelib.php
lib/outputcomponents.php
lib/outputlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/phpmailer/README_MOODLE.txt
lib/phpmailer/src/PHPMailer.php
lib/phpminimumversionlib.php
lib/phpunit/classes/arraydataset.php
lib/questionlib.php
lib/requirejs/moodle-config.js
lib/setuplib.php
lib/statslib.php
lib/tablelib.php
lib/templates/form_autocomplete_input.mustache
lib/templates/pix_icon.mustache
lib/templates/pix_icon_fontawesome.mustache
lib/templates/popover_region.mustache
lib/templates/url_select.mustache
lib/testing/generator/data_generator.php
lib/testing/tests/generator_test.php
lib/tests/accesslib_test.php
lib/tests/adhoc_task_test.php
lib/tests/behat/behat_data_generators.php
lib/tests/behat/behat_general.php
lib/tests/component_test.php
lib/tests/environment_test.php
lib/tests/gradelib_test.php
lib/tests/grouplib_test.php
lib/tests/mathslib_test.php
lib/tests/moodlelib_test.php
lib/tests/mustache_template_source_loader_test.php [new file with mode: 0644]
lib/tests/output_external_test.php [deleted file]
lib/tests/outputcomponents_test.php
lib/tests/outputrequirementslib_test.php
lib/tests/questionlib_test.php
lib/tests/statslib_test.php
lib/tests/task_database_logger_test.php [new file with mode: 0644]
lib/tests/task_logging_test.php [new file with mode: 0644]
lib/tests/upgradelib_test.php
lib/tests/user_test.php
lib/typo3/class.t3lib_div.php
lib/typo3/readme_moodle.txt
lib/upgrade.txt
lib/userkey/tests/privacy_provider_test.php [moved from lib/userkey/tests/privacy_provider.php with 100% similarity]
media/player/videojs/lang/en/media_videojs.php
message/amd/build/message_drawer_view_conversation_patcher.min.js
message/amd/build/message_drawer_view_conversation_state_manager.min.js
message/amd/build/message_drawer_view_overview_section.min.js
message/amd/build/message_drawer_view_search.min.js
message/amd/build/message_repository.min.js
message/amd/src/message_drawer_view_conversation_patcher.js
message/amd/src/message_drawer_view_conversation_state_manager.js
message/amd/src/message_drawer_view_overview_section.js
message/amd/src/message_drawer_view_search.js
message/amd/src/message_repository.js
message/classes/api.php
message/classes/helper.php
message/classes/search/base_message.php
message/classes/task/migrate_message_data.php
message/externallib.php
message/lib.php
message/output/airnotifier/classes/privacy/provider.php
message/output/email/db/upgrade.php
message/output/email/message_output_email.php
message/output/jabber/db/upgrade.php
message/output/jabber/lang/en/message_jabber.php
message/output/popup/db/upgrade.php
message/templates/message_drawer_contacts_list.mustache
message/templates/message_drawer_messages_list.mustache
message/templates/message_drawer_non_contacts_list.mustache
message/templates/message_drawer_view_contact_body_content.mustache
message/templates/message_drawer_view_overview_section.mustache
message/templates/message_drawer_view_overview_section_group_messages.mustache
message/templates/message_drawer_view_overview_section_messages.mustache
message/templates/message_drawer_view_search_body.mustache
message/templates/message_drawer_view_search_results_content.mustache
message/tests/api_test.php
message/tests/externallib_test.php
message/tests/helper_test.php [new file with mode: 0644]
message/tests/migrate_message_data_task_test.php
message/tests/search_received_test.php
message/tests/search_sent_test.php
mod/assign/amd/build/grading_navigation.min.js
mod/assign/amd/build/grading_navigation_user_info.min.js
mod/assign/amd/build/participant_selector.min.js
mod/assign/amd/src/grading_navigation.js
mod/assign/amd/src/grading_navigation_user_info.js
mod/assign/amd/src/participant_selector.js
mod/assign/backup/moodle2/backup_assign_stepslib.php
mod/assign/classes/event/remove_submission_form_viewed.php [new file with mode: 0644]
mod/assign/classes/output/grading_app.php
mod/assign/db/access.php
mod/assign/db/install.xml
mod/assign/db/upgrade.php
mod/assign/externallib.php
mod/assign/feedback/comments/db/upgrade.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/editpdf/ajax.php
mod/assign/feedback/editpdf/classes/combined_document.php
mod/assign/feedback/editpdf/classes/document_services.php
mod/assign/feedback/editpdf/classes/renderer.php
mod/assign/feedback/editpdf/classes/task/convert_submissions.php
mod/assign/feedback/editpdf/db/upgrade.php
mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php
mod/assign/feedback/editpdf/styles.css
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/globals.js
mod/assign/feedback/editpdf/yui/src/editor/meta/editor.json
mod/assign/feedback/file/db/upgrade.php
mod/assign/gradingbatchoperationsform.php
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/mod_form.php
mod/assign/module.js
mod/assign/renderer.php
mod/assign/settings.php
mod/assign/styles.css
mod/assign/submission/comments/db/upgrade.php
mod/assign/submission/file/db/upgrade.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/db/upgrade.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/submissionplugin.php
mod/assign/templates/grading_actions.mustache
mod/assign/templates/grading_navigation_user_selector.mustache
mod/assign/tests/behat/allow_another_attempt.feature
mod/assign/tests/behat/edit_student_submission.feature
mod/assign/tests/behat/grading_status.feature
mod/assign/tests/behat/group_submission.feature
mod/assign/tests/behat/hide_grader.feature [new file with mode: 0644]
mod/assign/tests/behat/page_titles.feature [new file with mode: 0644]
mod/assign/tests/behat/remove_submission.feature [new file with mode: 0644]
mod/assign/tests/externallib_test.php
mod/assign/tests/locallib_test.php
mod/assign/upgrade.txt
mod/assign/version.php
mod/assignment/db/upgrade.php
mod/book/db/upgrade.php
mod/book/edit.php
mod/book/edit_form.php
mod/book/lang/en/book.php
mod/book/lib.php
mod/book/locallib.php
mod/book/tests/behat/create_chapters.feature
mod/book/tests/behat/reorganize_chapters.feature
mod/book/tests/behat/show_hide_chapters.feature
mod/book/tool/print/classes/output/print_book_chapter_page.php [new file with mode: 0644]
mod/book/tool/print/classes/output/print_book_page.php [new file with mode: 0644]
mod/book/tool/print/classes/output/renderer.php [new file with mode: 0644]
mod/book/tool/print/index.php
mod/book/tool/print/locallib.php
mod/book/tool/print/print.css
mod/book/tool/print/templates/print_book.mustache [new file with mode: 0644]
mod/book/tool/print/templates/print_book_chapter.mustache [new file with mode: 0644]
mod/chat/db/upgrade.php
mod/chat/lib.php
mod/choice/db/upgrade.php
mod/choice/lib.php
mod/data/db/upgrade.php
mod/data/lib.php
mod/feedback/db/upgrade.php
mod/feedback/lib.php
mod/feedback/tests/behat/coursemapping.feature
mod/folder/db/upgrade.php
mod/folder/lib.php
mod/folder/module.js
mod/forum/classes/task/cron_task.php
mod/forum/classes/task/send_user_digests.php [new file with mode: 0644]
mod/forum/classes/task/send_user_notifications.php [new file with mode: 0644]
mod/forum/db/upgrade.php
mod/forum/deprecatedlib.php
mod/forum/index.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/templates/forum_post_emaildigestfull_textemail.mustache
mod/forum/tests/behat/no_groups_in_course.feature
mod/forum/tests/cron_trait.php [new file with mode: 0644]
mod/forum/tests/generator_trait.php [new file with mode: 0644]
mod/forum/tests/lib_test.php
mod/forum/tests/mail_group_test.php [new file with mode: 0644]
mod/forum/tests/mail_test.php
mod/forum/tests/maildigest_test.php
mod/forum/tests/qanda_test.php [new file with mode: 0644]
mod/glossary/db/upgrade.php
mod/glossary/editcategories.php
mod/glossary/lang/en/glossary.php
mod/glossary/lib.php
mod/glossary/locallib.php
mod/glossary/tests/behat/behat_mod_glossary.php
mod/glossary/tests/behat/categories.feature
mod/glossary/tests/behat/entries_require_approval.feature
mod/imscp/db/upgrade.php
mod/imscp/lib.php
mod/label/db/upgrade.php
mod/label/lib.php
mod/lesson/continue.php
mod/lesson/db/upgrade.php
mod/lesson/lib.php
mod/lesson/locallib.php
mod/lesson/pagetypes/branchtable.php
mod/lesson/pagetypes/cluster.php
mod/lesson/pagetypes/endofbranch.php
mod/lesson/pagetypes/endofcluster.php
mod/lesson/tests/lib_test.php
mod/lesson/tests/locallib_test.php
mod/lti/db/upgrade.php
mod/lti/lib.php
mod/lti/service/gradebookservices/classes/local/service/gradebookservices.php
mod/lti/service/gradebookservices/tests/task_cleanup_test.php
mod/page/db/upgrade.php
mod/page/lib.php
mod/page/tests/lib_test.php
mod/quiz/amd/build/repaginate.min.js [new file with mode: 0644]
mod/quiz/amd/src/repaginate.js [new file with mode: 0644]
mod/quiz/attemptlib.php
mod/quiz/classes/output/edit_renderer.php
mod/quiz/classes/question/bank/add_action_column.php
mod/quiz/db/upgrade.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/report/overview/db/upgrade.php
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/settings.php
mod/quiz/styles.css
mod/quiz/tests/attempt_test.php
mod/quiz/tests/behat/editing_add_from_question_bank.feature
mod/quiz/tests/lib_test.php
mod/quiz/tests/quiz_question_bank_view_test.php [new file with mode: 0644]
mod/quiz/yui/build/moodle-mod_quiz-repaginate/moodle-mod_quiz-repaginate-debug.js [deleted file]
mod/quiz/yui/build/moodle-mod_quiz-repaginate/moodle-mod_quiz-repaginate-min.js [deleted file]
mod/quiz/yui/build/moodle-mod_quiz-repaginate/moodle-mod_quiz-repaginate.js [deleted file]
mod/quiz/yui/src/repaginate/build.json [deleted file]
mod/quiz/yui/src/repaginate/js/repaginate.js [deleted file]
mod/quiz/yui/src/repaginate/meta/repaginate.json [deleted file]
mod/resource/db/upgrade.php
mod/resource/lib.php
mod/resource/locallib.php
mod/resource/tests/lib_test.php
mod/scorm/db/install.xml
mod/scorm/db/upgrade.php
mod/scorm/lib.php
mod/scorm/locallib.php
mod/scorm/tests/lib_test.php
mod/scorm/version.php
mod/survey/db/upgrade.php
mod/survey/lib.php
mod/url/db/upgrade.php
mod/url/lib.php
mod/url/tests/lib_test.php
mod/wiki/db/upgrade.php
mod/wiki/module.js
mod/workshop/classes/external.php
mod/workshop/db/upgrade.php
mod/workshop/form/accumulative/db/upgrade.php
mod/workshop/form/comments/db/upgrade.php
mod/workshop/form/numerrors/db/upgrade.php
mod/workshop/form/rubric/db/upgrade.php
mod/workshop/lib.php
mod/workshop/tests/external_test.php
phpunit.xml.dist
pix/e/file-text.png [new file with mode: 0644]
pix/e/file-text.svg [new file with mode: 0644]
pix/g/g1.png [new file with mode: 0644]
pix/g/g1.svg [new file with mode: 0644]
pix/i/customfield.png [new file with mode: 0644]
pix/i/customfield.svg [new file with mode: 0644]
pix/i/flagged.svg [new file with mode: 0644]
pix/i/unflagged.svg [new file with mode: 0644]
portfolio/boxnet/db/upgrade.php
portfolio/classes/privacy/provider.php
portfolio/googledocs/db/upgrade.php
portfolio/mahara/lang/en/portfolio_mahara.php
portfolio/picasa/db/upgrade.php
privacy/classes/local/request/moodle_content_writer.php
question/behaviour/behaviourbase.php
question/behaviour/manualgraded/db/upgrade.php
question/behaviour/manualgraded/tests/walkthrough_test.php
question/category_class.php
question/classes/bank/delete_action_column.php
question/classes/bank/preview_action_column.php
question/classes/bank/tags_action_column.php
question/engine/bank.php
question/engine/tests/helpers.php
question/import_form.php
question/question.php
question/tests/bank_view_test.php
question/tests/behat/copy_questions.feature
question/tests/behat/question_categories.feature
question/tests/behat/question_categories_idnumber.feature
question/type/calculated/db/upgrade.php
question/type/calculated/edit_calculated_form.php
question/type/calculatedmulti/edit_calculatedmulti_form.php
question/type/calculatedmulti/questiontype.php
question/type/calculatedmulti/tests/helper.php
question/type/calculatedmulti/tests/walkthrough_test.php
question/type/ddimageortext/amd/build/form.min.js
question/type/ddimageortext/amd/build/question.min.js
question/type/ddimageortext/amd/src/form.js
question/type/ddimageortext/amd/src/question.js
question/type/ddimageortext/edit_ddimageortext_form.php
question/type/ddimageortext/lang/en/qtype_ddimageortext.php
question/type/ddimageortext/tests/edit_form_test.php [new file with mode: 0644]
question/type/ddimageortext/tests/helper.php
question/type/ddimageortext/tests/walkthrough_test.php
question/type/ddmarker/amd/build/form.min.js
question/type/ddmarker/amd/build/question.min.js
question/type/ddmarker/amd/src/form.js
question/type/ddmarker/amd/src/question.js
question/type/ddmarker/db/upgrade.php
question/type/ddmarker/edit_ddmarker_form.php
question/type/ddmarker/tests/edit_form_test.php [new file with mode: 0644]
question/type/ddwtos/amd/build/ddwtos.min.js
question/type/ddwtos/amd/src/ddwtos.js
question/type/ddwtos/tests/behat/preview.feature
question/type/ddwtos/tests/edit_form_test.php
question/type/ddwtos/tests/helper.php
question/type/essay/db/upgrade.php
question/type/gapselect/edit_form_base.php
question/type/gapselect/renderer.php
question/type/gapselect/rendererbase.php
question/type/gapselect/tests/behat/basic_test.feature
question/type/gapselect/tests/edit_form_test.php
question/type/gapselect/tests/helper.php
question/type/gapselect/tests/walkthrough_test.php
question/type/match/db/upgrade.php
question/type/match/renderer.php
question/type/multianswer/db/upgrade.php
question/type/multichoice/db/upgrade.php
question/type/numerical/db/upgrade.php
question/type/random/classes/task/remove_unused_questions.php [new file with mode: 0644]
question/type/random/db/tasks.php [new file with mode: 0644]
question/type/random/db/upgrade.php
question/type/random/lang/en/qtype_random.php
question/type/random/tests/cleanup_task_test.php [new file with mode: 0644]
question/type/random/version.php
question/type/randomsamatch/db/upgrade.php
question/type/shortanswer/db/upgrade.php
report/completion/index.php
report/insights/templates/insight.mustache
report/insights/templates/insight_details.mustache
report/insights/templates/insights_list.mustache
report/performance/lang/en/report_performance.php
report/progress/index.php
report/security/lang/en/report_security.php
report/security/locallib.php
repository/boxnet/db/upgrade.php
repository/classes/privacy/provider.php
repository/dropbox/db/upgrade.php
repository/flickr/db/upgrade.php
repository/googledocs/db/upgrade.php
repository/onedrive/classes/privacy/provider.php
repository/onedrive/db/upgrade.php
repository/picasa/db/upgrade.php
search/classes/area_category.php [new file with mode: 0644]
search/classes/base.php
search/classes/base_block.php
search/classes/base_mod.php
search/classes/manager.php
search/classes/output/form/search.php
search/classes/output/renderer.php
search/engine/solr/settings.php
search/index.php
search/tests/area_category_test.php [new file with mode: 0644]
search/tests/base_test.php
search/tests/manager_test.php
tag/tests/external_test.php
theme/boost/config.php
theme/boost/scss/editor.scss [new file with mode: 0644]
theme/boost/scss/moodle/admin.scss
theme/boost/scss/moodle/blocks.scss
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/course.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/question.scss
theme/boost/scss/preset/default.scss
theme/boost/style/moodle.css
theme/boost/templates/core/form_autocomplete_input.mustache
theme/boost/templates/core_form/element-template.mustache
theme/boost/templates/login.mustache
theme/boost/templates/mod_assign/grading_actions.mustache
theme/boost/templates/mod_assign/grading_navigation_user_selector.mustache
theme/boost/templates/navbar.mustache
theme/boost/upgrade.txt
theme/bootstrapbase/less/moodle/blocks.less
theme/bootstrapbase/less/moodle/bs4-compat.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/templates/block_myoverview/course-action-menu.mustache
theme/bootstrapbase/templates/core_message/message_drawer_view_conversation_header_content_type_private.mustache
theme/bootstrapbase/templates/core_message/message_drawer_view_overview_section.mustache
theme/more/db/upgrade.php
theme/upgrade.txt
user/amd/build/status_field.min.js
user/amd/src/status_field.js
user/classes/output/myprofile/renderer.php
user/classes/output/status_field.php
user/classes/participants_table.php
user/classes/privacy/provider.php
user/classes/search/user.php
user/externallib.php
user/index.php
user/lib.php
user/profile/field/checkbox/classes/privacy/provider.php
user/profile/field/datetime/classes/privacy/provider.php
user/profile/field/menu/classes/privacy/provider.php
user/profile/field/text/classes/privacy/provider.php
user/profile/field/textarea/classes/privacy/provider.php
user/profile/lib.php
user/templates/status_details.mustache
user/templates/status_field.mustache
user/tests/behat/edit_user_enrolment.feature
user/tests/externallib_test.php
user/tests/search_test.php
userpix/index.php
version.php
webservice/classes/privacy/provider.php

index aa0db16..ec1b68c 100644 (file)
@@ -14,7 +14,7 @@ language: php
 php:
     # We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
     - 7.2
-    - 7.0
+    - 7.1
 
 addons:
   postgresql: "9.6"
@@ -63,7 +63,7 @@ matrix:
         # Exclude it on all versions except for 7.2
 
         - env: DB=mysqli   TASK=PHPUNIT
-          php: 7.0
+          php: 7.1
 
 cache:
     directories:
diff --git a/admin/classes/task_log_table.php b/admin/classes/task_log_table.php
new file mode 100644 (file)
index 0000000..2bb892d
--- /dev/null
@@ -0,0 +1,288 @@
+<?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/>.
+
+/**
+ * Task log table.
+ *
+ * @package    core_admin
+ * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_admin;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/tablelib.php');
+
+/**
+ * Table to display list of task logs.
+ *
+ * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class task_log_table extends \table_sql {
+
+    /**
+     * Constructor for the task_log table.
+     *
+     * @param   string      $filter
+     * @param   int         $resultfilter
+     */
+    public function __construct(string $filter = '', int $resultfilter = null) {
+        global $DB;
+
+        if (-1 === $resultfilter) {
+            $resultfilter = null;
+        }
+
+        parent::__construct('tasklogs');
+
+        $columnheaders = [
+            'classname'  => get_string('name'),
+            'type'       => get_string('tasktype', 'admin'),
+            'userid'     => get_string('user', 'admin'),
+            'timestart'  => get_string('task_starttime', 'admin'),
+            'duration'   => get_string('task_duration', 'admin'),
+            'db'         => get_string('task_dbstats', 'admin'),
+            'result'     => get_string('task_result', 'admin'),
+            'actions'    => '',
+        ];
+        $this->define_columns(array_keys($columnheaders));
+        $this->define_headers(array_values($columnheaders));
+
+        // The name column is a header.
+        $this->define_header_column('classname');
+
+        // This table is not collapsible.
+        $this->collapsible(false);
+
+        // The actions class should not wrap. Use the BS text utility class.
+        $this->column_class('actions', 'text-nowrap');
+
+        // Allow pagination.
+        $this->pageable(true);
+
+        // Allow sorting. Default to sort by timestarted DESC.
+        $this->sortable(true, 'timestart', SORT_DESC);
+
+        // Add filtering.
+        $where = [];
+        $params = [];
+        if (!empty($filter)) {
+            $orwhere = [];
+            $filter = str_replace('\\', '\\\\', $filter);
+
+            // Check the class name.
+            $orwhere[] = $DB->sql_like('classname', ':classfilter', false, false);
+            $params['classfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
+
+            $orwhere[] = $DB->sql_like('output', ':outputfilter', false, false);
+            $params['outputfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
+
+            $where[] = "(" . implode(' OR ', $orwhere) . ")";
+        }
+
+        if (null !== $resultfilter) {
+            $where[] = 'tl.result = :result';
+            $params['result'] = $resultfilter;
+        }
+
+        $where = implode(' AND ', $where);
+
+        $this->set_sql('', '', $where, $params);
+    }
+
+    /**
+     * Query the db. Store results in the table object for use by build_table.
+     *
+     * @param int $pagesize size of page for paginated displayed table.
+     * @param bool $useinitialsbar do you want to use the initials bar. Bar
+     * will only be used if there is a fullname column defined for the table.
+     */
+    public function query_db($pagesize, $useinitialsbar = true) {
+        global $DB;
+
+        // Fetch the attempts.
+        $sort = $this->get_sql_sort();
+        if ($sort) {
+            $sort = "ORDER BY $sort";
+        }
+
+        $extrafields = get_extra_user_fields(\context_system::instance());
+        $userfields = \user_picture::fields('u', $extrafields, 'userid2', 'user');
+
+        $where = '';
+        if (!empty($this->sql->where)) {
+            $where = "WHERE {$this->sql->where}";
+        }
+
+        $sql = "SELECT
+                    tl.*,
+                    tl.dbreads + tl.dbwrites AS db,
+                    tl.timeend - tl.timestart AS duration,
+                    {$userfields}
+                FROM {task_log} tl
+           LEFT JOIN {user} u ON u.id = tl.userid
+                {$where}
+                {$sort}";
+
+        $this->pagesize($pagesize, $DB->count_records_sql("SELECT COUNT('x') FROM {task_log} tl {$where}", $this->sql->params));
+        if (!$this->is_downloading()) {
+            $this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
+        } else {
+            $this->rawdata = $DB->get_records_sql($sql, $this->sql->params);
+        }
+    }
+
+    /**
+     * Format the name cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_classname($row) : string {
+        $output = '';
+        if (class_exists($row->classname)) {
+            $task = new $row->classname;
+            if ($task instanceof \core\task\scheduled_task) {
+                $output = $task->get_name();
+            }
+        }
+
+        $output .= \html_writer::tag('div', "\\{$row->classname}", [
+                'class' => 'task-class',
+            ]);
+        return $output;
+    }
+
+    /**
+     * Format the type cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_type($row) : string {
+        if (\core\task\database_logger::TYPE_SCHEDULED == $row->type) {
+            return get_string('task_type:scheduled', 'admin');
+        } else {
+            return get_string('task_type:adhoc', 'admin');
+        }
+    }
+
+    /**
+     * Format the timestart cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_result($row) : string {
+        if ($row->result) {
+            return get_string('task_result:failed', 'admin');
+        } else {
+            return get_string('success');
+        }
+    }
+
+    /**
+     * Format the timestart cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_timestart($row) : string {
+        return userdate($row->timestart, get_string('strftimedatetimeshort', 'langconfig'));
+    }
+
+    /**
+     * Format the duration cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_duration($row) : string {
+        $duration = round($row->timeend - $row->timestart, 2);
+
+        if (empty($duration)) {
+            // The format_time function returns 'now' when the difference is exactly 0.
+            // Note: format_time performs concatenation in exactly this fashion so we should do this for consistency.
+            return '0 ' . get_string('secs', 'moodle');
+        }
+
+        return format_time($duration);
+    }
+
+    /**
+     * Format the DB details cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_db($row) : string {
+        $output = '';
+
+        $output .= \html_writer::div(get_string('task_stats:dbreads', 'admin', $row->dbreads));
+        $output .= \html_writer::div(get_string('task_stats:dbwrites', 'admin', $row->dbwrites));
+
+        return $output;
+    }
+
+    /**
+     * Format the actions cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_actions($row) : string {
+        global $OUTPUT;
+
+        $actions = [];
+
+        $url = new \moodle_url('/admin/tasklogs.php', ['logid' => $row->id]);
+
+        // Quick view.
+        $actions[] = $OUTPUT->action_icon(
+            $url,
+            new \pix_icon('e/search', get_string('view')),
+            new \popup_action('click', $url)
+        );
+
+        // Download.
+        $actions[] = $OUTPUT->action_icon(
+            new \moodle_url($url, ['download' => true]),
+            new \pix_icon('t/download', get_string('download'))
+        );
+
+        return implode('&nbsp;', $actions);
+    }
+
+    /**
+     * Format the user cell.
+     *
+     * @param   \stdClass $row
+     * @return  string
+     */
+    public function col_userid($row) : string {
+        if (empty($row->userid)) {
+            return '';
+        }
+
+        $user = (object) [];
+        username_load_fields_from_object($user, $row, 'user');
+
+        return fullname($user);
+    }
+}
diff --git a/admin/cli/uninstall_plugins.php b/admin/cli/uninstall_plugins.php
new file mode 100644 (file)
index 0000000..e093d89
--- /dev/null
@@ -0,0 +1,163 @@
+<?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/>.
+
+/**
+ * CLI script to uninstall plugins.
+ *
+ * @package    core
+ * @subpackage cli
+ * @copyright  2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+
+require(__DIR__ . '/../../config.php');
+require_once($CFG->libdir . '/clilib.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+$help = "Command line tool to uninstall plugins.
+
+Options:
+    -h --help                   Print this help.
+    --show-all                  Displays a list of all installed plugins.
+    --show-missing              Displays a list of plugins missing from disk.
+    --purge-missing             Uninstall all missing from disk plugins.
+    --plugins=<plugin name>     A comma separated list of plugins to be uninstalled. E.g. mod_assign,mod_forum
+    --run                       Execute uninstall. If this option is not set, then the script will be run in a dry mode.
+
+Examples:
+
+    # php uninstall_plugins.php  --show-all
+        Prints tab-separated list of all installed plugins.
+
+    # php uninstall_plugins.php  --show-missing
+        Prints tab-separated list of all missing from disk plugins.
+
+    # php uninstall_plugins.php  --purge-missing
+        A dry run of uninstalling all missing plugins.
+
+    # php uninstall_plugins.php  --purge-missing --run
+        Run uninstall of all missing plugins.
+
+    # php uninstall_plugins.php  --plugins=mod_assign,mod_forum
+        A dry run of uninstalling mod_assign and mod_forum plugins.
+
+    # php uninstall_plugins.php  --plugins=mod_assign,mod_forum --run
+        Run uninstall for mod_assign and mod_forum plugins.
+";
+
+list($options, $unrecognised) = cli_get_params([
+    'help' => false,
+    'show-all' => false,
+    'show-missing' => false,
+    'purge-missing' => false,
+    'plugins' => false,
+    'run' => false,
+], [
+    'h' => 'help'
+]);
+
+if ($unrecognised) {
+    $unrecognised = implode(PHP_EOL.'  ', $unrecognised);
+    cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised));
+}
+
+if ($options['help']) {
+    cli_writeln($help);
+    exit(0);
+}
+
+$pluginman = core_plugin_manager::instance();
+$plugininfo = $pluginman->get_plugins();
+
+if ($options['show-all'] || $options['show-missing']) {
+    foreach ($plugininfo as $type => $plugins) {
+        foreach ($plugins as $name => $plugin) {
+            $pluginstring = $plugin->component . "\t" . $plugin->displayname;
+
+            if ($options['show-all']) {
+                cli_writeln($pluginstring);
+            } else {
+                if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
+                    cli_writeln($pluginstring);
+                }
+            }
+        }
+    }
+
+    exit(0);
+}
+
+if ($options['purge-missing']) {
+    foreach ($plugininfo as $type => $plugins) {
+        foreach ($plugins as $name => $plugin) {
+            if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
+
+                $pluginstring = $plugin->component . "\t" . $plugin->displayname;
+
+                if ($pluginman->can_uninstall_plugin($plugin->component)) {
+                    if ($options['run']) {
+                        cli_writeln('Uninstalling: ' . $pluginstring);
+
+                        $progress = new progress_trace_buffer(new text_progress_trace(), true);
+                        $pluginman->uninstall_plugin($plugin->component, $progress);
+                        $progress->finished();
+                        cli_write($progress->get_buffer());
+                    } else {
+                        cli_writeln('Will be uninstalled: ' . $pluginstring);
+                    }
+                } else {
+                    cli_writeln('Can not be uninstalled: ' . $pluginstring);
+                }
+            }
+        }
+    }
+
+    exit(0);
+}
+
+if ($options['plugins']) {
+    $components = explode(',', $options['plugins']);
+    foreach ($components as $component) {
+        $plugin = $pluginman->get_plugin_info($component);
+
+        if (is_null($plugin)) {
+            cli_writeln('Unknown plugin: ' . $component);
+        } else {
+            $pluginstring = $plugin->component . "\t" . $plugin->displayname;
+
+            if ($pluginman->can_uninstall_plugin($plugin->component)) {
+                if ($options['run']) {
+                    cli_writeln('Uninstalling: ' . $pluginstring);
+                    $progress = new progress_trace_buffer(new text_progress_trace(), true);
+                    $pluginman->uninstall_plugin($plugin->component, $progress);
+                    $progress->finished();
+                    cli_write($progress->get_buffer());
+                } else {
+                    cli_writeln('Will be uninstalled: ' . $pluginstring);
+                }
+            } else {
+                cli_writeln('Can not be uninstalled: ' . $pluginstring);
+            }
+        }
+    }
+
+    exit(0);
+}
+
+cli_writeln($help);
+exit(0);
diff --git a/admin/customfields.php b/admin/customfields.php
new file mode 100644 (file)
index 0000000..2f7681e
--- /dev/null
@@ -0,0 +1,62 @@
+<?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/>.
+
+/**
+ * Allows the admin to enable, disable and uninstall custom fields
+ *
+ * @package    core_admin
+ * @copyright  2018 Daniel Neis Araujo
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$action  = required_param('action', PARAM_ALPHANUMEXT);
+$customfieldname = required_param('field', PARAM_PLUGIN);
+
+$syscontext = context_system::instance();
+$PAGE->set_url('/admin/customfields.php');
+$PAGE->set_context($syscontext);
+
+require_login();
+require_capability('moodle/site:config', $syscontext);
+require_sesskey();
+
+$return = new moodle_url('/admin/settings.php', array('section' => 'managecustomfields'));
+
+$customfieldplugins = core_plugin_manager::instance()->get_plugins_of_type('customfield');
+$sortorder = array_flip(array_keys($customfieldplugins));
+
+if (!isset($customfieldplugins[$customfieldname])) {
+    print_error('customfieldnotfound', 'error', $return, $customfieldname);
+}
+
+switch ($action) {
+    case 'disable':
+        if ($customfieldplugins[$customfieldname]->is_enabled()) {
+            set_config('disabled', 1, 'customfield_'. $customfieldname);
+            core_plugin_manager::reset_caches();
+        }
+        break;
+    case 'enable':
+        if (!$customfieldplugins[$customfieldname]->is_enabled()) {
+            unset_config('disabled', 'customfield_'. $customfieldname);
+            core_plugin_manager::reset_caches();
+        }
+        break;
+}
+redirect($return);
index 40e66be..ca20d0c 100644 (file)
       </CUSTOM_CHECK>
     </CUSTOM_CHECKS>
   </MOODLE>
+  <MOODLE version="3.7" requires="3.2">
+    <UNICODE level="required">
+      <FEEDBACK>
+        <ON_ERROR message="unicoderequired" />
+      </FEEDBACK>
+    </UNICODE>
+    <DATABASE level="required">
+      <VENDOR name="mariadb" version="5.5.31" />
+      <VENDOR name="mysql" version="5.6" />
+      <VENDOR name="postgres" version="9.4" />
+      <VENDOR name="mssql" version="10.0" />
+      <VENDOR name="oracle" version="11.2" />
+    </DATABASE>
+    <PHP version="7.1.0" level="required">
+    </PHP>
+    <PCREUNICODE level="optional">
+      <FEEDBACK>
+        <ON_CHECK message="pcreunicodewarning" />
+      </FEEDBACK>
+    </PCREUNICODE>
+    <PHP_EXTENSIONS>
+      <PHP_EXTENSION name="iconv" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="iconvrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="mbstring" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="mbstringrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="curl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="curlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="openssl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="opensslrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="tokenizer" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="tokenizerrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlrpc" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="xmlrpcrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="soap" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="soaprecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="ctype" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ctyperequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zip" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ziprequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zlib" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="gd" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="gdrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="simplexml" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="simplexmlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="spl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="splrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="pcre" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="dom" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xml" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlreader" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="intl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="intlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="json" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="hash" level="required"/>
+      <PHP_EXTENSION name="fileinfo" level="required"/>
+    </PHP_EXTENSIONS>
+    <PHP_SETTINGS>
+      <PHP_SETTING name="memory_limit" value="96M" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="settingmemorylimit" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="file_uploads" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingfileuploads" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="opcache.enable" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opcacherecommended" />
+        </FEEDBACK>
+      </PHP_SETTING>
+    </PHP_SETTINGS>
+    <CUSTOM_CHECKS>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="unsupporteddbstorageengine" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="quizattemptsupgradedmessage" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="slashargumentswarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="unsupporteddbtablerowformat" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="unoconvwarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="libcurlwarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="unsupporteddbfileformat" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="unsupporteddbfilepertable" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="unsupporteddblargeprefix" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="ishttpswarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="incompleteunicodesupport" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="sixtyfourbitswarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+    </CUSTOM_CHECKS>
+  </MOODLE>
 </COMPATIBILITY_MATRIX>
index e04c319..c34a437 100644 (file)
@@ -823,9 +823,11 @@ if (isset($SESSION->pluginuninstallreturn)) {
 // Print default admin page with notifications.
 $errorsdisplayed = defined('WARN_DISPLAY_ERRORS_ENABLED');
 
-// We make the assumption that at least one schedule task should run once per day.
-$lastcron = $DB->get_field_sql('SELECT MAX(lastruntime) FROM {task_scheduled}');
+$lastcron = get_config('tool_task', 'lastcronstart');
 $cronoverdue = ($lastcron < time() - 3600 * 24);
+$lastcroninterval = get_config('tool_task', 'lastcroninterval');
+$expectedfrequency = $CFG->expectedcronfrequency ?? 200;
+$croninfrequent = !$cronoverdue && ($lastcroninterval > $expectedfrequency || $lastcron < time() - $expectedfrequency);
 $dbproblems = $DB->diagnose();
 $maintenancemode = !empty($CFG->maintenance_enabled);
 
@@ -886,4 +888,4 @@ $output = $PAGE->get_renderer('core', 'admin');
 echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed, $cronoverdue, $dbproblems,
                                        $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
                                        $registered, $cachewarnings, $eventshandlers, $themedesignermode, $devlibdir,
-                                       $mobileconfigured, $overridetossl, $invalidforgottenpasswordurl);
+                                       $mobileconfigured, $overridetossl, $invalidforgottenpasswordurl, $croninfrequent);
index da59ef6..57eaa0e 100644 (file)
@@ -281,6 +281,7 @@ class core_admin_renderer extends plugin_renderer_base {
      * @param bool $mobileconfigured Whether the mobile web services have been enabled
      * @param bool $overridetossl Whether or not ssl is being forced.
      * @param bool $invalidforgottenpasswordurl Whether the forgotten password URL does not link to a valid URL.
+     * @param bool $croninfrequent If true, warn that cron hasn't run in the past few minutes
      *
      * @return string HTML to output.
      */
@@ -288,7 +289,7 @@ class core_admin_renderer extends plugin_renderer_base {
             $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch,
             $buggyiconvnomb, $registered, array $cachewarnings = array(), $eventshandlers = 0,
             $themedesignermode = false, $devlibdir = false, $mobileconfigured = false,
-            $overridetossl = false, $invalidforgottenpasswordurl = false) {
+            $overridetossl = false, $invalidforgottenpasswordurl = false, $croninfrequent = false) {
         global $CFG;
         $output = '';
 
@@ -302,6 +303,7 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->display_errors_warning($errorsdisplayed);
         $output .= $this->buggy_iconv_warning($buggyiconvnomb);
         $output .= $this->cron_overdue_warning($cronoverdue);
+        $output .= $this->cron_infrequent_warning($croninfrequent);
         $output .= $this->db_problems($dbproblems);
         $output .= $this->maintenance_mode_warning($maintenancemode);
         $output .= $this->overridetossl_warning($overridetossl);
@@ -614,6 +616,24 @@ class core_admin_renderer extends plugin_renderer_base {
                 $this->help_icon('cron', 'admin'));
     }
 
+    /**
+     * Render an appropriate message if cron is not being run frequently (recommended every minute).
+     *
+     * @param bool $croninfrequent
+     * @return string HTML to output.
+     */
+    public function cron_infrequent_warning(bool $croninfrequent) : string {
+        global $CFG;
+
+        if (!$croninfrequent) {
+            return '';
+        }
+
+        $expectedfrequency = $CFG->expectedcronfrequency ?? 200;
+        return $this->warning(get_string('croninfrequent', 'admin', $expectedfrequency) . '&nbsp;' .
+                $this->help_icon('cron', 'admin'));
+    }
+
     /**
      * Render an appropriate message if there are any problems with the DB set-up.
      * @param bool $dbproblems
index 911a86b..fb5b0a3 100644 (file)
@@ -153,6 +153,7 @@ $table = new html_table();
 $table->id = 'core-search-areas';
 $table->head = [
     get_string('searcharea', 'search'),
+    get_string('searchareacategories', 'search'),
     get_string('enable'),
     get_string('newestdocindexed', 'admin'),
     get_string('searchlastrun', 'admin'),
@@ -165,6 +166,14 @@ foreach ($searchareas as $area) {
     $areaid = $area->get_area_id();
     $columns = array(new html_table_cell($area->get_visible_name()));
 
+    $areacategories = [];
+    foreach (\core_search\manager::get_search_area_categories() as $category) {
+        if (key_exists($areaid, $category->get_areas())) {
+            $areacategories[] = $category->get_visiblename();
+        }
+    }
+    $columns[] = new html_table_cell(implode(', ', $areacategories));
+
     if ($area->is_enabled()) {
         $columns[] = $OUTPUT->action_icon(admin_searcharea_action_url('disable', $areaid),
             new pix_icon('t/hide', get_string('disable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
index f23bf5f..739c96e 100644 (file)
@@ -37,6 +37,12 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
             array('moodle/category:manage', 'moodle/course:create')
         )
     );
+    $ADMIN->add('courses',
+        new admin_externalpage('course_customfield', new lang_string('course_customfield', 'admin'),
+            $CFG->wwwroot . '/course/customfield.php',
+            array('moodle/course:configurecustomfields')
+        )
+    );
     $ADMIN->add('courses',
         new admin_externalpage('addcategory', new lang_string('addcategory', 'admin'),
             new moodle_url('/course/editcategory.php', array('parent' => 0)),
index 9a2202f..616d10e 100644 (file)
@@ -59,6 +59,18 @@ if ($hassiteconfig) {
         $plugin->load_settings($ADMIN, 'formatsettings', $hassiteconfig);
     }
 
+    // Custom fields.
+    $ADMIN->add('modules', new admin_category('customfieldsettings', new lang_string('customfields', 'core_customfield')));
+    $temp = new admin_settingpage('managecustomfields', new lang_string('managecustomfields', 'core_admin'));
+    $temp->add(new admin_setting_managecustomfields());
+    $ADMIN->add('customfieldsettings', $temp);
+    $plugins = core_plugin_manager::instance()->get_plugins_of_type('customfield');
+    core_collator::asort_objects_by_property($plugins, 'displayname');
+    foreach ($plugins as $plugin) {
+        /** @var \core\plugininfo\customfield $plugin */
+        $plugin->load_settings($ADMIN, 'customfieldsettings', $hassiteconfig);
+    }
+
     // blocks
     $ADMIN->add('modules', new admin_category('blocksettings', new lang_string('blocks')));
     $ADMIN->add('blocksettings', new admin_page_manageblocks());
@@ -573,6 +585,25 @@ if ($hassiteconfig) {
             new lang_string('searchallavailablecourses_desc', 'admin'),
             0, $options));
 
+    // Search display options.
+    $temp->add(new admin_setting_heading('searchdisplay', new lang_string('searchdisplay', 'admin'), ''));
+    $temp->add(new admin_setting_configcheckbox('searchenablecategories',
+        new lang_string('searchenablecategories', 'admin'),
+        new lang_string('searchenablecategories_desc', 'admin'),
+        0));
+    $options = [];
+    foreach (\core_search\manager::get_search_area_categories() as $category) {
+        $options[$category->get_name()] = $category->get_visiblename();
+    }
+    $temp->add(new admin_setting_configselect('searchdefaultcategory',
+        new lang_string('searchdefaultcategory', 'admin'),
+        new lang_string('searchdefaultcategory_desc', 'admin'),
+        \core_search\manager::SEARCH_AREA_CATEGORY_ALL, $options));
+    $temp->add(new admin_setting_configcheckbox('searchhideallcategory',
+        new lang_string('searchhideallcategory', 'admin'),
+        new lang_string('searchhideallcategory_desc', 'admin'),
+        0));
+
     $ADMIN->add('searchplugins', $temp);
     $ADMIN->add('searchplugins', new admin_externalpage('searchareas', new lang_string('searchareas', 'admin'),
         new moodle_url('/admin/searchareas.php')));
index f4c24c0..96c7871 100644 (file)
@@ -4,7 +4,6 @@
 
 if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
 
-
 // "systempaths" settingpage
 $temp = new admin_settingpage('systempaths', new lang_string('systempaths','admin'));
 $temp->add(new admin_setting_configexecutable('pathtophp', new lang_string('pathtophp', 'admin'),
@@ -212,6 +211,92 @@ $temp->add(new admin_setting_configtext('curltimeoutkbitrate', new lang_string('
 $ADMIN->add('server', $temp);
 
 
+$ADMIN->add('server', new admin_category('taskconfig', new lang_string('taskadmintitle', 'admin')));
+$temp = new admin_settingpage('taskprocessing', new lang_string('taskprocessing','admin'));
+$temp->add(
+    new admin_setting_configtext(
+        'task_scheduled_concurrency_limit',
+        new lang_string('task_scheduled_concurrency_limit', 'admin'),
+        new lang_string('task_scheduled_concurrency_limit_desc', 'admin'),
+        3,
+        PARAM_INT
+    )
+);
+
+$temp->add(
+    new admin_setting_configduration(
+        'task_scheduled_max_runtime',
+        new lang_string('task_scheduled_max_runtime', 'admin'),
+        new lang_string('task_scheduled_max_runtime_desc', 'admin'),
+        30 * MINSECS
+    )
+);
+
+$temp->add(
+    new admin_setting_configtext(
+        'task_adhoc_concurrency_limit',
+        new lang_string('task_adhoc_concurrency_limit', 'admin'),
+        new lang_string('task_adhoc_concurrency_limit_desc', 'admin'),
+        3,
+        PARAM_INT
+    )
+);
+
+$temp->add(
+    new admin_setting_configduration(
+        'task_adhoc_max_runtime',
+        new lang_string('task_adhoc_max_runtime', 'admin'),
+        new lang_string('task_adhoc_max_runtime_desc', 'admin'),
+        30 * MINSECS
+    )
+);
+$ADMIN->add('taskconfig', $temp);
+
+$temp = new admin_settingpage('tasklogging', new lang_string('tasklogging','admin'));
+$temp->add(
+    new admin_setting_configselect(
+        'task_logmode',
+        new lang_string('task_logmode', 'admin'),
+        new lang_string('task_logmode_desc', 'admin'),
+        \core\task\logmanager::MODE_ALL,
+        [
+            \core\task\logmanager::MODE_ALL => new lang_string('task_logmode_all', 'admin'),
+            \core\task\logmanager::MODE_FAILONLY => new lang_string('task_logmode_failonly', 'admin'),
+            \core\task\logmanager::MODE_NONE => new lang_string('task_logmode_none', 'admin'),
+        ]
+    )
+);
+
+if (\core\task\logmanager::uses_standard_settings()) {
+    $temp->add(
+        new admin_setting_configduration(
+            'task_logretention',
+            new \lang_string('task_logretention', 'admin'),
+            new \lang_string('task_logretention_desc', 'admin'),
+            28 * DAYSECS
+        )
+    );
+
+    $temp->add(
+        new admin_setting_configtext(
+            'task_logretainruns',
+            new \lang_string('task_logretainruns', 'admin'),
+            new \lang_string('task_logretainruns_desc', 'admin'),
+            20,
+            PARAM_INT
+        )
+    );
+}
+$ADMIN->add('taskconfig', $temp);
+
+if (\core\task\logmanager::uses_standard_settings()) {
+    $ADMIN->add('taskconfig', new admin_externalpage(
+        'tasklogs',
+        new lang_string('tasklogs','admin'),
+        "{$CFG->wwwroot}/{$CFG->admin}/tasklogs.php"
+    ));
+}
+
 // E-mail settings.
 $ADMIN->add('server', new admin_category('email', new lang_string('categoryemail', 'admin')));
 
index db26385..a108b42 100644 (file)
@@ -21,8 +21,28 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
         0)
     );
 
-    $options = array(DAYSECS=>new lang_string('secondstotime86400'), WEEKSECS=>new lang_string('secondstotime604800'), 2620800=>new lang_string('nummonths', 'moodle', 1), 15724800=>new lang_string('nummonths', 'moodle', 6),0=>new lang_string('never'));
-    $optionalsubsystems->add(new admin_setting_configselect('messagingdeletereadnotificationsdelay', new lang_string('messagingdeletereadnotificationsdelay', 'admin'), new lang_string('configmessagingdeletereadnotificationsdelay', 'admin'), 604800, $options));
+    $options = array(
+        DAYSECS => new lang_string('secondstotime86400'),
+        WEEKSECS => new lang_string('secondstotime604800'),
+        2620800 => new lang_string('nummonths', 'moodle', 1),
+        7862400 => new lang_string('nummonths', 'moodle', 3),
+        15724800 => new lang_string('nummonths', 'moodle', 6),
+        0 => new lang_string('never')
+    );
+    $optionalsubsystems->add(new admin_setting_configselect(
+        'messagingdeletereadnotificationsdelay',
+        new lang_string('messagingdeletereadnotificationsdelay', 'admin'),
+        new lang_string('configmessagingdeletereadnotificationsdelay', 'admin'),
+        604800,
+        $options)
+    );
+    $optionalsubsystems->add(new admin_setting_configselect(
+        'messagingdeleteallnotificationsdelay',
+        new lang_string('messagingdeleteallnotificationsdelay', 'admin'),
+        new lang_string('configmessagingdeleteallnotificationsdelay', 'admin'),
+        2620800,
+        $options)
+    );
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('messagingallowemailoverride', new lang_string('messagingallowemailoverride', 'admin'), new lang_string('configmessagingallowemailoverride','admin'), 0));
 
diff --git a/admin/tasklogs.php b/admin/tasklogs.php
new file mode 100644 (file)
index 0000000..6684a7e
--- /dev/null
@@ -0,0 +1,92 @@
+<?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/>.
+
+/**
+ * Task log.
+ *
+ * @package    admin
+ * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
+ * @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->libdir}/tablelib.php");
+require_once("{$CFG->libdir}/filelib.php");
+
+$filter = optional_param('filter', '', PARAM_RAW);
+$result = optional_param('result', null, PARAM_INT);
+
+$pageurl = new \moodle_url('/admin/tasklogs.php');
+$pageurl->param('filter', $filter);
+
+$PAGE->set_url($pageurl);
+$PAGE->set_context(context_system::instance());
+$PAGE->set_pagelayout('admin');
+$strheading = get_string('tasklogs', 'tool_task');
+$PAGE->set_title($strheading);
+$PAGE->set_heading($strheading);
+
+require_login();
+
+require_capability('moodle/site:config', context_system::instance());
+admin_externalpage_setup('tasklogs');
+
+$logid = optional_param('logid', null, PARAM_INT);
+$download = optional_param('download', false, PARAM_BOOL);
+
+if (null !== $logid) {
+    $log = $DB->get_record('task_log', ['id' => $logid], '*', MUST_EXIST);
+
+    if ($download) {
+        $filename = str_replace('\\', '_', $log->classname) . "-{$log->id}.log";
+        header("Content-Disposition: attachment; filename=\"{$filename}\"");
+    }
+
+    readstring_accel($log->output, 'text/plain', false);
+    exit;
+}
+
+$renderer = $PAGE->get_renderer('tool_task');
+
+echo $OUTPUT->header();
+echo $OUTPUT->render_from_template('core_admin/tasklogs', (object) [
+    'action' => $pageurl->out(),
+    'filter' => $filter,
+    'resultfilteroptions' => [
+        (object) [
+            'value' => -1,
+            'title' => get_string('all'),
+            'selected' => (-1 === $result),
+        ],
+        (object) [
+            'value' => 0,
+            'title' => get_string('success'),
+            'selected' => (0 === $result),
+        ],
+        (object) [
+            'value' => 1,
+            'title' => get_string('task_result:failed', 'admin'),
+            'selected' => (1 === $result),
+        ],
+    ],
+]);
+
+$table = new \core_admin\task_log_table($filter, $result);
+$table->baseurl = $pageurl;
+$table->out(100, false);
+
+echo $OUTPUT->footer();
diff --git a/admin/templates/tasklogs.mustache b/admin/templates/tasklogs.mustache
new file mode 100644 (file)
index 0000000..c3180a2
--- /dev/null
@@ -0,0 +1,34 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template core_admin/tasklogs
+
+    Task Logs template.
+}}
+<form class="form-inline" method="GET" action="{{{action}}}">
+    <label class="sr-only" for="tasklog-filter">{{#str}}filter{{/str}}</label>
+    <input class="form-control" type="text" id="tasklog-filter" name="filter" value="{{{filter}}}">
+
+    <label class="sr-only" for="tasklog-resultfilter">{{#str}}resultfilter, admin{{/str}}</label>
+    <select class="form-control custom-select" name="result" id="tasklog-resultfilter">
+        {{#resultfilteroptions}}
+        <option value="{{{value}}}"{{#selected}} selected="selected"{{/selected}}>{{title}}</option>
+        {{/resultfilteroptions}}
+    </select>
+
+    <input class="btn btn-primary" type="submit" value="{{#str}}filter{{/str}}">
+</form>
index e88125b..83a19af 100644 (file)
@@ -109,6 +109,17 @@ require_once(__DIR__ . '/../../../../lib/behat/lib.php');
 require_once(__DIR__ . '/../../../../lib/behat/classes/behat_command.php');
 require_once(__DIR__ . '/../../../../lib/behat/classes/behat_config_manager.php');
 
+// Remove error handling overrides done in config.php. This is consistent with admin/tool/behat/cli/util_single_run.php.
+$CFG->debug = (E_ALL | E_STRICT);
+$CFG->debugdisplay = 1;
+error_reporting($CFG->debug);
+ini_set('display_errors', '1');
+ini_set('log_errors', '1');
+
+// Import the necessary libraries.
+require_once($CFG->libdir . '/setuplib.php');
+require_once($CFG->libdir . '/behat/classes/util.php');
+
 // For drop option check if parallel site.
 if ((empty($options['parallel'])) && ($options['drop']) || $options['updatesteps']) {
     $options['parallel'] = behat_config_manager::get_behat_run_config_value('parallel');
index 2e98656..967fde3 100644 (file)
@@ -218,7 +218,7 @@ if ($options['install']) {
     // Run behat command to get steps in feature files.
     $featurestepscmd = behat_command::get_behat_command(true);
     $featurestepscmd .= ' --config ' . behat_config_manager::get_behat_cli_config_filepath();
-    $featurestepscmd .= ' --dry-run --format=moodle_step_count';
+    $featurestepscmd .= ' --dry-run --format=moodle_stepcount';
     $processes = cli_execute_parallel(array($featurestepscmd), __DIR__ . "/../../../../");
     $status = print_update_step_output(array_pop($processes), $behatstepfile);
 
index e771cc9..53fa80a 100644 (file)
@@ -29,9 +29,6 @@ defined('MOODLE_INTERNAL') || die();
 function xmldb_tool_customlang_upgrade($oldversion) {
     global $CFG;
 
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     // Automatically generated Moodle v3.3.0 release upgrade line.
     // Put any upgrade step following this.
 
@@ -41,5 +38,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
     // Automatically generated Moodle v3.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 48f7d3c..1fba508 100644 (file)
Binary files a/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js and b/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js differ
index 8d587ca..7e3a102 100644 (file)
@@ -26,8 +26,7 @@ define(['jquery'],
 
         var SELECTORS = {
             PURPOSE_SELECT: '#id_purposeid',
-            RETENTION_FIELD_BOOST: '#id_error_retention_current',
-            RETENTION_FIELD_CLEAN: '#fitem_id_retention_current [data-fieldtype=static]',
+            RETENTION_FIELD: '#fitem_id_retention_current [data-fieldtype=static]',
         };
 
         /**
@@ -65,18 +64,7 @@ define(['jquery'],
             $(SELECTORS.PURPOSE_SELECT).on('change', function(ev) {
                 var selected = $(ev.currentTarget).val();
                 var selectedPurpose = this.purposeRetentionPeriods[selected];
-
-                var cleanSelector = $(SELECTORS.RETENTION_FIELD_CLEAN);
-                if (cleanSelector.length > 0) {
-                    cleanSelector.text(selectedPurpose);
-                } else {
-                    var boostSelector = $(SELECTORS.RETENTION_FIELD_BOOST);
-                    var retentionField = boostSelector.siblings();
-                    if (retentionField.length > 0) {
-                        retentionField.text(selectedPurpose);
-                    }
-                }
-
+                $(SELECTORS.RETENTION_FIELD).text(selectedPurpose);
             }.bind(this));
         };
 
index 90e7b2a..7672da3 100644 (file)
@@ -920,9 +920,17 @@ class expired_contexts_manager {
      * @return  bool
      */
     protected static function is_course_context_expired_or_unprotected_for_user(\context $context, \stdClass $user) {
-        $expiryrecords = self::get_nested_expiry_info_for_courses($context->path);
 
-        $info = $expiryrecords[$context->path]->info;
+        if ($context->get_course_context()->instanceid == SITEID) {
+            // The is an activity in the site course (front page).
+            $purpose = data_registry::get_effective_contextlevel_value(CONTEXT_SYSTEM, 'purpose');
+            $info = static::get_expiry_info($purpose);
+
+        } else {
+            $expiryrecords = self::get_nested_expiry_info_for_courses($context->path);
+            $info = $expiryrecords[$context->path]->info;
+        }
+
         if ($info->is_fully_expired()) {
             // This context is fully expired.
             return true;
index 3dd839e..20c28f9 100644 (file)
@@ -689,6 +689,7 @@ class external extends external_api {
      * @throws restricted_context_exception
      */
     public static function get_users($query) {
+        global $DB;
         $params = external_api::validate_parameters(self::get_users_parameters(), [
             'query' => $query
         ]);
@@ -703,15 +704,30 @@ class external extends external_api {
         // Exclude admins and guest user.
         $excludedusers = array_keys(get_admins()) + [guest_user()->id];
         $sort = 'lastname ASC, firstname ASC';
-        $fields = 'id, email, ' . $allusernames;
-        $users = get_users(true, $query, true, $excludedusers, $sort, '', '', 0, 30, $fields);
+        $fields = 'id,' . $allusernames;
+
+        $extrafields = get_extra_user_fields($context);
+        if (!empty($extrafields)) {
+            $fields .= ',' . implode(',', $extrafields);
+        }
+
+        list($sql, $params) = users_search_sql($query, '', false, $extrafields, $excludedusers);
+        $users = $DB->get_records_select('user', $sql, $params, $sort, $fields, 0, 30);
+
         $useroptions = [];
         foreach ($users as $user) {
-            $useroptions[$user->id] = (object)[
+            $useroption = (object)[
                 'id' => $user->id,
-                'fullname' => fullname($user),
-                'email' => $user->email
+                'fullname' => fullname($user)
             ];
+            $useroption->extrafields = [];
+            foreach ($extrafields as $extrafield) {
+                $useroption->extrafields[] = (object)[
+                    'name' => $extrafield,
+                    'value' => $user->$extrafield
+                ];
+            }
+            $useroptions[$user->id] = $useroption;
         }
 
         return $useroptions;
@@ -729,7 +745,13 @@ class external extends external_api {
             [
                 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'),
                 'fullname' => new external_value(core_user::get_property_type('firstname'), 'The fullname of the user'),
-                'email' => new external_value(core_user::get_property_type('email'), 'The user\'s email address', VALUE_OPTIONAL),
+                'extrafields' => new external_multiple_structure(
+                    new external_single_structure([
+                            'name' => new external_value(PARAM_TEXT, 'Name of the extrafield.'),
+                            'value' => new external_value(PARAM_TEXT, 'Value of the extrafield.')
+                        ]
+                    ), 'List of extra fields', VALUE_OPTIONAL
+                )
             ]
         ));
     }
index 99fd156..47989cb 100644 (file)
@@ -313,5 +313,8 @@ function xmldb_tool_dataprivacy_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2018112500, 'tool', 'dataprivacy');
     }
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 7a7015a..e510ae0 100644 (file)
     overflow-y: scroll;
 }
 
-dd a.contactdpo {
-    /* Reverting dd's left margin */
-    margin-left: -10px;
-}
-
-.card dd a.contactdpo {
-    /* Reverting dd's left margin */
-    margin-left: inherit;
-}
-
 [data-region="data-requests-table"] .moodle-actionmenu {
     min-width: 150px;
 }
index 759650c..0f8443d 100644 (file)
     Example context (json):
     {
         "fullname": "Admin User",
-        "email": "admin@example.com"
+        "extrafields": [
+            {
+                "name": "email",
+                "value": "admin@example.com"
+            },
+            {
+                "name": "phone1",
+                "value": "0123456789"
+            }
+        ]
     }
 }}
 <span>
     <span>{{fullname}}</span>
-    <span><small>{{email}}</small></span>
+    {{#extrafields}}
+        <span><small>{{value}}</small></span>
+    {{/extrafields}}
 </span>
index 1947f62..78751cc 100644 (file)
@@ -6,21 +6,29 @@ Feature: Data export from the privacy API
 
   Background:
     Given the following "users" exist:
-      | username | firstname      | lastname |
-      | victim   | Victim User    | 1        |
-      | parent   | Long-suffering | Parent   |
+      | username  | firstname      | lastname  | institution |
+      | victim    | Victim User    | 1         | University1 |
+      | victim2   | Victim User    | 2         | University2 |
+      | requester | The            | Requester | University3 |
+      | 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       |           |
+      | capability                                   | permission | role    | contextlevel | reference |
+      | tool/dataprivacy:makedatarequestsforchildren | Allow      | tired   | System       |           |
+      | tool/dataprivacy:managedatarequests          | Allow      | manager | System       |           |
+      | moodle/site:viewuseridentity                 | Prevent    | manager | System       |           |
     And the following "role assigns" exist:
       | user   | role  | contextlevel | reference |
       | parent | tired | User         | victim    |
+    And the following "system role assigns" exist:
+      | user      | role    | contextlevel |
+      | requester | manager | User         |
     And the following config values are set as admin:
       | contactdataprotectionofficer | 1  | tool_dataprivacy |
       | privacyrequestexpiry         | 55 | tool_dataprivacy |
+      | dporoles                     | 1  | tool_dataprivacy |
     And the following data privacy "categories" exist:
       | name          |
       | Site category |
@@ -127,3 +135,19 @@ Feature: Data export from the privacy API
 
     And I should see "Expired" in the "Victim User 1" "table_row"
     And I should not see "Actions"
+
+  @javascript
+  Scenario: Test search for user using extra field.
+    Given the following "permission overrides" exist:
+      | capability                   | permission | role    | contextlevel | reference |
+      | moodle/site:viewuseridentity | Allow      | manager | System       |           |
+    And the following config values are set as admin:
+      | showuseridentity | institution |
+    And I log in as "requester"
+    And I navigate to "Users > Privacy and policies > Data requests" in site administration
+    And I follow "New request"
+    And I set the field "Search" to "University1"
+    Then I should see "Victim User 1"
+    When I reload the page
+    And I set the field "Search" to "University2"
+    Then I should see "Victim User 2"
index 08ffb80..6deb919 100644 (file)
@@ -2222,6 +2222,40 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
         $this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($blockcontext, $user));
     }
 
+    /**
+     * Test the is_context_expired functions when supplied with the front page course.
+     */
+    public function test_is_context_expired_frontpage() {
+        $this->resetAfterTest();
+
+        $purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
+
+        $frontcourse = get_site();
+        $frontcoursecontext = \context_course::instance($frontcourse->id);
+
+        $sitenews = $this->getDataGenerator()->create_module('forum', ['course' => $frontcourse->id]);
+        $cm = get_coursemodule_from_instance('forum', $sitenews->id);
+        $sitenewscontext = \context_module::instance($cm->id);
+
+        $user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
+
+        $this->assertFalse(expired_contexts_manager::is_context_expired($frontcoursecontext));
+        $this->assertFalse(expired_contexts_manager::is_context_expired($sitenewscontext));
+
+        $this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
+        $this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
+
+        // Protecting the course contextlevel does not impact the front page.
+        $purposes->course->set('protected', 1)->save();
+        $this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
+        $this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
+
+        // Protecting the system contextlevel affects the front page, too.
+        $purposes->system->set('protected', 1)->save();
+        $this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
+        $this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
+    }
+
     /**
      * Test the is_context_expired functions when supplied with an expired course.
      */
index b17811a..7cb7df7 100644 (file)
@@ -970,4 +970,143 @@ class tool_dataprivacy_external_testcase extends externallib_advanced_testcase {
         $this->expectException(required_capability_exception::class);
         $result = external::bulk_deny_data_requests([$requestid1]);
     }
+
+    /**
+     * Test for external::get_users(), case search using non-identity field without
+     * facing any permission problem.
+     *
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_parameter_exception
+     * @throws required_capability_exception
+     * @throws restricted_context_exception
+     */
+    public function test_get_users_using_using_non_identity() {
+        $this->resetAfterTest();
+        $context = context_system::instance();
+        $requester = $this->getDataGenerator()->create_user();
+        $role = $this->getDataGenerator()->create_role();
+        role_assign($role, $requester->id, $context);
+        assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
+        $this->setUser($requester);
+
+        $this->getDataGenerator()->create_user([
+            'firstname' => 'First Student'
+        ]);
+        $student2 = $this->getDataGenerator()->create_user([
+            'firstname' => 'Second Student'
+        ]);
+
+        $results = external::get_users('Second');
+        $this->assertCount(1, $results);
+        $this->assertEquals((object)[
+            'id' => $student2->id,
+            'fullname' => fullname($student2),
+            'extrafields' => []
+        ], $results[$student2->id]);
+    }
+
+    /**
+     * Test for external::get_users(), case search using identity field but
+     * don't have "moodle/site:viewuseridentity" permission.
+     *
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_parameter_exception
+     * @throws required_capability_exception
+     * @throws restricted_context_exception
+     */
+    public function test_get_users_using_identity_without_permission() {
+        global $CFG;
+
+        $this->resetAfterTest();
+        $CFG->showuseridentity = 'institution';
+
+        // Create requester user and assign correct capability.
+        $context = context_system::instance();
+        $requester = $this->getDataGenerator()->create_user();
+        $role = $this->getDataGenerator()->create_role();
+        role_assign($role, $requester->id, $context);
+        assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
+        $this->setUser($requester);
+
+        $this->getDataGenerator()->create_user([
+            'institution' => 'University1'
+        ]);
+
+        $results = external::get_users('University1');
+        $this->assertEmpty($results);
+    }
+
+    /**
+     * Test for external::get_users(), case search using disabled identity field
+     * even they have "moodle/site:viewuseridentity" permission.
+     *
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_parameter_exception
+     * @throws required_capability_exception
+     * @throws restricted_context_exception
+     */
+    public function test_get_users_using_field_not_in_identity() {
+        $this->resetAfterTest();
+
+        $context = context_system::instance();
+        $requester = $this->getDataGenerator()->create_user();
+        $role = $this->getDataGenerator()->create_role();
+        role_assign($role, $requester->id, $context);
+        assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
+        assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $role, $context);
+        $this->setUser($requester);
+
+        $this->getDataGenerator()->create_user([
+            'institution' => 'University1'
+        ]);
+
+        $results = external::get_users('University1');
+        $this->assertEmpty($results);
+    }
+
+    /**
+     * Test for external::get_users(), case search using enabled identity field
+     * with "moodle/site:viewuseridentity" permission.
+     *
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_parameter_exception
+     * @throws required_capability_exception
+     * @throws restricted_context_exception
+     */
+    public function test_get_users() {
+        global $CFG;
+        $this->resetAfterTest();
+        $CFG->showuseridentity = 'institution';
+        $context = context_system::instance();
+        $requester = $this->getDataGenerator()->create_user();
+        $role = $this->getDataGenerator()->create_role();
+        role_assign($role, $requester->id, $context);
+        assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
+        assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $role, $context);
+        $this->setUser($requester);
+
+        $student1 = $this->getDataGenerator()->create_user([
+            'institution' => 'University1'
+        ]);
+        $this->getDataGenerator()->create_user([
+            'institution' => 'University2'
+        ]);
+
+        $results = external::get_users('University1');
+        $this->assertCount(1, $results);
+        $this->assertEquals((object)[
+            'id' => $student1->id,
+            'fullname' => fullname($student1),
+            'extrafields' => [
+                0 => (object)[
+                    'name' => 'institution',
+                    'value' => 'University1'
+                ]
+            ]
+        ], $results[$student1->id]);
+    }
 }
index a56cc8a..411f0ac 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2018120300;
+$plugin->version   = 2019011500;
 $plugin->requires  = 2018112800;        // Moodle 3.5dev (Build 2018031600) and upwards.
 $plugin->component = 'tool_dataprivacy';
index 2c7e2b0..bbf1961 100644 (file)
@@ -33,9 +33,6 @@ defined('MOODLE_INTERNAL') || die();
 function xmldb_tool_log_upgrade($oldversion) {
     global $CFG;
 
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     // Automatically generated Moodle v3.3.0 release upgrade line.
     // Put any upgrade step following this.
 
@@ -45,5 +42,8 @@ function xmldb_tool_log_upgrade($oldversion) {
     // Automatically generated Moodle v3.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 7b6fed2..04137ab 100644 (file)
@@ -27,9 +27,6 @@ defined('MOODLE_INTERNAL') || die();
 function xmldb_logstore_database_upgrade($oldversion) {
     global $CFG;
 
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     // Automatically generated Moodle v3.3.0 release upgrade line.
     // Put any upgrade step following this.
 
@@ -39,5 +36,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
     // Automatically generated Moodle v3.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index f0dd415..d6cd26d 100644 (file)
@@ -27,9 +27,6 @@ defined('MOODLE_INTERNAL') || die();
 function xmldb_logstore_standard_upgrade($oldversion) {
     global $CFG;
 
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     // Automatically generated Moodle v3.3.0 release upgrade line.
     // Put any upgrade step following this.
 
@@ -39,5 +36,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
     // Automatically generated Moodle v3.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 67fbb42..f8579c9 100644 (file)
@@ -33,26 +33,6 @@ defined('MOODLE_INTERNAL') || die();
 function xmldb_tool_monitor_upgrade($oldversion) {
     global $CFG, $DB;
 
-    $dbman = $DB->get_manager();
-
-    if ($oldversion < 2016052305) {
-
-        // Define field inactivedate to be added to tool_monitor_subscriptions.
-        $table = new xmldb_table('tool_monitor_subscriptions');
-        $field = new xmldb_field('inactivedate', XMLDB_TYPE_INTEGER, '10', null, true, null, 0, 'lastnotificationsent');
-
-        // Conditionally launch add field inactivedate.
-        if (!$dbman->field_exists($table, $field)) {
-            $dbman->add_field($table, $field);
-        }
-
-        // Monitor savepoint reached.
-        upgrade_plugin_savepoint(true, 2016052305, 'tool', 'monitor');
-    }
-
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     if ($oldversion < 2017021300) {
 
         // Delete "orphaned" subscriptions.
@@ -79,5 +59,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
     // Automatically generated Moodle v3.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 52e20a0..cf1a33b 100644 (file)
@@ -60,5 +60,8 @@ function xmldb_tool_policy_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2018091800, 'tool', 'policy');
     }
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 0de826c..cf03700 100644 (file)
@@ -129,6 +129,7 @@ if ($execute = $options['execute']) {
     $predbqueries = $DB->perf_get_queries();
     $pretime = microtime(true);
 
+    \core\task\logmanager::start_logging($task);
     $fullname = $task->get_name() . ' (' . get_class($task) . ')';
     mtrace('Execute scheduled task: ' . $fullname);
     // NOTE: it would be tricky to move this code to \core\task\manager class,
index b1172e0..a28b478 100644 (file)
@@ -34,7 +34,7 @@ $string['disabled'] = 'Disabled';
 $string['disabled_help'] = 'Disabled scheduled tasks are not executed from cron, however they can still be executed manually via the CLI tool.';
 $string['edittaskschedule'] = 'Edit task schedule: {$a}';
 $string['enablerunnow'] = 'Allow \'Run now\' for scheduled tasks';
-$string['enablerunnow_desc'] = 'Allows administrators to run a single scheduled task immediately, rather than waiting for it to run as scheduled. The task runs on the web server, so some sites may wish to disable this feature to avoid potential performance issues.';
+$string['enablerunnow_desc'] = 'Allows administrators to run a single scheduled task immediately, rather than waiting for it to run as scheduled. The feature requires \'Path to PHP CLI\' (pathtophp) to be set in System paths. The task runs on the web server, so you may wish to disable this feature to avoid potential performance issues.';
 $string['faildelay'] = 'Fail delay';
 $string['lastruntime'] = 'Last run';
 $string['nextruntime'] = 'Next run';
@@ -48,6 +48,7 @@ $string['runpattern'] = 'Run pattern';
 $string['scheduledtasks'] = 'Scheduled tasks';
 $string['scheduledtaskchangesdisabled'] = 'Modifications to the list of scheduled tasks have been prevented in Moodle configuration';
 $string['taskdisabled'] = 'Task disabled';
+$string['tasklogs'] = 'Task logs';
 $string['taskscheduleday'] = 'Day';
 $string['taskscheduleday_help'] = 'Day of month field for task schedule. The field uses the same format as unix cron. Some examples are:<br/><ul><li><strong>*</strong> Every day</li><li><strong>*/2</strong> Every 2nd day</li><li><strong>1</strong> The first of every month</li><li><strong>1,15</strong> The first and fifteenth of every month</li></ul>';
 $string['taskscheduledayofweek'] = 'Day of week';
@@ -59,4 +60,4 @@ $string['taskscheduleminute_help'] = 'Minute field for task schedule. The field
 $string['taskschedulemonth'] = 'Month';
 $string['taskschedulemonth_help'] = 'Month field for task schedule. The field uses the same format as unix cron. Some examples are:<br/><ul><li><strong>*</strong> Every month</li><li><strong>*/2</strong> Every second month</li><li><strong>1</strong> Every January</li><li><strong>1,5</strong> Every January and May</li></ul>';
 $string['privacy:metadata'] = 'The Scheduled task configuration plugin does not store any personal data.';
-
+$string['viewlogs'] = 'View logs for {$a}';
index 88dbdd4..3afd20c 100644 (file)
@@ -41,20 +41,33 @@ class tool_task_renderer extends plugin_renderer_base {
     public function scheduled_tasks_table($tasks) {
         global $CFG;
 
+        $showloglink = \core\task\logmanager::has_log_report();
+
         $table = new html_table();
-        $table->head  = array(get_string('name'),
-                              get_string('component', 'tool_task'),
-                              get_string('edit'),
-                              get_string('lastruntime', 'tool_task'),
-                              get_string('nextruntime', 'tool_task'),
-                              get_string('taskscheduleminute', 'tool_task'),
-                              get_string('taskschedulehour', 'tool_task'),
-                              get_string('taskscheduleday', 'tool_task'),
-                              get_string('taskscheduledayofweek', 'tool_task'),
-                              get_string('taskschedulemonth', 'tool_task'),
-                              get_string('faildelay', 'tool_task'),
-                              get_string('default', 'tool_task'));
+        $table->head = [
+            get_string('name'),
+            get_string('component', 'tool_task'),
+            get_string('edit'),
+            get_string('logs'),
+            get_string('lastruntime', 'tool_task'),
+            get_string('nextruntime', 'tool_task'),
+            get_string('taskscheduleminute', 'tool_task'),
+            get_string('taskschedulehour', 'tool_task'),
+            get_string('taskscheduleday', 'tool_task'),
+            get_string('taskscheduledayofweek', 'tool_task'),
+            get_string('taskschedulemonth', 'tool_task'),
+            get_string('faildelay', 'tool_task'),
+            get_string('default', 'tool_task'),
+        ];
+
         $table->attributes['class'] = 'admintable generaltable';
+        $table->colclasses = [];
+
+        if (!$showloglink) {
+            // Hide the log links.
+            $table->colclasses['3'] = 'hidden';
+        }
+
         $data = array();
         $yes = get_string('yes');
         $no = get_string('no');
@@ -72,6 +85,14 @@ class tool_task_renderer extends plugin_renderer_base {
                 $editlink = $this->render(new pix_icon('t/locked', get_string('scheduledtaskchangesdisabled', 'tool_task')));
             }
 
+            $loglink = '';
+            if ($showloglink) {
+                $loglink = $this->action_icon(
+                    \core\task\logmanager::get_url_for_task_class(get_class($task)),
+                    new pix_icon('e/file-text', get_string('viewlogs', 'tool_task', $task->get_name())
+                ));
+            }
+
             $namecell = new html_table_cell($task->get_name() . "\n" . html_writer::tag('span', '\\'.get_class($task),
                 array('class' => 'task-class text-ltr')));
             $namecell->header = true;
@@ -125,6 +146,7 @@ class tool_task_renderer extends plugin_renderer_base {
                         $namecell,
                         $componentcell,
                         new html_table_cell($editlink),
+                        new html_table_cell($loglink),
                         new html_table_cell($lastrun . $runnow),
                         new html_table_cell($nextrun),
                         new html_table_cell($task->get_minute()),
@@ -136,11 +158,11 @@ class tool_task_renderer extends plugin_renderer_base {
                         new html_table_cell($customised)));
 
             // Cron-style values must always be LTR.
-            $row->cells[5]->attributes['class'] = 'text-ltr';
             $row->cells[6]->attributes['class'] = 'text-ltr';
             $row->cells[7]->attributes['class'] = 'text-ltr';
             $row->cells[8]->attributes['class'] = 'text-ltr';
             $row->cells[9]->attributes['class'] = 'text-ltr';
+            $row->cells[10]->attributes['class'] = 'text-ltr';
 
             if ($disabled) {
                 $row->attributes['class'] = 'disabled';
index ac75859..ac9858e 100644 (file)
 defined('MOODLE_INTERNAL') || die;
 
 if ($hassiteconfig) {
-    $ADMIN->add('server', new admin_externalpage('scheduledtasks', new lang_string('scheduledtasks','tool_task'), "$CFG->wwwroot/$CFG->admin/tool/task/scheduledtasks.php"));
+    $ADMIN->add(
+        'taskconfig',
+        new admin_externalpage(
+            'scheduledtasks',
+            new lang_string('scheduledtasks', 'tool_task'),
+            "$CFG->wwwroot/$CFG->admin/tool/task/scheduledtasks.php"
+        )
+    );
 }
index ce5adad..a8582de 100644 (file)
@@ -35,9 +35,6 @@ use tool_usertours\manager;
 function xmldb_tool_usertours_upgrade($oldversion) {
     global $CFG, $DB;
 
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     // Automatically generated Moodle v3.3.0 release upgrade line.
     // Put any upgrade step following this.
 
@@ -54,5 +51,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2018113002, 'tool', 'usertours');
     }
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 9dd0838..d531a86 100644 (file)
@@ -215,8 +215,8 @@ $string['yesmissingindexesfound'] = '<p>Some missing indexes have been found in
 <p>After doing that, it\'s highly recommended to execute this utility again to check that no more missing indexes are found.</p>';
 $string['yeswrongdefaultsfound'] = '<p>Some inconsistent defaults have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to fix them all. Remember to backup your data first!</p>
 <p>After doing that, it\'s highly recommended to execute this utility again to check that no more inconsistent defaults are found.</p>';
-$string['yeswrongintsfound'] = '<p>Some wrong integers have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them. Remember to backup your data first!</p>
-<p>After doing that, it\'s highly recommended to execute this utility again to check that no more wrong integers are found.</p>';
+$string['yeswrongintsfound'] = '<p>Some wrong integers have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to fix them. Remember to backup your data first!</p>
+<p>After fixing them, it is highly recommended to execute this utility again to check that no more wrong integers are found.</p>';
 $string['yeswrongoraclesemanticsfound'] = '<p>Some Oracle columns using BYTE semantics have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them. Remember to backup your data first!</p>
 <p>After doing that, it\'s highly recommended to execute this utility again to check that no more wrong semantics are found.</p>';
 $string['privacy:metadata'] = 'The XMLDB editor plugin does not store any personal data.';
index 5cd88b9..4a0d576 100644 (file)
@@ -169,10 +169,7 @@ abstract class base extends \core_analytics\calculable {
 
             if (!is_null($calculatedvalue)) {
                 $notnulls[$sampleid] = $sampleid;
-                if ($calculatedvalue > self::MAX_VALUE || $calculatedvalue < self::MIN_VALUE) {
-                    throw new \coding_exception('Calculated values should be higher than ' . self::MIN_VALUE .
-                        ' and lower than ' . self::MAX_VALUE . ' ' . $calculatedvalue . ' received');
-                }
+                $this->validate_calculated_value($calculatedvalue);
             }
 
             $calculations[$sampleid] = $calculatedvalue;
@@ -182,4 +179,19 @@ abstract class base extends \core_analytics\calculable {
 
         return array($features, $newcalculations, $notnulls);
     }
+
+    /**
+     * Validates the calculated value.
+     *
+     * @throws \coding_exception
+     * @param float $calculatedvalue
+     * @return true
+     */
+    protected function validate_calculated_value($calculatedvalue) {
+        if ($calculatedvalue > self::MAX_VALUE || $calculatedvalue < self::MIN_VALUE) {
+            throw new \coding_exception('Calculated values should be higher than ' . self::MIN_VALUE .
+                ' and lower than ' . self::MAX_VALUE . ' ' . $calculatedvalue . ' received');
+        }
+        return true;
+    }
 }
index a730996..2e4d4a9 100644 (file)
@@ -41,9 +41,7 @@ abstract class binary extends discrete {
      * @return array
      */
     public static final function get_classes() {
-        // It does not really matter, all \core_analytics\local\indicator\discrete get_classes calls have been overwriten as we
-        // only need 1 column here.
-        return array(0);
+        return [-1, 1];
     }
 
     /**
index 8a8aa66..b04b4c6 100644 (file)
@@ -52,8 +52,7 @@ abstract class discrete extends base {
     public static function get_feature_headers() {
         $fullclassname = '\\' . get_called_class();
 
-        $headers = array($fullclassname);
-        foreach (self::get_classes() as $class) {
+        foreach (static::get_classes() as $class) {
             $headers[] = $fullclassname . '/' . $class;
         }
 
@@ -116,26 +115,45 @@ abstract class discrete extends base {
      */
     protected function to_features($calculatedvalues) {
 
-        $classes = self::get_classes();
+        $classes = static::get_classes();
 
         foreach ($calculatedvalues as $sampleid => $calculatedvalue) {
 
-            $classindex = array_search($calculatedvalue, $classes, true);
+            // Using intval as it may come as a float from the db.
+            $classindex = array_search(intval($calculatedvalue), $classes, true);
 
-            if (!$classindex) {
-                throw new \coding_exception(get_class($this) . ' calculated "' . $calculatedvalue .
-                    '" which is not one of its defined classes (' . json_encode($classes) . ')');
+            if ($classindex === false && !is_null($calculatedvalue)) {
+                throw new \coding_exception(get_class($this) . ' calculated value "' . $calculatedvalue .
+                    '" is not one of its defined classes (' . json_encode($classes) . ')');
             }
 
             // We transform the calculated value into multiple features, one for each of the possible classes.
             $features = array_fill(0, count($classes), 0);
 
             // 1 to the selected value.
-            $features[$classindex] = 1;
+            if (!is_null($calculatedvalue)) {
+                $features[$classindex] = 1;
+            }
 
             $calculatedvalues[$sampleid] = $features;
         }
 
         return $calculatedvalues;
     }
+
+    /**
+     * Validates the calculated value.
+     *
+     * @param float $calculatedvalue
+     * @return true
+     */
+    protected function validate_calculated_value($calculatedvalue) {
+
+        // Using intval as it may come as a float from the db.
+        if (!in_array(intval($calculatedvalue), static::get_classes())) {
+            throw new \coding_exception(get_class($this) . ' calculated value "' . $calculatedvalue .
+                '" is not one of its defined classes (' . json_encode(static::get_classes()) . ')');
+        }
+        return true;
+    }
 }
diff --git a/analytics/tests/fixtures/test_indicator_discrete.php b/analytics/tests/fixtures/test_indicator_discrete.php
new file mode 100644 (file)
index 0000000..983b655
--- /dev/null
@@ -0,0 +1,90 @@
+<?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/>.
+
+/**
+ * Test indicator.
+ *
+ * @package   core_analytics
+ * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Test indicator.
+ *
+ * @package   core_analytics
+ * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class test_indicator_discrete extends \core_analytics\local\indicator\discrete {
+
+    /**
+     * Returns the name.
+     *
+     * If there is a corresponding '_help' string this will be shown as well.
+     *
+     * @return \lang_string
+     */
+    public static function get_name() : \lang_string {
+        // Using a string that exists and contains a corresponding '_help' string.
+        return new \lang_string('allowstealthmodules');
+    }
+
+    /**
+     * The different classes this discrete indicator provides.
+     * @return [type] [description]
+     */
+    protected static function get_classes() {
+        return [0, 1, 2, 3, 4];
+    }
+
+    /**
+     * Just for testing.
+     *
+     * @param  float $value
+     * @param  string $subtype
+     * @return string
+     */
+    public function get_calculation_outcome($value, $subtype = false) {
+        return self::OUTCOME_OK;
+    }
+
+    /**
+     * Custom indicator calculated value display as otherwise we would display meaningless numbers to users.
+     *
+     * @param  float  $value
+     * @param  string $subtype
+     * @return string
+     */
+    public function get_display_value($value, $subtype = false) {
+        return $value;
+    }
+
+    /**
+     * calculate_sample
+     *
+     * @param int $sampleid
+     * @param string $sampleorigin
+     * @param int $starttime
+     * @param int $endtime
+     * @return float
+     */
+    protected function calculate_sample($sampleid, $sampleorigin, $starttime = false, $endtime = false) {
+        return 4;
+    }
+}
index 8b934dd..19106a6 100644 (file)
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class test_indicator_random extends \core_analytics\local\indicator\binary {
+class test_indicator_random extends \core_analytics\local\indicator\linear {
 
     /**
      * Returns a lang_string object representing the name for the indicator.
diff --git a/analytics/tests/indicator_test.php b/analytics/tests/indicator_test.php
new file mode 100644 (file)
index 0000000..add3f3d
--- /dev/null
@@ -0,0 +1,99 @@
+<?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 the indicator API.
+ *
+ * @package   core_analytics
+ * @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once(__DIR__ . '/fixtures/test_indicator_max.php');
+require_once(__DIR__ . '/fixtures/test_indicator_discrete.php');
+require_once(__DIR__ . '/fixtures/test_indicator_min.php');
+
+/**
+ * Unit tests for the model.
+ *
+ * @package   core_analytics
+ * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class analytics_indicator_testcase extends advanced_testcase {
+
+    /**
+     * test_validate_calculated_value
+     *
+     * @param string $indicatorclass
+     * @param array $returnedvalue
+     * @dataProvider validate_calculated_value
+     * @return null
+     */
+    public function test_validate_calculated_value($indicatorclass, $returnedvalue) {
+        $indicator = new $indicatorclass();
+        list($values, $unused) = $indicator->calculate([1], 'notrelevanthere');
+        $this->assertEquals($returnedvalue, $values[0]);
+    }
+
+    /**
+     * Data provider for test_validate_calculated_value
+     *
+     * @return array
+     */
+    public function validate_calculated_value() {
+        return [
+            'max' => ['test_indicator_max', [1]],
+            'min' => ['test_indicator_min', [-1]],
+            'discrete' => ['test_indicator_discrete', [0, 0, 0, 0, 1]],
+        ];
+    }
+
+    /**
+     * test_validate_calculated_value_exceptions
+     *
+     * @param string $indicatorclass
+     * @param string $willreturn
+     * @dataProvider validate_calculated_value_exceptions
+     * @expectedException \coding_exception
+     * @return null
+     */
+    public function test_validate_calculated_value_exceptions($indicatorclass, $willreturn) {
+
+        $indicator = new $indicatorclass();
+        $indicatormock = $this->getMockBuilder(get_class($indicator))
+            ->setMethods(['calculate_sample'])
+            ->getMock();
+        $indicatormock->method('calculate_sample')->willReturn($willreturn);
+        list($values, $unused) = $indicatormock->calculate([1], 'notrelevanthere');
+
+    }
+
+    /**
+     * Data provider for test_validate_calculated_value_exceptions
+     *
+     * @return array
+     */
+    public function validate_calculated_value_exceptions() {
+        return [
+            'max' => ['test_indicator_max', 2],
+            'min' => ['test_indicator_min', -2],
+            'discrete' => ['test_indicator_discrete', 7],
+        ];
+    }
+}
index 34d82f7..7d74868 100644 (file)
@@ -452,7 +452,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
         $indicator = $this->getMockBuilder('test_indicator_max')->setMethods(['calculate_sample'])->getMock();
         $indicator->expects($this->never())->method('calculate_sample');
 
-        $existingcalcs = array(111 => 1, 222 => 0.5);
+        $existingcalcs = array(111 => 1, 222 => -1);
         $sampleids = array(111 => 111, 222 => 222);
         list($values, $unused) = $indicator->calculate($sampleids, $sampleorigin, $starttime, $endtime, $existingcalcs);
     }
index 5eb8434..56e5a7d 100644 (file)
@@ -130,21 +130,10 @@ class auth_plugin_cas extends auth_plugin_ldap {
             }
 
             $authCAS = optional_param('authCAS', '', PARAM_RAW);
-            if ($authCAS == 'NOCAS') {
+            if ($authCAS != 'CAS') {
                 return;
             }
-            // Show authentication form for multi-authentication.
-            // Test pgtIou parameter for proxy mode (https connection in background from CAS server to the php server).
-            if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
-                $PAGE->set_url('/login/index.php');
-                $PAGE->navbar->add($CASform);
-                $PAGE->set_title("$site->fullname: $CASform");
-                $PAGE->set_heading($site->fullname);
-                echo $OUTPUT->header();
-                include($CFG->dirroot.'/auth/cas/cas_form.html');
-                echo $OUTPUT->footer();
-                exit();
-            }
+
         }
 
         // Connection to CAS server
@@ -363,4 +352,35 @@ class auth_plugin_cas extends auth_plugin_ldap {
             phpCAS::logoutWithRedirectService($backurl);
         }
     }
+
+    /**
+     * Return a list of identity providers to display on the login page.
+     *
+     * @param string|moodle_url $wantsurl The requested URL.
+     * @return array List of arrays with keys url, iconurl and name.
+     */
+    public function loginpage_idp_list($wantsurl) {
+        if (empty($this->config->hostname)) {
+            // CAS is not configured.
+            return [];
+        }
+
+        $iconurl = moodle_url::make_pluginfile_url(
+            context_system::instance()->id,
+            'auth_cas',
+            'logo',
+            null,
+            '/',
+            $this->config->auth_logo);
+
+        return [
+            [
+                'url' => new moodle_url(get_login_url(), [
+                        'authCAS' => 'CAS',
+                    ]),
+                'iconurl' => $iconurl,
+                'name' => format_string($this->config->auth_name),
+            ],
+        ];
+    }
 }
diff --git a/auth/cas/cas_form.html b/auth/cas/cas_form.html
deleted file mode 100644 (file)
index 52319a3..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="loginbox clearfix">
-<div class="loginpanel">
-<div>
-<a href="<?php echo get_login_url() . '?authCAS=CAS';?>"><?php print_string('accesCAS', 'auth_cas');?></a>
-</div>
-<br/>
-<div>
-<a href="<?php echo get_login_url() . '?authCAS=NOCAS';?>"><?php print_string('accesNOCAS', 'auth_cas');?></a>
-</div>
-</div>
-</div>
index f331335..4df297c 100644 (file)
@@ -32,9 +32,6 @@ defined('MOODLE_INTERNAL') || die();
 function xmldb_auth_cas_upgrade($oldversion) {
     global $CFG;
 
-    // Automatically generated Moodle v3.2.0 release upgrade line.
-    // Put any upgrade step following this.
-
     if ($oldversion < 2017020700) {
         // Convert info in config plugins from auth/cas to auth_cas.
         upgrade_fix_config_auth_plugin_names('cas');
@@ -51,5 +48,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
     // Automatically generated Moodle v3.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Automatically generated Moodle v3.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index a7c3662..3e465f9 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['accesCAS'] = 'CAS users';
-$string['accesNOCAS'] = 'other users';
+$string['auth_cas_auth_name'] = 'Authentication method name';
+$string['auth_cas_auth_name_description'] = 'Provide a name for the CAS authentication method that is familiar to your users.';
+$string['auth_cas_auth_logo'] = 'Authentication method logo';
+$string['auth_cas_auth_logo_description'] = 'Provide a logo for the CAS authentication method that is familiar to your users.';
 $string['auth_cas_auth_user_create'] = 'Create users externally';
+$string['auth_cas_auth_service'] = 'CAS';
 $string['auth_cas_baseuri'] = 'URI of the server (nothing if no baseUri)<br />For example, if the CAS server responds to host.domaine.fr/CAS/ then<br />cas_baseuri = CAS/';
 $string['auth_cas_baseuri_key'] = 'Base URI';
 $string['auth_cas_broken_password'] = 'You cannot proceed without changing your password, however there is no available page for changing it. Please contact your Moodle Administrator.';
@@ -75,3 +78,7 @@ $string['noldapserver'] = 'No LDAP server configured for CAS! Syncing disabled.'
 $string['pluginname'] = 'CAS server (SSO)';
 $string['synctask'] = 'CAS users sync job';
 $string['privacy:metadata'] = 'The CAS server (SSO) authentication plugin does not store any personal data.';
+
+// Deprecated since Moodle 3.7.
+$string['accesCAS'] = 'CAS users';
+$string['accesNOCAS'] = 'other users';
diff --git a/auth/cas/lang/en/deprecated.txt b/auth/cas/lang/en/deprecated.txt
new file mode 100644 (file)
index 0000000..6854e8e
--- /dev/null
@@ -0,0 +1,2 @@
+accesCAS,auth_cas
+accesNOCAS,auth_cas
diff --git a/auth/cas/lib.php b/auth/cas/lib.php
new file mode 100644 (file)
index 0000000..7127556
--- /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