Merge branch 'MDL-61950-master' of git://github.com/ryanwyllie/moodle
authorJun Pataleta <jun@moodle.com>
Wed, 18 Apr 2018 01:46:57 +0000 (09:46 +0800)
committerJun Pataleta <jun@moodle.com>
Wed, 18 Apr 2018 01:46:57 +0000 (09:46 +0800)
764 files changed:
.eslintignore
.stylelintignore
admin/cli/install.php
admin/cli/upgrade.php
admin/settings/appearance.php
admin/settings/security.php
admin/settings/users.php
admin/tool/log/store/database/classes/helper.php
admin/tool/lp/templates/manage_competencies_page.mustache
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/monitor/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/monitor/classes/rule.php
admin/tool/monitor/lang/en/tool_monitor.php
admin/tool/monitor/tests/privacy_test.php [new file with mode: 0644]
admin/tool/profiling/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/profiling/lang/en/tool_profiling.php
admin/tool/recyclebin/classes/category_bin.php
admin/tool/recyclebin/classes/course_bin.php
admin/tool/uploadcourse/classes/course.php
admin/tool/uploadcourse/classes/helper.php
admin/tool/uploadcourse/classes/processor.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/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]
auth/cas/classes/privacy/provider.php [new file with mode: 0644]
auth/cas/lang/en/auth_cas.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/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/controller/restore_controller.class.php
backup/controller/tests/controller_test.php
backup/converter/convertlib.php
backup/converter/imscc11/backuplib.php
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/moodle1_converter_test.php
backup/import.php
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/moodle2/tests/moodle2_test.php
backup/restorefile.php
backup/util/factories/backup_factory.class.php
backup/util/helper/backup_file_manager.class.php
backup/util/helper/backup_general_helper.class.php
backup/util/helper/backup_helper.class.php
backup/util/helper/convert_helper.class.php
backup/util/plan/backup_plan.class.php
backup/util/plan/restore_plan.class.php
backup/util/ui/base_moodleform.class.php
backup/util/ui/renderer.php
backup/util/ui/restore_ui_stage.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/communitycourse.php
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/myoverview/templates/course-event-list-item.mustache
blocks/myoverview/templates/course-summary.mustache
blocks/myoverview/templates/courses-view-course-item.mustache
blocks/myoverview/templates/courses-view.mustache
blocks/myoverview/templates/event-list-item.mustache
blocks/myoverview/templates/timeline-view.mustache
blocks/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]
blocks/tests/behat/behat_blocks.php
calendar/templates/month_detailed.mustache
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
config-dist.php
course/classes/management_renderer.php
course/classes/output/modchooser_item.php
course/externallib.php
course/format/lib.php
course/format/singleactivity/classes/privacy/provider.php [new file with mode: 0644]
course/format/singleactivity/lang/en/format_singleactivity.php
course/format/singleactivity/lib.php
course/format/social/classes/privacy/provider.php [new file with mode: 0644]
course/format/social/lang/en/format_social.php
course/format/social/lib.php
course/format/topics/classes/privacy/provider.php [new file with mode: 0644]
course/format/topics/lang/en/format_topics.php
course/format/topics/lib.php
course/format/upgrade.txt
course/format/weeks/classes/privacy/provider.php [new file with mode: 0644]
course/format/weeks/lang/en/format_weeks.php
course/format/weeks/lib.php
course/tests/behat/category_change_visibility.feature
course/tests/behat/category_management.feature
course/tests/behat/course_category_management_listing.feature
course/tests/behat/course_change_visibility.feature
course/tests/behat/course_search.feature
course/tests/behat/navigate_course_list.feature
course/tests/externallib_test.php
course/tests/restore_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/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/ipn.php
enrol/paypal/lang/en/enrol_paypal.php
enrol/paypal/lib.php
enrol/paypal/version.php
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/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
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/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
install.php
install/lang/de_ch/langconfig.php
install/lang/id/moodle.php
lang/en/admin.php
lang/en/antivirus.php
lang/en/badges.php
lang/en/cohort.php
lang/en/deprecated.txt
lang/en/editor.php
lang/en/install.php
lang/en/message.php
lang/en/moodle.php
lang/en/portfolio.php
lang/en/tag.php
lib/amd/build/adapter.min.js [new file with mode: 0644]
lib/amd/build/popper.min.js [new file with mode: 0644]
lib/amd/src/adapter.js [new file with mode: 0644]
lib/amd/src/popper.js [new file with mode: 0644]
lib/antivirus/clamav/classes/scanner.php
lib/antivirus/clamav/tests/scanner_test.php
lib/badgeslib.php
lib/behat/classes/partial_named_selector.php
lib/classes/antivirus/manager.php
lib/classes/antivirus/scanner.php
lib/classes/component.php
lib/classes/hub/publication.php
lib/classes/output/icon_system_fontawesome.php
lib/classes/plugin_manager.php
lib/classes/task/file_temp_cleanup_task.php
lib/db/events.php
lib/db/install.xml
lib/db/upgrade.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/sqlsrv_native_moodle_temptables.php
lib/dmllib.php
lib/editor/atto/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/atto/db/upgrade.php
lib/editor/atto/lang/en/editor_atto.php
lib/editor/atto/plugins/recordrtc/classes/privacy/provider.php [moved from theme/boost/classes/output/core/admin_renderer.php with 60% similarity]
lib/editor/atto/plugins/recordrtc/lang/en/atto_recordrtc.php [new file with mode: 0644]
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: 0644]
lib/editor/atto/plugins/recordrtc/yui/src/button/js/button.js [new file with mode: 0644]
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/settings.php
lib/editor/atto/tests/privacy_provider.php [new file with mode: 0644]
lib/editor/atto/version.php
lib/editor/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tests/privacy_provider_test.php [new file with mode: 0644]
lib/editor/textarea/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/textarea/lang/en/editor_textarea.php
lib/editor/tinymce/classes/privacy/provider.php [new file with mode: 0644]
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/externallib.php
lib/moodlelib.php
lib/myprofilelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/pagelib.php
lib/setup.php
lib/setuplib.php
lib/tablelib.php
lib/testing/classes/util.php
lib/tests/antivirus_test.php
lib/tests/behat/alpha_chooser.feature
lib/tests/component_test.php
lib/tests/cronlib_test.php
lib/tests/fixtures/testable_antivirus.php
lib/tests/moodle_page_test.php
lib/tests/scheduled_task_test.php
lib/tests/weblib_format_text_test.php
lib/thirdpartylibs.xml
lib/upgradelib.php
lib/weblib.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/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/helper.php
message/classes/task/migrate_message_data.php [new file with mode: 0644]
message/index.php
message/output/airnotifier/classes/privacy/provider.php
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/popup/classes/api.php
message/output/popup/db/install.xml
message/output/popup/db/upgrade.php
message/output/popup/message_output_popup.php
message/output/popup/notifications.php
message/output/popup/tests/base.php
message/output/popup/version.php
message/tests/migrate_message_data_task_test.php [new file with mode: 0644]
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/assignment/type/offline/classes/privacy/provider.php [new file with mode: 0644]
mod/assignment/type/offline/lang/en/assignment_offline.php
mod/assignment/type/online/classes/privacy/provider.php [new file with mode: 0644]
mod/assignment/type/online/lang/en/assignment_online.php
mod/assignment/type/upload/classes/privacy/provider.php [new file with mode: 0644]
mod/assignment/type/upload/lang/en/assignment_upload.php
mod/assignment/type/uploadsingle/classes/privacy/provider.php [new file with mode: 0644]
mod/assignment/type/uploadsingle/lang/en/assignment_uploadsingle.php
mod/book/tool/exportimscp/classes/privacy/provider.php [new file with mode: 0644]
mod/book/tool/exportimscp/lang/en/booktool_exportimscp.php
mod/book/tool/importhtml/classes/privacy/provider.php [new file with mode: 0644]
mod/book/tool/importhtml/lang/en/booktool_importhtml.php
mod/book/tool/print/classes/privacy/provider.php [new file with mode: 0644]
mod/book/tool/print/lang/en/booktool_print.php
mod/choice/classes/privacy/provider.php
mod/feedback/classes/privacy/provider.php [new file with mode: 0644]
mod/feedback/lang/en/feedback.php
mod/feedback/tests/privacy_test.php [new file with mode: 0644]
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/lesson/renderer.php
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/resources/contextmemberships.php
mod/lti/service/memberships/classes/local/resources/linkmemberships.php
mod/lti/service/memberships/classes/local/service/memberships.php
mod/lti/service/memberships/lang/en/ltiservice_memberships.php
mod/lti/service/profile/classes/local/resources/profile.php
mod/lti/service/toolsettings/classes/local/resources/contextsettings.php
mod/lti/service/toolsettings/classes/local/resources/linksettings.php
mod/lti/service/toolsettings/classes/local/resources/systemsettings.php
mod/quiz/attempt.php
mod/quiz/attemptlib.php
mod/quiz/autosave.ajax.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/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/tags_test.php
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/install.xml
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/upgrade.txt
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]
portfolio/boxnet/classes/privacy/provider.php [new file with mode: 0644]
portfolio/boxnet/lang/en/portfolio_boxnet.php
portfolio/boxnet/tests/privacy_provider_test.php [new file with mode: 0644]
portfolio/classes/privacy/legacy_polyfill.php [new file with mode: 0644]
portfolio/classes/privacy/portfolio_provider.php [new file with mode: 0644]
portfolio/classes/privacy/provider.php [new file with mode: 0644]
portfolio/download/classes/privacy/provider.php [new file with mode: 0644]
portfolio/download/lang/en/portfolio_download.php
portfolio/flickr/classes/privacy/provider.php [new file with mode: 0644]
portfolio/flickr/lang/en/portfolio_flickr.php
portfolio/flickr/tests/privacy_provider_test.php [new file with mode: 0644]
portfolio/googledocs/classes/privacy/provider.php [new file with mode: 0644]
portfolio/googledocs/lang/en/portfolio_googledocs.php
portfolio/googledocs/tests/privacy_provider_test.php [new file with mode: 0644]
portfolio/mahara/classes/privacy/provider.php [new file with mode: 0644]
portfolio/mahara/lang/en/portfolio_mahara.php
portfolio/mahara/tests/privacy_provider_test.php [new file with mode: 0644]
portfolio/picasa/classes/privacy/provider.php [new file with mode: 0644]
portfolio/picasa/lang/en/portfolio_picasa.php
portfolio/picasa/tests/privacy_provider_test.php [new file with mode: 0644]
portfolio/tests/privacy_legacy_polyfill_test.php [new file with mode: 0644]
portfolio/tests/privacy_provider_test.php [new file with mode: 0644]
privacy/classes/local/request/helper.php
privacy/classes/manager.php
privacy/classes/tests/request/content_writer.php
privacy/tests/provider_test.php [new file with mode: 0644]
privacy/tests/tests_content_writer_test.php [new file with mode: 0644]
question/classes/external.php
question/lib.php
question/type/edit_question_form.php
question/type/tags_form.php
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/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
search/classes/manager.php
search/classes/output/form/search.php
search/index.php
search/tests/behat/search_query.feature
search/tests/manager_test.php
tag/classes/tag.php
tag/tests/behat/edit_tag.feature
theme/boost/amd/build/alert.min.js
theme/boost/amd/build/button.min.js
theme/boost/amd/build/carousel.min.js
theme/boost/amd/build/collapse.min.js
theme/boost/amd/build/dropdown.min.js
theme/boost/amd/build/form-display-errors.min.js
theme/boost/amd/build/modal.min.js
theme/boost/amd/build/popover.min.js
theme/boost/amd/build/scrollspy.min.js
theme/boost/amd/build/tab.min.js
theme/boost/amd/build/tooltip.min.js
theme/boost/amd/build/util.min.js
theme/boost/amd/src/alert.js
theme/boost/amd/src/button.js
theme/boost/amd/src/carousel.js
theme/boost/amd/src/collapse.js
theme/boost/amd/src/dropdown.js
theme/boost/amd/src/form-display-errors.js
theme/boost/amd/src/modal.js
theme/boost/amd/src/popover.js
theme/boost/amd/src/scrollspy.js
theme/boost/amd/src/tab.js
theme/boost/amd/src/tooltip.js
theme/boost/amd/src/util.js
theme/boost/classes/output/core_course/management/renderer.php [new file with mode: 0644]
theme/boost/classes/output/core_renderer.php
theme/boost/cli/import-bootswatch.php
theme/boost/lang/en/theme_boost.php
theme/boost/lib.php
theme/boost/readme_moodle.txt
theme/boost/scss/bootstrap.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/LICENSE
theme/boost/scss/bootstrap/_alert.scss
theme/boost/scss/bootstrap/_animation.scss [deleted file]
theme/boost/scss/bootstrap/_badge.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_breadcrumb.scss
theme/boost/scss/bootstrap/_button-group.scss
theme/boost/scss/bootstrap/_buttons.scss
theme/boost/scss/bootstrap/_card.scss
theme/boost/scss/bootstrap/_carousel.scss
theme/boost/scss/bootstrap/_close.scss
theme/boost/scss/bootstrap/_code.scss
theme/boost/scss/bootstrap/_custom-forms.scss
theme/boost/scss/bootstrap/_custom.scss [deleted file]
theme/boost/scss/bootstrap/_dropdown.scss
theme/boost/scss/bootstrap/_forms.scss
theme/boost/scss/bootstrap/_functions.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_grid.scss
theme/boost/scss/bootstrap/_images.scss
theme/boost/scss/bootstrap/_input-group.scss
theme/boost/scss/bootstrap/_jumbotron.scss
theme/boost/scss/bootstrap/_list-group.scss
theme/boost/scss/bootstrap/_media.scss
theme/boost/scss/bootstrap/_mixins.scss
theme/boost/scss/bootstrap/_modal.scss
theme/boost/scss/bootstrap/_nav.scss
theme/boost/scss/bootstrap/_navbar.scss
theme/boost/scss/bootstrap/_normalize.scss [deleted file]
theme/boost/scss/bootstrap/_pagination.scss
theme/boost/scss/bootstrap/_popover.scss
theme/boost/scss/bootstrap/_print.scss
theme/boost/scss/bootstrap/_progress.scss
theme/boost/scss/bootstrap/_reboot.scss
theme/boost/scss/bootstrap/_root.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_tables.scss
theme/boost/scss/bootstrap/_tags.scss [deleted file]
theme/boost/scss/bootstrap/_tooltip.scss
theme/boost/scss/bootstrap/_transitions.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_type.scss
theme/boost/scss/bootstrap/_utilities.scss
theme/boost/scss/bootstrap/_variables.scss
theme/boost/scss/bootstrap/bootstrap-flex.scss [deleted file]
theme/boost/scss/bootstrap/bootstrap-grid.scss
theme/boost/scss/bootstrap/bootstrap-reboot.scss
theme/boost/scss/bootstrap/bootstrap.scss
theme/boost/scss/bootstrap/mixins/_alert.scss
theme/boost/scss/bootstrap/mixins/_background-variant.scss
theme/boost/scss/bootstrap/mixins/_badge.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_border-radius.scss
theme/boost/scss/bootstrap/mixins/_box-shadow.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_breakpoints.scss
theme/boost/scss/bootstrap/mixins/_buttons.scss
theme/boost/scss/bootstrap/mixins/_cards.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_caret.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_clearfix.scss
theme/boost/scss/bootstrap/mixins/_float.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_forms.scss
theme/boost/scss/bootstrap/mixins/_gradients.scss
theme/boost/scss/bootstrap/mixins/_grid-framework.scss
theme/boost/scss/bootstrap/mixins/_grid.scss
theme/boost/scss/bootstrap/mixins/_hover.scss
theme/boost/scss/bootstrap/mixins/_image.scss
theme/boost/scss/bootstrap/mixins/_list-group.scss
theme/boost/scss/bootstrap/mixins/_nav-divider.scss
theme/boost/scss/bootstrap/mixins/_navbar-align.scss
theme/boost/scss/bootstrap/mixins/_pagination.scss
theme/boost/scss/bootstrap/mixins/_progress.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_pulls.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_reset-filter.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_reset-text.scss
theme/boost/scss/bootstrap/mixins/_resize.scss
theme/boost/scss/bootstrap/mixins/_screen-reader.scss
theme/boost/scss/bootstrap/mixins/_tab-focus.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_tag.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_text-emphasis.scss
theme/boost/scss/bootstrap/mixins/_text-hide.scss
theme/boost/scss/bootstrap/mixins/_text-truncate.scss
theme/boost/scss/bootstrap/mixins/_transition.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_visibility.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_align.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_background.scss
theme/boost/scss/bootstrap/utilities/_borders.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_display.scss
theme/boost/scss/bootstrap/utilities/_embed.scss [moved from theme/boost/scss/bootstrap/_responsive-embed.scss with 60% similarity]
theme/boost/scss/bootstrap/utilities/_flex.scss
theme/boost/scss/bootstrap/utilities/_float.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_position.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_pulls.scss [deleted file]
theme/boost/scss/bootstrap/utilities/_sizing.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_spacing.scss
theme/boost/scss/bootstrap/utilities/_text.scss
theme/boost/scss/bootstrap/utilities/_visibility.scss
theme/boost/scss/fontawesome.scss [moved from theme/boost/scss/fontawesome/moodle-path.scss with 88% similarity]
theme/boost/scss/fontawesome/readme_moodle.txt
theme/boost/scss/moodle.scss
theme/boost/scss/moodle/admin.scss
theme/boost/scss/moodle/backup-restore.scss
theme/boost/scss/moodle/blocks.scss
theme/boost/scss/moodle/bootswatch.scss
theme/boost/scss/moodle/bs2-compat.scss
theme/boost/scss/moodle/bs4alphacompat.scss [new file with mode: 0644]
theme/boost/scss/moodle/calendar.scss
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/course.scss
theme/boost/scss/moodle/debug.scss
theme/boost/scss/moodle/drawer.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/icons.scss
theme/boost/scss/moodle/message.scss
theme/boost/scss/moodle/modal.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/responsive-tabs.scss [deleted file]
theme/boost/scss/moodle/search.scss
theme/boost/scss/moodle/undo.scss
theme/boost/scss/moodle/user.scss
theme/boost/scss/preset/default.scss
theme/boost/scss/preset/plain.scss
theme/boost/templates/admin_setting_tabs.mustache
theme/boost/templates/block_search_forums/search_form.mustache
theme/boost/templates/columns1.mustache
theme/boost/templates/columns2.mustache
theme/boost/templates/core/action_menu.mustache
theme/boost/templates/core/action_menu_trigger.mustache
theme/boost/templates/core/block.mustache
theme/boost/templates/core/custom_menu_item.mustache
theme/boost/templates/core/dataformat_selector.mustache
theme/boost/templates/core/filemanager_fileselect.mustache
theme/boost/templates/core/filemanager_modal_generallayout.mustache
theme/boost/templates/core/filemanager_page_generallayout.mustache
theme/boost/templates/core/form_autocomplete_selection.mustache
theme/boost/templates/core/help_icon.mustache
theme/boost/templates/core/initials_bar.mustache [new file with mode: 0644]
theme/boost/templates/core/loginform.mustache
theme/boost/templates/core/modal.mustache
theme/boost/templates/core/preferences_groups.mustache
theme/boost/templates/core/settings_link_page.mustache
theme/boost/templates/core/settings_link_page_single.mustache
theme/boost/templates/core/signup_form_layout.mustache
theme/boost/templates/core/tabtree.mustache
theme/boost/templates/core_admin/setting.mustache
theme/boost/templates/core_form/element-advcheckbox-inline.mustache
theme/boost/templates/core_form/element-advcheckbox.mustache
theme/boost/templates/core_form/element-autocomplete-inline.mustache
theme/boost/templates/core_form/element-autocomplete.mustache
theme/boost/templates/core_form/element-button.mustache
theme/boost/templates/core_form/element-checkbox-inline.mustache
theme/boost/templates/core_form/element-checkbox.mustache
theme/boost/templates/core_form/element-date_time_selector-inline.mustache
theme/boost/templates/core_form/element-date_time_selector.mustache
theme/boost/templates/core_form/element-password.mustache
theme/boost/templates/core_form/element-passwordunmask.mustache
theme/boost/templates/core_form/element-radio-inline.mustache
theme/boost/templates/core_form/element-radio.mustache
theme/boost/templates/core_form/element-select-inline.mustache
theme/boost/templates/core_form/element-select.mustache
theme/boost/templates/core_form/element-selectgroups-inline.mustache
theme/boost/templates/core_form/element-selectgroups.mustache
theme/boost/templates/core_form/element-selectwithlink.mustache
theme/boost/templates/core_form/element-tags-inline.mustache
theme/boost/templates/core_form/element-tags.mustache
theme/boost/templates/core_form/element-template-inline.mustache
theme/boost/templates/core_form/element-template.mustache
theme/boost/templates/core_form/element-text-inline.mustache
theme/boost/templates/core_form/element-text.mustache
theme/boost/templates/core_form/element-textarea.mustache
theme/boost/templates/core_form/element-url.mustache
theme/boost/templates/core_grades/edit_tree.mustache
theme/boost/templates/custom_menu_footer.mustache
theme/boost/templates/flat_navigation.mustache
theme/boost/templates/footer.mustache [new file with mode: 0644]
theme/boost/templates/head.mustache [new file with mode: 0644]
theme/boost/templates/header.mustache
theme/boost/templates/login.mustache
theme/boost/templates/maintenance.mustache
theme/boost/templates/mod_forum/quick_search_form.mustache
theme/boost/templates/nav-drawer.mustache
theme/boost/templates/navbar-secure.mustache [moved from theme/boost/templates/header-secure.mustache with 66% similarity]
theme/boost/templates/navbar.mustache [new file with mode: 0644]
theme/boost/templates/secure.mustache
theme/boost/templates/tool_usertours/tourstep.mustache
theme/boost/tests/behat/behat_theme_boost_behat_action_menu.php
theme/boost/tests/behat/behat_theme_boost_behat_navigation.php
theme/boost/thirdpartylibs.xml
theme/boost/upgrade.txt
user/classes/participants_table.php
user/editlib.php
user/lib.php
user/tests/behat/set_default_homepage.feature
user/tests/behat/set_email_display.feature [new file with mode: 0644]
user/tests/behat/view_participants.feature
version.php
webservice/rest/classes/privacy/provider.php [new file with mode: 0644]
webservice/rest/lang/en/webservice_rest.php
webservice/soap/classes/privacy/provider.php [new file with mode: 0644]
webservice/soap/lang/en/webservice_soap.php
webservice/xmlrpc/classes/privacy/provider.php [new file with mode: 0644]
webservice/xmlrpc/lang/en/webservice_xmlrpc.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 240a819..1e5837f 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) {
@@ -443,6 +442,7 @@ if ($interactive) {
     }
 }
 $CFG->tempdir       = $CFG->dataroot.'/temp';
