Merge branch 'MDL-42965_master' of https://github.com/totara/openbadges
authorSam Hemelryk <sam@moodle.com>
Mon, 16 Dec 2013 00:47:57 +0000 (13:47 +1300)
committerSam Hemelryk <sam@moodle.com>
Mon, 16 Dec 2013 00:47:57 +0000 (13:47 +1300)
475 files changed:
.jshintrc
admin/cli/install.php
admin/cli/install_database.php
admin/enrol.php
admin/environment.xml
admin/index.php
admin/mnet/access_control.php
admin/renderer.php
admin/settings/appearance.php
admin/settings/plugins.php
admin/settings/server.php
admin/tests/behat/behat_admin.php
admin/tests/behat/display_short_names.feature
admin/tests/behat/upload_users.feature
admin/tool/behat/cli/util.php
admin/tool/behat/tests/behat/basic_actions.feature
admin/tool/behat/tests/behat/data_generators.feature
admin/tool/behat/tests/tool_behat_test.php
admin/tool/customlang/index.php
admin/tool/dbtransfer/locallib.php
admin/tool/generator/classes/backend.php
admin/tool/innodb/index.php
admin/tool/langimport/index.php
admin/tool/multilangupgrade/index.php
admin/tool/phpunit/webrunner.php
admin/tool/replace/classes/form.php [new file with mode: 0644]
admin/tool/replace/index.php
admin/tool/replace/lang/en/tool_replace.php
admin/tool/replace/version.php
admin/tool/uploadcourse/classes/processor.php
admin/tool/uploadcourse/tests/behat/create.feature
admin/tool/uploadcourse/tests/behat/update.feature
admin/tool/uploadcourse/tests/course_test.php
admin/tool/uploadcourse/tests/helper_test.php
admin/tool/uploadcourse/tests/processor_test.php
admin/tool/uploaduser/index.php
admin/tool/uploaduser/picture.php
admin/webservice/forms.php
auth/db/auth.php
auth/email/auth.php
auth/imap/auth.php
auth/imap/config.html
auth/ldap/auth.php
auth/pop3/auth.php
auth/upgrade.txt
auth/webservice/auth.php
backup/backup.php
backup/controller/backup_controller.class.php
backup/controller/restore_controller.class.php
backup/moodle2/backup_custom_fields.php
backup/util/dbops/restore_dbops.class.php
backup/util/helper/backup_cron_helper.class.php
backup/util/plan/tests/step_test.php
backup/util/progress/core_backup_progress.class.php
backup/util/progress/tests/progress_test.php
backup/util/ui/base_moodleform.class.php
backup/util/ui/restore_ui_stage.class.php
backup/util/ui/tests/behat/backup_courses.feature
backup/util/ui/tests/behat/behat_backup.php
backup/util/ui/tests/behat/duplicate_activities.feature
backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js [new file with mode: 0644]
backup/util/ui/yui/confirmcancel/confirmcancel.js [deleted file]
backup/util/ui/yui/src/backupselectall/build.json [new file with mode: 0644]
backup/util/ui/yui/src/backupselectall/js/backupselectall.js [moved from backup/util/ui/yui/backupselectall/backupselectall.js with 88% similarity]
backup/util/ui/yui/src/backupselectall/js/backupselectall.json [new file with mode: 0644]
backup/util/ui/yui/src/backupselectall/meta/backupselectall.json [new file with mode: 0644]
backup/util/ui/yui/src/confirmcancel/build.json [new file with mode: 0644]
backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js [new file with mode: 0644]
backup/util/ui/yui/src/confirmcancel/meta/confirmcancel.json [new file with mode: 0644]
badges/tests/behat/add_badge.feature
badges/tests/behat/award_badge.feature
blocks/activity_modules/tests/behat/block_activity_modules.feature [new file with mode: 0644]
blocks/blog_recent/block_blog_recent.php
blocks/comments/tests/behat/behat_block_comments.php
blocks/course_list/block_course_list.php
blocks/navigation/tests/behat/view_my_courses.feature
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js
blocks/navigation/yui/src/navigation/js/navigation.js
blocks/rss_client/block_rss_client.php
blocks/site_main_menu/block_site_main_menu.php
blocks/tags/block_tags.php
blocks/tests/behat/behat_blocks.php
blog/index.php
blog/locallib.php
blog/tests/behat/comment.feature
blog/tests/bloglib_test.php
blog/upgrade.txt [new file with mode: 0644]
cache/admin.php
cache/classes/definition.php
cache/classes/factory.php
cache/classes/helper.php
cache/classes/loaders.php
cache/locallib.php
calendar/lib.php
cohort/tests/behat/behat_cohort.php
cohort/tests/behat/upload_cohort_users.feature
comment/lib.php
completion/tests/behat/enable_manual_complete_mark.feature
completion/tests/behat/restrict_activity_by_date.feature
completion/tests/behat/restrict_activity_by_grade.feature
composer.json
config-dist.php
course/format/renderer.php
course/lib.php
course/tests/behat/activities_group_icons.feature
course/tests/behat/add_activities.feature
course/tests/behat/behat_course.php
course/tests/behat/course_category_management_listing.feature
course/tests/behat/course_controls.feature
course/tests/courselib_test.php
course/tests/externallib_test.php
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js [new file with mode: 0644]
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js [new file with mode: 0644]
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js [new file with mode: 0644]
course/yui/src/toolboxes/build.json [new file with mode: 0644]
course/yui/src/toolboxes/js/resource.js [new file with mode: 0644]
course/yui/src/toolboxes/js/section.js [new file with mode: 0644]
course/yui/src/toolboxes/js/shared.js [new file with mode: 0644]
course/yui/src/toolboxes/js/toolbox.js [new file with mode: 0644]
course/yui/src/toolboxes/meta/toolboxes.json [new file with mode: 0644]
course/yui/toolboxes/toolboxes.js [deleted file]
enrol/category/locallib.php
enrol/cohort/locallib.php
enrol/database/lib.php
enrol/flatfile/lib.php
enrol/guest/lang/en/enrol_guest.php
enrol/guest/lib.php
enrol/imsenterprise/lib.php
enrol/ldap/lib.php
enrol/manual/lib.php
enrol/manual/yui/quickenrolment/quickenrolment.js
enrol/meta/locallib.php
enrol/paypal/ipn.php
enrol/self/db/upgrade.php
enrol/self/lib.php
enrol/self/version.php
enrol/yui/notification/notification.js [deleted file]
filter/algebra/thirdpartylibs.xml [new file with mode: 0644]
filter/tex/db/upgrade.php
filter/tex/filter.php
filter/tex/lang/en/filter_tex.php
filter/tex/latex.php
filter/tex/lib.php
filter/tex/pix.php
filter/tex/settings.php
filter/tex/texdebug.php
filter/tex/version.php
grade/import/csv/index.php
grade/import/xml/import.php
grade/import/xml/index.php
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/grader/preferences.php
grade/report/lib.php
grade/report/upgrade.txt
group/clientlib.js
group/tests/behat/behat_groups.php
group/tests/behat/create_groups.feature
group/tests/behat/groups_import.feature
group/tests/behat/id_uniqueness.feature
install.php
install/lang/de/install.php
install/lang/hu/install.php
install/lang/ru/install.php
lang/en/admin.php
lang/en/blog.php
lang/en/cache.php
lang/en/message.php
lang/en/mnet.php
lang/en/moodle.php
lang/en/plugin.php
lang/en/repository.php
lib/accesslib.php
lib/adminlib.php
lib/ajax/getsiteadminbranch.php
lib/authlib.php
lib/badgeslib.php
lib/behat/behat_base.php
lib/behat/behat_files.php
lib/behat/classes/behat_command.php
lib/behat/classes/behat_config_manager.php
lib/behat/classes/util.php
lib/behat/form_field/behat_form_editor.php
lib/behat/form_field/behat_form_field.php
lib/behat/form_field/behat_form_select.php
lib/behat/lib.php
lib/classes/component.php
lib/classes/event/base.php
lib/classes/event/blog_association_created.php [new file with mode: 0644]
lib/classes/event/blog_entries_viewed.php [new file with mode: 0644]
lib/classes/event/comment_created.php [new file with mode: 0644]
lib/classes/event/comment_deleted.php [new file with mode: 0644]
lib/classes/event/comments_viewed.php [moved from mod/book/classes/event/instances_list_viewed.php with 56% similarity]
lib/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
lib/classes/event/course_module_instances_list_viewed.php
lib/classes/event/course_module_viewed.php [new file with mode: 0644]
lib/classes/minify.php
lib/classes/php_time_limit.php [new file with mode: 0644]
lib/classes/plugininfo/base.php
lib/classes/session/file.php
lib/classes/session/manager.php
lib/classes/update/checker.php
lib/classes/update/deployer.php
lib/classes/useragent.php
lib/completionlib.php
lib/cronlib.php
lib/deprecatedlib.php
lib/dml/moodle_database.php
lib/dml/mssql_native_moodle_database.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/pgsql_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/editor/tinymce/module.js
lib/editor/tinymce/plugins/managefiles/manage_form.php
lib/editor/tinymce/tests/behat/edit_available_icons.feature
lib/enrollib.php
lib/externallib.php
lib/filelib.php
lib/form/form.js
lib/form/yui/build/moodle-form-shortforms/moodle-form-shortforms-debug.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-shortforms/moodle-form-shortforms-min.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-shortforms/moodle-form-shortforms.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-showadvanced/moodle-form-showadvanced-debug.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-showadvanced/moodle-form-showadvanced-min.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-showadvanced/moodle-form-showadvanced.js [new file with mode: 0644]
lib/form/yui/shortforms/shortforms.js [deleted file]
lib/form/yui/showadvanced/showadvanced.js [deleted file]
lib/form/yui/src/shortforms/build.json [new file with mode: 0644]
lib/form/yui/src/shortforms/js/shortforms.js [new file with mode: 0644]
lib/form/yui/src/shortforms/meta/shortforms.json [new file with mode: 0644]
lib/form/yui/src/showadvanced/build.json [new file with mode: 0644]
lib/form/yui/src/showadvanced/js/showadvanced.js [new file with mode: 0644]
lib/form/yui/src/showadvanced/meta/showadvanced.json [new file with mode: 0644]
lib/formslib.php
lib/grouplib.php
lib/javascript-static.js
lib/messagelib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/setup.php
lib/setuplib.php
lib/statslib.php
lib/testing/generator/block_generator.php
lib/testing/generator/data_generator.php
lib/testing/generator/module_generator.php
lib/tests/behat/behat_deprecated.php [new file with mode: 0644]
lib/tests/behat/behat_forms.php
lib/tests/behat/behat_general.php
lib/tests/behat/behat_hooks.php
lib/tests/behat/behat_navigation.php
lib/tests/behat/behat_permissions.php
lib/tests/event_course_module_instance_list_viewed.php [new file with mode: 0644]
lib/tests/event_course_module_viewed.php [new file with mode: 0644]
lib/tests/event_deprecated_test.php [new file with mode: 0644]
lib/tests/event_test.php
lib/tests/filelib_test.php
lib/tests/fixtures/event_fixtures.php
lib/tests/fixtures/event_mod_badfixtures.php [moved from lib/phpunit/generatorlib.php with 59% similarity]
lib/tests/fixtures/event_mod_fixtures.php [new file with mode: 0644]
lib/tests/grouplib_test.php
lib/tests/messagelib_test.php
lib/tests/minify_test.php
lib/tests/performance/filtersettingsperformancetester.php
lib/tests/setuplib_test.php
lib/tests/statslib_test.php
lib/tests/theme_config_test.php
lib/tests/useragent_test.php
lib/upgrade.txt
lib/upgradelib.php
lib/webdavlib.php
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks.js
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js [new file with mode: 0644]
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer.js [new file with mode: 0644]
lib/yui/dragdrop/dragdrop.js [deleted file]
lib/yui/src/actionmenu/js/actionmenu.js
lib/yui/src/actionmenu/meta/actionmenu.json
lib/yui/src/blocks/js/blocks.js
lib/yui/src/dragdrop/build.json [new file with mode: 0644]
lib/yui/src/dragdrop/js/dragdrop.js [new file with mode: 0644]
lib/yui/src/dragdrop/meta/dragdrop.json [new file with mode: 0644]
lib/yui/src/maintenancemodetimer/build.json [new file with mode: 0644]
lib/yui/src/maintenancemodetimer/js/maintenancemodetimer.js [new file with mode: 0644]
lib/yui/src/maintenancemodetimer/meta/maintenancemodetimer.json [new file with mode: 0644]
lib/yuilib/3.13.0/io-upload-iframe/io-upload-iframe-coverage.js
lib/yuilib/3.13.0/io-upload-iframe/io-upload-iframe-debug.js
lib/yuilib/3.13.0/io-upload-iframe/io-upload-iframe-min.js
lib/yuilib/3.13.0/io-upload-iframe/io-upload-iframe.js
mdeploy.php
message/defaultoutputs.php
message/module.js
message/renderer.php
message/tests/behat/block_users.feature
message/tests/behat/display_history.feature
message/tests/behat/manage_contacts.feature
message/tests/behat/search_history.feature
mod/assign/backup/moodle2/restore_assign_activity_task.class.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/file/importziplib.php
mod/assign/feedback/file/locallib.php
mod/assign/feedback/offline/importgradesform.php
mod/assign/feedback/offline/locallib.php
mod/assign/locallib.php
mod/assign/module.js
mod/assign/renderer.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/tests/behat/allow_another_attempt.feature [new file with mode: 0644]
mod/assign/tests/behat/file_submission.feature
mod/assign/tests/upgradelib_test.php
mod/assign/upgradelib.php
mod/book/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/book/classes/event/course_module_viewed.php
mod/book/index.php
mod/book/lang/en/book.php
mod/book/tests/events_test.php
mod/chat/chatd.php
mod/chat/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/chat/gui_header_js/jsupdated.php
mod/chat/index.php
mod/chat/lang/en/chat.php
mod/chat/tests/events_test.php
mod/choice/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/choice/classes/event/course_module_viewed.php
mod/choice/index.php
mod/choice/lang/en/choice.php
mod/choice/tests/events_test.php
mod/choice/view.php
mod/data/data.js
mod/data/field/picture/field.class.php
mod/data/import.php
mod/feedback/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/feedback/classes/event/course_module_viewed.php
mod/feedback/index.php
mod/feedback/lang/en/feedback.php
mod/feedback/view.php
mod/folder/renderer.php
mod/forum/lib.php
mod/forum/tests/behat/add_forum.feature
mod/forum/tests/behat/edit_post_student.feature
mod/forum/tests/behat/edit_post_teacher.feature
mod/forum/tests/behat/track_read_posts.feature
mod/lesson/lib.php
mod/page/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/page/classes/event/course_module_viewed.php
mod/page/index.php
mod/page/lang/en/page.php
mod/page/view.php
mod/quiz/db/log.php
mod/quiz/lib.php
mod/quiz/report/overview/report.php
mod/quiz/report/statistics/statistics_graph.php
mod/quiz/version.php
mod/scorm/tests/behat/add_scorm.feature
mod/wiki/admin.php
mod/wiki/classes/event/comment_created.php [new file with mode: 0644]
mod/wiki/classes/event/comment_deleted.php [new file with mode: 0644]
mod/wiki/classes/event/comments_viewed.php [new file with mode: 0644]
mod/wiki/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/wiki/classes/event/course_module_viewed.php [new file with mode: 0644]
mod/wiki/classes/event/page_created.php [moved from mod/chat/classes/event/instances_list_viewed.php with 57% similarity]
mod/wiki/classes/event/page_deleted.php [moved from mod/page/classes/event/instances_list_viewed.php with 59% similarity]
mod/wiki/classes/event/page_diff_viewed.php [new file with mode: 0644]
mod/wiki/classes/event/page_history_viewed.php [moved from mod/choice/classes/event/instances_list_viewed.php with 59% similarity]
mod/wiki/classes/event/page_locks_deleted.php [new file with mode: 0644]
mod/wiki/classes/event/page_map_viewed.php [new file with mode: 0644]
mod/wiki/classes/event/page_updated.php [moved from mod/feedback/classes/event/instances_list_viewed.php with 59% similarity]
mod/wiki/classes/event/page_version_deleted.php [new file with mode: 0644]
mod/wiki/classes/event/page_version_restored.php [new file with mode: 0644]
mod/wiki/classes/event/page_version_viewed.php [new file with mode: 0644]
mod/wiki/classes/event/page_viewed.php [new file with mode: 0644]
mod/wiki/comments.php
mod/wiki/create.php
mod/wiki/diff.php
mod/wiki/edit.php
mod/wiki/history.php
mod/wiki/index.php
mod/wiki/instancecomments.php
mod/wiki/lang/en/wiki.php
mod/wiki/locallib.php
mod/wiki/map.php
mod/wiki/module.js
mod/wiki/overridelocks.php
mod/wiki/pagelib.php
mod/wiki/prettyview.php
mod/wiki/restoreversion.php
mod/wiki/tests/behat/page_history.feature
mod/wiki/tests/behat/preview_page.feature
mod/wiki/tests/events_test.php [new file with mode: 0644]
mod/wiki/view.php
mod/wiki/viewversion.php
mod/workshop/classes/event/course_module_viewed.php
mod/workshop/lang/en/workshop.php
mod/workshop/view.php
my/indexsys.php
question/classes/statistics/questions/calculator.php
question/editlib.php
question/engine/upgrade/upgradelib.php
question/export_form.php
question/format.php
question/import_form.php
question/tests/behat/behat_question.php
question/tests/behat/edit_questions.feature
question/tests/behat/preview_question.feature
question/type/multichoice/styles.css
report/security/index.php
report/security/locallib.php
repository/boxnet/lib.php
repository/dropbox/lib.php
repository/equella/lib.php
repository/filepicker.php
repository/filesystem/lib.php
repository/lib.php
repository/recent/tests/behat/add_recent.feature
repository/recent/tests/behat/behat_repository_recent.php [deleted file]
repository/repository_ajax.php
repository/repository_callback.php
repository/tests/behat/behat_filepicker.php
repository/tests/behat/cancel_add_file.feature
repository/tests/behat/create_folders.feature
repository/tests/behat/create_shortcut.feature [new file with mode: 0644]
repository/tests/behat/delete_files.feature
repository/tests/behat/overwrite_file.feature [new file with mode: 0644]
repository/tests/behat/zip_and_unzip.feature
repository/upgrade.txt
repository/upload/tests/behat/behat_repository_upload.php
repository/upload/tests/behat/upload_file.feature
tag/manage.php
theme/afterburner/style/afterburner_styles.css
theme/base/style/admin.css
theme/base/style/core.css
theme/base/style/question.css
theme/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/readme_moodle.txt
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-debug.js
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-min.js
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapcollapse.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapdropdown.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapengine.js
theme/canvas/style/core.css
theme/clean/layout/secure.php
theme/yui_combo.php
user/editadvanced.php
user/editadvanced_form.php
user/editlib.php
user/externallib.php
user/lib.php
user/profilesys.php
user/renderer.php
user/tests/behat/edituserpassword.feature [new file with mode: 0644]
version.php

