Merge branch 'MDL-70269' of https://github.com/timhunt/moodle
authorSara Arjona <sara@moodle.com>
Thu, 15 Apr 2021 13:36:31 +0000 (15:36 +0200)
committerVíctor Déniz <victor@moodle.com>
Fri, 16 Apr 2021 06:57:00 +0000 (07:57 +0100)
1049 files changed:
.eslintignore
.eslintrc
.github/workflows/push.yml
.gitignore
.grunt/babel-plugin-add-module-to-define.js [moved from babel-plugin-add-module-to-define.js with 98% similarity]
.grunt/components.js [moved from GruntfileComponents.js with 81% similarity]
.grunt/tasks/eslint.js [new file with mode: 0644]
.grunt/tasks/gherkinlint.js [new file with mode: 0644]
.grunt/tasks/ignorefiles.js [new file with mode: 0644]
.grunt/tasks/javascript.js [new file with mode: 0644]
.grunt/tasks/sass.js [new file with mode: 0644]
.grunt/tasks/shifter.js [new file with mode: 0644]
.grunt/tasks/startup.js [new file with mode: 0644]
.grunt/tasks/style.js [new file with mode: 0644]
.grunt/tasks/stylelint.js [new file with mode: 0644]
.grunt/tasks/watch.js [new file with mode: 0644]
.stylelintignore
.travis.yml
Gruntfile.js
TRADEMARK.txt
admin/classes/task_log_table.php
admin/purgecaches.php
admin/roles/assign.php
admin/settings/courses.php
admin/settings/plugins.php
admin/settings/server.php
admin/tests/behat/browse_users.feature [new file with mode: 0644]
admin/tests/behat/manage_tokens.feature
admin/tests/behat/webservice_users.feature [new file with mode: 0644]
admin/tool/behat/tests/behat/datetime_strings.feature
admin/tool/behat/tests/manager_util_test.php
admin/tool/cohortroles/classes/output/cohort_role_assignments_table.php
admin/tool/cohortroles/index.php
admin/tool/dataprivacy/amd/build/contactdpo.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/contactdpo.min.js.map [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/myrequestactions.min.js
admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map
admin/tool/dataprivacy/amd/src/contactdpo.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/myrequestactions.js
admin/tool/dataprivacy/classes/api.php
admin/tool/dataprivacy/classes/external.php
admin/tool/dataprivacy/classes/form/contactdpo.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/local/helper.php
admin/tool/dataprivacy/classes/output/renderer.php
admin/tool/dataprivacy/createdatarequest_form.php
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
admin/tool/dataprivacy/lib.php
admin/tool/dataprivacy/templates/contact_dpo.mustache [deleted file]
admin/tool/dataprivacy/tests/api_test.php
admin/tool/dataprivacy/tests/behat/contact_privacy_officer.feature
admin/tool/dataprivacy/tests/behat/my_data_requests.feature [new file with mode: 0644]
admin/tool/dataprivacy/tests/coverage.php
admin/tool/dataprivacy/tests/expired_contexts_test.php
admin/tool/dataprivacy/tests/filtered_userlist_test.php
admin/tool/health/tests/healthlib_test.php
admin/tool/httpsreplace/tests/httpsreplace_test.php
admin/tool/langimport/tests/locale_test.php
admin/tool/lp/classes/external.php
admin/tool/lp/classes/output/template_plans_table.php
admin/tool/lpmigrate/tests/processor_test.php
admin/tool/monitor/tests/eventobservers_test.php
admin/tool/monitor/tests/subscription_test.php
admin/tool/oauth2/classes/form/issuer.php
admin/tool/oauth2/issuers.php
admin/tool/oauth2/lang/en/deprecated.txt [new file with mode: 0644]
admin/tool/oauth2/lang/en/tool_oauth2.php
admin/tool/oauth2/tests/behat/basic_settings.feature [new file with mode: 0644]
admin/tool/policy/classes/acceptances_table.php
admin/tool/policy/classes/api.php
admin/tool/policy/classes/form/accept_policy.php
admin/tool/uploaduser/classes/process.php
admin/tool/usertours/classes/manager.php
admin/tool/usertours/tests/step_test.php
admin/tool/usertours/tests/tour_test.php
admin/upgrade.txt
admin/user.php
admin/user/user_bulk_cohortadd.php
admin/user/user_bulk_display.php
admin/webservice/forms.php
admin/webservice/service_users.php
admin/webservice/tokens.php
analytics/tests/indicator_test.php
analytics/tests/prediction_test.php
auth/cas/CAS/moodle_readme.txt
auth/cas/thirdpartylibs.xml
availability/classes/info.php
availability/condition/completion/tests/behat/availability_completion.feature
availability/condition/completion/tests/condition_test.php
availability/condition/date/tests/condition_test.php
availability/condition/grade/tests/condition_test.php
availability/condition/group/tests/condition_test.php
availability/condition/grouping/tests/condition_test.php
availability/condition/profile/classes/condition.php
availability/condition/profile/classes/frontend.php
availability/condition/profile/tests/condition_test.php
availability/tests/info_test.php
availability/tests/tree_test.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_stepslib.php
backup/moodle2/tests/moodle2_course_format_test.php
backup/moodle2/tests/restore_gradebook_structure_step_test.php
backup/tests/backup_cleanup_task_test.php [new file with mode: 0644]
backup/util/helper/backup_helper.class.php
backup/util/helper/tests/async_helper_test.php
backup/util/loggers/file_logger.class.php
backup/util/settings/tests/settings_test.php
backup/util/ui/backup_ui_stage.class.php
badges/assertion.php
badges/backpack-connect.php
badges/backpack.js [deleted file]
badges/badge.php
badges/classes/assertion.php
badges/classes/form/external_backpack.php
badges/classes/oauth2/client.php
badges/classes/output/external_badge.php
badges/classes/output/issued_badge.php
badges/criteria/award_criteria_cohort.php
badges/criteria/award_criteria_profile.php
badges/index.php
badges/mybadges.php
badges/newbadge.php
badges/recipients.php
badges/renderer.php
badges/tests/badgeslib_test.php
badges/tests/behat/award_badge.feature
badges/tests/behat/criteria_activity.feature
badges/tests/events_test.php
badges/upgrade.txt
blocks/activity_results/block_activity_results.php
blocks/mentees/block_mentees.php
blocks/online_users/classes/fetcher.php
blocks/rss_client/tests/cron_test.php
blog/locallib.php
blog/rsslib.php
blog/tests/privacy_test.php
cache/stores/file/lib.php
cache/tests/cache_test.php
calendar/classes/privacy/provider.php
calendar/lib.php
calendar/renderer.php
calendar/tests/calendar_event_exporter_test.php
calendar/tests/event_vault_test.php
calendar/tests/externallib_test.php
calendar/tests/local_api_test.php
calendar/tests/privacy_test.php
calendar/tests/rrule_manager_test.php
cohort/tests/privacy_test.php
comment/lib.php
comment/locallib.php
competency/tests/api_test.php
competency/tests/event_test.php
competency/tests/external_test.php
completion/classes/cm_completion_details.php [new file with mode: 0644]
completion/tests/behat/behat_completion.php
completion/tests/behat/completion_course_page_display.feature [moved from completion/tests/behat/completion_course_page_checkboxes.feature with 67% similarity]
completion/tests/behat/custom_completion_display_conditions.feature [new file with mode: 0644]
completion/tests/behat/enable_manual_complete_mark.feature
completion/tests/behat/restrict_section_availability.feature
completion/tests/cm_completion_details_test.php [new file with mode: 0644]
completion/upgrade.txt
composer.json
composer.lock
contentbank/amd/build/upload.min.js [new file with mode: 0644]
contentbank/amd/build/upload.min.js.map [new file with mode: 0644]
contentbank/amd/src/upload.js [new file with mode: 0644]
contentbank/classes/form/upload_files.php [new file with mode: 0644]
contentbank/files_form.php [deleted file]
contentbank/index.php
contentbank/templates/bankcontent/toolbar.mustache
contentbank/tests/behat/edit_content.feature
contentbank/tests/contentbank_test.php
contentbank/tests/privacy_test.php
contentbank/upgrade.txt
contentbank/upload.php [deleted file]
contentbank/view.php
course/amd/build/events.min.js
course/amd/build/events.min.js.map
course/amd/build/manual_completion_toggle.min.js [new file with mode: 0644]
course/amd/build/manual_completion_toggle.min.js.map [new file with mode: 0644]
course/amd/build/repository.min.js
course/amd/build/repository.min.js.map
course/amd/build/view.min.js [new file with mode: 0644]
course/amd/build/view.min.js.map [new file with mode: 0644]
course/amd/src/events.js
course/amd/src/manual_completion_toggle.js [new file with mode: 0644]
course/amd/src/repository.js
course/amd/src/view.js [new file with mode: 0644]
course/classes/cache/course_image.php [new file with mode: 0644]
course/classes/category.php
course/classes/course_format.php [new file with mode: 0644]
course/classes/external/course_summary_exporter.php
course/classes/output/activity_information.php [new file with mode: 0644]
course/classes/output/cm_format.php [new file with mode: 0644]
course/classes/output/course_format.php [new file with mode: 0644]
course/classes/output/course_format/addsection.php [new file with mode: 0644]
course/classes/output/course_format/frontpagesection.php [new file with mode: 0644]
course/classes/output/course_format/sectionnavigation.php [new file with mode: 0644]
course/classes/output/course_format/sectionselector.php [new file with mode: 0644]
course/classes/output/section_format.php [new file with mode: 0644]
course/classes/output/section_format/availability.php [new file with mode: 0644]
course/classes/output/section_format/cmitem.php [new file with mode: 0644]
course/classes/output/section_format/cmlist.php [new file with mode: 0644]
course/classes/output/section_format/cmsummary.php [new file with mode: 0644]
course/classes/output/section_format/controlmenu.php [new file with mode: 0644]
course/classes/output/section_format/header.php [new file with mode: 0644]
course/classes/output/section_format/summary.php [new file with mode: 0644]
course/completion.js [deleted file]
course/dnduploadlib.php
course/edit_form.php
course/externallib.php
course/format/README.txt
course/format/formatlegacy.php
course/format/lib.php
course/format/renderer.php
course/format/singleactivity/lib.php
course/format/singleactivity/renderer.php
course/format/social/lib.php
course/format/topics/classes/output/section_format/controlmenu.php [new file with mode: 0644]
course/format/topics/format.php
course/format/topics/lib.php
course/format/topics/renderer.php
course/format/weeks/format.php
course/format/weeks/lib.php
course/format/weeks/renderer.php
course/lib.php
course/modlib.php
course/recent_form.php
course/renderer.php
course/templates/activity_date.mustache [new file with mode: 0644]
course/templates/activity_info.mustache [new file with mode: 0644]
course/templates/completion_automatic.mustache [new file with mode: 0644]
course/templates/completion_manual.mustache [new file with mode: 0644]
course/templates/local/cm_format.mustache [new file with mode: 0644]
course/templates/local/course_format.mustache [new file with mode: 0644]
course/templates/local/course_format/addsection.mustache [new file with mode: 0644]
course/templates/local/course_format/frontpagesection.mustache [new file with mode: 0644]
course/templates/local/course_format/sectionnavigation.mustache [new file with mode: 0644]
course/templates/local/course_format/sectionselector.mustache [new file with mode: 0644]
course/templates/local/section_format.mustache [new file with mode: 0644]
course/templates/local/section_format/availability.mustache [new file with mode: 0644]
course/templates/local/section_format/cmitem.mustache [new file with mode: 0644]
course/templates/local/section_format/cmlist.mustache [new file with mode: 0644]
course/templates/local/section_format/cmsummary.mustache [new file with mode: 0644]
course/templates/local/section_format/controlmenu.mustache [new file with mode: 0644]
course/templates/local/section_format/header.mustache [new file with mode: 0644]
course/templates/local/section_format/summary.mustache [new file with mode: 0644]
course/tests/behat/behat_course.php
course/tests/behat/course_activity_dates.feature [new file with mode: 0644]
course/tests/behat/paged_course_navigation.feature
course/tests/category_hooks_test.php
course/tests/course_image_cache_test.php [new file with mode: 0644]
course/tests/course_summary_exporter_test.php [new file with mode: 0644]
course/tests/courseformat_test.php
course/tests/courselib_test.php
course/tests/fixtures/format_theunittest.php
course/tests/fixtures/image.jpg [new file with mode: 0644]
course/togglecompletion.php
course/upgrade.txt
course/view.php
customfield/amd/build/form.min.js
customfield/amd/build/form.min.js.map
customfield/amd/src/form.js
customfield/templates/list.mustache
customfield/templates/nofields.mustache [new file with mode: 0644]
enrol/ajax.php
enrol/externallib.php
enrol/flatfile/tests/privacy_provider_test.php
enrol/locallib.php
enrol/manual/classes/enrol_users_form.php
enrol/otherusers.php
enrol/paypal/tests/privacy_provider_test.php
enrol/self/lib.php
enrol/self/locallib.php
favourites/tests/component_favourite_service_test.php
favourites/tests/user_favourite_service_test.php
files/converter/unoconv/tests/converter_test.php
files/tests/converter_test.php
files/tests/privacy_test.php
filter/data/tests/filter_test.php
filter/displayh5p/tests/filter_test.php
filter/tex/mimetex.darwin
filter/tex/mimetex.exe
filter/tex/mimetex.freebsd
filter/tex/mimetex.linux
filter/tex/mimetex.linux.aarch64
filter/tex/readme_moodle.txt
filter/tex/thirdpartylibs.xml
grade/edit/tree/lib.php
grade/grading/form/guide/db/services.php
grade/grading/form/guide/tests/coverage.php
grade/grading/form/rubric/db/services.php
grade/grading/form/rubric/tests/coverage.php
grade/grading/lib.php
grade/grading/manage.php
grade/grading/tests/coverage.php
grade/grading/tests/privacy_test.php
grade/report/grader/ajax_callbacks.php
grade/report/grader/lib.php
grade/report/history/classes/helper.php
grade/report/history/classes/output/tablelog.php
grade/report/history/users_ajax.php
grade/report/history/yui/build/moodle-gradereport_history-userselector/moodle-gradereport_history-userselector-debug.js
grade/report/history/yui/build/moodle-gradereport_history-userselector/moodle-gradereport_history-userselector-min.js
grade/report/history/yui/build/moodle-gradereport_history-userselector/moodle-gradereport_history-userselector.js
grade/report/history/yui/src/userselector/js/userselector.js
grade/report/user/externallib.php
grade/tests/coverage.php
grade/tests/reportlib_test.php
group/autogroup.php
group/index.php
group/lib.php
group/overview.php
group/tests/privacy_provider_test.php
h5p/tests/coverage.php
install/lang/hat/admin.php
install/lang/he/langconfig.php
install/lang/it/install.php
install/lang/mk/admin.php
install/lang/pt/install.php
install/lang/sk/install.php
install/lang/sr_cr/install.php
install/lang/sr_lt/install.php
install/lang/sv/error.php
install/lang/sv/install.php
install/lang/sv/langconfig.php
lang/en/admin.php
lang/en/badges.php
lang/en/cache.php
lang/en/completion.php
lang/en/course.php
lang/en/deprecated.txt
lang/en/moodle.php
lang/en/webservice.php
lib/accesslib.php
lib/adminlib.php
lib/adodb/LICENSE.md
lib/adodb/README.md
lib/adodb/adodb-active-record.inc.php
lib/adodb/adodb-active-recordx.inc.php
lib/adodb/adodb-csvlib.inc.php
lib/adodb/adodb-datadict.inc.php
lib/adodb/adodb-error.inc.php
lib/adodb/adodb-errorhandler.inc.php
lib/adodb/adodb-errorpear.inc.php
lib/adodb/adodb-exceptions.inc.php
lib/adodb/adodb-iterator.inc.php
lib/adodb/adodb-lib.inc.php
lib/adodb/adodb-loadbalancer.inc.php [new file with mode: 0644]
lib/adodb/adodb-memcache.lib.inc.php
lib/adodb/adodb-pager.inc.php
lib/adodb/adodb-pear.inc.php
lib/adodb/adodb-perf.inc.php
lib/adodb/adodb-php4.inc.php
lib/adodb/adodb-time.inc.php
lib/adodb/adodb-xmlschema.inc.php
lib/adodb/adodb-xmlschema03.inc.php
lib/adodb/adodb.inc.php
lib/adodb/datadict/datadict-access.inc.php
lib/adodb/datadict/datadict-db2.inc.php
lib/adodb/datadict/datadict-firebird.inc.php
lib/adodb/datadict/datadict-generic.inc.php
lib/adodb/datadict/datadict-ibase.inc.php
lib/adodb/datadict/datadict-informix.inc.php
lib/adodb/datadict/datadict-mssql.inc.php
lib/adodb/datadict/datadict-mssqlnative.inc.php
lib/adodb/datadict/datadict-mysql.inc.php
lib/adodb/datadict/datadict-oci8.inc.php
lib/adodb/datadict/datadict-postgres.inc.php
lib/adodb/datadict/datadict-sapdb.inc.php
lib/adodb/datadict/datadict-sqlite.inc.php
lib/adodb/datadict/datadict-sybase.inc.php
lib/adodb/drivers/adodb-access.inc.php
lib/adodb/drivers/adodb-ado.inc.php
lib/adodb/drivers/adodb-ado5.inc.php
lib/adodb/drivers/adodb-ado_access.inc.php
lib/adodb/drivers/adodb-ado_mssql.inc.php
lib/adodb/drivers/adodb-ads.inc.php
lib/adodb/drivers/adodb-borland_ibase.inc.php
lib/adodb/drivers/adodb-csv.inc.php
lib/adodb/drivers/adodb-db2.inc.php
lib/adodb/drivers/adodb-db2oci.inc.php
lib/adodb/drivers/adodb-db2ora.inc.php
lib/adodb/drivers/adodb-fbsql.inc.php
lib/adodb/drivers/adodb-firebird.inc.php
lib/adodb/drivers/adodb-ibase.inc.php
lib/adodb/drivers/adodb-informix.inc.php
lib/adodb/drivers/adodb-informix72.inc.php
lib/adodb/drivers/adodb-ldap.inc.php
lib/adodb/drivers/adodb-mssql.inc.php
lib/adodb/drivers/adodb-mssql_n.inc.php
lib/adodb/drivers/adodb-mssqlnative.inc.php
lib/adodb/drivers/adodb-mssqlpo.inc.php
lib/adodb/drivers/adodb-mysql.inc.php
lib/adodb/drivers/adodb-mysqli.inc.php
lib/adodb/drivers/adodb-mysqlpo.inc.php
lib/adodb/drivers/adodb-mysqlt.inc.php
lib/adodb/drivers/adodb-netezza.inc.php
lib/adodb/drivers/adodb-oci8.inc.php
lib/adodb/drivers/adodb-oci805.inc.php
lib/adodb/drivers/adodb-oci8po.inc.php
lib/adodb/drivers/adodb-oci8quercus.inc.php
lib/adodb/drivers/adodb-odbc.inc.php
lib/adodb/drivers/adodb-odbc_db2.inc.php
lib/adodb/drivers/adodb-odbc_mssql.inc.php
lib/adodb/drivers/adodb-odbc_mssql2012.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-odbc_oracle.inc.php
lib/adodb/drivers/adodb-odbtp.inc.php
lib/adodb/drivers/adodb-odbtp_unicode.inc.php
lib/adodb/drivers/adodb-oracle.inc.php
lib/adodb/drivers/adodb-pdo.inc.php
lib/adodb/drivers/adodb-pdo_dblib.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-pdo_firebird.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-pdo_mssql.inc.php
lib/adodb/drivers/adodb-pdo_mysql.inc.php
lib/adodb/drivers/adodb-pdo_oci.inc.php
lib/adodb/drivers/adodb-pdo_pgsql.inc.php
lib/adodb/drivers/adodb-pdo_sqlite.inc.php
lib/adodb/drivers/adodb-pdo_sqlsrv.inc.php
lib/adodb/drivers/adodb-postgres.inc.php
lib/adodb/drivers/adodb-postgres64.inc.php
lib/adodb/drivers/adodb-postgres7.inc.php
lib/adodb/drivers/adodb-postgres8.inc.php
lib/adodb/drivers/adodb-postgres9.inc.php
lib/adodb/drivers/adodb-proxy.inc.php
lib/adodb/drivers/adodb-sapdb.inc.php
lib/adodb/drivers/adodb-sqlanywhere.inc.php
lib/adodb/drivers/adodb-sqlite.inc.php
lib/adodb/drivers/adodb-sqlite3.inc.php
lib/adodb/drivers/adodb-sqlitepo.inc.php
lib/adodb/drivers/adodb-sybase.inc.php
lib/adodb/drivers/adodb-sybase_ase.inc.php
lib/adodb/drivers/adodb-text.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-vfp.inc.php
lib/adodb/perf/perf-db2.inc.php
lib/adodb/perf/perf-informix.inc.php
lib/adodb/perf/perf-mssql.inc.php
lib/adodb/perf/perf-mssqlnative.inc.php
lib/adodb/perf/perf-mysql.inc.php
lib/adodb/perf/perf-oci8.inc.php
lib/adodb/perf/perf-postgres.inc.php
lib/adodb/pivottable.inc.php
lib/adodb/readme_moodle.txt
lib/adodb/rsfilter.inc.php
lib/adodb/toexport.inc.php
lib/adodb/tohtml.inc.php
lib/adodb/xmlschema03.dtd
lib/amd/build/modal.min.js
lib/amd/build/modal.min.js.map
lib/amd/build/templates.min.js
lib/amd/build/templates.min.js.map
lib/amd/build/toast.min.js.map
lib/amd/src/modal.js
lib/amd/src/templates.js
lib/amd/src/toast.js
lib/antivirus/clamav/tests/scanner_test.php
lib/authlib.php
lib/badgeslib.php
lib/behat/classes/util.php
lib/behat/form_field/behat_form_date.php
lib/classes/activity_dates.php [new file with mode: 0644]
lib/classes/check/access/riskadmin.php
lib/classes/check/access/riskbackup_result.php
lib/classes/check/access/riskxss_result.php
lib/classes/event/base.php
lib/classes/navigation/views/primary.php [new file with mode: 0644]
lib/classes/navigation/views/secondary.php [new file with mode: 0644]
lib/classes/navigation/views/view.php [new file with mode: 0644]
lib/classes/oauth2/api.php
lib/classes/oauth2/discovery/base_definition.php [new file with mode: 0644]
lib/classes/oauth2/discovery/imsbadgeconnect.php [new file with mode: 0644]
lib/classes/oauth2/discovery/openidconnect.php [new file with mode: 0644]
lib/classes/oauth2/issuer.php
lib/classes/oauth2/service/custom.php [new file with mode: 0644]
lib/classes/oauth2/service/facebook.php [new file with mode: 0644]
lib/classes/oauth2/service/google.php [new file with mode: 0644]
lib/classes/oauth2/service/imsobv2p1.php [new file with mode: 0644]
lib/classes/oauth2/service/issuer_interface.php [new file with mode: 0644]
lib/classes/oauth2/service/microsoft.php [new file with mode: 0644]
lib/classes/oauth2/service/nextcloud.php [new file with mode: 0644]
lib/classes/task/backup_cleanup_task.php
lib/classes/task/file_temp_cleanup_task.php
lib/classes/task/send_failed_login_notifications_task.php
lib/classes/task/send_new_user_passwords_task.php
lib/classes/user.php
lib/completionlib.php
lib/datalib.php
lib/db/caches.php
lib/db/install.xml
lib/db/renamedclasses.php
lib/db/services.php
lib/db/upgrade.php
lib/ddl/tests/ddl_test.php
lib/deprecatedlib.php
lib/dml/tests/dml_read_slave_test.php
lib/dml/tests/dml_test.php
lib/dml/tests/pgsql_native_moodle_database_test.php
lib/dml/tests/pgsql_native_recordset_test.php
lib/editor/atto/plugins/html/thirdpartylibs.xml
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-beautify/moodle-atto_html-beautify-debug.js
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-beautify/moodle-atto_html-beautify-min.js
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-beautify/moodle-atto_html-beautify.js
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-codemirror/moodle-atto_html-codemirror-debug.js
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-codemirror/moodle-atto_html-codemirror-min.js
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-codemirror/moodle-atto_html-codemirror.js
lib/editor/atto/plugins/html/yui/src/beautify/LICENSE
lib/editor/atto/plugins/html/yui/src/beautify/js/beautify-css.js
lib/editor/atto/plugins/html/yui/src/beautify/js/beautify-html.js
lib/editor/atto/plugins/html/yui/src/beautify/js/beautify.js
lib/editor/atto/plugins/html/yui/src/beautify/readme_moodle.txt
lib/editor/atto/plugins/html/yui/src/codemirror/js/codemirror.js
lib/editor/atto/plugins/html/yui/src/codemirror/js/css.js
lib/editor/atto/plugins/html/yui/src/codemirror/js/htmlmixed.js
lib/editor/atto/plugins/html/yui/src/codemirror/js/javascript.js
lib/editor/atto/plugins/html/yui/src/codemirror/js/xml.js
lib/filelib.php
lib/filestorage/file_system_filedir.php
lib/filestorage/tests/file_storage_test.php
lib/filestorage/tests/file_system_filedir_test.php
lib/filestorage/tests/file_system_test.php
lib/filestorage/tests/zip_packer_test.php
lib/form/amd/build/modalform.min.js
lib/form/amd/build/modalform.min.js.map
lib/form/amd/src/modalform.js
lib/form/templates/element-autocomplete-inline.mustache
lib/formslib.php
lib/gdlib.php
lib/grouplib.php
lib/modinfolib.php
lib/moodlelib.php
lib/myprofilelib.php
lib/navigationlib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/pagelib.php
lib/phpunit/bootstrap.php
lib/phpunit/classes/autoloader.php [deleted file]
lib/phpunit/classes/base_testcase.php
lib/phpunit/classes/constraint_object_is_equal_with_exceptions.php
lib/phpunit/classes/coverage_info.php
lib/phpunit/classes/util.php
lib/phpunit/phpunit.xsd
lib/plagiarismlib.php
lib/plist/CODE_OF_CONDUCT.md [new file with mode: 0644]
lib/plist/CONTRIBUTING.md [new file with mode: 0644]
lib/plist/LICENSE.md [moved from lib/plist/LICENSE with 91% similarity]
lib/plist/README.md
lib/plist/classes/CFPropertyList/CFArray.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFBinaryPropertyList.php
lib/plist/classes/CFPropertyList/CFBoolean.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFData.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFDate.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFDictionary.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFNumber.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFPropertyList.php
lib/plist/classes/CFPropertyList/CFString.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/CFType.php
lib/plist/classes/CFPropertyList/CFTypeDetector.php
lib/plist/classes/CFPropertyList/CFUid.php [new file with mode: 0644]
lib/plist/classes/CFPropertyList/IOException.php
lib/plist/classes/CFPropertyList/PListException.php
lib/plist/readme_moodle.txt
lib/rsslib.php
lib/table/amd/build/dynamic.min.js
lib/table/amd/build/dynamic.min.js.map
lib/table/amd/build/local/dynamic/selectors.min.js
lib/table/amd/build/local/dynamic/selectors.min.js.map
lib/table/amd/src/dynamic.js
lib/table/amd/src/local/dynamic/selectors.js
lib/table/tests/coverage.php
lib/table/tests/local/filter/filterset_test.php
lib/tablelib.php
lib/templates/local/toast/message.mustache
lib/testing/generator/data_generator.php
lib/testing/tests/generator_test.php
lib/tests/accesslib_test.php
lib/tests/admintree_test.php
lib/tests/antivirus_test.php
lib/tests/behat/app_behat_runtime.js
lib/tests/behat/behat_transformations.php
lib/tests/behat/datetime_any.feature [new file with mode: 0644]
lib/tests/completionlib_test.php
lib/tests/component_test.php
lib/tests/content/export/exportable_items/exportable_filearea_test.php
lib/tests/content/export/exportable_items/exportable_stored_file_test.php
lib/tests/content/export/exportable_items/exportable_textarea_test.php
lib/tests/content/export/exporters/course_exporter_test.php
lib/tests/core_media_player_native.php
lib/tests/coverage.php
lib/tests/curl_security_helper_test.php
lib/tests/datalib_test.php
lib/tests/event_test.php
lib/tests/filelib_test.php
lib/tests/filter_manager_test.php
lib/tests/filterlib_test.php
lib/tests/grouplib_test.php
lib/tests/h5p_get_content_types_task_test.php
lib/tests/message_test.php
lib/tests/messagelib_test.php
lib/tests/moodle_url_test.php
lib/tests/moodlelib_test.php
lib/tests/navigation/views/primary_test.php [new file with mode: 0644]
lib/tests/navigation/views/secondary_test.php [new file with mode: 0644]
lib/tests/oauth2_test.php
lib/tests/outputcomponents_test.php
lib/tests/plugin_manager_test.php
lib/tests/portfoliolib_test.php
lib/tests/setuplib_test.php
lib/tests/tablelib_test.php
lib/tests/task_database_logger_test.php
lib/tests/theme_config_test.php
lib/tests/user_test.php
lib/tests/weblib_format_text_test.php
lib/tests/weblib_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/upgradelib.php
lib/xapi/tests/coverage.php
lib/yuilib/2in3/2.9.0/build/yui2-treeview/yui2-treeview-debug.js
lib/yuilib/2in3/2.9.0/build/yui2-treeview/yui2-treeview-min.js
lib/yuilib/2in3/2.9.0/build/yui2-treeview/yui2-treeview.js
lib/yuilib/readme_moodle.txt
login/lib.php
login/tests/lib_test.php
media/player/html5audio/tests/player_test.php
media/player/html5video/tests/player_test.php
media/player/swf/tests/player_test.php
media/player/videojs/amd/build/loader.min.js
media/player/videojs/amd/build/loader.min.js.map
media/player/videojs/amd/src/loader.js
media/player/videojs/db/services.php
media/player/videojs/tests/player_test.php
media/player/vimeo/tests/player_test.php
media/player/youtube/tests/player_test.php
message/amd/build/message_drawer_view_conversation_patcher.min.js
message/amd/build/message_drawer_view_conversation_patcher.min.js.map
message/amd/src/message_drawer_view_conversation_patcher.js
message/classes/api.php
message/classes/helper.php
message/externallib.php
message/lib.php
message/output/airnotifier/checkconfiguration.php [new file with mode: 0644]
message/output/airnotifier/classes/manager.php
message/output/airnotifier/lang/en/message_airnotifier.php
message/output/airnotifier/settings.php
message/output/airnotifier/tests/manager_test.php [new file with mode: 0644]
message/output/email/classes/task/send_email_task.php
message/output/email/tests/send_email_task_test.php
message/tests/api_test.php
message/tests/behat/delete_messages.feature
message/tests/behat/message_delete_conversation.feature
message/tests/behat/message_send_messages.feature
message/tests/behat/self_conversation.feature
message/tests/behat/unread_messages.feature
message/tests/externallib_test.php
message/tests/privacy_provider_test.php
mnet/peer.php
mod/assign/classes/completion/custom_completion.php [new file with mode: 0644]
mod/assign/classes/output/grading_app.php
mod/assign/extensionform.php
mod/assign/externallib.php
mod/assign/feedback/editpdf/classes/pdf.php
mod/assign/feedback/file/locallib.php
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/override_form.php
mod/assign/overridedelete.php
mod/assign/overrides.php
mod/assign/renderer.php
mod/assign/submission/comments/tests/privacy_test.php
mod/assign/tests/behat/set_availability.feature [new file with mode: 0644]
mod/assign/tests/custom_completion_test.php [new file with mode: 0644]
mod/assign/tests/externallib_test.php
mod/assign/tests/lib_test.php
mod/assign/tests/locallib_test.php
mod/book/tests/lib_test.php
mod/book/view.php
mod/chat/lib.php
mod/chat/tests/format_message_test.php
mod/chat/view.php
mod/choice/classes/dates.php [new file with mode: 0644]
mod/choice/lib.php
mod/choice/report.php
mod/choice/tests/behat/activity_info_completion_automatic.feature [new file with mode: 0644]
mod/choice/tests/behat/activity_info_completion_manual.feature [new file with mode: 0644]
mod/choice/tests/dates_test.php [new file with mode: 0644]
mod/choice/view.php
mod/data/classes/completion/custom_completion.php [new file with mode: 0644]
mod/data/lang/en/data.php
mod/data/lib.php
mod/data/locallib.php
mod/data/preset.php
mod/data/tests/custom_completion_test.php [new file with mode: 0644]
mod/data/tests/lib_test.php
mod/data/tests/search_test.php
mod/data/view.php
mod/feedback/classes/completion/custom_completion.php [new file with mode: 0644]
mod/feedback/classes/responses_table.php
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/feedback/tests/custom_completion_test.php [new file with mode: 0644]
mod/feedback/view.php
mod/folder/view.php
mod/forum/classes/completion/custom_completion.php [new file with mode: 0644]
mod/forum/classes/form/export_form.php
mod/forum/classes/local/vaults/discussion_list.php
mod/forum/classes/privacy/provider.php
mod/forum/classes/subscriptions.php
mod/forum/deprecatedlib.php
mod/forum/externallib.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/renderer.php
mod/forum/report/summary/classes/summary_table.php
mod/forum/rsslib.php
mod/forum/tests/behat/completion_condition_number_discussions.feature
mod/forum/tests/coverage.php
mod/forum/tests/cron_trait.php
mod/forum/tests/custom_completion_test.php [new file with mode: 0644]
mod/forum/tests/externallib_test.php
mod/forum/tests/lib_test.php
mod/forum/tests/mail_test.php
mod/forum/tests/privacy_provider_test.php
mod/forum/view.php
mod/glossary/classes/completion/custom_completion.php [new file with mode: 0644]
mod/glossary/classes/entry_query_builder.php
mod/glossary/db/services.php
mod/glossary/lang/en/glossary.php
mod/glossary/lib.php
mod/glossary/rsslib.php
mod/glossary/tests/custom_completion_test.php [new file with mode: 0644]
mod/glossary/tests/lib_test.php
mod/glossary/view.php
mod/h5pactivity/db/services.php
mod/h5pactivity/mod_form.php
mod/h5pactivity/tests/behat/attempt_options.feature [new file with mode: 0644]
mod/h5pactivity/tests/behat/result_longfillin.feature
mod/h5pactivity/tests/coverage.php
mod/h5pactivity/tests/privacy_test.php
mod/h5pactivity/view.php
mod/imscp/locallib.php
mod/imscp/view.php
mod/lesson/classes/completion/custom_completion.php [new file with mode: 0644]
mod/lesson/essay.php
mod/lesson/lang/en/lesson.php
mod/lesson/lib.php
mod/lesson/locallib.php
mod/lesson/override_form.php
mod/lesson/overridedelete.php
mod/lesson/overrides.php
mod/lesson/renderer.php
mod/lesson/tests/behat/completion_condition_end_reached.feature
mod/lesson/tests/behat/completion_condition_time_spent.feature
mod/lesson/tests/custom_completion_test.php [new file with mode: 0644]
mod/lti/locallib.php
mod/lti/service.php
mod/lti/tests/behat/lti_activity_completion.feature [new file with mode: 0644]
mod/lti/view.php
mod/page/tests/behat/page_activity_completion.feature [new file with mode: 0644]
mod/page/view.php
mod/quiz/accessrule/seb/tests/access_manager_test.php
mod/quiz/accessrule/seb/tests/quiz_settings_test.php
mod/quiz/accessrule/seb/tests/rule_test.php
mod/quiz/classes/completion/custom_completion.php [new file with mode: 0644]
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/override_form.php
mod/quiz/overridedelete.php
mod/quiz/overrides.php
mod/quiz/renderer.php
mod/quiz/report/attemptsreport.php
mod/quiz/report/attemptsreport_table.php
mod/quiz/report/default.php
mod/quiz/report/grading/report.php
mod/quiz/report/overview/report.php
mod/quiz/tests/behat/attempt_basic.feature
mod/quiz/tests/behat/completion_condition_attempts_used.feature
mod/quiz/tests/behat/completion_condition_minimum_attempts.feature
mod/quiz/tests/behat/completion_condition_passing_grade.feature
mod/quiz/tests/behat/editing_add_from_question_bank.feature
mod/quiz/tests/behat/editing_add_random.feature
mod/quiz/tests/behat/preview.feature
mod/quiz/tests/custom_completion_test.php [new file with mode: 0644]
mod/quiz/tests/lib_test.php
mod/resource/locallib.php
mod/scorm/classes/completion/custom_completion.php [new file with mode: 0644]
mod/scorm/datamodels/scormlib.php
mod/scorm/lang/en/scorm.php
mod/scorm/report/basic/classes/report.php
mod/scorm/report/interactions/classes/report.php
mod/scorm/report/objectives/classes/report.php
mod/scorm/report/userreport.php
mod/scorm/report/userreportinteractions.php
mod/scorm/report/userreporttracks.php
mod/scorm/tests/custom_completion_test.php [new file with mode: 0644]
mod/scorm/tests/privacy_test.php
mod/scorm/view.php
mod/survey/classes/completion/custom_completion.php [new file with mode: 0644]
mod/survey/lang/en/survey.php
mod/survey/lib.php
mod/survey/tests/behat/survey_completion.feature
mod/survey/tests/custom_completion_test.php [new file with mode: 0644]
mod/survey/view.php
mod/url/locallib.php
mod/url/tests/behat/url_activity_completion.feature [new file with mode: 0644]
mod/wiki/renderer.php
mod/wiki/tests/behat/wiki_activity_completion.feature [new file with mode: 0644]
mod/wiki/tests/lib_test.php
mod/wiki/tests/wikiparser_test.php
mod/workshop/allocation/manual/lib.php
mod/workshop/lang/en/workshop.php
mod/workshop/lib.php
mod/workshop/locallib.php
mod/workshop/renderer.php
mod/workshop/tests/behat/workshop_activity_completion.feature [new file with mode: 0644]
mod/workshop/tests/behat/workshop_assessment.feature
mod/workshop/view.php
payment/gateway/paypal/db/services.php
phpunit.xml.dist
plagiarism/upgrade.txt
privacy/classes/tests/request/content_writer.php
privacy/tests/contextlist_test.php
privacy/tests/coverage.php
privacy/tests/manager_test.php
privacy/tests/moodle_content_writer_test.php
privacy/tests/sitepolicy_test.php
privacy/tests/tests_content_writer_test.php
question/behaviour/adaptive/tests/walkthrough_test.php
question/behaviour/adaptivenopenalty/tests/walkthrough_test.php
question/behaviour/deferredcbm/tests/walkthrough_test.php
question/behaviour/deferredfeedback/tests/walkthrough_test.php
question/behaviour/immediatecbm/tests/walkthrough_test.php
question/behaviour/immediatefeedback/tests/walkthrough_test.php
question/behaviour/missing/tests/missingbehaviour_test.php
question/classes/bank/creator_name_column.php
question/classes/bank/modifier_name_column.php
question/classes/bank/search/category_condition.php
question/engine/tests/helpers.php
question/engine/tests/questionattempt_test.php
question/format/xml/tests/xmlformat_test.php
question/question.php
question/type/ddwtos/tests/behat/add.feature
question/type/edit_question_form.php
question/type/essay/question.php
question/type/essay/renderer.php
question/type/essay/tests/question_test.php
question/type/essay/tests/walkthrough_test.php
question/type/gapselect/edit_form_base.php
question/type/gapselect/questiontypebase.php
question/type/gapselect/tests/behat/add.feature [new file with mode: 0644]
question/type/gapselect/tests/edit_form_test.php
question/type/match/tests/question_test.php
question/type/missingtype/tests/missingtype_test.php
question/type/multianswer/tests/walkthrough_test.php
question/type/multichoice/edit_multichoice_form.php
question/type/multichoice/questiontype.php
question/type/multichoice/tests/behat/add.feature
question/type/multichoice/tests/question_multi_test.php
question/type/questiontypebase.php
question/type/random/tests/questiontype_test.php
question/type/randomsamatch/tests/question_test.php
question/type/shortanswer/edit_shortanswer_form.php
question/type/shortanswer/questiontype.php
question/type/shortanswer/tests/behat/add.feature
question/type/truefalse/tests/walkthrough_test.php
question/type/upgrade.txt
rating/classes/external.php
rating/index.php
rating/lib.php
report/competency/lib.php
report/completion/index.php
report/configlog/classes/output/report_table.php
report/insights/classes/output/insight.php
report/log/classes/renderable.php
report/log/classes/table_log.php
report/log/locallib.php
report/loglive/classes/table_log.php
report/participation/index.php
report/progress/index.php
report/progress/tests/behat/activity_completion_report.feature
report/stats/locallib.php
repository/dropbox/tests/api_test.php
repository/flickr_public/lib.php
repository/googledocs/classes/googledocs_content.php [new file with mode: 0644]
repository/googledocs/classes/googledocs_content_search.php [new file with mode: 0644]
repository/googledocs/classes/helper.php [new file with mode: 0644]
repository/googledocs/classes/local/browser/googledocs_drive_content.php [new file with mode: 0644]
repository/googledocs/classes/local/browser/googledocs_root_content.php [new file with mode: 0644]
repository/googledocs/classes/local/browser/googledocs_shared_drives_content.php [new file with mode: 0644]
repository/googledocs/classes/local/node/file_node.php [new file with mode: 0644]
repository/googledocs/classes/local/node/folder_node.php [new file with mode: 0644]
repository/googledocs/classes/local/node/node.php [new file with mode: 0644]
repository/googledocs/classes/rest.php
repository/googledocs/lang/en/repository_googledocs.php
repository/googledocs/lib.php
repository/googledocs/tests/googledocs_content_testcase.php [new file with mode: 0644]
repository/googledocs/tests/googledocs_search_content_test.php [new file with mode: 0644]
repository/googledocs/tests/helper_test.php [new file with mode: 0644]
repository/googledocs/tests/local/browser/googledocs_drive_content_test.php [new file with mode: 0644]
repository/googledocs/tests/local/browser/googledocs_root_content_test.php [new file with mode: 0644]
repository/googledocs/tests/local/browser/googledocs_shared_drives_content_test.php [new file with mode: 0644]
repository/googledocs/tests/local/node/file_node_test.php [new file with mode: 0644]
repository/googledocs/tests/local/node/folder_node_test.php [new file with mode: 0644]
repository/googledocs/tests/repository_googledocs_testcase.php [new file with mode: 0644]
repository/googledocs/version.php
repository/upgrade.txt
search/classes/engine.php
search/engine/solr/tests/engine_test.php
search/tests/area_category_test.php
search/tests/base_activity_test.php
search/tests/base_test.php
security.txt
tag/classes/manage_table.php
theme/boost/amd/build/bootstrap/alert.min.js
theme/boost/amd/build/bootstrap/alert.min.js.map
theme/boost/amd/build/bootstrap/button.min.js
theme/boost/amd/build/bootstrap/button.min.js.map
theme/boost/amd/build/bootstrap/carousel.min.js
theme/boost/amd/build/bootstrap/carousel.min.js.map
theme/boost/amd/build/bootstrap/collapse.min.js
theme/boost/amd/build/bootstrap/collapse.min.js.map
theme/boost/amd/build/bootstrap/dropdown.min.js
theme/boost/amd/build/bootstrap/dropdown.min.js.map
theme/boost/amd/build/bootstrap/index.min.js [deleted file]
theme/boost/amd/build/bootstrap/index.min.js.map [deleted file]
theme/boost/amd/build/bootstrap/modal.min.js
theme/boost/amd/build/bootstrap/modal.min.js.map
theme/boost/amd/build/bootstrap/popover.min.js
theme/boost/amd/build/bootstrap/popover.min.js.map
theme/boost/amd/build/bootstrap/scrollspy.min.js
theme/boost/amd/build/bootstrap/scrollspy.min.js.map
theme/boost/amd/build/bootstrap/tab.min.js
theme/boost/amd/build/bootstrap/tab.min.js.map
theme/boost/amd/build/bootstrap/toast.min.js
theme/boost/amd/build/bootstrap/toast.min.js.map
theme/boost/amd/build/bootstrap/tools/sanitizer.min.js.map
theme/boost/amd/build/bootstrap/tooltip.min.js
theme/boost/amd/build/bootstrap/tooltip.min.js.map
theme/boost/amd/build/bootstrap/util.min.js.map
theme/boost/amd/build/index.min.js [new file with mode: 0644]
theme/boost/amd/build/index.min.js.map [new file with mode: 0644]
theme/boost/amd/build/loader.min.js
theme/boost/amd/build/loader.min.js.map
theme/boost/amd/src/bootstrap/alert.js
theme/boost/amd/src/bootstrap/button.js
theme/boost/amd/src/bootstrap/carousel.js
theme/boost/amd/src/bootstrap/collapse.js
theme/boost/amd/src/bootstrap/dropdown.js
theme/boost/amd/src/bootstrap/index.js [deleted file]
theme/boost/amd/src/bootstrap/modal.js
theme/boost/amd/src/bootstrap/popover.js
theme/boost/amd/src/bootstrap/scrollspy.js
theme/boost/amd/src/bootstrap/tab.js
theme/boost/amd/src/bootstrap/toast.js
theme/boost/amd/src/bootstrap/tools/sanitizer.js
theme/boost/amd/src/bootstrap/tooltip.js
theme/boost/amd/src/bootstrap/util.js
theme/boost/amd/src/index.js [new file with mode: 0644]
theme/boost/amd/src/loader.js
theme/boost/readme_moodle.txt
theme/boost/scss/bootstrap/_alert.scss
theme/boost/scss/bootstrap/_breadcrumb.scss
theme/boost/scss/bootstrap/_card.scss
theme/boost/scss/bootstrap/_carousel.scss
theme/boost/scss/bootstrap/_custom-forms.scss
theme/boost/scss/bootstrap/_dropdown.scss
theme/boost/scss/bootstrap/_functions.scss
theme/boost/scss/bootstrap/_grid.scss
theme/boost/scss/bootstrap/_input-group.scss
theme/boost/scss/bootstrap/_list-group.scss
theme/boost/scss/bootstrap/_modal.scss
theme/boost/scss/bootstrap/_nav.scss
theme/boost/scss/bootstrap/_navbar.scss
theme/boost/scss/bootstrap/_pagination.scss
theme/boost/scss/bootstrap/_progress.scss
theme/boost/scss/bootstrap/_reboot.scss
theme/boost/scss/bootstrap/_root.scss
theme/boost/scss/bootstrap/_spinners.scss
theme/boost/scss/bootstrap/_toasts.scss
theme/boost/scss/bootstrap/_type.scss
theme/boost/scss/bootstrap/_variables.scss
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/_border-radius.scss
theme/boost/scss/bootstrap/mixins/_forms.scss
theme/boost/scss/bootstrap/mixins/_grid-framework.scss
theme/boost/scss/bootstrap/mixins/_grid.scss
theme/boost/scss/bootstrap/mixins/_image.scss
theme/boost/scss/bootstrap/mixins/_screen-reader.scss
theme/boost/scss/bootstrap/mixins/_transition.scss
theme/boost/scss/bootstrap/utilities/_borders.scss
theme/boost/scss/bootstrap/utilities/_text.scss
theme/boost/scss/moodle.scss
theme/boost/scss/moodle/contentbank.scss
theme/boost/scss/moodle/course.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/tables.scss
theme/boost/scss/moodle/toasts.scss [new file with mode: 0644]
theme/boost/scss/preset/default.scss
theme/boost/style/moodle.css
theme/boost/thirdpartylibs.xml
theme/classic/scss/preset/default.scss
theme/classic/style/moodle.css
user/action_redir.php
user/amd/build/form_user_selector.min.js [new file with mode: 0644]
user/amd/build/form_user_selector.min.js.map [new file with mode: 0644]
user/amd/build/local/participantsfilter/filtertypes/country.min.js [new file with mode: 0644]
user/amd/build/local/participantsfilter/filtertypes/country.min.js.map [new file with mode: 0644]
user/amd/build/participants.min.js
user/amd/build/participants.min.js.map
user/amd/src/form_user_selector.js [new file with mode: 0644]
user/amd/src/local/participantsfilter/filtertypes/country.js [new file with mode: 0644]
user/amd/src/participants.js
user/classes/external/search_identity.php [new file with mode: 0644]
user/classes/external/user_summary_exporter.php
user/classes/fields.php [moved from lib/classes/user_fields.php with 93% similarity]
user/classes/output/participants_filter.php
user/classes/search/user.php
user/classes/table/participants.php
user/classes/table/participants_filterset.php
user/classes/table/participants_search.php
user/editlib.php
user/index.php
user/lib.php
user/selector/lib.php
user/templates/form_user_selector_suggestion.mustache [new file with mode: 0644]
user/tests/behat/filter_participants.feature
user/tests/behat/view_full_profile.feature
user/tests/externallib_test.php
user/tests/fields_test.php [moved from lib/tests/user_fields_test.php with 84% similarity]
user/tests/table/participants_search_test.php
user/upgrade.txt
userpix/index.php
version.php
webservice/classes/token_filter.php [new file with mode: 0644]
webservice/classes/token_form.php [new file with mode: 0644]
webservice/classes/token_table.php
webservice/lib.php
webservice/renderer.php
webservice/tests/generator/behat_core_webservice_generator.php [new file with mode: 0644]
webservice/tests/generator/lib.php [new file with mode: 0644]
webservice/tests/lib_test.php
webservice/upgrade.txt
webservice/xmlrpc/tests/locallib_test.php
webservice/xmlrpc/tests/xmlrpc_server_test.php

index 0d0a6ea..2262b1f 100644 (file)
@@ -1,4 +1,5 @@
 # Generated by "grunt ignorefiles"
+!/.grunt
 */**/yui/src/*/meta/
 */**/build/
 node_modules/
@@ -83,7 +84,6 @@ theme/boost/amd/src/bootstrap/button.js
 theme/boost/amd/src/bootstrap/carousel.js
 theme/boost/amd/src/bootstrap/collapse.js
 theme/boost/amd/src/bootstrap/dropdown.js
-theme/boost/amd/src/bootstrap/index.js
 theme/boost/amd/src/bootstrap/modal.js
 theme/boost/amd/src/bootstrap/popover.js
 theme/boost/amd/src/bootstrap/tools/sanitizer.js
@@ -92,4 +92,5 @@ theme/boost/amd/src/bootstrap/tab.js
 theme/boost/amd/src/bootstrap/toast.js
 theme/boost/amd/src/bootstrap/tooltip.js
 theme/boost/amd/src/bootstrap/util.js
-theme/boost/scss/fontawesome/
\ No newline at end of file
+theme/boost/amd/src/index.js
+theme/boost/scss/fontawesome/
index e44591a..0dce8d8 100644 (file)
--- a/.eslintrc
+++ b/.eslintrc
       }
     },
     {
-      files: ["**/amd/src/*.js", "**/amd/src/**/*.js", "Gruntfile*.js", "babel-plugin-add-module-to-define.js"],
+      files: ["**/amd/src/*.js", "**/amd/src/**/*.js", "Gruntfile.js", ".grunt/*.js", ".grunt/tasks/*.js"],
       // We support es6 now. Woot!
       env: {
         es6: true
index 35fd4a7..0c4f1ba 100644 (file)
@@ -1,6 +1,12 @@
 name: Core
 
-on: [push]
+on:
+  push:
+    branches-ignore:
+      - master
+      - MOODLE_[0-9]+_STABLE
+    tags-ignore:
+      - v[0-9]+.[0-9]+.[0-9]+*
 
 env:
   php: 7.4
index 41e19ac..8bc6f60 100644 (file)
@@ -46,3 +46,4 @@ composer.phar
 atlassian-ide-plugin.xml
 /node_modules/
 /.vscode/
+moodle-plugin-ci.phar
similarity index 98%
rename from babel-plugin-add-module-to-define.js
rename to .grunt/babel-plugin-add-module-to-define.js
index dfe68c6..2e76d31 100644 (file)
@@ -39,7 +39,7 @@ module.exports = ({template, types}) => {
     const fs = require('fs');
     const path = require('path');
     const cwd = process.cwd();
-    const ComponentList = require(path.resolve('GruntfileComponents.js'));
+    const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
 
     /**
      * Search the list of components that match the given file name
similarity index 81%
rename from GruntfileComponents.js
rename to .grunt/components.js
index 74bd9de..848632d 100644 (file)
@@ -92,6 +92,16 @@ const fetchComponentData = () => {
     return componentData;
 };
 
+/**
+ * Get the list of component paths.
+ *
+ * @param   {string} relativeTo
+ * @returns {array}
+ */
+const getComponentPaths = (relativeTo = '') => fetchComponentData().pathList.map(componentPath => {
+    return componentPath.replace(relativeTo, '');
+});
+
 /**
  * Get the list of paths to build AMD sources.
  *
@@ -140,6 +150,49 @@ const getThirdPartyLibsList = relativeTo => {
         .sort();
 };
 
+/**
+ * Get the list of thirdparty library paths.
+ *
+ * @returns {array}
+ */
+const getThirdPartyPaths = () => {
+    const DOMParser = require('xmldom').DOMParser;
+    const fs = require('fs');
+    const path = require('path');
+    const xpath = require('xpath');
+
+    const thirdpartyfiles = getThirdPartyLibsList(fs.realpathSync('./'));
+    const libs = ['node_modules/', 'vendor/'];
+
+    const addLibToList = lib => {
+        if (!lib.match('\\*') && fs.statSync(lib).isDirectory()) {
+            // Ensure trailing slash on dirs.
+            lib = lib.replace(/\/?$/, '/');
+        }
+
+        // Look for duplicate paths before adding to array.
+        if (libs.indexOf(lib) === -1) {
+            libs.push(lib);
+        }
+    };
+
+    thirdpartyfiles.forEach(function(file) {
+        const dirname = path.dirname(file);
+
+        const xmlContent = fs.readFileSync(file, 'utf8');
+        const doc = new DOMParser().parseFromString(xmlContent);
+        const nodes = xpath.select("/libraries/library/location/text()", doc);
+
+        nodes.forEach(function(node) {
+            let lib = path.posix.join(dirname, node.toString());
+            addLibToList(lib);
+        });
+    });
+
+    return libs;
+
+};
+
 /**
  * Find the name of the component matching the specified path.
  *
@@ -182,7 +235,9 @@ const getOwningComponentDirectory = checkPath => {
 module.exports = {
     getAmdSrcGlobList,
     getComponentFromPath,
+    getComponentPaths,
     getOwningComponentDirectory,
     getYuiSrcGlobList,
     getThirdPartyLibsList,
+    getThirdPartyPaths,
 };
diff --git a/.grunt/tasks/eslint.js b/.grunt/tasks/eslint.js
new file mode 100644 (file)
index 0000000..b7408ac
--- /dev/null
@@ -0,0 +1,64 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+    const files = grunt.moodleEnv.files;
+
+    // Project configuration.
+    grunt.config.merge({
+        eslint: {
+            // Even though warnings dont stop the build we don't display warnings by default because
+            // at this moment we've got too many core warnings.
+            // To display warnings call: grunt eslint --show-lint-warnings
+            // To fail on warnings call: grunt eslint --max-lint-warnings=0
+            // Also --max-lint-warnings=-1 can be used to display warnings but not fail.
+            options: {
+                quiet: (!grunt.option('show-lint-warnings')) && (typeof grunt.option('max-lint-warnings') === 'undefined'),
+                maxWarnings: ((typeof grunt.option('max-lint-warnings') !== 'undefined') ? grunt.option('max-lint-warnings') : -1)
+            },
+
+            // Check AMD src files.
+            amd: {src: files ? files : grunt.moodleEnv.amdSrc},
+
+            // Check YUI module source files.
+            yui: {src: files ? files : grunt.moodleEnv.yuiSrc},
+        },
+    });
+
+    grunt.loadNpmTasks('grunt-eslint');
+
+    // On watch, we dynamically modify config to build only affected files. This
+    // method is slightly complicated to deal with multiple changed files at once (copied
+    // from the grunt-contrib-watch readme).
+    let changedFiles = Object.create(null);
+    const onChange = grunt.util._.debounce(function() {
+        const files = Object.keys(changedFiles);
+        grunt.config('eslint.amd.src', files);
+        grunt.config('eslint.yui.src', files);
+        changedFiles = Object.create(null);
+    }, 200);
+
+    grunt.event.on('watch', (action, filepath) => {
+        changedFiles[filepath] = action;
+        onChange();
+    });
+};
diff --git a/.grunt/tasks/gherkinlint.js b/.grunt/tasks/gherkinlint.js
new file mode 100644 (file)
index 0000000..cc269a5
--- /dev/null
@@ -0,0 +1,89 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+    /**
+     * Get the list of feature files to pass to the gherkin linter.
+     *
+     * @returns {Array}
+     */
+    const getGherkinLintTargets = () => {
+        if (grunt.moodleEnv.files) {
+            // Specific files were requested. Only check these.
+            return grunt.moodleEnv.files;
+        }
+
+        if (grunt.moodleEnv.inComponent) {
+            return [`${grunt.moodleEnv.runDir}/tests/behat/*.feature`];
+        }
+
+        return ['**/tests/behat/*.feature'];
+    };
+
+    const handler = function() {
+        const done = this.async();
+        const options = grunt.config('gherkinlint.options');
+
+        // Grab the gherkin-lint linter and required scaffolding.
+        const linter = require('gherkin-lint/dist/linter.js');
+        const featureFinder = require('gherkin-lint/dist/feature-finder.js');
+        const configParser = require('gherkin-lint/dist/config-parser.js');
+        const formatter = require('gherkin-lint/dist/formatters/stylish.js');
+
+        // Run the linter.
+        return linter.lint(
+            featureFinder.getFeatureFiles(grunt.file.expand(options.files)),
+            configParser.getConfiguration(configParser.defaultConfigFileName)
+        )
+        .then(results => {
+            // Print the results out uncondtionally.
+            formatter.printResults(results);
+
+            return results;
+        })
+        .then(results => {
+            // Report on the results.
+            // The done function takes a bool whereby a falsey statement causes the task to fail.
+            return results.every(result => result.errors.length === 0);
+        })
+        .then(done); // eslint-disable-line promise/no-callback-in-promise
+    };
+
+    grunt.registerTask('gherkinlint', 'Run gherkinlint against the current directory', handler);
+
+    grunt.config.set('gherkinlint', {
+        options: {
+            files: getGherkinLintTargets(),
+        }
+    });
+
+    grunt.config.merge({
+        watch: {
+            gherkinlint: {
+                files: [grunt.moodleEnv.inComponent ? 'tests/behat/*.feature' : '**/tests/behat/*.feature'],
+                tasks: ['gherkinlint'],
+            },
+        },
+    });
+
+    return handler;
+};
diff --git a/.grunt/tasks/ignorefiles.js b/.grunt/tasks/ignorefiles.js
new file mode 100644 (file)
index 0000000..d8b9ec1
--- /dev/null
@@ -0,0 +1,59 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+    /**
+     * Generate ignore files (utilising thirdpartylibs.xml data)
+     */
+    const handler = function() {
+        const path = require('path');
+        const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
+
+        // An array of paths to third party directories.
+        const thirdPartyPaths = ComponentList.getThirdPartyPaths();
+
+        // Generate .eslintignore.
+        const eslintIgnores = [
+            '# Generated by "grunt ignorefiles"',
+            // Do not ignore the .grunt directory.
+            '!/.grunt',
+
+            // Ignore all yui/src meta directories and build directories.
+            '*/**/yui/src/*/meta/',
+            '*/**/build/',
+        ].concat(thirdPartyPaths);
+        grunt.file.write('.eslintignore', eslintIgnores.join('\n'));
+
+        // Generate .stylelintignore.
+        const stylelintIgnores = [
+            '# Generated by "grunt ignorefiles"',
+            '**/yui/build/*',
+            'theme/boost/style/moodle.css',
+            'theme/classic/style/moodle.css',
+        ].concat(thirdPartyPaths);
+        grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
+    };
+
+    grunt.registerTask('ignorefiles', 'Generate ignore files for linters', handler);
+
+    return handler;
+};
diff --git a/.grunt/tasks/javascript.js b/.grunt/tasks/javascript.js
new file mode 100644 (file)
index 0000000..4cc309c
--- /dev/null
@@ -0,0 +1,141 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Function to generate the destination for the uglify task
+ * (e.g. build/file.min.js). This function will be passed to
+ * the rename property of files array when building dynamically:
+ * http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
+ *
+ * @param {String} destPath the current destination
+ * @param {String} srcPath the  matched src path
+ * @return {String} The rewritten destination path.
+ */
+const babelRename = function(destPath, srcPath) {
+    destPath = srcPath.replace('src', 'build');
+    destPath = destPath.replace('.js', '.min.js');
+    return destPath;
+};
+
+module.exports = grunt => {
+    // Load the Shifter tasks.
+    require('./shifter')(grunt);
+
+    // Load ESLint.
+    require('./eslint')(grunt);
+
+    const path = require('path');
+
+    // Register JS tasks.
+    grunt.registerTask('yui', ['eslint:yui', 'shifter']);
+    grunt.registerTask('amd', ['eslint:amd', 'babel']);
+    grunt.registerTask('js', ['amd', 'yui']);
+
+    // Register NPM tasks.
+    grunt.loadNpmTasks('grunt-contrib-uglify');
+    grunt.loadNpmTasks('grunt-contrib-watch');
+
+    // Load the Babel tasks and config.
+    grunt.loadNpmTasks('grunt-babel');
+    grunt.config.merge({
+        babel: {
+            options: {
+                sourceMaps: true,
+                comments: false,
+                plugins: [
+                    'transform-es2015-modules-amd-lazy',
+                    'system-import-transformer',
+                    // This plugin modifies the Babel transpiling for "export default"
+                    // so that if it's used then only the exported value is returned
+                    // by the generated AMD module.
+                    //
+                    // It also adds the Moodle plugin name to the AMD module definition
+                    // so that it can be imported as expected in other modules.
+                    path.resolve('.grunt/babel-plugin-add-module-to-define.js'),
+                    '@babel/plugin-syntax-dynamic-import',
+                    '@babel/plugin-syntax-import-meta',
+                    ['@babel/plugin-proposal-class-properties', {'loose': false}],
+                    '@babel/plugin-proposal-json-strings'
+                ],
+                presets: [
+                    ['minify', {
+                        // This minification plugin needs to be disabled because it breaks the
+                        // source map generation and causes invalid source maps to be output.
+                        simplify: false,
+                        builtIns: false
+                    }],
+                    ['@babel/preset-env', {
+                        targets: {
+                            browsers: [
+                                ">0.25%",
+                                "last 2 versions",
+                                "not ie <= 10",
+                                "not op_mini all",
+                                "not Opera > 0",
+                                "not dead"
+                            ]
+                        },
+                        modules: false,
+                        useBuiltIns: false
+                    }]
+                ]
+            },
+            dist: {
+                files: [{
+                    expand: true,
+                    src: grunt.moodleEnv.files ? grunt.moodleEnv.files : grunt.moodleEnv.amdSrc,
+                    rename: babelRename
+                }]
+            }
+        },
+    });
+
+    grunt.config.merge({
+        watch: {
+            amd: {
+                files: grunt.moodleEnv.inComponent
+                    ? ['amd/src/*.js', 'amd/src/**/*.js']
+                    : ['**/amd/src/**/*.js'],
+                tasks: ['amd']
+            },
+        },
+    });
+
+    // On watch, we dynamically modify config to build only affected files. This
+    // method is slightly complicated to deal with multiple changed files at once (copied
+    // from the grunt-contrib-watch readme).
+    let changedFiles = Object.create(null);
+    const onChange = grunt.util._.debounce(function() {
+        const files = Object.keys(changedFiles);
+        grunt.config('babel.dist.files', [{expand: true, src: files, rename: babelRename}]);
+        changedFiles = Object.create(null);
+    }, 200);
+
+    grunt.event.on('watch', function(action, filepath) {
+        changedFiles[filepath] = action;
+        onChange();
+    });
+
+    return {
+        babelRename,
+    };
+};
diff --git a/.grunt/tasks/sass.js b/.grunt/tasks/sass.js
new file mode 100644 (file)
index 0000000..cdb8bb1
--- /dev/null
@@ -0,0 +1,40 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+    grunt.loadNpmTasks('grunt-sass');
+
+    grunt.config.merge({
+        sass: {
+            dist: {
+                files: {
+                    "theme/boost/style/moodle.css": "theme/boost/scss/preset/default.scss",
+                    "theme/classic/style/moodle.css": "theme/classic/scss/classicgrunt.scss"
+                }
+            },
+            options: {
+                implementation: require('node-sass'),
+                includePaths: ["theme/boost/scss/", "theme/classic/scss/"]
+            }
+        },
+    });
+};
diff --git a/.grunt/tasks/shifter.js b/.grunt/tasks/shifter.js
new file mode 100644 (file)
index 0000000..b364af5
--- /dev/null
@@ -0,0 +1,155 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/* eslint-env node */
+
+module.exports = grunt => {
+    /**
+     * Shifter task. Is configured with a path to a specific file or a directory,
+     * in the case of a specific file it will work out the right module to be built.
+     *
+     * Note that this task runs the invidiaul shifter jobs async (becase it spawns
+     * so be careful to to call done().
+     */
+    const handler = function() {
+        const done = this.async();
+        const options = grunt.config('shifter.options');
+        const async = require('async');
+        const path = require('path');
+
+        // Run the shifter processes one at a time to avoid confusing output.
+        async.eachSeries(options.paths, function(src, filedone) {
+            var args = [];
+            args.push(path.normalize(process.cwd() + '/node_modules/shifter/bin/shifter'));
+
+            // Always ignore the node_modules directory.
+            args.push('--excludes', 'node_modules');
+
+            // Determine the most appropriate options to run with based upon the current location.
+            if (grunt.file.isMatch('**/yui/**/*.js', src)) {
+                // When passed a JS file, build our containing module (this happen with
+                // watch).
+                grunt.log.debug('Shifter passed a specific JS file');
+                src = path.dirname(path.dirname(src));
+                options.recursive = false;
+            } else if (grunt.file.isMatch('**/yui/src', src)) {
+                // When in a src directory --walk all modules.
+                grunt.log.debug('In a src directory');
+                args.push('--walk');
+                options.recursive = false;
+            } else if (grunt.file.isMatch('**/yui/src/*', src)) {
+                // When in module, only build our module.
+                grunt.log.debug('In a module directory');
+                options.recursive = false;
+            } else if (grunt.file.isMatch('**/yui/src/*/js', src)) {
+                // When in module src, only build our module.
+                grunt.log.debug('In a source directory');
+                src = path.dirname(src);
+                options.recursive = false;
+            }
+
+            if (grunt.option('watch')) {
+                grunt.fail.fatal('The --watch option has been removed, please use `grunt watch` instead');
+            }
+
+            // Add the stderr option if appropriate
+            if (grunt.option('verbose')) {
+                args.push('--lint-stderr');
+            }
+
+            if (grunt.option('no-color')) {
+                args.push('--color=false');
+            }
+
+            var execShifter = function() {
+
+                grunt.log.ok("Running shifter on " + src);
+                grunt.util.spawn({
+                    cmd: "node",
+                    args: args,
+                    opts: {cwd: src, stdio: 'inherit', env: process.env}
+                }, function(error, result, code) {
+                    if (code) {
+                        grunt.fail.fatal('Shifter failed with code: ' + code);
+                    } else {
+                        grunt.log.ok('Shifter build complete.');
+                        filedone();
+                    }
+                });
+            };
+
+            // Actually run shifter.
+            if (!options.recursive) {
+                execShifter();
+            } else {
+                // Check that there are yui modules otherwise shifter ends with exit code 1.
+                if (grunt.file.expand({cwd: src}, '**/yui/src/**/*.js').length > 0) {
+                    args.push('--recursive');
+                    execShifter();
+                } else {
+                    grunt.log.ok('No YUI modules to build.');
+                    filedone();
+                }
+            }
+        }, done);
+    };
+
+    // Register the shifter task.
+    grunt.registerTask('shifter', 'Run Shifter against the current directory', handler);
+
+    // Configure it.
+    grunt.config.set('shifter', {
+        options: {
+            recursive: true,
+            // Shifter takes a relative path.
+            paths: grunt.moodleEnv.files ? grunt.moodleEnv.files : [grunt.moodleEnv.runDir]
+        }
+    });
+
+    grunt.config.merge({
+        watch: {
+            yui: {
+                files: grunt.moodleEnv.inComponent
+                    ? ['yui/src/*.json', 'yui/src/**/*.js']
+                    : ['**/yui/src/**/*.js'],
+                tasks: ['yui']
+            },
+        },
+    });
+
+    // On watch, we dynamically modify config to build only affected files. This
+    // method is slightly complicated to deal with multiple changed files at once (copied
+    // from the grunt-contrib-watch readme).
+    let changedFiles = Object.create(null);
+    const onChange = grunt.util._.debounce(function() {
+        const files = Object.keys(changedFiles);
+        grunt.config('shifter.options.paths', files);
+        changedFiles = Object.create(null);
+    }, 200);
+
+    grunt.event.on('watch', (action, filepath) => {
+        changedFiles[filepath] = action;
+        onChange();
+    });
+
+    return handler;
+};
diff --git a/.grunt/tasks/startup.js b/.grunt/tasks/startup.js
new file mode 100644 (file)
index 0000000..b532852
--- /dev/null
@@ -0,0 +1,48 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+    /**
+     * Generate ignore files (utilising thirdpartylibs.xml data)
+     */
+    const handler = function() {
+        const path = require('path');
+
+        // Are we in a YUI directory?
+        if (path.basename(path.resolve(grunt.moodleEnv.cwd, '../../')) == 'yui') {
+            grunt.task.run('yui');
+        // Are we in an AMD directory?
+        } else if (grunt.moodleEnv.inAMD) {
+            grunt.task.run('amd');
+        } else {
+            // Run them all!.
+            grunt.task.run('css');
+            grunt.task.run('js');
+            grunt.task.run('gherkinlint');
+        }
+    };
+
+    // Register the startup task.
+    grunt.registerTask('startup', 'Run the correct tasks for the current directory', handler);
+
+    return handler;
+};
diff --git a/.grunt/tasks/style.js b/.grunt/tasks/style.js
new file mode 100644 (file)
index 0000000..05f713c
--- /dev/null
@@ -0,0 +1,29 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+    // Load the Style Lint tasks.
+    require('./stylelint')(grunt);
+
+    // Load the SASS tasks.
+    require('./sass')(grunt);
+};
diff --git a/.grunt/tasks/stylelint.js b/.grunt/tasks/stylelint.js
new file mode 100644 (file)
index 0000000..4da9067
--- /dev/null
@@ -0,0 +1,181 @@
+// 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/>.
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright  2021 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+module.exports = grunt => {
+
+    const getCssConfigForFiles = files => {
+        return {
+            stylelint: {
+                css: {
+                    // Use a fully-qualified path.
+                    src: files,
+                    options: {
+                        configOverrides: {
+                            rules: {
+                                // These rules have to be disabled in .stylelintrc for scss compat.
+                                "at-rule-no-unknown": true,
+                            }
+                        }
+                    }
+                },
+            },
+        };
+    };
+
+    const getScssConfigForFiles = files => {
+        return {
+            stylelint: {
+                scss: {
+                    options: {syntax: 'scss'},
+                    src: files,
+                },
+            },
+        };
+    };
+
+    /**
+     * Register any stylelint tasks.
+     *
+     * @param {Object} grunt
+     * @param {Array} files
+     * @param {String} fullRunDir
+     */
+    const registerStyleLintTasks = () => {
+        const glob = require('glob');
+
+        // The stylelinters do not handle the case where a configuration was provided but no files were included.
+        // Keep track of whether any files were found.
+        let hasCss = false;
+        let hasScss = false;
+
+        // The stylelint processors do not take a path argument. They always check all provided values.
+        // As a result we must check through each glob and determine if any files match the current directory.
+        const scssFiles = [];
+        const cssFiles = [];
+
+        const requestedFiles = grunt.moodleEnv.files;
+        if (requestedFiles) {
+            // Grunt was called with a files argument.
+            // Check whether each of the requested files matches either the CSS or SCSS source file list.
+
+            requestedFiles.forEach(changedFilePath => {
+                let matchesGlob;
+
+                // Check whether this watched path matches any watched SCSS file.
+                matchesGlob = grunt.moodleEnv.scssSrc.some(watchedPathGlob => {
+                    return glob.sync(watchedPathGlob).indexOf(changedFilePath) !== -1;
+                });
+                if (matchesGlob) {
+                    scssFiles.push(changedFilePath);
+                    hasScss = true;
+                }
+
+                // Check whether this watched path matches any watched CSS file.
+                matchesGlob = grunt.moodleEnv.cssSrc.some(watchedPathGlob => {
+                    return glob.sync(watchedPathGlob).indexOf(changedFilePath) !== -1;
+                });
+                if (matchesGlob) {
+                    cssFiles.push(changedFilePath);
+                    hasCss = true;
+                }
+            });
+        } else {
+            // Grunt was called without a list of files.
+            // The start directory (runDir) may be a child dir of the project.
+            // Check each scssSrc file to see if it's in the start directory.
+            // This means that we can lint just mod/*/styles.css if started in the mod directory.
+
+            grunt.moodleEnv.scssSrc.forEach(path => {
+                if (path.startsWith(grunt.moodleEnv.runDir)) {
+                    scssFiles.push(path);
+                    hasScss = true;
+                }
+            });
+
+            grunt.moodleEnv.cssSrc.forEach(path => {
+                if (path.startsWith(grunt.moodleEnv.runDir)) {
+                    cssFiles.push(path);
+                    hasCss = true;
+                }
+            });
+        }
+
+        // Register the tasks.
+        const scssTasks = ['sass'];
+        if (hasScss) {
+            grunt.config.merge(getScssConfigForFiles(scssFiles));
+            scssTasks.unshift('stylelint:scss');
+        }
+
+        const cssTasks = [];
+        if (hasCss) {
+            grunt.config.merge(getCssConfigForFiles(cssFiles));
+            cssTasks.push('stylelint:css');
+        }
+
+        // The tasks must be registered, even if empty to ensure a consistent command list.
+        // They jsut won't run anything.
+        grunt.registerTask('scss', scssTasks);
+        grunt.registerTask('rawcss', cssTasks);
+    };
+
+    // Register CSS tasks.
+    grunt.loadNpmTasks('grunt-stylelint');
+
+    // Register the style lint tasks.
+    registerStyleLintTasks();
+    grunt.registerTask('css', ['scss', 'rawcss']);
+
+    const getCoreThemeMatches = () => {
+        const scssMatch = 'scss/**/*.scss';
+
+        if (grunt.moodleEnv.inTheme) {
+            return [scssMatch];
+        }
+
+        if (grunt.moodleEnv.runDir.startsWith('theme')) {
+            return [`*/${scssMatch}`];
+        }
+
+        return [`theme/*/${scssMatch}`];
+    };
+
+    // Add the watch configuration for rawcss, and scss.
+    grunt.config.merge({
+        watch: {
+            rawcss: {
+                files: [
+                    '**/*.css',
+                ],
+                excludes: [
+                    '**/moodle.css',
+                    '**/editor.css',
+                ],
+                tasks: ['rawcss']
+            },
+            scss: {
+                files: getCoreThemeMatches(),
+                tasks: ['scss']
+            },
+        },
+    });
+};
diff --git a/.grunt/tasks/watch.js b/.grunt/tasks/watch.js
new file mode 100644 (file)
index 0000000..25c6487
--- /dev/null
@@ -0,0 +1,272 @@
+/**
+ * This is a wrapper task to handle the grunt watch command. It attempts to use
+ * Watchman to monitor for file changes, if it's installed, because it's much faster.
+ *
+ * If Watchman isn't installed then it falls back to the grunt-contrib-watch file
+ * watcher for backwards compatibility.
+ */
+
+/* eslint-env node */
+
+module.exports = grunt => {
+    /**
+     * This is a wrapper task to handle the grunt watch command. It attempts to use
+     * Watchman to monitor for file changes, if it's installed, because it's much faster.
+     *
+     * If Watchman isn't installed then it falls back to the grunt-contrib-watch file
+     * watcher for backwards compatibility.
+     */
+    const watchHandler = function() {
+        const async = require('async');
+        const watchTaskDone = this.async();
+        let watchInitialised = false;
+        let watchTaskQueue = {};
+        let processingQueue = false;
+
+        const watchman = require('fb-watchman');
+        const watchmanClient = new watchman.Client();
+
+        // Grab the tasks and files that have been queued up and execute them.
+        var processWatchTaskQueue = function() {
+            if (!Object.keys(watchTaskQueue).length || processingQueue) {
+                // If there is nothing in the queue or we're already processing then wait.
+                return;
+            }
+
+            processingQueue = true;
+
+            // Grab all tasks currently in the queue.
+            var queueToProcess = watchTaskQueue;
+            // Reset the queue.
+            watchTaskQueue = {};
+
+            async.forEachSeries(
+                Object.keys(queueToProcess),
+                function(task, next) {
+                    var files = queueToProcess[task];
+                    var filesOption = '--files=' + files.join(',');
+                    grunt.log.ok('Running task ' + task + ' for files ' + filesOption);
+
+                    // Spawn the task in a child process so that it doesn't kill this one
+                    // if it failed.
+                    grunt.util.spawn(
+                        {
+                            // Spawn with the grunt bin.
+                            grunt: true,
+                            // Run from current working dir and inherit stdio from process.
+                            opts: {
+                                cwd: grunt.moodleEnv.fullRunDir,
+                                stdio: 'inherit'
+                            },
+                            args: [task, filesOption]
+                        },
+                        function(err, res, code) {
+                            if (code !== 0) {
+                                // The grunt task failed.
+                                grunt.log.error(err);
+                            }
+
+                            // Move on to the next task.
+                            next();
+                        }
+                    );
+                },
+                function() {
+                    // No longer processing.
+                    processingQueue = false;
+                    // Once all of the tasks are done then recurse just in case more tasks
+                    // were queued while we were processing.
+                    processWatchTaskQueue();
+                }
+            );
+        };
+
+        const originalWatchConfig = grunt.config.get(['watch']);
+        const watchConfig = Object.keys(originalWatchConfig).reduce(function(carry, key) {
+            if (key == 'options') {
+                return carry;
+            }
+
+            const value = originalWatchConfig[key];
+
+            const taskNames = value.tasks;
+            const files = value.files;
+            let excludes = [];
+            if (value.excludes) {
+                excludes = value.excludes;
+            }
+
+            taskNames.forEach(function(taskName) {
+                carry[taskName] = {
+                    files,
+                    excludes,
+                };
+            });
+
+            return carry;
+        }, {});
+
+        watchmanClient.on('error', function(error) {
+            // We have to add an error handler here and parse the error string because the
+            // example way from the docs to check if Watchman is installed doesn't actually work!!
+            // See: https://github.com/facebook/watchman/issues/509
+            if (error.message.match('Watchman was not found')) {
+                // If watchman isn't installed then we should fallback to the other watch task.
+                grunt.log.ok('It is recommended that you install Watchman for better performance using the "watch" command.');
+
+                // Fallback to the old grunt-contrib-watch task.
+                grunt.renameTask('watch-grunt', 'watch');
+                grunt.task.run(['watch']);
+                // This task is finished.
+                watchTaskDone(0);
+            } else {
+                grunt.log.error(error);
+                // Fatal error.
+                watchTaskDone(1);
+            }
+        });
+
+        watchmanClient.on('subscription', function(resp) {
+            if (resp.subscription !== 'grunt-watch') {
+                return;
+            }
+
+            resp.files.forEach(function(file) {
+                grunt.log.ok('File changed: ' + file.name);
+
+                var fullPath = grunt.moodleEnv.fullRunDir + '/' + file.name;
+                Object.keys(watchConfig).forEach(function(task) {
+
+                    const fileGlobs = watchConfig[task].files;
+                    var match = fileGlobs.some(function(fileGlob) {
+                        return grunt.file.isMatch(`**/${fileGlob}`, fullPath);
+                    });
+
+                    if (match) {
+                        // If we are watching a subdirectory then the file.name will be relative
+                        // to that directory. However the grunt tasks  expect the file paths to be
+                        // relative to the Gruntfile.js location so let's normalise them before
+                        // adding them to the queue.
+                        var relativePath = fullPath.replace(grunt.moodleEnv.gruntFilePath + '/', '');
+                        if (task in watchTaskQueue) {
+                            if (!watchTaskQueue[task].includes(relativePath)) {
+                                watchTaskQueue[task] = watchTaskQueue[task].concat(relativePath);
+                            }
+                        } else {
+                            watchTaskQueue[task] = [relativePath];
+                        }
+                    }
+                });
+            });
+
+            processWatchTaskQueue();
+        });
+
+        process.on('SIGINT', function() {
+            // Let the user know that they may need to manually stop the Watchman daemon if they
+            // no longer want it running.
+            if (watchInitialised) {
+                grunt.log.ok('The Watchman daemon may still be running and may need to be stopped manually.');
+            }
+
+            process.exit();
+        });
+
+        // Initiate the watch on the current directory.
+        watchmanClient.command(['watch-project', grunt.moodleEnv.fullRunDir], function(watchError, watchResponse) {
+            if (watchError) {
+                grunt.log.error('Error initiating watch:', watchError);
+                watchTaskDone(1);
+                return;
+            }
+
+            if ('warning' in watchResponse) {
+                grunt.log.error('warning: ', watchResponse.warning);
+            }
+
+            var watch = watchResponse.watch;
+            var relativePath = watchResponse.relative_path;
+            watchInitialised = true;
+
+            watchmanClient.command(['clock', watch], function(clockError, clockResponse) {
+                if (clockError) {
+                    grunt.log.error('Failed to query clock:', clockError);
+                    watchTaskDone(1);
+                    return;
+                }
+
+                // Generate the expression query used by watchman.
+                // Documentation is limited, but see https://facebook.github.io/watchman/docs/expr/allof.html for examples.
+                // We generate an expression to match any value in the files list of all of our tasks, but excluding
+                // all value in the  excludes list of that task.
+                //
+                // [anyof, [
+                //      [allof, [
+                //          [anyof, [
+                //              ['match', validPath, 'wholename'],
+                //              ['match', validPath, 'wholename'],
+                //          ],
+                //          [not,
+                //              [anyof, [
+                //                  ['match', invalidPath, 'wholename'],
+                //                  ['match', invalidPath, 'wholename'],
+                //              ],
+                //          ],
+                //      ],
+                var matchWholeName = fileGlob => ['match', fileGlob, 'wholename'];
+                var matches = Object.keys(watchConfig).map(function(task) {
+                    const matchAll = [];
+                    matchAll.push(['anyof'].concat(watchConfig[task].files.map(matchWholeName)));
+
+                    if (watchConfig[task].excludes.length) {
+                        matchAll.push(['not', ['anyof'].concat(watchConfig[task].excludes.map(matchWholeName))]);
+                    }
+
+                    return ['allof'].concat(matchAll);
+                });
+
+                matches = ['anyof'].concat(matches);
+
+                var sub = {
+                    expression: matches,
+                    // Which fields we're interested in.
+                    fields: ["name", "size", "type"],
+                    // Add our time constraint.
+                    since:&