+$CFG->backuptempdir = $CFG->tempdir.'/backup';
 $CFG->cachedir      = $CFG->dataroot.'/cache';
 $CFG->localcachedir = $CFG->dataroot.'/localcache';
 
index 6c10b9a..5915e24 100644 (file)
@@ -131,10 +131,11 @@ if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
     cli_error(get_string('pluginschecktodo', 'admin'));
 }
 
+$a = new stdClass();
+$a->oldversion = $oldversion;
+$a->newversion = $newversion;
+
 if ($interactive) {
-    $a = new stdClass();
-    $a->oldversion = $oldversion;
-    $a->newversion = $newversion;
     echo cli_heading(get_string('databasechecking', '', $a)) . PHP_EOL;
 }
 
@@ -193,5 +194,5 @@ admin_apply_default_settings(NULL, false);
 // to immediately start browsing the site.
 upgrade_themes();
 
-echo get_string('cliupgradefinished', 'admin')."\n";
+echo get_string('cliupgradefinished', 'admin', $a)."\n";
 exit(0); // 0 means success
index 7486db8..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));
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'),
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 e99f7c5..9f5c317 100644 (file)
@@ -39,7 +39,7 @@
 </h2>
 <div>{{{framework.description}}}</div>
     <h3>{{#str}}competencies, core_competency{{/str}}</h3>
-    <div class="row-fluid">
+    <div class="row-fluid row">
         <div class="span6 col-lg-6">
             <p>
                 <form data-region="filtercompetencies" data-frameworkid="{{framework.id}}" class="form-inline">
@@ -56,7 +56,7 @@
         </div>
 
         <div class="span6 card col-lg-6">
-            <div class="card-block">
+            <div class="card-block card-body">
                 <div class="card-title">
                     <h4 data-region="selected-competency">{{#str}}selectedcompetency, tool_lp{{/str}}</h4>
                         <span data-region="competencyactionsmenu" class="pull-xs-right">
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 160c4f0..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.
@@ -337,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'),
@@ -357,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 7df769b..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),
                         )
                     )
                 ),
