Merge branch 'MDL-43411-master' of git://github.com/andrewnicols/moodle
authorDamyon Wiese <damyon@moodle.com>
Tue, 7 Jan 2014 04:53:07 +0000 (12:53 +0800)
committerDamyon Wiese <damyon@moodle.com>
Tue, 7 Jan 2014 04:53:07 +0000 (12:53 +0800)
406 files changed:
admin/cli/install.php
admin/cli/install_database.php
admin/environment.xml
admin/index.php
admin/settings/courses.php
admin/settings/development.php
admin/tool/behat/cli/util.php
admin/tool/behat/tests/manager_test.php [moved from admin/tool/behat/tests/tool_behat_test.php with 95% similarity]
admin/tool/health/index.php
auth/mnet/auth.php
backup/cc/entities.class.php
backup/moodle2/backup_course_task.class.php
backup/moodle2/restore_course_task.class.php
backup/restore.php
backup/restorefile.php
backup/util/dbops/backup_controller_dbops.class.php
backup/util/helper/tests/backup_encode_content_test.php [new file with mode: 0644]
badges/action.php
badges/award.php
badges/criteria/award_criteria.php
badges/criteria/award_criteria_activity.php
badges/criteria/award_criteria_course.php
badges/criteria/award_criteria_courseset.php
badges/criteria/award_criteria_manual.php
badges/criteria/award_criteria_overall.php
badges/criteria/award_criteria_profile.php
badges/index.php
badges/tests/badgeslib_test.php
badges/upgrade.txt [new file with mode: 0644]
blocks/admin_bookmarks/block_admin_bookmarks.php
blocks/comments/classes/event/comment_created.php [moved from install/lang/es_ar/langconfig.php with 56% similarity]
blocks/comments/classes/event/comment_deleted.php [moved from install/lang/es_es/langconfig.php with 55% similarity]
blocks/comments/tests/events_test.php [new file with mode: 0644]
blocks/community/yui/comments/comments.js
blocks/community/yui/imagegallery/imagegallery.js
blocks/course_list/block_course_list.php
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/recent_activity/renderer.php
blocks/site_main_menu/block_site_main_menu.php
blocks/social_activities/block_social_activities.php
blog/tests/bloglib_test.php
cache/classes/definition.php
cache/classes/dummystore.php
cache/classes/factory.php
cache/classes/helper.php
cache/classes/store.php
cache/stores/memcache/lib.php
cache/stores/session/lib.php
cache/stores/static/lib.php
cache/upgrade.txt
comment/lib.php
config-dist.php
course/classes/management/helper.php
course/dndupload.js
course/dnduploadlib.php
course/externallib.php
course/format/singleactivity/lib.php
course/lib.php
course/loginas.php
course/renderer.php
course/tests/behat/activities_edit_with_block_dock.feature [new file with mode: 0644]
course/tests/behat/behat_course.php
course/tests/externallib_test.php
course/view.php
course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js [new file with mode: 0644]
course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js [new file with mode: 0644]
course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js [new file with mode: 0644]
course/yui/modchooser/modchooser.js [deleted file]
course/yui/src/modchooser/build.json [new file with mode: 0644]
course/yui/src/modchooser/js/modchooser.js [new file with mode: 0644]
course/yui/src/modchooser/meta/modchooser.json [new file with mode: 0644]
enrol/locallib.php
enrol/manual/yui/quickenrolment/quickenrolment.js
enrol/yui/rolemanager/rolemanager.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js
filter/glossary/yui/src/autolinker/js/autolinker.js
filter/urltolink/filter.php
filter/urltolink/tests/filter_test.php
grade/report/grader/lib.php
grade/tests/edittreelib_test.php
install.php
install/lang/it/error.php
install/lang/zh_tw/error.php
lang/en/admin.php
lang/en/backup.php
lang/en/badges.php
lang/en/install.php
lang/en/moodle.php
lib/accesslib.php
lib/adminlib.php
lib/adodb/adodb.inc.php
lib/adodb/readme_moodle.txt
lib/ajax/ajaxlib.php
lib/ajax/getsiteadminbranch.php
lib/badgeslib.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_checkbox.php
lib/behat/lib.php
lib/classes/component.php
lib/classes/event/assessable_uploaded.php
lib/classes/event/blog_association_created.php
lib/classes/event/blog_comment_created.php [new file with mode: 0644]
lib/classes/event/blog_comment_deleted.php [new file with mode: 0644]
lib/classes/event/blog_entries_viewed.php
lib/classes/event/comment_created.php
lib/classes/event/comment_deleted.php
lib/classes/event/comments_viewed.php
lib/classes/event/content_viewed.php
lib/classes/event/course_category_deleted.php
lib/classes/event/course_content_deleted.php
lib/classes/event/course_created.php
lib/classes/event/course_deleted.php
lib/classes/event/course_module_created.php
lib/classes/event/course_module_deleted.php
lib/classes/event/course_module_updated.php
lib/classes/event/course_reset_ended.php
lib/classes/event/course_reset_started.php
lib/classes/event/course_restored.php
lib/classes/event/course_section_updated.php
lib/classes/event/course_updated.php
lib/classes/event/group_member_added.php
lib/classes/event/note_created.php
lib/classes/event/note_deleted.php
lib/classes/event/note_updated.php
lib/classes/event/notes_viewed.php
lib/classes/event/role_assigned.php
lib/classes/event/role_deleted.php
lib/classes/event/role_unassigned.php
lib/classes/event/user_deleted.php
lib/classes/event/user_enrolment_created.php
lib/classes/event/user_enrolment_deleted.php
lib/classes/event/user_enrolment_updated.php
lib/classes/event/user_list_viewed.php
lib/classes/event/user_loggedin.php
lib/classes/event/user_loggedinas.php
lib/classes/event/user_loggedout.php
lib/classes/event/user_profile_viewed.php
lib/classes/event/webservice_function_called.php
lib/classes/event/webservice_login_failed.php
lib/classes/event/webservice_service_created.php
lib/classes/event/webservice_token_created.php
lib/configonlylib.php
lib/coursecatlib.php
lib/cronlib.php
lib/db/upgrade.php
lib/ddl/tests/ddl_test.php
lib/dml/moodle_database.php
lib/dml/moodle_temptables.php
lib/dml/mssql_native_moodle_database.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/oci_native_moodle_database.php
lib/dml/pdo_moodle_database.php
lib/dml/pgsql_native_moodle_database.php
lib/dml/pgsql_native_moodle_temptables.php
lib/dml/sqlsrv_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/editor/tinymce/plugins/managefiles/lib.php
lib/filelib.php
lib/filestorage/mbz_packer.php
lib/filestorage/tests/mbz_packer_test.php
lib/filestorage/tests/tgz_packer_test.php
lib/filestorage/tgz_packer.php
lib/form/url.php
lib/grade/grade_category.php
lib/grade/tests/grade_category_test.php
lib/htaccess
lib/javascript-static.js
lib/medialib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrequirementslib.php
lib/rsslib.php
lib/setup.php
lib/setuplib.php
lib/statslib.php
lib/testing/classes/util.php
lib/testing/lib.php
lib/tests/accesslib_test.php
lib/tests/admintree_test.php
lib/tests/ajaxlib_test.php [new file with mode: 0644]
lib/tests/behat/behat_hooks.php
lib/tests/filelib_test.php
lib/tests/modinfolib_test.php
lib/tests/weblib_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/weblib.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-chooserdialogue/moodle-core-chooserdialogue-debug.js
lib/yui/build/moodle-core-chooserdialogue/moodle-core-chooserdialogue-min.js
lib/yui/build/moodle-core-chooserdialogue/moodle-core-chooserdialogue.js
lib/yui/build/moodle-core-formchangechecker/moodle-core-formchangechecker-debug.js
lib/yui/build/moodle-core-formchangechecker/moodle-core-formchangechecker-min.js
lib/yui/build/moodle-core-formchangechecker/moodle-core-formchangechecker.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue.js
lib/yui/build/moodle-core-tooltip/moodle-core-tooltip-debug.js
lib/yui/build/moodle-core-tooltip/moodle-core-tooltip-min.js
lib/yui/build/moodle-core-tooltip/moodle-core-tooltip.js
lib/yui/src/actionmenu/js/actionmenu.js
lib/yui/src/blocks/js/blockregion.js
lib/yui/src/chooserdialogue/js/chooserdialogue.js
lib/yui/src/formchangechecker/js/formchangechecker.js
lib/yui/src/notification/js/dialogue.js
lib/yui/src/tooltip/js/tooltip.js
lib/yui/src/tooltip/meta/tooltip.json
lib/yuilib/gallery/gallery-sm-treeview-sortable/gallery-sm-treeview-sortable-debug.js [moved from mod/scorm/yui/src/treeview/js/gallery-sm-treeview-sortable-debug.js with 89% similarity]
lib/yuilib/gallery/gallery-sm-treeview-sortable/gallery-sm-treeview-sortable-min.js [new file with mode: 0644]
lib/yuilib/gallery/gallery-sm-treeview-sortable/gallery-sm-treeview-sortable.js [new file with mode: 0644]
lib/yuilib/gallery/gallery-sm-treeview-templates/gallery-sm-treeview-templates-debug.js [new file with mode: 0644]
lib/yuilib/gallery/gallery-sm-treeview-templates/gallery-sm-treeview-templates-min.js [new file with mode: 0644]
lib/yuilib/gallery/gallery-sm-treeview-templates/gallery-sm-treeview-templates.js [new file with mode: 0644]
lib/yuilib/gallery/gallery-sm-treeview/assets/gallery-sm-treeview-core.css [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/moodle-mod_scorm-treeview-core.css with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/assets/skins/sam/folder.png [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder.png with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/assets/skins/sam/folder@2x.png [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder@2x.png with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/assets/skins/sam/gallery-sm-treeview-skin.css [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview-skin.css with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/assets/skins/sam/gallery-sm-treeview.css [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview.css with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/assets/skins/sam/item.png [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item.png with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/assets/skins/sam/item@2x.png [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item@2x.png with 100% similarity]
lib/yuilib/gallery/gallery-sm-treeview/gallery-sm-treeview-debug.js [moved from mod/scorm/yui/src/treeview/js/gallery-sm-treeview-debug.js with 95% similarity]
lib/yuilib/gallery/gallery-sm-treeview/gallery-sm-treeview-min.js [new file with mode: 0644]
lib/yuilib/gallery/gallery-sm-treeview/gallery-sm-treeview.js [moved from mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-debug.js with 94% similarity]
lib/yuilib/readme_moodle.txt
mod/assign/classes/event/assessable_submitted.php
mod/assign/classes/event/marker_updated.php
mod/assign/classes/event/submission_status_updated.php
mod/assign/classes/event/workflow_state_updated.php
mod/assign/db/access.php
mod/assign/feedback/comments/lang/en/assignfeedback_comments.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/comments/settings.php
mod/assign/feedback/comments/version.php
mod/assign/feedback/editpdf/classes/pdf.php
mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php
mod/assign/feedback/editpdf/locallib.php
mod/assign/feedback/editpdf/settings.php
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/commentsearch.js
mod/assign/feedback/editpdf/yui/src/editor/js/dropdown.js
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/renderable.php
mod/assign/renderer.php
mod/assign/submission/comments/classes/event/comment_created.php [new file with mode: 0644]
mod/assign/submission/comments/classes/event/comment_deleted.php [new file with mode: 0644]
mod/assign/submission/comments/lib.php
mod/assign/submission/comments/tests/events_test.php [new file with mode: 0644]
mod/assign/submission/file/classes/event/assessable_uploaded.php
mod/assign/submission/onlinetext/classes/event/assessable_uploaded.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/submission/onlinetext/tests/events_test.php
mod/assign/tests/behat/allow_another_attempt.feature [new file with mode: 0644]
mod/assign/tests/behat/comment_inline.feature [new file with mode: 0644]
mod/assign/tests/behat/edit_previous_feedback.feature [new file with mode: 0644]
mod/assign/tests/locallib_test.php
mod/assign/upgrade.txt
mod/assign/version.php
mod/assignment/lib.php
mod/assignment/type/online/assignment.class.php
mod/assignment/type/online/classes/event/assessable_uploaded.php
mod/assignment/type/upload/assignment.class.php
mod/assignment/type/upload/classes/event/assessable_submitted.php
mod/assignment/type/upload/classes/event/assessable_uploaded.php
mod/assignment/type/uploadsingle/assignment.class.php
mod/assignment/view.php
mod/book/backup/moodle1/lib.php
mod/chat/chatd.php
mod/chat/classes/event/sessions_viewed.php
mod/choice/classes/event/answer_submitted.php
mod/choice/classes/event/answer_updated.php
mod/choice/classes/event/report_viewed.php
mod/data/classes/event/comment_created.php [new file with mode: 0644]
mod/data/classes/event/comment_deleted.php [new file with mode: 0644]
mod/data/field/url/field.class.php
mod/data/tests/lib_test.php
mod/feedback/classes/event/course_module_viewed.php
mod/feedback/classes/event/response_deleted.php
mod/feedback/classes/event/response_submitted.php
mod/folder/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/folder/classes/event/course_module_viewed.php [new file with mode: 0644]
mod/folder/classes/event/folder_updated.php [new file with mode: 0644]
mod/folder/edit.php
mod/folder/index.php
mod/folder/lang/en/folder.php
mod/folder/lib.php
mod/folder/tests/events_test.php [new file with mode: 0644]
mod/folder/view.php
mod/forum/classes/event/assessable_uploaded.php
mod/forum/lib.php
mod/forum/post.php
mod/glossary/classes/event/comment_created.php [new file with mode: 0644]
mod/glossary/classes/event/comment_deleted.php [new file with mode: 0644]
mod/glossary/tests/events_test.php [new file with mode: 0644]
mod/lesson/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/lesson/classes/event/course_module_viewed.php [new file with mode: 0644]
mod/lesson/classes/event/essay_attempt_viewed.php [new file with mode: 0644]
mod/lesson/classes/event/highscore_added.php [new file with mode: 0644]
mod/lesson/classes/event/highscores_viewed.php [new file with mode: 0644]
mod/lesson/classes/event/lesson_ended.php [new file with mode: 0644]
mod/lesson/classes/event/lesson_started.php [new file with mode: 0644]
mod/lesson/classes/file_info.php [new file with mode: 0644]
mod/lesson/essay.php
mod/lesson/highscores.php
mod/lesson/index.php
mod/lesson/lang/en/lesson.php
mod/lesson/lib.php
mod/lesson/locallib.php
mod/lesson/tests/events_test.php [new file with mode: 0644]
mod/lesson/view.php
mod/lti/classes/event/unknown_service_api_called.php
mod/quiz/classes/event/attempt_abandoned.php
mod/quiz/classes/event/attempt_becameoverdue.php
mod/quiz/classes/event/attempt_submitted.php
mod/resource/lib.php
mod/scorm/locallib.php
mod/scorm/module.js
mod/scorm/tests/behat/add_scorm.feature
mod/scorm/thirdpartylibs.xml [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-min.js [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable.js [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/moodle-mod_scorm-treeview-core.css [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder.png [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder@2x.png [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item.png [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item@2x.png [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview.css [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-debug.js [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-min.js [deleted file]
mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview.js [deleted file]
mod/scorm/yui/src/treeview/assets/moodle-mod_scorm-treeview-core.css [deleted file]
mod/scorm/yui/src/treeview/assets/skins/sam/folder.png [deleted file]
mod/scorm/yui/src/treeview/assets/skins/sam/folder@2x.png [deleted file]
mod/scorm/yui/src/treeview/assets/skins/sam/item.png [deleted file]
mod/scorm/yui/src/treeview/assets/skins/sam/item@2x.png [deleted file]
mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css [deleted file]
mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview.css [deleted file]
mod/scorm/yui/src/treeview/build.json [deleted file]
mod/scorm/yui/src/treeview/js/treeview-sortable.js [deleted file]
mod/scorm/yui/src/treeview/js/treeview.js [deleted file]
mod/scorm/yui/src/treeview/meta/sm-treeview.json [deleted file]
mod/scorm/yui/src/treeview/readme_moodle.txt [deleted file]
mod/url/classes/event/course_module_instance_list_viewed.php [new file with mode: 0644]
mod/url/classes/event/course_module_viewed.php [new file with mode: 0644]
mod/url/index.php
mod/url/view.php
mod/wiki/classes/event/comment_created.php
mod/wiki/classes/event/comment_deleted.php
mod/wiki/classes/event/comments_viewed.php
mod/wiki/classes/event/page_deleted.php
mod/wiki/classes/event/page_diff_viewed.php
mod/wiki/classes/event/page_locks_deleted.php
mod/wiki/classes/event/page_map_viewed.php
mod/wiki/classes/event/page_updated.php
mod/wiki/classes/event/page_version_deleted.php
mod/wiki/classes/event/page_version_restored.php
mod/wiki/classes/event/page_version_viewed.php
mod/wiki/tests/events_test.php
mod/workshop/classes/event/assessable_uploaded.php
portfolio/googledocs/lib.php
report/log/classes/event/content_viewed.php
report/loglive/classes/event/content_viewed.php
report/outline/classes/event/content_viewed.php
report/participation/classes/event/content_viewed.php
report/stats/classes/event/content_viewed.php
repository/dropbox/lib.php
repository/filepicker.js
repository/repository_ajax.php
theme/base/style/core.css
theme/base/style/course.css
theme/bootstrapbase/layout/columns1.php
theme/bootstrapbase/layout/columns2.php
theme/bootstrapbase/layout/columns3.php
theme/bootstrapbase/layout/popup.php
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/course.less
theme/bootstrapbase/less/moodle/undo.less
theme/bootstrapbase/renderers/core_renderer.php
theme/bootstrapbase/style/moodle.css
theme/clean/layout/columns1.php
theme/clean/layout/columns2.php
theme/clean/layout/columns3.php
theme/image.php
theme/yui_combo.php
theme/yui_image.php
user/index.php
user/profile/index.php
version.php

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 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 a07104c..3cdb0eb 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();
 }
@@ -119,10 +119,6 @@ if (ini_get_bool('session.auto_start')) {
     print_error('phpvaroff', 'debug', '', (object)array('name'=>'session.auto_start', 'link'=>$documentationlink));
 }
 
-if (ini_get_bool('magic_quotes_runtime')) {
-    print_error('phpvaroff', 'debug', '', (object)array('name'=>'magic_quotes_runtime', 'link'=>$documentationlink));
-}
-
 if (!ini_get_bool('file_uploads')) {
     print_error('phpvaron', 'debug', '', (object)array('name'=>'file_uploads', 'link'=>$documentationlink));
 }
index d69b8e2..f9afd0a 100644 (file)
@@ -26,7 +26,8 @@ $capabilities = array(
     'moodle/backup:backupcourse',
     'moodle/category:manage',
     'moodle/course:create',
-    'moodle/site:approvecourse'
+    'moodle/site:approvecourse',
+    'moodle/restore:restorecourse'
 );
 if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
     // Speedup for non-admins, add all caps used on this page.
@@ -42,6 +43,12 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
             array('moodle/category:manage')
         )
     );
+    $ADMIN->add('courses',
+        new admin_externalpage('restorecourse', new lang_string('restorecourse', 'admin'),
+            new moodle_url('/backup/restorefile.php', array('contextid' => context_system::instance()->id)),
+            array('moodle/course:create')
+        )
+    );
 
     // Course Default Settings Page.
     // NOTE: these settings must be applied after all other settings because they depend on them.
index a1ad6e0..b99d519 100644 (file)
@@ -27,9 +27,6 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configcheckbox('enabletgzbackups',
             new lang_string('enabletgzbackups', 'admin'),
             new lang_string('enabletgzbackups_desc', 'admin'), 0));
-    $temp->add(new admin_setting_php_extension_enabled('zlibenabled',
-            get_string('zlibenabled', 'admin'),
-            get_string('enabletgzbackups_nozlib', 'admin'), 'zlib'));
 
     $ADMIN->add('experimental', $temp);
 
index 28c933a..790406f 100644 (file)
@@ -78,85 +78,29 @@ if (!empty($options['help'])) {
     exit(0);
 }
 
-
-// Checking $CFG->behat_* vars and values.
+// Describe this script.
 define('BEHAT_UTIL', true);
 define('CLI_SCRIPT', true);
-define('ABORT_AFTER_CONFIG', true);
 define('NO_OUTPUT_BUFFERING', true);
 define('IGNORE_COMPONENT_CACHE', true);
 
-error_reporting(E_ALL | E_STRICT);
-ini_set('display_errors', '1');
-ini_set('log_errors', '1');
-
-// Getting $CFG data.
+// Only load CFG from config.php, stop ASAP in lib/setup.php.
+define('ABORT_AFTER_CONFIG', true);
 require_once(__DIR__ . '/../../../../config.php');
 
-// 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)) {
-    if (!mkdir($CFG->behat_dataroot, $CFG->directorypermissions)) {
-        behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory can not be created');
-    }
-}
-if (!is_dir($CFG->behat_dataroot) || !is_writable($CFG->behat_dataroot)) {
-    behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory has no permissions or is not a directory');
-}
-
-// Check that the directory does not contains other things.
-if (!file_exists("$CFG->behat_dataroot/behattestdir.txt")) {
-    if ($dh = opendir($CFG->behat_dataroot)) {
-        while (($file = readdir($dh)) !== false) {
-            if ($file === 'behat' or $file === '.' or $file === '..' or $file === '.DS_Store') {
-                continue;
-            }
-            behat_error(BEHAT_EXITCODE_CONFIG, '$CFG->behat_dataroot directory is not empty, ensure this is the directory where you want to install behat test dataroot');
-        }
-        closedir($dh);
-        unset($dh);
-        unset($file);
-    }
-
-    // Now we create dataroot directory structure for behat tests.
-    testing_initdataroot($CFG->behat_dataroot, 'behat');
-}
-
-// Overrides vars with behat-test ones.
-$vars = array('wwwroot', 'prefix', 'dataroot');
-foreach ($vars as $var) {
-    $CFG->{$var} = $CFG->{'behat_' . $var};
-}
-
-// Clean $CFG extra values before performing any action.
-behat_clean_init_config();
-
-$CFG->noemailever = true;
-$CFG->passwordsaltmain = 'moodle';
-
-$CFG->themerev = 1;
-$CFG->jsrev = 1;
-
-// Unset cache and temp directories to reset them again with the new $CFG->dataroot.
-unset($CFG->cachedir);
-unset($CFG->localcachedir);
-unset($CFG->tempdir);
+// Remove error handling overrides done in config.php.
+$CFG->debug = (E_ALL | E_STRICT);
+$CFG->debugdisplay = 1;
+error_reporting($CFG->debug);
+ini_set('display_errors', '1');
+ini_set('log_errors', '1');
 
-// Continues setup.
+// Finish moodle init.
 define('ABORT_AFTER_CONFIG_CANCEL', true);
 require("$CFG->dirroot/lib/setup.php");
 
+raise_memory_limit(MEMORY_HUGE);
+
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->libdir.'/upgradelib.php');
 require_once($CFG->libdir.'/clilib.php');
@@ -185,7 +129,7 @@ if ($options['install']) {
     behat_util::start_test_mode();
     $runtestscommand = behat_command::get_behat_command() . ' --config '
         . $CFG->behat_dataroot . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
-    mtrace("Acceptance tests environment enabled, to run the tests use:\n " . $runtestscommand);
+    mtrace("Acceptance tests environment enabled on $CFG->behat_wwwroot, to run the tests use:\n " . $runtestscommand);
 } else if ($options['disable']) {
     behat_util::stop_test_mode();
     mtrace("Acceptance tests environment disabled");
similarity index 95%
rename from admin/tool/behat/tests/tool_behat_test.php
rename to admin/tool/behat/tests/manager_test.php
index 09db5e6..1dc44ef 100644 (file)
@@ -15,7 +15,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Unit tests for admin/tool/behat.
+ * Unit tests for behat manager.
  *
  * @package   tool_behat
  * @copyright  2012 David Monllaó
@@ -30,45 +30,13 @@ require_once($CFG->libdir . '/behat/classes/util.php');
 require_once($CFG->libdir . '/behat/classes/behat_config_manager.php');
 
 /**
- * Allows access to internal methods without exposing them.
- *
- * @package    tool_behat
- * @copyright  2012 David Monllaó
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class testable_behat_config_manager extends behat_config_manager {
-
-    /**
-     * Allow access to protected method
-     * @see parent::merge_config()
-     * @param mixed $config
-     * @param mixed $localconfig
-     * @return mixed
-     */
-    public static function merge_config($config, $localconfig) {
-        return parent::merge_config($config, $localconfig);
-    }
-
-    /**
-     * Allow access to protected method
-     * @see parent::get_config_file_contents()
-     * @param array $features
-     * @param array $stepsdefinitions
-     * @return string
-     */
-    public static function get_config_file_contents($features, $stepsdefinitions) {
-        return parent::get_config_file_contents($features, $stepsdefinitions);
-    }
-}
-
-/**
- * Tool behat tests.
+ * Behat manager tests.
  *
  * @package    tool_behat
  * @copyright  2012 David Monllaó
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_behat_testcase extends advanced_testcase {
+class tool_behat_manager_testcase extends advanced_testcase {
 
     /**
      * behat_config_manager tests.
@@ -103,7 +71,7 @@ class tool_behat_testcase extends advanced_testcase {
 
         $array = testable_behat_config_manager::merge_config($array1, $array2);
 
-        // Overriddes are applied.
+        // Overrides are applied.
         $this->assertEquals('OVERRIDDEN1', $array['simple']);
         $this->assertEquals('OVERRIDDEN2', $array['array']['one']);
 
@@ -150,10 +118,8 @@ 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();
-        }
+        // Add some fake test url.
+        $CFG->behat_wwwroot = 'http://example.com/behat';
 
         // To avoid user value at config.php level.
         unset($CFG->behat_config);
@@ -194,3 +160,34 @@ class tool_behat_testcase extends advanced_testcase {
 
 }
 
+/**
+ * Allows access to internal methods without exposing them.
+ *
+ * @package    tool_behat
+ * @copyright  2012 David Monllaó
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class testable_behat_config_manager extends behat_config_manager {
+
+    /**
+     * Allow access to protected method
+     * @see parent::merge_config()
+     * @param mixed $config
+     * @param mixed $localconfig
+     * @return mixed
+     */
+    public static function merge_config($config, $localconfig) {
+        return parent::merge_config($config, $localconfig);
+    }
+
+    /**
+     * Allow access to protected method
+     * @see parent::get_config_file_contents()
+     * @param array $features
+     * @param array $stepsdefinitions
+     * @return string
+     */
+    public static function get_config_file_contents($features, $stepsdefinitions) {
+        return parent::get_config_file_contents($features, $stepsdefinitions);
+    }
+}
index 3966865..f0fe6c8 100644 (file)
@@ -240,25 +240,6 @@ class problem_000005 extends problem_base {
     }
 }
 
-class problem_000006 extends problem_base {
-    function title() {
-        return 'PHP: magic_quotes_runtime is enabled';
-    }
-    function exists() {
-        return (ini_get_bool('magic_quotes_runtime'));
-    }
-    function severity() {
-        return SEVERITY_SIGNIFICANT;
-    }
-    function description() {
-        return 'Your PHP configuration includes an enabled setting, magic_quotes_runtime, that <strong>must be disabled</strong> in order for Moodle to work correctly. Notable symptoms arising from this misconfiguration include strange display errors whenever a text field that includes single or double quotes is processed.';
-    }
-    function solution() {
-        global $CFG;
-        return '<p>There are two ways you can solve this problem:</p><ol><li>If you have access to your main <strong>php.ini</strong> file, then find the line that looks like this: <pre>magic_quotes_runtime = On</pre> and change it to <pre>magic_quotes_runtime = Off</pre> and then restart your web server. Be warned that this, as any other PHP setting change, might affect other web applications running on the server.</li><li>Finally, you may be able to change this setting just for your site by creating or editing the file <strong>'.$CFG->dirroot.'/.htaccess</strong> to contain this line: <pre>php_value magic_quotes_runtime "Off"</pre></li></ol>';
-    }
-}
-
 class problem_000007 extends problem_base {
     function title() {
         return 'PHP: file_uploads is disabled';
index e995021..ff28703 100644 (file)
@@ -941,7 +941,6 @@ class auth_plugin_mnet extends auth_plugin_base {
         // run the keepalive client
         $this->keepalive_client();
 
-        // admin/cron.php should have run srand for us
         $random100 = rand(0,100);
         if ($random100 < 10) {     // Approximately 10% of the time.
             // nuke olden sessions
index c9e5526..4f441ba 100644 (file)
@@ -309,7 +309,6 @@ class entities {
             $source = str_split($source, 1);
 
             for ($i = 1; $i <= $length; $i++) {
-                mt_srand((double) microtime() * 1000000);
                 $num = mt_rand(1, count($source));
                 $response .= $source[$num - 1];
             }
index 7e37f99..11f245f 100644 (file)
@@ -131,20 +131,38 @@ class backup_course_task extends backup_task {
     /**
      * Code the transformations to perform in the course in
      * order to get transportable (encoded) links
+     * @param string $content content in which to encode links.
+     * @return string content with links encoded.
      */
     static public function encode_content_links($content) {
-        global $CFG;
-
-        $base = preg_quote($CFG->wwwroot, '/');
 
         // Link to the course main page (it also covers "&topic=xx" and "&week=xx"
-        // because they don't become transformed (section number) in backup/restore
-        $search = '/(' . $base . '\/course\/view.php\?id\=)([0-9]+)/';
-        $content= preg_replace($search, '$@COURSEVIEWBYID*$2@$', $content);
+        // because they don't become transformed (section number) in backup/restore.
+        $content = self::encode_links_helper($content, 'COURSEVIEWBYID',       '/course/view.php?id=');
+
+        // A few other key course links.
+        $content = self::encode_links_helper($content, 'GRADEINDEXBYID',       '/grade/index.php?id=');
+        $content = self::encode_links_helper($content, 'GRADEREPORTINDEXBYID', '/grade/report/index.php?id=');
+        $content = self::encode_links_helper($content, 'BADGESVIEWBYID',       '/badges/view.php?type=2&id=');
+        $content = self::encode_links_helper($content, 'USERINDEXVIEWBYID',    '/user/index.php?id=');
 
         return $content;
     }
 
+    /**
+     * Helper method, used by encode_content_links.
+     * @param string $content content in which to encode links.
+     * @param unknown_type $name the name of this type of encoded link.
+     * @param unknown_type $path the path that identifies this type of link, up
+     *      to the ?paramname= bit.
+     * @return string content with one type of link encoded.
+     */
+    static private function encode_links_helper($content, $name, $path) {
+        global $CFG;
+        $base = preg_quote($CFG->wwwroot . $path, '/');
+        return preg_replace('/(' . $base . ')([0-9]+)/', '$@' . $name . '*$2@$', $content);
+    }
+
 // Protected API starts here
 
     /**
index 74bfef4..e9058b8 100644 (file)
@@ -135,10 +135,17 @@ class restore_course_task extends restore_task {
     static public function define_decode_rules() {
         $rules = array();
 
-        $rules[] = new restore_decode_rule('COURSEVIEWBYID', '/course/view.php?id=$1', 'course');
+        // Link to the course main page (it also covers "&topic=xx" and "&week=xx"
+        // because they don't become transformed (section number) in backup/restore.
+        $rules[] = new restore_decode_rule('COURSEVIEWBYID',       '/course/view.php?id=$1',        'course');
 
-        return $rules;
+        // A few other key course links.
+        $rules[] = new restore_decode_rule('GRADEINDEXBYID',       '/grade/index.php?id=$1',        'course');
+        $rules[] = new restore_decode_rule('GRADEREPORTINDEXBYID', '/grade/report/index.php?id=$1', 'course');
+        $rules[] = new restore_decode_rule('BADGESVIEWBYID',       '/badges/view.php?type=2&id=$1', 'course');
+        $rules[] = new restore_decode_rule('USERINDEXVIEWBYID',    '/user/index.php?id=$1',         'course');
 
+        return $rules;
     }
 
 // Protected API starts here
index 6e19ef9..9612163 100644 (file)
@@ -17,9 +17,17 @@ $PAGE->set_pagelayout('standard');
 require_login($course, null, $cm);
 require_capability('moodle/restore:restorecourse', $context);
 
+if (is_null($course)) {
+    $coursefullname = $SITE->fullname;
+    $courseshortname = $SITE->shortname;
+} else {
+    $coursefullname = $course->fullname;
+    $courseshortname = $course->shortname;
+}
+
 // Show page header.
-$PAGE->set_title($course->shortname . ': ' . get_string('restore'));
-$PAGE->set_heading($course->fullname);
+$PAGE->set_title($courseshortname . ': ' . get_string('restore'));
+$PAGE->set_heading($coursefullname);
 
 $renderer = $PAGE->get_renderer('core','backup');
 echo $OUTPUT->header();
index 1d12d0d..411aeb6 100644 (file)
@@ -61,6 +61,14 @@ switch ($context->contextlevel) {
 require_login($course, false, $cm);
 require_capability('moodle/restore:restorecourse', $context);
 
+if (is_null($course)) {
+    $courseid = 0;
+    $coursefullname = $SITE->fullname;
+} else {
+    $courseid = $course->id;
+    $coursefullname = $course->fullname;
+}
+
 $browser = get_file_browser();
 
 // check if tmp dir exists
@@ -83,7 +91,7 @@ if ($action == 'choosebackupfile') {
                     'pathnamehash' => $file->get_pathnamehash(), 'contenthash' => $file->get_contenthash()));
         } else {
             // If it's some weird other kind of file then use old code.
-            $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
+            $filename = restore_controller::get_tempdir_name($courseid, $USER->id);
             $pathname = $tmpdir . '/' . $filename;
             $fileinfo->copy_to_pathname($pathname);
             $restore_url = new moodle_url('/backup/restore.php', array(
@@ -98,14 +106,14 @@ if ($action == 'choosebackupfile') {
 
 $PAGE->set_url($url);
 $PAGE->set_context($context);
-$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_title(get_string('course') . ': ' . $coursefullname);
 $PAGE->set_heading($heading);
 $PAGE->set_pagelayout('admin');
 
 $form = new course_restore_form(null, array('contextid'=>$contextid));
 $data = $form->get_data();
 if ($data && has_capability('moodle/restore:uploadfile', $context)) {
-    $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
+    $filename = restore_controller::get_tempdir_name($courseid, $USER->id);
     $pathname = $tmpdir . '/' . $filename;
     $form->save_file('backupfile', $pathname);
     $restore_url = new moodle_url('/backup/restore.php', array('contextid'=>$contextid, 'filename'=>$filename));
index 6d066b4..d9715d1 100644 (file)
@@ -175,11 +175,7 @@ abstract class backup_controller_dbops extends backup_dbops {
     public static function decode_backup_temp_info($info) {
         // We encode all data except null.
         if ($info != null) {
-            if (extension_loaded('zlib')) {
-                return unserialize(gzuncompress(base64_decode($info)));
-            } else {
-                return unserialize(base64_decode($info));
-            }
+            return unserialize(gzuncompress(base64_decode($info)));
         }
         return $info;
     }
@@ -195,11 +191,7 @@ abstract class backup_controller_dbops extends backup_dbops {
         if ($info != null) {
             // We compress if possible. It reduces db, network and memory storage. The saving is greater than CPU compression cost.
             // Compression level 1 is chosen has it produces good compression with the smallest possible overhead, see MDL-40618.
-            if (extension_loaded('zlib')) {
-                return base64_encode(gzcompress(serialize($info), 1));
-            } else {
-                return base64_encode(serialize($info));
-            }
+            return base64_encode(gzcompress(serialize($info), 1));
         }
         return $info;
     }
diff --git a/backup/util/helper/tests/backup_encode_content_test.php b/backup/util/helper/tests/backup_encode_content_test.php
new file mode 100644 (file)
index 0000000..26299d9
--- /dev/null
@@ -0,0 +1,56 @@
+<?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/>.
+
+/**
+ * @package    core_backup
+ * @category   phpunit
+ * @copyright  2013 The Open University
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+global $CFG;
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_course_task.class.php');
+
+
+
+/**
+ * Tests for encoding content links in backup_course_task.
+ *
+ * The code that this tests is acutally in backup/moodle2/backup_course_task.class.php,
+ * but there is no place for unit tests near there, and perhaps one day it will
+ * be refactored so it becomes more generic.
+ */
+class backup_course_task_testcase extends basic_testcase {
+
+    /**
+     * Test the encode_content_links method for course.
+     */
+    public function test_course_encode_content_links() {
+        global $CFG;
+        $encoded = backup_course_task::encode_content_links(
+                $CFG->wwwroot . '/course/view.php?id=123, ' .
+                $CFG->wwwroot . '/grade/index.php?id=123, ' .
+                $CFG->wwwroot . '/grade/report/index.php?id=123, ' .
+                $CFG->wwwroot . '/badges/view.php?type=2&id=123 and ' .
+                $CFG->wwwroot . '/user/index.php?id=123.');
+        $this->assertEquals('$@COURSEVIEWBYID*123@$, $@GRADEINDEXBYID*123@$, ' .
+                '$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$ and $@USERINDEXVIEWBYID*123@$.', $encoded);
+    }
+}
index 929d9a0..31843a2 100644 (file)
@@ -29,7 +29,6 @@ require_once($CFG->libdir . '/badgeslib.php');
 
 $badgeid = required_param('id', PARAM_INT);
 $copy = optional_param('copy', 0, PARAM_BOOL);
-$delete    = optional_param('delete', 0, PARAM_BOOL);
 $activate = optional_param('activate', 0, PARAM_BOOL);
 $deactivate = optional_param('lock', 0, PARAM_BOOL);
 $confirm   = optional_param('confirm', 0, PARAM_BOOL);
@@ -61,38 +60,6 @@ if ($return !== 0) {
 }
 $returnurl->remove_params('awards');
 
-if ($delete) {
-    require_capability('moodle/badges:deletebadge', $context);
-
-    $PAGE->url->param('delete', 1);
-    if ($confirm) {
-        require_sesskey();
-        $badge->delete();
-        redirect(new moodle_url('/badges/index.php', array('type' => $badge->type, 'id' => $badge->courseid)));
-    }
-
-    $strheading = get_string('delbadge', 'badges');
-    $PAGE->navbar->add($strheading);
-    $PAGE->set_title($strheading);
-    $PAGE->set_heading($badge->name);
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading($strheading);
-
-    $urlparams = array(
-        'id' => $badge->id,
-        'delete' => 1,
-        'confirm' => 1,
-        'sesskey' => sesskey()
-    );
-    $continue = new moodle_url('/badges/action.php', $urlparams);
-    $cancel = new moodle_url('/badges/index.php', array('type' => $badge->type, 'id' => $badge->courseid));
-
-    $message = get_string('delconfirm', 'badges', $badge->name);
-    echo $OUTPUT->confirm($message, $continue, $cancel);
-    echo $OUTPUT->footer();
-    die;
-}
-
 if ($copy) {
     require_sesskey();
     require_capability('moodle/badges:createbadge', $context);
index 9c3db8c..124e178 100644 (file)
@@ -109,7 +109,7 @@ if (count($acceptedroles) > 1) {
             $pageurl = new moodle_url('/badges/award.php', array('id' => $badgeid));
             $issuerrole = new stdClass();
             $issuerrole->roleid = $role;
-            $roleselect = get_string('selectaward', 'badges') . $OUTPUT->single_select(new moodle_url($pageurl), 'role', $select, $role);
+            $roleselect = get_string('selectaward', 'badges') . $OUTPUT->single_select(new moodle_url($pageurl), 'role', $select, $role, null);
         }
     } else {
         echo $OUTPUT->header();
index 96526c4..4287cdd 100644 (file)
@@ -236,9 +236,20 @@ abstract class award_criteria {
      * Review this criteria and decide if the user has completed
      *
      * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    abstract public function review($userid);
+    abstract public function review($userid, $filtered = false);
+
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    abstract public function get_completed_criteria_sql();
 
     /**
      * Mark this criteria as complete for a user
index ff9b92e..8f4d4b0 100644 (file)
@@ -37,13 +37,20 @@ class award_criteria_activity extends award_criteria {
     public $criteriatype = BADGE_CRITERIA_TYPE_ACTIVITY;
 
     private $courseid;
+    private $coursestartdate;
 
     public $required_param = 'module';
     public $optional_params = array('bydate');
 
     public function __construct($record) {
+        global $DB;
         parent::__construct($record);
-        $this->courseid = self::get_course();
+
+        $course = $DB->get_record_sql('SELECT b.courseid, c.startdate
+                        FROM {badge} b INNER JOIN {course} c ON b.courseid = c.id
+                        WHERE b.id = :badgeid ', array('badgeid' => $this->badgeid));
+        $this->courseid = $course->courseid;
+        $this->coursestartdate = $course->startdate;
     }
 
     /**
@@ -95,17 +102,6 @@ class award_criteria_activity extends award_criteria {
         }
     }
 
-    /**
-     * Return course ID for activities
-     *
-     * @return int
-     */
-    private function get_course() {
-        global $DB;
-        $courseid = $DB->get_field('badge', 'courseid', array('id' => $this->badgeid));
-        return $courseid;
-    }
-
     /**
      * Add appropriate new criteria options to the form
      *
@@ -184,14 +180,17 @@ class award_criteria_activity extends award_criteria {
      * Review this criteria and decide if it has been completed
      *
      * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    public function review($userid) {
-        global $DB;
+    public function review($userid, $filtered = false) {
         $completionstates = array(COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS);
-        $course = $DB->get_record('course', array('id' => $this->courseid));
+        $course = new stdClass();
+        $course->id = $this->courseid;
 
-        if ($course->startdate > time()) {
+        if ($this->coursestartdate > time()) {
             return false;
         }
 
@@ -217,7 +216,7 @@ class award_criteria_activity extends award_criteria {
                 } else {
                     return false;
                 }
-            } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            } else {
                 if (in_array($data->completionstate, $completionstates) && $check_date) {
                     return true;
                 } else {
@@ -229,4 +228,44 @@ class award_criteria_activity extends award_criteria {
 
         return $overall;
     }
+
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    public function get_completed_criteria_sql() {
+        $join = '';
+        $where = '';
+        $params = array();
+
+        if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            foreach ($this->params as $param) {
+                $moduledata[] = " cmc.coursemoduleid = :completedmodule{$param['module']} ";
+                $params["completedmodule{$param['module']}"] = $param['module'];
+            }
+            if (!empty($moduledata)) {
+                $extraon = implode(' OR ', $moduledata);
+                $join = " JOIN {course_modules_completion} cmc ON cmc.userid = u.id AND
+                          ( cmc.completionstate = :completionpass OR cmc.completionstate = :completioncomplete ) AND ({$extraon})";
+                $params["completionpass"] = COMPLETION_COMPLETE_PASS;
+                $params["completioncomplete"] = COMPLETION_COMPLETE;
+            }
+            return array($join, $where, $params);
+        } else {
+            foreach ($this->params as $param) {
+                $join .= " LEFT JOIN {course_modules_completion} cmc{$param['module']} ON
+                          cmc{$param['module']}.userid = u.id AND
+                          cmc{$param['module']}.coursemoduleid = :completedmodule{$param['module']} AND
+                          ( cmc{$param['module']}.completionstate = :completionpass{$param['module']} OR
+                            cmc{$param['module']}.completionstate = :completioncomplete{$param['module']} )";
+                $where .= " AND cmc{$param['module']}.coursemoduleid IS NOT NULL ";
+                $params["completedmodule{$param['module']}"] = $param['module'];
+                $params["completionpass{$param['module']}"] = COMPLETION_COMPLETE_PASS;
+                $params["completioncomplete{$param['module']}"] = COMPLETION_COMPLETE;
+            }
+            return array($join, $where, $params);
+        }
+    }
 }
index c6089a2..295d927 100644 (file)
@@ -38,9 +38,23 @@ class award_criteria_course extends award_criteria {
     /* @var int Criteria [BADGE_CRITERIA_TYPE_COURSE] */
     public $criteriatype = BADGE_CRITERIA_TYPE_COURSE;
 
+    private $courseid;
+    private $coursestartdate;
+
     public $required_param = 'course';
     public $optional_params = array('grade', 'bydate');
 
+    public function __construct($record) {
+        global $DB;
+        parent::__construct($record);
+
+        $course = $DB->get_record_sql('SELECT b.courseid, c.startdate
+                        FROM {badge} b INNER JOIN {course} c ON b.courseid = c.id
+                        WHERE b.id = :badgeid ', array('badgeid' => $this->badgeid));
+        $this->courseid = $course->courseid;
+        $this->coursestartdate = $course->startdate;
+    }
+
     /**
      * Add appropriate form elements to the criteria form
      *
@@ -151,18 +165,22 @@ class award_criteria_course extends award_criteria {
      * Review this criteria and decide if it has been completed
      *
      * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    public function review($userid) {
-        global $DB;
-        foreach ($this->params as $param) {
-            $course = $DB->get_record('course', array('id' => $param['course']));
+    public function review($userid, $filtered = false) {
+        $course = new stdClass();
+        $course->id = $this->courseid;
 
-            if ($course->startdate > time()) {
-                return false;
-            }
+        if ($this->coursestartdate > time()) {
+            return false;
+        }
 
-            $info = new completion_info($course);
+        $info = new completion_info($course);
+
+        foreach ($this->params as $param) {
             $check_grade = true;
             $check_date = true;
 
@@ -171,7 +189,7 @@ class award_criteria_course extends award_criteria {
                 $check_grade = ($grade->grade >= $param['grade']);
             }
 
-            if (isset($param['bydate'])) {
+            if (!$filtered && isset($param['bydate'])) {
                 $cparams = array(
                         'userid' => $userid,
                         'course' => $course->id,
@@ -188,4 +206,27 @@ class award_criteria_course extends award_criteria {
 
         return false;
     }
-}
\ No newline at end of file
+
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    public function get_completed_criteria_sql() {
+        // We have only one criterion here, so taking the first one.
+        $coursecriteria = reset($this->params);
+
+        $join = " LEFT JOIN {course_completions} cc ON cc.userid = u.id AND cc.timecompleted > 0";
+        $where = ' AND cc.course = :courseid ';
+        $params['courseid'] = $this->courseid;
+
+        // Add by date parameter.
+        if (isset($param['bydate'])) {
+            $where .= ' AND cc.timecompleted <= :completebydate';
+            $params['completebydate'] = $coursecriteria['bydate'];
+        }
+
+        return array($join, $where, $params);
+    }
+}
index a20e70b..5aabfe8 100644 (file)
@@ -202,12 +202,17 @@ class award_criteria_courseset extends award_criteria {
     /**
      * Review this criteria and decide if it has been completed
      *
+     * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    public function review($userid) {
-        global $DB;
+    public function review($userid, $filtered = false) {
         foreach ($this->params as $param) {
-            $course = $DB->get_record('course', array('id' => $param['course']));
+            $course =  new stdClass();
+            $course->id = $param['course'];
+
             $info = new completion_info($course);
             $check_grade = true;
             $check_date = true;
@@ -217,7 +222,7 @@ class award_criteria_courseset extends award_criteria {
                 $check_grade = ($grade->grade >= $param['grade']);
             }
 
-            if (isset($param['bydate'])) {
+            if (!$filtered && isset($param['bydate'])) {
                 $cparams = array(
                         'userid' => $userid,
                         'course' => $course->id,
@@ -235,7 +240,7 @@ class award_criteria_courseset extends award_criteria {
                 } else {
                     return false;
                 }
-            } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            } else {
                 if ($info->is_course_complete($userid) && $check_grade && $check_date) {
                     return true;
                 } else {
@@ -247,4 +252,39 @@ class award_criteria_courseset extends award_criteria {
 
         return $overall;
     }
+
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    public function get_completed_criteria_sql() {
+        $join = '';
+        $where = '';
+        $params = array();
+
+        if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            foreach ($this->params as $param) {
+                $coursedata[] = " cc.course = :completedcourse{$param['course']} ";
+                $params["completedcourse{$param['course']}"] = $param['course'];
+            }
+            if (!empty($coursedata)) {
+                $extraon = implode(' OR ', $coursedata);
+                $join = " JOIN {course_completions} cc ON cc.userid = u.id AND
+                          cc.timecompleted > 0 AND ({$extraon})";
+            }
+            return array($join, $where, $params);
+        } else {
+            foreach ($this->params as $param) {
+                $join .= " LEFT JOIN {course_completions} cc{$param['course']} ON
+                          cc{$param['course']}.userid = u.id AND
+                          cc{$param['course']}.course = :completedcourse{$param['course']} AND
+                          cc{$param['course']}.timecompleted > 0 ";
+                $where .= " AND cc{$param['course']}.course IS NOT NULL ";
+                $params["completedcourse{$param['course']}"] = $param['course'];
+            }
+            return array($join, $where, $params);
+        }
+    }
 }
index 672c9f8..616ba97 100644 (file)
@@ -142,11 +142,19 @@ class award_criteria_manual extends award_criteria {
      * Review this criteria and decide if it has been completed
      *
      * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    public function review($userid) {
+    public function review($userid, $filtered = false) {
         global $DB;
 
+        // Users were already filtered by criteria completion.
+        if ($filtered) {
+            return true;
+        }
+
         $overall = false;
         foreach ($this->params as $param) {
             $crit = $DB->get_record('badge_manual_award', array('issuerrole' => $param['role'], 'recipientid' => $userid, 'badgeid' => $this->badgeid));
@@ -157,7 +165,7 @@ class award_criteria_manual extends award_criteria {
                     $overall = true;
                     continue;
                 }
-            } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            } else {
                 if (!$crit) {
                     $overall = false;
                     continue;
@@ -169,6 +177,41 @@ class award_criteria_manual extends award_criteria {
         return $overall;
     }
 
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    public function get_completed_criteria_sql() {
+        $join = '';
+        $where = '';
+        $params = array();
+
+        if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            foreach ($this->params as $param) {
+                $roledata[] = " bma.issuerrole = :issuerrole{$param['role']} ";
+                $params["issuerrole{$param['role']}"] = $param['role'];
+            }
+            if (!empty($roledata)) {
+                $extraon = implode(' OR ', $roledata);
+                $join = " JOIN {badge_manual_award} bma ON bma.recipientid = u.id
+                          AND bma.badgeid = :badgeid{$this->badgeid} AND ({$extraon})";
+                $params["badgeid{$this->badgeid}"] = $this->badgeid;
+            }
+            return array($join, $where, $params);
+        } else {
+            foreach ($this->params as $param) {
+                $join .= " LEFT JOIN {badge_manual_award} bma{$param['role']} ON
+                          bma{$param['role']}.recipientid = u.id AND
+                          bma{$param['role']}.issuerrole = :issuerrole{$param['role']} ";
+                $where .= " AND bma{$param['role']}.issuerrole IS NOT NULL ";
+                $params["issuerrole{$param['role']}"] = $param['role'];
+            }
+            return array($join, $where, $params);
+        }
+    }
+
     /**
      * Delete this criterion
      *
index f04e3a4..32e8711 100644 (file)
@@ -86,9 +86,12 @@ class award_criteria_overall extends award_criteria {
      * Overall criteria review should be called only from other criteria handlers.
      *
      * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    public function review($userid) {
+    public function review($userid, $filtered = false) {
         global $DB;
 
         $sql = "SELECT bc.*, bcm.critid, bcm.userid, bcm.datemet
@@ -114,7 +117,7 @@ class award_criteria_overall extends award_criteria {
                     $overall = true;
                     continue;
                 }
-            } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+            } else {
                 if ($crit->datemet === null) {
                     $overall = false;
                     continue;
@@ -127,6 +130,16 @@ class award_criteria_overall extends award_criteria {
         return $overall;
     }
 
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    public function get_completed_criteria_sql() {
+        return array('', '', array());
+    }
+
     /**
      * Add appropriate criteria elements to the form
      *
index 9940e90..18915a0 100644 (file)
@@ -156,35 +156,86 @@ class award_criteria_profile extends award_criteria {
      * Review this criteria and decide if it has been completed
      *
      * @param int $userid User whose criteria completion needs to be reviewed.
+     * @param bool $filtered An additional parameter indicating that user list
+     *        has been reduced and some expensive checks can be skipped.
+     *
      * @return bool Whether criteria is complete
      */
-    public function review($userid) {
+    public function review($userid, $filtered = false) {
         global $DB;
 
-        $overall = false;
+        // Users were already filtered by criteria completion, no checks required.
+        if ($filtered) {
+            return true;
+        }
+
+        $join = '';
+        $where = '';
+        $sqlparams = array();
+        $rule = ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) ? ' OR ' : ' AND ';
+
         foreach ($this->params as $param) {
             if (is_numeric($param['field'])) {
-                $crit = $DB->get_field('user_info_data', 'data', array('userid' => $userid, 'fieldid' => $param['field']));
+                $infodata[] = " uid.fieldid = :fieldid{$param['field']} ";
+                $sqlparams["fieldid{$param['field']}"] = $param['field'];
             } else {
-                $crit = $DB->get_field('user', $param['field'], array('id' => $userid));
+                $userdata[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
             }
+        }
 
-            if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
-                if (!$crit) {
-                    return false;
-                } else {
-                    $overall = true;
-                    continue;
-                }
-            } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
-                if (!$crit) {
-                    $overall = false;
-                    continue;
-                } else {
-                    return true;
-                }
-            }
+        // Add user custom field parameters if there are any.
+        if (!empty($infodata)) {
+            $extraon = implode($rule, $infodata);
+            $join = " LEFT JOIN {user_info_data} uid ON uid.userid = u.id AND ({$extraon})";
         }
+
+        // Add user table field parameters if there are any.
+        if (!empty($userdata)) {
+            $extraon = implode($rule, $userdata);
+            $where = " AND ({$extraon})";
+        }
+
+        $sqlparams['userid'] = $userid;
+        $sql = "SELECT u.* FROM {user} u " . $join . " WHERE u.id = :userid " . $where;
+        $overall = $DB->record_exists_sql($sql, $sqlparams);
+
         return $overall;
     }
+
+    /**
+     * Returns array with sql code and parameters returning all ids
+     * of users who meet this particular criterion.
+     *
+     * @return array list($join, $where, $params)
+     */
+    public function get_completed_criteria_sql() {
+        global $DB;
+
+        $join = '';
+        $where = '';
+        $params = array();
+        $rule = ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) ? ' OR ' : ' AND ';
+
+        foreach ($this->params as $param) {
+            if (is_numeric($param['field'])) {
+                $infodata[] = " uid.fieldid = :fieldid{$param['field']} ";
+                $params["fieldid{$param['field']}"] = $param['field'];
+            } else {
+                $userdata[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
+            }
+        }
+
+        // Add user custom fields if there are any.
+        if (!empty($infodata)) {
+            $extraon = implode($rule, $infodata);
+            $join = " LEFT JOIN {user_info_data} uid ON uid.userid = u.id AND ({$extraon})";
+        }
+
+        // Add user table fields if there are any.
+        if (!empty($userdata)) {
+            $extraon = implode($rule, $userdata);
+            $where = " AND ({$extraon})";
+        }
+        return array($join, $where, $params);
+    }
 }
index 6c73990..ecc0d4f 100644 (file)
@@ -30,12 +30,12 @@ require_once($CFG->libdir . '/badgeslib.php');
 $type       = required_param('type', PARAM_INT);
 $courseid   = optional_param('id', 0, PARAM_INT);
 $page       = optional_param('page', 0, PARAM_INT);
-$activate   = optional_param('activate', 0, PARAM_INT);
 $deactivate = optional_param('lock', 0, PARAM_INT);
 $sortby     = optional_param('sort', 'name', PARAM_ALPHA);
 $sorthow    = optional_param('dir', 'ASC', PARAM_ALPHA);
 $confirm    = optional_param('confirm', false, PARAM_BOOL);
 $delete     = optional_param('delete', 0, PARAM_INT);
+$archive    = optional_param('archive', 0, PARAM_INT);
 $msg        = optional_param('msg', '', PARAM_TEXT);
 
 if (!in_array($sortby, array('name', 'status'))) {
@@ -107,20 +107,34 @@ $PAGE->requires->js('/badges/backpack.js');
 $PAGE->requires->js_init_call('check_site_access', null, false);
 $output = $PAGE->get_renderer('core', 'badges');
 
-if ($delete && has_capability('moodle/badges:deletebadge', $PAGE->context)) {
-    $badge = new badge($delete);
+if (($delete || $archive) && has_capability('moodle/badges:deletebadge', $PAGE->context)) {
+    $badgeid = ($archive != 0) ? $archive : $delete;
+    $badge = new badge($badgeid);
     if (!$confirm) {
         echo $output->header();
-        echo $output->confirm(
-                    get_string('delconfirm', 'badges', $badge->name),
-                    new moodle_url($PAGE->url, array('delete' => $badge->id, 'confirm' => 1)),
-                    $returnurl
-                );
+        // Archive this badge?
+        echo $output->heading(get_string('archivebadge', 'badges', $badge->name));
+        $archivebutton = $output->single_button(
+                            new moodle_url($PAGE->url, array('archive' => $badge->id, 'confirm' => 1)),
+                            get_string('archiveconfirm', 'badges'));
+        echo $output->box(get_string('archivehelp', 'badges') . $archivebutton, 'generalbox');
+
+        // Delete this badge?
+        echo $output->heading(get_string('delbadge', 'badges', $badge->name));
+        $deletebutton = $output->single_button(
+                            new moodle_url($PAGE->url, array('delete' => $badge->id, 'confirm' => 1)),
+                            get_string('delconfirm', 'badges'));
+        echo $output->box(get_string('deletehelp', 'badges') . $deletebutton, 'generalbox');
+
+        // Go back.
+        echo $output->action_link($returnurl, get_string('cancel'));
+
         echo $output->footer();
         die();
     } else {
         require_sesskey();
-        $badge->delete();
+        $archiveonly = ($archive != 0) ? true : false;
+        $badge->delete($archiveonly);
         redirect($returnurl);
     }
 }
index b70d377..97b3271 100644 (file)
@@ -292,9 +292,10 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
         $criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
         $criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
         $criteria_overall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE, 'badgeid' => $badge->id));
-        $criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address'));
+        $criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address', 'field_aim' => 'aim'));
 
         $this->user->address = 'Test address';
+        $this->user->aim = '999999999';
         $sink = $this->redirectEmails();
         user_update_user($this->user, false);
         $this->assertCount(1, $sink->get_messages());
diff --git a/badges/upgrade.txt b/badges/upgrade.txt
new file mode 100644 (file)
index 0000000..bab46ee
--- /dev/null
@@ -0,0 +1,15 @@
+This files describes API changes in /badges/*,
+information provided here is intended especially for developers.
+
+=== 2.7 ===
+
+* get_completed_criteria_sql() - This method was added to award_criteria class and must be overriden
+  in all criteria classes. This method returns an array consisting of SQL JOIN statement, WHERE conditions,
+  and any parameters that might be required. The results are used in lib/badgeslib.php in review_all_criteria()
+  to reduce to the minimum the number of users to review and award badges.
+* New optional parameter $filtered in review() allows to indicate that some expensive checks can be skipped
+  if the list of users has been initially filtered based on met criteria.
+* New optional parameter $archive in delete() in badge class in badgeslib.php
+  allows to indicate that a badge should be archived instead of fully deleted.
+  If this parameter is set to FALSE, a badge will all its information, criteria,
+  and awards will be removed from the database.
index 84303c3..09071ee 100644 (file)
@@ -107,7 +107,7 @@ class block_admin_bookmarks extends block_base {
 
         $this->content->footer = '';
         $this->page->settingsnav->initialise();
-        $node = $this->page->settingsnav->get('root', navigation_node::TYPE_SETTING);
+        $node = $this->page->settingsnav->get('root', navigation_node::TYPE_SITE_ADMIN);
         if (!$node || !$node->contains_active_node()) {
             return $this->content;
         }
similarity index 56%
rename from install/lang/es_ar/langconfig.php
rename to blocks/comments/classes/event/comment_created.php
index 617a3b5..db96870 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Automatically generated strings for Moodle installer
- *
- * Do not edit this file manually! It contains just a subset of strings
- * needed during the very first steps of installation. This file was
- * generated automatically by export-installer.php (which is part of AMOS
- * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
- * list of strings defined in /install/stringnames.txt.
+ * block_comments comment created event.
  *
- * @package   installer
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    block_comments
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+namespace block_comments\event;
 defined('MOODLE_INTERNAL') || die();
 
-$string['parentlanguage'] = 'es';
-$string['thislanguage'] = 'Español - Argentina';
+/**
+ * block_comments comment created event.
+ *
+ * @package    block_comments
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class comment_created extends \core\event\comment_created {
+    // No need to override any method.
+}
similarity index 55%
rename from install/lang/es_es/langconfig.php
rename to blocks/comments/classes/event/comment_deleted.php
index 64e5358..a347c0b 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Automatically generated strings for Moodle installer
- *
- * Do not edit this file manually! It contains just a subset of strings
- * needed during the very first steps of installation. This file was
- * generated automatically by export-installer.php (which is part of AMOS
- * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
- * list of strings defined in /install/stringnames.txt.
+ * block_comments comment deleted event.
  *
- * @package   installer
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    block_comments
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+namespace block_comments\event;
 defined('MOODLE_INTERNAL') || die();
 
-$string['parentlanguage'] = 'es';
-$string['thisdirection'] = 'ltr';
-$string['thislanguage'] = 'Español - España';
+/**
+ * block_comments comment deleted event.
+ *
+ * @package    block_comments
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class comment_deleted extends \core\event\comment_deleted {
+    // No need to override any method.
+}
diff --git a/blocks/comments/tests/events_test.php b/blocks/comments/tests/events_test.php
new file mode 100644 (file)
index 0000000..7cb8e88
--- /dev/null
@@ -0,0 +1,182 @@
+<?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/>.
+
+/**
+ * Events tests.
+ *
+ * @package    block_comments
+ * @category   test
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Events tests class.
+ *
+ * @package    block_comments
+ * @category   test
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_comments_events_testcase extends advanced_testcase {
+    /** @var stdClass Keeps course object */
+    private $course;
+
+    /** @var stdClass Keeps wiki object */
+    private $wiki;
+
+    /**
+     * Setup test data.
+     */
+    public function setUp() {
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        // Create course and wiki.
+        $this->course = $this->getDataGenerator()->create_course();
+        $this->wiki = $this->getDataGenerator()->create_module('wiki', array('course' => $this->course->id));
+    }
+
+    /**
+     * Test comment_created event.
+     */
+    public function test_comment_created() {
+        global $CFG;
+
+        require_once($CFG->dirroot . '/comment/lib.php');
+
+        // Comment on course page.
+        $context = context_course::instance($this->course->id);
+        $args = new stdClass;
+        $args->context = $context;
+        $args->course = $this->course;
+        $args->area = 'page_comments';
+        $args->itemid = 0;
+        $args->component = 'block_comments';
+        $args->linktext = get_string('showcomments');
+        $args->notoggle = true;
+        $args->autostart = true;
+        $args->displaycancel = false;
+        $comment = new comment($args);
+
+        // Triggering and capturing the event.
+        $sink = $this->redirectEvents();
+        $comment->add('New comment');
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = reset($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\block_comments\event\comment_created', $event);
+        $this->assertEquals($context, $event->get_context());
+        $url = new moodle_url('/course/view.php', array('id' => $this->course->id));
+        $this->assertEquals($url, $event->get_url());
+
+        // Comments when block is on module (wiki) page.
+        $context = context_module::instance($this->wiki->id);
+        $args = new stdClass;
+        $args->context   = $context;
+        $args->course    = $this->course;
+        $args->area      = 'page_comments';
+        $args->itemid    = 0;
+        $args->component = 'block_comments';
+        $args->linktext  = get_string('showcomments');
+        $args->notoggle  = true;
+        $args->autostart = true;
+        $args->displaycancel = false;
+        $comment = new comment($args);
+
+        // Triggering and capturing the event.
+        $sink = $this->redirectEvents();
+        $comment->add('New comment 1');
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = reset($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\block_comments\event\comment_created', $event);
+        $this->assertEquals($context, $event->get_context());
+        $url = new moodle_url('/mod/wiki/view.php', array('id' => $this->wiki->id));
+        $this->assertEquals($url, $event->get_url());
+    }
+
+    /**
+     * Test comment_deleted event.
+     */
+    public function test_comment_deleted() {
+        global $CFG;
+
+        require_once($CFG->dirroot . '/comment/lib.php');
+
+        // Comment on course page.
+        $context = context_course::instance($this->course->id);
+        $args = new stdClass;
+        $args->context   = $context;
+        $args->course    = $this->course;
+        $args->area      = 'page_comments';
+        $args->itemid    = 0;
+        $args->component = 'block_comments';
+        $args->linktext  = get_string('showcomments');
+        $args->notoggle  = true;
+        $args->autostart = true;
+        $args->displaycancel = false;
+        $comment = new comment($args);
+        $newcomment = $comment->add('New comment');
+
+        // Triggering and capturing the event.
+        $sink = $this->redirectEvents();
+        $comment->delete($newcomment->id);
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = reset($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\block_comments\event\comment_deleted', $event);
+        $this->assertEquals($context, $event->get_context());
+        $url = new moodle_url('/course/view.php', array('id' => $this->course->id));
+        $this->assertEquals($url, $event->get_url());
+
+        // Comments when block is on module (wiki) page.
+        $context = context_module::instance($this->wiki->id);
+        $args = new stdClass;
+        $args->context   = $context;
+        $args->course    = $this->course;
+        $args->area      = 'page_comments';
+        $args->itemid    = 0;
+        $args->component = 'block_comments';
+        $args->linktext  = get_string('showcomments');
+        $args->notoggle  = true;
+        $args->autostart = true;
+        $args->displaycancel = false;
+        $comment = new comment($args);
+        $newcomment = $comment->add('New comment 1');
+
+        // Triggering and capturing the event.
+        $sink = $this->redirectEvents();
+        $comment->delete($newcomment->id);
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = reset($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\block_comments\event\comment_deleted', $event);
+        $this->assertEquals($context, $event->get_context());
+        $url = new moodle_url('/mod/wiki/view.php', array('id' => $this->wiki->id));
+        $this->assertEquals($url, $event->get_url());
+    }
+}
index a591ad9..e720c18 100644 (file)
@@ -23,7 +23,7 @@ YUI.add('moodle-block_community-comments', function(Y) {
                         .append(Y.one('#commentoverlay-'+commentid+' .commenttitle').get('innerHTML')),
                     bodyContent:Y.one('#commentoverlay-'+commentid).get('innerHTML'),
                     visible: false, //by default it is not displayed
-                    lightbox : false,
+                    modal: false,
                     zIndex:100,
                     closeButtonTitle: this.get('closeButtonTitle')
                 });
index 7696f20..b48a204 100644 (file)
@@ -38,7 +38,7 @@ YUI.add('moodle-block_community-imagegallery', function(Y) {
                 headerContent:Y.one('#imagetitleoverlay').get('innerHTML'),
                 bodyContent:Y.one('#imageoverlay').get('innerHTML'),
                 visible: false, //by default it is not displayed
-                lightbox : false,
+                modal: false,
                 zIndex:100
             });
 
@@ -101,7 +101,7 @@ YUI.add('moodle-block_community-imagegallery', function(Y) {
                 + screennumber + ' / ' + this.imageidnumbers[imageid] + ' </h1></div>' + nextimagelink,
                 bodyContent:Y.one('#imageoverlay').get('innerHTML'),
                 visible: false, //by default it is not displayed
-                lightbox : false,
+                modal: false,
                 zIndex:100,
                 closeButtonTitle: this.get('closeButtonTitle')
             });
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 7a116ee..cb86259 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 c48a7c3..e3f475d 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 0ecd3b9..7e484e0 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 7db6ca9..1ff285d 100644 (file)
@@ -648,7 +648,7 @@ BRANCH.prototype = {
         try {
             var object = Y.JSON.parse(outcome.responseText);
             if (object.error) {
-                Y.use('moodle-core-notification-ajaxException', function () {
+                Y.use('moodle-core-notification-ajaxexception', function () {
                     return new M.core.ajaxException(object).show();
                 });
                 return false;
index b77f584..2e96eeb 100644 (file)
@@ -114,11 +114,11 @@ class block_recent_activity_renderer extends plugin_renderer_base {
                 break;
             case 'add mod':
                 $text = get_string('added', 'moodle', $cm->modfullname). '<br />'.
-                    html_writer::link($cm->get_url(), format_string($cm->name, true));
+                    html_writer::link($cm->url, format_string($cm->name, true));
                 break;
             case 'update mod':
                 $text = get_string('updated', 'moodle', $cm->modfullname). '<br />'.
-                    html_writer::link($cm->get_url(), format_string($cm->name, true));
+                    html_writer::link($cm->url, format_string($cm->name, true));
                 break;
             default:
                 return '';
index 762bb64..3aaa309 100644 (file)
@@ -44,7 +44,7 @@ class block_site_main_menu extends block_list {
                     $content = $cm->get_formatted_content(array('overflowdiv' => true, 'noclean' => true));
                     $instancename = $cm->get_formatted_name();
 
-                    if (!($url = $cm->get_url())) {
+                    if (!($url = $cm->url)) {
                         $this->content->items[] = $content;
                         $this->content->icons[] = '';
                     } else {
@@ -123,7 +123,7 @@ class block_site_main_menu extends block_list {
                     $instancename = $mod->get_formatted_name();
                     $linkcss = $mod->visible ? '' : ' class="dimmed" ';
 
-                    if (!($url = $mod->get_url())) {
+                    if (!($url = $mod->url)) {
                         $this->content->items[] = $content . $editbuttons;
                         $this->content->icons[] = '';
                     } else {
index b802221..96f7a90 100644 (file)
@@ -46,7 +46,7 @@ class block_social_activities extends block_list {
                     $content = $cm->get_formatted_content(array('overflowdiv' => true, 'noclean' => true));
                     $instancename = $cm->get_formatted_name();
 
-                    if (!($url = $cm->get_url())) {
+                    if (!($url = $cm->url)) {
                         $this->content->items[] = $content;
                         $this->content->icons[] = '';
                     } else {
@@ -110,7 +110,7 @@ class block_social_activities extends block_list {
 
                     $linkcss = $mod->visible ? '' : ' class="dimmed" ';
 
-                    if (!($url = $mod->get_url())) {
+                    if (!($url = $mod->url)) {
                         $this->content->items[] = $content . $editbuttons;
                         $this->content->icons[] = '';
                     } else {
index 47c8a29..0109fd4 100644 (file)
@@ -362,7 +362,7 @@ class core_bloglib_testcase extends advanced_testcase {
     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);
 
@@ -382,5 +382,76 @@ class core_bloglib_testcase extends advanced_testcase {
         $arr = array(SITEID, 'blog', 'view', $url2->out(), 'view blog entry');
         $this->assertEventLegacyLogData($arr, $event);
     }
+
+    /**
+     * Test comment_created event.
+     */
+    public function test_blog_comment_created_event() {
+        global $USER, $CFG;
+
+        $this->setAdminUser();
+
+        require_once($CFG->dirroot . '/comment/lib.php');
+        $context = context_user::instance($USER->id);
+
+        $cmt = new stdClass();
+        $cmt->context = $context;
+        $cmt->courseid = $this->courseid;
+        $cmt->area = 'format_blog';
+        $cmt->itemid = $this->postid;
+        $cmt->showcount = 1;
+        $cmt->component = 'blog';
+        $manager = new comment($cmt);
+
+        // Triggering and capturing the event.
+        $sink = $this->redirectEvents();
+        $manager->add("New comment");
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = reset($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\core\event\blog_comment_created', $event);
+        $this->assertEquals($context, $event->get_context());
+        $this->assertEquals($this->postid, $event->other['itemid']);
+        $url = new moodle_url('/blog/index.php', array('entryid' => $this->postid));
+        $this->assertEquals($url, $event->get_url());
+    }
+
+    /**
+     * Test comment_deleted event.
+     */
+    public function test_blog_comment_deleted_event() {
+        global $USER, $CFG;
+
+        $this->setAdminUser();
+
+        require_once($CFG->dirroot . '/comment/lib.php');
+        $context = context_user::instance($USER->id);
+
+        $cmt = new stdClass();
+        $cmt->context = $context;
+        $cmt->courseid = $this->courseid;
+        $cmt->area = 'format_blog';
+        $cmt->itemid = $this->postid;
+        $cmt->showcount = 1;
+        $cmt->component = 'blog';
+        $manager = new comment($cmt);
+        $newcomment = $manager->add("New comment");
+
+        // Triggering and capturing the event.
+        $sink = $this->redirectEvents();
+        $manager->delete($newcomment->id);
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = reset($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\core\event\blog_comment_deleted', $event);
+        $this->assertEquals($context, $event->get_context());
+        $this->assertEquals($this->postid, $event->other['itemid']);
+        $url = new moodle_url('/blog/index.php', array('entryid' => $this->postid));
+        $this->assertEquals($url, $event->get_url());
+    }
 }
 
index 4463e0a..e6e341a 100644 (file)
@@ -281,7 +281,7 @@ class cache_definition {
      * An array of identifiers provided to this cache when it was initialised.
      * @var array
      */
-    protected $identifiers = array();
+    protected $identifiers = null;
 
     /**
      * Key prefix for use with single key cache stores
@@ -654,6 +654,9 @@ class cache_definition {
      * @return array
      */
     public function get_identifiers() {
+        if (!isset($this->identifiers)) {
+            return array();
+        }
         return $this->identifiers;
     }
 
@@ -766,11 +769,22 @@ class cache_definition {
      * @throws coding_exception
      */
     public function set_identifiers(array $identifiers = array()) {
+        // If we are setting the exact same identifiers then just return as nothing really changed.
+        // We don't care about order as cache::make will use the same definition order all the time.
+        if ($identifiers === $this->identifiers) {
+            return;
+        }
+
         foreach ($this->requireidentifiers as $identifier) {
             if (!isset($identifiers[$identifier])) {
                 throw new coding_exception('Identifier required for cache has not been provided: '.$identifier);
             }
         }
+
+        if ($this->identifiers === null) {
+            // Initialize identifiers if they have not been.
+            $this->identifiers = array();
+        }
         foreach ($identifiers as $name => $value) {
             $this->identifiers[$name] = (string)$value;
         }
@@ -893,7 +907,7 @@ class cache_definition {
                 'area' => $this->area,
                 'siteidentifier' => $this->get_cache_identifier()
             );
-            if (!empty($this->identifiers)) {
+            if (isset($this->identifiers) && !empty($this->identifiers)) {
                 $identifiers = array();
                 foreach ($this->identifiers as $key => $value) {
                     $identifiers[] = htmlentities($key, ENT_QUOTES, 'UTF-8').'='.htmlentities($value, ENT_QUOTES, 'UTF-8');
index 55ee3ad..14eecd8 100644 (file)
@@ -129,14 +129,6 @@ class cachestore_dummy extends cache_store {
         return (!empty($this->definition));
     }
 
-    /**
-     * Returns true if this is ready.
-     * @return bool
-     */
-    public function is_ready() {
-        return true;
-    }
-
     /**
      * Returns true the given mode is supported.
      * @param int $mode
index f850e69..2a9621c 100644 (file)
@@ -236,6 +236,11 @@ class cache_factory {
     public function create_cache(cache_definition $definition) {
         $class = $definition->get_cache_class();
         $stores = cache_helper::get_stores_suitable_for_definition($definition);
+        foreach ($stores as $key => $store) {
+            if (!$store::are_requirements_met()) {
+                unset($stores[$key]);
+            }
+        }
         if (count($stores) === 0) {
             // 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);
@@ -253,7 +258,7 @@ class cache_factory {
     /**
      * Creates a store instance given its name and configuration.
      *
-     * If the store has already been instantiated then the original objetc will be returned. (reused)
+     * If the store has already been instantiated then the original object will be returned. (reused)
      *
      * @param string $name The name of the store (must be unique remember)
      * @param array $details
@@ -267,6 +272,7 @@ class cache_factory {
             $store = new $class($details['name'], $details['configuration']);
             $this->stores[$name] = $store;
         }
+        /* @var cache_store $store */
         $store = $this->stores[$name];
         if (!$store->is_ready() || !$store->is_supported_mode($definition->get_mode())) {
             return false;
index 90076aa..c21ac47 100644 (file)
@@ -468,6 +468,7 @@ class cache_helper {
         $class = $store['class'];
 
         // Found the store: is it ready?
+        /* @var cache_store $instance */
         $instance = new $class($store['name'], $store['configuration']);
         if (!$instance->is_ready()) {
             unset($instance);
index d757157..aad5043 100644 (file)
@@ -179,7 +179,9 @@ abstract class cache_store implements cache_store_interface {
      * Returns true if this cache store instance is ready to use.
      * @return bool
      */
-    abstract public function is_ready();
+    public function is_ready() {
+        return forward_static_call(array($this, 'are_requirements_met'));
+    }
 
     /**
      * Retrieves an item from the cache store given its key.
index c71f108..1ee6e5e 100644 (file)
@@ -131,7 +131,7 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
 
         $this->connection = new Memcache;
         foreach ($this->servers as $server) {
-            $this->connection->addServer($server[0], $server[1], true, $server[2]);
+            $this->connection->addServer($server[0], (int) $server[1], true, (int) $server[2]);
         }
         // Test the connection to the pool of servers.
         $this->isready = @$this->connection->set($this->parse_key('ping'), 'ping', MEMCACHE_COMPRESSED, 1);
index d5e245e..d320b8c 100644 (file)
@@ -222,14 +222,6 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
         return (is_array($this->store));
     }
 
-    /**
-     * Returns true if this store instance is ready to be used.
-     * @return bool
-     */
-    public function is_ready() {
-        return true;
-    }
-
     /**
      * Retrieves an item from the cache store given its key.
      *
index e54ee83..5f7de85 100644 (file)
@@ -217,14 +217,6 @@ class cachestore_static extends static_data_store implements cache_is_key_aware,
         return (is_array($this->store));
     }
 
-    /**
-     * Returns true if this store instance is ready to be used.
-     * @return bool
-     */
-    public function is_ready() {
-        return true;
-    }
-
     /**
      * Retrieves an item from the cache store given its key.
      *
index 38f9c4b..75ce1e6 100644 (file)
@@ -1,6 +1,9 @@
 This files describes API changes in /cache/stores/* - cache store plugins.
 Information provided here is intended especially for developers.
 
+=== 2.7 ===
+* cache_store::is_ready is no longer abstract, calling cache_store::are_requirements_met by default.
+
 === 2.6 ===
 * All cache instances are recorded and subsequent requests are given a reference to the original instance.
 * The persistent option for the cache definition has been deprecated. Please use the staticacceleration option instead.
index 8033ac0..443382f 100644 (file)
@@ -652,7 +652,11 @@ class comment {
             $newcmt->time = userdate($newcmt->timecreated, $newcmt->strftimeformat);
 
             // Trigger comment created event.
-            $eventclassname = '\\' . $this->component . '\\event\comment_created';
+            if (core_component::is_core_subsystem($this->component)) {
+                $eventclassname = '\\core\\event\\' . $this->component . '_comment_created';
+            } else {
+                $eventclassname = '\\' . $this->component . '\\event\comment_created';
+            }
             if (class_exists($eventclassname)) {
                 $event = $eventclassname::create(
                         array(
@@ -724,7 +728,11 @@ class comment {
         }
         $DB->delete_records('comments', array('id'=>$commentid));
         // Trigger comment delete event.
-        $eventclassname = '\\' . $this->component . '\\event\comment_deleted';
+        if (core_component::is_core_subsystem($this->component)) {
+            $eventclassname = '\\core\\event\\' . $this->component . '_comment_deleted';
+        } else {
+            $eventclassname = '\\' . $this->component . '\\event\comment_deleted';
+        }
         if (class_exists($eventclassname)) {
             $event = $eventclassname::create(
                     array(
index e7f1631..451fa87 100644 (file)
@@ -584,22 +584,12 @@ $CFG->admin = 'admin';
 //=========================================================================
 // 11. BEHAT SUPPORT
 //=========================================================================
-// Behat needs a separate data directory and unique database prefix:
+// Behat test site needs a unique www root, data directory and database prefix:
 //
+// $CFG->behat_wwwroot = 'http://127.0.0.1/moodle';
 // $CFG->behat_prefix = 'bht_';
 // $CFG->behat_dataroot = '/home/example/bht_moodledata';
 //
-// To set a seperate wwwroot for Behat to use, use $CFG->behat_wwwroot; this is set automatically
-// to http://localhost:8000 as it is the proposed PHP built-in server URL. Instead of that you can,
-// for example, use an alias, add a host to /etc/hosts or add a new virtual host having a URL
-// poiting to your production site and another one poiting to your test site. Note that you need
-// to ensure that this URL is not accessible from the www as the behat test site uses "sugar"
-// credentials (admin/admin) and can be easily hackable.
-//
-// Example:
-//   $CFG->behat_wwwroot = 'http://192.168.1.250:8000';
-//   $CFG->behat_wwwroot = 'http://localhost/moodlesitetesting';
-//
 // You can override default Moodle configuration for Behat and add your own
 // params; here you can add more profiles, use different Mink drivers than Selenium...
 // These params would be merged with the default Moodle behat.yml, giving priority
@@ -644,16 +634,6 @@ $CFG->admin = 'admin';
 //       )
 //   );
 //
-// You can completely switch to test environment when "php admin/tool/behat/cli/util --enable",
-// this means that all the site accesses will be routed to the test environment instead of
-// the regular one, so NEVER USE THIS SETTING IN PRODUCTION SITES. This setting is useful
-// when working with cloud CI (continous integration) servers which requires public sites to run the
-// tests, or in testing/development installations when you are developing in a pre-PHP 5.4 server.
-// Note that with this setting enabled $CFG->behat_wwwroot is ignored and $CFG->behat_wwwroot
-// value will be the regular $CFG->wwwroot value.
-// Example:
-//   $CFG->behat_switchcompletely = true;
-//
 // You can force the browser session (not user's sessions) to restart after N seconds. This could
 // be useful if you are using a cloud-based service with time restrictions in the browser side.
 // Setting this value the browser session that Behat is using will be restarted. Set the time in
index a26144d..9a02d19 100644 (file)
@@ -278,6 +278,14 @@ class helper {
             );
         }
 
+        if ($category->can_restore_courses_into()) {
+            $actions['restore'] = array(
+                'url' => new \moodle_url('/backup/restorefile.php', array('contextid' => $category->get_context()->id)),
+                'icon' => new \pix_icon('i/restore', new \lang_string('restorecourse', 'admin')),
+                'string' => new \lang_string('restorecourse', 'admin')
+            );
+        }
+
         return $actions;
     }
 
index d279d39..00b19d4 100644 (file)
@@ -736,38 +736,13 @@ M.course_dndupload = {
                     var result = JSON.parse(xhr.responseText);
                     if (result) {
                         if (result.error == 0) {
-                            // All OK - update the dummy element
-                            if (result.content) {
-                                // A label
-                                resel.indentdiv.innerHTML = '<div class="activityinstance" ></div>' + result.content + result.commands;
-                            } else {
-                                // Not a label
-                                resel.icon.src = result.icon;
-                                resel.a.href = result.link;
-                                resel.namespan.innerHTML = result.name;
-
-                                if (!parseInt(result.visible, 10)) {
-                                    resel.a.className = 'dimmed';
-                                }
-
-                                if (result.groupingname) {
-                                    resel.groupingspan.innerHTML = '(' + result.groupingname + ')';
-                                } else {
-                                    resel.div.removeChild(resel.groupingspan);
-                                }
-
-                                resel.div.removeChild(resel.progressouter);
-                                resel.indentdiv.innerHTML += result.commands;
-                                if (result.onclick) {
-                                    resel.a.onclick = result.onclick;
-                                }
-                                if (self.Y.UA.gecko > 0) {
-                                    // Fix a Firefox bug which makes sites with a '~' in their wwwroot
-                                    // log the user out when clicking on the link (before refreshing the page).
-                                    resel.div.innerHTML = unescape(resel.div.innerHTML);
-                                }
+                            // All OK - replace the dummy element.
+                            resel.li.outerHTML = result.fullcontent;
+                            if (self.Y.UA.gecko > 0) {
+                                // Fix a Firefox bug which makes sites with a '~' in their wwwroot
+                                // log the user out when clicking on the link (before refreshing the page).
+                                resel.li.outerHTML = unescape(resel.li.outerHTML);
                             }
-                            resel.li.id = result.elementid;
                             self.add_editing(result.elementid);
                         } else {
                             // Error - remove the dummy element
@@ -986,39 +961,14 @@ M.course_dndupload = {
                     var result = JSON.parse(xhr.responseText);
                     if (result) {
                         if (result.error == 0) {
-                            // All OK - update the dummy element
-                            if (result.content) {
-                                // A label
-                                resel.indentdiv.innerHTML = '<div class="activityinstance" ></div>' + result.content + result.commands;
-                            } else {
-                                // Not a label
-                                resel.icon.src = result.icon;
-                                resel.a.href = result.link;
-                                resel.namespan.innerHTML = result.name;
-
-                                if (!parseInt(result.visible, 10)) {
-                                    resel.a.className = 'dimmed';
-                                }
-
-                                if (result.groupingname) {
-                                    resel.groupingspan.innerHTML = '(' + result.groupingname + ')';
-                                } else {
-                                    resel.div.removeChild(resel.groupingspan);
-                                }
-
-                                resel.div.removeChild(resel.progressouter);
-                                resel.div.innerHTML += result.commands;
-                                if (result.onclick) {
-                                    resel.a.onclick = result.onclick;
-                                }
-                                if (self.Y.UA.gecko > 0) {
-                                    // Fix a Firefox bug which makes sites with a '~' in their wwwroot
-                                    // log the user out when clicking on the link (before refreshing the page).
-                                    resel.div.innerHTML = unescape(resel.div.innerHTML);
-                                }
+                            // All OK - replace the dummy element.
+                            resel.li.outerHTML = result.fullcontent;
+                            if (self.Y.UA.gecko > 0) {
+                                // Fix a Firefox bug which makes sites with a '~' in their wwwroot
+                                // log the user out when clicking on the link (before refreshing the page).
+                                resel.li.outerHTML = unescape(resel.li.outerHTML);
                             }
-                            resel.li.id = result.elementid;
-                            self.add_editing(result.elementid, sectionnumber);
+                            self.add_editing(result.elementid);
                         } else {
                             // Error - remove the dummy element
                             resel.parent.removeChild(resel.li);
index 3fa6bb2..84aea52 100644 (file)
@@ -722,29 +722,18 @@ class dndupload_ajax_processor {
      */
     protected function send_response($mod) {
         global $OUTPUT, $PAGE;
-        $courserenderer = $PAGE->get_renderer('core', 'course');
 
         $resp = new stdClass();
         $resp->error = self::ERROR_OK;
-        $resp->icon = $mod->get_icon_url()->out();
-        $resp->name = $mod->name;
-        if ($mod->has_view()) {
-            $resp->link = $mod->get_url()->out();
-        } else {
-            $resp->link = null;
-        }
-        $resp->content = $mod->get_content();
-        $resp->elementid = 'module-'.$mod->id;
-        $actions = course_get_cm_edit_actions($mod, 0, $mod->sectionnum);
-        $resp->commands = ' '. $courserenderer->course_section_cm_edit_actions($actions, $mod);
-        $resp->onclick = $mod->get_on_click();
-        $resp->visible = $mod->visible;
-
-        // If using groupings, then display grouping name.
-        if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', $this->context)) {
-            $groupings = groups_get_all_groupings($this->course->id);
-            $resp->groupingname = format_string($groupings[$mod->groupingid]->name);
-        }
+        $resp->elementid = 'module-' . $mod->id;
+
+        $courserenderer = $PAGE->get_renderer('core', 'course');
+        $completioninfo = new completion_info($this->course);
+        $info = get_fast_modinfo($this->course);
+        $sr = null;
+        $modulehtml = $courserenderer->course_section_cm($this->course, $completioninfo,
+                $mod, null, array());
+        $resp->fullcontent = $courserenderer->course_section_cm_list_item($this->course, $completioninfo, $mod, $sr);
 
         echo $OUTPUT->header();
         echo json_encode($resp);
index 2d61c28..f8b2fa4 100644 (file)
@@ -148,15 +148,15 @@ class core_course_external extends external_api {
                         $modcontext = context_module::instance($cm->id);
 
                         if (!empty($cm->showdescription) or $cm->modname == 'label') {
-                            // We want to use the external format. However from reading get_formatted_content(), get_content() format is always FORMAT_HTML.
-                            list($module['description'], $descriptionformat) = external_format_text($cm->get_content(),
+                            // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
+                            list($module['description'], $descriptionformat) = external_format_text($cm->content,
                                 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
                         }
 
                         //url of the module
-                        $url = $cm->get_url();
+                        $url = $cm->url;
                         if ($url) { //labels don't have url
-                            $module['url'] = $cm->get_url()->out(false);
+                            $module['url'] = $url->out(false);
                         }
 
                         $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
index 396e4d6..dfe2970 100644 (file)
@@ -100,7 +100,7 @@ class format_singleactivity extends format_base {
         if (!$cm->uservisible) {
             return null;
         }
-        $action = $cm->get_url();
+        $action = $cm->url;
         if (!$action) {
             // Do not add to navigation activity without url (i.e. labels).
             return null;
@@ -412,13 +412,13 @@ class format_singleactivity extends format_base {
                     // Student views an empty course page.
                     return;
                 }
-            } else if (!$cm->uservisible || !$cm->get_url()) {
+            } else if (!$cm->uservisible || !$cm->url) {
                 // Activity is set but not visible to current user or does not have url.
                 // Display course page (either empty or with availability restriction info).
                 return;
             } else {
                 // Everything is set up and accessible, redirect to the activity page!
-                redirect($cm->get_url());
+                redirect($cm->url);
             }
         }
     }
index 05b8efe..c1e9a14 100644 (file)
@@ -43,15 +43,12 @@ define('COURSE_MAX_COURSES_PER_DROPDOWN', 1000);
 // Max users in log dropdown before switching to optional.
 define('COURSE_MAX_USERS_PER_DROPDOWN', 1000);
 define('FRONTPAGENEWS', '0');
-define('FRONTPAGECOURSELIST', '1'); // Not used. TODO MDL-38832 remove.
 define('FRONTPAGECATEGORYNAMES', '2');
-define('FRONTPAGETOPICONLY', '3'); // Not used. TODO MDL-38832 remove.
 define('FRONTPAGECATEGORYCOMBO', '4');
 define('FRONTPAGEENROLLEDCOURSELIST', '5');
 define('FRONTPAGEALLCOURSELIST', '6');
 define('FRONTPAGECOURSESEARCH', '7');
 // Important! Replaced with $CFG->frontpagecourselimit - maximum number of courses displayed on the frontpage.
-define('FRONTPAGECOURSELIMIT',    200); // TODO MDL-38832 remove.
 define('EXCELROWS', 65535);
 define('FIRSTUSEDEXCELROW', 3);
 
index bfc3495..9feeec5 100644 (file)
@@ -58,6 +58,23 @@ if (has_capability('moodle/user:loginas', $systemcontext)) {
         print_error('usernotincourse');
     }
     $context = $coursecontext;
+
+    // Check if course has SEPARATEGROUPS and user is part of that group.
+    if (groups_get_course_groupmode($course) == SEPARATEGROUPS &&
+            !has_capability('moodle/site:accessallgroups', $context)) {
+        $samegroup = false;
+        if ($groups = groups_get_all_groups($course->id, $USER->id)) {
+            foreach ($groups as $group) {
+                if (groups_is_member($group->id, $userid)) {
+                    $samegroup = true;
+                    break;
+                }
+            }
+        }
+        if (!$samegroup) {
+            print_error('nologinas');
+        }
+    }
 }
 
 // Login as this user and return to course home page.
index 1001212..e7f1b6b 100644 (file)
@@ -743,7 +743,7 @@ class core_course_renderer extends plugin_renderer_base {
             // nothing to be displayed to the user
             return $output;
         }
-        $url = $mod->get_url();
+        $url = $mod->url;
         if (!$url) {
             return $output;
         }
@@ -793,7 +793,7 @@ class core_course_renderer extends plugin_renderer_base {
 
         // Get on-click attribute value if specified and decode the onclick - it
         // has already been encoded for display (puke).
-        $onclick = htmlspecialchars_decode($mod->get_on_click(), ENT_QUOTES);
+        $onclick = htmlspecialchars_decode($mod->onclick, ENT_QUOTES);
 
         $groupinglabel = '';
         if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($mod->course))) {
@@ -851,7 +851,7 @@ class core_course_renderer extends plugin_renderer_base {
         } else {
             $textclasses .= ' dimmed_text';
         }
-        if ($mod->get_url()) {
+        if ($mod->url) {
             if ($content) {
                 // If specified, display extra content after link.
                 $output = html_writer::tag('div', $content, array('class' =>
@@ -923,7 +923,7 @@ class core_course_renderer extends plugin_renderer_base {
     public function course_section_cm_list_item($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) {
         $output = '';
         if ($modulehtml = $this->course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions)) {
-            $modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->get_extra_classes();
+            $modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->extraclasses;
             $output .= html_writer::tag('li', $modulehtml, array('class' => $modclasses, 'id' => 'module-' . $mod->id));
         }
         return $output;
@@ -937,7 +937,6 @@ class core_course_renderer extends plugin_renderer_base {
      *
      * This function calls:
      * {@link core_course_renderer::course_section_cm_name()}
-     * {@link cm_info::get_after_link()}
      * {@link core_course_renderer::course_section_cm_text()}
      * {@link core_course_renderer::course_section_cm_availability()}
      * {@link core_course_renderer::course_section_cm_completion()}
@@ -1005,7 +1004,7 @@ class core_course_renderer extends plugin_renderer_base {
             }
 
             // Module can put text after the link (e.g. forum unread)
-            $output .= $mod->get_after_link();
+            $output .= $mod->afterlink;
 
             // Closing the tag which contains everything but edit icons. Content part of the module should not be part of this.
             $output .= html_writer::end_tag('div'); // .activityinstance
@@ -1018,7 +1017,7 @@ class core_course_renderer extends plugin_renderer_base {
         // it should work similarly (at least in terms of ordering) to an
         // activity.
         $contentpart = $this->course_section_cm_text($mod, $displayoptions);
-        $url = $mod->get_url();
+        $url = $mod->url;
         if (empty($url)) {
             $output .= $contentpart;
         }
@@ -1027,7 +1026,7 @@ class core_course_renderer extends plugin_renderer_base {
         if ($this->page->user_is_editing()) {
             $editactions = course_get_cm_edit_actions($mod, $mod->indent, $sectionreturn);
             $modicons .= ' '. $this->course_section_cm_edit_actions($editactions, $mod, $displayoptions);
-            $modicons .= $mod->get_after_edit_icons();
+            $modicons .= $mod->afterediticons;
         }
 
         $modicons .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
diff --git a/course/tests/behat/activities_edit_with_block_dock.feature b/course/tests/behat/activities_edit_with_block_dock.feature
new file mode 100644 (file)
index 0000000..89d1717
--- /dev/null
@@ -0,0 +1,29 @@
+@core @core_course
+Feature: Open the edit menu when a block is docked
+  In order to edit an activity with a block docked
+  As a teacher
+  I need to open the action menu
+
+  @javascript
+  Scenario: Open the action menu with a block docked
+    Given the following "users" exists:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com |
+    And the following "courses" exists:
+      | fullname | shortname | format |
+      | Course 1 | C1 | weeks |
+    And the following "course enrolments" exists:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Glossary" to section "1" and I fill the form with:
+      | Name | Test glossary name |
+      | Description | Test glossary description |
+    And I dock "Navigation" block
+    When I open "Test glossary name" actions menu
+    Then "Test glossary name" actions menu should be open
+    And I reload the page
+    When I open "Test glossary name" actions menu
+    Then "Test glossary name" actions menu should be open
index d31c1b9..07b948f 100644 (file)
@@ -643,6 +643,29 @@ class behat_course extends behat_base {
         return new Given('I click on "a[role=\'menuitem\']" "css_element" in the "' . $this->escape($activityname) . '" activity');
     }
 
+    /**
+     * Checks that the specified activity's action menu is open.
+     *
+     * @Then /^"(?P<activity_name_string>(?:[^"]|\\")*)" actions menu should be open$/
+     * @throws DriverException The step is not available when Javascript is disabled
+     * @param string $activityname
+     */
+    public function actions_menu_should_be_open($activityname) {
+
+        if (!$this->running_javascript()) {
+            throw new DriverException('Activities actions menu not available when Javascript is disabled');
+        }
+
+        // If it is already closed we do nothing.
+        $activitynode = $this->get_activity_node($activityname);
+        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
+        if (empty($classes['action-menu-shown'])) {
+            throw new ExpectationException(sprintf("The action menu for '%s' is not open", $activityname), $this->getSession());
+        }
+
+        return;
+    }
+
     /**
      * Indents to the right the activity or resource specified by it's name. Editing mode should be on.
      *
index 92a5f6c..994161a 100644 (file)
@@ -600,13 +600,13 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         foreach($firstsection['modules'] as $module) {
             if ($module['id'] == $forumcm->id and $module['modname'] == 'forum') {
                 $cm = $modinfo->cms[$forumcm->id];
-                $formattedtext = format_text($cm->get_content(), FORMAT_HTML,
+                $formattedtext = format_text($cm->content, FORMAT_HTML,
                     array('noclean' => true, 'para' => false, 'filter' => false));
                 $this->assertEquals($formattedtext, $module['description']);
                 $testexecuted = $testexecuted + 1;
             } else if ($module['id'] == $labelcm->id and $module['modname'] == 'label') {
                 $cm = $modinfo->cms[$labelcm->id];
-                $formattedtext = format_text($cm->get_content(), FORMAT_HTML,
+                $formattedtext = format_text($cm->content, FORMAT_HTML,
                     array('noclean' => true, 'para' => false, 'filter' => false));
                 $this->assertEquals($formattedtext, $module['description']);
                 $testexecuted = $testexecuted + 1;
index 5430b6c..16b2ca0 100644 (file)
         $PAGE->set_button($buttons);
     }
 
-    $PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+    // If viewing a section, make the title more specific
+    if ($section and $section > 0 and course_format_uses_sections($course->format)) {
+        $sectionname = get_string('sectionname', "format_$course->format");
+        $sectiontitle = get_section_name($course, $section);
+        $PAGE->set_title(get_string('coursesectiontitle', 'moodle', array('course' => $course->fullname, 'sectiontitle' => $sectiontitle, 'sectionname' => $sectionname)));
+    } else {
+        $PAGE->set_title(get_string('coursetitle', 'moodle', array('course' => $course->fullname)));
+    }
+
     $PAGE->set_heading($course->fullname);
     echo $OUTPUT->header();
 
diff --git a/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js
new file mode 100644 (file)
index 0000000..96126f3
Binary files /dev/null and b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js differ
diff --git a/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js
new file mode 100644 (file)
index 0000000..33f576f
Binary files /dev/null and b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js differ
diff --git a/course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js
new file mode 100644 (file)
index 0000000..96126f3
Binary files /dev/null and b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js differ
diff --git a/course/yui/modchooser/modchooser.js b/course/yui/modchooser/modchooser.js
deleted file mode 100644 (file)
index ce39f93..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-YUI.add('moodle-course-modchooser', function(Y) {
-    var CSS = {
-        PAGECONTENT : 'div#page-content',
-        SECTION : 'li.section',
-        SECTIONMODCHOOSER : 'span.section-modchooser-link',
-        SITEMENU : 'div.block_site_main_menu',
-        SITETOPIC : 'div.sitetopic'
-    };
-
-    var MODCHOOSERNAME = 'course-modchooser';
-
-    var MODCHOOSER = function() {
-        MODCHOOSER.superclass.constructor.apply(this, arguments);
-    }
-
-    Y.extend(MODCHOOSER, M.core.chooserdialogue, {
-        // The current section ID
-        sectionid : null,
-
-        // The hidden element holding the jump param
-        jumplink : null,
-
-        initializer : function(config) {
-            var dialogue = Y.one('.chooserdialoguebody');
-            var header = Y.one('.choosertitle');
-            var params = {};
-            this.setup_chooser_dialogue(dialogue, header, params);
-
-            // Initialize existing sections and register for dynamically created sections
-            this.setup_for_section();
-            M.course.coursebase.register_module(this);
-
-            // Catch the page toggle
-            Y.all('.block_settings #settingsnav .type_course .modchoosertoggle a').on('click', this.toggle_mod_chooser, this);
-        },
-        /**
-         * Update any section areas within the scope of the specified
-         * selector with AJAX equivalents
-         *
-         * @param baseselector The selector to limit scope to
-         * @return void
-         */
-        setup_for_section : function(baseselector) {
-            if (!baseselector) {
-                var baseselector = CSS.PAGECONTENT;
-            }
-
-            // Setup for site topics
-            Y.one(baseselector).all(CSS.SITETOPIC).each(function(section) {
-                this._setup_for_section(section);
-            }, this);
-
-            // Setup for standard course topics
-            Y.one(baseselector).all(CSS.SECTION).each(function(section) {
-                this._setup_for_section(section);
-            }, this);
-
-            // Setup for the block site menu
-            Y.one(baseselector).all(CSS.SITEMENU).each(function(section) {
-                this._setup_for_section(section);
-            }, this);
-        },
-        _setup_for_section : function(section, sectionid) {
-            var chooserspan = section.one(CSS.SECTIONMODCHOOSER);
-            if (!chooserspan) {
-                return;
-            }
-            var chooserlink = Y.Node.create("<a href='#' />");
-            chooserspan.get('children').each(function(node) {
-                chooserlink.appendChild(node);
-            });
-            chooserspan.insertBefore(chooserlink);
-            chooserlink.on('click', this.display_mod_chooser, this);
-        },
-        /**
-         * Display the module chooser
-         *
-         * @param e Event Triggering Event
-         * @param secitonid integer The ID of the section triggering the dialogue
-         * @return void
-         */
-        display_mod_chooser : function (e) {
-            // Set the section for this version of the dialogue
-            if (e.target.ancestor(CSS.SITETOPIC)) {
-                // The site topic has a sectionid of 1
-                this.sectionid = 1;
-            } else if (e.target.ancestor(CSS.SECTION)) {
-                var section = e.target.ancestor(CSS.SECTION);
-                this.sectionid = section.get('id').replace('section-', '');
-            } else if (e.target.ancestor(CSS.SITEMENU)) {
-                // The block site menu has a sectionid of 0
-                this.sectionid = 0;
-            }
-            this.display_chooser(e);
-        },
-        toggle_mod_chooser : function(e) {
-            // Get the add section link
-            var modchooserlinks = Y.all('div.addresourcemodchooser');
-
-            // Get the dropdowns
-            var dropdowns = Y.all('div.addresourcedropdown');
-
-            if (modchooserlinks.size() == 0) {
-                // Continue with non-js action if there are no modchoosers to add
-                return;
-            }
-
-            // We need to update the text and link
-            var togglelink = Y.one('.block_settings #settingsnav .type_course .modchoosertoggle a');
-
-            // The actual text is in the last child
-            var toggletext = togglelink.get('lastChild');
-
-            var usemodchooser;
-            // Determine whether they're currently hidden
-            if (modchooserlinks.item(0).hasClass('visibleifjs')) {
-                // The modchooser is currently visible, hide it
-                usemodchooser = 0;
-                modchooserlinks
-                    .removeClass('visibleifjs')
-                    .addClass('hiddenifjs');
-                dropdowns
-                    .addClass('visibleifjs')
-                    .removeClass('hiddenifjs');
-                toggletext.set('data', M.util.get_string('modchooserenable', 'moodle'));
-                togglelink.set('href', togglelink.get('href').replace('off', 'on'));
-            } else {
-                // The modchooser is currently not visible, show it
-                usemodchooser = 1;
-                modchooserlinks
-                    .addClass('visibleifjs')
-                    .removeClass('hiddenifjs');
-                dropdowns
-                    .removeClass('visibleifjs')
-                    .addClass('hiddenifjs');
-                toggletext.set('data', M.util.get_string('modchooserdisable', 'moodle'));
-                togglelink.set('href', togglelink.get('href').replace('on', 'off'));
-            }
-
-            M.util.set_user_preference('usemodchooser', usemodchooser);
-
-            // Prevent the page from reloading
-            e.preventDefault();
-        },
-        option_selected : function(thisoption) {
-            // Add the sectionid to the URL
-            this.jumplink.set('value', thisoption.get('value') + '&section=' + this.sectionid);
-        }
-    },
-    {
-        NAME : MODCHOOSERNAME,
-        ATTRS : {
-            maxheight : {
-                value : 800
-            }
-        }
-    });
-    M.course = M.course || {};
-    M.course.init_chooser = function(config) {
-        return new MODCHOOSER(config);
-    }
-},
-'@VERSION@', {
-    requires:['base', 'overlay', 'moodle-core-chooserdialogue', 'moodle-course-coursebase']
-}
-);
diff --git a/course/yui/src/modchooser/build.json b/course/yui/src/modchooser/build.json
new file mode 100644 (file)
index 0000000..569ab7b
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "name": "moodle-course-modchooser",
+  "builds": {
+    "moodle-course-modchooser": {
+      "jsfiles": [
+        "modchooser.js"
+      ]
+    }
+  }
+}
diff --git a/course/yui/src/modchooser/js/modchooser.js b/course/yui/src/modchooser/js/modchooser.js
new file mode 100644 (file)
index 0000000..0bd9825
--- /dev/null
@@ -0,0 +1,173 @@
+/**
+ * The activity chooser dialogue for courses.
+ *
+ * @moodle-course-modchooser
+ */
+
+var CSS = {
+    PAGECONTENT : 'div#page-content',
+    SECTION : 'li.section',
+    SECTIONMODCHOOSER : 'span.section-modchooser-link',
+    SITEMENU : 'div.block_site_main_menu',
+    SITETOPIC : 'div.sitetopic'
+};
+
+var MODCHOOSERNAME = 'course-modchooser';
+
+/**
+ * The activity chooser dialogue for courses.
+ *
+ * @constructor
+ * @class M.course.modchooser
+ * @extends M.core.chooserdialogue
+ */
+var MODCHOOSER = function() {
+    MODCHOOSER.superclass.constructor.apply(this, arguments);
+};
+
+Y.extend(MODCHOOSER, M.core.chooserdialogue, {
+    // The current section ID
+    sectionid : null,
+
+    // The hidden element holding the jump param
+    jumplink : null,
+
+    initializer : function() {
+        var dialogue = Y.one('.chooserdialoguebody');
+        var header = Y.one('.choosertitle');
+        var params = {};
+        this.setup_chooser_dialogue(dialogue, header, params);
+
+        // Initialize existing sections and register for dynamically created sections
+        this.setup_for_section();
+        M.course.coursebase.register_module(this);
+
+        // Catch the page toggle
+        Y.all('.block_settings #settingsnav .type_course .modchoosertoggle a').on('click', this.toggle_mod_chooser, this);
+    },
+    /**
+     * Update any section areas within the scope of the specified
+     * selector with AJAX equivalents
+     *
+     * @param baseselector The selector to limit scope to
+     * @return void
+     */
+    setup_for_section : function(baseselector) {
+        if (!baseselector) {
+            baseselector = CSS.PAGECONTENT;
+        }
+
+        // Setup for site topics
+        Y.one(baseselector).all(CSS.SITETOPIC).each(function(section) {
+            this._setup_for_section(section);
+        }, this);
+
+        // Setup for standard course topics
+        Y.one(baseselector).all(CSS.SECTION).each(function(section) {
+            this._setup_for_section(section);
+        }, this);
+
+        // Setup for the block site menu
+        Y.one(baseselector).all(CSS.SITEMENU).each(function(section) {
+            this._setup_for_section(section);
+        }, this);
+    },
+    _setup_for_section : function(section) {
+        var chooserspan = section.one(CSS.SECTIONMODCHOOSER);
+        if (!chooserspan) {
+            return;
+        }
+        var chooserlink = Y.Node.create("<a href='#' />");
+        chooserspan.get('children').each(function(node) {
+            chooserlink.appendChild(node);
+        });
+        chooserspan.insertBefore(chooserlink);
+        chooserlink.on('click', this.display_mod_chooser, this);
+    },
+    /**
+        * Display the module chooser
+        *
+        * @param e Event Triggering Event
+        * @param secitonid integer The ID of the section triggering the dialogue
+        * @return void
+        */
+    display_mod_chooser : function (e) {
+        // Set the section for this version of the dialogue
+        if (e.target.ancestor(CSS.SITETOPIC)) {
+            // The site topic has a sectionid of 1
+            this.sectionid = 1;
+        } else if (e.target.ancestor(CSS.SECTION)) {
+            var section = e.target.ancestor(CSS.SECTION);
+            this.sectionid = section.get('id').replace('section-', '');
+        } else if (e.target.ancestor(CSS.SITEMENU)) {
+            // The block site menu has a sectionid of 0
+            this.sectionid = 0;
+        }
+        this.display_chooser(e);
+    },
+    toggle_mod_chooser : function(e) {
+        // Get the add section link
+        var modchooserlinks = Y.all('div.addresourcemodchooser');
+
+        // Get the dropdowns
+        var dropdowns = Y.all('div.addresourcedropdown');
+
+        if (modchooserlinks.size() === 0) {
+            // Continue with non-js action if there are no modchoosers to add
+            return;
+        }
+
+        // We need to update the text and link
+        var togglelink = Y.one('.block_settings #settingsnav .type_course .modchoosertoggle a');
+
+        // The actual text is in the last child
+        var toggletext = togglelink.get('lastChild');
+
+        var usemodchooser;
+        // Determine whether they're currently hidden
+        if (modchooserlinks.item(0).hasClass('visibleifjs')) {
+            // The modchooser is currently visible, hide it
+            usemodchooser = 0;
+            modchooserlinks
+                .removeClass('visibleifjs')
+                .addClass('hiddenifjs');
+            dropdowns
+                .addClass('visibleifjs')
+                .removeClass('hiddenifjs');
+            toggletext.set('data', M.util.get_string('modchooserenable', 'moodle'));
+            togglelink.set('href', togglelink.get('href').replace('off', 'on'));
+        } else {
+            // The modchooser is currently not visible, show it
+            usemodchooser = 1;
+            modchooserlinks
+                .addClass('visibleifjs')
+                .removeClass('hiddenifjs');
+            dropdowns
+                .removeClass('visibleifjs')
+                .addClass('hiddenifjs');
+            toggletext.set('data', M.util.get_string('modchooserdisable', 'moodle'));
+            togglelink.set('href', togglelink.get('href').replace('on', 'off'));
+        }
+
+        M.util.set_user_preference('usemodchooser', usemodchooser);
+
+        // Prevent the page from reloading
+        e.preventDefault();
+    },
+    option_selected : function(thisoption) {
+        // Add the sectionid to the URL
+        this.jumplink.set('value', thisoption.get('value') + '&section=' + this.sectionid);
+    }
+},
+{
+    NAME : MODCHOOSERNAME,
+    ATTRS : {
+        maxheight : {
+            value : 800
+        }
+    }
+});
+M.course = M.course || {};
+M.course.init_chooser = function(config) {
+    return new MODCHOOSER(config);
+};
diff --git a/course/yui/src/modchooser/meta/modchooser.json b/course/yui/src/modchooser/meta/modchooser.json
new file mode 100644 (file)
index 0000000..2ef461b
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "moodle-course-modchooser": {
+    "requires": [
+        "moodle-core-chooserdialogue",
+        "moodle-course-coursebase"
+    ]
+  }
+}
index 1746697..6ad641f 100644 (file)
@@ -1058,7 +1058,7 @@ class course_enrolment_manager {
                     continue;
                 } else if ($ue->timestart and $ue->timeend) {
                     $period = get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend)));
-                    $periodoutside = ($ue->timestart && $ue->timeend && $now < $ue->timestart && $now > $ue->timeend);
+                    $periodoutside = ($ue->timestart && $ue->timeend && ($now < $ue->timestart || $now > $ue->timeend));
                 } else if ($ue->timestart) {
                     $period = get_string('periodstart', 'enrol', userdate($ue->timestart));
                     $periodoutside = ($ue->timestart && $now < $ue->timestart);
index 8362185..dee47d8 100644 (file)
@@ -156,11 +156,8 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             }
 
             this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url(collapsedimage, 'moodle'));
-            this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).once('click', function() {
-                // We want to do this just once, the first time the controls are shown.
-                this.populateStartDates();
-                this.populateDuration();
-            }, this);
+            this.populateStartDates();
+            this.populateDuration();
             this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).on('click', function(){
                 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).toggleClass(CSS.ACTIVE);
                 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).toggleClass(CSS.HIDDEN);
index 093a58a..cbde453 100644 (file)
@@ -113,7 +113,7 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             var event = this.on('assignablerolesloaded', function(){
                 event.detach();
                 var s = M.str.role, confirmation = {
-                    lightbox :  true,
+                    modal:  true,
                     visible  :  true,
                     centered :  true,
                     title    :  s.confirmunassigntitle,
index c5beda0..0a18679 100644 (file)
Binary files a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js differ
index 605f8f5..5d6f83b 100644 (file)
Binary files a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js differ
index c5beda0..0a18679 100644 (file)
Binary files a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js differ
index efee206..3217f4a 100644 (file)
@@ -68,7 +68,7 @@ Y.extend(AUTOLINKER, Y.Base, {
                 for (key in data.entries) {
                     definition = data.entries[key].definition + data.entries[key].attachments;
                     alertpanel = new M.core.alert({title:data.entries[key].concept,
-                        message:definition, lightbox:false, yesLabel: M.util.get_string('ok', 'moodle')});
+                        message:definition, modal:false, yesLabel: M.util.get_string('ok', 'moodle')});
                     alertpanel.show();
                     Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus();
                 }
index 290e7ed..538d03e 100644 (file)
@@ -132,8 +132,13 @@ class filter_urltolink extends moodle_text_filter {
         $querystring = '(?:\?(?:[\pL0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)';
         $fragment = '(?:\#(?:[\pL0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)';
 
-        $regex = "(?<!=[\"'])$urlstart((?:$domainsegment\.)+$domainsegment|$numericip)" .
-                "($port?$path$querystring?$fragment?)(?<![]),.;])";
+        // Lookbehind assertions.
+        // Is not HTML attribute or CSS URL property. Unfortunately legit text like "url(http://...)" will not be a link.
+        $lookbehindstart = "(?<!=[\"']|\burl\([\"' ]|\burl\()";
+        $lookbehindend = "(?<![]),.;])";
+
+        $regex = "$lookbehindstart$urlstart((?:$domainsegment\.)+$domainsegment|$numericip)" .
+                "($port?$path$querystring?$fragment?)$lookbehindend";
         if ($unicoderegexp) {
             $regex = '#' . $regex . '#ui';
         } else {
index e448d26..58bd4a3 100644 (file)
@@ -31,19 +31,6 @@ require_once($CFG->dirroot . '/filter/urltolink/filter.php'); // Include the cod
 
 class filter_urltolink_testcase extends basic_testcase {
 
-    /**
-     * Helper function that represents the legacy implementation
-     * of convert_urls_into_links()
-     */
-    protected function old_convert_urls_into_links(&$text) {
-        /// Make lone URLs into links.   eg http://moodle.com/
-        $text = preg_replace("%([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])%i",
-            '$1<a href="$2://$3$4" target="_blank">$2://$3$4</a>', $text);
-        /// eg www.moodle.com
-        $text = preg_replace("%([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])%i",
-            '$1<a href="http://www.$2$3" target="_blank">www.$2$3</a>', $text);
-    }
-
     function get_convert_urls_into_links_test_cases() {
         $texts = array (
             //just a url
@@ -144,6 +131,11 @@ class filter_urltolink_testcase extends basic_testcase {
             '<td background="www.moodle.org">&nbsp;</td>' => '<td background="www.moodle.org">&nbsp;</td>',
             '<form name="input" action="http://moodle.org/submit.asp" method="get">'=>'<form name="input" action="http://moodle.org/submit.asp" method="get">',
             '<td background="https://www.moodle.org">&nbsp;</td>' => '<td background="https://www.moodle.org">&nbsp;</td>',
+            // CSS URLs.
+            '<table style="background-image: url(\'http://moodle.org/pic.jpg\');">' => '<table style="background-image: url(\'http://moodle.org/pic.jpg\');">',
+            '<table style="background-image: url(http://moodle.org/pic.jpg);">' => '<table style="background-image: url(http://moodle.org/pic.jpg);">',
+            '<table style="background-image: url("http://moodle.org/pic.jpg");">' => '<table style="background-image: url("http://moodle.org/pic.jpg");">',
+            '<table style="background-image: url( http://moodle.org/pic.jpg );">' => '<table style="background-image: url( http://moodle.org/pic.jpg );">',
             //partially escaped img tag
             'partially escaped img tag &lt;img src="http://moodle.org/logo/logo-240x60.gif" />' => 'partially escaped img tag &lt;img src="http://moodle.org/logo/logo-240x60.gif" />',
             //fully escaped img tag. Commented out as part of MDL-21183
@@ -181,32 +173,6 @@ class filter_urltolink_testcase extends basic_testcase {
         $this->assertEquals($correctresult, $text);
     }
 
-    function test_convert_urls_into_links_performance() {
-        $testablefilter = new testable_filter_urltolink();
-
-        $reps = 1000;
-        $text = file_get_contents(__DIR__ . '/fixtures/sample.txt');
-        $time_start = microtime(true);
-        for($i=0;$i<$reps;$i++) {
-            $testablefilter->convert_urls_into_links($text);
-        }
-        $time_end = microtime(true);
-        $new_time = $time_end - $time_start;
-
-        $time_start = microtime(true);
-        for($i=0;$i<$reps;$i++) {
-            $this->old_convert_urls_into_links($text);
-        }
-        $time_end = microtime(true);
-        $old_time = $time_end - $time_start;
-
-        $fast_enough = false;
-        if( $new_time < $old_time ) {
-            $fast_enough = true;
-        }
-
-        $this->assertEquals($fast_enough, true, 'Timing test: ' . $new_time . 'secs (new) < ' . $old_time . 'secs (old)');
-    }
 }
 
 
index f68ba22..f4a357c 100644 (file)
@@ -473,9 +473,12 @@ class grade_report_grader extends grade_report {
                            AND ue.status = :uestatus
                            AND e.status = :estatus
                            AND e.courseid = :courseid
+                           AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)
                   GROUP BY ue.userid";
             $coursecontext = $this->context->get_course_context(true);
-            $params = array_merge($uparams, array('estatus'=>ENROL_INSTANCE_ENABLED, 'uestatus'=>ENROL_USER_ACTIVE, 'courseid'=>$coursecontext->instanceid));
+            $time = time();
+            $params = array_merge($uparams, array('estatus' => ENROL_INSTANCE_ENABLED, 'uestatus' => ENROL_USER_ACTIVE,
+                    'courseid' => $coursecontext->instanceid, 'now1' => $time, 'now2' => $time));
             $useractiveenrolments = $DB->get_records_sql($sql, $params);
 
             $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
index 07c59e7..f3d768c 100644 (file)
@@ -99,9 +99,14 @@ class core_grade_edittreelib_testcase extends advanced_testcase {
         $this->assertEquals($scale->id, $gradeitem->scaleid);
         $this->assertEquals($scalestring, $cell->text, "Grade text matches scale");
 
-        // Now change it to no grade.
+        // Now change it to no grade with gradebook feedback enabled.
+        $adminconfig = $assign->get_admin_config();
+        $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
+        $gradebookplugin .= '_enabled';
+
         $instance = $assign->get_instance();
         $instance->grade = 0;
+        $instance->$gradebookplugin = 1;
         $instance->instance = $instance->id;
         $assign->update_instance($instance);
 
@@ -111,6 +116,19 @@ class core_grade_edittreelib_testcase extends advanced_testcase {
         $this->assertEquals(GRADE_TYPE_TEXT, $gradeitem->gradetype);
         $this->assertEquals(null, $gradeitem->scaleid);
         $this->assertEquals(' - ', $cell->text, 'Grade text matches empty value of " - "');
+
+        // Now change it to no grade with gradebook feedback disabled.
+        $instance = $assign->get_instance();
+        $instance->grade = 0;
+        $instance->$gradebookplugin = 0;
+        $instance->instance = $instance->id;
+        $assign->update_instance($instance);
+
+        $gradeitem = grade_item::fetch($gradeitemparams);
+        $cell = $column->get_item_cell($gradeitem, array());
+
+        $this->assertEquals(GRADE_TYPE_NONE, $gradeitem->gradetype);
+        $this->assertEquals(null, $gradeitem->scaleid);
     }
 }
 
index 0c250a0..75c7ccf 100644 (file)
@@ -60,10 +60,10 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
 @ini_set('display_errors', '1');
 
 // 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
-    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;
 }
@@ -108,10 +108,6 @@ $config = new stdClass();
 $config->lang = $lang;
 
 if (!empty($_POST)) {
-    if (install_ini_get_bool('magic_quotes_gpc')) {
-        $_POST = array_map('stripslashes', $_POST);
-    }
-
     $config->stage = (int)$_POST['stage'];
 
     if (isset($_POST['previous'])) {
@@ -530,11 +526,10 @@ if ($config->stage == INSTALL_DATABASETYPE) {
 
 
 if ($config->stage == INSTALL_ENVIRONMENT or $config->stage == INSTALL_PATHS) {
-    $version_fail = (version_compare(phpversion(), "5.3.3") < 0);
     $curl_fail    = ($lang !== 'en' and !extension_loaded('curl')); // needed for lang pack download
     $zip_fail     = ($lang !== 'en' and !extension_loaded('zip'));  // needed for lang pack download
 
-    if ($version_fail or $curl_fail or $zip_fail) {
+    if ($curl_fail or $zip_fail) {
         $config->stage = INSTALL_ENVIRONMENT;
 
         install_print_header($config, get_string('environmenthead', 'install'),
@@ -542,10 +537,6 @@ if ($config->stage == INSTALL_ENVIRONMENT or $config->stage == INSTALL_PATHS) {
                                       get_string('environmentsub2', 'install'));
 
         echo '<div id="envresult"><dl>';
-        if ($version_fail) {
-            $a = (object)array('needed'=>'5.3.3', 'current'=>phpversion());
-            echo '<dt>'.get_string('phpversion', 'install').'</dt><dd>'.get_string('environmentrequireversion', 'admin', $a).'</dd>';
-        }
         if ($curl_fail) {
             echo '<dt>'.get_string('phpextension', 'install', 'cURL').'</dt><dd>'.get_string('environmentrequireinstall', 'admin').'</dd>';
         }
index 954c712..eba828b 100644 (file)
@@ -30,7 +30,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['cannotcreatedboninstall'] = '<p>Non è possibile creare il database </p> <p>Il database non esiste o l\'utente non è autorizzato a crearlo.</p>
+$string['cannotcreatedboninstall'] = '<p>Non è possibile creare il database </p> <p>Il database non esiste e l\'utente fornito non è autorizzato a crearlo.</p>
 <p>E\' necessario che l\'amministratore del sito  verifichi  la configurazione del database.</p>';
 $string['cannotcreatelangdir'] = 'Non è possibile creare la cartella lang';
 $string['cannotcreatetempdir'] = 'Non è possibile creare la cartella temp';
index e678417..56d14f3 100644 (file)
@@ -30,6 +30,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['cannotcreatedboninstall'] = '<p>無法建立資料庫</p>
+<p>指定之資料庫並不存在。使用者並未有權限建立資料庫</p>
+<p>網站管理員需查明資料庫之組態.</p>';
 $string['cannotcreatelangdir'] = '無法建立語言資料夾。';
 $string['cannotcreatetempdir'] = '無法建立暫存資料夾。';
 $string['cannotdownloadcomponents'] = '無法下載元件。';
index 7006d1a..5bff7f6 100644 (file)
@@ -490,7 +490,6 @@ $string['enablesafebrowserintegration'] = 'Enable Safe Exam Browser integration'
 $string['enablestats'] = 'Enable statistics';
 $string['enabletgzbackups'] = 'Enable new backup format';
 $string['enabletgzbackups_desc'] = 'If enabled, future backups will be created in a new compression format for .mbz files (internally stored as a .tar.gz file). This removes the 4GB backup size restriction and may improve performance. Restore supports both formats and the difference should be transparent to users.';
-$string['enabletgzbackups_nozlib'] = 'PHP extension &lsquo;zlib&rsquo; is not available. The new backup format relies on this extension and will be disabled until zlib is installed and enabled.';
 $string['enabletrusttext'] = 'Enable trusted content';
 $string['enablewebservices'] = 'Enable web services';
 $string['enablewsdocumentation'] = 'Web services documentation';
@@ -521,7 +520,6 @@ $string['experimental'] = 'Experimental';
 $string['experimentalsettings'] = 'Experimental settings';
 $string['extendedusernamechars'] = 'Allow extended characters in usernames';
 $string['extramemorylimit'] = 'Extra PHP memory limit';
-$string['fatalmagicquotesruntime'] = '<p>Serious configuration error detected, please notify server administrator.</p><p> To operate properly, Moodle requires that administrator changes PHP settings.</p><p><code>magic_quotes_runtime</code> must be set to <code>off</code>.</p><p>This setting is controlled by editing <code>php.ini</code>, Apache/IIS <br />configuration or <code>.htaccess</code> file on the server.</p>';
 $string['fatalsessionautostart'] = '<p>Serious configuration error detected, please notify server administrator.</p><p> To operate properly, Moodle requires that administrator changes PHP settings.</p><p><code>session.auto_start</code> must be set to <code>off</code>.</p><p>This setting is controlled by editing <code>php.ini</code>, Apache/IIS <br />configuration or <code>.htaccess</code> file on the server.</p>';
 $string['filecreated'] = 'New file created';
 $string['filestoredin'] = 'Save file into folder :';
@@ -921,6 +919,7 @@ $string['purgecaches']= 'Purge all caches';
 $string['purgecachesconfirm']= 'Moodle can cache themes, javascript, language strings, filtered text, rss feeds and many other pieces of calculated data.  Purging these caches will delete that data from the server and force browsers to refetch data, so that you can be sure you are seeing the most up-to-date values produced by the current code.  There is no danger in purging caches, but your site may appear slower for a while until the server and clients calculate new information and cache it.';
 $string['purgecachesfinished']= 'All caches were purged.';
 $string['requestcategoryselection'] = 'Enable category selection';
+$string['restorecourse'] = 'Restore course';
 $string['restorernewroleid'] = 'Restorers\' role in courses';
 $string['restorernewroleid_help'] = 'If the user does not already have the permission to manage the newly restored course, the user is automatically assigned this role and enrolled if necessary. Select "None" if you do not want restorers to be able to manage every restored course.';
 $string['reverseproxy'] = 'Reverse proxy';
@@ -1127,7 +1126,6 @@ $string['webproxyinfo'] = 'Fill in following options if your Moodle server can n
 $string['xmlrpcrecommended'] = 'The xmlrpc extension is needed for hub communication, and useful for web services and Moodle networking';
 $string['yuicomboloading'] = 'YUI combo loading';
 $string['ziprequired'] = 'The Zip PHP extension is now required by Moodle, info-ZIP binaries or PclZip library are not used anymore.';
-$string['zlibenabled'] = 'zlib enabled';
 
 
 $string['caching'] = 'Caching';
index c7d814f..e37caaa 100644 (file)
@@ -117,7 +117,6 @@ $string['errorminbackup20version'] = 'This backup file has been created with one
 $string['errorrestorefrontpage'] = 'Restoring over front page is not allowed.';
 $string['errorinvalidformat'] = 'Unknown backup format';
 $string['errorinvalidformatinfo'] = 'The selected file is not a valid Moodle backup file and can\'t be restored.';
-$string['errortgznozlib'] = 'The selected file is in the new backup format and cannot be restored because the zlib PHP extension is not available on this system.';
 $string['executionsuccess'] = 'The backup file was successfully created.';
 $string['filename'] = 'Filename';
 $string['filealiasesrestorefailures'] = 'Aliases restore failures';
index 14cd22b..099d8e7 100644 (file)
@@ -55,6 +55,10 @@ $string['anymethodactivity'] = 'Any of the selected activities is complete';
 $string['anymethodcourseset'] = 'Any of the selected courses is complete';
 $string['anymethodmanual'] = 'Any of the selected roles awards the badge';
 $string['anymethodprofile'] = 'Any of the selected profile fields has been completed';
+$string['archivebadge'] = 'Would you like to delete badge \'{$a}\', but keep existing issued badges?';
+$string['archiveconfirm'] = 'Delete and keep existing issued badges';
+$string['archivehelp'] = '<p>This option means that the badge will be marked as "retired" and will no longer appear in the list of badges. Users will no longer be able to earn this badge, however existing badge recipients will still be able to display this badge on their profile page and push it to their external backpacks.</p>
+<p>If you would like your users to retain access to the earned badges it is important to select this option instead of fully deleting badges.</p>';
 $string['attachment'] = 'Attach badge to message';
 $string['attachment_help'] = 'If checked, an issued badge file will be attached to the recepient\'s email for download. Email attachments must be enabled in site settings to use this option.';
 $string['award'] = 'Award badge';
@@ -196,8 +200,10 @@ $string['defaultissuercontact'] = 'Default badge issuer contact details';
 $string['defaultissuercontact_desc'] = 'An email address associated with the badge issuer.';
 $string['defaultissuername'] = 'Default badge issuer name';
 $string['defaultissuername_desc'] = 'Name of the issuing agent or authority.';
-$string['delbadge'] = 'Delete badge';
-$string['delconfirm'] = 'Are you sure that you want to delete badge \'{$a}\'?';
+$string['delbadge'] = 'Would you like to delete badge \'{$a}\' and remove all existing issued badges?';
+$string['delconfirm'] = 'Delete and remove existing issued badges';
+$string['deletehelp'] = '<p>Fully deleting a badge means that all its information and criteria records will be permanently removed. Users who have earned this badge will no longer be able to access it and display it on their profile pages.</p>
+<p>Note: Users who have earned this badge and have already pushed it to their external backpack, will still have this badge in their external backpack. However, they will not be able to access criteria and evidence pages linking back to this web site.</p>';
 $string['delcritconfirm'] = 'Are you sure that you want to delete this criterion?';
 $string['delparamconfirm'] = 'Are you sure that you want to delete this parameter?';
 $string['description'] = 'Description';
index 882655a..e9da636 100644 (file)
@@ -145,15 +145,6 @@ $string['inputwebdirectory'] = 'Moodle directory:';
 $string['installation'] = 'Installation';
 $string['langdownloaderror'] = 'Unfortunately the language "{$a}" could not be downloaded. The installation process will continue in English.';
 $string['langdownloadok'] = 'The language "{$a}" was installed successfully. The installation process will continue in this language.';
-$string['magicquotesruntime'] = 'Magic quotes run time';
-$string['magicquotesruntimeerror'] = 'This should be off';
-$string['magicquotesruntimehelp'] = '<p>Magic quotes runtime should be turned off for Moodle to function properly.</p>
-
-<p>Normally it is off by default ... see the setting <b>magic_quotes_runtime</b> in your php.ini file.</p>
-
-<p>If you don\'t have access to your php.ini, you might be able to place the following line in a file 
-   called .htaccess within your Moodle directory:</p>
-   <blockquote><div>php_value magic_quotes_runtime Off</div></blockquote>';
 $string['memorylimit'] = 'Memory limit';
 $string['memorylimiterror'] = 'The PHP memory limit is set quite low ... you may run into problems later.';
 $string['memorylimithelp'] = '<p>The PHP memory limit for your server is currently set to {$a}.</p>
index f2f102a..0f537a4 100644 (file)
@@ -372,12 +372,14 @@ $string['courserequestsupport'] = 'Supporting information to help the administra
 $string['courserestore'] = 'Course restore';
 $string['courses'] = 'Courses';
 $string['coursesectionsummaries'] = 'Course section summaries';
+$string['coursesectiontitle'] = 'Course: {$a->course}, {$a->sectionname}: {$a->sectiontitle}';
 $string['coursesettings'] = 'Course default settings';
 $string['coursesmovedout'] = 'Courses moved out from {$a}';
 $string['coursespending'] = 'Courses pending approval';
 $string['coursestart'] = 'Course start';
 $string['coursesummary'] = 'Course summary';
 $string['coursesummary_help'] = 'The course summary is displayed in the list of courses. A course search searches course summary text in addition to course names.';
+$string['coursetitle'] = 'Course: {$a->course}';
 $string['courseupdates'] = 'Course updates';
 $string['create'] = 'Create';
 $string['createaccount'] = 'Create my new account';
index f035ae7..8e92faf 100644 (file)
@@ -5462,6 +5462,10 @@ abstract class context extends stdClass implements IteratorAggregate {
     public function delete() {
         global $DB;
 
+        if ($this->_contextlevel <= CONTEXT_SYSTEM) {
+            throw new coding_exception('Cannot delete system context');
+        }
+
         // double check the context still exists
         if (!$DB->record_exists('context', array('id'=>$this->_id))) {
             context::cache_remove($this);
@@ -5557,6 +5561,11 @@ abstract class context extends stdClass implements IteratorAggregate {
     public function get_child_contexts() {
         global $DB;
 
+        if (empty($this->_path) or empty($this->_depth)) {
+            debugging('Can not find child contexts of context '.$this->_id.' try rebuilding of context paths');
+            return array();
+        }
+
         $sql = "SELECT ctx.*
                   FROM {context} ctx
                  WHERE ctx.path LIKE ?";
@@ -6557,6 +6566,11 @@ class context_coursecat extends context {
     public function get_child_contexts() {
         global $DB;
 
+        if (empty($this->_path) or empty($this->_depth)) {
+            debugging('Can not find child contexts of context '.$this->_id.' try rebuilding of context paths');
+            return array();
+        }
+
         $sql = "SELECT ctx.*
                   FROM {context} ctx
                  WHERE ctx.path LIKE ? AND (ctx.depth = ? OR ctx.contextlevel = ?)";
index a190faa..41c86ef 100644 (file)
@@ -1660,11 +1660,21 @@ abstract class admin_setting {
             rebuild_course_cache(0, true);
         }
 
-        add_to_config_log($name, $oldvalue, $value, $this->plugin);
+        $this->add_to_config_log($name, $oldvalue, $value);
 
         return true; // BC only
     }
 
+    /**
+     * Log config changes if necessary.
+     * @param string $name
+     * @param string $oldvalue
+     * @param string $value
+     */
+    protected function add_to_config_log($name, $oldvalue, $value) {
+        add_to_config_log($name, $oldvalue, $value, $this->plugin);
+    }
+
     /**
      * Returns current value of this setting
      * @return mixed array or string depending on instance, NULL means not set yet
@@ -2161,6 +2171,22 @@ class admin_setting_configpasswordunmask extends admin_setting_configtext {
         parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
     }
 
+    /**
+     * Log config changes if necessary.
+     * @param string $name
+     * @param string $oldvalue
+     * @param string $value
+     */
+    protected function add_to_config_log($name, $oldvalue, $value) {
+        if ($value !== '') {
+            $value = '********';
+        }
+        if ($oldvalue !== '' and $oldvalue !== null) {
+            $oldvalue = '********';
+        }
+        parent::add_to_config_log($name, $oldvalue, $value);
+    }
+
     /**
      * Returns XHTML for the field
      * Writes Javascript into the HTML below right before the last div
index d0c2695..11b5ef2 100644 (file)
                                die("Illegal path http:// or ftp://");
                }
                
-                       
-               // Initialize random number generator for randomizing cache flushes
-               // -- note Since PHP 4.2.0, the seed  becomes optional and defaults to a random value if omitted.
-                srand(((double)microtime())*1000000);
-               
                /**
                 * ADODB version as a string.
                 */
index aee9248..27ff1b5 100644 (file)
@@ -19,6 +19,6 @@ Added:
  * readme_moodle.txt - this file ;-)
 
 Our changes:
- * none
+ * Removed random seed initialization from lib/adodb/adodb.inc.php:172
 
 skodak, iarenaza, moodler, stronk7
index c57e6c0..c9f21f8 100644 (file)
@@ -67,3 +67,40 @@ function ajaxenabled(array $browsers = null) {
         return false;
     }
 }
+
+/**
+ * Starts capturing output whilst processing an AJAX request.
+ *
+ * This should be used in combination with ajax_check_captured_output to
+ * report any captured output to the user.
+ *
+ * @retrun Boolean Returns true on success or false on failure.
+ */
+function ajax_capture_output() {
+    // Start capturing output in case of broken plugins.
+    return ob_start();
+}
+
+/**
+ * Check captured output for content. If the site has a debug level of
+ * debugdeveloper set, and the content is non-empty, then throw a coding
+ * exception which can be captured by the Y.IO request and displayed to the
+ * user.
+ *
+ * @return Any output that was captured.
+ */
+function ajax_check_captured_output() {
+    global $CFG;
+
+    // Retrieve the output - there should be none.
+    $output = ob_get_contents();
+    ob_end_clean();
+
+    if ($CFG->debugdeveloper && !empty($output)) {
+        // Only throw an error if the site is in debugdeveloper.
+        throw new coding_exception('Unexpected output whilst processing AJAX request. ' .
+                'This could be caused by trailing whitespace. Output received: ' .
+                var_export($output, true));
+    }
+    return $output;
+}
index 9f7d940..459d1c3 100644 (file)
@@ -40,7 +40,7 @@ if ($branchtype !== navigation_node::TYPE_SITE_ADMIN) {
 }
 
 // Start capturing output in case of broken plugins.
-ob_start();
+ajax_capture_output();
 
 $PAGE->set_context(context_system::instance());
 $PAGE->set_url('/lib/ajax/getsiteadminbranch.php', array('type'=>$branchtype));
@@ -51,12 +51,5 @@ $sitenavigation = new settings_navigation_ajax($PAGE);
 $converter = new navigation_json();
 $branch = $sitenavigation->get('root');
 
-$output = ob_get_contents();
-ob_end_clean();
-if ($CFG->debugdeveloper && !empty($output)) {
-    throw new coding_exception('Unexpected output whilst building the administration tree. ' .
-            'This could be caused by trailing whitespace. Output received: ' .
-            var_export($output, true));
-} else {
-    echo $converter->convert($branch);
-}
+ajax_check_captured_output();
+echo $converter->convert($branch);
index 57c86fa..f233078 100644 (file)
@@ -429,51 +429,63 @@ class badge {
         core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
-        // For site level badges, get all active site users who can earn this badge and haven't got it yet.
-        if ($this->type == BADGE_TYPE_SITE) {
-            $sql = 'SELECT DISTINCT u.id, bi.badgeid
+        foreach ($this->criteria as $crit) {
+            // Overall criterion is decided when other criteria are reviewed.
+            if ($crit->criteriatype == BADGE_CRITERIA_TYPE_OVERALL) {
+                continue;
+            }
+
+            list($extrajoin, $extrawhere, $extraparams) = $crit->get_completed_criteria_sql();
+            // For site level badges, get all active site users who can earn this badge and haven't got it yet.
+            if ($this->type == BADGE_TYPE_SITE) {
+                $sql = "SELECT DISTINCT u.id, bi.badgeid
                         FROM {user} u
+                        {$extrajoin}
                         LEFT JOIN {badge_issued} bi
                             ON u.id = bi.userid AND bi.badgeid = :badgeid
-                        WHERE bi.badgeid IS NULL AND u.id != :guestid AND u.deleted = 0';
-            $toearn = $DB->get_fieldset_sql($sql, array('badgeid' => $this->id, 'guestid' => $CFG->siteguest));
-        } else {
-            // For course level badges, get users who can earn this badge in the course.
-            // These are all enrolled users with capability moodle/badges:earnbadge.
-            $earned = $DB->get_fieldset_select('badge_issued', 'userid AS id', 'badgeid = :badgeid', array('badgeid' => $this->id));
-            $users = get_enrolled_users($this->get_context(), 'moodle/badges:earnbadge', 0, 'u.id');
-            $toearn = array_diff(array_keys($users), $earned);
-        }
+                        WHERE bi.badgeid IS NULL AND u.id != :guestid AND u.deleted = 0 " . $extrawhere;
+                $params = array_merge(array('badgeid' => $this->id, 'guestid' => $CFG->siteguest), $extraparams);
+                $toearn = $DB->get_fieldset_sql($sql, $params);
+            } else {
+                // For course level badges, get all users who already earned the badge in this course.
+                // Then find the ones who are enrolled in the course and don't have a badge yet.
+                $earned = $DB->get_fieldset_select('badge_issued', 'userid AS id', 'badgeid = :badgeid', array('badgeid' => $this->id));
+                $wheresql = '';
+                $earnedparams = array();
+                if (!empty($earned)) {
+                    list($earnedsql, $earnedparams) = $DB->get_in_or_equal($earned, SQL_PARAMS_NAMED, 'u', false);
+                    $wheresql = ' WHERE u.id ' . $earnedsql;
+                }
+                list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->get_context(), 'moodle/badges:earnbadge', 0, true);
+                $sql = "SELECT u.id
+                        FROM {user} u
+                        {$extrajoin}
+                        JOIN ({$enrolledsql}) je ON je.id = u.id " . $wheresql . $extrawhere;
+                $params = array_merge($enrolledparams, $earnedparams, $extraparams);
+                $toearn = $DB->get_fieldset_sql($sql, $params);
+            }
 
-        foreach ($toearn as $uid) {
-            $toreview = false;
-            foreach ($this->criteria as $crit) {
-                if ($crit->criteriatype != BADGE_CRITERIA_TYPE_OVERALL) {
-                    if ($crit->review($uid)) {
-                        $crit->mark_complete($uid);
-                        if ($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->method == BADGE_CRITERIA_AGGREGATION_ANY) {
-                            $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid);
-                            $this->issue($uid);
-                            $awards++;
-                            break;
-                        } else {
-                            $toreview = true;
-                            continue;
-                        }
+            foreach ($toearn as $uid) {
+                $reviewoverall = false;
+                if ($crit->review($uid, true)) {
+                    $crit->mark_complete($uid);
+                    if ($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+                        $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid);
+                        $this->issue($uid);
+                        $awards++;
                     } else {
-                        if ($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->method == BADGE_CRITERIA_AGGREGATION_ANY) {
-                            continue;
-                        } else {
-                            break;
-                        }
+                        $reviewoverall = true;
                     }
+                } else {
+                    // Will be reviewed some other time.
+                    $reviewoverall = false;
+                }
+                // Review overall if it is required.
+                if ($reviewoverall && $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($uid)) {
+                    $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid);
+                    $this->issue($uid);
+                    $awards++;
                 }
-            }
-            // Review overall if it is required.
-            if ($toreview && $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($uid)) {
-                $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid);
-                $this->issue($uid);
-                $awards++;
             }
         }
 
@@ -591,13 +603,42 @@ class badge {
     }
 
     /**
-     * Marks the badge as archived.
-     * For reporting and historical purposed we cannot completely delete badges.
-     * We will just change their status to BADGE_STATUS_ARCHIVED.
+     * Fully deletes the badge or marks it as archived.
+     *
+     * @param $archive bool Achive a badge without actual deleting of any data.
      */
-    public function delete() {
-        $this->status = BADGE_STATUS_ARCHIVED;
-        $this->save();
+    public function delete($archive = true) {
+        global $DB;
+
+        if ($archive) {
+            $this->status = BADGE_STATUS_ARCHIVED;
+            $this->save();
+            return;
+        }
+
+        $fs = get_file_storage();
+
+        // Remove all issued badge image files and badge awards.
+        // Cannot bulk remove area files here because they are issued in user context.
+        $awards = $this->get_awards();
+        foreach ($awards as $award) {
+            $usercontext = context_user::instance($award->userid);
+            $fs->delete_area_files($usercontext->id, 'badges', 'userbadge', $this->id);
+        }
+        $DB->delete_records('badge_issued', array('badgeid' => $this->id));
+
+        // Remove all badge criteria.
+        $criteria = $this->get_criteria();
+        foreach ($criteria as $criterion) {
+            $criterion->delete();
+        }
+
+        // Delete badge images.
+        $badgecontext = $this->get_context();
+        $fs->delete_area_files($badgecontext->id, 'badges', 'badgeimage', $this->id);
+
+        // Finally, remove badge itself.
+        $DB->delete_records('badge', array('id' => $this->id));
     }
 }
 
index 20762ae..619dcd6 100644 (file)
@@ -100,18 +100,11 @@ class behat_command {
      * It checks behat dependencies have been installed and runs
      * the behat help command to ensure it works as expected
      *
-     * @param  bool $checkphp Extra check for the PHP version
      * @return int Error code or 0 if all ok
      */
-    public static function behat_setup_problem($checkphp = false) {
+    public static function behat_setup_problem() {
         global $CFG;
 
-        // We don't check the PHP version if $CFG->behat_switchcompletely has been enabled.
-        // Here we are in CLI.
-        if (empty($CFG->behat_switchcompletely) && empty($CFG->behat_wwwroot) && $checkphp && version_compare(PHP_VERSION, '5.4.0', '<')) {
-            behat_error(BEHAT_EXITCODE_REQUIREMENT, 'PHP 5.4 is required. See config-dist.php for possible alternatives');
-        }
-
         $clibehaterrorstr = "Behat dependencies not installed. Ensure you ran the composer installer. " . self::DOCS_URL . "#Installation\n";
 
         // Moodle setting.
@@ -148,7 +141,9 @@ class behat_command {
         }
 
         // Checking behat dataroot existence otherwise echo about admin/tool/behat/cli/init.php.
-        $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
+        if (!empty($CFG->behat_dataroot)) {
+            $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
+        }
         if (empty($CFG->behat_dataroot) || !is_dir($CFG->behat_dataroot) || !is_writable($CFG->behat_dataroot)) {
             self::output_msg(get_string('runclitool', 'tool_behat', 'php admin/tool/behat/cli/init.php'));
             return BEHAT_EXITCODE_CONFIG;
index d559ff4..a967cad 100644 (file)
@@ -183,6 +183,11 @@ class behat_config_manager {
         // We require here when we are sure behat dependencies are available.
         require_once($CFG->dirroot . '/vendor/autoload.php');
 
+        // It is possible that it has no value as we don't require a full behat setup to list the step definitions.
+        if (empty($CFG->behat_wwwroot)) {
+            $CFG->behat_wwwroot = 'http://itwillnotbeused.com';
+        }
+
         $basedir = $CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat';
         $config = array(
             'default' => array(
index c8504fe..ca16c26 100644 (file)
@@ -102,7 +102,10 @@ class behat_util extends testing_util {
 
         // Sets maximum debug level.
         set_config('debug', DEBUG_DEVELOPER);
-        set_config('debugdisplay', true);
+        set_config('debugdisplay', 1);
+
+        // Disable some settings that are not wanted on test sites.
+        set_config('noemailever', 1);
 
         // Keeps the current version of database and dataroot.
         self::store_versions_hash();
@@ -181,7 +184,7 @@ class behat_util extends testing_util {
      * features and steps definitions.
      *
      * Stores a file in dataroot/behat to allow Moodle to switch
-     * to the test environment when using cli-server (or $CFG->behat_switchcompletely)
+     * to the test environment when using cli-server.
      * @throws coding_exception
      * @return void
      */
@@ -193,7 +196,7 @@ class behat_util extends testing_util {
         }
 
         // Checks the behat set up and the PHP version.
-        if ($errorcode = behat_command::behat_setup_problem(true)) {
+        if ($errorcode = behat_command::behat_setup_problem()) {
             exit($errorcode);
         }
 
@@ -227,7 +230,7 @@ class behat_util extends testing_util {
         }
 
         // Checks the behat set up and the PHP version, returning an error code if something went wrong.
-        if ($errorcode = behat_command::behat_setup_problem(true)) {
+        if ($errorcode = behat_command::behat_setup_problem()) {
             return $errorcode;
         }
 
index 73f9b61..fb220d1 100644 (file)
@@ -47,6 +47,11 @@ class behat_form_checkbox extends behat_form_field {
      */
     public function set_value($value) {
 
+        if (!$this->running_javascript()) {
+            $this->field->check();
+            return;
+        }
+
         if (!empty($value) && !$this->field->isChecked()) {
             // Check it if it should be checked and it is not.
             $this->field->click();
index 667125e..74d45cf 100644 (file)
@@ -153,9 +153,9 @@ function behat_clean_init_config() {
 
     $allowed = array_flip(array(
         'wwwroot', 'dataroot', 'dirroot', 'admin', 'directorypermissions', 'filepermissions',
-        'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix', 'dboptions',
-        'proxyhost', 'proxyport', 'proxytype', 'proxyuser', 'proxypassword', 'proxybypass',
-        'theme'
+        'umaskpermissions', 'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix',
+        'dboptions', 'proxyhost', 'proxyport', 'proxytype', 'proxyuser', 'proxypassword',
+        'proxybypass', 'theme'
     ));
 
     // Add extra allowed settings.
@@ -180,56 +180,81 @@ function behat_clean_init_config() {
 function behat_check_config_vars() {
     global $CFG;
 
-    // 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)) {
+    // Verify prefix value.
+    if (empty($CFG->behat_prefix)) {
         behat_error(BEHAT_EXITCODE_CONFIG,
-            'Define $CFG->behat_prefix in config.php with a value different than $CFG->prefix and $CFG->phpunit_prefix');
+            'Define $CFG->behat_prefix in config.php');
     }
-
-    // $CFG->behat_wwwroot must be different than CFG->wwwroot if it is set, it may not be set as
-    // it can take the default value and we should also consider that will have the same value than
-    // $CFG->wwwroot if $CFG->behat_switchcompletely is set.
-    if (!empty($CFG->behat_wwwroot) && $CFG->behat_wwwroot == $CFG->wwwroot && empty($CFG->behat_switchcompletely)) {
+    if (!empty($CFG->prefix) and $CFG->behat_prefix == $CFG->prefix) {
+        behat_error(BEHAT_EXITCODE_CONFIG,
+            '$CFG->behat_prefix in config.php must be different from $CFG->prefix');
+    }
+    if (!empty($CFG->phpunit_prefix) and $CFG->behat_prefix == $CFG->phpunit_prefix) {
         behat_error(BEHAT_EXITCODE_CONFIG,
-            'Define $CFG->behat_wwwroot in config.php with a value different than $CFG->wwwroot');
+            '$CFG->behat_prefix in config.php must be different from $CFG->phpunit_prefix');
     }
 
-    // CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot.
-    $CFG->dataroot = realpath($CFG->dataroot);
-    if (!empty($CFG->behat_dataroot) && is_dir($CFG->behat_dataroot)) {
-        $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
+    // Verify behat wwwroot value.
+    if (empty($CFG->behat_wwwroot)) {
+        behat_error(BEHAT_EXITCODE_CONFIG,
+            'Define $CFG->behat_wwwroot in config.php');
     }
-    if (empty($CFG->behat_dataroot) ||
-           ($CFG->behat_dataroot == $CFG->dataroot) ||
-           (!empty($CFG->phpunit_dataroot) && is_dir($CFG->phpunit_dataroot)
-                && $CFG->behat_dataroot ==