index 06de646..89f05f1 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -42,7 +42,7 @@
     "sub":          false,
     "supernew":     false,
     "maxerr":       500,
-    "maxlen":       150,
+    "maxlen":       180,
     "passfail":     false,
     "latedef":      true
 }
index a6c3873..df61269 100644 (file)
@@ -142,10 +142,10 @@ define('PHPUNIT_TEST', false);
 define('IGNORE_COMPONENT_CACHE', true);
 
 // Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.3.3") < 0) {
+if (version_compare(phpversion(), "5.4.4") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
-    fwrite(STDERR, "Moodle 2.5 or later requires at least PHP 5.3.3 (currently using version $phpversion).\n");
+    fwrite(STDERR, "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).\n");
     fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
     exit(1);
 }
index 3ab6279..91ac890 100644 (file)
@@ -62,10 +62,10 @@ Example:
 ";
 
 // Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.3.3") < 0) {
+if (version_compare(phpversion(), "5.4.4") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
-    fwrite(STDERR, "Moodle 2.5 or later requires at least PHP 5.3.3 (currently using version $phpversion).\n");
+    fwrite(STDERR, "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).\n");
     fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
     exit(1);
 }
index 642d93b..5f26eff 100644 (file)
@@ -108,7 +108,7 @@ switch ($action) {
         echo $OUTPUT->header();
 
         // This may take a long time.
-        set_time_limit(0);
+        core_php_time_limit::raise();
 
         // Disable plugin to prevent concurrent cron execution.
         unset($enabled[$enrol]);
index 1e937cd..4edfcd1 100644 (file)
       </PHP_SETTING>
     </PHP_SETTINGS>
   </MOODLE>
+  <MOODLE version="2.7" requires="2.2">
+    <UNICODE level="required">
+      <FEEDBACK>
+        <ON_ERROR message="unicoderequired" />
+      </FEEDBACK>
+    </UNICODE>
+    <DATABASE level="required">
+      <VENDOR name="mariadb" version="5.5.31" />
+      <VENDOR name="mysql" version="5.5.31" />
+      <VENDOR name="postgres" version="9.1" />
+      <VENDOR name="mssql" version="10.0" />
+      <VENDOR name="oracle" version="10.2" />
+    </DATABASE>
+    <PHP version="5.4.4" level="required">
+    </PHP>
+    <PCREUNICODE level="optional">
+      <FEEDBACK>
+        <ON_CHECK message="pcreunicodewarning" />
+      </FEEDBACK>
+    </PCREUNICODE>
+    <PHP_EXTENSIONS>
+      <PHP_EXTENSION name="iconv" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="iconvrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="mbstring" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="mbstringrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="curl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="curlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="openssl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opensslrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="tokenizer" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="tokenizerrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlrpc" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="xmlrpcrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="soap" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="soaprecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="ctype" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ctyperequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zip" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ziprequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zlib" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="gd" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="gdrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="simplexml" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="simplexmlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="spl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="splrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="pcre" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="dom" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xml" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="intl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="intlrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="json" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="hash" level="required"/>
+    </PHP_EXTENSIONS>
+    <PHP_SETTINGS>
+      <PHP_SETTING name="memory_limit" value="96M" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="settingmemorylimit" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="file_uploads" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingfileuploads" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="opcache.enable" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opcacherecommended" />
+        </FEEDBACK>
+      </PHP_SETTING>
+    </PHP_SETTINGS>
+  </MOODLE>
 </COMPATIBILITY_MATRIX>
index ea9f37d..e42687a 100644 (file)
@@ -30,10 +30,10 @@ if (!file_exists('../config.php')) {
 }
 
 // Check that PHP is of a sufficient version as soon as possible
-if (version_compare(phpversion(), '5.3.3') < 0) {
+if (version_compare(phpversion(), '5.4.4') < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it to later place
-    echo "Moodle 2.5 or later requires at least PHP 5.3.3 (currently using version $phpversion).<br />";
+    echo "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).<br />";
     echo "Please upgrade your server software or install older Moodle version.";
     die();
 }
@@ -47,7 +47,9 @@ if (!function_exists('iconv')) {
 
 define('NO_OUTPUT_BUFFERING', true);
 
-if (empty($_GET['cache']) and empty($_POST['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey'])) {
+if ((isset($_GET['cache']) and $_GET['cache'] === '0')
+        or (isset($_POST['cache']) and $_POST['cache'] === '0')
+        or (!isset($_POST['cache']) and !isset($_GET['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey']))) {
     // Prevent caching at all cost when visiting this page directly,
     // we redirect to self once we known no upgrades are necessary.
     // Note: $_GET and $_POST are used here intentionally because our param cleaning is not loaded yet.
@@ -90,9 +92,7 @@ $newaddonreq    = optional_param('installaddonrequest', null, PARAM_RAW);
 
 // Set up PAGE.
 $url = new moodle_url('/admin/index.php');
-if ($cache) {
-    $url->param('cache', 1);
-}
+$url->param('cache', $cache);
 $PAGE->set_url($url);
 unset($url);
 
@@ -267,12 +267,13 @@ if (!$cache and $version > $CFG->version) {  // upgrade
     $PAGE->set_pagelayout('maintenance');
     $PAGE->set_popup_notification_allowed(false);
 
+    /** @var core_admin_renderer $output */
+    $output = $PAGE->get_renderer('core', 'admin');
+
     if (upgrade_stale_php_files_present()) {
         $PAGE->set_title($stradministration);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_stale_php_files_page();
         die();
     }
@@ -287,8 +288,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strdatabasechecking);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_confirm_page($a->newversion, $maturity, $testsite);
         die();
 
@@ -302,8 +301,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strcurrentrelease);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_environment_page($release, $envstatus, $environment_results);
         die();
 
@@ -315,23 +312,13 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strplugincheck);
         $PAGE->set_cacheable(false);
 
-        $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1));
-
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
-
-        // check plugin dependencies first
-        $failed = array();
-        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
-            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
-            die();
-        }
-        unset($failed);
+        $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0));
 
         if ($fetchupdates) {
-            // no sesskey support guaranteed here
-            if (empty($CFG->disableupdatenotifications)) {
-                \core\update\checker::instance()->fetch();
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            $updateschecker = \core\update\checker::instance();
+            if ($updateschecker->enabled()) {
+                $updateschecker->fetch();
             }
             redirect($reloadurl);
         }
@@ -342,6 +329,7 @@ if (!$cache and $version > $CFG->version) {  // upgrade
 
             $deploydata = $deployer->submitted_data();
             if (!empty($deploydata)) {
+                // No sesskey support guaranteed here, because sessions might not work yet.
                 echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
                 die();
             }
@@ -349,11 +337,22 @@ if (!$cache and $version > $CFG->version) {  // upgrade
 
         echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                 $version, $showallplugins, $reloadurl,
-                new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1)));
+                new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1, 'cache'=>0)));
         die();
 
     } else {
-        // Launch main upgrade
+        // Always verify plugin dependencies!
+        $failed = array();
+        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $PAGE->set_pagelayout('maintenance');
+            $PAGE->set_popup_notification_allowed(false);
+            $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0));
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
+            die();
+        }
+        unset($failed);
+
+        // Launch main upgrade.
         upgrade_core($version, true);
     }
 } else if ($version < $CFG->version) {
@@ -373,6 +372,10 @@ if (!$cache and $branch <> $CFG->branch) {  // Update the branch
 if (!$cache and moodle_needs_upgrading()) {
     if (!$PAGE->headerprinted) {
         // means core upgrade or installation was not already done
+
+        /** @var core_admin_renderer $output */
+        $output = $PAGE->get_renderer('core', 'admin');
+
         if (!$confirmplugins) {
             $strplugincheck = get_string('plugincheck');
 
@@ -384,40 +387,46 @@ if (!$cache and moodle_needs_upgrading()) {
             $PAGE->set_cacheable(false);
 
             if ($fetchupdates) {
-                // no sesskey support guaranteed here
-                \core\update\checker::instance()->fetch();
+                require_sesskey();
+                $updateschecker = \core\update\checker::instance();
+                if ($updateschecker->enabled()) {
+                    $updateschecker->fetch();
+                }
                 redirect($PAGE->url);
             }
 
-            $output = $PAGE->get_renderer('core', 'admin');
-
             $deployer = \core\update\deployer::instance();
             if ($deployer->enabled()) {
                 $deployer->initialize($PAGE->url, $PAGE->url);
 
                 $deploydata = $deployer->submitted_data();
                 if (!empty($deploydata)) {
+                    require_sesskey();
                     echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
                     die();
                 }
             }
 
-            // check plugin dependencies first
-            $failed = array();
-            if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
-                echo $output->unsatisfied_dependencies_page($version, $failed, $PAGE->url);
-                die();
-            }
-            unset($failed);
-
-            // dependencies check passed, let's rock!
+            // Show plugins info.
             echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                     $version, $showallplugins,
                     new moodle_url($PAGE->url),
-                    new moodle_url('/admin/index.php', array('confirmplugincheck'=>1)));
+                    new moodle_url('/admin/index.php', array('confirmplugincheck'=>1, 'cache'=>0)));
+            die();
+        }
+
+        // Make sure plugin dependencies are always checked.
+        $failed = array();
+        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $PAGE->set_pagelayout('maintenance');
+            $PAGE->set_popup_notification_allowed(false);
+            $reloadurl = new moodle_url('/admin/index.php', array('cache' => 0));
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
             die();
         }
+        unset($failed);
     }
+
     // install/upgrade all plugins and other parts
     upgrade_noncore(true);
 }
@@ -477,6 +486,17 @@ if (during_initial_install()) {
     upgrade_finished('upgradesettings.php');
 }
 
+if (has_capability('moodle/site:config', context_system::instance())) {
+    if ($fetchupdates) {
+        require_sesskey();
+        $updateschecker = \core\update\checker::instance();
+        if ($updateschecker->enabled()) {
+            $updateschecker->fetch();
+        }
+        redirect(new moodle_url('/admin/index.php', array('cache' => 0)));
+    }
+}
+
 // Now we can be sure everything was upgraded and caches work fine,
 // redirect if necessary to make sure caching is enabled.
 if (!$cache) {
@@ -564,12 +584,6 @@ $registered = $DB->count_records('registration_hubs', array('huburl' => HUB_MOOD
 
 admin_externalpage_setup('adminnotifications');
 
-if ($fetchupdates) {
-    require_sesskey();
-    $updateschecker->fetch();
-    redirect(new moodle_url('/admin/index.php'));
-}
-
 $output = $PAGE->get_renderer('core', 'admin');
 echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
         $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
index 79c744c..375b3fa 100644 (file)
@@ -16,8 +16,6 @@ require_login();
 
 admin_externalpage_setup('ssoaccesscontrol');
 
-echo $OUTPUT->header();
-
 if (!extension_loaded('openssl')) {
     print_error('requiresopenssl', 'mnet');
 }
@@ -65,9 +63,11 @@ if (!empty($action) and confirm_sesskey()) {
 
             if (mnet_update_sso_access_control($idrec->username, $idrec->mnet_host_id, $accessctrl)) {
                 if ($accessctrl == 'allow') {
-                    redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('uset'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
-                } elseif ($accessctrl == 'deny') {
-                    redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
+                    redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('user' => $idrec->username,
+                        'host' => $mnethosts[$idrec->mnet_host_id])));
+                } else if ($accessctrl == 'deny') {
+                    redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user' => $idrec->username,
+                        'host' => $mnethosts[$idrec->mnet_host_id])));
                 }
             }
             break;
@@ -118,6 +118,8 @@ if ($form = data_submitted() and confirm_sesskey()) {
     exit;
 }
 
+echo $OUTPUT->header();
+
 // Explain
 echo $OUTPUT->box(get_string('ssoacldescr','mnet'));
 // Are the needed bits enabled?
index 0f84e00..3ba44bb 100644 (file)
@@ -140,7 +140,7 @@ class core_admin_renderer extends plugin_renderer_base {
     public function upgrade_confirm_page($strnewversion, $maturity, $testsite) {
         $output = '';
 
-        $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1));
+        $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'cache' => 0));
         $continue = new single_button($continueurl, get_string('continue'), 'get');
         $cancelurl = new moodle_url('/admin/index.php');
 
@@ -170,7 +170,7 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->environment_check_table($envstatus, $environment_results);
 
         if (!$envstatus) {
-            $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1));
+            $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1, 'cache' => 0));
 
         } else {
             $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess');
@@ -179,7 +179,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $output .= $this->box(get_string('langpackwillbeupdated', 'admin'), 'generalbox', 'notice');
             }
 
-            $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1)));
+            $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)));
         }
 
         $output .= $this->footer();
@@ -711,7 +711,7 @@ class core_admin_renderer extends plugin_renderer_base {
         }
 
         $updateinfo .= $this->container_start('checkforupdates');
-        $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 1));
+        $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0));
         $updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin'));
         if ($fetch) {
             $updateinfo .= $this->container(get_string('checkforupdateslast', 'core_plugin',
@@ -962,7 +962,7 @@ class core_admin_renderer extends plugin_renderer_base {
             $out .= $this->output->heading(get_string('nonehighlighted', 'core_plugin'));
             if (empty($options['full'])) {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)),
                     get_string('nonehighlightedinfo', 'core_plugin'));
             }
             $out .= $this->output->container_end();
@@ -972,11 +972,11 @@ class core_admin_renderer extends plugin_renderer_base {
             $out .= $this->output->heading(get_string('somehighlighted', 'core_plugin', $sumofhighlighted));
             if (empty($options['full'])) {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)),
                     get_string('somehighlightedinfo', 'core_plugin'));
             } else {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0, 'cache' => 0)),
                     get_string('somehighlightedonly', 'core_plugin'));
             }
             $out .= $this->output->container_end();
@@ -1176,13 +1176,14 @@ class core_admin_renderer extends plugin_renderer_base {
             get_string('displayname', 'core_plugin'),
             get_string('source', 'core_plugin'),
             get_string('version', 'core_plugin'),
+            get_string('release', 'core_plugin'),
             get_string('availability', 'core_plugin'),
             get_string('actions', 'core_plugin'),
             get_string('notes','core_plugin'),
         );
-        $table->headspan = array(1, 1, 1, 1, 2, 1);
+        $table->headspan = array(1, 1, 1, 1, 1, 2, 1);
         $table->colclasses = array(
-            'pluginname', 'source', 'version', 'availability', 'settings', 'uninstall', 'notes'
+            'pluginname', 'source', 'version', 'release', 'availability', 'settings', 'uninstall', 'notes'
         );
 
         foreach ($plugininfo as $type => $plugins) {
@@ -1238,6 +1239,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 }
 
                 $version = new html_table_cell($plugin->versiondb);