@@ -330,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 e61657c..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;
@@ -305,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(
diff --git a/admin/tool/monitor/classes/privacy/provider.php b/admin/tool/monitor/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..9b481d3
--- /dev/null
@@ -0,0 +1,211 @@
+<?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 class for requesting user data.
+ *
+ * @package    tool_monitor
+ * @copyright  2018 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_monitor\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\local\metadata\collection;
+use \core_privacy\local\request\contextlist;
+use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\transform;
+use \core_privacy\local\request\writer;
+use \tool_monitor\subscription_manager;
+use \tool_monitor\rule_manager;
+
+/**
+ * Privacy provider for tool_monitor
+ *
+ * @package    tool_monitor
+ * @copyright  2018 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\plugin\provider {
+
+    /**
+     * Get information about the user data stored by this plugin.
+     *
+     * @param  collection $collection An object for storing metadata.
+     * @return collection The metadata.
+     */
+    public static function get_metadata(collection $collection) : collection {
+        $toolmonitorrules = [
+            'description' => 'privacy:metadata:description',
+            'name' => 'privacy:metadata:name',
+            'userid' => 'privacy:metadata:userid',
+            'plugin' => 'privacy:metadata:plugin',
+            'eventname' => 'privacy:metadata:eventname',
+            'template' => 'privacy:metadata:template',
+            'frequency' => 'privacy:metadata:frequency',
+            'timewindow' => 'privacy:metadata:timewindow',
+            'timemodified' => 'privacy:metadata:timemodifiedrule',
+            'timecreated' => 'privacy:metadata:timecreatedrule'
+        ];
+        $toolmonitorsubscriptions = [
+            'userid' => 'privacy:metadata:useridsub',
+            'timecreated' => 'privacy:metadata:timecreatedsub',
+            'lastnotificationsent' => 'privacy:metadata:lastnotificationsent',
+            'inactivedate' => 'privacy:metadata:inactivedate'
+        ];
+        // Tool monitor history doesn't look like it is used at all.
+        $toolmonitorhistory = [
+            'userid' => 'privacy:metadata:useridhistory',
+            'timesent' => 'privacy:metadata:timesent'
+        ];
+        $collection->add_database_table('tool_monitor_rules', $toolmonitorrules, 'privacy:metadata:rulessummary');
+        $collection->add_database_table('tool_monitor_subscriptions', $toolmonitorsubscriptions,
+                'privacy:metadata:subscriptionssummary');
+        $collection->add_database_table('tool_monitor_history', $toolmonitorhistory, 'privacy:metadata:historysummary');
+        $collection->link_subsystem('core_message', 'privacy:metadata:messagesummary');
+        return $collection;
+    }
+
+    /**
+     * Return all contexts for this userid. In this situation the user context.
+     *
+     * @param  int $userid The user ID.
+     * @return contextlist The list of context IDs.
+     */
+    public static function get_contexts_for_userid(int $userid) : contextlist {
+        $params = ['useridrules' => $userid, 'useridsubscriptions' => $userid, 'contextuserrule' => CONTEXT_USER,
+                'contextusersub' => CONTEXT_USER];
+        $sql = "SELECT DISTINCT ctx.id
+                  FROM {context} ctx
+             LEFT JOIN {tool_monitor_rules} mr ON ctx.instanceid = mr.userid AND ctx.contextlevel = :contextuserrule
+             LEFT JOIN {tool_monitor_subscriptions} ms ON ctx.instanceid = ms.userid AND ctx.contextlevel = :contextusersub
+                 WHERE (ms.userid = :useridrules OR mr.userid = :useridsubscriptions)";
+
+        $contextlist = new contextlist();
+        $contextlist->add_from_sql($sql, $params);
+        return $contextlist;
+    }
+
+    /**
+     * Export all event monitor information for the list of contexts and this user.
+     *
+     * @param  approved_contextlist $contextlist The list of approved contexts for a user.
+     */
+    public static function export_user_data(approved_contextlist $contextlist) {
+        global $DB;
+        // Export rules.
+        $context = \context_user::instance($contextlist->get_user()->id);
+        $rules = $DB->get_records('tool_monitor_rules', ['userid' => $contextlist->get_user()->id]);
+        if ($rules) {
+            static::export_monitor_rules($rules, $context);
+        }
+        // Export subscriptions.
+        $subscriptions = subscription_manager::get_user_subscriptions(0, 0, $contextlist->get_user()->id);
+        if ($subscriptions) {
+            static::export_monitor_subscriptions($subscriptions, $context);
+        }
+    }
+
+    /**
+     * Delete all user data for this context.
+     *
+     * @param  \context $context The context to delete data for.
+     */
+    public static function delete_data_for_all_users_in_context(\context $context) {
+        // Only delete data for user contexts.
+        if ($context->contextlevel == CONTEXT_USER) {
+            static::delete_user_data($context->instanceid);
+        }
+    }
+
+    /**
+     * Delete all user data for this user only.
+     *
+     * @param  approved_contextlist $contextlist The list of approved contexts for a user.
+     */
+    public static function delete_data_for_user(approved_contextlist $contextlist) {
+        static::delete_user_data($contextlist->get_user()->id);
+    }
+
+    /**
+     * This does the deletion of user data for the event monitor.
+     *
+     * @param  int $userid The user ID
+     */
+    protected static function delete_user_data(int $userid) {
+        global $DB;
+        // Delete this user's subscriptions first.
+        subscription_manager::delete_user_subscriptions($userid);
+        // Because we only use user contexts the instance ID is the user ID.
+        // Get the rules and check if this user has the capability to delete them.
+        $rules = $DB->get_records('tool_monitor_rules', ['userid' => $userid]);
+        foreach ($rules as $ruledata) {
+            $rule = rule_manager::get_rule($ruledata);
+            // If no-one is suscribed to the rule then it is safe to delete.
+            if ($rule->can_manage_rule($userid) && subscription_manager::count_rule_subscriptions($rule->id) == 0) {
+                $rule->delete_rule();
+            }
+        }
+    }
+
+    /**
+     * This formats and then exports the monitor rules.
+     *
+     * @param  array $rules The monitor rules.
+     * @param  context_user $context The user context
+     */
+    protected static function export_monitor_rules(array $rules, \context_user $context) {
+        foreach ($rules as $rule) {
+            $rule = rule_manager::get_rule($rule);
+            $ruledata = new \stdClass();
+            $ruledata->name = $rule->name;
+            $ruledata->eventname = $rule->get_event_name();
+            $ruledata->description = $rule->get_description($context);
+            $ruledata->plugin = $rule->get_plugin_name();
+            $ruledata->template = $rule->template;
+            $ruledata->frequency = $rule->get_filters_description();
+            $ruledata->course = $rule->get_course_name($context);
+            $ruledata->timecreated = transform::datetime($rule->timecreated);
+            $ruledata->timemodified = transform::datetime($rule->timemodified);
+            writer::with_context($context)->export_data([get_string('privacy:createdrules', 'tool_monitor'),
+                    $rule->name . '_' . $rule->id], $ruledata);
+        }
+    }
+
+    /**
+     * This formats and then exports the event monitor subscriptions.
+     *
+     * @param  array $subscriptions Subscriptions
+     * @param  \context_user $context The user context
+     */
+    protected static function export_monitor_subscriptions(array $subscriptions, \context_user $context) {
+        foreach ($subscriptions as $subscription) {
+            $subscriptiondata = new \stdClass();
+            $subscriptiondata->instancename = $subscription->get_instance_name();
+            $subscriptiondata->eventname = $subscription->get_event_name();
+            $subscriptiondata->frequency = $subscription->get_filters_description();
+            $subscriptiondata->name = $subscription->get_name($context);
+            $subscriptiondata->description = $subscription->get_description($context);
+            $subscriptiondata->pluginname = $subscription->get_plugin_name();
+            $subscriptiondata->course = $subscription->get_course_name($context);
+            $subscriptiondata->timecreated = transform::datetime($subscription->timecreated);
+            $subscriptiondata->lastnotificationsent = transform::datetime($subscription->lastnotificationsent);
+            writer::with_context($context)->export_data([get_string('privacy:subscriptions', 'tool_monitor'),
+                    $subscriptiondata->name . '_' . $subscription->id, $subscriptiondata->course, $subscriptiondata->instancename],
+                    $subscriptiondata);
+        }
+    }
+}
index 3c5e6d2..4cc3920 100644 (file)
@@ -51,14 +51,15 @@ class rule {
     }
 
     /**
-     * Can the current user manage this rule?
+     * Can the user manage this rule? Defaults to $USER.
      *
+     * @param int $userid Check against this userid.
      * @return bool true if the current user can manage this rule, else false.
      */
