Merge branch 'MDL-61708-lti-fullnamedisplay' of https://github.com/wjroes/moodle
authorJun Pataleta <jun@moodle.com>
Mon, 16 Apr 2018 07:29:08 +0000 (15:29 +0800)
committerJun Pataleta <jun@moodle.com>
Mon, 16 Apr 2018 07:29:08 +0000 (15:29 +0800)
697 files changed:
.eslintignore
.stylelintignore
.travis.yml
admin/cli/install.php
admin/searchareas.php
admin/settings/appearance.php
admin/settings/plugins.php
admin/settings/security.php
admin/settings/users.php
admin/tests/behat/search_areas.feature [new file with mode: 0644]
admin/tool/log/store/database/classes/helper.php
admin/tool/messageinbound/classes/manager.php
admin/tool/mobile/classes/api.php
admin/tool/mobile/classes/external.php
admin/tool/mobile/db/services.php
admin/tool/mobile/lang/en/tool_mobile.php
admin/tool/mobile/tests/externallib_test.php
admin/tool/mobile/tests/fixtures/output/mobile.php [new file with mode: 0644]
admin/tool/mobile/upgrade.txt
admin/tool/mobile/version.php
admin/tool/uploaduser/user_form.php
admin/tool/usertours/amd/build/popper.min.js [deleted file]
admin/tool/usertours/amd/build/tour.min.js
admin/tool/usertours/amd/readme_moodle.txt
admin/tool/usertours/amd/src/popper.js [deleted file]
admin/tool/usertours/amd/src/tour.js
admin/tool/usertours/classes/manager.php
admin/tool/usertours/classes/step.php
admin/tool/usertours/tests/privacy_provider_test.php [moved from admin/tool/usertours/tests/privcacy_provider_test.php with 100% similarity]
admin/tool/usertours/thirdpartylibs.xml
admin/tool/usertours/upgrade.txt [new file with mode: 0644]
admin/user.php
analytics/classes/local/target/base.php
analytics/tests/course_test.php
auth/cas/classes/privacy/provider.php [new file with mode: 0644]
auth/cas/lang/en/auth_cas.php
auth/classes/digital_consent.php
auth/db/classes/privacy/provider.php [new file with mode: 0644]
auth/db/lang/en/auth_db.php
auth/db/tests/db_test.php
auth/email/auth.php
auth/email/classes/privacy/provider.php [new file with mode: 0644]
auth/email/lang/en/auth_email.php
auth/ldap/classes/privacy/provider.php [new file with mode: 0644]
auth/ldap/lang/en/auth_ldap.php
auth/lti/classes/privacy/provider.php [new file with mode: 0644]
auth/lti/lang/en/auth_lti.php
auth/mnet/classes/privacy/provider.php [new file with mode: 0644]
auth/mnet/lang/en/auth_mnet.php
auth/nologin/classes/privacy/provider.php [new file with mode: 0644]
auth/nologin/lang/en/auth_nologin.php
auth/none/classes/privacy/provider.php [new file with mode: 0644]
auth/none/lang/en/auth_none.php
auth/shibboleth/classes/privacy/provider.php [new file with mode: 0644]
auth/shibboleth/lang/en/auth_shibboleth.php
auth/test_settings.php
auth/webservice/classes/privacy/provider.php [new file with mode: 0644]
auth/webservice/lang/en/auth_webservice.php
availability/condition/completion/classes/privacy/provider.php [new file with mode: 0644]
availability/condition/completion/lang/en/availability_completion.php
availability/condition/date/classes/privacy/provider.php [new file with mode: 0644]
availability/condition/date/lang/en/availability_date.php
availability/condition/grade/classes/privacy/provider.php [new file with mode: 0644]
availability/condition/grade/lang/en/availability_grade.php
availability/condition/group/classes/privacy/provider.php [new file with mode: 0644]
availability/condition/group/lang/en/availability_group.php
availability/condition/grouping/classes/privacy/provider.php [new file with mode: 0644]
availability/condition/grouping/lang/en/availability_grouping.php
availability/condition/profile/classes/privacy/provider.php [new file with mode: 0644]
availability/condition/profile/lang/en/availability_profile.php
backup/moodle2/backup_plan_builder.class.php
backup/moodle2/backup_qtype_extrafields_plugin.class.php [new file with mode: 0644]
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_plan_builder.class.php
backup/moodle2/restore_qtype_extrafields_plugin.class.php [new file with mode: 0644]
backup/moodle2/restore_stepslib.php
backup/moodle2/restore_subplugin.class.php
backup/moodle2/tests/behat/import_multiple_times.feature [new file with mode: 0644]
backup/util/plan/base_task.class.php
badges/classes/observer.php
badges/criteria/award_criteria.php
badges/criteria/award_criteria_cohort.php [new file with mode: 0644]
badges/tests/badgeslib_test.php
badges/tests/behat/criteria_cohort.feature [new file with mode: 0644]
blocks/community/classes/privacy/provider.php [new file with mode: 0644]
blocks/community/lang/en/block_community.php
blocks/community/tests/privacy_test.php [new file with mode: 0644]
blocks/html/classes/privacy/provider.php
blocks/myprofile/block_myprofile.php
blocks/rss_client/classes/privacy/provider.php [new file with mode: 0644]
blocks/rss_client/lang/en/block_rss_client.php
blocks/rss_client/tests/privacy_test.php [new file with mode: 0644]
calendar/classes/local/event/data_access/event_vault.php
calendar/externallib.php
calendar/lib.php
calendar/templates/month_mini.mustache
calendar/type/gregorian/classes/privacy/provider.php [new file with mode: 0644]
calendar/type/gregorian/lang/en/calendartype_gregorian.php
calendar/view.php
cohort/classes/external/cohort_summary_exporter.php
cohort/edit_form.php
cohort/externallib.php
cohort/lib.php
cohort/tests/behat/upload_cohorts.feature
cohort/tests/cohortlib_test.php
cohort/tests/externallib_test.php
cohort/tests/fixtures/uploadcohorts4.csv [new file with mode: 0644]
cohort/upload_form.php
completion/tests/externallib_test.php
composer.json
composer.lock
config-dist.php
course/classes/management_renderer.php
course/classes/output/modchooser_item.php
course/classes/search/section.php [new file with mode: 0644]
course/externallib.php
course/format/lib.php
course/format/singleactivity/lib.php
course/format/social/lib.php
course/format/topics/lib.php
course/format/upgrade.txt
course/format/weeks/lib.php
course/tests/behat/course_controls.feature
course/tests/behat/paged_course_navigation.feature
course/tests/externallib_test.php
course/tests/search_test.php
dataformat/csv/classes/privacy/provider.php [new file with mode: 0644]
dataformat/csv/lang/en/dataformat_csv.php
dataformat/excel/classes/privacy/provider.php [new file with mode: 0644]
dataformat/excel/lang/en/dataformat_excel.php
dataformat/html/classes/privacy/provider.php [new file with mode: 0644]
dataformat/html/lang/en/dataformat_html.php
dataformat/json/classes/privacy/provider.php [new file with mode: 0644]
dataformat/json/lang/en/dataformat_json.php
dataformat/ods/classes/privacy/provider.php [new file with mode: 0644]
dataformat/ods/lang/en/dataformat_ods.php
enrol/category/classes/task/enrol_category_sync.php [new file with mode: 0644]
enrol/category/db/tasks.php [new file with mode: 0644]
enrol/category/lang/en/enrol_category.php
enrol/category/lib.php
enrol/category/version.php
enrol/cohort/classes/task/enrol_cohort_sync.php [new file with mode: 0644]
enrol/cohort/db/tasks.php [new file with mode: 0644]
enrol/cohort/lang/en/enrol_cohort.php
enrol/cohort/lib.php
enrol/cohort/version.php
enrol/database/lib.php
enrol/database/tests/sync_test.php
enrol/locallib.php
enrol/lti/lib.php
enrol/lti/settings.php
enrol/lti/tests/behat/basic_settings.feature
enrol/manual/classes/task/send_expiry_notifications.php [new file with mode: 0644]
enrol/manual/classes/task/sync_enrolments.php [new file with mode: 0644]
enrol/manual/db/tasks.php [new file with mode: 0644]
enrol/manual/lang/en/enrol_manual.php
enrol/manual/lib.php
enrol/manual/version.php
enrol/meta/classes/task/enrol_meta_sync.php [new file with mode: 0644]
enrol/meta/db/tasks.php [new file with mode: 0644]
enrol/meta/lang/en/enrol_meta.php
enrol/meta/lib.php
enrol/meta/version.php
enrol/paypal/classes/task/process_expirations.php [new file with mode: 0644]
enrol/paypal/db/tasks.php [new file with mode: 0644]
enrol/paypal/lang/en/enrol_paypal.php
enrol/paypal/lib.php
enrol/paypal/version.php
enrol/self/bulkchangeforms.php [new file with mode: 0644]
enrol/self/classes/deleteselectedusers_form.php [new file with mode: 0644]
enrol/self/classes/deleteselectedusers_operation.php [new file with mode: 0644]
enrol/self/classes/editselectedusers_form.php [new file with mode: 0644]
enrol/self/classes/editselectedusers_operation.php [new file with mode: 0644]
enrol/self/classes/task/send_expiry_notifications.php [new file with mode: 0644]
enrol/self/classes/task/sync_enrolments.php [new file with mode: 0644]
enrol/self/db/tasks.php [new file with mode: 0644]
enrol/self/lang/en/enrol_self.php
enrol/self/lib.php
enrol/self/locallib.php
enrol/self/version.php
enrol/test_settings.php
files/converter/unoconv/classes/privacy/provider.php [new file with mode: 0644]
files/converter/unoconv/lang/en/fileconverter_unoconv.php
files/renderer.php
filter/activitynames/classes/privacy/provider.php [new file with mode: 0644]
filter/activitynames/lang/en/filter_activitynames.php
filter/algebra/classes/privacy/provider.php [new file with mode: 0644]
filter/algebra/lang/en/filter_algebra.php
filter/censor/classes/privacy/provider.php [new file with mode: 0644]
filter/censor/lang/en/filter_censor.php
filter/data/classes/privacy/provider.php [new file with mode: 0644]
filter/data/lang/en/filter_data.php
filter/emailprotect/classes/privacy/provider.php [new file with mode: 0644]
filter/emailprotect/lang/en/filter_emailprotect.php
filter/emoticon/classes/privacy/provider.php [new file with mode: 0644]
filter/emoticon/lang/en/filter_emoticon.php
filter/glossary/classes/privacy/provider.php [new file with mode: 0644]
filter/glossary/lang/en/filter_glossary.php
filter/mathjaxloader/classes/privacy/provider.php [new file with mode: 0644]
filter/mathjaxloader/lang/en/filter_mathjaxloader.php
filter/mediaplugin/classes/privacy/provider.php [new file with mode: 0644]
filter/mediaplugin/lang/en/filter_mediaplugin.php
filter/multilang/classes/privacy/provider.php [new file with mode: 0644]
filter/multilang/lang/en/filter_multilang.php
filter/tex/classes/privacy/provider.php [new file with mode: 0644]
filter/tex/lang/en/filter_tex.php
filter/tidy/classes/privacy/provider.php [new file with mode: 0644]
filter/tidy/lang/en/filter_tidy.php
filter/urltolink/classes/privacy/provider.php [new file with mode: 0644]
filter/urltolink/lang/en/filter_urltolink.php
grade/export/lib.php
grade/export/ods/classes/privacy/provider.php [new file with mode: 0644]
grade/export/ods/lang/en/gradeexport_ods.php
grade/export/txt/classes/privacy/provider.php [new file with mode: 0644]
grade/export/txt/lang/en/gradeexport_txt.php
grade/export/xls/classes/privacy/provider.php [new file with mode: 0644]
grade/export/xls/lang/en/gradeexport_xls.php
grade/export/xml/classes/privacy/provider.php [new file with mode: 0644]
grade/export/xml/lang/en/gradeexport_xml.php
grade/import/csv/classes/privacy/provider.php [new file with mode: 0644]
grade/import/csv/lang/en/gradeimport_csv.php
grade/import/direct/classes/privacy/provider.php [new file with mode: 0644]
grade/import/direct/lang/en/gradeimport_direct.php
grade/import/xml/classes/privacy/provider.php [new file with mode: 0644]
grade/import/xml/lang/en/gradeimport_xml.php
group/index.php
install.php
install/lang/af/admin.php
install/lang/de_ch/langconfig.php [new file with mode: 0644]
install/lang/fo/moodle.php
install/lang/pt/error.php
install/lang/sk/install.php
iplookup/tests/geoip_test.php
lang/en/admin.php
lang/en/antivirus.php
lang/en/badges.php
lang/en/cache.php
lang/en/cohort.php
lang/en/deprecated.txt
lang/en/install.php
lang/en/message.php
lang/en/moodle.php
lang/en/question.php
lang/en/search.php
lang/en/tag.php
lib/adminlib.php
lib/amd/build/adapter.min.js [new file with mode: 0644]
lib/amd/build/auto_rows.min.js
lib/amd/build/popper.min.js [new file with mode: 0644]
lib/amd/src/adapter.js [new file with mode: 0644]
lib/amd/src/auto_rows.js
lib/amd/src/popper.js [new file with mode: 0644]
lib/antivirus/clamav/classes/privacy/provider.php [new file with mode: 0644]
lib/antivirus/clamav/classes/scanner.php
lib/antivirus/clamav/lang/en/antivirus_clamav.php
lib/antivirus/clamav/tests/scanner_test.php
lib/badgeslib.php
lib/classes/antivirus/manager.php
lib/classes/antivirus/scanner.php
lib/classes/component.php
lib/classes/event/course_module_completion_updated.php
lib/classes/event/message_deleted.php
lib/classes/event/message_sent.php
lib/classes/event/message_viewed.php
lib/classes/event/notification_sent.php [new file with mode: 0644]
lib/classes/event/notification_viewed.php [new file with mode: 0644]
lib/classes/message/manager.php
lib/classes/output/icon_system_fontawesome.php
lib/classes/plugin_manager.php
lib/classes/task/messaging_cleanup_task.php
lib/classes/task/tag_cron_task.php
lib/coursecatlib.php
lib/db/caches.php
lib/db/events.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/deprecatedlib.php
lib/dml/mariadb_native_moodle_database.php
lib/dml/moodle_database.php
lib/dml/mssql_native_moodle_database.php [deleted file]
lib/dml/mssql_native_moodle_recordset.php [deleted file]
lib/dml/mssql_native_moodle_temptables.php [deleted file]
lib/dml/mysqli_native_moodle_database.php
lib/dml/pgsql_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_temptables.php
lib/dmllib.php
lib/editor/atto/db/upgrade.php
lib/editor/atto/plugins/recordrtc/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/lang/en/atto_recordrtc.php [new file with mode: 0755]
lib/editor/atto/plugins/recordrtc/lib.php [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/pix/i/audiortc.png [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/pix/i/videortc.png [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/pix/icon.png [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/settings.php [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/styles.css [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/version.php [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/build/moodle-atto_recordrtc-button/moodle-atto_recordrtc-button-debug.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/build/moodle-atto_recordrtc-button/moodle-atto_recordrtc-button-min.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/build/moodle-atto_recordrtc-button/moodle-atto_recordrtc-button.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/build/moodle-atto_recordrtc-recording/moodle-atto_recordrtc-recording-debug.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/build/moodle-atto_recordrtc-recording/moodle-atto_recordrtc-recording-min.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/build/moodle-atto_recordrtc-recording/moodle-atto_recordrtc-recording.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/button/build.json [new file with mode: 0755]
lib/editor/atto/plugins/recordrtc/yui/src/button/js/button.js [new file with mode: 0755]
lib/editor/atto/plugins/recordrtc/yui/src/button/meta/button.json [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/build.json [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/js/abstractmodule.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/js/audiomodule.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/js/commonmodule.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/js/compatcheckmodule.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/js/videomodule.js [new file with mode: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/recording/meta/recording.json [new file with mode: 0644]
lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
lib/editor/atto/plugins/table/yui/src/button/js/button.js
lib/editor/atto/settings.php
lib/editor/atto/version.php
lib/editor/tinymce/plugins/ctrlhelp/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/ctrlhelp/lang/en/tinymce_ctrlhelp.php
lib/editor/tinymce/plugins/managefiles/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/managefiles/lang/en/tinymce_managefiles.php
lib/editor/tinymce/plugins/moodleemoticon/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/moodleemoticon/lang/en/tinymce_moodleemoticon.php
lib/editor/tinymce/plugins/moodleimage/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/moodleimage/lang/en/tinymce_moodleimage.php
lib/editor/tinymce/plugins/moodlemedia/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/moodlemedia/lang/en/tinymce_moodlemedia.php
lib/editor/tinymce/plugins/moodlenolink/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/moodlenolink/lang/en/tinymce_moodlenolink.php
lib/editor/tinymce/plugins/pdw/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/pdw/lang/en/tinymce_pdw.php
lib/editor/tinymce/plugins/spellchecker/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/spellchecker/lang/en/tinymce_spellchecker.php
lib/editor/tinymce/plugins/wrap/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/plugins/wrap/lang/en/tinymce_wrap.php
lib/externallib.php
lib/messagelib.php
lib/moodlelib.php
lib/myprofilelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/pagelib.php
lib/phpunit/classes/message_sink.php
lib/phpunit/classes/phpmailer_sink.php
lib/phpunit/classes/util.php
lib/questionlib.php
lib/scssphp/Base/Range.php
lib/scssphp/Block.php
lib/scssphp/Colors.php
lib/scssphp/Compiler.php
lib/scssphp/Compiler/Environment.php
lib/scssphp/Exception/CompilerException.php
lib/scssphp/Exception/ParserException.php
lib/scssphp/Exception/RangeException.php
lib/scssphp/Exception/ServerException.php
lib/scssphp/Formatter.php
lib/scssphp/Formatter/Compact.php
lib/scssphp/Formatter/Compressed.php
lib/scssphp/Formatter/Crunched.php
lib/scssphp/Formatter/Debug.php
lib/scssphp/Formatter/Expanded.php
lib/scssphp/Formatter/Nested.php
lib/scssphp/Formatter/OutputBlock.php
lib/scssphp/Node.php
lib/scssphp/Node/Number.php
lib/scssphp/Parser.php
lib/scssphp/Server.php [deleted file]
lib/scssphp/SourceMap/Base64VLQEncoder.php [new file with mode: 0644]
lib/scssphp/SourceMap/SourceMapGenerator.php [new file with mode: 0644]
lib/scssphp/Type.php
lib/scssphp/Util.php
lib/scssphp/Version.php
lib/scssphp/moodle_readme.txt
lib/tablelib.php
lib/testing/generator/data_generator.php
lib/tests/antivirus_test.php
lib/tests/component_test.php
lib/tests/coursecatlib_test.php
lib/tests/fixtures/testable_antivirus.php
lib/tests/message_test.php
lib/tests/messagelib_test.php
lib/tests/moodle_page_test.php
lib/tests/moodlelib_test.php
lib/tests/questionlib_test.php
lib/tests/weblib_format_text_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/upgradelib.php
lib/weblib.php
lib/xmldb/xmldb_structure.php
media/player/html5audio/classes/privacy/provider.php [new file with mode: 0644]
media/player/html5audio/lang/en/media_html5audio.php
media/player/html5video/classes/privacy/provider.php [new file with mode: 0644]
media/player/html5video/lang/en/media_html5video.php
media/player/swf/classes/privacy/provider.php [new file with mode: 0644]
media/player/swf/lang/en/media_swf.php
media/player/videojs/classes/plugin.php
media/player/videojs/classes/privacy/provider.php [new file with mode: 0644]
media/player/videojs/lang/en/media_videojs.php
media/player/videojs/settings.php
media/player/videojs/styles.css
media/player/videojs/tests/player_test.php
media/player/vimeo/classes/privacy/provider.php [new file with mode: 0644]
media/player/vimeo/lang/en/media_vimeo.php
media/player/youtube/classes/privacy/provider.php [new file with mode: 0644]
media/player/youtube/lang/en/media_youtube.php
message/classes/api.php
message/classes/helper.php
message/classes/search/base_message.php
message/classes/search/message_received.php
message/classes/search/message_sent.php
message/classes/task/migrate_message_data.php [new file with mode: 0644]
message/externallib.php
message/index.php
message/lib.php
message/output/airnotifier/classes/privacy/provider.php [new file with mode: 0644]
message/output/airnotifier/lang/en/message_airnotifier.php
message/output/airnotifier/tests/privacy_test.php [new file with mode: 0644]
message/output/email/classes/privacy/provider.php [new file with mode: 0644]
message/output/email/lang/en/message_email.php
message/output/email/tests/privacy_test.php [new file with mode: 0644]
message/output/jabber/classes/privacy/provider.php [new file with mode: 0644]
message/output/jabber/lang/en/message_jabber.php
message/output/jabber/tests/privacy_test.php [new file with mode: 0644]
message/output/popup/amd/build/notification_repository.min.js
message/output/popup/amd/src/notification_repository.js
message/output/popup/classes/api.php
message/output/popup/classes/output/popup_notification.php
message/output/popup/db/install.xml
message/output/popup/db/upgrade.php
message/output/popup/externallib.php
message/output/popup/message_output_popup.php
message/output/popup/notifications.php
message/output/popup/tests/base.php
message/output/popup/tests/behat/message_popover_unread.feature
message/output/popup/version.php
message/tests/api_test.php
message/tests/events_test.php
message/tests/externallib_test.php
message/tests/messagelib_test.php
message/tests/migrate_message_data_task_test.php [new file with mode: 0644]
message/tests/search_received_test.php
message/tests/search_sent_test.php
message/upgrade.txt
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/comment.js
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/renderer.php
mod/assign/tests/locallib_test.php
mod/book/edit.php
mod/choice/classes/privacy/provider.php
mod/feedback/classes/responses_table.php
mod/feedback/item/multichoicerated/lib.php
mod/feedback/lang/en/feedback.php
mod/folder/download_folder.php
mod/glossary/import.php
mod/glossary/lib.php
mod/glossary/tests/behat/import_entries.feature
mod/glossary/tests/fixtures/musicians.xml [new file with mode: 0644]
mod/glossary/view.php
mod/lesson/locallib.php
mod/lesson/pagetypes/branchtable.php
mod/lesson/pagetypes/matching.php
mod/lesson/pagetypes/multichoice.php
mod/lesson/pagetypes/numerical.php
mod/lesson/pagetypes/shortanswer.php
mod/lesson/pagetypes/truefalse.php
mod/lesson/renderer.php
mod/lesson/tests/behat/lesson_exit_enter_clusters.feature [new file with mode: 0644]
mod/lti/classes/local/ltiservice/resource_base.php
mod/lti/classes/local/ltiservice/response.php
mod/lti/classes/local/ltiservice/service_base.php
mod/lti/edit_form.php
mod/lti/lang/en/lti.php
mod/lti/locallib.php
mod/lti/service/gradebookservices/backup/moodle2/backup_ltiservice_gradebookservices_subplugin.class.php [new file with mode: 0644]
mod/lti/service/gradebookservices/backup/moodle2/restore_ltiservice_gradebookservices_subplugin.class.php [new file with mode: 0644]
mod/lti/service/gradebookservices/classes/local/resources/lineitem.php [new file with mode: 0644]
mod/lti/service/gradebookservices/classes/local/resources/lineitems.php [new file with mode: 0644]
mod/lti/service/gradebookservices/classes/local/resources/results.php [new file with mode: 0644]
mod/lti/service/gradebookservices/classes/local/resources/scores.php [new file with mode: 0644]
mod/lti/service/gradebookservices/classes/local/service/gradebookservices.php [new file with mode: 0644]
mod/lti/service/gradebookservices/classes/task/cleanup_task.php [new file with mode: 0644]
mod/lti/service/gradebookservices/db/install.xml [new file with mode: 0644]
mod/lti/service/gradebookservices/db/tasks.php [new file with mode: 0644]
mod/lti/service/gradebookservices/lang/en/ltiservice_gradebookservices.php [new file with mode: 0644]
mod/lti/service/gradebookservices/tests/task_cleanup_test.php [new file with mode: 0644]
mod/lti/service/gradebookservices/version.php [new file with mode: 0644]
mod/lti/service/memberships/classes/local/resource/linkmemberships.php [deleted file]
mod/lti/service/memberships/classes/local/resources/contextmemberships.php [moved from mod/lti/service/memberships/classes/local/resource/contextmemberships.php with 50% similarity]
mod/lti/service/memberships/classes/local/resources/linkmemberships.php [new file with mode: 0644]
mod/lti/service/memberships/classes/local/service/memberships.php
mod/lti/service/memberships/db/renamedclasses.php [new file with mode: 0644]
mod/lti/service/memberships/lang/en/ltiservice_memberships.php
mod/lti/service/profile/classes/local/resources/profile.php [moved from mod/lti/service/profile/classes/local/resource/profile.php with 94% similarity]
mod/lti/service/profile/classes/local/service/profile.php
mod/lti/service/profile/db/renamedclasses.php [new file with mode: 0644]
mod/lti/service/toolproxy/classes/local/resources/toolproxy.php [moved from mod/lti/service/toolproxy/classes/local/resource/toolproxy.php with 98% similarity]
mod/lti/service/toolproxy/classes/local/service/toolproxy.php
mod/lti/service/toolproxy/db/renamedclasses.php [new file with mode: 0644]
mod/lti/service/toolsettings/classes/local/resources/contextsettings.php [moved from mod/lti/service/toolsettings/classes/local/resource/contextsettings.php with 88% similarity]
mod/lti/service/toolsettings/classes/local/resources/linksettings.php [moved from mod/lti/service/toolsettings/classes/local/resource/linksettings.php with 92% similarity]
mod/lti/service/toolsettings/classes/local/resources/systemsettings.php [moved from mod/lti/service/toolsettings/classes/local/resource/systemsettings.php with 91% similarity]
mod/lti/service/toolsettings/classes/local/service/toolsettings.php
mod/lti/service/toolsettings/db/renamedclasses.php [new file with mode: 0644]
mod/quiz/attempt.php
mod/quiz/attemptlib.php
mod/quiz/autosave.ajax.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/backup/moodle2/restore_quiz_stepslib.php
mod/quiz/classes/form/randomquestion_form.php
mod/quiz/comment.php
mod/quiz/lang/en/quiz.php
mod/quiz/locallib.php
mod/quiz/processattempt.php
mod/quiz/renderer.php
mod/quiz/report/overview/db/install.xml
mod/quiz/report/overview/db/upgrade.php
mod/quiz/report/overview/version.php
mod/quiz/review.php
mod/quiz/reviewquestion.php
mod/quiz/summary.php
mod/quiz/tests/attempt_test.php
mod/quiz/tests/attempts_test.php
mod/quiz/tests/fixtures/random_by_tag_quiz.mbz [new file with mode: 0644]
mod/quiz/tests/locallib_test.php
mod/quiz/tests/tags_test.php [new file with mode: 0644]
mod/scorm/classes/external.php
mod/scorm/datamodels/scorm_12.php
mod/scorm/datamodels/scorm_12lib.php
mod/scorm/datamodels/scorm_13lib.php
mod/scorm/db/upgrade.php
mod/scorm/lang/en/scorm.php
mod/scorm/lib.php
mod/scorm/locallib.php
mod/scorm/mod_form.php
mod/scorm/settings.php
mod/scorm/tests/behat/multisco_review_mode.feature
mod/scorm/tests/packages/RuntimeMinimumCalls_SCORM12-mini.zip [new file with mode: 0644]
mod/scorm/tests/packages/readme_moodle.txt
mod/scorm/upgrade.txt
mod/scorm/version.php
mod/survey/lib.php
mod/wiki/comments.php
mod/wiki/db/access.php
mod/wiki/diff.php
mod/wiki/files.php
mod/wiki/history.php
mod/wiki/lib.php
mod/wiki/map.php
mod/wiki/pagelib.php
mod/wiki/prettyview.php
mod/wiki/tests/externallib_test.php
mod/wiki/view.php
mod/wiki/viewversion.php
phpunit.xml.dist
pix/i/addblock.png [new file with mode: 0644]
pix/i/addblock.svg [new file with mode: 0644]
pix/i/emtpy.png [new file with mode: 0644]
pix/i/emtpy.svg [new file with mode: 0644]
pix/i/home.svg [new file with mode: 0644]
pix/i/privatefiles.svg [new file with mode: 0644]
pix/i/section.svg [new file with mode: 0644]
plagiarism/classes/privacy/legacy_polyfill.php [new file with mode: 0644]
plagiarism/classes/privacy/plagiarism_provider.php
plagiarism/tests/privacy_legacy_polyfill_test.php [new file with mode: 0644]
privacy/classes/local/metadata/collection.php
privacy/classes/local/metadata/types/plugintype_link.php
privacy/classes/local/metadata/types/subsystem_link.php
privacy/classes/local/request/helper.php
privacy/classes/local/request/moodle_content_writer.php
privacy/classes/manager.php
privacy/classes/tests/request/content_writer.php
privacy/tests/collection_test.php
privacy/tests/legacy_polyfill_test.php
privacy/tests/manager_test.php
privacy/tests/moodle_content_writer_test.php
privacy/tests/provider_test.php [new file with mode: 0644]
privacy/tests/tests_content_writer_test.php [new file with mode: 0644]
privacy/tests/types_plugintype_link_test.php
privacy/tests/types_subsystem_link_test.php
question/amd/build/edit_tags.min.js
question/amd/build/repository.min.js
question/amd/src/edit_tags.js
question/amd/src/repository.js
question/behaviour/manualgraded/behaviour.php
question/classes/bank/tags_action_column.php
question/classes/bank/view.php
question/classes/external.php
question/engine/tests/helpers.php
question/format.php
question/format/xml/format.php
question/format/xml/tests/xmlformat_test.php
question/lib.php
question/question.php
question/tests/backup_test.php [new file with mode: 0644]
question/tests/behat/move_question_categories.feature
question/tests/externallib_test.php
question/tests/generator/lib.php
question/type/calculatedsimple/tests/helper.php
question/type/ddwtos/tests/questiontype_test.php
question/type/edit_question_form.php
question/type/essay/backup/moodle2/backup_qtype_essay_plugin.class.php
question/type/essay/db/install.xml
question/type/essay/db/upgrade.php
question/type/essay/edit_essay_form.php
question/type/essay/lang/en/qtype_essay.php
question/type/essay/question.php
question/type/essay/questiontype.php
question/type/essay/renderer.php
question/type/essay/tests/behat/file_type_restriction.feature [new file with mode: 0644]
question/type/essay/tests/fixtures/testquestion.moodle.xml
question/type/essay/tests/helper.php
question/type/essay/tests/walkthrough_test.php
question/type/essay/version.php
question/type/gapselect/tests/edit_form_test.php
question/type/gapselect/tests/helper.php
question/type/gapselect/tests/questiontype_test.php
question/type/gapselect/tests/walkthrough_test.php
question/type/questionbase.php
question/type/shortanswer/backup/moodle2/backup_qtype_shortanswer_plugin.class.php
question/type/shortanswer/backup/moodle2/restore_qtype_shortanswer_plugin.class.php
question/type/tags_form.php
question/type/upgrade.txt
report/backups/classes/privacy/provider.php [new file with mode: 0644]
report/backups/lang/en/report_backups.php
report/competency/classes/privacy/provider.php [new file with mode: 0644]
report/competency/lang/en/report_competency.php
report/completion/classes/privacy/provider.php [new file with mode: 0644]
report/completion/lang/en/report_completion.php
report/configlog/classes/privacy/provider.php [new file with mode: 0644]
report/configlog/lang/en/report_configlog.php
report/courseoverview/classes/privacy/provider.php [new file with mode: 0644]
report/courseoverview/lang/en/report_courseoverview.php
report/eventlist/classes/privacy/provider.php [new file with mode: 0644]
report/eventlist/lang/en/report_eventlist.php
report/insights/classes/privacy/provider.php [new file with mode: 0644]
report/insights/lang/en/report_insights.php
report/log/classes/privacy/provider.php [new file with mode: 0644]
report/log/lang/en/report_log.php
report/loglive/classes/privacy/provider.php [new file with mode: 0644]
report/loglive/lang/en/report_loglive.php
report/outline/classes/privacy/provider.php [new file with mode: 0644]
report/outline/lang/en/report_outline.php
report/participation/classes/privacy/provider.php [new file with mode: 0644]
report/participation/lang/en/report_participation.php
report/performance/classes/privacy/provider.php [new file with mode: 0644]
report/performance/lang/en/report_performance.php
report/progress/classes/privacy/provider.php [new file with mode: 0644]
report/progress/lang/en/report_progress.php
report/questioninstances/classes/privacy/provider.php [new file with mode: 0644]
report/questioninstances/lang/en/report_questioninstances.php
report/security/classes/privacy/provider.php [new file with mode: 0644]
report/security/lang/en/report_security.php
report/security/locallib.php
report/stats/classes/privacy/provider.php [new file with mode: 0644]
report/stats/lang/en/report_stats.php
report/usersessions/classes/privacy/provider.php [new file with mode: 0644]
report/usersessions/lang/en/report_usersessions.php
repository/filepicker.js
search/classes/engine.php
search/classes/manager.php
search/classes/output/form/search.php
search/engine/simpledb/classes/engine.php [new file with mode: 0644]
search/engine/simpledb/db/install.php [new file with mode: 0644]
search/engine/simpledb/db/install.xml [new file with mode: 0644]
search/engine/simpledb/db/uninstall.php [new file with mode: 0644]
search/engine/simpledb/lang/en/search_simpledb.php [new file with mode: 0644]
search/engine/simpledb/tests/engine_test.php [new file with mode: 0644]
search/engine/simpledb/version.php [moved from message/output/popup/db/events.php with 68% similarity]
search/engine/solr/classes/engine.php
search/engine/solr/lang/en/search_solr.php
search/engine/solr/tests/engine_test.php
search/index.php
search/tests/engine_test.php
search/tests/manager_test.php
search/upgrade.txt
theme/boost/templates/core/filemanager_modal_generallayout.mustache
theme/boost/templates/core_form/element-select.mustache
theme/boost/templates/flat_navigation.mustache
user/classes/participants_table.php
user/editlib.php
user/index.php
user/lib.php
user/tests/behat/set_email_display.feature [new file with mode: 0644]
user/tests/behat/view_participants.feature
version.php
webservice/lib.php

index 8e67b3e..84e8dc0 100644 (file)
@@ -4,7 +4,6 @@
 node_modules/
 vendor/
 admin/tool/usertours/amd/src/tour.js
-admin/tool/usertours/amd/src/popper.js
 auth/cas/CAS/
 enrol/lti/ims-blti/
 filter/algebra/AlgParser.pm
@@ -57,7 +56,9 @@ lib/maxmind/MaxMind/
 lib/ltiprovider/
 lib/amd/src/truncate.js
 lib/fonts/
+lib/amd/src/adapter.js
 lib/validateurlsyntax.php
+lib/amd/src/popper.js
 media/player/videojs/amd/src/video-lazy.js
 media/player/videojs/amd/src/Youtube-lazy.js
 media/player/videojs/videojs/
index 38e84cd..83f1e76 100644 (file)
@@ -5,7 +5,6 @@ theme/more/style/custom.css
 node_modules/
 vendor/
 admin/tool/usertours/amd/src/tour.js
-admin/tool/usertours/amd/src/popper.js
 auth/cas/CAS/
 enrol/lti/ims-blti/
 filter/algebra/AlgParser.pm
@@ -58,7 +57,9 @@ lib/maxmind/MaxMind/
 lib/ltiprovider/
 lib/amd/src/truncate.js
 lib/fonts/
+lib/amd/src/adapter.js
 lib/validateurlsyntax.php
+lib/amd/src/popper.js
 media/player/videojs/amd/src/video-lazy.js
 media/player/videojs/amd/src/Youtube-lazy.js
 media/player/videojs/videojs/
index 9e436f5..a1217e8 100644 (file)
@@ -248,7 +248,7 @@ script:
     - >
       if [ "$TASK" = 'PHPUNIT' ];
       then
-        vendor/bin/phpunit;
+        vendor/bin/phpunit --fail-on-risky --disallow-test-output --verbose;
       fi
 
     - >
index 240a819..1ff43fe 100644 (file)
@@ -219,7 +219,6 @@ $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'n
                    'pgsql'  => moodle_database::get_driver_instance('pgsql',  'native'),
                    'oci'    => moodle_database::get_driver_instance('oci',    'native'),
                    'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
-                   'mssql'  => moodle_database::get_driver_instance('mssql',  'native'), // FreeTDS driver
                   );
 foreach ($databases as $type=>$database) {
     if ($database->driver_installed() !== true) {
index 9cac439..bf178b5 100644 (file)
@@ -35,10 +35,7 @@ try {
     // Continue, we return an error later depending on the requested action.
 }
 
-echo $OUTPUT->header();
-
 if ($action) {
-    require_sesskey();
 
     if ($areaid) {
         // We need to check that the area exists.
@@ -48,45 +45,76 @@ if ($action) {
         }
     }
 
-    // All actions but enable/disable need the search engine to be ready.
     if ($action !== 'enable' && $action !== 'disable') {
+        // All actions but enable/disable need the search engine to be ready.
         if (!empty($searchmanagererror)) {
             throw $searchmanagererror;
         }
+
+        // Show confirm prompt for all these actions as they may be inadvisable, or may cause
+        // an interruption in search functionality, on production systems.
+        if (!optional_param('confirm', 0, PARAM_INT)) {
+            // Display confirmation prompt.
+            $a = null;
+            if ($areaid) {
+                $a = html_writer::tag('strong', $area->get_visible_name());
+            }
+
+            $actionparams = ['sesskey' => sesskey(), 'action' => $action, 'confirm' => 1];
+            if ($areaid) {
+                $actionparams['areaid'] = $areaid;
+            }
+            $actionurl = new moodle_url('/admin/searchareas.php', $actionparams);
+            $cancelurl = new moodle_url('/admin/searchareas.php');
+            echo $OUTPUT->header();
+            echo $OUTPUT->confirm(get_string('confirm_' . $action, 'search', $a),
+                    new single_button($actionurl, get_string('continue'), 'post', true),
+                    new single_button($cancelurl, get_string('cancel'), 'get'));
+            echo $OUTPUT->footer();
+            exit;
+        }
     }
 
+    // We are now taking an actual action, so require sesskey.
+    require_sesskey();
+
     switch ($action) {
         case 'enable':
             $area->set_enabled(true);
-            echo $OUTPUT->notification(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            \core\notification::add(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
             break;
         case 'disable':
             $area->set_enabled(false);
-            echo $OUTPUT->notification(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            \core\notification::add(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
             break;
         case 'delete':
             $search = \core_search\manager::instance();
             $search->delete_index($areaid);
-            echo $OUTPUT->notification(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            \core\notification::add(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
             break;
         case 'indexall':
             $searchmanager->index();
-            echo $OUTPUT->notification(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            \core\notification::add(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
             break;
         case 'reindexall':
             $searchmanager->index(true);
-            echo $OUTPUT->notification(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            \core\notification::add(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
             break;
         case 'deleteall':
             $searchmanager->delete_index();
-            echo $OUTPUT->notification(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            \core\notification::add(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
             break;
         default:
             throw new moodle_exception('invalidaction');
             break;
     }
+
+    // Redirect back to the main page after taking action.
+    redirect(new moodle_url('/admin/searchareas.php'));
 }
 
+echo $OUTPUT->header();
+
 $searchareas = \core_search\manager::get_search_areas_list();
 if (empty($searchmanagererror)) {
     $areasconfig = $searchmanager->get_areas_config($searchareas);
@@ -192,9 +220,12 @@ echo $OUTPUT->footer();
  * @return moodle_url
  */
 function admin_searcharea_action_url($action, $areaid = false) {
-    $params = array('action' => $action, 'sesskey' => sesskey());
+    $params = array('action' => $action);
     if ($areaid) {
         $params['areaid'] = $areaid;
     }
+    if ($action === 'disable' || $action === 'enable') {
+        $params['sesskey'] = sesskey();
+    }
     return new moodle_url('/admin/searchareas.php', $params);
 }
index 5e13a2f..db05c1d 100644 (file)
@@ -22,6 +22,7 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // sp
     $temp->add(new admin_setting_configcheckbox('allowuserthemes', new lang_string('allowuserthemes', 'admin'), new lang_string('configallowuserthemes', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('allowcoursethemes', new lang_string('allowcoursethemes', 'admin'), new lang_string('configallowcoursethemes', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('allowcategorythemes',  new lang_string('allowcategorythemes', 'admin'), new lang_string('configallowcategorythemes', 'admin'), 0));
+    $temp->add(new admin_setting_configcheckbox('allowcohortthemes',  new lang_string('allowcohortthemes', 'admin'), new lang_string('configallowcohortthemes', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('allowthemechangeonurl',  new lang_string('allowthemechangeonurl', 'admin'), new lang_string('configallowthemechangeonurl', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('allowuserblockhiding', new lang_string('allowuserblockhiding', 'admin'), new lang_string('configallowuserblockhiding', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('allowblockstodock', new lang_string('allowblockstodock', 'admin'), new lang_string('configallowblockstodock', 'admin'), 1));
@@ -199,7 +200,7 @@ preferences,moodle|/user/preferences.php|preferences',
 
     // "documentation" settingpage
     $temp = new admin_settingpage('documentation', new lang_string('moodledocs'));
-    $temp->add(new admin_setting_configtext('docroot', new lang_string('docroot', 'admin'), new lang_string('configdocroot', 'admin'), 'http://docs.moodle.org', PARAM_URL));
+    $temp->add(new admin_setting_configtext('docroot', new lang_string('docroot', 'admin'), new lang_string('configdocroot', 'admin'), 'https://docs.moodle.org', PARAM_URL));
     $ltemp = array('' => get_string('forceno'));
     $ltemp += get_string_manager()->get_list_of_translations(true);
     $temp->add(new admin_setting_configselect('doclang', get_string('doclang', 'admin'), get_string('configdoclang', 'admin'), '', $ltemp));
index c55b1f1..9a2202f 100644 (file)
@@ -556,7 +556,7 @@ if ($hassiteconfig) {
     // Search engine selection.
     $temp->add(new admin_setting_heading('searchengineheading', new lang_string('searchengine', 'admin'), ''));
     $temp->add(new admin_setting_configselect('searchengine',
-                                new lang_string('selectsearchengine', 'admin'), '', 'solr', $engines));
+                                new lang_string('selectsearchengine', 'admin'), '', 'simpledb', $engines));
     $temp->add(new admin_setting_heading('searchoptionsheading', new lang_string('searchoptions', 'admin'), ''));
     $temp->add(new admin_setting_configcheckbox('searchindexwhendisabled',
             new lang_string('searchindexwhendisabled', 'admin'), new lang_string('searchindexwhendisabled_desc', 'admin'),
index a6ac61f..b6a9f89 100644 (file)
@@ -43,6 +43,9 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configtext('userquota', new lang_string('userquota', 'admin'),
                 new lang_string('configuserquota', 'admin', $params), $defaultuserquota, PARAM_INT, 30));
 
+    $temp->add(new admin_setting_configcheckbox('forceclean', new lang_string('forceclean', 'core_admin'),
+        new lang_string('forceclean_desc', 'core_admin'), 0));
+
     $temp->add(new admin_setting_configcheckbox('allowobjectembed', new lang_string('allowobjectembed', 'admin'), new lang_string('configallowobjectembed', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('enabletrusttext', new lang_string('enabletrusttext', 'admin'), new lang_string('configenabletrusttext', 'admin'), 0));
     $temp->add(new admin_setting_configselect('maxeditingtime', new lang_string('maxeditingtime','admin'), new lang_string('configmaxeditingtime','admin'), 1800,
index 7fc752f..d444961 100644 (file)
@@ -28,7 +28,7 @@ if ($hassiteconfig
         $choices['1'] = new lang_string('emaildisplayyes');
         $choices['2'] = new lang_string('emaildisplaycourse');
         $temp->add(new admin_setting_configselect('defaultpreference_maildisplay', new lang_string('emaildisplay'),
-            '', 2, $choices));
+            new lang_string('emaildisplay_help'), 2, $choices));
 
         $choices = array();
         $choices['0'] = new lang_string('textformat');
@@ -150,6 +150,7 @@ if ($hassiteconfig
         $temp->add(new admin_setting_configmultiselect('hiddenuserfields', new lang_string('hiddenuserfields', 'admin'),
                    new lang_string('confighiddenuserfields', 'admin'), array(),
                        array('description' => new lang_string('description'),
+                             'email' => new lang_string('email'),
                              'city' => new lang_string('city'),
                              'country' => new lang_string('country'),
                              'timezone' => new lang_string('timezone'),
diff --git a/admin/tests/behat/search_areas.feature b/admin/tests/behat/search_areas.feature
new file mode 100644 (file)
index 0000000..c1c4909
--- /dev/null
@@ -0,0 +1,21 @@
+@core @core_admin
+Feature: Use the search areas admin screen
+  In order to control search indexing
+  As an admin
+  I need to use the search areas admin screen
+
+  Background:
+    Given I log in as "admin"
+    And I navigate to "Plugins > Search > Search areas" in site administration
+
+  Scenario: Disable and enable a search area
+    When I click on "Disable" "link" in the "Book - resource information" "table_row"
+    Then I should see "Search area disabled" in the ".alert-success" "css_element"
+    And I should see "Search area disabled" in the "Book - resource information" "table_row"
+
+    When I click on "Enable" "link" in the "Book - resource information" "table_row"
+    Then I should see "Search area enabled" in the ".alert-success" "css_element"
+    And I should not see "Search area disabled" in the "Book - resource information" "table_row"
+
+  # Note: Other scenarios are not currently easy to implement in Behat because there is no mock
+  # search engine - we could add testing once Moodle has an internal database search engine.
index 1acb99c..bad2c1b 100644 (file)
@@ -44,8 +44,7 @@ class helper {
             'native/mariadb' => \moodle_database::get_driver_instance('mariadb', 'native')->get_name(),
             'native/pgsql'   => \moodle_database::get_driver_instance('pgsql', 'native')->get_name(),
             'native/oci'     => \moodle_database::get_driver_instance('oci', 'native')->get_name(),
-            'native/sqlsrv'  => \moodle_database::get_driver_instance('sqlsrv', 'native')->get_name(),
-            'native/mssql'   => \moodle_database::get_driver_instance('mssql', 'native')->get_name()
+            'native/sqlsrv'  => \moodle_database::get_driver_instance('sqlsrv', 'native')->get_name()
         );
     }
 
index c9925e4..c8ef59f 100644 (file)
@@ -666,25 +666,9 @@ class manager {
 
         if (!empty($CFG->antiviruses)) {
             mtrace("--> Attempting virus scan of '{$attachment->filename}'");
-
-            // Store the file on disk - it will need to be virus scanned first.
-            $itemid = rand(1, 999999999);;
-            $directory = make_temp_directory("/messageinbound/{$itemid}", false);
-            $filepath = $directory . "/" . $attachment->filename;
-            if (!$fp = fopen($filepath, "w")) {
-                // Unable to open the temporary file to write this to disk.
-                mtrace("--> Unable to save the file to disk for virus scanning. Check file permissions.");
-
-                throw new \core\message\inbound\processing_failed_exception('attachmentfilepermissionsfailed',
-                        'tool_messageinbound');
-            }
-
-            fwrite($fp, $attachment->content);
-            fclose($fp);
-
             // Perform a virus scan now.
             try {
-                \core\antivirus\manager::scan_file($filepath, $attachment->filename, true);
+                \core\antivirus\manager::scan_data($attachment->content);
             } catch (\core\antivirus\scanner_exception $e) {
                 mtrace("--> A virus was found in the attachment '{$attachment->filename}'.");
                 $this->inform_attachment_virus();
index 4841569..9ef98e1 100644 (file)
@@ -90,6 +90,25 @@ class api {
 
                 require("$path/db/mobile.php");
                 foreach ($addons as $addonname => $addoninfo) {
+
+                    // Add handlers (for site add-ons).
+                    $handlers = !empty($addoninfo['handlers']) ? $addoninfo['handlers'] : array();
+                    $handlers = json_encode($handlers); // JSON formatted, since it is a complex structure that may vary over time.
+
+                    // Now language strings used by the app.
+                    $lang = array();
+                    if (!empty($addoninfo['lang'])) {
+                        $stringmanager = get_string_manager();
+                        $langs = $stringmanager->get_list_of_translations();
+                        foreach ($langs as $langid => $langname) {
+                            foreach ($addoninfo['lang'] as $stringinfo) {
+                                $lang[$langid][$stringinfo[0]] =
+                                    $stringmanager->get_string($stringinfo[0], $stringinfo[1], null, $langid);
+                            }
+                        }
+                    }
+                    $lang = json_encode($lang);
+
                     $plugininfo = array(
                         'component' => $component,
                         'version' => $version,
@@ -97,7 +116,9 @@ class api {
                         'dependencies' => !empty($addoninfo['dependencies']) ? $addoninfo['dependencies'] : array(),
                         'fileurl' => '',
                         'filehash' => '',
-                        'filesize' => 0
+                        'filesize' => 0,
+                        'handlers' => $handlers,
+                        'lang' => $lang,
                     );
 
                     // All the mobile packages must be under the plugin mobile directory.
@@ -151,6 +172,11 @@ class api {
             'tool_mobile_disabledfeatures' => get_config('tool_mobile', 'disabledfeatures'),
             'country' => clean_param($CFG->country, PARAM_NOTAGS),
             'agedigitalconsentverification' => \core_auth\digital_consent::is_age_digital_consent_verification_enabled(),
+            'autolang' => $CFG->autolang,
+            'lang' => clean_param($CFG->lang, PARAM_LANG),  // Avoid breaking WS because of incorrect package langs.
+            'langmenu' => $CFG->langmenu,
+            'langlist' => $CFG->langlist,
+            'locale' => $CFG->locale,
         );
 
         $typeoflogin = get_config('tool_mobile', 'typeoflogin');
@@ -332,6 +358,7 @@ class api {
         }
 
         $features = array(
+            'NoDelegate_CoreOffline' => new lang_string('offlineuse', 'tool_mobile'),
             '$mmLoginEmailSignup' => new lang_string('startsignup'),
             "$mainmenu" => array(
                 '$mmSideMenuDelegate_mmCourses' => new lang_string('mycourses'),
@@ -352,6 +379,8 @@ class api {
                 '$mmCoursesDelegate_mmaGrades' => new lang_string('grades', 'grades'),
                 '$mmCoursesDelegate_mmaCourseCompletion' => new lang_string('coursecompletion', 'completion'),
                 '$mmCoursesDelegate_mmaNotes' => new lang_string('notes', 'notes'),
+                'NoDelegate_CoreCourseDownload' => new lang_string('downloadcourse', 'tool_mobile'),
+                'NoDelegate_CoreCoursesDownload' => new lang_string('downloadcourses', 'tool_mobile'),
             ),
             "$user" => array(
                 '$mmUserDelegate_mmaBadges' => new lang_string('badges', 'badges'),
index 65a5ccf..6a46264 100644 (file)
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
 require_once("$CFG->libdir/externallib.php");
 
 use external_api;
+use external_files;
 use external_function_parameters;
 use external_value;
 use external_single_structure;
@@ -37,6 +38,7 @@ use context_system;
 use moodle_exception;
 use moodle_url;
 use core_text;
+use coding_exception;
 
 /**
  * This is the external API for this tool.
@@ -91,7 +93,9 @@ class external extends external_api {
                             'fileurl' => new external_value(PARAM_URL, 'The addon package url for download
                                                             or empty if it doesn\'t exist.'),
                             'filehash' => new external_value(PARAM_RAW, 'The addon package hash or empty if it doesn\'t exist.'),
-                            'filesize' => new external_value(PARAM_INT, 'The addon package size or empty if it doesn\'t exist.')
+                            'filesize' => new external_value(PARAM_INT, 'The addon package size or empty if it doesn\'t exist.'),
+                            'handlers' => new external_value(PARAM_RAW, 'Handlers definition (JSON)', VALUE_OPTIONAL),
+                            'lang' => new external_value(PARAM_RAW, 'Language strings used by the handlers (JSON)', VALUE_OPTIONAL),
                         )
                     )
                 ),
@@ -168,6 +172,12 @@ class external extends external_api {
                     (only if age verification is enabled).', VALUE_OPTIONAL),
                 'supportemail' => new external_value(PARAM_EMAIL, 'Site support contact email
                     (only if age verification is enabled).', VALUE_OPTIONAL),
+                'autolang' => new external_value(PARAM_INT, 'Whether to detect default language
+                    from browser setting.', VALUE_OPTIONAL),
+                'lang' => new external_value(PARAM_LANG, 'Default language for the site.', VALUE_OPTIONAL),
+                'langmenu' => new external_value(PARAM_INT, 'Whether the language menu should be displayed.', VALUE_OPTIONAL),
+                'langlist' => new external_value(PARAM_RAW, 'Languages on language menu.', VALUE_OPTIONAL),
+                'locale' => new external_value(PARAM_RAW, 'Sitewide locale.', VALUE_OPTIONAL),
                 'warnings' => new external_warnings(),
             )
         );
@@ -324,4 +334,130 @@ class external extends external_api {
             )
         );
     }
+
+    /**
+     * Returns description of get_content() parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.5
+     */
+    public static function get_content_parameters() {
+        return new external_function_parameters(
+            array(
+                'component' => new external_value(PARAM_COMPONENT, 'Component where the class is e.g. mod_assign.'),
+                'method' => new external_value(PARAM_ALPHANUMEXT, 'Method to execute in class \$component\output\mobile.'),
+                'args' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'name' => new external_value(PARAM_ALPHANUMEXT, 'Param name.'),
+                            'value' => new external_value(PARAM_RAW, 'Param value.')
+                        )
+                    ), 'Args for the method are optional.', VALUE_OPTIONAL
+                )
+            )
+        );
+    }
+
+    /**
+     * Returns a piece of content to be displayed in the Mobile app, it usually returns a template, javascript and
+     * other structured data that will be used to render a view in the Mobile app..
+     *
+     * Callbacks (placed in \$component\output\mobile) that are called by this web service are responsible for doing the
+     * appropriate security checks to access the information to be returned.
+     *
+     * @param string $component fame of the component.
+     * @param string $method function method name in class \$component\output\mobile.
+     * @param array $args optional arguments for the method.
+     * @return array HTML, JavaScript and other required data and information to create a view in the app.
+     * @since Moodle 3.5
+     * @throws coding_exception
+     */
+    public static function get_content($component, $method, $args = array()) {
+        global $OUTPUT, $PAGE, $USER;
+
+        $params = self::validate_parameters(self::get_content_parameters(),
+            array(
+                'component' => $component,
+                'method' => $method,
+                'args' => $args
+            )
+        );
+
+        // Reformat arguments into something less unwieldy.
+        $arguments = array();
+        foreach ($params['args'] as $paramargument) {
+            $arguments[$paramargument['name']] = $paramargument['value'];
+        }
+
+        // The component was validated via the PARAM_COMPONENT parameter type.
+        $classname = '\\' . $params['component'] .'\output\mobile';
+        if (!method_exists($classname, $params['method'])) {
+            throw new coding_exception("Missing method in $classname");
+        }
+        $result = call_user_func_array(array($classname, $params['method']), array($arguments));
+
+        // Populate otherdata.
+        $otherdata = array();
+        if (!empty($result['otherdata'])) {
+            $result['otherdata'] = (array) $result['otherdata'];
+            foreach ($result['otherdata'] as $name => $value) {
+                $otherdata[] = array(
+                    'name' => $name,
+                    'value' => $value
+                );
+            }
+        }
+
+        return array(
+            'templates'  => !empty($result['templates']) ? $result['templates'] : array(),
+            'javascript' => !empty($result['javascript']) ? $result['javascript'] : '',
+            'otherdata'  => $otherdata,
+            'files'      => !empty($result['files']) ? $result['files'] : array(),
+            'restrict'   => !empty($result['restrict']) ? $result['restrict'] : array(),
+        );
+    }
+
+    /**
+     * Returns description of get_content() result value
+     *
+     * @return array
+     * @since Moodle 3.5
+     */
+    public static function get_content_returns() {
+        return new external_single_structure(
+            array(
+                'templates' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'id' => new external_value(PARAM_TEXT, 'ID of the template.'),
+                            'html' => new external_value(PARAM_RAW, 'HTML code.'),
+                        )
+                    ),
+                    'Templates required by the generated content.'
+                ),
+                'javascript' => new external_value(PARAM_RAW, 'JavaScript code.'),
+                'otherdata' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'name' => new external_value(PARAM_RAW, 'Field name.'),
+                            'value' => new external_value(PARAM_RAW, 'Field value.')
+                        )
+                    ),
+                    'Other data that can be used or manipulated by the template via 2-way data-binding.'
+                ),
+                'files' => new external_files('Files in the content.'),
+                'restrict' => new external_single_structure(
+                    array(
+                        'users' => new external_multiple_structure(
+                            new external_value(PARAM_INT, 'user id'), 'List of allowed users.', VALUE_OPTIONAL
+                        ),
+                        'courses' => new external_multiple_structure(
+                            new external_value(PARAM_INT, 'course id'), 'List of allowed courses.', VALUE_OPTIONAL
+                        ),
+                    ),
+                    'Restrict this content to certain users or courses.'
+                )
+            )
+        );
+    }
 }
index aa64cc7..5e329c6 100644 (file)
@@ -60,6 +60,13 @@ $functions = array(
                             Is created only in https sites and is restricted by time and ip address.',
         'type'        => 'write',
         'services'    => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-    )
+    ),
+    'tool_mobile_get_content' => array(
+        'classname'   => 'tool_mobile\external',
+        'methodname'  => 'get_content',
+        'description' => 'Returns a piece of content to be displayed in the Mobile app.',
+        'type'        => 'read',
+        'services'    => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
 );
 
index d3ddbec..1693ba5 100644 (file)
@@ -51,6 +51,8 @@ Mis calificaciones|https://someurl.xyz/local/mygrades/index.php|embedded|es
 $string['disabledfeatures'] = 'Disabled features';
 $string['disabledfeatures_desc'] = 'Select here the features you want to disable in the Mobile app for your site. Please note that some features listed here could be already disabled via other site settings. You will have to log out and log in again in the app to see the changes.';
 $string['displayerrorswarning'] = 'Display debug messages (debugdisplay) is enabled. It should be disabled.';
+$string['downloadcourse'] = 'Download course';
+$string['downloadcourses'] = 'Download courses';
 $string['enablesmartappbanners'] = 'Enable App Banners';
 $string['enablesmartappbanners_desc'] = 'If enabled, a banner promoting the mobile app will be displayed when accessing the site using a mobile browser.';
 $string['forcedurlscheme'] = 'If you want to allow only your custom branded app to be opened via a browser window, then specify its URL scheme here; otherwise leave the field empty.';
@@ -79,13 +81,14 @@ $string['mobilecssurl'] = 'CSS';
 $string['mobilefeatures'] = 'Mobile features';
 $string['mobilenotificationsdisabledwarning'] = 'Mobile notifications are not enabled. They should be enabled in Manage message outputs.';
 $string['mobilesettings'] = 'Mobile settings';
+$string['offlineuse'] = 'Offline use';
 $string['pluginname'] = 'Moodle Mobile tools';
+$string['pluginnotenabledorconfigured'] = 'Plugin not enabled or configured.';
+$string['remoteaddons'] = 'Remote add-ons';
 $string['selfsignedoruntrustedcertificatewarning'] = 'It seems that the HTTPS certificate is self-signed or not trusted. The mobile app will only work with trusted sites.';
 $string['setuplink'] = 'App download page';
 $string['setuplink_desc'] = 'URL of page with links to download the mobile app from the App Store and Google Play.';
 $string['smartappbanners'] = 'App Banners';
-$string['pluginnotenabledorconfigured'] = 'Plugin not enabled or configured.';
-$string['remoteaddons'] = 'Remote add-ons';
 $string['typeoflogin'] = 'Type of login';
 $string['typeoflogin_desc'] = 'If the site uses a SSO authentication method, then select via a browser window or via an embedded browser. An embedded browser provides a better user experience, though it doesn\'t work with all SSO plugins.';
 $string['getmoodleonyourmobile'] = 'Get the mobile app';
index 19483ee..0b8873b 100644 (file)
@@ -29,6 +29,7 @@ defined('MOODLE_INTERNAL') || die();
 global $CFG;
 
 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/admin/tool/mobile/tests/fixtures/output/mobile.php');
 
 use tool_mobile\external;
 use tool_mobile\api;
@@ -88,6 +89,11 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
             'launchurl' => "$CFG->wwwroot/$CFG->admin/tool/mobile/launch.php",
             'country' => $CFG->country,
             'agedigitalconsentverification' => \core_auth\digital_consent::is_age_digital_consent_verification_enabled(),
+            'autolang' => $CFG->autolang,
+            'lang' => $CFG->lang,
+            'langmenu' => $CFG->langmenu,
+            'langlist' => $CFG->langlist,
+            'locale' => $CFG->locale,
             'warnings' => array()
         );
         $this->assertEquals($expected, $result);
@@ -101,6 +107,8 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
         set_config('logocompact', 'mock.png', 'core_admin');
         set_config('forgottenpasswordurl', 'mailto:fake@email.zy'); // Test old hack.
         set_config('agedigitalconsentverification', 1);
+        set_config('autolang', 1);
+        set_config('lang', 'a_b');  // Set invalid lang.
 
         list($authinstructions, $notusedformat) = external_format_text($authinstructions, FORMAT_MOODLE, $context->id);
         $expected['registerauth'] = 'email';
@@ -110,6 +118,8 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
         $expected['agedigitalconsentverification'] = true;
         $expected['supportname'] = $CFG->supportname;
         $expected['supportemail'] = $CFG->supportemail;
+        $expected['autolang'] = '1';
+        $expected['lang'] = ''; // Expect empty because it was set to an invalid lang.
 
         if ($logourl = $OUTPUT->get_logo_url()) {
             $expected['logourl'] = $logourl->out(false);
@@ -296,4 +306,53 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
         $this->expectExceptionMessage(get_string('autologinkeygenerationlockout', 'tool_mobile'));
         $result = external::get_autologin_key($token->privatetoken);
     }
+
+    /**
+     * Test get_content.
+     */
+    public function test_get_content() {
+
+        $paramval = 16;
+        $result = external::get_content('tool_mobile', 'test_view', array(array('name' => 'param1', 'value' => $paramval)));
+        $result = external_api::clean_returnvalue(external::get_content_returns(), $result);
+        $this->assertCount(1, $result['templates']);
+        $this->assertCount(1, $result['otherdata']);
+        $this->assertCount(2, $result['restrict']['users']);
+        $this->assertCount(2, $result['restrict']['courses']);
+        $this->assertEquals('alert();', $result['javascript']);
+        $this->assertEquals('main', $result['templates'][0]['id']);
+        $this->assertEquals('The HTML code', $result['templates'][0]['html']);
+        $this->assertEquals('otherdata1', $result['otherdata'][0]['name']);
+        $this->assertEquals($paramval, $result['otherdata'][0]['value']);
+        $this->assertEquals(array(1, 2), $result['restrict']['users']);
+        $this->assertEquals(array(3, 4), $result['restrict']['courses']);
+        $this->assertEmpty($result['files']);
+    }
+
+    /**
+     * Test get_content non existent function in valid component.
+     */
+    public function test_get_content_non_existent_function() {
+
+        $this->expectException('coding_exception');
+        $result = external::get_content('tool_mobile', 'test_blahblah');
+    }
+
+    /**
+     * Test get_content incorrect component.
+     */
+    public function test_get_content_invalid_component() {
+
+        $this->expectException('moodle_exception');
+        $result = external::get_content('tool_mobile\hack', 'test_view');
+    }
+
+    /**
+     * Test get_content non existent component.
+     */
+    public function test_get_content_non_existent_component() {
+
+        $this->expectException('moodle_exception');
+        $result = external::get_content('tool_blahblahblah', 'test_view');
+    }
 }
diff --git a/admin/tool/mobile/tests/fixtures/output/mobile.php b/admin/tool/mobile/tests/fixtures/output/mobile.php
new file mode 100644 (file)
index 0000000..d803743
--- /dev/null
@@ -0,0 +1,60 @@
+<?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/>.
+
+/**
+ * Mock class for get_content.
+ *
+ * @package tool_mobile
+ * @copyright 2018 Juan Leyva
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_mobile\output;
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Mock class for get_content.
+ *
+ * @package tool_mobile
+ * @copyright 2018 Juan Leyva
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mobile {
+
+    /**
+     * Returns a test view.
+     * @param  array $args Arguments from tool_mobile_get_content WS
+     *
+     * @return array       HTML, javascript and otherdata
+     */
+    public static function test_view($args) {
+        $args = (object) $args;
+
+        return array(
+            'templates' => array(
+                array(
+                    'id' => 'main',
+                    'html' => 'The HTML code',
+                ),
+            ),
+            'javascript' => 'alert();',
+            'otherdata' => array('otherdata1' => $args->param1),
+            'restrict' => array('users' => array(1, 2), 'courses' => array(3, 4)),
+            'files' => array()
+        );
+    }
+}
index 6c2c704..2149382 100644 (file)
@@ -1,6 +1,11 @@
 This files describes changes in tool_mobile code.
 Information provided here is intended especially for developers.
 
+=== 3.5 ===
+
+ * External function tool_mobile::tool_mobile_get_plugins_supporting_mobile now returns additional plugins information required by
+   Moodle Mobile 3.5.0.
+
 === 3.4 ===
 
  * External function tool_mobile::tool_mobile_get_plugins_supporting_mobile is now available via AJAX for not logged users.
index c09e6fd..438a6d5 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 defined('MOODLE_INTERNAL') || die();
-$plugin->version   = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2017111301; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2017110800; // Requires this Moodle version.
 $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
 $plugin->dependencies = array(
index c291a73..c0eb764 100644 (file)
@@ -229,6 +229,7 @@ class admin_uploaduser_form2 extends moodleform {
         $choices = array(0 => get_string('emaildisplayno'), 1 => get_string('emaildisplayyes'), 2 => get_string('emaildisplaycourse'));
         $mform->addElement('select', 'maildisplay', get_string('emaildisplay'), $choices);
         $mform->setDefault('maildisplay', core_user::get_property_default('maildisplay'));
+        $mform->addHelpButton('maildisplay', 'emaildisplay');
 
         $choices = array(0 => get_string('textformat'), 1 => get_string('htmlformat'));
         $mform->addElement('select', 'mailformat', get_string('emailformat'), $choices);
diff --git a/admin/tool/usertours/amd/build/popper.min.js b/admin/tool/usertours/amd/build/popper.min.js
deleted file mode 100644 (file)
index 40214ba..0000000
Binary files a/admin/tool/usertours/amd/build/popper.min.js and /dev/null differ
index f4b8763..8912559 100644 (file)
Binary files a/admin/tool/usertours/amd/build/tour.min.js and b/admin/tool/usertours/amd/build/tour.min.js differ
index d2db94e..1fb69a3 100644 (file)
@@ -5,13 +5,6 @@ Flexitour Instructions
 1. Clone https://github.com/andrewnicols/flexitour into an unrelated directory
 2. Copy /build/tour.js to amd/src/tour.js
 3. Open the amd/src/tour.js file and find the AMD module define.
-4. Change the "popper" inclusion to "./popper"
+4. Change the "popper" inclusion to "core/popper"
 5. Update thirdpartylibs.xml
 6. Run `grunt amd`
-
-Popper.js Instructions
-----------------------
-1. Clone https://github.com/FezVrasta/popper.js into an unrelated directory
-2. Copy /build/popper.js to amd/src/popper.js
-3. Update thirdpartylibs.xml
-4. Run `grunt amd`
diff --git a/admin/tool/usertours/amd/src/popper.js b/admin/tool/usertours/amd/src/popper.js
deleted file mode 100644 (file)
index a1d9d8c..0000000
+++ /dev/null
@@ -1,2032 +0,0 @@
-/**!
- * @fileOverview Kickass library to create and place poppers near their reference elements.
- * @version 1.0.8
- * @license
- * Copyright (c) 2016 Federico Zivolo and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */    
-(function (global, factory) {
-       typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-       typeof define === 'function' && define.amd ? define(factory) :
-       (global.Popper = factory());
-}(this, (function () { 'use strict';
-
-/**
- * Returns the offset parent of the given element
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element
- * @returns {Element} offset parent
- */
-function getOffsetParent(element) {
-    // NOTE: 1 DOM access here
-    var offsetParent = element.offsetParent;
-    var nodeName = offsetParent && offsetParent.nodeName;
-
-    if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
-        return window.document.documentElement;
-    }
-
-    return offsetParent;
-}
-
-/**
- * Get CSS computed property of the given element
- * @method
- * @memberof Popper.Utils
- * @argument {Eement} element
- * @argument {String} property
- */
-function getStyleComputedProperty(element, property) {
-    if (element.nodeType !== 1) {
-        return [];
-    }
-    // NOTE: 1 DOM access here
-    var css = window.getComputedStyle(element, null);
-    return property ? css[property] : css;
-}
-
-/**
- * Returns the parentNode or the host of the element
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element
- * @returns {Element} parent
- */
-function getParentNode(element) {
-    if (element.nodeName === 'HTML') {
-        return element;
-    }
-    return element.parentNode || element.host;
-}
-
-/**
- * Returns the scrolling parent of the given element
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element
- * @returns {Element} scroll parent
- */
-function getScrollParent(element) {
-    // Return body, `getScroll` will take care to get the correct `scrollTop` from it
-    if (!element || ['HTML', 'BODY', '#document'].indexOf(element.nodeName) !== -1) {
-        return window.document.body;
-    }
-
-    // Firefox want us to check `-x` and `-y` variations as well
-
-    var _getStyleComputedProp = getStyleComputedProperty(element),
-        overflow = _getStyleComputedProp.overflow,
-        overflowX = _getStyleComputedProp.overflowX,
-        overflowY = _getStyleComputedProp.overflowY;
-
-    if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) {
-        return element;
-    }
-
-    return getScrollParent(getParentNode(element));
-}
-
-/**
- * Check if the given element is fixed or is inside a fixed parent
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element
- * @argument {Element} customContainer
- * @returns {Boolean} answer to "isFixed?"
- */
-function isFixed(element) {
-    var nodeName = element.nodeName;
-    if (nodeName === 'BODY' || nodeName === 'HTML') {
-        return false;
-    }
-    if (getStyleComputedProperty(element, 'position') === 'fixed') {
-        return true;
-    }
-    return isFixed(getParentNode(element));
-}
-
-/**
- * Helper used to get the position which will be applied to the popper
- * @method
- * @memberof Popper.Utils
- * @param {HTMLElement} element - popper element
- * @returns {String} position
- */
-function getPosition(element) {
-  var container = getOffsetParent(element);
-
-  // Decide if the popper will be fixed
-  // If the reference element is inside a fixed context, the popper will be fixed as well to allow them to scroll together
-  var isParentFixed = isFixed(container);
-  return isParentFixed ? 'fixed' : 'absolute';
-}
-
-/*
- * Helper to detect borders of a given element
- * @method
- * @memberof Popper.Utils
- * @param {CSSStyleDeclaration} styles - result of `getStyleComputedProperty` on the given element
- * @param {String} axis - `x` or `y`
- * @return {Number} borders - the borders size of the given axis
- */
-
-function getBordersSize(styles, axis) {
-  var sideA = axis === 'x' ? 'Left' : 'Top';
-  var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
-
-  return Number(styles['border' + sideA + 'Width'].split('px')[0]) + Number(styles['border' + sideB + 'Width'].split('px')[0]);
-}
-
-/**
- * Get bounding client rect of given element
- * @method
- * @memberof Popper.Utils
- * @param {HTMLElement} element
- * @return {Object} client rect
- */
-function getBoundingClientRect(element) {
-    var isIE10 = navigator.appVersion.indexOf('MSIE 10') !== -1;
-    var rect = void 0;
-
-    // IE10 10 FIX: Please, don't ask, the element isn't
-    // considered in DOM in some circumstances...
-    // This isn't reproducible in IE10 compatibility mode of IE11
-    if (isIE10) {
-        try {
-            rect = element.getBoundingClientRect();
-        } catch (err) {
-            rect = {};
-        }
-    } else {
-        rect = element.getBoundingClientRect();
-    }
-
-    var result = {
-        left: rect.left,
-        top: rect.top,
-        right: rect.right,
-        bottom: rect.bottom,
-        width: rect.right - rect.left,
-        height: rect.bottom - rect.top
-    };
-
-    // IE10 FIX: `getBoundingClientRect`, when executed on `documentElement`
-    // will not take in account the `scrollTop` and `scrollLeft`
-    if (element.nodeName === 'HTML' && isIE10) {
-        var _window$document$docu = window.document.documentElement,
-            scrollTop = _window$document$docu.scrollTop,
-            scrollLeft = _window$document$docu.scrollLeft;
-
-        result.top -= scrollTop;
-        result.bottom -= scrollTop;
-        result.left -= scrollLeft;
-        result.right -= scrollLeft;
-    }
-
-    // subtract scrollbar size from sizes
-    var horizScrollbar = rect.width - (element.clientWidth || rect.right - rect.left);
-    var vertScrollbar = rect.height - (element.clientHeight || rect.bottom - rect.top);
-
-    // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
-    // we make this check conditional for performance reasons
-    if (horizScrollbar || vertScrollbar) {
-        var styles = getStyleComputedProperty(element);
-        horizScrollbar -= getBordersSize(styles, 'x');
-        vertScrollbar -= getBordersSize(styles, 'y');
-    }
-
-    result.right -= horizScrollbar;
-    result.width -= horizScrollbar;
-    result.bottom -= vertScrollbar;
-    result.height -= vertScrollbar;
-
-    return result;
-}
-
-function getScroll(element) {
-    var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
-
-    var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
-    var nodeName = element.nodeName;
-
-    if (nodeName === 'BODY' || nodeName === 'HTML') {
-        var html = window.document.documentElement;
-        var scrollingElement = window.document.scrollingElement || html;
-        return scrollingElement[upperSide];
-    }
-
-    return element[upperSide];
-}
-
-/*
- * Sum or subtract the element scroll values (left and top) from a given rect object
- * @method
- * @memberof Popper.Utils
- * @param {Object} rect - Rect object you want to change
- * @param {HTMLElement} element - The element from the function reads the scroll values
- * @param {Boolean} subtract - set to true if you want to subtract the scroll values
- * @return {Object} rect - The modifier rect object
- */
-function includeScroll(rect, element) {
-  var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
-
-  var scrollTop = getScroll(element, 'top');
-  var scrollLeft = getScroll(element, 'left');
-  var modifier = subtract ? -1 : 1;
-  rect.top += scrollTop * modifier;
-  rect.bottom += scrollTop * modifier;
-  rect.left += scrollLeft * modifier;
-  rect.right += scrollLeft * modifier;
-  return rect;
-}
-
-/**
- * Given an element and one of its parents, return the offset
- * @method
- * @memberof Popper.Utils
- * @param {HTMLElement} element
- * @param {HTMLElement} parent
- * @return {Object} rect
- */
-function getOffsetRectRelativeToCustomParent(element, parent) {
-    var fixed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
-    var transformed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
-
-    var scrollParent = getScrollParent(parent);
-    var elementRect = getBoundingClientRect(element);
-    var parentRect = getBoundingClientRect(parent);
-
-    var rect = {
-        top: elementRect.top - parentRect.top,
-        left: elementRect.left - parentRect.left,
-        bottom: elementRect.top - parentRect.top + elementRect.height,
-        right: elementRect.left - parentRect.left + elementRect.width,
-        width: elementRect.width,
-        height: elementRect.height
-    };
-
-    if (fixed && !transformed) {
-        rect = includeScroll(rect, scrollParent, true);
-    }
-    // When a popper doesn't have any positioned or scrollable parents, `offsetParent.contains(scrollParent)`
-    // will return a "false positive". This is happening because `getOffsetParent` returns `html` node,
-    // and `scrollParent` is the `body` node. Hence the additional check.
-    else if (getOffsetParent(element).contains(scrollParent) && scrollParent.nodeName !== 'BODY') {
-            rect = includeScroll(rect, parent);
-        }
-
-    // subtract borderTopWidth and borderTopWidth from final result
-    var styles = getStyleComputedProperty(parent);
-    var borderTopWidth = Number(styles.borderTopWidth.split('px')[0]);
-    var borderLeftWidth = Number(styles.borderLeftWidth.split('px')[0]);
-
-    rect.top -= borderTopWidth;
-    rect.bottom -= borderTopWidth;
-    rect.left -= borderLeftWidth;
-    rect.right -= borderLeftWidth;
-
-    return rect;
-}
-
-function getWindowSizes() {
-    var body = window.document.body;
-    var html = window.document.documentElement;
-    return {
-        height: Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
-        width: Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth)
-    };
-}
-
-/**
- * Get the position of the given element, relative to its offset parent
- * @method
- * @memberof Popper.Utils
- * @param {Element} element
- * @return {Object} position - Coordinates of the element and its `scrollTop`
- */
-function getOffsetRect(element) {
-    var elementRect = void 0;
-    if (element.nodeName === 'HTML') {
-        var _getWindowSizes = getWindowSizes(),
-            width = _getWindowSizes.width,
-            height = _getWindowSizes.height;
-
-        elementRect = {
-            width: width,
-            height: height,
-            left: 0,
-            top: 0
-        };
-    } else {
-        elementRect = {
-            width: element.offsetWidth,
-            height: element.offsetHeight,
-            left: element.offsetLeft,
-            top: element.offsetTop
-        };
-    }
-
-    elementRect.right = elementRect.left + elementRect.width;
-    elementRect.bottom = elementRect.top + elementRect.height;
-
-    // position
-    return elementRect;
-}
-
-function getOffsetRectRelativeToViewport(element) {
-    // Offset relative to offsetParent
-    var relativeOffset = getOffsetRect(element);
-
-    if (element.nodeName !== 'HTML') {
-        var offsetParent = getOffsetParent(element);
-        var parentOffset = getOffsetRectRelativeToViewport(offsetParent);
-        var offset = {
-            width: relativeOffset.offsetWidth,
-            height: relativeOffset.offsetHeight,
-            left: relativeOffset.left + parentOffset.left,
-            top: relativeOffset.top + parentOffset.top,
-            right: relativeOffset.right - parentOffset.right,
-            bottom: relativeOffset.bottom - parentOffset.bottom
-        };
-        return offset;
-    }
-
-    return relativeOffset;
-}
-
-function getTotalScroll(element) {
-    var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
-
-    var scrollParent = getScrollParent(element);
-    var scroll = getScroll(scrollParent, side);
-
-    if (['BODY', 'HTML'].indexOf(scrollParent.nodeName) === -1) {
-        return scroll + getTotalScroll(getParentNode(scrollParent), side);
-    }
-    return scroll;
-}
-
-/**
- * Computed the boundaries limits and return them
- * @method
- * @memberof Popper.Utils
- * @param {Object} data - Object containing the property "offsets" generated by `_getOffsets`
- * @param {Number} padding - Boundaries padding
- * @param {Element} boundariesElement - Element used to define the boundaries
- * @returns {Object} Coordinates of the boundaries
- */
-function getBoundaries(popper, padding, boundariesElement) {
-    // NOTE: 1 DOM access here
-    var boundaries = { top: 0, left: 0 };
-    var offsetParent = getOffsetParent(popper);
-
-    // Handle viewport case
-    if (boundariesElement === 'viewport') {
-        var _getOffsetRectRelativ = getOffsetRectRelativeToViewport(offsetParent),
-            left = _getOffsetRectRelativ.left,
-            top = _getOffsetRectRelativ.top;
-
-        var _window$document$docu = window.document.documentElement,
-            width = _window$document$docu.clientWidth,
-            height = _window$document$docu.clientHeight;
-
-
-        if (getPosition(popper) === 'fixed') {
-            boundaries.right = width;
-            boundaries.bottom = height;
-        } else {
-            var scrollLeft = getTotalScroll(popper, 'left');
-            var scrollTop = getTotalScroll(popper, 'top');
-
-            boundaries = {
-                top: 0 - top,
-                right: width - left + scrollLeft,
-                bottom: height - top + scrollTop,
-                left: 0 - left
-            };
-        }
-    }
-    // Handle other cases based on DOM element used as boundaries
-    else {
-            var boundariesNode = void 0;
-            if (boundariesElement === 'scrollParent') {
-                boundariesNode = getScrollParent(getParentNode(popper));
-            } else if (boundariesElement === 'window') {
-                boundariesNode = window.document.body;
-            } else {
-                boundariesNode = boundariesElement;
-            }
-
-            // In case of BODY, we need a different computation
-            if (boundariesNode.nodeName === 'BODY') {
-                var _getWindowSizes = getWindowSizes(),
-                    _height = _getWindowSizes.height,
-                    _width = _getWindowSizes.width;
-
-                boundaries.right = _width;
-                boundaries.bottom = _height;
-            }
-            // for all the other DOM elements, this one is good
-            else {
-                    boundaries = getOffsetRectRelativeToCustomParent(boundariesNode, offsetParent, isFixed(popper));
-                }
-        }
-
-    // Add paddings
-    boundaries.left += padding;
-    boundaries.top += padding;
-    boundaries.right -= padding;
-    boundaries.bottom -= padding;
-
-    return boundaries;
-}
-
-/**
- * Utility used to transform the `auto` placement to the placement with more
- * available space.
- * @method
- * @memberof Popper.Utils
- * @argument {Object} data - The data object generated by update method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function computeAutoPlacement(placement, refRect, popper) {
-    if (placement.indexOf('auto') === -1) {
-        return placement;
-    }
-
-    var boundaries = getBoundaries(popper, 0, 'scrollParent');
-
-    var sides = {
-        top: refRect.top - boundaries.top,
-        right: boundaries.right - refRect.right,
-        bottom: boundaries.bottom - refRect.bottom,
-        left: refRect.left - boundaries.left
-    };
-
-    var computedPlacement = Object.keys(sides).sort(function (a, b) {
-        return sides[b] - sides[a];
-    })[0];
-    var variation = placement.split('-')[1];
-
-    return computedPlacement + (variation ? '-' + variation : '');
-}
-
-var nativeHints = ['native code', '[object MutationObserverConstructor]' // for mobile safari iOS 9.0
-];
-
-/**
- * Determine if a function is implemented natively (as opposed to a polyfill).
- * @argument {Function | undefined} fn the function to check
- * @returns {boolean}
- */
-var isNative = (function (fn) {
-  return nativeHints.some(function (hint) {
-    return (fn || '').toString().indexOf(hint) > -1;
-  });
-});
-
-var isBrowser = typeof window !== 'undefined';
-var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
-var timeoutDuration = 0;
-for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
-    if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
-        timeoutDuration = 1;
-        break;
-    }
-}
-
-function microtaskDebounce(fn) {
-    var scheduled = false;
-    var i = 0;
-    var elem = document.createElement('span');
-
-    // MutationObserver provides a mechanism for scheduling microtasks, which
-    // are scheduled *before* the next task. This gives us a way to debounce
-    // a function but ensure it's called *before* the next paint.
-    var observer = new MutationObserver(function () {
-        fn();
-        scheduled = false;
-    });
-
-    observer.observe(elem, { attributes: true });
-
-    return function () {
-        if (!scheduled) {
-            scheduled = true;
-            elem.setAttribute('x-index', i);
-            i = i + 1; // don't use compund (+=) because it doesn't get optimized in V8
-        }
-    };
-}
-
-function taskDebounce(fn) {
-    var scheduled = false;
-    return function () {
-        if (!scheduled) {
-            scheduled = true;
-            setTimeout(function () {
-                scheduled = false;
-                fn();
-            }, timeoutDuration);
-        }
-    };
-}
-
-// It's common for MutationObserver polyfills to be seen in the wild, however
-// these rely on Mutation Events which only occur when an element is connected
-// to the DOM. The algorithm used in this module does not use a connected element,
-// and so we must ensure that a *native* MutationObserver is available.
-var supportsNativeMutationObserver = isBrowser && isNative(window.MutationObserver);
-
-/**
-* Create a debounced version of a method, that's asynchronously deferred
-* but called in the minimum time possible.
-*
-* @method
-* @memberof Popper.Utils
-* @argument {Function} fn
-* @returns {Function}
-*/
-var debounce = supportsNativeMutationObserver ? microtaskDebounce : taskDebounce;
-
-/**
- * Mimics the `find` method of Array
- * @method
- * @memberof Popper.Utils
- * @argument {Array} arr
- * @argument prop
- * @argument value
- * @returns index or -1
- */
-function find(arr, check) {
-    // use native find if supported
-    if (Array.prototype.find) {
-        return arr.find(check);
-    }
-
-    // use `filter` to obtain the same behavior of `find`
-    return arr.filter(check)[0];
-}
-
-/**
- * Return the index of the matching object
- * @method
- * @memberof Popper.Utils
- * @argument {Array} arr
- * @argument prop
- * @argument value
- * @returns index or -1
- */
-function findIndex(arr, prop, value) {
-    // use native findIndex if supported
-    if (Array.prototype.findIndex) {
-        return arr.findIndex(function (cur) {
-            return cur[prop] === value;
-        });
-    }
-
-    // use `find` + `indexOf` if `findIndex` isn't supported
-    var match = find(arr, function (obj) {
-        return obj[prop] === value;
-    });
-    return arr.indexOf(match);
-}
-
-var classCallCheck = function (instance, Constructor) {
-  if (!(instance instanceof Constructor)) {
-    throw new TypeError("Cannot call a class as a function");
-  }
-};
-
-var createClass = function () {
-  function defineProperties(target, props) {
-    for (var i = 0; i < props.length; i++) {
-      var descriptor = props[i];
-      descriptor.enumerable = descriptor.enumerable || false;
-      descriptor.configurable = true;
-      if ("value" in descriptor) descriptor.writable = true;
-      Object.defineProperty(target, descriptor.key, descriptor);
-    }
-  }
-
-  return function (Constructor, protoProps, staticProps) {
-    if (protoProps) defineProperties(Constructor.prototype, protoProps);
-    if (staticProps) defineProperties(Constructor, staticProps);
-    return Constructor;
-  };
-}();
-
-
-
-
-
-var defineProperty = function (obj, key, value) {
-  if (key in obj) {
-    Object.defineProperty(obj, key, {
-      value: value,
-      enumerable: true,
-      configurable: true,
-      writable: true
-    });
-  } else {
-    obj[key] = value;
-  }
-
-  return obj;
-};
-
-var _extends = Object.assign || function (target) {
-  for (var i = 1; i < arguments.length; i++) {
-    var source = arguments[i];
-
-    for (var key in source) {
-      if (Object.prototype.hasOwnProperty.call(source, key)) {
-        target[key] = source[key];
-      }
-    }
-  }
-
-  return target;
-};
-
-/**
- * Given the popper offsets, generate an output similar to getBoundingClientRect
- * @method
- * @memberof Popper.Utils
- * @argument {Object} popperOffsets
- * @returns {Object} ClientRect like output
- */
-function getClientRect(popperOffsets) {
-    return _extends({}, popperOffsets, {
-        right: popperOffsets.left + popperOffsets.width,
-        bottom: popperOffsets.top + popperOffsets.height
-    });
-}
-
-/**
- * Get the outer sizes of the given element (offset size + margins)
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element
- * @returns {Object} object containing width and height properties
- */
-function getOuterSizes(element) {
-    var styles = window.getComputedStyle(element);
-    var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom);
-    var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);
-    var result = {
-        width: element.offsetWidth + y,
-        height: element.offsetHeight + x
-    };
-    return result;
-}
-
-/**
- * Get the opposite placement of the given one/
- * @method
- * @memberof Popper.Utils
- * @argument {String} placement
- * @returns {String} flipped placement
- */
-function getOppositePlacement(placement) {
-  var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
-  return placement.replace(/left|right|bottom|top/g, function (matched) {
-    return hash[matched];
-  });
-}
-
-/**
- * Get offsets to the popper
- * @method
- * @memberof Popper.Utils
- * @param {Object} position - CSS position the Popper will get applied
- * @param {HTMLElement} popper - the popper element
- * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
- * @param {String} placement - one of the valid placement options
- * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
- */
-function getPopperOffsets(position, popper, referenceOffsets, placement) {
-    placement = placement.split('-')[0];
-
-    // Get popper node sizes
-    var popperRect = getOuterSizes(popper);
-
-    // Add position, width and height to our offsets object
-    var popperOffsets = {
-        position: position,
-        width: popperRect.width,
-        height: popperRect.height
-    };
-
-    // depending by the popper placement we have to compute its offsets slightly differently
-    var isHoriz = ['right', 'left'].indexOf(placement) !== -1;
-    var mainSide = isHoriz ? 'top' : 'left';
-    var secondarySide = isHoriz ? 'left' : 'top';
-    var measurement = isHoriz ? 'height' : 'width';
-    var secondaryMeasurement = !isHoriz ? 'height' : 'width';
-
-    popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
-    if (placement === secondarySide) {
-        popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
-    } else {
-        popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
-    }
-
-    return popperOffsets;
-}
-
-/**
- * Get offsets to the reference element
- * @method
- * @memberof Popper.Utils
- * @param {Object} state
- * @param {Element} popper - the popper element
- * @param {Element} reference - the reference element (the popper will be relative to this)
- * @returns {Object} An object containing the offsets which will be applied to the popper
- */
-function getReferenceOffsets(state, popper, reference) {
-  var isParentFixed = state.position === 'fixed';
-  var isParentTransformed = state.isParentTransformed;
-  var offsetParent = getOffsetParent(isParentFixed && isParentTransformed ? reference : popper);
-
-  return getOffsetRectRelativeToCustomParent(reference, offsetParent, isParentFixed, isParentTransformed);
-}
-
-/**
- * Get the prefixed supported property name
- * @method
- * @memberof Popper.Utils
- * @argument {String} property (camelCase)
- * @returns {String} prefixed property (camelCase)
- */
-function getSupportedPropertyName(property) {
-    var prefixes = [false, 'ms', 'webkit', 'moz', 'o'];
-    var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
-
-    for (var i = 0; i < prefixes.length - 1; i++) {
-        var prefix = prefixes[i];
-        var toCheck = prefix ? '' + prefix + upperProp : property;
-        if (typeof window.document.body.style[toCheck] !== 'undefined') {
-            return toCheck;
-        }
-    }
-    return null;
-}
-
-/**
- * Check if the given variable is a function
- * @method
- * @memberof Popper.Utils
- * @argument {*} functionToCheck - variable to check
- * @returns {Boolean} answer to: is a function?
- */
-function isFunction(functionToCheck) {
-  var getType = {};
-  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
-}
-
-/**
- * Helper used to know if the given modifier is enabled.
- * @method
- * @memberof Popper.Utils
- * @returns {Boolean}
- */
-function isModifierEnabled(modifiers, modifierName) {
-    return modifiers.some(function (_ref) {
-        var name = _ref.name,
-            enabled = _ref.enabled;
-        return enabled && name === modifierName;
-    });
-}
-
-/**
- * Helper used to know if the given modifier depends from another one.
- * It checks if the needed modifier is listed and enabled.
- * @method
- * @memberof Popper.Utils
- * @param {Array} modifiers - list of modifiers
- * @param {String} requestingName - name of requesting modifier
- * @param {String} requestedName - name of requested modifier
- * @returns {Boolean}
- */
-function isModifierRequired(modifiers, requestingName, requestedName) {
-    var requesting = find(modifiers, function (_ref) {
-        var name = _ref.name;
-        return name === requestingName;
-    });
-
-    return !!requesting && modifiers.some(function (modifier) {
-        return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
-    });
-}
-
-/**
- * Tells if a given input is a number
- * @method
- * @memberof Popper.Utils
- * @param {*} input to check
- * @return {Boolean}
- */
-function isNumeric(n) {
-  return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
-}
-
-/**
- * Check if the given element has transforms applied to itself or a parent
- * @method
- * @memberof Popper.Utils
- * @param  {Element} element
- * @return {Boolean} answer to "isTransformed?"
- */
-function isTransformed(element) {
-    if (element.nodeName === 'BODY') {
-        return false;
-    }
-    if (getStyleComputedProperty(element, 'transform') !== 'none') {
-        return true;
-    }
-    return getParentNode(element) ? isTransformed(getParentNode(element)) : element;
-}
-
-/**
- * Remove event listeners used to update the popper position
- * @method
- * @memberof Popper.Utils
- * @private
- */
-function removeEventListeners(reference, state) {
-    // Remove resize event listener on window
-    window.removeEventListener('resize', state.updateBound);
-
-    // Remove scroll event listener on scroll parents
-    state.scrollParents.forEach(function (target) {
-        target.removeEventListener('scroll', state.updateBound);
-    });
-
-    // Reset state
-    state.updateBound = null;
-    state.scrollParents = [];
-    state.scrollElement = null;
-    state.eventsEnabled = false;
-    return state;
-}
-
-/**
- * Loop trough the list of modifiers and run them in order, each of them will then edit the data object
- * @method
- * @memberof Popper.Utils
- * @param {Object} data
- * @param {Array} modifiers
- * @param {Function} ends
- */
-function runModifiers(modifiers, data, ends) {
-    var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
-
-    modifiersToRun.forEach(function (modifier) {
-        if (modifier.enabled && isFunction(modifier.function)) {
-            data = modifier.function(data, modifier);
-        }
-    });
-
-    return data;
-}
-
-/**
- * Set the attributes to the given popper
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element - Element to apply the attributes to
- * @argument {Object} styles - Object with a list of properties and values which will be applied to the element
- */
-function setAttributes(element, attributes) {
-    Object.keys(attributes).forEach(function (prop) {
-        var value = attributes[prop];
-        if (value !== false) {
-            element.setAttribute(prop, attributes[prop]);
-        } else {
-            element.removeAttribute(prop);
-        }
-    });
-}
-
-/**
- * Set the style to the given popper
- * @method
- * @memberof Popper.Utils
- * @argument {Element} element - Element to apply the style to
- * @argument {Object} styles - Object with a list of properties and values which will be applied to the element
- */
-function setStyles(element, styles) {
-    Object.keys(styles).forEach(function (prop) {
-        var unit = '';
-        // add unit if the value is numeric and is one of the following
-        if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
-            unit = 'px';
-        }
-        element.style[prop] = styles[prop] + unit;
-    });
-}
-
-function attachToScrollParents(scrollParent, event, callback, scrollParents) {
-    var isBody = scrollParent.nodeName === 'BODY';
-    var target = isBody ? window : scrollParent;
-    target.addEventListener(event, callback, { passive: true });
-
-    if (!isBody) {
-        attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
-    }
-    scrollParents.push(target);
-}
-
-/**
- * Setup needed event listeners used to update the popper position
- * @method
- * @memberof Popper.Utils
- * @private
- */
-function setupEventListeners(reference, options, state, updateBound) {
-    // Resize event listener on window
-    state.updateBound = updateBound;
-    window.addEventListener('resize', state.updateBound, { passive: true });
-
-    // Scroll event listener on scroll parents
-    var scrollElement = getScrollParent(reference);
-    attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
-    state.scrollElement = scrollElement;
-    state.eventsEnabled = true;
-
-    return state;
-}
-
-/** @namespace Popper.Utils */
-var Utils = {
-    computeAutoPlacement: computeAutoPlacement,
-    debounce: debounce,
-    findIndex: findIndex,
-    getBordersSize: getBordersSize,
-    getBoundaries: getBoundaries,
-    getBoundingClientRect: getBoundingClientRect,
-    getClientRect: getClientRect,
-    getOffsetParent: getOffsetParent,
-    getOffsetRect: getOffsetRect,
-    getOffsetRectRelativeToCustomParent: getOffsetRectRelativeToCustomParent,
-    getOuterSizes: getOuterSizes,
-    getParentNode: getParentNode,
-    getPopperOffsets: getPopperOffsets,
-    getPosition: getPosition,
-    getReferenceOffsets: getReferenceOffsets,
-    getScroll: getScroll,
-    getScrollParent: getScrollParent,
-    getStyleComputedProperty: getStyleComputedProperty,
-    getSupportedPropertyName: getSupportedPropertyName,
-    getTotalScroll: getTotalScroll,
-    getWindowSizes: getWindowSizes,
-    includeScroll: includeScroll,
-    isFixed: isFixed,
-    isFunction: isFunction,
-    isModifierEnabled: isModifierEnabled,
-    isModifierRequired: isModifierRequired,
-    isNative: isNative,
-    isNumeric: isNumeric,
-    isTransformed: isTransformed,
-    removeEventListeners: removeEventListeners,
-    runModifiers: runModifiers,
-    setAttributes: setAttributes,
-    setStyles: setStyles,
-    setupEventListeners: setupEventListeners
-};
-
-/**
- * Apply the computed styles to the popper element
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by `update` method
- * @argument {Object} data.styles - List of style properties - values to apply to popper element
- * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The same data object
- */
-function applyStyle(data, options) {
-    // apply the final offsets to the popper
-    // NOTE: 1 DOM access here
-    var styles = {
-        position: data.offsets.popper.position
-    };
-
-    var attributes = {
-        'x-placement': data.placement
-    };
-
-    // round top and left to avoid blurry text
-    var left = Math.round(data.offsets.popper.left);
-    var top = Math.round(data.offsets.popper.top);
-
-    // if gpuAcceleration is set to true and transform is supported,
-    //  we use `translate3d` to apply the position to the popper we
-    // automatically use the supported prefixed version if needed
-    var prefixedProperty = getSupportedPropertyName('transform');
-    if (options.gpuAcceleration && prefixedProperty) {
-        styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';
-        styles.top = 0;
-        styles.left = 0;
-        styles.willChange = 'transform';
-    }
-    // othwerise, we use the standard `left` and `top` properties
-    else {
-            styles.left = left;
-            styles.top = top;
-            styles.willChange = 'top, left';
-        }
-
-    // any property present in `data.styles` will be applied to the popper,
-    // in this way we can make the 3rd party modifiers add custom styles to it
-    // Be aware, modifiers could override the properties defined in the previous
-    // lines of this modifier!
-    setStyles(data.instance.popper, _extends({}, styles, data.styles));
-
-    // any property present in `data.attributes` will be applied to the popper,
-    // they will be set as HTML attributes of the element
-    setAttributes(data.instance.popper, _extends({}, attributes, data.attributes));
-
-    // if the arrow style has been computed, apply the arrow style
-    if (data.offsets.arrow) {
-        setStyles(data.arrowElement, data.offsets.arrow);
-    }
-
-    return data;
-}
-
-/**
- * Set the x-placement attribute before everything else because it could be used to add margins to the popper
- * margins needs to be calculated to get the correct popper offsets
- * @method
- * @memberof Popper.modifiers
- * @param {HTMLElement} reference - The reference element used to position the popper
- * @param {HTMLElement} popper - The HTML element used as popper.
- * @param {Object} options - Popper.js options
- */
-function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
-    // compute reference element offsets
-    var referenceOffsets = getReferenceOffsets(state, popper, reference);
-
-    // compute auto placement, store placement inside the data object,
-    // modifiers will be able to edit `placement` if needed
-    // and refer to originalPlacement to know the original value
-    options.placement = computeAutoPlacement(options.placement, referenceOffsets, popper);
-
-    popper.setAttribute('x-placement', options.placement);
-    return options;
-}
-
-/**
- * Modifier used to move the arrowElements on the edge of the popper to make sure them are always between the popper and the reference element
- * It will use the CSS outer size of the arrowElement element to know how many pixels of conjuction are needed
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by update method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function arrow(data, options) {
-    // arrow depends on keepTogether in order to work
-    if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
-        console.warn('WARNING: `keepTogether` modifier is required by arrow modifier in order to work, be sure to include it before `arrow`!');
-        return data;
-    }
-
-    var arrowElement = options.element;
-
-    // if arrowElement is a string, suppose it's a CSS selector
-    if (typeof arrowElement === 'string') {
-        arrowElement = data.instance.popper.querySelector(arrowElement);
-
-        // if arrowElement is not found, don't run the modifier
-        if (!arrowElement) {
-            return data;
-        }
-    } else {
-        // if the arrowElement isn't a query selector we must check that the
-        // provided DOM node is child of its popper node
-        if (!data.instance.popper.contains(arrowElement)) {
-            console.warn('WARNING: `arrow.element` must be child of its popper element!');
-            return data;
-        }
-    }
-
-    var placement = data.placement.split('-')[0];
-    var popper = getClientRect(data.offsets.popper);
-    var reference = data.offsets.reference;
-    var isVertical = ['left', 'right'].indexOf(placement) !== -1;
-
-    var len = isVertical ? 'height' : 'width';
-    var side = isVertical ? 'top' : 'left';
-    var altSide = isVertical ? 'left' : 'top';
-    var opSide = isVertical ? 'bottom' : 'right';
-    var arrowElementSize = getOuterSizes(arrowElement)[len];
-
-    //
-    // extends keepTogether behavior making sure the popper and its reference have enough pixels in conjuction
-    //
-
-    // top/left side
-    if (reference[opSide] - arrowElementSize < popper[side]) {
-        data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
-    }
-    // bottom/right side
-    if (reference[side] + arrowElementSize > popper[opSide]) {
-        data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
-    }
-
-    // compute center of the popper
-    var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
-
-    // Compute the sideValue using the updated popper offsets
-    var sideValue = center - getClientRect(data.offsets.popper)[side];
-
-    // prevent arrowElement from being placed not contiguously to its popper
-    sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
-
-    data.arrowElement = arrowElement;
-    data.offsets.arrow = {};
-    data.offsets.arrow[side] = sideValue;
-    data.offsets.arrow[altSide] = ''; // make sure to unset any eventual altSide value from the DOM node
-
-    return data;
-}
-
-/**
- * Get the opposite placement variation of the given one/
- * @method
- * @memberof Popper.Utils
- * @argument {String} placement variation
- * @returns {String} flipped placement variation
- */
-function getOppositeVariation(variation) {
-    if (variation === 'end') {
-        return 'start';
-    } else if (variation === 'start') {
-        return 'end';
-    }
-    return variation;
-}
-
-/**
- * Modifier used to flip the placement of the popper when the latter is starting overlapping its reference element.
- * Requires the `preventOverflow` modifier before it in order to work.
- * **NOTE:** data.instance modifier will run all its previous modifiers everytime it tries to flip the popper!
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by update method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function flip(data, options) {
-    // if `inner` modifier is enabled, we can't use the `flip` modifier
-    if (isModifierEnabled(data.instance.modifiers, 'inner')) {
-        return data;
-    }
-
-    if (data.flipped && data.placement === data.originalPlacement) {
-        // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
-        return data;
-    }
-
-    var boundaries = getBoundaries(data.instance.popper, options.padding, options.boundariesElement);
-
-    var placement = data.placement.split('-')[0];
-    var placementOpposite = getOppositePlacement(placement);
-    var variation = data.placement.split('-')[1] || '';
-
-    var flipOrder = [];
-
-    if (options.behavior === 'flip') {
-        flipOrder = [placement, placementOpposite];
-    } else {
-        flipOrder = options.behavior;
-    }
-
-    flipOrder.forEach(function (step, index) {
-        if (placement !== step || flipOrder.length === index + 1) {
-            return data;
-        }
-
-        placement = data.placement.split('-')[0];
-        placementOpposite = getOppositePlacement(placement);
-
-        var popperOffsets = getClientRect(data.offsets.popper);
-        var refOffsets = data.offsets.reference;
-
-        // using floor because the reference offsets may contain decimals we are not going to consider here
-        var floor = Math.floor;
-        var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);
-
-        var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
-        var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
-        var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
-        var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
-
-        var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
-
-        // flip the variation if required
-        var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
-        var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
-
-        if (overlapsRef || overflowsBoundaries || flippedVariation) {
-            // this boolean to detect any flip loop
-            data.flipped = true;
-
-            if (overlapsRef || overflowsBoundaries) {
-                placement = flipOrder[index + 1];
-            }
-
-            if (flippedVariation) {
-                variation = getOppositeVariation(variation);
-            }
-
-            data.placement = placement + (variation ? '-' + variation : '');
-            data.offsets.popper = getPopperOffsets(data.instance.state.position, data.instance.popper, data.offsets.reference, data.placement);
-
-            data = runModifiers(data.instance.modifiers, data, 'flip');
-        }
-    });
-    return data;
-}
-
-/**
- * Modifier used to make sure the popper is always near its reference element
- * It cares only about the first axis, you can still have poppers with margin
- * between the popper and its reference element.
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by update method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function keepTogether(data) {
-    var popper = getClientRect(data.offsets.popper);
-    var reference = data.offsets.reference;
-    var placement = data.placement.split('-')[0];
-    var floor = Math.floor;
-    var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
-    var side = isVertical ? 'right' : 'bottom';
-    var opSide = isVertical ? 'left' : 'top';
-    var measurement = isVertical ? 'width' : 'height';
-
-    if (popper[side] < floor(reference[opSide])) {
-        data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
-    }
-    if (popper[opSide] > floor(reference[side])) {
-        data.offsets.popper[opSide] = floor(reference[side]);
-    }
-
-    return data;
-}
-
-/**
- * Modifier used to add an offset to the popper, useful if you more granularity positioning your popper.
- * The offsets will shift the popper on the side of its reference element.
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by update method
- * @argument {Object} options - Modifiers configuration and options
- * @argument {Number|String} options.offset=0
- *      Basic usage allows a number used to nudge the popper by the given amount of pixels.
- *      You can pass a percentage value as string (eg. `20%`) to nudge by the given percentage (relative to reference element size)
- *      Other supported units are `vh` and `vw` (relative to viewport)
- *      Additionally, you can pass a pair of values (eg. `10 20` or `2vh 20%`) to nudge the popper
- *      on both axis.
- *      A note about percentage values, if you want to refer a percentage to the popper size instead of the reference element size,
- *      use `%p` instead of `%` (eg: `20%p`). To make it clearer, you can replace `%` with `%r` and use eg.`10%p 25%r`.
- *      > **Heads up!** The order of the axis is relative to the popper placement: `bottom` or `top` are `X,Y`, the other are `Y,X`
- * @returns {Object} The data object, properly modified
- */
-function offset(data, options) {
-    var placement = data.placement;
-    var popper = data.offsets.popper;
-
-    var offsets = void 0;
-    if (isNumeric(options.offset)) {
-        offsets = [options.offset, 0];
-    } else {
-        // split the offset in case we are providing a pair of offsets separated
-        // by a blank space
-        offsets = options.offset.split(' ');
-
-        // itherate through each offset to compute them in case they are percentages
-        offsets = offsets.map(function (offset, index) {
-            // separate value from unit
-            var split = offset.match(/(\d*\.?\d*)(.*)/);
-            var value = +split[1];
-            var unit = split[2];
-
-            // use height if placement is left or right and index is 0 otherwise use width
-            // in this way the first offset will use an axis and the second one
-            // will use the other one
-            var useHeight = placement.indexOf('right') !== -1 || placement.indexOf('left') !== -1;
-
-            if (index === 1) {
-                useHeight = !useHeight;
-            }
-
-            var measurement = useHeight ? 'height' : 'width';
-
-            // if is a percentage relative to the popper (%p), we calculate the value of it using
-            // as base the sizes of the popper
-            // if is a percentage (% or %r), we calculate the value of it using as base the
-            // sizes of the reference element
-            if (unit.indexOf('%') === 0) {
-                var element = void 0;
-                switch (unit) {
-                    case '%p':
-                        element = data.offsets.popper;
-                        break;
-                    case '%':
-                    case '$r':
-                    default:
-                        element = data.offsets.reference;
-                }
-
-                var rect = getClientRect(element);
-                var len = rect[measurement];
-                return len / 100 * value;
-            }
-            // if is a vh or vw, we calculate the size based on the viewport
-            else if (unit === 'vh' || unit === 'vw') {
-                    var size = void 0;
-                    if (unit === 'vh') {
-                        size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
-                    } else {
-                        size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
-                    }
-                    return size / 100 * value;
-                }
-                // if is an explicit pixel unit, we get rid of the unit and keep the value
-                else if (unit === 'px') {
-                        return +value;
-                    }
-                    // if is an implicit unit, it's px, and we return just the value
-                    else {
-                            return +offset;
-                        }
-        });
-    }
-
-    if (data.placement.indexOf('left') !== -1) {
-        popper.top += offsets[0];
-        popper.left -= offsets[1] || 0;
-    } else if (data.placement.indexOf('right') !== -1) {
-        popper.top += offsets[0];
-        popper.left += offsets[1] || 0;
-    } else if (data.placement.indexOf('top') !== -1) {
-        popper.left += offsets[0];
-        popper.top -= offsets[1] || 0;
-    } else if (data.placement.indexOf('bottom') !== -1) {
-        popper.left += offsets[0];
-        popper.top += offsets[1] || 0;
-    }
-    return data;
-}
-
-/**
- * Modifier used to prevent the popper from being positioned outside the boundary.
- *
- * An scenario exists where the reference itself is not within the boundaries. We can
- * say it has "escaped the boundaries" — or just "escaped". In this case we need to
- * decide whether the popper should either:
- *
- * - detach from the reference and remain "trapped" in the boundaries, or
- * - if it should be ignore the boundary and "escape with the reference"
- *
- * When `escapeWithReference` is `true`, and reference is completely outside the
- * boundaries, the popper will overflow (or completely leave) the boundaries in order
- * to remain attached to the edge of the reference.
- *
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by `update` method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function preventOverflow(data, options) {
-    var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
-    var boundaries = getBoundaries(data.instance.popper, options.padding, boundariesElement);
-    options.boundaries = boundaries;
-
-    var order = options.priority;
-    var popper = getClientRect(data.offsets.popper);
-
-    var check = {
-        primary: function primary(placement) {
-            var value = popper[placement];
-            if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
-                value = Math.max(popper[placement], boundaries[placement]);
-            }
-            return defineProperty({}, placement, value);
-        },
-        secondary: function secondary(placement) {
-            var mainSide = placement === 'right' ? 'left' : 'top';
-            var value = popper[mainSide];
-            if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
-                value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
-            }
-            return defineProperty({}, mainSide, value);
-        }
-    };
-
-    order.forEach(function (placement) {
-        var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
-        popper = _extends({}, popper, check[side](placement));
-    });
-
-    data.offsets.popper = popper;
-
-    return data;
-}
-
-/**
- * Modifier used to shift the popper on the start or end of its reference element side
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by `update` method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function shift(data) {
-    var placement = data.placement;
-    var basePlacement = placement.split('-')[0];
-    var shiftvariation = placement.split('-')[1];
-
-    // if shift shiftvariation is specified, run the modifier
-    if (shiftvariation) {
-        var reference = data.offsets.reference;
-        var popper = getClientRect(data.offsets.popper);
-        var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
-        var side = isVertical ? 'left' : 'top';
-        var measurement = isVertical ? 'width' : 'height';
-
-        var shiftOffsets = {
-            start: defineProperty({}, side, reference[side]),
-            end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])
-        };
-
-        data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);
-    }
-
-    return data;
-}
-
-/**
- * Modifier used to hide the popper when its reference element is outside of the
- * popper boundaries. It will set an x-hidden attribute which can be used to hide
- * the popper when its reference is out of boundaries.
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by update method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function hide(data) {
-    if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
-        console.warn('WARNING: preventOverflow modifier is required by hide modifier in order to work, be sure to include it before hide!');
-        return data;
-    }
-
-    var refRect = data.offsets.reference;
-    var bound = find(data.instance.modifiers, function (modifier) {
-        return modifier.name === 'preventOverflow';
-    }).boundaries;
-
-    if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
-        // Avoid unnecessary DOM access if visibility hasn't changed
-        if (data.hide === true) {
-            return data;
-        }
-
-        data.hide = true;
-        data.attributes['x-out-of-boundaries'] = '';
-    } else {
-        // Avoid unnecessary DOM access if visibility hasn't changed
-        if (data.hide === false) {
-            return data;
-        }
-
-        data.hide = false;
-        data.attributes['x-out-of-boundaries'] = false;
-    }
-
-    return data;
-}
-
-/**
- * Modifier used to make the popper flow toward the inner of the reference element.
- * By default, when this modifier is disabled, the popper will be placed outside
- * the reference element.
- * @method
- * @memberof Modifiers
- * @argument {Object} data - The data object generated by `update` method
- * @argument {Object} options - Modifiers configuration and options
- * @returns {Object} The data object, properly modified
- */
-function inner(data) {
-    var placement = data.placement;
-    var basePlacement = placement.split('-')[0];
-    var popper = getClientRect(data.offsets.popper);
-    var reference = getClientRect(data.offsets.reference);
-    var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
-
-    var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
-
-    popper[isHoriz ? 'left' : 'top'] = reference[placement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
-
-    data.placement = getOppositePlacement(placement);
-    data.offsets.popper = getClientRect(popper);
-
-    return data;
-}
-
-/**
- * Modifiers are plugins used to alter the behavior of your poppers.
- * Popper.js uses a set of 7 modifiers to provide all the basic functionalities
- * needed by the library.
- *
- * Each modifier is an object containing several properties listed below.
- * @namespace Modifiers
- * @param {Object} modifier - Modifier descriptor
- * @param {Integer} modifier.order
- *      The `order` property defines the execution order of the modifiers.
- *      The built-in modifiers have orders with a gap of 100 units in between,
- *      this allows you to inject additional modifiers between the existing ones
- *      without having to redefine the order of all of them.
- *      The modifiers are executed starting from the one with the lowest order.
- * @param {Boolean} modifier.enabled - When `true`, the modifier will be used.
- * @param {Modifiers~modifier} modifier.function - Modifier function.
- * @param {Modifiers~onLoad} modifier.onLoad - Function executed on popper initalization
- * @return {Object} data - Each modifier must return the modified `data` object.
- */
-var modifiers = {
-  shift: {
-    order: 100,
-    enabled: true,
-    function: shift
-  },
-  offset: {
-    order: 200,
-    enabled: true,
-    function: offset,
-    // nudges popper from its origin by the given amount of pixels (can be negative)
-    offset: 0
-  },
-  preventOverflow: {
-    order: 300,
-    enabled: true,
-    function: preventOverflow,
-    // popper will try to prevent overflow following these priorities
-    //  by default, then, it could overflow on the left and on top of the boundariesElement
-    priority: ['left', 'right', 'top', 'bottom'],
-    // amount of pixel used to define a minimum distance between the boundaries and the popper
-    // this makes sure the popper has always a little padding between the edges of its container
-    padding: 5,
-    boundariesElement: 'scrollParent'
-  },
-  keepTogether: {
-    order: 400,
-    enabled: true,
-    function: keepTogether
-  },
-  arrow: {
-    order: 500,
-    enabled: true,
-    function: arrow,
-    // selector or node used as arrow
-    element: '[x-arrow]'
-  },
-  flip: {
-    order: 600,
-    enabled: true,
-    function: flip,
-    // the behavior used to change the popper's placement
-    behavior: 'flip',
-    // the popper will flip if it hits the edges of the boundariesElement - padding
-    padding: 5,
-    boundariesElement: 'viewport'
-  },
-  inner: {
-    order: 700,
-    enabled: false,
-    function: inner
-  },
-  hide: {
-    order: 800,
-    enabled: true,
-    function: hide
-  },
-  applyStyle: {
-    order: 900,
-    enabled: true,
-    // if true, it uses the CSS 3d transformation to position the popper
-    gpuAcceleration: true,
-    function: applyStyle,
-    onLoad: applyStyleOnLoad
-  }
-};
-
-/**
- * Modifiers can edit the `data` object to change the beheavior of the popper.
- * This object contains all the informations used by Popper.js to compute the
- * popper position.
- * The modifier can edit the data as needed, and then `return` it as result.
- *
- * @callback Modifiers~modifier
- * @param {dataObject} data
- * @return {dataObject} modified data
- */
-
-/**
- * The `dataObject` is an object containing all the informations used by Popper.js
- * this object get passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
- * @name dataObject
- * @property {Object} data.instance The Popper.js instance
- * @property {String} data.placement Placement applied to popper
- * @property {String} data.originalPlacement Placement originally defined on init
- * @property {Boolean} data.flipped True if popper has been flipped by flip modifier
- * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper.
- * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
- * @property {Object} data.styles Any CSS property defined here will be applied to the popper, it expects the JavaScript nomenclature (eg. `marginBottom`)
- * @property {Object} data.boundaries Offsets of the popper boundaries
- * @property {Object} data.offsets The measurements of popper, reference and arrow elements.
- * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
- * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
- * @property {Object} data.offsets.arro] `top` and `left` offsets, only one of them will be different from 0
- */
-
-// Utils
-// Modifiers
-// default options
-var DEFAULTS = {
-    // placement of the popper
-    placement: 'bottom',
-
-    // whether events (resize, scroll) are initially enabled
-    eventsEnabled: true,
-
-    /**
-     * Callback called when the popper is created.
-     * By default, is set to no-op.
-     * Access Popper.js instance with `data.instance`.
-     * @callback createCallback
-     * @static
-     * @param {dataObject} data
-     */
-    onCreate: function onCreate() {},
-
-    /**
-     * Callback called when the popper is updated, this callback is not called
-     * on the initialization/creation of the popper, but only on subsequent
-     * updates.
-     * By default, is set to no-op.
-     * Access Popper.js instance with `data.instance`.
-     * @callback updateCallback
-     * @static
-     * @param {dataObject} data
-     */
-    onUpdate: function onUpdate() {},
-
-    // list of functions used to modify the offsets before they are applied to the popper
-    modifiers: modifiers
-};
-
-/**
- * Create a new Popper.js instance
- * @class Popper
- * @param {HTMLElement} reference - The reference element used to position the popper
- * @param {HTMLElement} popper - The HTML element used as popper.
- * @param {Object} options
- * @param {String} options.placement=bottom
- *      Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -end),
- *      left(-start, -end)`
- *
- * @param {Boolean} options.eventsEnabled=true
- *      Whether events (resize, scroll) are initially enabled
- * @param {Boolean} options.gpuAcceleration=true
- *      When this property is set to true, the popper position will be applied using CSS3 translate3d, allowing the
- *      browser to use the GPU to accelerate the rendering.
- *      If set to false, the popper will be placed using `top` and `left` properties, not using the GPU.
- *
- * @param {Boolean} options.removeOnDestroy=false
- *      Set to true if you want to automatically remove the popper when you call the `destroy` method.
- *
- * @param {Object} options.modifiers
- *      List of functions used to modify the data before they are applied to the popper (see source code for default values)
- *
- * @param {Object} options.modifiers.arrow - Arrow modifier configuration
- * @param {String|HTMLElement} options.modifiers.arrow.element='[x-arrow]'
- *      The DOM Node used as arrow for the popper, or a CSS selector used to get the DOM node. It must be child of
- *      its parent Popper. Popper.js will apply to the given element the style required to align the arrow with its
- *      reference element.
- *      By default, it will look for a child node of the popper with the `x-arrow` attribute.
- *
- * @param {Object} options.modifiers.offset - Offset modifier configuration
- * @param {Number} options.modifiers.offset.offset=0
- *      Amount of pixels the popper will be shifted (can be negative).
- *
- * @param {Object} options.modifiers.preventOverflow - PreventOverflow modifier configuration
- * @param {Array} [options.modifiers.preventOverflow.priority=['left', 'right', 'top', 'bottom']]
- *      Priority used when Popper.js tries to avoid overflows from the boundaries, they will be checked in order,
- *      this means that the last one will never overflow
- * @param {String|HTMLElement} options.modifiers.preventOverflow.boundariesElement='scrollParent'
- *      Boundaries used by the modifier, can be `scrollParent`, `window`, `viewport` or any DOM element.
- * @param {Number} options.modifiers.preventOverflow.padding=5
- *      Amount of pixel used to define a minimum distance between the boundaries and the popper
- *      this makes sure the popper has always a little padding between the edges of its container.
- *
- * @param {Object} options.modifiers.flip - Flip modifier configuration
- * @param {String|Array} options.modifiers.flip.behavior='flip'
- *      The behavior used by the `flip` modifier to change the placement of the popper when the latter is trying to
- *      overlap its reference element. Defining `flip` as value, the placement will be flipped on
- *      its axis (`right - left`, `top - bottom`).
- *      You can even pass an array of placements (eg: `['right', 'left', 'top']` ) to manually specify
- *      how alter the placement when a flip is needed. (eg. in the above example, it would first flip from right to left,
- *      then, if even in its new placement, the popper is overlapping its reference element, it will be moved to top)
- * @param {String|HTMLElement} options.modifiers.flip.boundariesElement='viewport'
- *      The element which will define the boundaries of the popper position, the popper will never be placed outside
- *      of the defined boundaries (except if `keepTogether` is enabled)
- *
- * @param {Object} options.modifiers.inner - Inner modifier configuration
- * @param {Number} options.modifiers.inner.enabled=false
- *      Set to `true` to make the popper flow toward the inner of the reference element.
- *
- * @param {Number} options.modifiers.flip.padding=5
- *      Amount of pixel used to define a minimum distance between the boundaries and the popper
- *      this makes sure the popper has always a little padding between the edges of its container.
- *
- * @param {createCallback} options.onCreate - onCreate callback
- *      Function called after the Popper has been instantiated.
- *
- * @param {updateCallback} options.onUpdate - onUpdate callback
- *      Function called on subsequent updates of Popper.
- *
- * @return {Object} instance - The generated Popper.js instance
- */
-
-var Popper = function () {
-    function Popper(reference, popper) {
-        var _this = this;
-
-        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
-        classCallCheck(this, Popper);
-
-        this.scheduleUpdate = function () {
-            return requestAnimationFrame(_this.update);
-        };
-
-        // make update() debounced, so that it only runs at most once-per-tick
-        this.update = debounce(this.update.bind(this));
-
-        // with {} we create a new object with the options inside it
-        this.options = _extends({}, Popper.Defaults, options);
-
-        // init state
-        this.state = {
-            isDestroyed: false,
-            isCreated: false,
-            scrollParents: []
-        };
-
-        // get reference and popper elements (allow jQuery wrappers)
-        this.reference = reference.jquery ? reference[0] : reference;
-        this.popper = popper.jquery ? popper[0] : popper;
-
-        // refactoring modifiers' list (Object => Array)
-        this.modifiers = Object.keys(Popper.Defaults.modifiers).map(function (name) {
-            return _extends({ name: name }, Popper.Defaults.modifiers[name]);
-        });
-
-        // assign default values to modifiers, making sure to override them with
-        // the ones defined by user
-        this.modifiers = this.modifiers.map(function (defaultConfig) {
-            var userConfig = options.modifiers && options.modifiers[defaultConfig.name] || {};
-            return _extends({}, defaultConfig, userConfig);
-        });
-
-        // add custom modifiers to the modifiers list
-        if (options.modifiers) {
-            this.options.modifiers = _extends({}, Popper.Defaults.modifiers, options.modifiers);
-            Object.keys(options.modifiers).forEach(function (name) {
-                // take in account only custom modifiers
-                if (Popper.Defaults.modifiers[name] === undefined) {
-                    var modifier = options.modifiers[name];
-                    modifier.name = name;
-                    _this.modifiers.push(modifier);
-                }
-            });
-        }
-
-        // get the popper position type
-        this.state.position = getPosition(this.reference);
-
-        // sort the modifiers by order
-        this.modifiers = this.modifiers.sort(function (a, b) {
-            return a.order - b.order;
-        });
-
-        // modifiers have the ability to execute arbitrary code when Popper.js get inited
-        // such code is executed in the same order of its modifier
-        // they could add new properties to their options configuration
-        // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
-        this.modifiers.forEach(function (modifierOptions) {
-            if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
-                modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
-            }
-        });
-
-        // determine how we should set the origin of offsets
-        this.state.isParentTransformed = isTransformed(this.popper.parentNode);
-
-        // fire the first update to position the popper in the right place
-        this.update();
-
-        var eventsEnabled = this.options.eventsEnabled;
-        if (eventsEnabled) {
-            // setup event listeners, they will take care of update the position in specific situations
-            this.enableEventListeners();
-        }
-
-        this.state.eventsEnabled = eventsEnabled;
-    }
-
-    //
-    // Methods
-    //
-
-    /**
-     * Updates the position of the popper, computing the new offsets and applying the new style
-     * Prefer `scheduleUpdate` over `update` because of performance reasons
-     * @method
-     * @memberof Popper
-     */
-
-
-    createClass(Popper, [{
-        key: 'update',
-        value: function update() {
-            // if popper is destroyed, don't perform any further update
-            if (this.state.isDestroyed) {
-                return;
-            }
-
-            var data = {
-                instance: this,
-                styles: {},
-                attributes: {},
-                flipped: false,
-                offsets: {}
-            };
-
-            // make sure to apply the popper position before any computation
-            this.state.position = getPosition(this.reference);
-            setStyles(this.popper, { position: this.state.position });
-
-            // compute reference element offsets
-            data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference);
-
-            // compute auto placement, store placement inside the data object,
-            // modifiers will be able to edit `placement` if needed
-            // and refer to originalPlacement to know the original value
-            data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper);
-
-            // store the computed placement inside `originalPlacement`
-            data.originalPlacement = this.options.placement;
-
-            // compute the popper offsets
-            data.offsets.popper = getPopperOffsets(this.state, this.popper, data.offsets.reference, data.placement);
-
-            // run the modifiers
-            data = runModifiers(this.modifiers, data);
-
-            // the first `update` will call `onCreate` callback
-            // the other ones will call `onUpdate` callback
-            if (!this.state.isCreated) {
-                this.state.isCreated = true;
-                this.options.onCreate(data);
-            } else {
-                this.options.onUpdate(data);
-            }
-        }
-
-        /**
-         * Schedule an update, it will run on the next UI update available
-         * @method scheduleUpdate
-         * @memberof Popper
-         */
-
-    }, {
-        key: 'destroy',
-
-
-        /**
-         * Destroy the popper
-         * @method
-         * @memberof Popper
-         */
-        value: function destroy() {
-            this.state.isDestroyed = true;
-
-            // touch DOM only if `applyStyle` modifier is enabled
-            if (isModifierEnabled(this.modifiers, 'applyStyle')) {
-                this.popper.removeAttribute('x-placement');
-                this.popper.style.left = '';
-                this.popper.style.position = '';
-                this.popper.style.top = '';
-                this.popper.style[getSupportedPropertyName('transform')] = '';
-            }
-
-            this.disableEventListeners();
-
-            // remove the popper if user explicity asked for the deletion on destroy
-            // do not use `remove` because IE11 doesn't support it
-            if (this.options.removeOnDestroy) {
-                this.popper.parentNode.removeChild(this.popper);
-            }
-            return this;
-        }
-
-        /**
-         * it will add resize/scroll events and start recalculating
-         * position of the popper element when they are triggered
-         * @method
-         * @memberof Popper
-         */
-
-    }, {
-        key: 'enableEventListeners',
-        value: function enableEventListeners() {
-            if (!this.state.eventsEnabled) {
-                this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
-            }
-        }
-
-        /**
-         * it will remove resize/scroll events and won't recalculate
-         * popper position when they are triggered. It also won't trigger onUpdate callback anymore,
-         * unless you call 'update' method manually.
-         * @method
-         * @memberof Popper
-         */
-
-    }, {
-        key: 'disableEventListeners',
-        value: function disableEventListeners() {
-            if (this.state.eventsEnabled) {
-                window.cancelAnimationFrame(this.scheduleUpdate);
-                this.state = removeEventListeners(this.reference, this.state);
-            }
-        }
-
-        /**
-         * Collection of utilities useful when writing custom modifiers
-         * @memberof Popper
-         */
-
-
-        /**
-         * List of accepted placements to use as values of the `placement` option
-         * @memberof Popper
-         */
-
-
-        /**
-         * Default Popper.js options
-         * @memberof Popper
-         */
-
-    }]);
-    return Popper;
-}();
-
-Popper.Utils = Utils;
-Popper.placements = ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'right', 'right-start', 'right-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end'];
-Popper.Defaults = DEFAULTS;
-
-return Popper;
-
-})));
-//# sourceMappingURL=popper.es5.js.map
index 61e6cf4..a8b963b 100644 (file)
@@ -2,7 +2,7 @@
 (function (root, factory) {
   if (typeof define === 'function' && define.amd) {
     // AMD. Register as an anonymous module unless amdModuleId is set
-    define(["jquery","./popper"], function (a0,b1) {
+    define(["jquery","core/popper"], function (a0,b1) {
       return (root['Tour'] = factory(a0,b1));
     });
   } else if (typeof module === 'object' && module.exports) {
index b481d2b..15f142c 100644 (file)
@@ -819,7 +819,7 @@ class manager {
         $existingtourrecords->close();
 
         foreach ($shippedtours as $filename => $version) {
-            $filepath = $CFG->dirroot . '/admin/tool/usertours/tours/' . $filename;
+            $filepath = $CFG->dirroot . "/{$CFG->admin}/tool/usertours/tours/" . $filename;
             $tourjson = file_get_contents($filepath);
             $tour = self::import_tour_from_json($tourjson);
 
index 6049e8f..4819ac0 100644 (file)
@@ -483,10 +483,9 @@ class step {
             $record = $this->to_record();
             unset($record->id);
             $this->id = $DB->insert_record('tool_usertours_steps', $record);
+            $this->get_tour()->reset_step_sortorder();
         }
 
-        $this->get_tour()->reset_step_sortorder();
-
         $this->reload();
 
         // Notify of a change to the step configuration.
index 11359bc..c6d1186 100644 (file)
@@ -7,11 +7,4 @@
     <version>0.12.2</version>
     <licenseversion>3</licenseversion>
   </library>
-  <library>
-    <location>amd/src/popper.js</location>
-    <name>Popper.js</name>
-    <license>MIT</license>
-    <version>v1.0.8</version>
-    <licenseversion></licenseversion>
-  </library>
 </libraries>
diff --git a/admin/tool/usertours/upgrade.txt b/admin/tool/usertours/upgrade.txt
new file mode 100644 (file)
index 0000000..c123bd4
--- /dev/null
@@ -0,0 +1,4 @@
+This files describes API changes in the tool_usertours code.
+
+=== 3.5 ===
+* Third party library Popper.js was moved from this plugin into core (core/popper)
index ac0f5f9..0caf98f 100644 (file)
 
     // Carry on with the user listing
     $context = context_system::instance();
-    $extracolumns = get_extra_user_fields($context);
+    // These columns are always shown in the users list.
+    $requiredcolumns = array('city', 'country', 'lastaccess');
+    // Extra columns containing the extra user fields, excluding the required columns (city and country, to be specific).
+    $extracolumns = get_extra_user_fields($context, $requiredcolumns);
     // Get all user name fields as an array.
     $allusernamefields = get_all_user_name_fields(false, null, null, null, true);
-    $columns = array_merge($allusernamefields, $extracolumns, array('city', 'country', 'lastaccess'));
+    $columns = array_merge($allusernamefields, $extracolumns, $requiredcolumns);
 
     foreach ($columns as $column) {
         $string[$column] = get_user_field_name($column);
 
     } else {
 
-        $countries = get_string_manager()->get_list_of_countries(false);
+        $countries = get_string_manager()->get_list_of_countries(true);
         if (empty($mnethosts)) {
             $mnethosts = $DB->get_records('mnet_host', null, 'id', 'id,wwwroot,name');
         }
index 3e84120..638b18b 100644 (file)
@@ -205,10 +205,10 @@ abstract class base extends \core_analytics\calculable {
                 $message->contexturlname = $message->subject;
                 $message->courseid = $coursecontext->instanceid;
 
-                $message->fullmessage = get_string('insightinfomessage', 'analytics', $insighturl->out());
+                $message->fullmessage = get_string('insightinfomessage', 'analytics', $insighturl->out(false));
                 $message->fullmessageformat = FORMAT_PLAIN;
                 $message->fullmessagehtml = get_string('insightinfomessagehtml', 'analytics', $insighturl->out());
-                $message->smallmessage = get_string('insightinfomessage', 'analytics', $insighturl->out());
+                $message->smallmessage = get_string('insightinfomessage', 'analytics', $insighturl->out(false));
                 $message->contexturl = $insighturl->out(false);
 
                 message_send($message);
index 0383c08..00cfea3 100644 (file)
@@ -36,7 +36,7 @@ class core_analytics_course_testcase extends advanced_testcase {
     public function setUp() {
         global $DB;
 
-        $this->course = $this->getDataGenerator()->create_course();
+        $this->course = $this->getDataGenerator()->create_course(['startdate' => 0]);
         $this->stu1 = $this->getDataGenerator()->create_user();
         $this->stu2 = $this->getDataGenerator()->create_user();
         $this->both = $this->getDataGenerator()->create_user();
diff --git a/auth/cas/classes/privacy/provider.php b/auth/cas/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..f7d6f53
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_cas.
+ *
+ * @package    auth_cas
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_cas\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_cas implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index 7a2846c..a7c3662 100644 (file)
@@ -74,3 +74,4 @@ $string['CASform'] = 'Authentication choice';
 $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.';
index e3e7e59..3f7e3af 100644 (file)
@@ -71,7 +71,7 @@ class digital_consent {
     public static function parse_age_digital_consent_map($ageconsentmap) {
 
         $ageconsentmapparsed = array();
-        $countries = get_string_manager()->get_list_of_countries();
+        $countries = get_string_manager()->get_list_of_countries(true);
         $isdefaultvaluepresent = false;
         $lines = preg_split('/\r|\n/', $ageconsentmap, -1, PREG_SPLIT_NO_EMPTY);
         foreach ($lines as $line) {
diff --git a/auth/db/classes/privacy/provider.php b/auth/db/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..c4008d1
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_db.
+ *
+ * @package    auth_db
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_db\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_db implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index a9af49f..216fd31 100644 (file)
@@ -69,3 +69,4 @@ $string['auth_dbuser_key'] = 'DB user';
 $string['auth_dbuserstoadd'] = 'User entries to add: {$a}';
 $string['auth_dbuserstoremove'] = 'User entries to remove: {$a}';
 $string['pluginname'] = 'External database';
+$string['privacy:metadata'] = 'The External database authentication plugin does not store any personal data.';
index 2900719..fd6a62c 100644 (file)
@@ -97,11 +97,7 @@ class auth_db_testcase extends advanced_testcase {
                 break;
 
             case 'mssql':
-                if (get_class($DB) == 'mssql_native_moodle_database') {
-                    set_config('type', 'mssql_n', 'auth_db');
-                } else {
-                    set_config('type', 'mssqlnative', 'auth_db');
-                }
+                set_config('type', 'mssqlnative', 'auth_db');
                 set_config('sybasequoting', '1', 'auth_db');
                 break;
 
index f1cbc11..f207f03 100644 (file)
@@ -113,7 +113,7 @@ class auth_plugin_email extends auth_plugin_base {
      * @since Moodle 3.2
      */
     public function user_signup_with_confirmation($user, $notify=true, $confirmationurl = null) {
-        global $CFG, $DB;
+        global $CFG, $DB, $SESSION;
         require_once($CFG->dirroot.'/user/profile/lib.php');
         require_once($CFG->dirroot.'/user/lib.php');
 
@@ -130,6 +130,11 @@ class auth_plugin_email extends auth_plugin_base {
         // Save any custom profile field information.
         profile_save_data($user);
 
+        // Save wantsurl against user's profile, so we can return them there upon confirmation.
+        if (!empty($SESSION->wantsurl)) {
+            set_user_preference('auth_email_wantsurl', $SESSION->wantsurl, $user);
+        }
+
         // Trigger event.
         \core\event\user_created::create_from_userid($user->id)->trigger();
 
@@ -166,7 +171,7 @@ class auth_plugin_email extends auth_plugin_base {
      * @param string $confirmsecret
      */
     function user_confirm($username, $confirmsecret) {
-        global $DB;
+        global $DB, $SESSION;
         $user = get_complete_user_data('username', $username);
 
         if (!empty($user)) {
@@ -178,6 +183,13 @@ class auth_plugin_email extends auth_plugin_base {
 
             } else if ($user->secret == $confirmsecret) {   // They have provided the secret key to get in
                 $DB->set_field("user", "confirmed", 1, array("id"=>$user->id));
+
+                if ($wantsurl = get_user_preferences('auth_email_wantsurl', false, $user)) {
+                    // Ensure user gets returned to page they were trying to access before signing up.
+                    $SESSION->wantsurl = $wantsurl;
+                    unset_user_preference('auth_email_wantsurl', $user);
+                }
+
                 return AUTH_CONFIRM_OK;
             }
         } else {
diff --git a/auth/email/classes/privacy/provider.php b/auth/email/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..8b410d3
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_email.
+ *
+ * @package    auth_email
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_email\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_email implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index 1170c8e..596b4f7 100644 (file)
@@ -28,3 +28,4 @@ $string['auth_emailrecaptcha'] = 'Adds a visual/audio confirmation form element
 $string['auth_emailrecaptcha_key'] = 'Enable reCAPTCHA element';
 $string['auth_emailsettings'] = 'Settings';
 $string['pluginname'] = 'Email-based self-registration';
+$string['privacy:metadata'] = 'The Email-based self-registration authentication plugin does not store any personal data.';
diff --git a/auth/ldap/classes/privacy/provider.php b/auth/ldap/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..3e09a16
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_ldap.
+ *
+ * @package    auth_ldap
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_ldap\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_ldap implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index 219996e..237423e 100644 (file)
@@ -165,3 +165,4 @@ $string['useracctctrlerror'] = 'Error getting userAccountControl for {$a}';
 // Deprecated since Moodle 3.4.
 $string['auth_ldap_creators'] = 'List of groups or contexts whose members are allowed to create new courses. Separate multiple groups with \';\'. Usually something like \'cn=teachers,ou=staff,o=myorg\'';
 $string['auth_ldap_creators_key'] = 'Creators';
+$string['privacy:metadata'] = 'The LDAP server authentication plugin does not store any personal data.';
diff --git a/auth/lti/classes/privacy/provider.php b/auth/lti/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..b7d26de
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_lti.
+ *
+ * @package    auth_lti
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_lti\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_lti implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index b7c00ff..5c2db7b 100644 (file)
@@ -24,3 +24,4 @@
 
 $string['auth_ltidescription'] = 'The LTI authentication plugin, together with the \'Publish as LTI tool\' enrolment plugin, allows remote users to access selected courses and activities. In other words, Moodle functions as an LTI tool provider.';
 $string['pluginname'] = 'LTI';
+$string['privacy:metadata'] = 'The LTI authentication plugin does not store any personal data.';
diff --git a/auth/mnet/classes/privacy/provider.php b/auth/mnet/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..3c79a2d
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_mnet.
+ *
+ * @package    auth_mnet
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_mnet\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_mnet implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index 7084050..6fa6ff1 100644 (file)
@@ -35,3 +35,4 @@ $string['sso_mnet_login_refused'] = 'Username {$a->user} is not permitted to log
 $string['sso_sp_description'] = 'Publish  this service to allow authenticated users from {$a} to access your site without having to re-login. <ul><li><em>Dependency</em>: You must also <strong>subscribe</strong> to the SSO (Identity Provider) service on {$a}.</li></ul><br />Subscribe to this service to allow your users to roam to the {$a} site without having to re-login there. <ul><li><em>Dependency</em>: You must also <strong>publish</strong> the SSO (Identity Provider) service to {$a}.</li></ul><br />';
 $string['sso_sp_name'] = 'SSO (Service Provider)';
 $string['pluginname'] = 'MNet authentication';
+$string['privacy:metadata'] = 'The MNet authentication plugin does not store any personal data.';
diff --git a/auth/nologin/classes/privacy/provider.php b/auth/nologin/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..d354e2f
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for auth_nologin.
+ *
+ * @package    auth_nologin
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace auth_nologin\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for auth_nologin implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index edb4c79..3feac85 100644 (file)
@@ -24,3 +24,4 @@
 
 $string['auth_nologindescription'] = 'Auxiliary plugin that prevents user to login into system and also discards any mail sent to the user. Can be used to <em>suspend</em> user accounts.';
 $string['pluginname'] = 'No login';
+$string['privacy:metadata'] = 'The No login authentication plugin does not store any personal data.';
diff --git a/auth/none/classes/privacy/provider.php b/auth/none/classes/privacy/provider.php