+                $release = new html_table_cell($plugin->release);
 
                 $isenabled = $plugin->is_enabled();
                 if (is_null($isenabled)) {
@@ -1283,7 +1285,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $notes = new html_table_cell($requiredby.$updateinfo);
 
                 $row->cells = array(
-                    $pluginname, $source, $version, $availability, $settings, $uninstall, $notes
+                    $pluginname, $source, $version, $release, $availability, $settings, $uninstall, $notes
                 );
                 $table->data[] = $row;
             }
index 1ee3a7a..2cf6f0f 100644 (file)
@@ -2,7 +2,12 @@
 
 // This file defines settingpages and externalpages under the "appearance" category
 
-if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
+$capabilities = array(
+    'moodle/my:configsyspages',
+    'moodle/tag:manage'
+);
+
+if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // speedup for non-admins, add all caps used on this page
 
     $ADMIN->add('appearance', new admin_category('themes', new lang_string('themes')));
     // "themesettings" settingpage
@@ -36,8 +41,11 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     }
 
 
-    // calendar
+    // Calendar settings.
     $temp = new admin_settingpage('calendar', new lang_string('calendarsettings','admin'));
+
+    $temp->add(new admin_setting_configselect('calendartype', new lang_string('calendartype', 'admin'),
+        new lang_string('calendartype_desc', 'admin'), 'gregorian', \core_calendar\type_factory::get_list_of_calendar_types()));
     $temp->add(new admin_setting_special_adminseesall());
     //this is hacky because we do not want to include the stuff from calendar/lib.php
     $temp->add(new admin_setting_configselect('calendar_site_timeformat', new lang_string('pref_timeformat', 'calendar'),
@@ -186,10 +194,12 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configcheckbox('doctonewwindow', new lang_string('doctonewwindow', 'admin'), new lang_string('configdoctonewwindow', 'admin'), 0));
     $ADMIN->add('appearance', $temp);
 
-    $temp = new admin_externalpage('mypage', new lang_string('mypage', 'admin'), $CFG->wwwroot . '/my/indexsys.php');
+    $temp = new admin_externalpage('mypage', new lang_string('mypage', 'admin'), $CFG->wwwroot . '/my/indexsys.php',
+            'moodle/my:configsyspages');
     $ADMIN->add('appearance', $temp);
 
-    $temp = new admin_externalpage('profilepage', new lang_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php');
+    $temp = new admin_externalpage('profilepage', new lang_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php',
+            'moodle/my:configsyspages');
     $ADMIN->add('appearance', $temp);
 
     // coursecontact is the person responsible for course - usually manages enrolments, receives notification, etc.
@@ -219,7 +229,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $ADMIN->add('appearance', $temp);
 
     // link to tag management interface
-    $ADMIN->add('appearance', new admin_externalpage('managetags', new lang_string('managetags', 'tag'), "$CFG->wwwroot/tag/manage.php"));
+    $ADMIN->add('appearance', new admin_externalpage('managetags', new lang_string('managetags', 'tag'), $CFG->wwwroot.'/tag/manage.php', 'moodle/tag:manage'));
 
     $temp = new admin_settingpage('additionalhtml', new lang_string('additionalhtml', 'admin'));
     $temp->add(new admin_setting_heading('additionalhtml_heading', new lang_string('additionalhtml_heading', 'admin'), new lang_string('additionalhtml_desc', 'admin')));
index 5391cf7..283d02a 100644 (file)
@@ -256,7 +256,10 @@ if ($hassiteconfig) {
 
     // Add common settings page
     $temp = new admin_settingpage('managerepositoriescommon', new lang_string('commonrepositorysettings', 'repository'));
-    $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120));
+    $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorygetfiletimeout', new lang_string('getfiletimeout', 'repository'), new lang_string('configgetfiletimeout', 'repository'), 30, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorysyncfiletimeout', new lang_string('syncfiletimeout', 'repository'), new lang_string('configsyncfiletimeout', 'repository'), 1, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorysyncimagetimeout', new lang_string('syncimagetimeout', 'repository'), new lang_string('configsyncimagetimeout', 'repository'), 3, PARAM_INT));
     $temp->add(new admin_setting_configcheckbox('repositoryallowexternallinks', new lang_string('allowexternallinks', 'repository'), new lang_string('configallowexternallinks', 'repository'), 1));
     $temp->add(new admin_setting_configcheckbox('legacyfilesinnewcourses', new lang_string('legacyfilesinnewcourses', 'admin'), new lang_string('legacyfilesinnewcourses_help', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('legacyfilesaddallowed', new lang_string('legacyfilesaddallowed', 'admin'), new lang_string('legacyfilesaddallowed_help', 'admin'), 1));
index ea2491a..20f69b2 100644 (file)
@@ -93,6 +93,7 @@ $options = array(
     GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
     GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR');
 $temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'), new lang_string('configgetremoteaddrconf', 'admin'), 0, $options));
+
 $temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'), new lang_string('webproxyinfo', 'admin')));
 $temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'), new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
 $temp->add(new admin_setting_configtext('proxyport', new lang_string('proxyport', 'admin'), new lang_string('configproxyport', 'admin'), 0, PARAM_INT));
@@ -195,6 +196,8 @@ if (PHP_INT_SIZE === 8) {
 $temp->add(new admin_setting_configselect('extramemorylimit', new lang_string('extramemorylimit', 'admin'),
                                           new lang_string('configextramemorylimit', 'admin'), '512M',
                                           $memoryoptions));
+$temp->add(new admin_setting_configtext('maxtimelimit', new lang_string('maxtimelimit', 'admin'),
+        new lang_string('maxtimelimit_desc', 'admin'), 0, PARAM_INT));
 
 $temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
                                         new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
index 10d39eb..58b7775 100644 (file)
@@ -59,7 +59,7 @@ class behat_admin extends behat_base {
             // We expect admin block to be visible, otherwise go to homepage.
             if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
                 $this->getSession()->visit($this->locate_path('/'));
-                $this->wait(self::TIMEOUT, '(document.readyState === "complete")');
+                $this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
             }
 
             // Search by label.
@@ -68,7 +68,7 @@ class behat_admin extends behat_base {
             $submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
             $submitsearch->press();
 
-            $this->wait(self::TIMEOUT, '(document.readyState === "complete")');
+            $this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
 
             // Admin settings does not use the same DOM structure than other moodle forms
             // but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
@@ -125,23 +125,4 @@ class behat_admin extends behat_base {
             $this->getSession()->wait($timeout, $javascript);
         }
     }
-
-    /**
-     * Goes to notification page ensuring site admin navigation is loaded.
-     *
-     * @Given /^I go to notifications page$/
-     * @return Given[]
-     */
-    public function i_go_to_notifications_page() {
-        if ($this->running_javascript()) {
-            return array(
-                new Given('I expand "' . get_string('administrationsite') . '" node'),
-                new Given('I follow "' . get_string('notifications') . '"')
-            );
-        } else {
-            return array(
-                new Given('I follow "' . get_string('administrationsite') . '"')
-            );
-        }
-    }
 }
index 86231b2..b5d6096 100644 (file)
@@ -15,7 +15,7 @@ Feature: Display extended course names
     And I should not see "C_shortname Course fullname"
 
   Scenario: Courses list with extended course names
-    Given I go to notifications page
+    Given I expand "Site administration" node
     And I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][contains(., 'Appearance')]" "xpath_element"
     And I check "Display extended course names"
     When I press "Save changes"
index ae9d83b..b276f44 100644 (file)
@@ -19,7 +19,7 @@ Feature: Upload users
     And I expand "Users" node
     And I expand "Accounts" node
     And I follow "Upload users"
-    When I upload "lib/tests/fixtures/upload_users.csv" file to "File" filepicker
+    When I upload "lib/tests/fixtures/upload_users.csv" file to "File" filemanager
     And I press "Upload users"
     Then I should see "Upload users preview"
     And I should see "Tom"
@@ -42,5 +42,4 @@ Feature: Upload users
     And I expand "Users" node
     And I follow "Groups"
     And I select "Section 1 (1)" from "groups"
-    And I wait "4" seconds
     And the "members" select box should contain "Tom Jones"
index 287a535..28c933a 100644 (file)
@@ -93,21 +93,17 @@ ini_set('log_errors', '1');
 // Getting $CFG data.
 require_once(__DIR__ . '/../../../../config.php');
 
-// CFG->behat_prefix must be set and with value different than CFG->prefix and phpunit_prefix.
-if (empty($CFG->behat_prefix) ||
-       ($CFG->behat_prefix == $CFG->prefix) ||
-       (!empty($CFG->phpunit_prefix) && $CFG->behat_prefix == $CFG->phpunit_prefix)) {
-    behat_error(BEHAT_EXITCODE_CONFIG,
-        'Define $CFG->behat_prefix in config.php with a value different than $CFG->prefix and $CFG->phpunit_prefix');
-}
-
-// CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot.
-if (empty($CFG->behat_dataroot) ||
-       ($CFG->behat_dataroot == $CFG->dataroot) ||
-       (!empty($CFG->phpunit_dataroot) && $CFG->behat_dataroot == $CFG->phpunit_dataroot)) {
-    behat_error(BEHAT_EXITCODE_CONFIG,
-        'Define $CFG->behat_dataroot in config.php with a value different than $CFG->dataroot and $CFG->phpunit_dataroot');
-}
+// When we use the utilities we don't know how the site
+// will be accessed, so if neither $CFG->behat_switchcompletely or
+// $CFG->behat_wwwroot are set we must think that the site will
+// be accessed using the built-in server which is set by default
+// to localhost:8000. We need to do this to prevent uses of the production
+// wwwroot when the site is being installed / dropped...
+$CFG->behat_wwwroot = behat_get_wwwroot();
+
+// Checking the integrity of the provided $CFG->behat_* vars
+// to prevent conflicts with production and phpunit environments.
+behat_check_config_vars();
 
 // Create behat_dataroot if it doesn't exists.
 if (!file_exists($CFG->behat_dataroot)) {
index 5b1bd50..83f0320 100644 (file)
@@ -39,7 +39,7 @@ Feature: Page contents assertions
       | Course 1 | C1 | 0 |
     And I log in as "admin"
     And I follow "Course 1"
-    When I click on "Move this to the dock" "button" in the "Administration" "block"
+    When I dock "Administration" block
     Then I should not see "Question bank" in the "region-pre" "region"
     And I click on "//div[@id='dock']/descendant::h2[normalize-space(.)='Administration']" "xpath_element"
 
@@ -49,5 +49,5 @@ Feature: Page contents assertions
       | fullname | shortname | category |
       | Course 1 | C1 | 0 |
     And I log in as "admin"
-    When I click on "Move this to the dock" "button" in the "Administration" "block"
+    When I dock "Administration" block
     Then I should not see "Turn editing on" in the "region-pre" "region"
index 45d1ed6..8f21a72 100644 (file)
@@ -1,4 +1,4 @@
-@tool @tool_behat
+@tool @tool_behat @_only_local
 Feature: Set up contextual data for tests
   In order to write tests quickly
   As a developer
@@ -143,13 +143,56 @@ Feature: Set up contextual data for tests
       | fullname | shortname |
       | Course 1 | C1 |
     And the following "activities" exists:
-      | activity | name | intro | course | idnumber |
-      | assign   | Test assignment name | Test assignment description | C1 | assign1 |
-      | data     | Test database name | Test database description | C1 | data1 |
+      | activity   | name                   | intro                         | course | idnumber    |
+      | assign     | Test assignment name   | Test assignment description   | C1     | assign1     |
+      | assignment | Test assignment22 name | Test assignment22 description | C1     | assignment1 |
+      | book       | Test book name         | Test book description         | C1     | book1       |
+      | chat       | Test chat name         | Test chat description         | C1     | chat1       |
+      | choice     | Test choice name       | Test choice description       | C1     | choice1     |
+      | data       | Test database name     | Test database description     | C1     | data1       |
+      | feedback   | Test feedback name     | Test feedback description     | C1     | feedback1   |
+      | folder     | Test folder name       | Test folder description       | C1     | folder1     |
+      | forum      | Test forum name        | Test forum description        | C1     | forum1      |
+      | glossary   | Test glossary name     | Test glossary description     | C1     | glossary1   |
+      | imscp      | Test imscp name        | Test imscp description        | C1     | imscp1      |
+      | label      | Test label name        | Test label description        | C1     | label1      |
+      | lesson     | Test lesson name       | Test lesson description       | C1     | lesson1     |
+      | lti        | Test lti name          | Test lti description          | C1     | lti1        |
+      | page       | Test page name         | Test page description         | C1     | page1       |
+      | quiz       | Test quiz name         | Test quiz description         | C1     | quiz1       |
+      | resource   | Test resource name     | Test resource description     | C1     | resource1   |
+      | scorm      | Test scorm name        | Test scorm description        | C1     | scorm1      |
+      | survey     | Test survey name       | Test survey description       | C1     | survey1     |
+      | url        | Test url name          | Test url description          | C1     | url1        |
+      | wiki       | Test wiki name         | Test wiki description         | C1     | wiki1       |
+      | workshop   | Test workshop name     | Test workshop description     | C1     | workshop1   |
     When I log in as "admin"
     And I follow "Course 1"
     Then I should see "Test assignment name"
+    # Assignment 2.2 module type is disabled by default
+    # And I should see "Test assignment22 name"
+    And I should see "Test book name"
+    And I should see "Test chat name"
+    And I should see "Test choice name"
     And I should see "Test database name"
+    # Feedback module type is disabled by default
+    # And I should see "Test feedback name"
+    And I should see "Test folder name"
+    And I should see "Test forum name"
+    And I should see "Test glossary name"
+    And I should see "Test imscp name"
+    # We don't see label name, we see only description:
+    And I should see "Test label description"
+    And I should see "Test lesson name"
+    And I should see "Test lti name"
+    And I should see "Test page name"
+    And I should see "Test quiz name"
+    And I should see "Test resource name"
+    And I should see "Test scorm name"
+    And I should see "Test survey name"
+    And I should see "Test url name"
+    And I should see "Test wiki name"
+    And I should see "Test workshop name"
     And I follow "Test assignment name"
     And I should see "Test assignment description"
 
@@ -187,8 +230,6 @@ Feature: Set up contextual data for tests
     Then the "groups" select box should contain "Group 1 (1)"
     And the "groups" select box should contain "Group 2 (1)"
     And I select "Group 1 (1)" from "groups"
-    And I wait "5" seconds
     And the "members" select box should contain "Student 1"
     And I select "Group 2 (1)" from "groups"
-    And I wait "5" seconds
     And the "members" select box should contain "Student 2"
index c8fa488..09db5e6 100644 (file)
@@ -150,6 +150,11 @@ class tool_behat_testcase extends advanced_testcase {
             $this->markTestSkipped('Behat not installed.');
         }
 
+        // It is possible that it has no value.
+        if (empty($CFG->behat_wwwroot)) {
+            $CFG->behat_wwwroot = behat_get_wwwroot();
+        }
+
         // To avoid user value at config.php level.
         unset($CFG->behat_config);
 
@@ -183,6 +188,8 @@ class tool_behat_testcase extends advanced_testcase {
         // Lists.
         $this->assertContains('- feature1', $contents);
         $this->assertContains('- feature3', $contents);
+
+        unset($CFG->behat_wwwroot);
     }
 
 }
index 431a911..4d120ff 100644 (file)
@@ -55,7 +55,7 @@ if ($action === 'checkout') {
     $progressbar->create();         // prints the HTML code of the progress bar
 
     // we may need a bit of extra execution time and memory here
-    @set_time_limit(HOURSECS);
+    core_php_time_limit::raise(HOURSECS);
     raise_memory_limit(MEMORY_EXTRA);
     tool_customlang_utils::checkout($lng, $progressbar);
 
index c7e622f..f37f62a 100644 (file)
@@ -50,7 +50,7 @@ require_once($CFG->libdir.'/dtllib.php');
  * @return does not return, calls die()
  */
 function tool_dbtransfer_export_xml_database($description, $mdb) {
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     \core\session\manager::write_close(); // Release session.
 
@@ -77,7 +77,7 @@ function tool_dbtransfer_export_xml_database($description, $mdb) {
  * @return void
  */
 function tool_dbtransfer_transfer_database(moodle_database $sourcedb, moodle_database $targetdb, progress_trace $feedback = null) {
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     \core\session\manager::write_close(); // Release session.
 
index da931db..4299599 100644 (file)
@@ -177,7 +177,7 @@ abstract class tool_generator_backend {
 
         // Update time limit so PHP doesn't time out.
         if (!CLI_SCRIPT) {
-            set_time_limit(120);
+            core_php_time_limit::raise(120);
         }
     }
 
index 06f4f6a..6e99834 100644 (file)
@@ -57,7 +57,7 @@ if (data_submitted() and $confirm and confirm_sesskey()) {
 
     echo $OUTPUT->notification('Please be patient and wait for this to complete...', 'notifysuccess');
 
-    set_time_limit(0);
+    core_php_time_limit::raise();
 
     foreach ($rs as $table) {
         $DB->set_debug(true);
index 82b5cc2..36b416f 100644 (file)
@@ -69,7 +69,7 @@ $notice_ok    = array();
 $notice_error = array();
 
 if (($mode == INSTALLATION_OF_SELECTED_LANG) and confirm_sesskey() and !empty($pack)) {
-    set_time_limit(0);
+    core_php_time_limit::raise();
     make_temp_directory('');
     make_upload_directory('lang');
 
@@ -125,7 +125,7 @@ if ($mode == DELETION_OF_SELECTED_LANG and !empty($uninstalllang)) {
 }
 
 if ($mode == UPDATE_ALL_LANG) {
-    set_time_limit(0);
+    core_php_time_limit::raise();
 
     $installer = new lang_installer();
 
index 04dd691..6b4ce5c 100644 (file)
@@ -59,7 +59,7 @@ echo $OUTPUT->box_start();
 
 /// Turn off time limits, sometimes upgrades can be slow.
 
-@set_time_limit(0);
+core_php_time_limit::raise();
 
 echo '<strong>Progress:</strong>';
 $i = 0;
index f52bf6b..b565ae1 100644 (file)
@@ -38,7 +38,7 @@ if (!$CFG->debugdeveloper) {
     error('Not available on production sites, sorry.');
 }
 
-set_time_limit(60*30);
+core_php_time_limit::raise(60*30);
 
 $oldcwd = getcwd();
 $code = 0;
diff --git a/admin/tool/replace/classes/form.php b/admin/tool/replace/classes/form.php
new file mode 100644 (file)
index 0000000..5c60510
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * Site wide search-replace form.
+ *
+ * @package    tool_replace
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/formslib.php");
+
+/**
+ * Site wide search-replace form.
+ */
+class tool_replace_form extends moodleform {
+    function definition() {
+        global $CFG, $DB;
+
+        $mform = $this->_form;
+
+        $mform->addElement('header', 'searchhdr', get_string('pluginname', 'tool_replace'));
+        $mform->setExpanded('searchhdr', true);
+
+        $mform->addElement('text', 'search', get_string('searchwholedb', 'tool_replace'), 'size="50"');
+        $mform->setType('search', PARAM_RAW);
+        $mform->addElement('static', 'searchst', '', get_string('searchwholedbhelp', 'tool_replace'));
+        $mform->addRule('search', get_string('required'), 'required', null, 'client');
+
+        $mform->addElement('text', 'replace', get_string('replacewith', 'tool_replace'), 'size="50"', PARAM_RAW);
+        $mform->addElement('static', 'replacest', '', get_string('replacewithhelp', 'tool_replace'));
+        $mform->setType('replace', PARAM_RAW);
+        $mform->addElement('checkbox', 'shorten', get_string('shortenoversized', 'tool_replace'));
+        $mform->addRule('replace', get_string('required'), 'required', null, 'client');
+
+        $mform->addElement('header', 'confirmhdr', get_string('confirm'));
+        $mform->setExpanded('confirmhdr', true);
+        $mform->addElement('checkbox', 'sure', get_string('disclaimer', 'tool_replace'));
+        $mform->addRule('sure', get_string('required'), 'required', null, 'client');
+
+        $this->add_action_buttons(false, get_string('doit', 'tool_replace'));
+    }
+
+    function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        if (empty($data['shorten']) and core_text::strlen($data['search']) < core_text::strlen($data['replace'])) {
+            $errors['shorten'] = get_string('required');
+        }
+
+        return $errors;
+    }
+}
index 67d945e..b3013d2 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Search and replace strings throughout all texts in the whole database
  *
- * @package    tool
- * @subpackage replace
+ * @package    tool_replace
  * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -31,56 +30,38 @@ require_once($CFG->libdir.'/adminlib.php');
 
 admin_externalpage_setup('toolreplace');
 
-$search  = optional_param('search', '', PARAM_RAW);
-$replace = optional_param('replace', '', PARAM_RAW);
-$sure    = optional_param('sure', 0, PARAM_BOOL);
-
-###################################################################
 echo $OUTPUT->header();
 
 echo $OUTPUT->heading(get_string('pageheader', 'tool_replace'));
 
-if ($DB->get_dbfamily() !== 'mysql' and $DB->get_dbfamily() !== 'postgres') {
-    //TODO: add $DB->text_replace() to DML drivers
+if (!$DB->replace_all_text_supported()) {
     echo $OUTPUT->notification(get_string('notimplemented', 'tool_replace'));
     echo $OUTPUT->footer();
     die;
 }
 
-if (!data_submitted() or !$search or !$replace or !confirm_sesskey() or !$sure) {   /// Print a form
-    echo $OUTPUT->notification(get_string('notsupported', 'tool_replace'));
-    echo $OUTPUT->notification(get_string('excludedtables', 'tool_replace'));
+echo $OUTPUT->box_start();
+echo $OUTPUT->notification(get_string('notsupported', 'tool_replace'));
+echo $OUTPUT->notification(get_string('excludedtables', 'tool_replace'));
+echo $OUTPUT->box_end();
+
+$form = new tool_replace_form();
 
-    echo $OUTPUT->box_start();
-    echo '<div class="mdl-align">';
-    echo '<form action="index.php" method="post"><div>';
-    echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
-    echo '<div><label for="search">'.get_string('searchwholedb', 'tool_replace').
-            ' </label><input id="search" type="text" name="search" size="40" /> ('.
-            get_string('searchwholedbhelp', 'tool_replace').')</div>';
-    echo '<div><label for="replace">'.get_string('replacewith', 'tool_replace').
-            ' </label><input type="text" id="replace" name="replace" size="40" /> ('.
-            get_string('replacewithhelp', 'tool_replace').')</div>';
-    echo '<div><label for="sure">'.get_string('disclaimer', 'tool_replace').' </label><input type="checkbox" id="sure" name="sure" value="1" /></div>';
-    echo '<div class="buttons"><input type="submit" class="singlebutton" value="Yes, do it now" /></div>';
-    echo '</div></form>';
-    echo '</div>';
-    echo $OUTPUT->box_end();
+if (!$data = $form->get_data()) {
+    $form->display();
     echo $OUTPUT->footer();
-    die;
+    die();
 }
 
+// Scroll to the end when finished.
+$PAGE->requires->js_init_code("window.scrollTo(0, 5000000);");
+
 echo $OUTPUT->box_start();
-db_replace($search, $replace);
+db_replace($data->search, $data->replace);
 echo $OUTPUT->box_end();
 
-/// Rebuild course cache which might be incorrect now
-echo $OUTPUT->notification(get_string('notifyrebuilding', 'tool_replace'), 'notifysuccess');
-rebuild_course_cache();
-echo $OUTPUT->notification(get_string('notifyfinished', 'tool_replace'), 'notifysuccess');
+// Course caches are now rebuilt on the fly.
 
 echo $OUTPUT->continue_button(new moodle_url('/admin/index.php'));
 
 echo $OUTPUT->footer();
-
-
index 9103e89..919ed67 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['disclaimer'] = 'I understand the risks of this operation:';
+$string['cannotfit'] = 'The replacement is longer than original and shortening is not allow, cannot continue.';
+$string['disclaimer'] = 'I understand the risks of this operation';
+$string['doit'] = 'Yes, do it!';
 $string['excludedtables'] = 'Several tables are not updated as part of the text replacement. This include configuration, log, events, and session tables.';
 $string['pageheader'] = 'Search and replace text throughout the whole database';
 $string['notifyfinished'] = '...finished';
 $string['notifyrebuilding'] = 'Rebuilding course cache...';
-$string['notimplemented'] = 'Sorry, this feature is implemented only for MySQL and PostgreSQL databases.';
+$string['notimplemented'] = 'Sorry, this feature is not implemented in your database driver.';
 $string['notsupported'] ='This script is not supported, always make complete backup before proceeding!<br />This operation can not be reverted!';
 $string['pluginname'] = 'DB search and replace';
-$string['replacewith'] = 'Replace with this string:';
+$string['replacewith'] = 'Replace with this string';
 $string['replacewithhelp'] = 'usually new server URL';
-$string['searchwholedb'] = 'Search whole database for:';
+$string['searchwholedb'] = 'Search whole database for';
 $string['searchwholedbhelp'] = 'usually previous server URL';
+$string['shortenoversized'] = 'Shorten result if necessary';
index 4496dcd..307be8f 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013110500; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013110501; // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013110500; // Requires this Moodle version
 $plugin->component = 'tool_replace'; // Full name of the plugin (used for diagnostics)
 
index 7113ded..8963f7e 100644 (file)
@@ -195,7 +195,7 @@ class tool_uploadcourse_processor {
         $errors = 0;
 
         // We will most certainly need extra time and memory to process big files.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Loop over the CSV lines.
@@ -335,7 +335,7 @@ class tool_uploadcourse_processor {
         $tracker->start();
 
         // We might need extra time and memory depending on the number of rows to preview.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Loop over the CSV lines.
index ee95ca1..ce2c98b 100644 (file)
@@ -15,7 +15,7 @@ Feature: An admin can create courses using a CSV file
 
   @javascript
   Scenario: Creation of unexisting courses
-    Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filepicker
+    Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
     And I click on "Preview" "button"
     When I click on "Upload courses" "button"
     Then I should see "The course exists and update is not allowed"
@@ -29,7 +29,7 @@ Feature: An admin can create courses using a CSV file
 
   @javascript
   Scenario: Creation of existing courses
-    Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filepicker
+    Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
     And I select "Create all, increment shortname if needed" from "Upload mode"
     And I click on "Preview" "button"
     When I click on "Upload courses" "button"
index 3a06d16..457f07d 100644 (file)
@@ -15,7 +15,7 @@ Feature: An admin can update courses using a CSV file
 
   @javascript
   Scenario: Updating a course fullname
-    Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filepicker
+    Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
     And I select "Only update existing courses" from "Upload mode"
     And I select "Update with CSV data only" from "Update mode"
     And I click on "Preview" "button"
index 8f86212..fd33545 100644 (file)
@@ -651,9 +651,6 @@ class tool_uploadcourse_course_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_restore_file() {
@@ -703,9 +700,6 @@ class tool_uploadcourse_course_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_restore_invalid_file() {
index ae42a1e..41a838c 100644 (file)
@@ -204,9 +204,6 @@ class tool_uploadcourse_helper_testcase extends advanced_testcase {
         $this->assertEquals($dir, $dir2);
 
         $CFG->keeptempdirectoriesonbackup = $oldcfg;
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_get_role_ids() {
index 648fcc6..1dd20dc 100644 (file)
@@ -96,9 +96,6 @@ class tool_uploadcourse_processor_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_restore_restore_file() {
@@ -136,9 +133,6 @@ class tool_uploadcourse_processor_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_shortname_template() {
index f6a178e..a45cb47 100644 (file)
@@ -36,7 +36,7 @@ require_once('user_form.php');
 $iid         = optional_param('iid', '', PARAM_INT);
 $previewrows = optional_param('previewrows', 10, PARAM_INT);
 
-@set_time_limit(60*60); // 1 hour should be enough
+core_php_time_limit::raise(60*60); // 1 hour should be enough
 raise_memory_limit(MEMORY_HUGE);
 
 require_login();
@@ -549,8 +549,6 @@ if ($formdata = $mform2->is_cancelled()) {
                         continue;
                     }
                     if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
-                        // this should never happen
-                        debugging("Could not find $column on the user objects", DEBUG_DEVELOPER);
                         continue;
                     }
                     if ($updatetype == UU_UPDATE_MISSING) {
index fe3d8e5..03091aa 100644 (file)
@@ -72,7 +72,7 @@ if ($formdata = $mform->get_data()) {
         // Large files are likely to take their time and memory. Let PHP know
         // that we'll take longer, and that the process should be recycled soon
         // to free up memory.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Create a unique temporary directory, to process the zip file
index 3aef57c..dea693b 100644 (file)
@@ -207,12 +207,14 @@ class web_service_token_form extends moodleform {
 
             if ($usertotal < 500) {
                 list($sort, $params) = users_order_by_sql('u');
-                //user searchable selector - get all users (admin and guest included)
-                //user must be confirmed, not deleted, not suspended, not guest
-                $sql = "SELECT u.id, u.firstname, u.lastname
-                            FROM {user} u
-                            WHERE u.deleted = 0 AND u.confirmed = 1 AND u.suspended = 0 AND u.id != :siteguestid
-                            ORDER BY $sort";
+                // User searchable selector - return users who are confirmed, not deleted, not suspended and not a guest.
+                $sql = 'SELECT u.id, ' . get_all_user_name_fields(true, 'u') . '
+                        FROM {user} u
+                        WHERE u.deleted = 0
+                        AND u.confirmed = 1
+                        AND u.suspended = 0
+                        AND u.id != :siteguestid
+                        ORDER BY ' . $sort;
                 $params['siteguestid'] = $CFG->siteguest;
                 $users = $DB->get_records_sql($sql, $params);
                 $options = array();
index 333142a..3b80e26 100644 (file)
@@ -399,6 +399,9 @@ class auth_plugin_db extends auth_plugin_base {
                 if (empty($user->lang)) {
                     $user->lang = $CFG->lang;
                 }
+                if (empty($user->calendartype)) {
+                    $user->calendartype = $CFG->calendartype;
+                }
                 $user->timecreated = time();
                 $user->timemodified = $user->timecreated;
                 if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) {
@@ -664,7 +667,7 @@ class auth_plugin_db extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        if ($this->is_internal()) {
+        if ($this->is_internal() || empty($this->config->changepasswordurl)) {
             // Standard form.
             return null;
         } else {
index a670cfc..b071e32 100644 (file)
@@ -90,6 +90,9 @@ class auth_plugin_email extends auth_plugin_base {
         require_once($CFG->dirroot.'/user/lib.php');
 
         $user->password = hash_internal_user_password($user->password);
+        if (empty($user->calendartype)) {
+            $user->calendartype = $CFG->calendartype;
+        }
 
         $user->id = user_create_user($user, false);
 
index 442f93e..b0d7f9f 100644 (file)
@@ -73,6 +73,10 @@ class auth_plugin_imap extends auth_plugin_base {
                     $host = '{'.$host.":{$this->config->port}/imap/tls}";
                 break;
 
+                case 'imapnosslcert':
+                    $host = '{'.$host.":{$this->config->port}/imap/novalidate-cert}";
+                break;
+
                 default:
                     $host = '{'.$host.":{$this->config->port}/imap}";
             }
@@ -120,7 +124,11 @@ class auth_plugin_imap extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        return new moodle_url($this->config->changepasswordurl);
+        if (!empty($this->config->changepasswordurl)) {
+            return new moodle_url($this->config->changepasswordurl);
+        } else {
+            return null;
+        }
     }
 
     /**
index e36fdd0..914d14f 100644 (file)
@@ -44,7 +44,7 @@ if (!isset($config->changepasswordurl)) {
     <td>
         <?php
 
-        $imaptypes = array('imap', 'imapssl', 'imapcert', 'imaptls');
+        $imaptypes = array('imap', 'imapssl', 'imapcert', 'imapnosslcert', 'imaptls');
         foreach ($imaptypes as $imaptype) {
             $imapoptions[$imaptype] = $imaptype;
         }
index f92c08d..a847112 100644 (file)
@@ -945,6 +945,9 @@ class auth_plugin_ldap extends auth_plugin_base {
                 if (empty($user->lang)) {
                     $user->lang = $CFG->lang;
                 }
+                if (empty($user->calendartype)) {
+                    $user->calendartype = $CFG->calendartype;
+                }
 
                 $id = user_create_user($user, false);
                 echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
@@ -1603,7 +1606,11 @@ class auth_plugin_ldap extends auth_plugin_base {
      */
     function change_password_url() {
         if (empty($this->config->stdchangepassword)) {
-            return new moodle_url($this->config->changepasswordurl);
+            if (!empty($this->config->changepasswordurl)) {
+                return new moodle_url($this->config->changepasswordurl);
+            } else {
+                return null;
+            }
         } else {
             return null;
         }
index 6b4bdff..5e25aa9 100644 (file)
@@ -120,7 +120,11 @@ class auth_plugin_pop3 extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        return new moodle_url($this->config->changepasswordurl);
+        if (!empty($this->config->changepasswordurl)) {
+            return new moodle_url($this->config->changepasswordurl);
+        } else {
+            return null;
+        }
     }
 
     /**
index 6085397..8334fed 100644 (file)
@@ -1,6 +1,10 @@
 This files describes API changes in /auth/* - plugins,
 information provided here is intended especially for developers.
 
+=== 2.7 ===
+
+* If you are returning a url in method change_password_url() from config, please make sure it is set before trying to use it.
+
 === 2.6 ===
 
 * can_be_manually_set() - This function was introduced in the base class and returns false by default. If overriden by
index 1fc1e99..1fc5694 100644 (file)
@@ -90,10 +90,12 @@ class auth_plugin_webservice extends auth_plugin_base {
     /**
      * Returns true if this authentication plugin is 'internal'.
      *
+     * Webserice auth doesn't use password fields, it uses only tokens.
+     *
      * @return bool
      */
     function is_internal() {
-        return true;
+        return false;
     }
 
     /**
index 1c1cf29..0f3bf7a 100644 (file)
@@ -130,6 +130,13 @@ if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
     // Carry out actual backup.
     $backup->execute();
 
+    // Backup controller gets saved/loaded so the logger object changes and we
+    // have to retrieve it.
+    $logger = $backup->get_controller()->get_logger();
+    while (!is_a($logger, 'core_backup_html_logger')) {
+        $logger = $logger->get_next();
+    }
+
     // Get HTML from logger.
     $loghtml = $logger->get_html();
 
index f38d263..345831c 100644 (file)
@@ -307,7 +307,7 @@ class backup_controller extends base_controller {
      */
     public function execute_plan() {
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         // If this is not a course backup, inform the plan we are not
         // including all the activities for sure. This will affect any
index 3349a18..f4d3958 100644 (file)
@@ -315,7 +315,7 @@ class restore_controller extends base_controller {
 
     public function execute_plan() {
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         // If this is not a course restore, inform the plan we are not
         // including all the activities for sure. This will affect any
@@ -349,7 +349,7 @@ class restore_controller extends base_controller {
             throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status);
         }
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         $this->precheck = restore_prechecks_helper::execute_prechecks($this, $droptemptablesafter);
         if (!array_key_exists('errors', $this->precheck)) { // No errors, can be executed
@@ -420,7 +420,7 @@ class restore_controller extends base_controller {
         require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
 
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         $this->progress->start_progress('Backup format conversion');
 
index 12929de..b5c32a5 100644 (file)
@@ -107,7 +107,19 @@ class file_nested_element extends backup_nested_element {
             backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
         } catch (file_exception $e) {
             $this->add_result(array('missing_files_in_pool' => true));
-            $this->add_log('missing file in pool: ' . $e->debuginfo, backup::LOG_WARNING);
+
+            // Build helpful log message with all information necessary to identify
+            // file location.
+            $context = context::instance_by_id($values->contextid, IGNORE_MISSING);
+            $contextname = '';
+            if ($context) {
+                $contextname = ' \'' . $context->get_context_name() . '\'';
+            }
+            $message = 'Missing file in pool: ' . $values->filepath  . $values->filename .
+                    ' (context ' . $values->contextid . $contextname . ', component ' .
+                    $values->component . ', filearea ' . $values->filearea . ', itemid ' .
+                    $values->itemid . ') [' . $e->debuginfo . ']';
+            $this->add_log($message, backup::LOG_WARNING);
         }
     }
 }
index a6d5bce..00fb8f3 100644 (file)
@@ -995,11 +995,7 @@ abstract class restore_dbops {
                 if ($includesfiles) {
                     // The file is not found in the backup.
                     if (!file_exists($backuppath)) {
-                        $result = new stdClass();
-                        $result->code = 'file_missing_in_backup';
-                        $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
-                        $result->level = backup::LOG_WARNING;
-                        $results[] = $result;
+                        $results[] = self::get_missing_file_result($file);
                         continue;
                     }
 
@@ -1028,11 +1024,7 @@ abstract class restore_dbops {
                             $fs->create_file_from_storedfile($file_record, $foundfile->id);
                         } else {
                             // A matching existing file record was not found in the database.
-                            $result = new stdClass();
-                            $result->code = 'file_missing_in_backup';
-                            $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
-                            $result->level = backup::LOG_WARNING;
-                            $results[] = $result;
+                            $results[] = self::get_missing_file_result($file);
                             continue;
                         }
                     }
@@ -1063,6 +1055,22 @@ abstract class restore_dbops {
         return $results;
     }
 
+    /**
+     * Returns suitable entry to include in log when there is a missing file.
+     *
+     * @param stdClass $file File definition
+     * @return stdClass Log entry
+     */
+    protected static function get_missing_file_result($file) {
+        $result = new stdClass();
+        $result->code = 'file_missing_in_backup';
+        $result->message = 'Missing file in backup: ' . $file->filepath  . $file->filename .
+                ' (old context ' . $file->contextid . ', component ' . $file->component .
+                ', filearea ' . $file->filearea . ', old itemid ' . $file->itemid . ')';
+        $result->level = backup::LOG_WARNING;
+        return $result;
+    }
+
     /**
      * Given one restoreid, create in DB all the users present
      * in backup_ids having newitemid = 0, as far as
index 914e4ab..6e13b42 100644 (file)
@@ -111,7 +111,7 @@ abstract class backup_cron_automated_helper {
             cron_trace_time_and_memory();
 
             // This could take a while!
-            @set_time_limit(0);
+            core_php_time_limit::raise();
             raise_memory_limit(MEMORY_EXTRA);
 
             $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup($admin->timezone, $now);
index ed7a70b..44afd8d 100644 (file)
@@ -132,9 +132,6 @@ class backup_step_testcase extends advanced_testcase {
 
         // Remove the test dir and any content
         @remove_dir(dirname($file));
-
-        // Clear the time limit, otherwise PHPUnit complains.
-        set_time_limit(0);
     }
 
     /**
index 74c80f6..7204e58 100644 (file)
@@ -31,9 +31,14 @@ abstract class core_backup_progress {
     const INDETERMINATE = -1;
 
     /**
+     * This value is set rather high to ensure there are no regressions from
+     * previous behaviour. For testing, it may be useful to set the
+     * frontendservertimeout config option to a lower value, such as 180
+     * seconds (default for some commercial products).
+     *
      * @var int The number of seconds that can pass without progress() calls.
      */
-    const TIME_LIMIT_WITHOUT_PROGRESS = 120;
+    const TIME_LIMIT_WITHOUT_PROGRESS = 3600;
 
     /**
      * @var int Time of last progress call.
@@ -201,7 +206,9 @@ abstract class core_backup_progress {
         // Update progress.
         $this->count++;
         $this->lastprogresstime = $now;
-        set_time_limit(self::TIME_LIMIT_WITHOUT_PROGRESS);
+
+        // Update time limit before next progress display.
+        core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
         $this->update_progress();
     }
 
index 28f9f03..1e4f8c8 100644 (file)
@@ -56,9 +56,11 @@ class backup_progress_testcase extends basic_testcase {
 
         // Make some progress and check that the time limit gets added.
         $progress->step_time();
+        core_php_time_limit::get_and_clear_unit_test_data();
         $progress->progress(2);
         $this->assertTrue($progress->was_update_called());
-        $this->assertEquals(120, ini_get('max_execution_time'));
+        $this->assertEquals(array(core_backup_progress::TIME_LIMIT_WITHOUT_PROGRESS),
+                core_php_time_limit::get_and_clear_unit_test_data());
 
         // Check the new value.
         $this->assert_min_max(0.2, 0.2, $progress);
@@ -77,9 +79,6 @@ class backup_progress_testcase extends basic_testcase {
 
         // There was 1 progress call.
         $this->assertEquals(1, $progress->get_progress_count());
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
@@ -158,8 +157,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.8, 0.8, $progress);
         $progress->end_progress();
         $this->assertFalse($progress->is_in_progress_section());
-
-        set_time_limit(0);
     }
 
     /**
@@ -192,8 +189,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.4, 1.0, $progress);
         $progress->end_progress();
         $this->assert_min_max(1.0, 1.0, $progress);
-
-        set_time_limit(0);
     }
 
     /**
@@ -208,9 +203,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.01, 0.01, $progress);
         $progress->end_progress();
         $this->assert_min_max(0.01, 0.01, $progress);
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
@@ -231,8 +223,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.02, 0.02, $progress);
         $progress->end_progress();
         $this->assert_min_max(0.02, 0.02, $progress);
-
-        set_time_limit(0);
     }
 
     /**
@@ -329,9 +319,6 @@ class backup_progress_testcase extends basic_testcase {
         } catch (coding_exception $e) {
             $this->assertEquals(1, preg_match('~would exceed max~', $e->getMessage()));
         }
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
index a252ec3..262142e 100644 (file)
@@ -73,6 +73,14 @@ abstract class base_moodleform extends moodleform {
      */
     function __construct(base_ui_stage $uistage, $action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true) {
         $this->uistage = $uistage;
+        // Add a class to the attributes to prevent the default collapsible behaviour.
+        if (!$attributes) {
+            $attributes = array();
+        }
+        $attributes['class'] = 'unresponsive';
+        if (!isset($attributes['enctype'])) {
+            $attributes['enctype'] = 'application/x-www-form-urlencoded'; // Enforce compatibility with our max_input_vars hack.
+        }
         parent::__construct($action, $customdata, $method, $target, $attributes, $editable);
     }
     /**
@@ -340,12 +348,12 @@ abstract class base_moodleform extends moodleform {
         $config->yesLabel = get_string('confirmcancelyes', 'backup');
         $config->noLabel = get_string('confirmcancelno', 'backup');
         $config->closeButtonTitle = get_string('close', 'editor');
-        $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.watch_cancel_buttons', array($config));
+        $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.confirmcancel.watch_cancel_buttons', array($config));
 
         // Get list of module types on course.
         $modinfo = get_fast_modinfo($COURSE);
         $modnames = $modinfo->get_used_module_names(true);
-        $PAGE->requires->yui_module('moodle-backup-backupselectall', 'M.core_backup.select_all_init',
+        $PAGE->requires->yui_module('moodle-backup-backupselectall', 'M.core_backup.backupselectall',
                 array($modnames));
         $PAGE->requires->strings_for_js(array('select', 'all', 'none'), 'moodle');
         $PAGE->requires->strings_for_js(array('showtypes', 'hidetypes'), 'backup');
index 6275e60..ef559fd 100644 (file)
@@ -846,6 +846,7 @@ class restore_ui_stage_process extends restore_ui_stage {
         $html .= html_writer::start_tag('form', array(
             'action'    => $url->out_omit_querystring(),
             'class'     => 'backup-restore',
+            'enctype'   => 'application/x-www-form-urlencoded', // Enforce compatibility with our max_input_vars hack.
             'method'    => 'post'));
         foreach ($url->params() as $name => $value) {
             $html .= html_writer::empty_tag('input', array(
index ddc406e..60b1871 100644 (file)
@@ -38,7 +38,7 @@ Feature: Backup Moodle courses
     And I press "Continue"
     And I click on "Continue" "button" in the ".bcs-current-course" "css_element"
     And "//div[contains(concat(' ', normalize-space(@class), ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element" should exists
-    And I check "Include course logs"
+    And "Include course logs" "checkbox" should exists
     And I press "Next"
 
   @javascript
index d06185e..84fb672 100644 (file)
@@ -56,27 +56,32 @@ class behat_backup extends behat_base {
 
         // Go to homepage.
         $this->getSession()->visit($this->locate_path('/'));
+        $this->wait();
 
         // Click the course link.
         $this->find_link($backupcourse)->click();
+        $this->wait();
 
         // Click the backup link.
         $this->find_link(get_string('backup'))->click();
+        $this->wait();
 
         // Initial settings.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('backupstage1action', 'backup'))->press();
+        $this->wait();
 
         // Schema settings.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('backupstage2action', 'backup'))->press();
+        $this->wait();
 
         // Confirmation and review, backup filename can also be specified.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('backupstage4action', 'backup'))->press();
 
         // Waiting for it to finish.
-        $this->wait(10);
+        $this->wait(self::EXTENDED_TIMEOUT);
 
         // Last backup continue button.
         $this->find_button(get_string('backupstage16action', 'backup'))->press();
@@ -101,12 +106,15 @@ class behat_backup extends behat_base {
 
         // Go to homepage.
         $this->getSession()->visit($this->locate_path('/'));
+        $this->wait();
 
         // Click the course link.
         $this->find_link($tocourse)->click();
+        $this->wait();
 
         // Click the import link.
         $this->find_link(get_string('import'))->click();
+        $this->wait();
 
         // Select the course.
         $exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from', $this->getSession());
@@ -121,18 +129,21 @@ class behat_backup extends behat_base {
         $radionode->click();
 
         $this->find_button(get_string('continue'))->press();
+        $this->wait();
 
         // Initial settings.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('importbackupstage1action', 'backup'))->press();
+        $this->wait();
 
         // Schema settings.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('importbackupstage2action', 'backup'))->press();
+        $this->wait();
 
         // Run it.
         $this->find_button(get_string('importbackupstage4action', 'backup'))->press();
-        $this->wait();
+        $this->wait(self::EXTENDED_TIMEOUT);
 
         // Continue and redirect to 'to' course.
         $this->find_button(get_string('continue'))->press();
@@ -294,17 +305,22 @@ class behat_backup extends behat_base {
         // Settings.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('restorestage4action', 'backup'))->press();
+        $this->wait();
 
         // Schema.
         $this->fill_backup_restore_form($options);
         $this->find_button(get_string('restorestage8action', 'backup'))->press();
+        $this->wait();
 
         // Review, no options here.
         $this->find_button(get_string('restorestage16action', 'backup'))->press();
-        $this->wait(10);
+        $this->wait();
 
         // Last restore continue button, redirected to restore course after this.
         $this->find_button(get_string('restorestage32action', 'backup'))->press();
+
+        // Long wait when waiting for the restore to finish.
+        $this->wait(self::EXTENDED_TIMEOUT);
     }
 
     /**
@@ -325,11 +341,15 @@ class behat_backup extends behat_base {
             return;
         }
 
+        // Wait for the page to be loaded and the JS ready.
+        $this->wait();
+
         // If we find any of the provided options in the current form we should set the value.
         $datahash = $options->getRowsHash();
         foreach ($datahash as $locator => $value) {
 
             try {
+                // Using $this->find* to enforce stability over speed.
                 $fieldnode = $this->find_field($locator);
                 $field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
                 $field->set_value($value);
@@ -341,9 +361,9 @@ class behat_backup extends behat_base {
     }
 
     /**
-     * Waits until the DOM is ready.
+     * Waits until the DOM and the page Javascript code is ready.
      *
-     * @param int To override the default timeout
+     * @param int $timeout The number of seconds that we wait.
      * @return void
      */
     protected function wait($timeout = false) {
@@ -355,7 +375,8 @@ class behat_backup extends behat_base {
         if (!$timeout) {
             $timeout = self::TIMEOUT;
         }
-        $this->getSession()->wait($timeout, '(document.readyState === "complete")');
+
+        $this->getSession()->wait($timeout * 1000, self::PAGE_READY_JS);
     }
 
 }
index 9675a62..3314372 100644 (file)
@@ -21,8 +21,8 @@ Feature: Duplicate activities
     And I add a "Database" to section "1" and I fill the form with:
       | Name | Test database name |
       | Description | Test database description |
-    And I open "Test database name" actions menu
-    When I click on "Duplicate" "link" in the "Test database name" activity
+    And I duplicate "Test database name" activity
+    And I wait until section "1" is available
     And I open "Test database name" actions menu
     And I click on "Edit settings" "link" in the "Test database name" activity
     And I fill the moodle form with:
diff --git a/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js
new file mode 100644 (file)
index 0000000..3de94d5
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js
new file mode 100644 (file)
index 0000000..b001213
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js
new file mode 100644 (file)
index 0000000..3de94d5
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js
new file mode 100644 (file)
index 0000000..b41423d
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js
new file mode 100644 (file)
index 0000000..db30391
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js
new file mode 100644 (file)
index 0000000..b41423d
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js differ
diff --git a/backup/util/ui/yui/confirmcancel/confirmcancel.js b/backup/util/ui/yui/confirmcancel/confirmcancel.js
deleted file mode 100644 (file)
index 77deaf1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-YUI.add('moodle-backup-confirmcancel', function(Y) {
-
-// Namespace for the backup
-M.core_backup = M.core_backup || {};
-/**
- * Adds confirmation dialogues to the cancel buttons on the page.
- *
- * @param {object} config
- */
-M.core_backup.watch_cancel_buttons = function(config) {
-    Y.all('.confirmcancel').each(function(){
-        this._confirmationListener = this._confirmationListener || this.on('click', function(e){
-            // Prevent the default event (sumbit) from firing
-            e.preventDefault();
-            // Create the confirm box
-            var confirm = new M.core.confirm(config);
-            // If the user clicks yes
-            confirm.on('complete-yes', function(e){
-                // Detach the listener for the confirm box so it doesn't fire again.
-                this._confirmationListener.detach();
-                // Simulate the original cancel button click
-                this.simulate('click');
-            }, this);
-            // Show the confirm box
-            confirm.show();
-        }, this);
-    });
-}
-
-}, '@VERSION@', {'requires':['base','node','node-event-simulate','moodle-core-notification']});
diff --git a/backup/util/ui/yui/src/backupselectall/build.json b/backup/util/ui/yui/src/backupselectall/build.json
new file mode 100644 (file)
index 0000000..85b6072
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "name": "moodle-backup-backupselectall",
+  "builds": {
+    "moodle-backup-backupselectall": {
+      "jsfiles": [
+        "backupselectall.js"
+      ]
+    }
+  }
+}
@@ -1,12 +1,18 @@
-YUI.add('moodle-backup-backupselectall', function(Y) {
+/**
+ * Adds select all/none links to the top of the backup/restore/import schema page.
+ *
+ * @module moodle-backup-backupselectall
+ */
 
 // Namespace for the backup
 M.core_backup = M.core_backup || {};
 
 /**
  * Adds select all/none links to the top of the backup/restore/import schema page.
+ *
+ * @class M.core_backup.backupselectall
  */
-M.core_backup.select_all_init = function(modnames) {
+M.core_backup.backupselectall = function(modnames) {
     var formid = null;
 
     var helper = function(e, check, type, mod) {
@@ -23,7 +29,7 @@ M.core_backup.select_all_init = function(modnames) {
             if (prefix && name.substring(0, prefix.length) !== prefix) {
                 return;
             }
-            if (name.substring(name.length - len) == type) {
+            if (name.substring(name.length - len) === type) {
                 checkbox.set('checked', check);
             }
         });
@@ -67,9 +73,9 @@ M.core_backup.select_all_init = function(modnames) {
     var withuserdata = false;
     Y.all('input[type="checkbox"]').each(function(checkbox) {
         var name = checkbox.get('name');
-        if (name.substring(name.length - 9) == '_userdata') {
+        if (name.substring(name.length - 9) === '_userdata') {
             withuserdata = '_userdata';
-        } else if (name.substring(name.length - 9) == '_userinfo') {
+        } else if (name.substring(name.length - 9) === '_userinfo') {
             withuserdata = '_userinfo';
         }
     });
@@ -103,7 +109,7 @@ M.core_backup.select_all_init = function(modnames) {
         if (!modnames.hasOwnProperty(mod)) {
             continue;
         }
-        var html = html_generator('include_setting section_level', 'mod_' + mod, modnames[mod]);
+        html = html_generator('include_setting section_level', 'mod_' + mod, modnames[mod]);
         if (withuserdata) {
             html += html_generator('normal_setting', 'userdata-mod_' + mod, modnames[mod]);
         }
@@ -127,26 +133,27 @@ M.core_backup.select_all_init = function(modnames) {
         modlist.currentlyshown = !modlist.currentlyshown;
 
         // Either hide or show the links.
-        var animcfg = { node: modlist, duration: 0.2 };
+        var animcfg = { node: modlist, duration: 0.2 },
+            anim;
         if (modlist.currentlyshown) {
             // Animate reveal of the module links.
             modlist.show();
             animcfg.to = { maxHeight: modlist.get('clientHeight') + 'px' };
             modlist.setStyle('maxHeight', '0px');
-            var anim = new Y.Anim(animcfg);
+            anim = new Y.Anim(animcfg);
             anim.on('end', function() { modlist.setStyle('maxHeight', 'none'); });
             anim.run();
         } else {
             // Animate hide of the module links.
             animcfg.to = { maxHeight: '0px' };
             modlist.setStyle('maxHeight', modlist.get('clientHeight') + 'px');
-            var anim = new Y.Anim(animcfg);
+            anim = new Y.Anim(animcfg);
             anim.on('end', function() { modlist.hide(); modlist.setStyle('maxHeight', 'none'); });
             anim.run();
         }
 
     };
-    Y.one('#backup-bytype').on('click', function(e) { toggletypes(); });
+    Y.one('#backup-bytype').on('click', function() { toggletypes(); });
 
     Y.one('#backup-all-included').on('click',  function(e) { helper(e, true,  '_included'); });
     Y.one('#backup-none-included').on('click', function(e) { helper(e, false, '_included'); });
@@ -154,6 +161,4 @@ M.core_backup.select_all_init = function(modnames) {
         Y.one('#backup-all-userdata').on('click',  function(e) { helper(e, true,  withuserdata); });
         Y.one('#backup-none-userdata').on('click', function(e) { helper(e, false, withuserdata); });
     }
-}
-
-}, '@VERSION@', {'requires':['base', 'node', 'event', 'node-event-simulate', 'anim']});
+};
diff --git a/backup/util/ui/yui/src/backupselectall/js/backupselectall.json b/backup/util/ui/yui/src/backupselectall/js/backupselectall.json
new file mode 100644 (file)
index 0000000..81f6ce7
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "moodle-backup-backupselectall": {
+    "requires": [
+        "node",
+        "event",
+        "node-event-simulate",
+        "anim"
+    ]
+  }
+}
diff --git a/backup/util/ui/yui/src/backupselectall/meta/backupselectall.json b/backup/util/ui/yui/src/backupselectall/meta/backupselectall.json
new file mode 100644 (file)
index 0000000..81f6ce7
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "moodle-backup-backupselectall": {
+    "requires": [
+        "node",
+        "event",
+        "node-event-simulate",
+        "anim"
+    ]
+  }
+}
diff --git a/backup/util/ui/yui/src/confirmcancel/build.json b/backup/util/ui/yui/src/confirmcancel/build.json
new file mode 100644 (file)
index 0000000..603210c
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name": "moodle-backup-confirmcancel",
+    "builds": {
+        "moodle-backup-confirmcancel": {
+            "jsfiles": [
+                "confirmcancel.js"
+            ]
+        }
+    }
+}
diff --git a/backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js b/backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js
new file mode 100644 (file)
index 0000000..5128d2d
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * Add a confirmation dialogue when cancelling a backup.
+ *
+ * @module moodle-backup-confirmcancel
+ */
+
+/**
+ * Add a confirmation dialogue when cancelling a backup.
+ *
+ * @class M.core_backup.confirmcancel
+ */
+
+
+// Namespace for the backup.
+M.core_backup = M.core_backup || {};
+
+M.core_backup.confirmcancel = {
+    /**
+     * An array of EventHandlers which call the confirm_cancel dialogue.
+     *
+     * @property listeners
+     * @protected
+     * @type Array
+     */
+    listeners: [],
+
+    /**
+     * The configuration supplied to this instance.
+     *
+     * @property config
+     * @protected
+     * @type Object
+     */
+    config: {},
+
+    /**
+     * Initializer to watch all cancel buttons.
+     *
+     * @method watch_cancel_buttons
+     * @param {Object} config The configuration for the confirmation dialogue.
+     */
+    watch_cancel_buttons: function(config) {
+        this.config = config;
+
+        this.listeners.push(
+            Y.one(Y.config.doc.body).delegate('click', this.confirm_cancel, '.confirmcancel', this)
+        );
+    },
+
+    /**
+     * Display the confirmation dialogue.
+     *
+     * @method confirm_cancel
+     * @protected
+     * @param {EventFacade} e
+     */
+    confirm_cancel: function(e) {
+        // Prevent the default event (submit) from firing.
+        e.preventDefault();
+
+        // Create the confirmation dialogue.
+        var confirm = new M.core.confirm(this.config);
+
+        // If the user clicks yes.
+        confirm.on('complete-yes', function(){
+            // Detach the listeners for the confirm box so they don't fire again.
+            new Y.EventHandle(M.core_backup.confirmcancel.listeners).detach();
+
+            // Simulate the original cancel button click.
+            e.currentTarget.simulate('click');
+        }, this);
+
+
+        // Show the confirm box.
+        confirm.show();
+    }
+};
diff --git a/backup/util/ui/yui/src/confirmcancel/meta/confirmcancel.json b/backup/util/ui/yui/src/confirmcancel/meta/confirmcancel.json
new file mode 100644 (file)
index 0000000..e352bdf
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "moodle-backup-confirmcancel": {
+        "requires": [
+            "node",
+            "node-event-simulate",
+            "moodle-core-notification-confirm"
+        ]
+    }
+}
index 9d4b5fa..64bf704 100644 (file)
@@ -36,7 +36,7 @@ Feature: Add badges to the system
       | Description | Test badge description |
       | issuername | Test Badge Site |
       | issuercontact | testuser@test-badge-site.com |
-    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     When I press "Create badge"
     Then I should see "Edit details"
     And I should see "Test Badge"
index d34d36e..458df34 100644 (file)
@@ -4,13 +4,10 @@ Feature: Award badges
   As an admin
   I need to add criteria to badges in the system
 
-  Background:
-    Given I am on homepage
-    And I log in as "admin"
-
   @javascript
   Scenario: Award profile badge
-    Given I expand "Site administration" node
+    Given I log in as "admin"
+    And I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
     And I fill the moodle form with:
@@ -18,7 +15,7 @@ Feature: Award badges
       | Description | Test badge description |
       | issuername | Test Badge Site |
       | issuercontact | testuser@test-badge-site.com |
-    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
     And I select "Profile completion" from "type"
     And I check "First name"
@@ -46,6 +43,7 @@ Feature: Award badges
       | username | firstname | lastname | email |
       | teacher | teacher | 1 | teacher1@asd.com |
       | student | student | 1 | student1@asd.com |
+    And I log in as "admin"
     And I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
@@ -53,7 +51,7 @@ Feature: Award badges
       | Name | Site Badge |
       | Description | Site badge description |
       | issuername | Tester of site badge |
-    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
     And I select "Manual issue by role" from "type"
     And I check "Teacher"
@@ -89,7 +87,6 @@ Feature: Award badges
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
       | student2 | C1 | student |
-    And I log out
     And I log in as "teacher1"
     And I follow "Course 1"
     And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
@@ -98,7 +95,7 @@ Feature: Award badges
       | Name | Course Badge |
       | Description | Course badge description |
       | issuername | Tester of course badge |
-    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
     And I select "Manual issue by role" from "type"
     And I check "Teacher"
@@ -133,7 +130,6 @@ Feature: Award badges
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And I log out
     And I log in as "admin"
     And I set the following administration settings values:
       | Enable completion tracking | 1 |
@@ -156,7 +152,7 @@ Feature: Award badges
       | Name | Course Badge |
       | Description | Course badge description |
       | issuername | Tester of course badge |
-    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
     And I select "Activity completion" from "type"
     And I check "Test assignment name"
@@ -172,7 +168,6 @@ Feature: Award badges
     And I follow "Home"
     And I follow "Course 1"
     And I press "Mark as complete: Test assignment name"
-    And I wait "2" seconds
     And I expand "My profile" node
     And I follow "My badges"
     Then I should see "Course Badge"
@@ -190,7 +185,6 @@ Feature: Award badges
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And I log out
     And I log in as "admin"
     And I set the following administration settings values:
       | Enable completion tracking | 1 |
@@ -219,7 +213,7 @@ Feature: Award badges
       | Name | Course Badge |
       | Description | Course badge description |
       | issuername | Tester of course badge |
-    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
     And I select "Course completion" from "type"
     And I fill the moodle form with:
diff --git a/blocks/activity_modules/tests/behat/block_activity_modules.feature b/blocks/activity_modules/tests/behat/block_activity_modules.feature
new file mode 100644 (file)
index 0000000..ba76908
--- /dev/null
@@ -0,0 +1,176 @@
+@block @block_activity_modules @_only_local
+Feature: Block activity modules
+  In order to overview activity modules in a course
+  As a manager
+  I can add activities block in a course or on the frontpage
+
+  Background:
+    Given I log in as "admin"
+    And I expand "Site administration" node
+    And I expand "Plugins" node
+    And I expand "Activity modules" node
+    And I follow "Manage activities"
+    And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Feedback" "table_row"
+    And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Assignment (2.2)" "table_row"
+
+  Scenario: Add activities block on the frontpage
+    And the following "activities" exists:
+      | activity   | name                        | intro                              | course               | idnumber    |
+      | assign     | Frontpage assignment name   | Frontpage assignment description   | Acceptance test site | assign0     |
+      | assignment | Frontpage assignment22 name | Frontpage assignment22 description | Acceptance test site | assignment0 |
+      | book       | Frontpage book name         | Frontpage book description         | Acceptance test site | book0       |
+      | chat       | Frontpage chat name         | Frontpage chat description         | Acceptance test site | chat0       |
+      | choice     | Frontpage choice name       | Frontpage choice description       | Acceptance test site | choice0     |
+      | data       | Frontpage database name     | Frontpage database description     | Acceptance test site | data0       |
+      | feedback   | Frontpage feedback name     | Frontpage feedback description     | Acceptance test site | feedback0   |
+      | forum      | Frontpage forum name        | Frontpage forum description        | Acceptance test site | forum0      |
+      | label      | Frontpage label name        | Frontpage label description        | Acceptance test site | label0      |
+      | lti        | Frontpage lti name          | Frontpage lti description          | Acceptance test site | lti0        |
+      | page       | Frontpage page name         | Frontpage page description         | Acceptance test site | page0       |
+      | quiz       | Frontpage quiz name         | Frontpage quiz description         | Acceptance test site | quiz0       |
+      | resource   | Frontpage resource name     | Frontpage resource description     | Acceptance test site | resource0   |
+      | imscp      | Frontpage imscp name        | Frontpage imscp description        | Acceptance test site | imscp0      |
+      | folder     | Frontpage folder name       | Frontpage folder description       | Acceptance test site | folder0     |
+      | glossary   | Frontpage glossary name     | Frontpage glossary description     | Acceptance test site | glossary0   |
+      | scorm      | Frontpage scorm name        | Frontpage scorm description        | Acceptance test site | scorm0      |
+      | lesson     | Frontpage lesson name       | Frontpage lesson description       | Acceptance test site | lesson0     |
+      | survey     | Frontpage survey name       | Frontpage survey description       | Acceptance test site | survey0     |
+      | url        | Frontpage url name          | Frontpage url description          | Acceptance test site | url0        |
+      | wiki       | Frontpage wiki name         | Frontpage wiki description         | Acceptance test site | wiki0       |
+      | workshop   | Frontpage workshop name     | Frontpage workshop description     | Acceptance test site | workshop0   |
+
+    And I am on homepage
+    When I follow "Turn editing on"
+    And I add the "Activities" block
+    And I click on "Assignments" "link" in the "Activities" "block"
+    Then I should see "Frontpage assignment name"
+    And I am on homepage
+    And I click on "Assignments (2.2)" "link" in the "Activities" "block"
+    And I should see "Frontpage assignment22 name"
+    And I am on homepage
+    And I click on "Chats" "link" in the "Activities" "block"
+    And I should see "Frontpage chat name"
+    And I am on homepage
+    And I click on "Choices" "link" in the "Activities" "block"
+    And I should see "Frontpage choice name"
+    And I am on homepage
+    And I click on "Databases" "link" in the "Activities" "block"
+    And I should see "Frontpage database name"
+    And I am on homepage
+    And I click on "Feedback" "link" in the "Activities" "block"
+    And I should see "Frontpage feedback name"
+    And I am on homepage
+    And I click on "Forums" "link" in the "Activities" "block"
+    And I should see "Frontpage forum name"
+    And I am on homepage
+    And I click on "External Tools" "link" in the "Activities" "block"
+    And I should see "Frontpage lti name"
+    And I am on homepage
+    And I click on "Quizzes" "link" in the "Activities" "block"
+    And I should see "Frontpage quiz name"
+    And I am on homepage
+    And I click on "Glossaries" "link" in the "Activities" "block"
+    And I should see "Frontpage glossary name"
+    And I am on homepage
+    And I click on "SCORM packages" "link" in the "Activities" "block"
+    And I should see "Frontpage scorm name"
+    And I am on homepage
+    And I click on "Lessons" "link" in the "Activities" "block"
+    And I should see "Frontpage lesson name"
+    And I am on homepage
+    And I click on "Wikis" "link" in the "Activities" "block"
+    And I should see "Frontpage wiki name"
+    And I am on homepage
+    And I click on "Workshop" "link" in the "Activities" "block"
+    And I should see "Frontpage workshop name"
+    And I am on homepage
+    And I click on "Resources" "link" in the "Activities" "block"
+    And I should see "Frontpage book name"
+    And I should see "Frontpage page name"
+    And I should see "Frontpage resource name"
+    And I should see "Frontpage imscp name"
+    And I should see "Frontpage folder name"
+    And I should see "Frontpage url name"
+
+  Scenario: Add activities block in a course
+    Given the following "courses" exists:
+      | fullname | shortname | format |
+      | Course 1 | C1        | topics |
+    And the following "activities" exists:
+      | activity   | name                   | intro                         | course | idnumber    |
+      | assign     | Test assignment name   | Test assignment description   | C1     | assign1     |
+      | assignment | Test assignment22 name | Test assignment22 description | C1     | assignment1 |
+      | book       | Test book name         | Test book description         | C1     | book1       |
+      | chat       | Test chat name         | Test chat description         | C1     | chat1       |
+      | choice     | Test choice name       | Test choice description       | C1     | choice1     |
+      | data       | Test database name     | Test database description     | C1     | data1       |
+      | feedback   | Test feedback name     | Test feedback description     | C1     | feedback1   |
+      | folder     | Test folder name       | Test folder description       | C1     | folder1     |
+      | forum      | Test forum name        | Test forum description        | C1     | forum1      |
+      | glossary   | Test glossary name     | Test glossary description     | C1     | glossary1   |
+      | imscp      | Test imscp name        | Test imscp description        | C1     | imscp1      |
+      | label      | Test label name        | Test label description        | C1     | label1      |
+      | lesson     | Test lesson name       | Test lesson description       | C1     | lesson1     |
+      | lti        | Test lti name          | Test lti description          | C1     | lti1        |
+      | page       | Test page name         | Test page description         | C1     | page1       |
+      | quiz       | Test quiz name         | Test quiz description         | C1     | quiz1       |
+      | resource   | Test resource name     | Test resource description     | C1     | resource1   |
+      | scorm      | Test scorm name        | Test scorm description        | C1     | scorm1      |
+      | survey     | Test survey name       | Test survey description       | C1     | survey1     |
+      | url        | Test url name          | Test url description          | C1     | url1        |
+      | wiki       | Test wiki name         | Test wiki description         | C1     | wiki1       |
+      | workshop   | Test workshop name     | Test workshop description     | C1     | workshop1   |
+
+    When I follow "Courses"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add the "Activities" block
+    And I click on "Assignments" "link" in the "Activities" "block"
+    Then I should see "Test assignment name"
+    And I follow "Course 1"
+    And I click on "Assignments (2.2)" "link" in the "Activities" "block"
+    And I should see "Test assignment22 name"
+    And I follow "Course 1"
+    And I click on "Chats" "link" in the "Activities" "block"
+    And I should see "Test chat name"
+    And I follow "Course 1"
+    And I click on "Choices" "link" in the "Activities" "block"
+    And I should see "Test choice name"
+    And I follow "Course 1"
+    And I click on "Databases" "link" in the "Activities" "block"
+    And I should see "Test database name"
+    And I follow "Course 1"
+    And I click on "Feedback" "link" in the "Activities" "block"
+    And I should see "Test feedback name"
+    And I follow "Course 1"
+    And I click on "Forums" "link" in the "Activities" "block"
+    And I should see "Test forum name"
+    And I follow "Course 1"
+    And I click on "External Tools" "link" in the "Activities" "block"
+    And I should see "Test lti name"
+    And I follow "Course 1"
+    And I click on "Quizzes" "link" in the "Activities" "block"
+    And I should see "Test quiz name"
+    And I follow "Course 1"
+    And I click on "Glossaries" "link" in the "Activities" "block"
+    And I should see "Test glossary name"
+    And I follow "Course 1"
+    And I click on "SCORM packages" "link" in the "Activities" "block"
+    And I should see "Test scorm name"
+    And I follow "Course 1"
+    And I click on "Lessons" "link" in the "Activities" "block"
+    And I should see "Test lesson name"
+    And I follow "Course 1"
+    And I click on "Wikis" "link" in the "Activities" "block"
+    And I should see "Test wiki name"
+    And I follow "Course 1"
+    And I click on "Workshop" "link" in the "Activities" "block"
+    And I should see "Test workshop name"
+    And I follow "Course 1"
+    And I click on "Resources" "link" in the "Activities" "block"
+    And I should see "Test book name"
+    And I should see "Test page name"
+    And I should see "Test resource name"
+    And I should see "Test imscp name"
+    And I should see "Test folder name"
+    And I should see "Test url name"
index 1bee81e..e002c94 100644 (file)
@@ -83,6 +83,7 @@ class block_blog_recent extends block_base {
 
         $this->content = new stdClass();
         $this->content->footer = '';
+        $this->content->text = '';
 
         $context = $this->page->context;
 
index e7eaf88..059fe1d 100644 (file)
@@ -65,9 +65,6 @@ class behat_block_comments extends behat_base {
 
             $this->find_link(get_string('savecomment'))->click();
 
-            // Wait for the AJAX request.
-            $this->getSession()->wait(4 * 1000, false);
-
         } else {
 
             $commentstextarea = $this->find('css', '.block_comments form textarea', $exception);
@@ -103,7 +100,7 @@ class behat_block_comments extends behat_base {
         $deleteicon = $this->find('css', '.comment-delete a img', $deleteexception, $commentnode);
         $deleteicon->click();
 
-        // Wait for the AJAX request.
+        // Wait for the animation to finish, in theory is just 1 sec, adding 4 just in case.
         $this->getSession()->wait(4 * 1000, false);
     }
 
index d459982..d4b9f94 100644 (file)
@@ -128,8 +128,7 @@ class block_course_list extends block_list {
             $this->content->items[] = get_string('remotecourses','mnet');
             $this->content->icons[] = '';
             foreach ($courses as $course) {
-                $coursecontext = context_course::instance($course->id);
-                $this->content->items[]="<a title=\"" . format_string($course->shortname, true, array('context' => $coursecontext)) . "\" ".
+                $this->content->items[]="<a title=\"" . format_string($course->shortname, true) . "\" ".
                     "href=\"{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}\">"
                     .$icon. format_string(get_course_display_name_for_list($course)) . "</a>";
             }
index 6287bad..d1670b4 100644 (file)
@@ -58,14 +58,11 @@ Feature: View my courses in navigation block
     And I should see "cat3" in the "Navigation" "block"
     And I should not see "cat2" in the "Navigation" "block"
     And I expand "cat3" node
-    And I wait "2" seconds
     And I should see "cat31" in the "Navigation" "block"
     And I should see "cat33" in the "Navigation" "block"
     And I should not see "cat32" in the "Navigation" "block"
     And I expand "cat31" node
-    And I wait "2" seconds
     And I should see "c31" in the "Navigation" "block"
     And I expand "cat33" node
-    And I wait "2" seconds
     And I should see "c331" in the "Navigation" "block"
     And I should not see "c332" in the "Navigation" "block"
index 5550c33..7a116ee 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js differ
index f0d0522..c48a7c3 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js differ
index 6dd4a48..0ecd3b9 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js differ
index 6ac1510..7db6ca9 100644 (file)
@@ -409,7 +409,7 @@ Y.extend(TREE, Y.Base, TREE.prototype, {
         instance : {
             value : false,
             setter : function(val) {
-                return parseInt(val);
+                return parseInt(val, 10);
             }
         }
     }
@@ -590,7 +590,7 @@ BRANCH.prototype = {
         } else {
             e.stopPropagation();
         }
-        if (e.type === 'actionkey' && e.action === 'enter' && e.target.test('A')) {
+        if ((e.type === 'actionkey' && e.action === 'enter') || e.target.test('a')) {
             // No ajaxLoad for enter.
             this.node.setAttribute('data-expandable', '0');
             this.node.setAttribute('data-loaded', '1');
@@ -647,6 +647,12 @@ BRANCH.prototype = {
         this.node.setAttribute('data-loaded', '1');
         try {
             var object = Y.JSON.parse(outcome.responseText);
+            if (object.error) {
+                Y.use('moodle-core-notification-ajaxException', function () {
+                    return new M.core.ajaxException(object).show();
+                });
+                return false;
+            }
             if (object.children && object.children.length > 0) {
                 var coursecount = 0;
                 for (var i in object.children) {
@@ -670,9 +676,16 @@ BRANCH.prototype = {
                 return true;
             }
             Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
-        } catch (ex) {
-            // If we got here then there was an error parsing the result.
-            Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+        } catch (error) {
+            if (outcome && outcome.status && outcome.status > 0) {
+                // If we got here then there was an error parsing the result.
+                Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+                Y.use('moodle-core-notification-exception', function () {
+                    return new M.core.exception(error).show();
+                });
+            }
+
+            return false;
         }
         // The branch is empty so class it accordingly
         this.node.replaceClass('branch', 'emptybranch');
index ed070a7..2f49298 100644 (file)
             mtrace('    ' . $rec->url . ' ', '');
             // Fetch the rss feed, using standard simplepie caching
             // so feeds will be renewed only if cache has expired
-            @set_time_limit(60);
+            core_php_time_limit::raise(60);
 
             $feed =  new moodle_simplepie();
             // set timeout for longer than normal to be agressive at
index 7f650c4..762bb64 100644 (file)
@@ -89,6 +89,20 @@ class block_site_main_menu extends block_list {
                 }
                 if (!$ismoving) {
                     $actions = course_get_cm_edit_actions($mod, -1);
+
+                    // Add the action move.
+                    $modcontext = context_module::instance($mod->id);
+                    $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
+                    if ($hasmanageactivities) {
+                        $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
+                        $actions['move'] = new action_menu_link_primary(
+                            new moodle_url($baseurl, array('copy' => $mod->id)),
+                            new pix_icon('t/move', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
+                            null,
+                            array('title' => get_string('move'))
+                        );
+                    }
+
                     $editbuttons = html_writer::tag('div',
                         $courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
                         array('class' => 'buttons')
index 6b1a793..d1a0371 100644 (file)
@@ -81,6 +81,7 @@ class block_tags extends block_base {
         }
 
         $this->content = new stdClass;
+        $this->content->text = '';
         $this->content->footer = '';
 
         // Get a list of tags.
index 36d8be2..28d59ca 100644 (file)
@@ -58,6 +58,20 @@ class behat_blocks extends behat_base {
         return $steps;
     }
 
+    /**
+     * Docks a block. Editing mode should be previously enabled.
+     *
+     * @Given /^I dock "(?P<block_name_string>(?:[^"]|\\")*)" block$/
+     * @param string $blockname
+     * @return Given
+     */
+    public function i_dock_block($blockname) {
+
+        // Looking for both title and alt.
+        $xpath = "//input[@type='image'][@title='" . get_string('dockblock', 'block', $blockname) . "' or @alt='" . get_string('addtodock', 'block') . "']";
+        return new Given('I click on " ' . $xpath . '" "xpath_element" in the "' . $this->escape($blockname) . '" "block"');
+    }
+
     /**
      * Opens a block's actions menu if it is not already opened.
      *
index 6b3d5db..efa99ea 100644 (file)
@@ -223,5 +223,13 @@ $bloglisting = new blog_listing($blogheaders['filters']);
 $bloglisting->print_entries();
 
 echo $OUTPUT->footer();
-
-add_to_log($courseid, 'blog', 'view', 'index.php?entryid='.$entryid.'&amp;tagid='.@$tagid.'&amp;tag='.$tag, 'view blog entry');
+$eventparams = array(
+    'other' => array('entryid' => $entryid, 'tagid' => $tagid, 'userid' => $userid, 'modid' => $modid, 'groupid' => $groupid,
+                     'search' => $search, 'fromstart' => $start)
+);
+if (!empty($userid)) {
+    $eventparams['relateduserid'] = $userid;
+}
+$eventparams['other']['courseid'] = ($courseid === SITEID) ? 0 : $courseid;
+$event = \core\event\blog_entries_viewed::create($eventparams);
+$event->trigger();
index a4e4210..c6a1084 100644 (file)
@@ -339,46 +339,62 @@ class blog_entry implements renderable {
     }
 
     /**
-     * function to add all context associations to an entry
-     * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
+     * Function to add all context associations to an entry.
+     * TODO : Remove $action in 2.9 (MDL-41330)
+     *
+     * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
      */
-    public function add_associations($action='add') {
-        global $DB, $USER;
+    public function add_associations($action = null) {
+
+        if (!empty($action)) {
+            debugging('blog_entry->add_associations() does not accept any argument', DEBUG_DEVELOPER);
+        }
 
         $this->remove_associations();
 
         if (!empty($this->courseassoc)) {
-            $this->add_association($this->courseassoc, $action);
+            $this->add_association($this->courseassoc);
         }
 
         if (!empty($this->modassoc)) {
-            $this->add_association($this->modassoc, $action);
+            $this->add_association($this->modassoc);
         }
     }
 
     /**
-     * add a single association for a blog entry
-     * @param int contextid - id of context to associate with the blog entry
+     * Add a single association for a blog entry
+     * TODO : Remove $action in 2.9 (MDL-41330)
+     *
+     * @param int $contextid - id of context to associate with the blog entry.
+     * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
      */
-    public function add_association($contextid, $action='add') {
-        global $DB, $USER;
+    public function add_association($contextid, $action = null) {
+        global $DB;
+
+        if (!empty($action)) {
+            debugging('blog_entry->add_association() accepts only one argument', DEBUG_DEVELOPER);
+        }
 
         $assocobject = new StdClass;
         $assocobject->contextid = $contextid;
         $assocobject->blogid = $this->id;
-        $DB->insert_record('blog_association', $assocobject);
+        $id = $DB->insert_record('blog_association', $assocobject);
 
+        // Trigger an association created event.
         $context = context::instance_by_id($contextid);
-        $courseid = null;
-
+        $eventparam = array(
+            'objectid' => $id,
+            'other' => array('associateid' => $context->instanceid, 'subject' => $this->subject, 'blogid' => $this->id),
+            'relateduserid' => $this->userid
+        );
         if ($context->contextlevel == CONTEXT_COURSE) {
-            $courseid = $context->instanceid;
-            add_to_log($courseid, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+            $eventparam['other']['associatetype'] = 'course';
+
         } else if ($context->contextlevel == CONTEXT_MODULE) {
-            $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
-            $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module));
-            add_to_log($cm->course, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject, $cm->id, $this->userid);
+            $eventparam['other']['associatetype'] = 'coursemodule';
         }
+        $event = \core\event\blog_association_created::create($eventparam);
+        $event->trigger();
     }
 
     /**
index 1527a3c..84501bd 100644 (file)
@@ -33,11 +33,9 @@ Feature: Comment on a blog entry
     And I follow "Comments (0)"
     When I fill in "content" with "$My own >nasty< \"string\"!"
     And I follow "Save comment"
-    And I wait "4" seconds
     Then I should see "$My own >nasty< \"string\"!"
     And I fill in "content" with "Another $Nasty <string?>"
     And I follow "Save comment"
-    And I wait "4" seconds
     And I should see "Comments (2)" in the ".comment-link" "css_element"
 
   @javascript
@@ -53,8 +51,8 @@ Feature: Comment on a blog entry
     And I follow "Comments (0)"
     And I fill in "content" with "$My own >nasty< \"string\"!"
     And I follow "Save comment"
-    And I wait "4" seconds
     When I click on ".comment-delete a" "css_element"
+    # Waiting for the animation to finish.
     And I wait "4" seconds
     Then I should not see "$My own >nasty< \"string\"!"
     And I follow "Blog post from user 1"
@@ -73,5 +71,4 @@ Feature: Comment on a blog entry
     When I follow "Comments (0)"
     And I fill in "content" with "$My own >nasty< \"string\"!"
     And I follow "Save comment"
-    And I wait "4" seconds
     Then I should see "$My own >nasty< \"string\"!"
index 689e3be..47c8a29 100644 (file)
@@ -250,5 +250,137 @@ class core_bloglib_testcase extends advanced_testcase {
         $this->assertEventLegacyLogData($arr, $event);
         $this->assertEventLegacyData($blog, $event);
     }
+
+
+    /**
+     * Tests for event blog_association_created.
+     */
+    public function test_blog_association_created_event() {
+        global $USER;
+
+        $this->setAdminUser();
+        $this->resetAfterTest();
+        $sitecontext = context_system::instance();
+        $coursecontext = context_course::instance($this->courseid);
+        $contextmodule = context_module::instance($this->cmid);
+
+        // Add blog associations with a course.
+        $blog = new blog_entry($this->postid);
+        $sink = $this->redirectEvents();
+        $blog->add_association($coursecontext->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $this->assertInstanceOf('\core\event\blog_association_created', $event);
+        $this->assertEquals($sitecontext->id, $event->contextid);
+        $this->assertEquals($blog->id, $event->other['blogid']);
+        $this->assertEquals($this->courseid, $event->other['associateid']);
+        $this->assertEquals('course', $event->other['associatetype']);
+        $this->assertEquals($blog->subject, $event->other['subject']);
+        $this->assertEquals($USER->id, $event->userid);
+        $this->assertEquals($this->userid, $event->relateduserid);
+        $this->assertEquals('blog_association', $event->objecttable);
+        $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+                     $blog->subject, 0, $this->userid);
+        $this->assertEventLegacyLogData($arr, $event);
+
+        // Add blog associations with a module.
+        $blog = new blog_entry($this->postid);
+        $sink = $this->redirectEvents();
+        $blog->add_association($contextmodule->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $this->assertEquals($blog->id, $event->other['blogid']);
+        $this->assertEquals($this->cmid, $event->other['associateid']);
+        $this->assertEquals('coursemodule', $event->other['associatetype']);
+        $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+                     $blog->subject, $this->cmid, $this->userid);
+        $this->assertEventLegacyLogData($arr, $event);
+    }
+
+    /**
+     * Tests for event blog_association_created validations.
+     */
+    public function test_blog_association_created_event_validations() {
+
+        $this->resetAfterTest();
+
+         // Make sure associatetype validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 2 , 'blogid' => 3, 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid associatetype', $e->getMessage());
+        }
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 2 , 'blogid' => 3, 'associatetype' => 'random', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid associatetype', $e->getMessage());
+        }
+        // Make sure associateid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('blogid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Associate id must be set', $e->getMessage());
+        }
+        // Make sure blogid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Blog id must be set', $e->getMessage());
+        }
+        // Make sure blogid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('blogid' => 3, 'associateid' => 3, 'associatetype' => 'course')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Subject must be set', $e->getMessage());
+        }
+    }
+
+    /**
+     * Tests for event blog_entries_viewed.
+     */
+    public function test_blog_entries_viewed_event() {
+
+        $this->setAdminUser();
+        $this->resetAfterTest();
+        $other = array('entryid' => $this->postid, 'tagid' => $this->tagid, 'userid' => $this->userid, 'modid' => $this->cmid,
+                       'groupid' => $this->groupid, 'courseid' => $this->courseid, 'search' => 'search', 'fromstart' => 2);
+
+        // Trigger event.
+        $sink = $this->redirectEvents();
+        $eventparams = array('other' => $other);
+        $eventinst = \core\event\blog_entries_viewed::create($eventparams);
+        $eventinst->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $url = new moodle_url('/blog/index.php', $other);
+        $url2 = new moodle_url('index.php', $other);
+        $this->assertEquals($url, $event->get_url());
+        $arr = array(SITEID, 'blog', 'view', $url2->out(), 'view blog entry');
+        $this->assertEventLegacyLogData($arr, $event);
+    }
 }
 
diff --git a/blog/upgrade.txt b/blog/upgrade.txt
new file mode 100644 (file)
index 0000000..8695b95
--- /dev/null
@@ -0,0 +1,4 @@
+=== 2.7 ===
+
+* blog_entry->add_association() does not accept any params.
+* blog_entry->add_associations() accepts only one param.
\ No newline at end of file
index f3e3acb..c9c35db 100644 (file)
@@ -282,6 +282,7 @@ if (!empty($action) && confirm_sesskey()) {
 
 $PAGE->set_title($title);
 $PAGE->set_heading($SITE->fullname);
+/* @var core_cache_renderer $renderer */
 $renderer = $PAGE->get_renderer('core_cache');
 
 echo $renderer->header();
index 281fa30..4463e0a 100644 (file)
@@ -963,4 +963,31 @@ class cache_definition {
     public function has_required_identifiers() {
         return (count($this->requireidentifiers) > 0);
     }
+
+    /**
+     * Returns the possible sharing options that can be used with this defintion.
+     *
+     * @return int
+     */
+    public function get_sharing_options() {
+        return $this->sharingoptions;
+    }
+
+    /**
+     * Returns the user entered sharing key for this definition.
+     *
+     * @return string
+     */
+    public function get_user_input_sharing_key() {
+        return $this->userinputsharingkey;
+    }
+
+    /**
+     * Returns the user selected sharing option for this definition.
+     *
+     * @return int
+     */
+    public function get_selected_sharing_option() {
+        return $this->selectedsharingoption;
+    }
 }
index 2843dae..f850e69 100644 (file)
@@ -235,14 +235,9 @@ class cache_factory {
      */
     public function create_cache(cache_definition $definition) {
         $class = $definition->get_cache_class();
-        if ($this->is_initialising()) {
-            // Do nothing we just want the dummy store.
-            $stores = array();
-        } else {
-            $stores = cache_helper::get_cache_stores($definition);
-        }
+        $stores = cache_helper::get_stores_suitable_for_definition($definition);
         if (count($stores) === 0) {
-            // Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
+            // Hmm still no stores, better provide a dummy store to mimic functionality. The dev will be none the wiser.
             $stores[] = $this->create_dummy_store($definition);
         }
         $loader = null;
index 96c6601..90076aa 100644 (file)
@@ -143,7 +143,7 @@ class cache_helper {
      *
      * @param array $stores
      * @param cache_definition $definition
-     * @return array
+     * @return cache_store[]
      */
     protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) {
         $return = array();
@@ -382,34 +382,40 @@ class cache_helper {
     /**
      * Record a cache hit in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $hits The number of hits to record (by default 1)
      */
-    public static function record_cache_hit($store, $definition) {
+    public static function record_cache_hit($store, $definition, $hits = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['hits']++;
+        self::$stats[$definition][$store]['hits'] += $hits;
     }
 
     /**
      * Record a cache miss in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $misses The number of misses to record (by default 1)
      */
-    public static function record_cache_miss($store, $definition) {
+    public static function record_cache_miss($store, $definition, $misses = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['misses']++;
+        self::$stats[$definition][$store]['misses'] += $misses;
     }
 
     /**
      * Record a cache set in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $sets The number of sets to record (by default 1)
      */
-    public static function record_cache_set($store, $definition) {
+    public static function record_cache_set($store, $definition, $sets = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['sets']++;
+        self::$stats[$definition][$store]['sets'] += $sets;
     }
 
     /**
@@ -672,4 +678,59 @@ class cache_helper {
             }
         }
     }
+
+    /**
+     * Returns an array of stores that would meet the requirements for every definition.
+     *
+     * These stores would be 100% suitable to map as defaults for cache modes.
+     *
+     * @return array[] An array of stores, keys are the store names.
+     */
+    public static function get_stores_suitable_for_mode_default() {
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
+        $requirements = 0;
+        foreach ($config->get_definitions() as $definition) {
+            $definition = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
+            $requirements = $requirements | $definition->get_requirements_bin();
+        }
+        $stores = array();
+        foreach ($config->get_all_stores() as $name => $store) {
+            if (!empty($store['features']) && ($store['features'] & $requirements)) {
+                $stores[$name] = $store;
+            }
+        }
+        return $stores;
+    }
+
+    /**
+     * Returns stores suitable for use with a given definition.
+     *
+     * @param cache_definition $definition
+     * @return cache_store[]
+     */
+    public static function get_stores_suitable_for_definition(cache_definition $definition) {
+        $factory = cache_factory::instance();
+        $stores = array();
+        if ($factory->is_initialising() || $factory->stores_disabled()) {
+            // No suitable stores here.
+            return $stores;
+        } else {
+            $stores = self::get_cache_stores($definition);
+            if (count($stores) === 0) {
+                // No suitable stores we found for the definition. We need to come up with a sensible default.
+                // If this has happened we can be sure that the user has mapped custom stores to either the
+                // mode of the definition. The first alternative to try is the system default for the mode.
+                // e.g. the default file store instance for application definitions.
+                $config = $factory->create_config_instance();
+                foreach ($config->get_stores($definition->get_mode()) as $name => $details) {
+                    if (!empty($details['default'])) {
+                        $stores[] = $factory->create_store_from_config($name, $details, $definition);
+                        break;
+                    }
+                }
+            }
+        }
+        return $stores;
+    }
 }
index 10ad620..994aef2 100644 (file)
@@ -463,6 +463,20 @@ class cache implements cache_loader {
             }
         }
 
+        if ($this->perfdebug) {
+            $hits = 0;
+            $misses = 0;
+            foreach ($fullresult as $value) {
+                if ($value === false) {
+                    $misses++;
+                } else {
+                    $hits++;
+                }
+            }
+            cache_helper::record_cache_hit($this->storetype, $this->definition->get_id(), $hits);
+            cache_helper::record_cache_miss($this->storetype, $this->definition->get_id(), $misses);
+        }
+
         // Return the result. Phew!
         return $fullresult;
     }
@@ -633,10 +647,11 @@ class cache implements cache_loader {
                 $this->static_acceleration_set($data[$key]['key'], $value);
             }
         }
-        if ($this->perfdebug) {
-            cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
+        $successfullyset = $this->store->set_many($data);
+        if ($this->perfdebug && $successfullyset) {
+            cache_helper::record_cache_set($this->storetype, $this->definition->get_id(), $successfullyset);
         }
-        return $this->store->set_many($data);
+        return $successfullyset;
     }
 
     /**
@@ -1937,7 +1952,19 @@ class cache_session extends cache {
         if ($hasmissingkeys && $strictness === MUST_EXIST) {
             throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
         }
-
+        if ($this->perfdebug) {
+            $hits = 0;
+            $misses = 0;
+            foreach ($return as $value) {
+                if ($value === false) {
+                    $misses++;
+                } else {
+                    $hits++;
+                }
+            }
+            cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id(), $hits);
+            cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id(), $misses);
+        }
         return $return;
 
     }
@@ -2011,10 +2038,11 @@ class cache_session extends cache {
                 'value' => $value
             );
         }
-        if ($this->perfdebug) {
-            cache_helper::record_cache_set($this->storetype, $definitionid);
+        $successfullyset = $this->get_store()->set_many($data);
+        if ($this->perfdebug && $successfullyset) {
+            cache_helper::record_cache_set($this->storetype, $definitionid, $successfullyset);
         }
-        return $this->get_store()->set_many($data);
+        return $successfullyset;
     }
 
     /**
index c72c52c..c7948cf 100644 (file)
@@ -778,63 +778,36 @@ abstract class cache_administration_helper extends cache_helper {
      * @return array
      */
     public static function get_definition_summaries() {
-        $instance = cache_config::instance();
-        $definitions = $instance->get_definitions();
-
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
         $storenames = array();
-        foreach ($instance->get_all_stores() as $key => $store) {
+        foreach ($config->get_all_stores() as $key => $store) {
             if (!empty($store['default'])) {
                 $storenames[$key] = new lang_string('store_'.$key, 'cache');
-            }
-        }
-
-        $modemappings = array();
-        foreach ($instance->get_mode_mappings() as $mapping) {
-            $mode = $mapping['mode'];
-            if (!array_key_exists($mode, $modemappings)) {
-                $modemappings[$mode] = array();
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $modemappings[$mode][] = $storenames[$mapping['store']];
             } else {
-                $modemappings[$mode][] = $mapping['store'];
+                $storenames[$store['name']] = $store['name'];
             }
         }
-
-        $definitionmappings = array();
-        foreach ($instance->get_definition_mappings() as $mapping) {
-            $definition = $mapping['definition'];
-            if (!array_key_exists($definition, $definitionmappings)) {
-                $definitionmappings[$definition] = array();
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $definitionmappings[$definition][] = $storenames[$mapping['store']];
-            } else {
-                $definitionmappings[$definition][] = $mapping['store'];
-            }
+        /* @var cache_definition[] $definitions */
+        $definitions = array();
+        foreach ($config->get_definitions() as $key => $definition) {
+            $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
         }
-
-        $return = array();
-
         foreach ($definitions as $id => $definition) {
-
             $mappings = array();
-            if (array_key_exists($id, $definitionmappings)) {
-                $mappings = $definitionmappings[$id];
-            } else if (empty($definition['mappingsonly'])) {
-                $mappings = $modemappings[$definition['mode']];
+            foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
+                $mappings[] = $storenames[$store->my_name()];
             }
-
             $return[$id] = array(
                 'id' => $id,
-                'name' => cache_helper::get_definition_name($definition),
-                'mode' => $definition['mode'],
-                'component' => $definition['component'],
-                'area' => $definition['area'],
+                'name' => $definition->get_name(),
+                'mode' => $definition->get_mode(),
+                'component' => $definition->get_component(),
+                'area' => $definition->get_area(),
                 'mappings' => $mappings,
-                'sharingoptions' => self::get_definition_sharing_options($definition['sharingoptions'], false),
-                'selectedsharingoption' => self::get_definition_sharing_options($definition['selectedsharingoption'], true),
-                'userinputsharingkey' => $definition['userinputsharingkey']
+                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
+                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
+                'userinputsharingkey' => $definition->get_user_input_sharing_key()
             );
         }
         return $return;
@@ -1126,7 +1099,10 @@ abstract class cache_administration_helper extends cache_helper {
      * @return array An array containing sub-arrays, one for each mode.
      */
     public static function get_default_mode_stores() {
+        global $OUTPUT;
         $instance = cache_config::instance();
+        $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
+        $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache'));
         $storenames = array();
         foreach ($instance->get_all_stores() as $key => $store) {
             if (!empty($store['default'])) {
@@ -1149,6 +1125,9 @@ abstract class cache_administration_helper extends cache_helper {
             } else {
                 $modemappings[$mode][$mapping['store']] = $mapping['store'];
             }
+            if (!array_key_exists($mapping['store'], $adequatestores)) {
+                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
+            }
         }
         return $modemappings;
     }
index c11141e..be9f906 100644 (file)
@@ -3025,7 +3025,7 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = nu
     $updatecount = 0;
 
     // Large calendars take a while...
-    set_time_limit(300);
+    core_php_time_limit::raise(300);
 
     // Mark all events in a subscription with a zero timestamp.
     if (!empty($subscriptionid)) {
index 5349c53..887533b 100644 (file)
@@ -54,7 +54,7 @@ class behat_cohort extends behat_base {
         $userid = $DB->get_field('user', 'id', array('username' => $username));
 
         $steps = array(
-            new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" table row'),
+            new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" "table_row"'),
             new Given('I select "' . $userid . '" from "' . get_string('potusers', 'cohort') . '"'),
             new Given('I press "' . get_string('add') . '"'),
             new Given('I press "' . get_string('backtocohorts', 'cohort') . '"')
index 47b169a..eb0ea17 100644 (file)
@@ -27,16 +27,16 @@ Feature: Upload users to a cohort
     And I expand "Users" node
     And I expand "Accounts" node
     When I follow "Upload users"
-    And I upload "lib/tests/fixtures/upload_users_cohorts.csv" file to "File" filepicker
+    And I upload "lib/tests/fixtures/upload_users_cohorts.csv" file to "File" filemanager
     And I press "Upload users"
     And I press "Upload users"
     And I press "Continue"
     And I follow "Cohorts"
-    And I click on "Assign" "link" in the "Cohort 1" table row
+    And I click on "Assign" "link" in the "Cohort 1" "table_row"
     Then the "Current users" select box should contain "Tom Jones (tomjones@example.com)"
     And the "Current users" select box should contain "Bob Jones (bobjones@example.com)"
     And I press "Back to cohorts"
-    And I click on "Assign" "link" in the "Cohort 2" table row
+    And I click on "Assign" "link" in the "Cohort 2" "table_row"
     And the "Current users" select box should contain "Mary Smith (marysmith@example.com)"
     And the "Current users" select box should contain "Alice Smith (alicesmith@example.com)"
     And I am on homepage
index 5381866..8033ac0 100644 (file)
@@ -651,6 +651,20 @@ class comment {
             }
             $newcmt->time = userdate($newcmt->timecreated, $newcmt->strftimeformat);
 
+            // Trigger comment created event.
+            $eventclassname = '\\' . $this->component . '\\event\comment_created';
+            if (class_exists($eventclassname)) {
+                $event = $eventclassname::create(
+                        array(
+                            'context' => $this->context,
+                            'objectid' => $newcmt->id,
+                            'other' => array(
+                                'itemid' => $this->itemid
+                                )
+                            ));
+                $event->trigger();
+            }
+
             return $newcmt;
         } else {
             throw new comment_exception('dbupdatefailed');
@@ -709,6 +723,20 @@ class comment {
             throw new comment_exception('nopermissiontocomment');
         }
         $DB->delete_records('comments', array('id'=>$commentid));
+        // Trigger comment delete event.
+        $eventclassname = '\\' . $this->component . '\\event\comment_deleted';
+        if (class_exists($eventclassname)) {
+            $event = $eventclassname::create(
+                    array(
+                        'context' => $this->context,
+                        'objectid' => $commentid,
+                        'other' => array(
+                            'itemid' => $this->itemid
+                            )
+                        ));
+            $event->add_record_snapshot('comments', $comment);
+            $event->trigger();
+        }
         return true;
     }
 
index a14b9b9..98d2982 100644 (file)
@@ -37,7 +37,6 @@ Feature: Allow students to manually mark an activity as complete
     And I log in as "student1"
     And I follow "Course 1"
     And I press "Mark as complete: Test forum name"
-    And I wait "3" seconds
     And I log out
     And I log in as "teacher1"
     And I follow "Course 1"