-    public function can_manage_rule() {
+    public function can_manage_rule($userid = null) {
         $courseid = $this->courseid;
         $context = empty($courseid) ? \context_system::instance() : \context_course::instance($this->courseid);
-        return has_capability('tool/monitor:managerules', $context);
+        return has_capability('tool/monitor:managerules', $context, $userid);
     }
 
     /**
index ed41095..81d20bb 100644 (file)
@@ -79,6 +79,28 @@ $string['monitor:managetool'] = 'Enable/disable event monitoring';
 $string['monitor:subscribe'] = 'Subscribe to event monitor rules';
 $string['norules'] = 'There are no event monitoring rules.';
 $string['pluginname'] = 'Event monitor';
+$string['privacy:createdrules'] = 'Event monitor rules I created';
+$string['privacy:metadata:description'] = 'Description of the rule';
+$string['privacy:metadata:eventname'] = 'Fully qualified name of the event';
+$string['privacy:metadata:frequency'] = 'Frequency of notifications';
+$string['privacy:metadata:historysummary'] = 'Stores the history of the message notifications sent';
+$string['privacy:metadata:inactivedate'] = 'Period of time, in days, after which an inactive subscription will be removed completely';
+$string['privacy:metadata:lastnotificationsent'] = 'When a notification was last sent for this subscription.';
+$string['privacy:metadata:messagesummary'] = 'Notifications are sent to the message system.';
+$string['privacy:metadata:name'] = 'Name of the rule';
+$string['privacy:metadata:plugin'] = 'Frankenstlye name of the plugin';
+$string['privacy:metadata:rulessummary'] = 'This stores monitor rules.';
+$string['privacy:metadata:subscriptionssummary'] = 'Stores user subscriptions to various rules';
+$string['privacy:metadata:template'] = 'Message template';
+$string['privacy:metadata:timecreatedrule'] = 'When this rule was created';
+$string['privacy:metadata:timecreatedsub'] = 'When this subscription was created';
+$string['privacy:metadata:timemodifiedrule'] = 'When this rule was last modified';
+$string['privacy:metadata:timesent'] = 'When the message was sent';
+$string['privacy:metadata:timewindow'] = 'Time window in seconds';
+$string['privacy:metadata:userid'] = 'Id of user who created the rule';
+$string['privacy:metadata:useridhistory'] = 'User to whom this notification was sent';
+$string['privacy:metadata:useridsub'] = 'User id of the subscriber';
+$string['privacy:subscriptions'] = 'My event monitor subscriptions';
 $string['processevents'] = 'Process events';
 $string['rulename'] = 'Rule name';
 $string['ruleareyousure'] = 'Are you sure you want to delete the rule "{$a}"?';
diff --git a/admin/tool/monitor/tests/privacy_test.php b/admin/tool/monitor/tests/privacy_test.php
new file mode 100644 (file)
index 0000000..5c5f11c
--- /dev/null
@@ -0,0 +1,289 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy test for the event monitor
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2018 Adrian Greeve <adriangreeve.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \tool_monitor\privacy\provider;
+use \core_privacy\local\request\approved_contextlist;
+
+/**
+ * Privacy test for the event monitor
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2018 Adrian Greeve <adriangreeve.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_privacy_testcase extends advanced_testcase {
+
+    /**
+     * Set up method.
+     */
+    public function setUp() {
+        $this->resetAfterTest();
+        // Enable monitor.
+        set_config('enablemonitor', 1, 'tool_monitor');
+    }
+
+    /**
+     * Assign a capability to $USER
+     * The function creates a student $USER if $USER->id is empty
+     *
+     * @param string $capability capability name
+     * @param int $contextid
+     * @param int $roleid
+     * @return int the role id - mainly returned for creation, so calling function can reuse it
+     */
+    public static function assign_user_capability($capability, $contextid, $roleid = null) {
+        global $USER;
+
+        // Create a new student $USER if $USER doesn't exist.
+        if (empty($USER->id)) {
+            $user  = self::getDataGenerator()->create_user();
+            self::setUser($user);
+        }
+
+        if (empty($roleid)) {
+            $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
+        }
+
+        assign_capability($capability, CAP_ALLOW, $roleid, $contextid);
+
+        role_assign($roleid, $USER->id, $contextid);
+
+        accesslib_clear_all_caches_for_unit_testing();
+
+        return $roleid;
+    }
+
+    /**
+     * Test that a collection with data is returned when calling this function.
+     */
+    public function test_get_metadata() {
+        $collection = new \core_privacy\local\metadata\collection('tool_monitor');
+        $collection = provider::get_metadata($collection);
+        $this->assertNotEmpty($collection);
+    }
+
+    /**
+     * Check that a user context is returned if there is any user data for this user.
+     */
+    public function test_get_contexts_for_userid() {
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext = \context_user::instance($user->id);
+        $usercontext2 = \context_user::instance($user2->id);
+        $this->assertEmpty(provider::get_contexts_for_userid($user->id));
+        $this->assertEmpty(provider::get_contexts_for_userid($user2->id));
+
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        // Create a rule with this user.
+        $this->setUser($user);
+        $rule = $monitorgenerator->create_rule();
+        $contextlist = provider::get_contexts_for_userid($user->id);
+
+        // Check that we only get back one context.
+        $this->assertCount(1, $contextlist);
+
+        // Check that a context is returned for just creating a rule.
+        $this->assertEquals($usercontext->id, $contextlist->get_contextids()[0]);
+
+        $this->setUser($user2);
+
+        $record = new stdClass();
+        $record->courseid = 0;
+        $record->userid = $user2->id;
+        $record->ruleid = $rule->id;
+
+        $subscription = $monitorgenerator->create_subscription($record);
+        $contextlist = provider::get_contexts_for_userid($user2->id);
+
+        // Check that we only get back one context.
+        $this->assertCount(1, $contextlist);
+
+        // Check that a context is returned for just subscribing to a rule.
+        $this->assertEquals($usercontext2->id, $contextlist->get_contextids()[0]);
+    }
+
+    /**
+     * Test that user data is exported correctly.
+     */
+    public function test_export_user_data() {
+        $user = $this->getDataGenerator()->create_user();
+        $usercontext = \context_user::instance($user->id);
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $this->setUser($user);
+        $rulerecord = (object)['name' => 'privacy rule'];
+        $rule = $monitorgenerator->create_rule($rulerecord);
+
+        $secondrulerecord = (object)['name' => 'privacy rule2'];
+        $rule2 = $monitorgenerator->create_rule($secondrulerecord);
+
+        $subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id];
+        $subscription = $monitorgenerator->create_subscription($subscription);
+
+        $writer = \core_privacy\local\request\writer::with_context($usercontext);
+        $this->assertFalse($writer->has_any_data());
+
+        $approvedlist = new approved_contextlist($user, 'tool_monitor', [$usercontext->id]);
+        provider::export_user_data($approvedlist);
+
+        // Check that the rules created by this user are exported.
+        $this->assertEquals($rulerecord->name, $writer->get_data([get_string('privacy:createdrules', 'tool_monitor'),
+                $rulerecord->name . '_' . $rule->id])->name);
+        $this->assertEquals($secondrulerecord->name, $writer->get_data([get_string('privacy:createdrules', 'tool_monitor'),
+                $secondrulerecord->name . '_' . $rule2->id])->name);
+
+        // Check that the subscriptions for this user are also exported.
+        $this->assertEquals($rulerecord->name, $writer->get_data([get_string('privacy:subscriptions', 'tool_monitor'),
+                $rulerecord->name . '_' . $subscription->id, 'Site' , 'All events'])->name);
+    }
+
+    /**
+     * Test deleting all user data for a specific context.
+     */
+    public function test_delete_data_for_all_users_in_context() {
+        global $DB;
+
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext = \context_user::instance($user->id);
+        $usercontext2 = \context_user::instance($user2->id);
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $this->setUser($user);
+        // Need to give user one the ability to manage rules.
+        $this->assign_user_capability('tool/monitor:managerules', \context_system::instance());
+
+        $rulerecord = (object)['name' => 'privacy rule'];
+        $rule = $monitorgenerator->create_rule($rulerecord);
+
+        $secondrulerecord = (object)['name' => 'privacy rule2'];
+        $rule2 = $monitorgenerator->create_rule($secondrulerecord);
+
+        $subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id];
+        $subscription = $monitorgenerator->create_subscription($subscription);
+
+        // Have user 2 subscribe to the second rule created by user 1.
+        $subscription2 = (object)['ruleid' => $rule2->id, 'userid' => $user2->id];
+        $subscription2 = $monitorgenerator->create_subscription($subscription2);
+
+        $this->setUser($user2);
+        $thirdrulerecord = (object)['name' => 'privacy rule for second user'];
+        $rule3 = $monitorgenerator->create_rule($thirdrulerecord);
+
+        $subscription3 = (object)['ruleid' => $rule3->id, 'userid' => $user2->id];
+        $subscription3 = $monitorgenerator->create_subscription($subscription3);
+
+        // Try a different context first.
+        provider::delete_data_for_all_users_in_context(context_system::instance());
+
+        // Get all of the monitor rules.
+        $dbrules = $DB->get_records('tool_monitor_rules');
+
+        // All of the rules should still be present.
+        $this->assertCount(3, $dbrules);
+        $this->assertEquals($user->id, $dbrules[$rule->id]->userid);
+        $this->assertEquals($user->id, $dbrules[$rule2->id]->userid);
+        $this->assertEquals($user2->id, $dbrules[$rule3->id]->userid);
+
+        // Delete everything for the first user context.
+        provider::delete_data_for_all_users_in_context($usercontext);
+
+        // Get all of the monitor rules.
+        $dbrules = $DB->get_records('tool_monitor_rules');
+
+        // Only the rules for user 1 that does not have any more subscriptions should be deleted (the first rule).
+        $this->assertCount(2, $dbrules);
+        $this->assertEquals($user->id, $dbrules[$rule2->id]->userid);
+        $this->assertEquals($user2->id, $dbrules[$rule3->id]->userid);
+
+        // Get all of the monitor subscriptions.
+        $dbsubs = $DB->get_records('tool_monitor_subscriptions');
+        // There should be two subscriptions left, both for user 2.
+        $this->assertCount(2, $dbsubs);
+        $this->assertEquals($user2->id, $dbsubs[$subscription2->id]->userid);
+        $this->assertEquals($user2->id, $dbsubs[$subscription3->id]->userid);
+    }
+
+    /**
+     * This should work identical to the above test.
+     */
+    public function test_delete_data_for_user() {
+        global $DB;
+
+        $user = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $usercontext = \context_user::instance($user->id);
+        $usercontext2 = \context_user::instance($user2->id);
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $this->setUser($user);
+        // Need to give user one the ability to manage rules.
+        $this->assign_user_capability('tool/monitor:managerules', \context_system::instance());
+
+        $rulerecord = (object)['name' => 'privacy rule'];
+        $rule = $monitorgenerator->create_rule($rulerecord);
+
+        $secondrulerecord = (object)['name' => 'privacy rule2'];
+        $rule2 = $monitorgenerator->create_rule($secondrulerecord);
+
+        $subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id];
+        $subscription = $monitorgenerator->create_subscription($subscription);
+
+        // Have user 2 subscribe to the second rule created by user 1.
+        $subscription2 = (object)['ruleid' => $rule2->id, 'userid' => $user2->id];
+        $subscription2 = $monitorgenerator->create_subscription($subscription2);
+
+        $this->setUser($user2);
+        $thirdrulerecord = (object)['name' => 'privacy rule for second user'];
+        $rule3 = $monitorgenerator->create_rule($thirdrulerecord);
+
+        $subscription3 = (object)['ruleid' => $rule3->id, 'userid' => $user2->id];
+        $subscription3 = $monitorgenerator->create_subscription($subscription3);
+
+        $approvedlist = new approved_contextlist($user, 'tool_monitor', [$usercontext->id]);
+
+        // Delete everything for the first user.
+        provider::delete_data_for_user($approvedlist);
+
+        // Get all of the monitor rules.
+        $dbrules = $DB->get_records('tool_monitor_rules');
+
+        // Only the rules for user 1 that does not have any more subscriptions should be deleted (the first rule).
+        $this->assertCount(2, $dbrules);
+        $this->assertEquals($user->id, $dbrules[$rule2->id]->userid);
+        $this->assertEquals($user2->id, $dbrules[$rule3->id]->userid);
+
+        // Get all of the monitor subscriptions.
+        $dbsubs = $DB->get_records('tool_monitor_subscriptions');
+        // There should be two subscriptions left, both for user 2.
+        $this->assertCount(2, $dbsubs);
+        $this->assertEquals($user2->id, $dbsubs[$subscription2->id]->userid);
+        $this->assertEquals($user2->id, $dbsubs[$subscription3->id]->userid);
+    }
+}
diff --git a/admin/tool/profiling/classes/privacy/provider.php b/admin/tool/profiling/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..5e758e6
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_profiling.
+ *
+ * @package    tool_profiling
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_profiling\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_profiling implementing null_provider.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
index 7c80c84..17479ba 100644 (file)
@@ -53,3 +53,4 @@ $string['summaryof'] = 'Summary of {$a}';
 $string['viewdetails'] = 'View profiling details';
 $string['viewdiff'] = 'View profiling differences with:';
 $string['viewdiffdetails'] = 'View profiling diff details';
+$string['privacy:metadata'] = 'The Profiling runs plugin does not store any personal data.';
index 2acb3b2..26d8b53 100644 (file)
@@ -207,9 +207,9 @@ class category_bin extends base_bin {
         // Get the backup file.
         $file = reset($files);
 
-        // Get a temp directory name and create it.
+        // Get a backup temp directory name and create it.
         $tempdir = \restore_controller::get_tempdir_name($context->id, $user->id);
-        $fulltempdir = make_temp_directory('/backup/' . $tempdir);
+        $fulltempdir = make_backup_temp_directory($tempdir);
 
         // Extract the backup to tmpdir.
         $fb = get_file_packer('application/vnd.moodle.backup');
index 67e1a21..e935d69 100644 (file)
@@ -211,9 +211,9 @@ class course_bin extends base_bin {
         // Get the backup file.
         $file = reset($files);
 
-        // Get a temp directory name and create it.
+        // Get a backup temp directory name and create it.
         $tempdir = \restore_controller::get_tempdir_name($context->id, $user->id);
-        $fulltempdir = make_temp_directory('/backup/' . $tempdir);
+        $fulltempdir = make_backup_temp_directory($tempdir);
 
         // Extract the backup to tempdir.
         $fb = get_file_packer('application/vnd.moodle.backup');
index ed3adea..c23c25f 100644 (file)
@@ -347,7 +347,7 @@ class tool_uploadcourse_course {
     /**
      * Get the directory of the object to restore.
      *
-     * @return string|false|null subdirectory in $CFG->tempdir/backup/..., false when an error occured
+     * @return string|false|null subdirectory in $CFG->backuptempdir/..., false when an error occured
      *                           and null when there is simply nothing.
      */
     protected function get_restore_content_dir() {
index 2873f3b..370d13a 100644 (file)
@@ -227,7 +227,7 @@ class tool_uploadcourse_helper {
         }
 
         // If we don't use the cache, or if we do and not set, or the directory doesn't exist any more.
-        if (!$usecache || (($backupid = $cache->get($cachekey)) === false || !is_dir("$CFG->tempdir/backup/$backupid"))) {
+        if (!$usecache || (($backupid = $cache->get($cachekey)) === false || !is_dir(get_backup_temp_directory($backupid)))) {
 
             // Use null instead of false because it would consider that the cache key has not been set.
             $backupid = null;
@@ -236,7 +236,7 @@ class tool_uploadcourse_helper {
                 // Extracting the backup file.
                 $packer = get_file_packer('application/vnd.moodle.backup');
                 $backupid = restore_controller::get_tempdir_name(SITEID, $USER->id);
-                $path = "$CFG->tempdir/backup/$backupid/";
+                $path = make_backup_temp_directory($backupid, false);
                 $result = $packer->extract_to_pathname($backupfile, $path);
                 if (!$result) {
                     $errors['invalidbackupfile'] = new lang_string('invalidbackupfile', 'tool_uploadcourse');
index 8963f7e..8f55eef 100644 (file)
@@ -259,7 +259,7 @@ class tool_uploadcourse_processor {
     /**
      * Get the directory of the object to restore.
      *
-     * @return string subdirectory in $CFG->tempdir/backup/...
+     * @return string subdirectory in $CFG->backuptempdir/...
      */
     protected function get_restore_content_dir() {
         $backupfile = null;
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'