Merge branch 'MDL-43262-master' of git://github.com/andrewnicols/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 13 Jan 2014 04:11:17 +0000 (12:11 +0800)
committerDan Poltawski <dan@moodle.com>
Mon, 13 Jan 2014 04:11:17 +0000 (12:11 +0800)
376 files changed:
admin/index.php
admin/settings/courses.php
admin/settings/development.php
admin/tool/health/index.php
admin/tool/multilangupgrade/lang/en/tool_multilangupgrade.php
admin/tool/qeupgradehelper/extracttestcase_form.php
admin/tool/spamcleaner/lang/en/tool_spamcleaner.php
admin/tool/xmldb/lang/en/tool_xmldb.php
admin/upgradesettings.php
auth/ldap/auth.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/index.php
badges/upgrade.txt
blocks/admin_bookmarks/block_admin_bookmarks.php
blocks/comments/classes/event/comment_created.php [new file with mode: 0644]
blocks/comments/classes/event/comment_deleted.php [new file with mode: 0644]
blocks/comments/tests/events_test.php [new file with mode: 0644]
blocks/private_files/styles.css [new file with mode: 0644]
blocks/recent_activity/renderer.php
blocks/settings/lang/en/block_settings.php
blocks/site_main_menu/block_site_main_menu.php
blocks/social_activities/block_social_activities.php
blog/tests/bloglib_test.php
cache/classes/dummystore.php
cache/classes/factory.php
cache/classes/helper.php
cache/classes/store.php
cache/stores/session/lib.php
cache/stores/static/lib.php
cache/upgrade.txt
calendar/renderer.php
comment/lib.php
composer.json
config-dist.php
course/classes/management/helper.php
course/dndupload.js
course/dnduploadlib.php
course/externallib.php
course/format/renderer.php
course/format/singleactivity/lib.php
course/lib.php
course/loginas.php
course/renderer.php
course/tests/externallib_test.php
course/yui/build/moodle-course-coursebase/moodle-course-coursebase-debug.js [new file with mode: 0644]
course/yui/build/moodle-course-coursebase/moodle-course-coursebase-min.js [new file with mode: 0644]
course/yui/build/moodle-course-coursebase/moodle-course-coursebase.js [new file with mode: 0644]
course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js [new file with mode: 0644]
course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js [new file with mode: 0644]
course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js [new file with mode: 0644]
course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-debug.js [new file with mode: 0644]
course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-min.js [new file with mode: 0644]
course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser.js [new file with mode: 0644]
course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js
course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js
course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js
course/yui/coursebase/coursebase.js [deleted file]
course/yui/dragdrop/dragdrop.js [deleted file]
course/yui/formatchooser/formatchooser.js [deleted file]
course/yui/src/coursebase/build.json [new file with mode: 0644]
course/yui/src/coursebase/js/coursebase.js [new file with mode: 0644]
course/yui/src/coursebase/meta/toolboxes.json [new file with mode: 0644]
course/yui/src/dragdrop/build.json [new file with mode: 0644]
course/yui/src/dragdrop/js/dragdrop.js [new file with mode: 0644]
course/yui/src/dragdrop/js/resource.js [new file with mode: 0644]
course/yui/src/dragdrop/js/section.js [new file with mode: 0644]
course/yui/src/dragdrop/meta/dragdrop.json [new file with mode: 0644]
course/yui/src/formatchooser/build.json [new file with mode: 0644]
course/yui/src/formatchooser/js/formatchooser.js [new file with mode: 0644]
course/yui/src/formatchooser/meta/formatchooser.json [new file with mode: 0644]
course/yui/src/modchooser/js/modchooser.js
enrol/locallib.php
enrol/renderer.php
enrol/yui/rolemanager/rolemanager.js
grade/report/grader/lib.php
grade/tests/edittreelib_test.php
install.php
install/lang/bs/error.php
install/lang/ckb/moodle.php
install/lang/no/langconfig.php
install/lang/zh_tw/error.php
lang/en/admin.php
lang/en/backup.php
lang/en/badges.php
lang/en/block.php
lang/en/completion.php
lang/en/error.php
lang/en/grading.php
lang/en/group.php
lang/en/install.php
lang/en/moodle.php
lang/en/question.php
lang/en/role.php
lang/en/webservice.php
lib/adminlib.php
lib/badgeslib.php
lib/behat/behat_base.php
lib/behat/form_field/behat_form_checkbox.php
lib/behat/form_field/behat_form_select.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/deprecatedlib.php
lib/editor/tinymce/plugins/managefiles/lib.php
lib/editor/tinymce/plugins/managefiles/version.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/dateselector.php
lib/form/datetimeselector.php
lib/form/yui/dateselector/dateselector.js
lib/htaccess
lib/installlib.php
lib/ldaplib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/password_compat/tests/PasswordGetInfoTest.php
lib/password_compat/tests/PasswordHashTest.php
lib/password_compat/tests/PasswordNeedsRehashTest.php
lib/password_compat/tests/PasswordVerifyTest.php
lib/phpunit/readme.md
lib/rsslib.php
lib/setup.php
lib/setuplib.php
lib/tests/admintree_test.php
lib/tests/behat/behat_hooks.php
lib/tests/filelib_test.php
lib/tests/modinfolib_test.php
lib/tests/moodlelib_test.php
lib/tests/weblib_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/weblib.php
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-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/blocks/js/blockregion.js
lib/yui/src/chooserdialogue/js/chooserdialogue.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
message/output/email/lang/en/message_email.php
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/locallib.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/dropdown.js
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.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/tests/locallib_test.php
mod/assign/version.php
mod/assign/yui/build/moodle-mod_assign-history/moodle-mod_assign-history-debug.js [new file with mode: 0644]
mod/assign/yui/build/moodle-mod_assign-history/moodle-mod_assign-history-min.js [new file with mode: 0644]
mod/assign/yui/build/moodle-mod_assign-history/moodle-mod_assign-history.js [new file with mode: 0644]
mod/assign/yui/history/history.js [deleted file]
mod/assign/yui/src/history/build.json [new file with mode: 0644]
mod/assign/yui/src/history/js/history.js [new file with mode: 0644]
mod/assign/yui/src/history/meta/history.json [new file with mode: 0644]
mod/assignment/lang/en/assignment.php
mod/assignment/type/online/assignment.class.php
mod/assignment/type/online/classes/event/assessable_uploaded.php
mod/assignment/type/upload/classes/event/assessable_submitted.php
mod/assignment/type/upload/classes/event/assessable_uploaded.php
mod/chat/chatd.php
mod/chat/classes/event/sessions_viewed.php
mod/chat/lib.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/lang/en/data.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/feedback/item/multichoice/lib.php
mod/feedback/item/multichoicerated/lib.php
mod/feedback/lang/en/feedback.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/forum/rsslib.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/lang/en/glossary.php
mod/glossary/tests/behat/entries_require_approval.feature
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/lti/lang/en/lti.php
mod/quiz/classes/event/attempt_abandoned.php
mod/quiz/classes/event/attempt_becameoverdue.php
mod/quiz/classes/event/attempt_submitted.php
mod/quiz/lang/en/quiz.php
mod/resource/lib.php
mod/scorm/lang/en/scorm.php
mod/scorm/locallib.php
mod/scorm/module.js
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/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/workshop/classes/event/assessable_uploaded.php
portfolio/boxnet/lang/en/portfolio_boxnet.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
theme/afterburner/style/afterburner_blocks.css
theme/base/style/blocks.css
theme/base/style/core.css
theme/bootstrapbase/less/moodle.less
theme/bootstrapbase/less/moodle/blocks.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/grade.less [new file with mode: 0644]
theme/bootstrapbase/less/moodle/user.less
theme/bootstrapbase/renderers/core_renderer.php
theme/bootstrapbase/style/moodle.css
theme/image.php
theme/standard/style/core.css
theme/yui_combo.php
theme/yui_image.php
user/profile/index.php
version.php

index e42687a..8bb73b0 100644 (file)
@@ -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));
 }
@@ -505,6 +501,11 @@ if (!$cache) {
 
 // Check for valid admin user - no guest autologin
 require_login(0, false);
+if (isguestuser()) {
+    // Login as real user!
+    $SESSION->wantsurl = (string)new moodle_url('/admin/index.php');
+    redirect(get_login_url());
+}
 $context = context_system::instance();
 require_capability('moodle/site:config', $context);
 
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 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 f874a46..62a4426 100644 (file)
@@ -23,5 +23,7 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['multilangupgradeinfo'] = 'The multilang filter syntax was changed in 1.8, &lt;lang&gt; tag is not supported any more. <br /><br />Example: &lt;span lang="en" class="multilang">Hello!&lt;/span&gt;&lt;span lang="es" class="multilang">Hola!&lt;/span&gt;<br /><br /><strong>Do you want to upgrade the syntax in all existing texts now?</strong>';
+$string['multilangupgradeinfo'] = '<p>The multilang filter syntax was changed in 1.8 and so the &lt;lang&gt; tag is not supported any more.</p>
+<p>Example: &lt;span lang="en" class="multilang"&gt;Hello!&lt;/span&gt;&lt;span lang="es" class="multilang"&gt;Hola!&lt;/span&gt;</p>
+<p>Do you want to upgrade the syntax in all existing texts now?</p>';
 $string['pluginname'] = 'Multilang upgrade';
index 0e60dc2..ca8619e 100644 (file)
@@ -46,7 +46,7 @@ class tool_qeupgradehelper_extract_options_form extends moodleform {
 
         $qtypes = core_component::get_plugin_list('qtype');
         foreach ($qtypes as $qtype => $notused) {
-            $qtypes[$qtype] = get_string($qtype, 'qtype_' . $qtype);
+            $qtypes[$qtype] = get_string('pluginname', 'qtype_' . $qtype);
         }
 
         $mform->addElement('header', 'h1', 'Either extract a specific question_session');
index df6b4a3..d9f5918 100644 (file)
@@ -27,7 +27,8 @@ $string['pluginname'] = 'Spam cleaner';
 $string['spamauto'] = 'Autodetect common spam patterns';
 $string['spamcannotdelete'] = 'Cannot delete this user';
 $string['spamcannotfinduser'] = 'No users matching your search';
-$string['spamcleanerintro'] = 'This script allows you to search all user profiles for certain strings and then delete those accounts which are obviously created by spammers.  You can search for multiple keywords using commas (eg casino, porn)<br /><br />Moodle docs has more information about <a href="http://docs.moodle.org/en/Reducing_spam_in_Moodle">Reducing spam in Moodle</a>.';
+$string['spamcleanerintro'] = '<p>This script allows you to search all user profiles for certain strings and then delete those accounts which are obviously created by spammers. You can search for multiple keywords using commas (e.g. casino, porn).</p>
+<p>For further information, see the documentation <a href="http://docs.moodle.org/en/Reducing_spam_in_Moodle">Reducing spam in Moodle</a>.</p>';
 $string['spamdeleteall'] = 'Delete all these user accounts';
 $string['spamdeleteallconfirm'] = 'Are you sure you want to delete all these user accounts?  You can not undo this.';
 $string['spamdeleteconfirm'] = 'Are you sure you want to delete this entry?  You can not undo this.';
index f89293f..638941f 100644 (file)
@@ -210,7 +210,11 @@ $string['wronglengthforenum'] = 'Incorrect length for enum field';
 $string['wrongnumberofreffields'] = 'Wrong number of reference fields';
 $string['wrongreservedwords'] = 'Currently used reserved words<br />(note that table names aren\'t important if using $CFG->prefix)';
 $string['wrongoraclesemantics'] = 'Wrong Oracle BYTE semantics found';
-$string['yesmissingindexesfound'] = 'Some missing indexes have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them (don\'t forget to backup your data before doing that).<br /><br />After doing that, it\'s highly recommended to execute this utility again to check that no more missing indexes are found.';
-$string['yeswrongdefaultsfound'] = 'Some inconsistent defaults have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to fix them all (don\'t forget to backup your data before doing that).<br /><br />After doing that, it\'s highly recommended to execute this utility again to check that no more inconsistent defaults are found.';
-$string['yeswrongintsfound'] = 'Some wrong integers have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them (don\'t forget to backup your data before doing that).<br /><br />After doing that, it\'s highly recommended to execute this utility again to check that no more wrong integers are found.';
-$string['yeswrongoraclesemanticsfound'] = 'Some Oracle columns using BYTE semantics have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them (don\'t forget to backup your data before doing that).<br /><br />After doing that, it\'s highly recommended to execute this utility again to check that no more wrong semantics are found.';
+$string['yesmissingindexesfound'] = '<p>Some missing indexes have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all of them. Remember to backup your data first!</p>
+<p>After doing that, it\'s highly recommended to execute this utility again to check that no more missing indexes are found.</p>';
+$string['yeswrongdefaultsfound'] = '<p>Some inconsistent defaults have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to fix them all. Remember to backup your data first!</p>
+<p>After doing that, it\'s highly recommended to execute this utility again to check that no more inconsistent defaults are found.</p>';
+$string['yeswrongintsfound'] = '<p>Some wrong integers have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them. Remember to backup your data first!</p>
+<p>After doing that, it\'s highly recommended to execute this utility again to check that no more wrong integers are found.</p>';
+$string['yeswrongoraclesemanticsfound'] = '<p>Some Oracle columns using BYTE semantics have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all them. Remember to backup your data first!</p>
+<p>After doing that, it\'s highly recommended to execute this utility again to check that no more wrong semantics are found.</p>';
index 5f56e33..6fe6b4b 100644 (file)
@@ -10,6 +10,11 @@ $return = optional_param('return', '', PARAM_ALPHA);
 
 /// no guest autologin
 require_login(0, false);
+if (isguestuser()) {
+    // Login as real user!
+    $SESSION->wantsurl = (string)new moodle_url('/admin/upgradesettings.php', array('return'=>$return));
+    redirect(get_login_url());
+}
 
 admin_externalpage_setup('upgradesettings'); // now hidden page
 $PAGE->set_pagelayout('maintenance'); // do not print any blocks or other rubbish, we want to force saving
index a847112..c64eb77 100644 (file)
@@ -616,7 +616,6 @@ class auth_plugin_ldap extends auth_plugin_base {
                 if ($user->firstaccess == 0) {
                     $user->firstaccess = time();
                 }
-                require_once($CFG->dirroot.'/user/lib.php');
                 user_update_user($user, false);
                 return AUTH_CONFIRM_OK;
             }
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 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 1a48ffd..bab46ee 100644 (file)
@@ -7,6 +7,9 @@ information provided here is intended especially for developers.
   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;
         }
diff --git a/blocks/comments/classes/event/comment_created.php b/blocks/comments/classes/event/comment_created.php
new file mode 100644 (file)
index 0000000..db96870
--- /dev/null
@@ -0,0 +1,37 @@
+<?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/>.
+
+/**
+ * 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
+ */
+
+namespace block_comments\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * 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.
+}
diff --git a/blocks/comments/classes/event/comment_deleted.php b/blocks/comments/classes/event/comment_deleted.php
new file mode 100644 (file)
index 0000000..a347c0b
--- /dev/null
@@ -0,0 +1,37 @@
+<?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/>.
+
+/**
+ * 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
+ */
+
+namespace block_comments\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * 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());
+    }
+}
diff --git a/blocks/private_files/styles.css b/blocks/private_files/styles.css
new file mode 100644 (file)
index 0000000..95fd6bb
--- /dev/null
@@ -0,0 +1,5 @@
+/* Rule so that the table tree view works with word-wrap: break-word. */
+.block_private_files .content table {
+    table-layout: fixed;
+    width: 100%;
+}
\ No newline at end of file
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 fd3f2b3..7442de8 100644 (file)
@@ -27,4 +27,4 @@
 $string['enabledock'] = 'Allow the user to dock this block';
 $string['pluginname'] = 'Administration';
 $string['settings:addinstance'] = 'Add a new administration block';
-$string['settings:myaddinstance'] = 'Add a new settings block to My home';
+$string['settings:myaddinstance'] = 'Add a new administration block to My home';
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 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..d7dad36 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,8 +272,10 @@ 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())) {
+        // We check are_requirements_met although we expect is_ready is going to check as well.
+        if (!$store::are_requirements_met() || !$store->is_ready() || !$store->is_supported_mode($definition->get_mode())) {
             return false;
         }
         // We always create a clone of the original store.
index 90076aa..39ee2e4 100644 (file)
@@ -468,8 +468,10 @@ 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()) {
+        // We check are_requirements_met although we expect is_ready is going to check as well.
+        if (!$instance::are_requirements_met() || !$instance->is_ready()) {
             unset($instance);
             return false;
         }
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 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 1a87d58..cd6cfa9 100644 (file)
@@ -598,79 +598,6 @@ class core_calendar_renderer extends plugin_renderer_base {
         return $output;
     }
 
-    /**
-     * Displays a filter selection table
-     *
-     * @param calendar_information $calendar
-     * @return string
-     * @deprecated since Moodle 2.4 MDL-32309
-     * @see calendar_filter_controls()
-     */
-    protected function filter_selection_table(calendar_information $calendar, moodle_url $returnurl = null) {
-        global $SESSION;
-        debugging('Method core_calendar_renderer::filter_selection_table() is deprecated, please use '.
-                'calendar_filter_controls() instead', DEBUG_DEVELOPER);
-
-        if ($returnurl === null) {
-            $returnurl = $this->page->url;
-        }
-
-        $output  = html_writer::start_tag('div', array('class'=>'filters'));
-        $output .= html_writer::start_tag('table');
-        $output .= html_writer::start_tag('tr');
-
-        // Global events
-        $link = new moodle_url(CALENDAR_URL.'set.php', array('var' => 'showglobal', 'return' => base64_encode($returnurl->out(false)), 'sesskey'=>sesskey()));
-        $strglobalevents = get_string('globalevents', 'calendar');
-        if (calendar_show_event_type(CALENDAR_EVENT_GLOBAL)) {
-            $output .= html_writer::tag('td', '', array('class'=>'calendar_event_global', 'style'=>'width:8px;'));
-            $output .= html_writer::tag('td', html_writer::tag('strong', $strglobalevents).' '.get_string('shown', 'calendar').' ('.html_writer::link($link, get_string('clickhide', 'calendar').'<span class="accesshide">'.$strglobalevents.'</span>').')');
-        } else {
-            $output .= html_writer::tag('td', '', array('style'=>'width:8px;'));
-            $output .= html_writer::tag('td', html_writer::tag('strong', $strglobalevents).' '.get_string('hidden', 'calendar').' ('.html_writer::link($link, get_string('clickshow', 'calendar').'<span class="accesshide">'.$strglobalevents.'</span>').')');
-        }
-
-        // Course events
-        $link = new moodle_url(CALENDAR_URL.'set.php', array('var'=>'showcourses', 'return' => base64_encode($returnurl->out(false)), 'sesskey'=>sesskey()));
-        $strcourseevents = get_string('courseevents', 'calendar');
-        if (calendar_show_event_type(CALENDAR_EVENT_COURSE)) {
-            $output .= html_writer::tag('td', '', array('class'=>'calendar_event_course', 'style'=>'width:8px;'));
-            $output .= html_writer::tag('td', html_writer::tag('strong', $strcourseevents).' '.get_string('shown', 'calendar').' ('.html_writer::link($link, get_string('clickhide', 'calendar').'<span class="accesshide">'.$strcourseevents.'</span>').')');
-        } else {
-            $output .= html_writer::tag('td', '', array('style'=>'width:8px;'));
-            $output .= html_writer::tag('td', html_writer::tag('strong', $strcourseevents).' '.get_string('hidden', 'calendar').' ('.html_writer::link($link, get_string('clickshow', 'calendar').'<span class="accesshide">'.$strcourseevents.'</span>').')');
-        }
-        $output .= html_writer::end_tag('tr');
-
-        if(isloggedin() && !isguestuser()) {
-            $output .= html_writer::start_tag('tr');
-            // Group events
-            $link = new moodle_url(CALENDAR_URL.'set.php', array('var'=>'showgroups', 'return' => base64_encode($returnurl->out(false)), 'sesskey'=>sesskey()));
-            $strgroupevents = get_string('groupevents', 'calendar');
-            if (calendar_show_event_type(CALENDAR_EVENT_GROUP)) {
-                $output .= html_writer::tag('td', '', array('class'=>'calendar_event_group', 'style'=>'width:8px;'));
-                $output .= html_writer::tag('td', html_writer::tag('strong', $strgroupevents).' '.get_string('shown', 'calendar').' ('.html_writer::link($link, get_string('clickhide', 'calendar').'<span class="accesshide">'.$strgroupevents.'</span>').')');
-            } else {
-                $output .= html_writer::tag('td', '', array('style'=>'width:8px;'));
-                $output .= html_writer::tag('td', html_writer::tag('strong', $strgroupevents).' '.get_string('hidden', 'calendar').' ('.html_writer::link($link, get_string('clickshow', 'calendar').'<span class="accesshide">'.$strgroupevents.'</span>').')');
-            }
-            // User events
-            $link = new moodle_url(CALENDAR_URL.'set.php', array('var'=>'showuser', 'return' => base64_encode($returnurl->out(false)), 'sesskey'=>sesskey()));
-            $struserevents = get_string('userevents', 'calendar');
-            if (calendar_show_event_type(CALENDAR_EVENT_USER)) {
-                $output .= html_writer::tag('td', '', array('class'=>'calendar_event_user', 'style'=>'width:8px;'));
-                $output .= html_writer::tag('td', html_writer::tag('strong', $struserevents).' '.get_string('shown', 'calendar').' ('.html_writer::link($link, get_string('clickhide', 'calendar').'<span class="accesshide">'.$struserevents.'</span>').')');
-            } else {
-                $output .= html_writer::tag('td', '', array('style'=>'width:8px;'));
-                $output .= html_writer::tag('td', html_writer::tag('strong', $struserevents).' '.get_string('hidden', 'calendar').' ('.html_writer::link($link, get_string('clickshow', 'calendar').'<span class="accesshide">'.$struserevents.'</span>').')');
-            }
-            $output .= html_writer::end_tag('tr');
-        }
-        $output .= html_writer::end_tag('table');
-        $output .= html_writer::end_tag('div');
-        return $output;
-    }
-
     /**
      * Displays upcoming events
      *
@@ -855,4 +782,4 @@ class core_calendar_renderer extends plugin_renderer_base {
         $html .= html_writer::end_tag('form');
         return $html;
     }
-}
\ No newline at end of file
+}
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 1810b79..4a24f70 100644 (file)
@@ -8,6 +8,6 @@
     "require-dev": {
         "phpunit/phpunit": "3.7.*",
         "phpunit/dbUnit": "1.2.*",
-        "moodlehq/behat-extension": "1.27.0"
+        "moodlehq/behat-extension": "1.27.3"
     }
 }
index 451fa87..964a646 100644 (file)
@@ -562,11 +562,8 @@ $CFG->admin = 'admin';
 //=========================================================================
 // 10. SECRET PASSWORD SALT
 //=========================================================================
-// A single site-wide password salt is no longer required *unless* you are
-// upgrading an older version of Moodle (prior to 2.5), or if you are using
-// a PHP version below 5.3.7. If upgrading, keep any values from your old
-// config.php file. If you are using PHP < 5.3.7 set to a long random string
-// below:
+// A site-wide password salt is no longer used in new installations.
+// If upgrading from 2.6 or older, keep all existing salts in config.php file.
 //
 // $CFG->passwordsaltmain = 'a_very_long_random_string_of_characters#@6&*1';
 //
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 d4a44a2..b719a13 100644 (file)
@@ -822,20 +822,4 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $options->overflowdiv = true;
         return format_text($summarytext, $section->summaryformat, $options);
     }
-
-    /**
-     * Is the section passed in the current section?
-     *
-     * @deprecated since 2.4
-     * @see format_base::is_section_current()
-     *
-     * @param stdClass $course The course entry from DB
-     * @param stdClass $section The course_section entry from the DB
-     * @return bool true if the section is current
-     */
-    protected final function is_section_current($section, $course) {
-        debugging('Function format_section_renderer_base::is_section_current() is deprecated. '.
-                'Use course_get_format($course)->is_section_current($section) instead', DEBUG_DEVELOPER);
-        return course_get_format($course)->is_section_current($section);
-    }
 }
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 d4e49b9..c1e9a14 100644 (file)
@@ -3207,7 +3207,7 @@ function include_course_ajax($course, $usedmodules = array(), $enabledmodules =
     );
 
     // Include course dragdrop
-    if ($course->id != $SITE->id) {
+    if (course_format_uses_sections($course->format)) {
         $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.course.init_section_dragdrop',
             array(array(
                 'courseid' => $course->id,
@@ -3245,8 +3245,8 @@ function include_course_ajax($course, $usedmodules = array(), $enabledmodules =
             'emptydragdropregion'
         ), 'moodle');
 
-    // Include format-specific strings
-    if ($course->id != $SITE->id) {
+    // Include section-specific strings for formats which support sections.
+    if (course_format_uses_sections($course->format)) {
         $PAGE->requires->strings_for_js(array(
                 'showfromothers',
                 'hidefromothers',
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..eb6ab4a 100644 (file)
@@ -194,8 +194,6 @@ class core_course_renderer extends plugin_renderer_base {
         $formcontent .= html_writer::start_tag('div', array('id' => 'typeformdiv'));
         $formcontent .= html_writer::tag('input', '', array('type' => 'hidden', 'id' => 'course',
                 'name' => 'course', 'value' => $course->id));
-        $formcontent .= html_writer::tag('input', '',
-                array('type' => 'hidden', 'class' => 'jump', 'name' => 'jump', 'value' => ''));
         $formcontent .= html_writer::tag('input', '', array('type' => 'hidden', 'name' => 'sesskey',
                 'value' => sesskey()));
         $formcontent .= html_writer::end_tag('div');
@@ -743,7 +741,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 +791,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 +849,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 +921,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 +935,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 +1002,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 +1015,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 +1024,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);
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;
diff --git a/course/yui/build/moodle-course-coursebase/moodle-course-coursebase-debug.js b/course/yui/build/moodle-course-coursebase/moodle-course-coursebase-debug.js
new file mode 100644 (file)
index 0000000..bd6b074
Binary files /dev/null and b/course/yui/build/moodle-course-coursebase/moodle-course-coursebase-debug.js differ
diff --git a/course/yui/build/moodle-course-coursebase/moodle-course-coursebase-min.js b/course/yui/build/moodle-course-coursebase/moodle-course-coursebase-min.js
new file mode 100644 (file)
index 0000000..5f78e62
Binary files /dev/null and b/course/yui/build/moodle-course-coursebase/moodle-course-coursebase-min.js differ
diff --git a/course/yui/build/moodle-course-coursebase/moodle-course-coursebase.js b/course/yui/build/moodle-course-coursebase/moodle-course-coursebase.js
new file mode 100644 (file)
index 0000000..e9bea2d
Binary files /dev/null and b/course/yui/build/moodle-course-coursebase/moodle-course-coursebase.js differ
diff --git a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js
new file mode 100644 (file)
index 0000000..1f1a6ae
Binary files /dev/null and b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js differ
diff --git a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js
new file mode 100644 (file)
index 0000000..90f2913
Binary files /dev/null and b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js differ
diff --git a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js
new file mode 100644 (file)
index 0000000..795c5bb
Binary files /dev/null and b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js differ
diff --git a/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-debug.js b/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-debug.js
new file mode 100644 (file)
index 0000000..4b22e97
Binary files /dev/null and b/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-debug.js differ
diff --git a/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-min.js b/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-min.js
new file mode 100644 (file)
index 0000000..7149638
Binary files /dev/null and b/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser-min.js differ
diff --git a/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser.js b/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser.js
new file mode 100644 (file)
index 0000000..4b22e97
Binary files /dev/null and b/course/yui/build/moodle-course-formatchooser/moodle-course-formatchooser.js differ
index 96126f3..d3bf0c1 100644 (file)
Binary files a/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js and b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-debug.js differ
index 33f576f..f9bddf4 100644 (file)
Binary files a/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js and b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser-min.js differ
index 96126f3..d3bf0c1 100644 (file)
Binary files a/course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js and b/course/yui/build/moodle-course-modchooser/moodle-course-modchooser.js differ
diff --git a/course/yui/coursebase/coursebase.js b/course/yui/coursebase/coursebase.js
deleted file mode 100644 (file)
index b02c362..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-YUI.add('moodle-course-coursebase', function(Y) {
-
-    /**
-     * The coursebase class
-     */
-    var COURSEBASENAME = 'course-coursebase';
-
-    var COURSEBASE = function() {
-        COURSEBASE.superclass.constructor.apply(this, arguments);
-    }
-
-    Y.extend(COURSEBASE, Y.Base, {
-        // Registered Modules
-        registermodules : [],
-
-        /**
-         * Initialize the coursebase module
-         */
-        initializer : function(config) {
-            // We don't actually perform any work here
-        },
-
-        /**
-         * Register a new Javascript Module
-         *
-         * @param object The instantiated module to call functions on
-         */
-        register_module : function(object) {
-            this.registermodules.push(object);
-        },
-
-        /**
-         * Invoke the specified function in all registered modules with the given arguments
-         *
-         * @param functionname The name of the function to call
-         * @param args The argument supplied to the function
-         */
-        invoke_function : function(functionname, args) {
-            for (module in this.registermodules) {
-                if (functionname in this.registermodules[module]) {
-                    this.registermodules[module][functionname](args);
-                }
-            }
-        }
-    },
-    {
-        NAME : COURSEBASENAME,
-        ATTRS : {}
-    }
-    );
-
-    // Ensure that M.course exists and that coursebase is initialised correctly
-    M.course = M.course || {};
-    M.course.coursebase = M.course.coursebase || new COURSEBASE();
-
-    // Abstract functions that needs to be defined per format (course/format/somename/format.js)
-    M.course.format = M.course.format || {}
-
-   /**
-    * Swap section (should be defined in format.js if requred)
-    *
-    * @param {YUI} Y YUI3 instance
-    * @param {string} node1 node to swap to
-    * @param {string} node2 node to swap with
-    * @return {NodeList} section list
-    */
-    M.course.format.swap_sections = M.course.format.swap_sections || function(Y, node1, node2) {
-        return null;
-    }
-
-   /**
-    * Process sections after ajax response (should be defined in format.js)
-    * If some response is expected, we pass it over to format, as it knows better
-    * hot to process it.
-    *
-    * @param {YUI} Y YUI3 instance
-    * @param {NodeList} list of sections
-    * @param {array} response ajax response
-    * @param {string} sectionfrom first affected section
-    * @param {string} sectionto last affected section
-    * @return void
-    */
-    M.course.format.process_sections = M.course.format.process_sections || function(Y, sectionlist, response, sectionfrom, sectionto) {
-        return null;
-    }
-
-   /**
-    * Get sections config for this format, for examples see function definition
-    * in the formats.
-    *
-    * @return {object} section list configuration
-    */
-    M.course.format.get_config = M.course.format.get_config || function() {
-        return {
-            container_node : null, // compulsory
-            container_class : null, // compulsory
-            section_wrapper_node : null, // optional
-            section_wrapper_class : null, // optional
-            section_node : null,  // compulsory
-            section_class : null  // compulsory
-        }
-    }
-
-   /**
-    * Get section list for this format (usually items inside container_node.container_class selector)
-    *
-    * @param {YUI} Y YUI3 instance
-    * @return {string} section selector
-    */
-    M.course.format.get_section_selector = M.course.format.get_section_selector || function(Y) {
-        var config = M.course.format.get_config();
-        if (config.section_node && config.section_class) {
-            return config.section_node + '.' + config.section_class;
-        }
-        console.log('section_node and section_class are not defined in M.course.format.get_config');
-        return null;
-    }
-
-   /**
-    * Get section wraper for this format (only used in case when each
-    * container_node.container_class node is wrapped in some other element).
-    *
-    * @param {YUI} Y YUI3 instance
-    * @return {string} section wrapper selector or M.course.format.get_section_selector
-    * if section_wrapper_node and section_wrapper_class are not defined in the format config.
-    */
-    M.course.format.get_section_wrapper = M.course.format.get_section_wrapper || function(Y) {
-        var config = M.course.format.get_config();
-        if (config.section_wrapper_node && config.section_wrapper_class) {
-            return config.section_wrapper_node + '.' + config.section_wrapper_class;
-        }
-        return M.course.format.get_section_selector(Y);
-    }
-
-   /**
-    * Get the tag of container node
-    *
-    * @return {string} tag of container node.
-    */
-    M.course.format.get_containernode = M.course.format.get_containernode || function() {
-        var config = M.course.format.get_config();
-        if (config.container_node) {
-            return config.container_node;
-        } else {
-            console.log('container_node is not defined in M.course.format.get_config');
-        }
-    }
-
-   /**
-    * Get the class of container node
-    *
-    * @return {string} class of the container node.
-    */
-    M.course.format.get_containerclass = M.course.format.get_containerclass || function() {
-        var config = M.course.format.get_config();
-        if (config.container_class) {
-            return config.container_class;
-        } else {
-            console.log('container_class is not defined in M.course.format.get_config');
-        }
-    }
-
-   /**
-    * Get the tag of draggable node (section wrapper if exists, otherwise section)
-    *
-    * @return {string} tag of the draggable node.
-    */
-    M.course.format.get_sectionwrappernode = M.course.format.get_sectionwrappernode || function() {
-        var config = M.course.format.get_config();
-        if (config.section_wrapper_node) {
-            return config.section_wrapper_node;
-        } else {
-            return config.section_node;
-        }
-    }
-
-   /**
-    * Get the class of draggable node (section wrapper if exists, otherwise section)
-    *
-    * @return {string} class of the draggable node.
-    */
-    M.course.format.get_sectionwrapperclass = M.course.format.get_sectionwrapperclass || function() {
-        var config = M.course.format.get_config();
-        if (config.section_wrapper_class) {
-            return config.section_wrapper_class;
-        } else {
-            return config.section_class;
-        }
-    }
-
-   /**
-    * Get the tag of section node
-    *
-    * @return {string} tag of section node.
-    */
-    M.course.format.get_sectionnode = M.course.format.get_sectionnode || function() {
-        var config = M.course.format.get_config();
-        if (config.section_node) {
-            return config.section_node;
-        } else {
-            console.log('section_node is not defined in M.course.format.get_config');
-        }
-    }
-
-   /**
-    * Get the class of section node
-    *
-    * @return {string} class of the section node.
-    */
-    M.course.format.get_sectionclass = M.course.format.get_sectionclass || function() {
-        var config = M.course.format.get_config();
-        if (config.section_class) {
-            return config.section_class;
-        } else {
-            console.log('section_class is not defined in M.course.format.get_config');
-        }
-
-    }
-
-},
-'@VERSION@', {
-    requires : ['base', 'node']
-}
-);
diff --git a/course/yui/dragdrop/dragdrop.js b/course/yui/dragdrop/dragdrop.js
deleted file mode 100644 (file)
index 7d8a9dd..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-YUI.add('moodle-course-dragdrop', function(Y) {
-
-    var CSS = {
-        ACTIONAREA: '.actions',
-        ACTIVITY : 'activity',
-        ACTIVITYINSTANCE : 'activityinstance',
-        CONTENT : 'content',
-        COURSECONTENT : 'course-content',
-        EDITINGMOVE : 'editing_move',
-        ICONCLASS : 'iconsmall',
-        JUMPMENU : 'jumpmenu',
-        LEFT : 'left',
-        LIGHTBOX : 'lightbox',
-        MOVEDOWN : 'movedown',
-        MOVEUP : 'moveup',
-        PAGECONTENT : 'page-content',
-        RIGHT : 'right',
-        SECTION : 'section',
-        SECTIONADDMENUS : 'section_add_menus',
-        SECTIONHANDLE : 'section-handle',
-        SUMMARY : 'summary',
-        SECTIONDRAGGABLE: 'sectiondraggable'
-    };
-
-    var DRAGSECTION = function() {
-        DRAGSECTION.superclass.constructor.apply(this, arguments);
-    };
-    Y.extend(DRAGSECTION, M.core.dragdrop, {
-        sectionlistselector : null,
-
-        initializer : function() {
-            // Set group for parent class
-            this.groups = [ CSS.SECTIONDRAGGABLE ];
-            this.samenodeclass = M.course.format.get_sectionwrapperclass();
-            this.parentnodeclass = M.course.format.get_containerclass();
-
-            // Check if we are in single section mode
-            if (Y.Node.one('.'+CSS.JUMPMENU)) {
-                return false;
-            }
-            // Initialise sections dragging
-            this.sectionlistselector = M.course.format.get_section_wrapper(Y);
-            if (this.sectionlistselector) {
-                this.sectionlistselector = '.'+CSS.COURSECONTENT+' '+this.sectionlistselector;
-
-                this.setup_for_section(this.sectionlistselector);
-
-                // Make each li element in the lists of sections draggable
-                var del = new Y.DD.Delegate({
-                    container: '.'+CSS.COURSECONTENT,
-                    nodes: '.' + CSS.SECTIONDRAGGABLE,
-                    target: true,
-                    handles: ['.'+CSS.LEFT],
-                    dragConfig: {groups: this.groups}
-                });
-                del.dd.plug(Y.Plugin.DDProxy, {
-                    // Don't move the node at the end of the drag
-                    moveOnEnd: false
-                });
-                del.dd.plug(Y.Plugin.DDConstrained, {
-                    // Keep it inside the .course-content
-                    constrain: '#'+CSS.PAGECONTENT,
-                    stickY: true
-                });
-                del.dd.plug(Y.Plugin.DDWinScroll);
-            }
-        },
-
-         /**
-         * Apply dragdrop features to the specified selector or node that refers to section(s)
-         *
-         * @param baseselector The CSS selector or node to limit scope to
-         * @return void
-         */
-        setup_for_section : function(baseselector) {
-            Y.Node.all(baseselector).each(function(sectionnode) {
-                // Determine the section ID
-                var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
-
-                // We skip the top section as it is not draggable
-                if (sectionid > 0) {
-                    // Remove move icons
-                    var movedown = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEDOWN);
-                    var moveup = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEUP);
-
-                    // Add dragger icon
-                    var title = M.util.get_string('movesection', 'moodle', sectionid);
-                    var cssleft = sectionnode.one('.'+CSS.LEFT);
-
-                    if ((movedown || moveup) && cssleft) {
-                        cssleft.setStyle('cursor', 'move');
-                        cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
-
-                        if (moveup) {
-                            moveup.remove();
-                        }
-                        if (movedown) {
-                            movedown.remove();
-                        }
-
-                        // This section can be moved - add the class to indicate this to Y.DD.
-                        sectionnode.addClass(CSS.SECTIONDRAGGABLE);
-                    }
-                }
-            }, this);
-        },
-
-        /*
-         * Drag-dropping related functions
-         */
-        drag_start : function(e) {
-            // Get our drag object
-            var drag = e.target;
-            // Creat a dummy structure of the outer elemnents for clean styles application
-            var containernode = Y.Node.create('<'+M.course.format.get_containernode()+'></'+M.course.format.get_containernode()+'>');
-            containernode.addClass(M.course.format.get_containerclass());
-            var sectionnode = Y.Node.create('<'+ M.course.format.get_sectionwrappernode()+'></'+ M.course.format.get_sectionwrappernode()+'>');
-            sectionnode.addClass( M.course.format.get_sectionwrapperclass());
-            sectionnode.setStyle('margin', 0);
-            sectionnode.setContent(drag.get('node').get('innerHTML'));
-            containernode.appendChild(sectionnode);
-            drag.get('dragNode').setContent(containernode);
-            drag.get('dragNode').addClass(CSS.COURSECONTENT);
-        },
-
-        drag_dropmiss : function(e) {
-            // Missed the target, but we assume the user intended to drop it
-            // on the last last ghost node location, e.drag and e.drop should be
-            // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
-            this.drop_hit(e);
-        },
-
-        get_section_index: function(node) {
-            var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
-                sectionList = Y.all(sectionlistselector),
-                nodeIndex = sectionList.indexOf(node),
-                zeroIndex = sectionList.indexOf(Y.one('#section-0'));
-
-            return (nodeIndex - zeroIndex);
-        },
-
-        drop_hit : function(e) {
-            var drag = e.drag;
-
-            // Get references to our nodes and their IDs.
-            var dragnode = drag.get('node'),
-                dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
-                loopstart = dragnodeid,
-
-                dropnodeindex = this.get_section_index(dragnode),
-                loopend = dropnodeindex;
-
-            if (dragnodeid === dropnodeindex) {
-                Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
-                return;
-            }
-
-            Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
-
-            if (loopstart > loopend) {
-                // If we're going up, we need to swap the loop order
-                // because loops can't go backwards.
-                loopstart = dropnodeindex;
-                loopend = dragnodeid;
-            }
-
-            // Get the list of nodes.
-            drag.get('dragNode').removeClass(CSS.COURSECONTENT);
-            var sectionlist = Y.Node.all(this.sectionlistselector);
-
-            // Add a lightbox if it's not there.
-            var lightbox = M.util.add_lightbox(Y, dragnode);
-
-            // Handle any variables which we must pass via AJAX.
-            var params = {},
-                pageparams = this.get('config').pageparams,
-                varname;
-
-            for (varname in pageparams) {
-                if (!pageparams.hasOwnProperty(varname)) {
-                    continue;
-                }
-                params[varname] = pageparams[varname];
-            }
-
-            // Prepare request parameters
-            params.sesskey = M.cfg.sesskey;
-            params.courseId = this.get('courseid');
-            params['class'] = 'section';
-            params.field = 'move';
-            params.id = dragnodeid;
-            params.value = dropnodeindex;
-
-            // Perform the AJAX request.
-            var uri = M.cfg.wwwroot + this.get('ajaxurl');
-            Y.io(uri, {
-                method: 'POST',
-                data: params,
-                on: {
-                    start : function() {
-                        lightbox.show();
-                    },
-                    success: function(tid, response) {
-                        // Update section titles, we can't simply swap them as
-                        // they might have custom title
-                        try {
-                            var responsetext = Y.JSON.parse(response.responseText);
-                            if (responsetext.error) {
-                                new M.core.ajaxException(responsetext);
-                            }
-                            M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
-                        } catch (e) {}
-
-                        // Update all of the section IDs - first unset them, then set them
-                        // to avoid duplicates in the DOM.
-                        var index;
-
-                        // Classic bubble sort algorithm is applied to the section
-                        // nodes between original drag node location and the new one.
-                        var swapped = false;
-                        do {
-                            swapped = false;
-                            for (index = loopstart; index <= loopend; index++) {
-                                if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
-                                            Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
-                                    Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) +
-                                            " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)));
-                                    // Swap section id.
-                                    var sectionid = sectionlist.item(index - 1).get('id');
-                                    sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
-                                    sectionlist.item(index).set('id', sectionid);
-
-                                    // See what format needs to swap.
-                                    M.course.format.swap_sections(Y, index - 1, index);
-
-                                    // Update flag.
-                                    swapped = true;
-                                }
-                            }
-                            loopend = loopend - 1;
-                        } while (swapped);
-
-                        window.setTimeout(function() {
-                            lightbox.hide();
-                        }, 250);
-                    },
-
-                    failure: function(tid, response) {
-                        this.ajax_failure(response);
-                        lightbox.hide();
-                    }
-                },
-                context:this
-            });
-        }
-
-    }, {
-        NAME : 'course-dragdrop-section',
-        ATTRS : {
-            courseid : {
-                value : null
-            },
-            ajaxurl : {
-                'value' : 0
-            },
-            config : {
-                'value' : 0
-            }
-        }
-    });
-
-    var DRAGRESOURCE = function() {
-        DRAGRESOURCE.superclass.constructor.apply(this, arguments);
-    };
-    Y.extend(DRAGRESOURCE, M.core.dragdrop, {
-        initializer : function(params) {
-            // Set group for parent class
-            this.groups = ['resource'];
-            this.samenodeclass = CSS.ACTIVITY;
-            this.parentnodeclass = CSS.SECTION;
-            this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
-
-            // Go through all sections
-            var sectionlistselector = M.course.format.get_section_selector(Y);
-            if (sectionlistselector) {
-                sectionlistselector = '.'+CSS.COURSECONTENT+' '+sectionlistselector;
-                this.setup_for_section(sectionlistselector);
-
-                // Initialise drag & drop for all resources/activities
-                var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length+2)+' li.'+CSS.ACTIVITY;
-                var del = new Y.DD.Delegate({
-                    container: '.'+CSS.COURSECONTENT,
-                    nodes: nodeselector,
-                    target: true,
-                    handles: ['.' + CSS.EDITINGMOVE],
-                    dragConfig: {groups: this.groups}
-                });
-                del.dd.plug(Y.Plugin.DDProxy, {
-                    // Don't move the node at the end of the drag
-                    moveOnEnd: false,
-                    cloneNode: true
-                });
-                del.dd.plug(Y.Plugin.DDConstrained, {
-                    // Keep it inside the .course-content
-                    constrain: '#'+CSS.PAGECONTENT
-                });
-                del.dd.plug(Y.Plugin.DDWinScroll);
-
-                M.course.coursebase.register_module(this);
-                M.course.dragres = this;
-            }
-        },
-
-         /**
-         * Apply dragdrop features to the specified selector or node that refers to section(s)
-         *
-         * @param baseselector The CSS selector or node to limit scope to
-         * @return void
-         */
-        setup_for_section : function(baseselector) {
-            Y.Node.all(baseselector).each(function(sectionnode) {
-                var resources = sectionnode.one('.'+CSS.CONTENT+' ul.'+CSS.SECTION);
-                // See if resources ul exists, if not create one
-                if (!resources) {
-                    var resources = Y.Node.create('<ul></ul>');
-                    resources.addClass(CSS.SECTION);
-                    sectionnode.one('.'+CSS.CONTENT+' div.'+CSS.SUMMARY).insert(resources, 'after');
-                }
-                resources.setAttribute('data-draggroups', this.groups.join(' '));
-                // Define empty ul as droptarget, so that item could be moved to empty list
-                var tar = new Y.DD.Drop({
-                    node: resources,
-                    groups: this.groups,
-                    padding: '20 0 20 0'
-                });
-
-                // Initialise each resource/activity in this section
-                this.setup_for_resource('#'+sectionnode.get('id')+' li.'+CSS.ACTIVITY);
-            }, this);
-        },
-        /**
-         * Apply dragdrop features to the specified selector or node that refers to resource(s)
-         *
-         * @param baseselector The CSS selector or node to limit scope to
-         * @return void
-         */
-        setup_for_resource : function(baseselector) {
-            Y.Node.all(baseselector).each(function(resourcesnode) {
-                // Replace move icons
-                var move = resourcesnode.one('a.'+CSS.EDITINGMOVE);
-                if (move) {
-                    move.replace(this.resourcedraghandle.cloneNode(true));
-                }
-            }, this);
-        },
-
-        drag_start : function(e) {
-            // Get our drag object
-            var drag = e.target;
-            drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
-            drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
-        },
-
-        drag_dropmiss : function(e) {
-            // Missed the target, but we assume the user intended to drop it
-            // on the last last ghost node location, e.drag and e.drop should be
-            // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
-            this.drop_hit(e);
-        },
-
-        drop_hit : function(e) {
-            var drag = e.drag;
-            // Get a reference to our drag node
-            var dragnode = drag.get('node');
-            var dropnode = e.drop.get('node');
-
-            // Add spinner if it not there
-            var actionarea = dragnode.one(CSS.ACTIONAREA);
-            var spinner = M.util.add_spinner(Y, actionarea);
-
-            var params = {};
-
-            // Handle any variables which we must pass back through to
-            var pageparams = this.get('config').pageparams;
-            var varname;
-            for (varname in pageparams) {
-                params[varname] = pageparams[varname];
-            }
-
-            // Prepare request parameters
-            params.sesskey = M.cfg.sesskey;
-            params.courseId = this.get('courseid');
-            params['class'] = 'resource';
-            params.field = 'move';
-            params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode));
-            params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
-
-            if (dragnode.next()) {
-                params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next()));
-            }
-
-            // Do AJAX request
-            var uri = M.cfg.wwwroot + this.get('ajaxurl');
-
-            Y.io(uri, {
-                method: 'POST',
-                data: params,
-                on: {
-                    start : function(tid) {
-                        this.lock_drag_handle(drag, CSS.EDITINGMOVE);
-                        spinner.show();
-                    },
-                    success: function(tid, response) {
-                        var responsetext = Y.JSON.parse(response.responseText);
-                        var params = {element: dragnode, visible: responsetext.visible};
-                        M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
-                        this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
-                        window.setTimeout(function(e) {
-                            spinner.hide();
-                        }, 250);
-                    },
-                    failure: function(tid, response) {
-                        this.ajax_failure(response);
-                        this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
-                        spinner.hide();
-                        // TODO: revert nodes location
-                    }
-                },
-                context:this
-            });
-        }
-    }, {
-        NAME : 'course-dragdrop-resource',
-        ATTRS : {
-            courseid : {
-                value : null
-            },
-            ajaxurl : {
-                'value' : 0
-            },
-            config : {
-                'value' : 0
-            }
-        }
-    });
-
-    M.course = M.course || {};
-    M.course.init_resource_dragdrop = function(params) {
-        new DRAGRESOURCE(params);
-    }
-    M.course.init_section_dragdrop = function(params) {
-        new DRAGSECTION(params);
-    }
-}, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'dd-scroll', 'moodle-core-dragdrop', 'moodle-core-notification', 'moodle-course-coursebase', 'moodle-course-util']});
diff --git a/course/yui/formatchooser/formatchooser.js b/course/yui/formatchooser/formatchooser.js
deleted file mode 100644 (file)
index 0597c4b..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-YUI.add('moodle-course-formatchooser', function(Y) {
-    var FORMATCHOOSER = function() {
-        FORMATCHOOSER.superclass.constructor.apply(this, arguments);
-    }
-
-    Y.extend(FORMATCHOOSER, Y.Base, {
-        initializer : function(params) {
-            if (params && params.formid) {
-                var updatebut = Y.one('#'+params.formid+' #id_updatecourseformat');
-                var formatselect = Y.one('#'+params.formid+' #id_format');
-                if (updatebut && formatselect) {
-                    updatebut.setStyle('display', 'none');
-                    formatselect.on('change', function() {
-                        updatebut.simulate('click');
-                    });
-                }
-            }
-        }
-    });
-
-    M.course = M.course || {};
-    M.course.init_formatchooser = function(params) {
-        return new FORMATCHOOSER(params);
-    }
-}, '@VERSION@', {requires:['base', 'node', 'node-event-simulate']});
diff --git a/course/yui/src/coursebase/build.json b/course/yui/src/coursebase/build.json
new file mode 100644 (file)
index 0000000..9ab4663
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name": "moodle-course-coursebase",
+    "builds": {
+        "moodle-course-coursebase": {
+            "jsfiles": [
+                "coursebase.js"
+            ]
+        }
+    }
+}
diff --git a/course/yui/src/coursebase/js/coursebase.js b/course/yui/src/coursebase/js/coursebase.js
new file mode 100644 (file)
index 0000000..4edc914
--- /dev/null
@@ -0,0 +1,225 @@
+/**
+ * The coursebase class to provide shared functionality to Modules within
+ * Moodle.
+ *
+ * @module moodle-course-coursebase
+ */
+var COURSEBASENAME = 'course-coursebase';
+
+var COURSEBASE = function() {
+    COURSEBASE.superclass.constructor.apply(this, arguments);
+};
+
+/**
+ * The coursebase class to provide shared functionality to Modules within
+ * Moodle.
+ *
+ * @class M.course.coursebase
+ * @constructor
+ */
+Y.extend(COURSEBASE, Y.Base, {
+    // Registered Modules
+    registermodules : [],
+
+    /**
+     * Register a new Javascript Module
+     *
+     * @method register_module
+     * @param {Object} The instantiated module to call functions on
+     * @chainable
+     */
+    register_module : function(object) {
+        this.registermodules.push(object);
+
+        return this;
+    },
+
+    /**
+     * Invoke the specified function in all registered modules with the given arguments
+     *
+     * @method invoke_function
+     * @param {String} functionname The name of the function to call
+     * @param {mixed} args The argument supplied to the function
+     * @chainable
+     */
+    invoke_function : function(functionname, args) {
+        var module;
+        for (module in this.registermodules) {
+            if (functionname in this.registermodules[module]) {
+                this.registermodules[module][functionname](args);
+            }
+        }
+
+        return this;
+    }
+}, {
+    NAME : COURSEBASENAME,
+    ATTRS : {}
+});
+
+// Ensure that M.course exists and that coursebase is initialised correctly
+M.course = M.course || {};
+M.course.coursebase = M.course.coursebase || new COURSEBASE();
+
+// Abstract functions that needs to be defined per format (course/format/somename/format.js)
+M.course.format = M.course.format || {};
+
+/**
+ * Swap section (should be defined in format.js if requred)
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {string} node1 node to swap to
+ * @param {string} node2 node to swap with
+ * @return {NodeList} section list
+ */
+M.course.format.swap_sections = M.course.format.swap_sections || function() {
+    return null;
+};
+
+/**
+ * Process sections after ajax response (should be defined in format.js)
+ * If some response is expected, we pass it over to format, as it knows better
+ * hot to process it.
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {NodeList} list of sections
+ * @param {array} response ajax response
+ * @param {string} sectionfrom first affected section
+ * @param {string} sectionto last affected section
+ * @return void
+ */
+M.course.format.process_sections = M.course.format.process_sections || function() {
+    return null;
+};
+
+/**
+* Get sections config for this format, for examples see function definition
+* in the formats.
+*
+* @return {object} section list configuration
+*/
+M.course.format.get_config = M.course.format.get_config || function() {
+    return {
+        container_node : null, // compulsory
+        container_class : null, // compulsory
+        section_wrapper_node : null, // optional
+        section_wrapper_class : null, // optional
+        section_node : null,  // compulsory
+        section_class : null  // compulsory
+    };
+};
+
+/**
+ * Get section list for this format (usually items inside container_node.container_class selector)
+ *
+ * @param {YUI} Y YUI3 instance
+ * @return {string} section selector
+ */
+M.course.format.get_section_selector = M.course.format.get_section_selector || function() {
+    var config = M.course.format.get_config();
+    if (config.section_node && config.section_class) {
+        return config.section_node + '.' + config.section_class;
+    }
+    Y.log('section_node and section_class are not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase');
+    return null;
+};
+
+/**
+ * Get section wraper for this format (only used in case when each
+ * container_node.container_class node is wrapped in some other element).
+ *
+ * @param {YUI} Y YUI3 instance
+ * @return {string} section wrapper selector or M.course.format.get_section_selector
+ * if section_wrapper_node and section_wrapper_class are not defined in the format config.
+ */
+M.course.format.get_section_wrapper = M.course.format.get_section_wrapper || function(Y) {
+    var config = M.course.format.get_config();
+    if (config.section_wrapper_node && config.section_wrapper_class) {
+        return config.section_wrapper_node + '.' + config.section_wrapper_class;
+    }
+    return M.course.format.get_section_selector(Y);
+};
+
+/**
+ * Get the tag of container node
+ *
+ * @return {string} tag of container node.
+ */
+M.course.format.get_containernode = M.course.format.get_containernode || function() {
+    var config = M.course.format.get_config();
+    if (config.container_node) {
+        return config.container_node;
+    } else {
+        Y.log('container_node is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase');
+    }
+};
+
+/**
+ * Get the class of container node
+ *
+ * @return {string} class of the container node.
+ */
+M.course.format.get_containerclass = M.course.format.get_containerclass || function() {
+    var config = M.course.format.get_config();
+    if (config.container_class) {
+        return config.container_class;
+    } else {
+        Y.log('container_class is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase');
+    }
+};
+
+/**
+ * Get the tag of draggable node (section wrapper if exists, otherwise section)
+ *
+ * @return {string} tag of the draggable node.
+ */
+M.course.format.get_sectionwrappernode = M.course.format.get_sectionwrappernode || function() {
+    var config = M.course.format.get_config();
+    if (config.section_wrapper_node) {
+        return config.section_wrapper_node;
+    } else {
+        return config.section_node;
+    }
+};
+
+/**
+ * Get the class of draggable node (section wrapper if exists, otherwise section)
+ *
+ * @return {string} class of the draggable node.
+ */
+M.course.format.get_sectionwrapperclass = M.course.format.get_sectionwrapperclass || function() {
+    var config = M.course.format.get_config();
+    if (config.section_wrapper_class) {
+        return config.section_wrapper_class;
+    } else {
+        return config.section_class;
+    }
+};
+
+/**
+ * Get the tag of section node
+ *
+ * @return {string} tag of section node.
+ */
+M.course.format.get_sectionnode = M.course.format.get_sectionnode || function() {
+    var config = M.course.format.get_config();
+    if (config.section_node) {
+        return config.section_node;
+    } else {
+        Y.log('section_node is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase');
+    }
+};
+
+/**
+ * Get the class of section node
+ *
+ * @return {string} class of the section node.
+ */
+M.course.format.get_sectionclass = M.course.format.get_sectionclass || function() {
+    var config = M.course.format.get_config();
+    if (config.section_class) {
+        return config.section_class;
+    } else {
+        Y.log('section_class is not defined in M.course.format.get_config', 'warn', 'moodle-course-coursebase');
+    }
+};
diff --git a/course/yui/src/coursebase/meta/toolboxes.json b/course/yui/src/coursebase/meta/toolboxes.json
new file mode 100644 (file)
index 0000000..41816cb
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "moodle-course-coursebase": {
+        "requires": [
+            "base",
+            "node"
+        ]
+    }
+}
diff --git a/course/yui/src/dragdrop/build.json b/course/yui/src/dragdrop/build.json
new file mode 100644 (file)
index 0000000..fb8e3ce
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    "name": "moodle-course-dragdrop",
+    "builds": {
+        "moodle-course-dragdrop": {
+            "jsfiles": [
+                "dragdrop.js",
+                "section.js",
+                "resource.js"
+            ]
+        }
+    }
+}
diff --git a/course/yui/src/dragdrop/js/dragdrop.js b/course/yui/src/dragdrop/js/dragdrop.js
new file mode 100644 (file)
index 0000000..00b30d4
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Drag and Drop for course sections and course modules.
+ *
+ * @module moodle-course-dragdrop
+ */
+
+var CSS = {
+    ACTIONAREA: '.actions',
+    ACTIVITY: 'activity',
+    ACTIVITYINSTANCE: 'activityinstance',
+    CONTENT: 'content',
+    COURSECONTENT: 'course-content',
+    EDITINGMOVE: 'editing_move',
+    ICONCLASS: 'iconsmall',
+    JUMPMENU: 'jumpmenu',
+    LEFT: 'left',
+    LIGHTBOX: 'lightbox',
+    MOVEDOWN: 'movedown',
+    MOVEUP: 'moveup',
+    PAGECONTENT: 'page-content',
+    RIGHT: 'right',
+    SECTION: 'section',
+    SECTIONADDMENUS: 'section_add_menus',
+    SECTIONHANDLE: 'section-handle',
+    SUMMARY: 'summary',
+    SECTIONDRAGGABLE: 'sectiondraggable'
+};
+
+M.course = M.course || {};
diff --git a/course/yui/src/dragdrop/js/resource.js b/course/yui/src/dragdrop/js/resource.js
new file mode 100644 (file)
index 0000000..6523e8b
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * Resource drag and drop.
+ *
+ * @class M.course.dragdrop.resource
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGRESOURCE = function() {
+    DRAGRESOURCE.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGRESOURCE, M.core.dragdrop, {
+    initializer: function() {
+        // Set group for parent class
+        this.groups = ['resource'];
+        this.samenodeclass = CSS.ACTIVITY;
+        this.parentnodeclass = CSS.SECTION;
+        this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
+
+        // Go through all sections
+        var sectionlistselector = M.course.format.get_section_selector(Y);
+        if (sectionlistselector) {
+            sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector;
+            this.setup_for_section(sectionlistselector);
+
+            // Initialise drag & drop for all resources/activities
+            var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY;
+            var del = new Y.DD.Delegate({
+                container: '.' + CSS.COURSECONTENT,
+                nodes: nodeselector,
+                target: true,
+                handles: ['.' + CSS.EDITINGMOVE],
+                dragConfig: {groups: this.groups}
+            });
+            del.dd.plug(Y.Plugin.DDProxy, {
+                // Don't move the node at the end of the drag
+                moveOnEnd: false,
+                cloneNode: true
+            });
+            del.dd.plug(Y.Plugin.DDConstrained, {
+                // Keep it inside the .course-content
+                constrain: '#' + CSS.PAGECONTENT
+            });
+            del.dd.plug(Y.Plugin.DDWinScroll);
+
+            M.course.coursebase.register_module(this);
+            M.course.dragres = this;
+        }
+    },
+
+    /**
+     * Apply dragdrop features to the specified selector or node that refers to section(s)
+     *
+     * @method setup_for_section
+     * @param {String} baseselector The CSS selector or node to limit scope to
+     */
+    setup_for_section: function(baseselector) {
+        Y.Node.all(baseselector).each(function(sectionnode) {
+            var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION);
+            // See if resources ul exists, if not create one
+            if (!resources) {
+                resources = Y.Node.create('<ul></ul>');
+                resources.addClass(CSS.SECTION);
+                sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after');
+            }
+            resources.setAttribute('data-draggroups', this.groups.join(' '));
+            // Define empty ul as droptarget, so that item could be moved to empty list
+            new Y.DD.Drop({
+                node: resources,
+                groups: this.groups,
+                padding: '20 0 20 0'
+            });
+
+            // Initialise each resource/activity in this section
+            this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY);
+        }, this);
+    },
+
+    /**
+     * Apply dragdrop features to the specified selector or node that refers to resource(s)
+     *
+     * @method setup_for_resource
+     * @param {String} baseselector The CSS selector or node to limit scope to
+     */
+    setup_for_resource: function(baseselector) {
+        Y.Node.all(baseselector).each(function(resourcesnode) {
+            // Replace move icons
+            var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
+            if (move) {
+                move.replace(this.resourcedraghandle.cloneNode(true));
+            }
+        }, this);
+    },
+
+    drag_start: function(e) {
+        // Get our drag object
+        var drag = e.target;
+        drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
+        drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
+    },
+
+    drag_dropmiss: function(e) {
+        // Missed the target, but we assume the user intended to drop it
+        // on the last last ghost node location, e.drag and e.drop should be
+        // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+        this.drop_hit(e);
+    },
+
+    drop_hit: function(e) {
+        var drag = e.drag;
+        // Get a reference to our drag node
+        var dragnode = drag.get('node');
+        var dropnode = e.drop.get('node');
+
+        // Add spinner if it not there
+        var actionarea = dragnode.one(CSS.ACTIONAREA);
+        var spinner = M.util.add_spinner(Y, actionarea);
+
+        var params = {};
+
+        // Handle any variables which we must pass back through to
+        var pageparams = this.get('config').pageparams;
+        var varname;
+        for (varname in pageparams) {
+            params[varname] = pageparams[varname];
+        }
+
+        // Prepare request parameters
+        params.sesskey = M.cfg.sesskey;
+        params.courseId = this.get('courseid');
+        params['class'] = 'resource';
+        params.field = 'move';
+        params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode));
+        params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
+
+        if (dragnode.next()) {
+            params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next()));
+        }
+
+        // Do AJAX request
+        var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+        Y.io(uri, {
+            method: 'POST',
+            data: params,
+            on: {
+                start: function() {
+                    this.lock_drag_handle(drag, CSS.EDITINGMOVE);
+                    spinner.show();
+                },
+                success: function(tid, response) {
+                    var responsetext = Y.JSON.parse(response.responseText);
+                    var params = {element: dragnode, visible: responsetext.visible};
+                    M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
+                    this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
+                    window.setTimeout(function() {
+                        spinner.hide();
+                    }, 250);
+                },
+                failure: function(tid, response) {
+                    this.ajax_failure(response);
+                    this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
+                    spinner.hide();
+                    // TODO: revert nodes location
+                }
+            },
+            context:this
+        });
+    }
+}, {
+    NAME: 'course-dragdrop-resource',
+    ATTRS: {
+        courseid: {
+            value: null
+        },
+        ajaxurl: {
+            value: 0
+        },
+        config: {
+            value: 0
+        }
+    }
+});
+
+M.course = M.course || {};
+M.course.init_resource_dragdrop = function(params) {
+    new DRAGRESOURCE(params);
+};
diff --git a/course/yui/src/dragdrop/js/section.js b/course/yui/src/dragdrop/js/section.js
new file mode 100644 (file)
index 0000000..d138030
--- /dev/null
@@ -0,0 +1,258 @@
+/**
+ * Section drag and drop.
+ *
+ * @class M.course.dragdrop.section
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGSECTION = function() {
+    DRAGSECTION.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGSECTION, M.core.dragdrop, {
+    sectionlistselector: null,
+
+    initializer: function() {
+        // Set group for parent class
+        this.groups = [ CSS.SECTIONDRAGGABLE ];
+        this.samenodeclass = M.course.format.get_sectionwrapperclass();
+        this.parentnodeclass = M.course.format.get_containerclass();
+
+        // Check if we are in single section mode
+        if (Y.Node.one('.' + CSS.JUMPMENU)) {
+            return false;
+        }
+        // Initialise sections dragging
+        this.sectionlistselector = M.course.format.get_section_wrapper(Y);
+        if (this.sectionlistselector) {
+            this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector;
+
+            this.setup_for_section(this.sectionlistselector);
+
+            // Make each li element in the lists of sections draggable
+            var del = new Y.DD.Delegate({
+                container: '.' + CSS.COURSECONTENT,
+                nodes: '.' + CSS.SECTIONDRAGGABLE,
+                target: true,
+                handles: ['.' + CSS.LEFT],
+                dragConfig: {groups: this.groups}
+            });
+            del.dd.plug(Y.Plugin.DDProxy, {
+                // Don't move the node at the end of the drag
+                moveOnEnd: false
+            });
+            del.dd.plug(Y.Plugin.DDConstrained, {
+                // Keep it inside the .course-content
+                constrain: '#' + CSS.PAGECONTENT,
+                stickY: true
+            });
+            del.dd.plug(Y.Plugin.DDWinScroll);
+        }
+    },
+
+     /**
+     * Apply dragdrop features to the specified selector or node that refers to section(s)
+     *
+     * @method setup_for_section
+     * @param {String} baseselector The CSS selector or node to limit scope to
+     */
+    setup_for_section: function(baseselector) {
+        Y.Node.all(baseselector).each(function(sectionnode) {
+            // Determine the section ID
+            var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
+
+            // We skip the top section as it is not draggable
+            if (sectionid > 0) {
+                // Remove move icons
+                var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN);
+                var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP);
+
+                // Add dragger icon
+                var title = M.util.get_string('movesection', 'moodle', sectionid);
+                var cssleft = sectionnode.one('.' + CSS.LEFT);
+
+                if ((movedown || moveup) && cssleft) {
+                    cssleft.setStyle('cursor', 'move');
+                    cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
+
+                    if (moveup) {
+                        moveup.remove();
+                    }
+                    if (movedown) {
+                        movedown.remove();
+                    }
+
+                    // This section can be moved - add the class to indicate this to Y.DD.
+                    sectionnode.addClass(CSS.SECTIONDRAGGABLE);
+                }
+            }
+        }, this);
+    },
+
+    /*
+     * Drag-dropping related functions
+     */
+    drag_start: function(e) {
+        // Get our drag object
+        var drag = e.target;
+        // Creat a dummy structure of the outer elemnents for clean styles application
+        var containernode = Y.Node.create('<' + M.course.format.get_containernode() + '></' + M.course.format.get_containernode() + '>');
+        containernode.addClass(M.course.format.get_containerclass());
+        var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + '></' + M.course.format.get_sectionwrappernode() + '>');
+        sectionnode.addClass( M.course.format.get_sectionwrapperclass());
+        sectionnode.setStyle('margin', 0);
+        sectionnode.setContent(drag.get('node').get('innerHTML'));
+        containernode.appendChild(sectionnode);
+        drag.get('dragNode').setContent(containernode);
+        drag.get('dragNode').addClass(CSS.COURSECONTENT);
+    },
+
+    drag_dropmiss: function(e) {
+        // Missed the target, but we assume the user intended to drop it
+        // on the last last ghost node location, e.drag and e.drop should be
+        // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+        this.drop_hit(e);
+    },
+
+    get_section_index: function(node) {
+        var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
+            sectionList = Y.all(sectionlistselector),
+            nodeIndex = sectionList.indexOf(node),
+            zeroIndex = sectionList.indexOf(Y.one('#section-0'));
+
+        return (nodeIndex - zeroIndex);
+    },
+
+    drop_hit: function(e) {
+        var drag = e.drag;
+
+        // Get references to our nodes and their IDs.
+        var dragnode = drag.get('node'),
+            dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
+            loopstart = dragnodeid,
+
+            dropnodeindex = this.get_section_index(dragnode),
+            loopend = dropnodeindex;
+
+        if (dragnodeid === dropnodeindex) {
+            Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+            return;
+        }
+
+        Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+
+        if (loopstart > loopend) {
+            // If we're going up, we need to swap the loop order
+            // because loops can't go backwards.
+            loopstart = dropnodeindex;
+            loopend = dragnodeid;
+        }
+
+        // Get the list of nodes.
+        drag.get('dragNode').removeClass(CSS.COURSECONTENT);
+        var sectionlist = Y.Node.all(this.sectionlistselector);
+
+        // Add a lightbox if it's not there.
+        var lightbox = M.util.add_lightbox(Y, dragnode);
+
+        // Handle any variables which we must pass via AJAX.
+        var params = {},
+            pageparams = this.get('config').pageparams,
+            varname;
+
+        for (varname in pageparams) {
+            if (!pageparams.hasOwnProperty(varname)) {
+                continue;
+            }
+            params[varname] = pageparams[varname];
+        }
+
+        // Prepare request parameters
+        params.sesskey = M.cfg.sesskey;
+        params.courseId = this.get('courseid');
+        params['class'] = 'section';
+        params.field = 'move';
+        params.id = dragnodeid;
+        params.value = dropnodeindex;
+
+        // Perform the AJAX request.
+        var uri = M.cfg.wwwroot + this.get('ajaxurl');
+        Y.io(uri, {
+            method: 'POST',
+            data: params,
+            on: {
+                start: function() {
+                    lightbox.show();
+                },
+                success: function(tid, response) {
+                    // Update section titles, we can't simply swap them as
+                    // they might have custom title
+                    try {
+                        var responsetext = Y.JSON.parse(response.responseText);
+                        if (responsetext.error) {
+                            new M.core.ajaxException(responsetext);
+                        }
+                        M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
+                    } catch (e) {}
+
+                    // Update all of the section IDs - first unset them, then set them
+                    // to avoid duplicates in the DOM.
+                    var index;
+
+                    // Classic bubble sort algorithm is applied to the section
+                    // nodes between original drag node location and the new one.
+                    var swapped = false;
+                    do {
+                        swapped = false;
+                        for (index = loopstart; index <= loopend; index++) {
+                            if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
+                                        Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
+                                Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) +
+                                        " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)));
+                                // Swap section id.
+                                var sectionid = sectionlist.item(index - 1).get('id');
+                                sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
+                                sectionlist.item(index).set('id', sectionid);
+
+                                // See what format needs to swap.
+                                M.course.format.swap_sections(Y, index - 1, index);
+
+                                // Update flag.
+                                swapped = true;
+                            }
+                        }
+                        loopend = loopend - 1;
+                    } while (swapped);
+
+                    window.setTimeout(function() {
+                        lightbox.hide();
+                    }, 250);
+                },
+
+                failure: function(tid, response) {
+                    this.ajax_failure(response);
+                    lightbox.hide();
+                }
+            },
+            context:this
+        });
+    }
+
+}, {
+    NAME: 'course-dragdrop-section',
+    ATTRS: {
+        courseid: {
+            value: null
+        },
+        ajaxurl: {
+            value: 0
+        },
+        config: {
+            value: 0
+        }
+    }
+});
+
+M.course = M.course || {};
+M.course.init_section_dragdrop = function(params) {
+    new DRAGSECTION(params);
+};
diff --git a/course/yui/src/dragdrop/meta/dragdrop.json b/course/yui/src/dragdrop/meta/dragdrop.json
new file mode 100644 (file)
index 0000000..5757d28
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "moodle-course-dragdrop": {
+        "requires": [
+            "base",
+            "node",
+            "io",
+            "dom",
+            "dd",
+            "dd-scroll",
+            "moodle-core-dragdrop",
+            "moodle-core-notification",
+            "moodle-course-coursebase",
+            "moodle-course-util"
+        ]
+    }
+}
diff --git a/course/yui/src/formatchooser/build.json b/course/yui/src/formatchooser/build.json
new file mode 100644 (file)
index 0000000..ddc0d41
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name": "moodle-course-formatchooser",
+    "builds": {
+        "moodle-course-formatchooser": {
+            "jsfiles": [
+                "formatchooser.js"
+            ]
+        }
+    }
+}
diff --git a/course/yui/src/formatchooser/js/formatchooser.js b/course/yui/src/formatchooser/js/formatchooser.js
new file mode 100644 (file)
index 0000000..8e380c1
--- /dev/null
@@ -0,0 +1,23 @@
+var FORMATCHOOSER = function() {
+    FORMATCHOOSER.superclass.constructor.apply(this, arguments);
+};
+
+Y.extend(FORMATCHOOSER, Y.Base, {
+    initializer : function(params) {
+        if (params && params.formid) {
+            var updatebut = Y.one('#'+params.formid+' #id_updatecourseformat');
+            var formatselect = Y.one('#'+params.formid+' #id_format');
+            if (updatebut && formatselect) {
+                updatebut.setStyle('display', 'none');
+                formatselect.on('change', function() {
+                    updatebut.simulate('click');
+                });
+            }
+        }
+    }
+});
+
+M.course = M.course || {};
+M.course.init_formatchooser = function(params) {
+    return new FORMATCHOOSER(params);
+};
diff --git a/course/yui/src/formatchooser/meta/formatchooser.json b/course/yui/src/formatchooser/meta/formatchooser.json
new file mode 100644 (file)
index 0000000..9b21178
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "moodle-course-formatchooser": {
+        "requires": [
+            "base",
+            "node",
+            "node-event-simulate"
+        ]
+    }
+}
index 0bd9825..b1033e3 100644 (file)
@@ -29,9 +29,6 @@ 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');
@@ -155,8 +152,11 @@ Y.extend(MODCHOOSER, M.core.chooserdialogue, {
         e.preventDefault();
     },
     option_selected : function(thisoption) {
-        // Add the sectionid to the URL
-        this.jumplink.set('value', thisoption.get('value') + '&section=' + this.sectionid);
+        // Add the sectionid to the URL.
+        this.hiddenRadioValue.setAttrs({
+            name: 'jump',
+            value: thisoption.get('value') + '&section=' + this.sectionid
+        });
     }
 },
 {
index 1746697..03e2f6c 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);
@@ -1260,15 +1260,18 @@ class enrol_user_button extends single_button {
      * @param string|array $modules One or more modules to require
      * @param string $function The JS function to call
      * @param array $arguments An array of arguments to pass to the function
-     * @param string $galleryversion The YUI gallery version of any modules required
+     * @param string $galleryversion Deprecated: The gallery version to use
      * @param bool $ondomready If true the call is postponed until the DOM is finished loading
      */
-    public function require_yui_module($modules, $function, array $arguments = null, $galleryversion = '2010.04.08-12-35', $ondomready = false) {
+    public function require_yui_module($modules, $function, array $arguments = null, $galleryversion = null, $ondomready = false) {
+        if ($galleryversion != null) {
+            debugging('The galleryversion parameter to yui_module has been deprecated since Moodle 2.3.', DEBUG_DEVELOPER);
+        }
+
         $js = new stdClass;
         $js->modules = (array)$modules;
         $js->function = $function;
         $js->arguments = $arguments;
-        $js->galleryversion = $galleryversion;
         $js->ondomready = $ondomready;
         $this->jsyuimodules[] = $js;
     }
@@ -1312,7 +1315,7 @@ class enrol_user_button extends single_button {
      */
     public function initialise_js(moodle_page $page) {
         foreach ($this->jsyuimodules as $js) {
-            $page->requires->yui_module($js->modules, $js->function, $js->arguments, $js->galleryversion, $js->ondomready);
+            $page->requires->yui_module($js->modules, $js->function, $js->arguments, null, $js->ondomready);
         }
         foreach ($this->jsinitcalls as $js) {
             $page->requires->js_init_call($js->function, $js->extraarguments, $js->ondomready, $js->module);
index 2a2ee1f..e957b8a 100644 (file)
@@ -184,10 +184,11 @@ class core_enrol_renderer extends plugin_renderer_base {
      * @return string
      */
     public function user_roles_and_actions($userid, $roles, $assignableroles, $canassign, $pageurl) {
-        $iconenroladd    = $this->output->pix_url('t/enroladd');
         $iconenrolremove = $this->output->pix_url('t/delete');
 
-        // get list of roles
+
+
+        // Get list of roles.
         $rolesoutput = '';
         foreach ($roles as $roleid=>$role) {
             if ($canassign and (is_siteadmin() or isset($assignableroles[$roleid])) and !$role['unchangeable']) {
@@ -210,9 +211,10 @@ class core_enrol_renderer extends plugin_renderer_base {
                 }
             }
             if (!$hasallroles) {
-                $url = new moodle_url($pageurl, array('action'=>'assign', 'user'=>$userid));
-                $icon = html_writer::empty_tag('img', array('alt'=>get_string('assignroles', 'role'), 'src'=>$iconenroladd));
-                $output = html_writer::tag('div', html_writer::link($url, $icon, array('class'=>'assignrolelink', 'title'=>get_string('assignroles', 'role'))), array('class'=>'addrole'));
+                $url = new moodle_url($pageurl, array('action' => 'assign', 'user' => $userid));
+                $roleicon = $this->output->pix_icon('i/assignroles', get_string('assignroles', 'role'));
+                $link = html_writer::link($url, $roleicon, array('class' => 'assignrolelink'));
+                $output = html_writer::tag('div', $link, array('class'=>'addrole'));
             }
         }
         $output .= html_writer::tag('div', $rolesoutput, array('class'=>'roles'));
@@ -230,9 +232,9 @@ class core_enrol_renderer extends plugin_renderer_base {
      * @return string
      */
     public function user_groups_and_actions($userid, $groups, $allgroups, $canmanagegroups, $pageurl) {
-        $iconenroladd    = $this->output->pix_url('t/enroladd');
         $iconenrolremove = $this->output->pix_url('t/delete');
-        $straddgroup = get_string('addgroup', 'group');
+
+        $groupicon = $this->output->pix_icon('i/group', get_string('addgroup', 'group'));
 
         $groupoutput = '';
         foreach($groups as $groupid=>$name) {
@@ -244,13 +246,13 @@ class core_enrol_renderer extends plugin_renderer_base {
                 $groupoutput .= html_writer::tag('div', $name, array('class'=>'group', 'rel'=>$groupid));
             }
         }
-        $groupoutput = html_writer::tag('div', $groupoutput, array('class'=>'groups'));
+        $output = '';
         if ($canmanagegroups && (count($groups) < count($allgroups))) {
-            $icon = html_writer::empty_tag('img', array('alt'=>$straddgroup, 'src'=>$iconenroladd));
             $url = new moodle_url($pageurl, array('action'=>'addmember', 'user'=>$userid));
-            $groupoutput .= html_writer::tag('div', html_writer::link($url, $icon), array('class'=>'addgroup'));
+            $output .= html_writer::tag('div', html_writer::link($url, $groupicon), array('class'=>'addgroup'));
         }
-        return $groupoutput;
+        $output = $output.html_writer::tag('div', $groupoutput, array('class'=>'groups'));
+        return $output;
     }
 
     /**
index cbde453..7e35cbd 100644 (file)
@@ -284,15 +284,6 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             if (allroles) {
                 this.get(CONTAINER).addClass('hasAllRoles');
             } else {
-                if (!link) {
-                    var m = this.get(MANIPULATOR);
-                    link = Y.Node.create('<div class="addrole"></div>').append(
-                        Y.Node.create('<img alt="" />').setAttribute('src', M.util.image_url('t/enroladd', 'moodle'))
-                    );
-                    link.on('click', m.addRole, m, this);
-                    this.get(CONTAINER).one('.col_role').insert(link, 0);
-                    this.set(ASSIGNROLELINK, link);
-                }
                 this.get(CONTAINER).removeClass('hasAllRoles');
             }
         },
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 c7f4fc5..75c7ccf 100644 (file)
@@ -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'])) {
index b7db900..b500d6d 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['cannotcreatedboninstall'] = '<p>Nije moguće kreirati bazu podataka.</p> <p>Određena baza podataka ne postoji i dati korisnik nema dozvolu da kreira bazu podataka.</p> <p>Site administrator bi trebao verificirati postavke baze podataka.</p>';
 $string['cannotcreatelangdir'] = 'Nije moguće kreirati direktorij jezika';
 $string['cannotcreatetempdir'] = 'Nije moguće kreirati privremeni direktorij';
-$string['cannotdownloadcomponents'] = 'Nije moguće preuzeti komponente.';
-$string['cannotdownloadzipfile'] = 'Nije moguće preuzeti arhivu';
+$string['cannotdownloadcomponents'] = 'Nije moguće preuzeti komponente';
+$string['cannotdownloadzipfile'] = 'Nije moguće preuzeti ZIP arhivu';
 $string['cannotfindcomponent'] = 'Nije moguće pronaći komponentu.';
 $string['cannotsavemd5file'] = 'Nije moguće sačuvati md5 datoteku.';
 $string['cannotsavezipfile'] = 'Nije moguće sačuvati ZIP arhivu.';
 $string['cannotunzipfile'] = 'Nije moguće raspakovati ZIP datoteku.';
 $string['componentisuptodate'] = 'Komponenta je dostupna u svojoj najnovijoj verziji';
+$string['dmlexceptiononinstall'] = '<p>Nastala je greška u bazi podataka: [{$a->errorcode}].<br />{$a->debuginfo}</p>';
 $string['downloadedfilecheckfailed'] = 'Nije uspjela provjera preuzete datoteke';
-$string['invalidmd5'] = 'Neispravna md5 datoteka';
+$string['invalidmd5'] = 'Provjerena varijabla (md5 datoteka) je pogrešna - pokušajte ponovo';
 $string['missingrequiredfield'] = 'Nedostaje neko obavezno polje';
 $string['remotedownloaderror'] = 'Preuzimanje komponente na Vaš server nije uspjelo. Provjerite podešavanja proksi servera. PHP cURL ekstenzija se preporučuje.<br /><br />Morate da preuzmete <a href="{$a->url}">{$a->url}</a> datoteku ručno, kopirate je u direktorij "{$a->dest}" na svom sereveru i tamo je raspakovati.';
 $string['wrongdestpath'] = 'Pogrešna odredišna putanja';
index 7507e8d..b0a7812 100644 (file)
@@ -31,4 +31,5 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['language'] = 'زمان';
+$string['next'] = 'دواتر';
 $string['reload'] = 'بارکردنەوە';
index 4312d19..c3103ca 100644 (file)
@@ -30,5 +30,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['parentlanguage'] = 'Arved språk';
 $string['thisdirection'] = 'ltr';
 $string['thislanguage'] = 'Norsk - bokmål';
index 56d14f3..d98781c 100644 (file)
@@ -42,6 +42,7 @@ $string['cannotsavemd5file'] = '無法儲存 md5 檔案。';
 $string['cannotsavezipfile'] = '無法儲存 ZIP 檔案。';
 $string['cannotunzipfile'] = '無法解壓縮檔案。';
 $string['componentisuptodate'] = '元件已經是最新的了。';
+$string['dmlexceptiononinstall'] = '<p>資料庫有誤 [{$a->錯誤碼}].<br />{$a->排除故障資訊}</p>';
 $string['downloadedfilecheckfailed'] = '下載檔案檢查錯誤。';
 $string['invalidmd5'] = '無效的 md5';
 $string['missingrequiredfield'] = '缺少部份必填欄位';
index 7006d1a..d3671be 100644 (file)
@@ -89,7 +89,7 @@ $string['bloglevel'] = 'Blog visibility';
 $string['bookmarkadded'] = 'Bookmark added.';
 $string['bookmarkalreadyexists'] = 'You have already bookmarked this page.';
 $string['bookmarkdeleted'] = 'Bookmark deleted.';
-$string['bookmarkthispage'] = 'bookmark this page';
+$string['bookmarkthispage'] = 'Bookmark this page';
 $string['cachejs'] = 'Cache Javascript';
 $string['cachejs_help'] = 'Javascript caching and compression greatly improves page loading performance. it is strongly recommended for production sites. Developers will probably want to disable this feature.';
 $string['cachetext'] = 'Text cache lifetime';
@@ -231,7 +231,7 @@ $string['configfrontpageloggedin'] = 'The items selected above will be displayed
 $string['configfullnamedisplay'] = 'This defines how names are shown when they are displayed in full. The default value, "language", leaves it to the string "fullnamedisplay" in the current language pack to decide. Some languages have different name display conventions.
 
 For most mono-lingual sites the most efficient setting is "firstname lastname", but you may choose to hide surnames altogether. Placeholders that can be used are: firstname, lastname, firstnamephonetic, lastnamephonetic, middlename, and alternatename.';
-$string['configgeoipfile'] = 'Location of GeoIP City binary data file. This file is not part of Moodle distribution and must be obtained separately from <a href="http://www.maxmind.com/">MaxMind</a>. You can either buy a commercial version or use the free version.<br />Simply download <a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz" >http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz</a> and extract it into "{$a}" directory on your server.';
+$string['configgeoipfile'] = 'Location of GeoIP City binary data file. This file is not part of Moodle distribution and must be obtained separately from <a href="http://www.maxmind.com/">MaxMind</a>. You can either buy a commercial version or use the free version. Simply download <a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz" >http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz</a> and extract it into "{$a}" directory on your server.';
 $string['configgetremoteaddrconf'] = 'If your server is behind a reverse proxy, you can use this setting to specify which HTTP headers can be trusted to contain the remote IP address. The headers are read in order, using the first one that is available.';
 $string['configgradebookroles'] = 'This setting allows you to control who appears on the gradebook.  Users need to have at least one of these roles in a course to be shown in the gradebook for that course.';
 $string['configgradeexport'] = 'Choose which gradebook export formats are your primary methods for exporting grades.  Chosen plugins will then set and use a "last exported" field for every grade.  For example, this might result in exported records being identified as being "new" or "updated".  If you are not sure about this then leave everything unchecked.';
@@ -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 :';
@@ -626,7 +624,7 @@ $string['latinexcelexport'] = 'Excel encoding';
 $string['legacyfilesaddallowed'] = 'Allow adding to legacy course files';
 $string['legacyfilesaddallowed_help'] = 'If a course has legacy course files, allow new files and folders to be added to it.';
 $string['legacyfilesinnewcourses'] = 'Legacy course files in new courses';
-$string['legacyfilesinnewcourses_help'] = 'By default legacy course files areas are available only in upgraded courses. Please note some features like single activity backup/restore are not compatible with this settings.';
+$string['legacyfilesinnewcourses_help'] = 'By default, legacy course files areas are available in upgraded courses only. Please note that some features such as activity backup and restore are not compatible with this setting.';
 $string['licensesettings'] = 'Licence settings';
 $string['linkadmincategories'] = 'Link admin categories';
 $string['linkadmincategories_help'] = 'If enabled admin setting categories will be displayed as links in the navigation and will lead to the admin category pages.';
@@ -757,7 +755,7 @@ $string['navshowcategories'] = 'Show course categories';
 $string['navshowmycoursecategories'] = 'Show my course categories';
 $string['navshowmycoursecategories_help'] = 'If enabled courses in the users my courses branch will be shown in categories.';
 $string['navsortmycoursessort'] = 'Sort my courses';
-$string['navsortmycoursessort_help'] = 'This determines whether courses are listed under My courses according to the sort order (i.e. the order set in Settings > Site Administration > Courses > Add/edit courses) or alphabetically by course setting.';
+$string['navsortmycoursessort_help'] = 'This determines whether courses are listed under My courses according to the sort order (i.e. the order set in Site administration > Courses > Manage courses and categories) or alphabetically by course setting.';
 $string['neverdeleteruns'] = 'Never delete runs';
 $string['nobookmarksforuser'] = 'You do not have any bookmarks.';
 $string['nodatabase'] = 'No database';
@@ -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';
@@ -1093,16 +1092,14 @@ This warning is often caused by unzipping a standard Moodle package over a previ
 This warning can also be caused by an incomplete checkout or update operation from the Git repository, in which case you may just have to wait for the operation to complete, or perhaps run the appropriate clean-up command and retry the operation.
 
 You can find more information in upgrade documentation at <a href="{$a}">{$a}</a>.';
-$string['upgradesure'] = 'Your Moodle files have been changed, and you are about to automatically upgrade your server to this version: <br /><br />
-<strong>{$a}</strong> <br /><br />
-Once you do this you can not go back again. <br /><br />
-Please note that this process can take a long time. <br /><br />
-Are you sure you want to upgrade this server to this version?';
+$string['upgradesure'] = '<p>Your Moodle files have been changed, and you are about to automatically upgrade your server to this version:</p>
+<p><strong>{$a}</strong></p>
+<p>Once you do this you can not go back again. Please note that this process can take a long time.</p>
+<p>Are you sure you want to upgrade this server to this version?</p>';
 $string['upgradetimedout'] = 'Upgrade timed out, please restart the upgrade.';
 $string['upgrade197notice'] = '<p>Moodle 1.9.7 contains a number of security fixes to user passwords and backups to protect the user data on your site. As a result some of your settings and permissions relating to backups may have changed.<br />
 See the <a href="http://docs.moodle.org/dev/Moodle_1.9.7_release_notes" target="_blank">Moodle 1.9.7 release notes</a> for full details.</p>';
 $string['upgrade197noticesubject'] = 'Moodle 1.9.7 upgrade security notices';
-$string['upgrade197salt'] = 'To reduce the risk of password theft, you are strongly recommended to set a password salt.<br />See the <a href="{$a}" target="_blank">password salting documentation</a> for details.';
 $string['upgradingdata'] = 'Upgrading data';
 $string['upgradinglogs'] = 'Upgrading logs';
 $string['upgradingversion'] = 'Upgrading to new version';
@@ -1127,7 +1124,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..9ad8f9e 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';
@@ -276,9 +282,9 @@ $string['makeprivate'] = 'Make private';
 $string['makepublic'] = 'Make public';
 $string['managebadges'] = 'Manage badges';
 $string['message'] = 'Message body';
-$string['messagebody'] = '<p>You have been awarded a badge "%badgename%"!</p>
+$string['messagebody'] = '<p>You have been awarded the badge "%badgename%"!</p>
 <p>More information about this badge can be found at %badgelink%.</p>
-<p>If there is no badge attached to this email, you can manage and download it from {$a} page.</p>';
+<p>You can manage and download the badge from {$a}.</p>';
 $string['messagesubject'] = 'Congratulations! You just earned a badge!';
 $string['method'] = 'This criterion is complete when...';
 $string['mingrade'] = 'Minimum grade required';
index 00527cd..e38b5a4 100644 (file)
@@ -50,6 +50,7 @@ $string['hidepanel'] = 'Hide panel';
 $string['moveblock'] = 'Move {$a} block';
 $string['moveblockafter'] = 'Move block to after {$a} block';
 $string['moveblockbefore'] = 'Move block to before {$a} block';
+$string['moveblockinregion'] = 'Move block to {$a} region';
 $string['movingthisblockcancel'] = 'Moving this block ({$a})';
 $string['onthispage'] = 'On this page';
 $string['pagetypes'] = 'Page types';
index a6b1f3c..fc9bc9e 100644 (file)
@@ -124,7 +124,7 @@ $string['err_nocourses'] = 'Course completion is not enabled for any other cours
 $string['err_nograde'] = 'A course pass grade has not been set for this course. To enable this criteria type you must create a pass grade for this course.';
 $string['err_noroles'] = 'There are no roles with the capability moodle/course:markcomplete in this course.';
 $string['err_nousers'] = 'There are no students on this course or group for whom completion information is displayed. (By default, completion information is displayed only for students, so if there are no students, you will see this error. Administrators can alter this option via the admin screens.)';
-$string['err_settingslocked'] = 'One or more students have already completed a criteria so the settings have been locked. Unlocking the completion criteria settings will delete any existing user data and may cause confusion.';
+$string['err_settingslocked'] = 'One or more students have already completed a criterion so the settings have been locked. Unlocking the completion criteria settings will delete any existing user data and may cause confusion.';
 $string['err_system'] = 'An internal error occurred in the completion system. (System administrators can enable debugging information to see more detail.)';
 $string['eventcoursecompleted'] = 'Course completed';
 $string['eventcoursecompletionupdated'] = 'Course completion updated';
index 2739a36..b5c1c3c 100644 (file)
@@ -194,8 +194,8 @@ $string['dbconnectionfailed'] = '<p>Error: Database connection failed</p>
 $string['dbdriverproblem'] = '<p>Error: database driver problem detected</p>
 <p>The site administrator should verify server configuration</p><p>{$a}</p>';
 $string['dbsessionbroken'] = 'Serious database session problem detected.<br /><br />Please notify server administrator.';
-$string['dbsessionhandlerproblem'] = 'Setting up of database session failed.<br /><br />Please notify server administrator.';
-$string['dbsessionmysqlpacketsize'] = 'Serious session error detected.<br /><br />Please notify administrator, this problem is most probably caused by small value in max_allowed_packet MySQL setting.';
+$string['dbsessionhandlerproblem'] = 'Setting up of database session failed. Please notify the server administrator.';
+$string['dbsessionmysqlpacketsize'] = 'A serious session error was detected. Please notify the site administrator. The problem is most probably caused by small value in max_allowed_packet MySQL setting.';
 $string['dbupdatefailed'] = 'Database update failed';
 $string['ddldependencyerror'] = '{$a->targettype} "{$a->targetname}" cannot be modified. Dependency found with {$a->offendingtype} "{$a->offendingname}"';
 $string['ddlexecuteerror'] = 'DDL sql execution error';
@@ -451,7 +451,8 @@ $string['querystringcannotbeempty'] = 'The query string cannot be empty.';
 $string['redirecterrordetected'] = 'Unsupported redirect detected, script execution terminated';
 $string['refoundto'] = 'Can be refunded to {$a}';
 $string['refoundtoorigi'] = 'Refunded to original amount: {$a}';
-$string['remotedownloaderror'] = 'Download of component to your server failed, please verify proxy settings, PHP cURL extension is highly recommended.<br /><br />You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.';
+$string['remotedownloaderror'] = '<p>The download of the component to your server failed. Please verify proxy settings; the PHP cURL extension is highly recommended.</p>
+<p>You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.</p>';
 $string['remotedownloadnotallowed'] = 'Download of components to your server isn\'t allowed (allow_url_fopen is disabled).<br /><br />You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.';
 $string['reportnotavailable'] = 'This type of report is only available for the site course';
 $string['requirecorrectaccess'] = 'Invalid url or port.';
@@ -470,12 +471,13 @@ $string['serverconnection'] = 'Error connecting to the server';
 $string['servicedonotexist'] = 'The service does not exist';
 $string['sessionwaiterr'] = 'Timed out while waiting for session lock.<br />Wait for your current requests to finish and try again later.';
 $string['sessioncookiesdisable'] = 'Incorrect use of require_key_login() - session cookies must be disabled!';
-$string['sessiondiskfull'] = 'The session partition is full. It is not possible to login at this time.<br /><br />Please notify server administrator.';
+$string['sessiondiskfull'] = 'The session partition is full. It is not possible to login at this time. Please notify the server administrator.';
 $string['sessionhandlerproblem'] = 'Session handler is misconfigured';
 $string['sessionerroruser'] = 'Your session has timed out.  Please login again.';
 $string['sessionerroruser2'] = 'A server error that affects your login session was detected. Please login again or restart your browser.';
 $string['sessionipnomatch'] = 'Sorry, but your IP number seems to have changed from when you first logged in.  This security feature prevents crackers stealing your identity while logged in to this site.  Normal users should not be seeing this message - please ask the site administrator for help.';
-$string['sessionipnomatch2'] = 'Sorry, but your IP number seems to have changed from when you first logged in.  This security feature prevents crackers stealing your identity while logged in to this site. You may see this error if you use wireless networks or if you are roaming between different networks. Please ask the site administrator for more help.<br /><br />If you want to continue please press F5 key to refresh this page.';
+$string['sessionipnomatch2'] = '<p>Sorry, but your IP number seems to have changed from when you first logged in. This security feature prevents crackers stealing your identity while logged in to this site. You may see this error if you use wireless networks or if you are roaming between different networks. Please ask the site administrator for more help.</p>
+<p>If you want to continue please press F5 key to refresh this page.</p>';
 $string['shortnametaken'] = 'Short name is already used for another course ({$a})';
 $string['scheduledbackupsdisabled'] = 'Scheduled backups have been disabled by the server admin';
 $string['socksnotsupported'] = 'SOCKS5 proxy is not supported in PHP4';
index 3be622d..589efa5 100644 (file)
@@ -61,10 +61,10 @@ $string['nosharedformfound'] = 'No template found';
 $string['searchtemplate'] = 'Grading forms search';
 $string['searchtemplate_help'] = 'You can search for a grading form and use it as a template for the new grading form here. Simply type words that should appear somewhere in the form name, its description or the form body itself. To search for a phrase, wrap the whole query in double quotes.
 
-By default, only the grading forms that have been saved as shared templates are included in the search results. You can also include all your own grading forms in the search results. This way, you can simply re-use your grading forms without sharing them. Only forms marked as \'Ready for usage\' can be re-used this way.';
+By default, only the grading forms that have been saved as shared templates are included in the search results. You can also include all your own grading forms in the search results. This way, you can simply re-use your grading forms without sharing them. Only forms marked as \'Ready for use\' can be re-used this way.';
 $string['searchownforms'] = 'include my own forms';
 $string['statusdraft'] = 'Draft';
-$string['statusready'] = 'Ready for usage';
+$string['statusready'] = 'Ready for use';
 $string['templatedelete'] = 'Delete';
 $string['templatedeleteconfirm'] = 'You are going to delete the shared template \'{$a}\'. Deleting a template does not affect existing forms that were created from it.';
 $string['templateedit'] = 'Edit';
index beaab45..715634e 100644 (file)
@@ -58,7 +58,9 @@ $string['deleteselectedgroup'] = 'Delete selected group';
 $string['editgroupingsettings'] = 'Edit grouping settings';
 $string['editgroupsettings'] = 'Edit group settings';
 $string['enrolmentkey'] = 'Enrolment key';
-$string['enrolmentkey_help'] = 'An enrolment key enables access to the course to be restricted to only those who know the key. If a group enrolment key is specified, then not only will entering that key let the user into the course, but it will also automatically make them a member of this group.';
+$string['enrolmentkey_help'] = 'An enrolment key enables access to the course to be restricted to only those who know the key. If a group enrolment key is specified, then not only will entering that key let the user into the course, but it will also automatically make them a member of this group.
+
+Note: Group enrolment keys must be enabled in the self enrolment settings and an enrolment key for the course must also be specified.';
 $string['erroraddremoveuser'] = 'Error adding/removing user {$a} to group';
 $string['erroreditgroup'] = 'Error creating/updating group {$a}';
 $string['erroreditgrouping'] = 'Error creating/updating grouping {$a}';
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 0f537a4..da2502e 100644 (file)
@@ -1573,7 +1573,12 @@ $string['searchagain'] = 'Search again';
 $string['searchbyemail'] = 'Search by email address';
 $string['searchbyusername'] = 'Search by username';
 $string['searchcourses'] = 'Search courses';
-$string['searchhelp'] = 'You can search for multiple words at once.<br /><br />word : find any match of this word within the text.<br />+word : only exact matching words will be found.<br />-word : don\'t include results containing this word.';
+$string['searchhelp'] = '<p>You can search for multiple words at once and can refine your search as follows:</p>
+<ul>
+<li>word - find any match of this word within the text.</li>
+<li>+word - only exact matching words will be found.</li>
+<li>-word - don\'t include results containing this word.</li>
+</ul>';
 $string['searchoptions'] = 'Search options';
 $string['searchresults'] = 'Search results';
 $string['sec'] = 'sec';
@@ -1614,7 +1619,7 @@ $string['selectmoduletoviewhelp'] = 'Select an activity or resource to view its
 Double-click on an activity or resource name to quickly add it.';
 $string['selectnos'] = 'Select all \'No\'';
 $string['selectperiod'] = 'Select period';
-$string['selectcategorysort'] = 'Which categories would you like to sort';
+$string['selectcategorysort'] = 'Which categories would you like to sort?';
 $string['selectcategorysortby'] = 'Select how you would like to sort categories';
 $string['selectcoursesortby'] = 'Select how you would like to sort courses';
 $string['senddetails'] = 'Send my details via email';
index 27b569d..092e50f 100644 (file)
@@ -92,7 +92,8 @@ $string['defaultinfofor'] = 'The default category for questions shared in contex
 $string['defaultmarkmustbepositive'] = 'The default mark must be positive.';
 $string['deletecoursecategorywithquestions'] = 'There are questions in the question bank associated with this course category. If you proceed, they will be deleted. You may wish to move them first, using the question bank interface.';
 $string['deletequestioncheck'] = 'Are you absolutely sure you want to delete \'{$a}\'?';
-$string['deletequestionscheck'] = 'Are you absolutely sure you want to delete the following questions?<br /><br />{$a}';
+$string['deletequestionscheck'] = '<p>Are you absolutely sure you want to delete the following questions?</p>
+<p>{$a}</p>';
 $string['deletingbehaviour'] = 'Deleting question behaviour \'{$a}\'';
 $string['deletingqtype'] = 'Deleting question type \'{$a}\'';
 $string['didnotmatchanyanswer'] = '[Did not match any answer]';
index dcbb81b..b089dfa 100644 (file)
@@ -124,7 +124,7 @@ $string['course:activityvisibility'] = 'Hide/show activities';
 $string['course:bulkmessaging'] = 'Send a message to many people';
 $string['course:create'] = 'Create courses';
 $string['course:delete'] = 'Delete courses';
-$string['course:viewsuspendedusers'] = 'Can view suspended users.';
+$string['course:viewsuspendedusers'] = 'View suspended users';
 $string['course:changecategory'] = 'Change course category';
 $string['course:changefullname'] = 'Change course full name';
 $string['course:changeidnumber'] = 'Change course ID number';
@@ -394,8 +394,8 @@ $string['user:manageblocks'] = 'Manage blocks on user profile of other users';
 $string['user:manageownblocks'] = 'Manage blocks on own public user profile';
 $string['user:manageownfiles'] = 'Manage files on own private file areas';
 $string['user:managesyspages'] = 'Configure default page layout for public user profiles';
-$string['user:readuserblogs'] = 'See all user blogs';
-$string['user:readuserposts'] = 'See all user posts';
+$string['user:readuserblogs'] = 'View all user blogs';
+$string['user:readuserposts'] = 'View all user forum posts';
 $string['user:update'] = 'Update user profiles';
 $string['user:viewalldetails'] = 'View user full information';
 $string['user:viewdetails'] = 'View user profiles';
index 52966b2..90c8d9e 100644 (file)
@@ -52,7 +52,7 @@ $string['createtokenforuser'] = 'Create a token for a user';
 $string['createtokenforuserdescription'] = 'Create a token for the web services user.';
 $string['createuser'] = 'Create a specific user';
 $string['createuserdescription'] = 'A web services user is required to represent the system controlling Moodle.';
-$string['criteriaerror'] = 'Missing permissions to search on a criteria.';
+$string['criteriaerror'] = 'Missing permissions to search on a criterion.';
 $string['default'] = 'Default to "{$a}"';
 $string['deleteaservice'] = 'Delete service';
 $string['deleteservice'] = 'Delete the service: {$a->name} (id: {$a->id})';
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 8d1bf9e..f233078 100644 (file)
@@ -603,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 1290493..ee3e01b 100644 (file)
@@ -55,7 +55,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
     /**
      * The timeout for each Behat step (load page, wait for an element to load...).
      */
-    const TIMEOUT = 3;
+    const TIMEOUT = 6;
 
     /**
      * And extended timeout for specific cases.
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 17bc943..dce4028 100644 (file)
@@ -104,6 +104,11 @@ class behat_form_select extends behat_form_field {
                 // Wrapped in a try & catch as we can fall into race conditions
                 // and the element may not be there.
                 try {
+
+                    // Wait for all the possible AJAX requests that have been
+                    // already triggered by selectOption() to be finished.
+                    $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
+
                     current($optionnodes)->click();
                 } catch (Exception $e) {
                     // We continue and return as this means that the element is not there or it is not the same.
@@ -112,6 +117,11 @@ class behat_form_select extends behat_form_field {
             }
 
         } else {
+
+            // Wait for all the possible AJAX requests that have been
+            // already triggered by selectOption() to be finished.
+            $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
+
             // Wrapped in a try & catch as we can fall into race conditions
             // and the element may not be there.
             try {
@@ -127,6 +137,10 @@ class behat_form_select extends behat_form_field {
                 return;
             }
 
+            // Wait for all the possible AJAX requests that have been
+            // already triggered by selectOption() to be finished.
+            $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
+
             // Wrapped in a try & catch as we can fall into race conditions
             // and the element may not be there.
             try {
index e6ea562..ce70ef0 100644 (file)
@@ -1013,4 +1013,14 @@ $cache = '.var_export($cache, true).';
             opcache_invalidate($file, true);
         }
     }
+
+    /**
+     * Return true if subsystemname is core subsystem.
+     *
+     * @param string $subsystemname name of the subsystem.
+     * @return bool true if core subsystem.
+     */
+    public static function is_core_subsystem($subsystemname) {
+        return isset(self::$subsystems[$subsystemname]);
+    }
 }
index 4e4ed8d..46aa825 100644 (file)
@@ -36,6 +36,13 @@ defined('MOODLE_INTERNAL') || die();
  *
  * Both events could be triggered in a row, first the uploaded, then the submitted.
  *
+ * @property-read array $other {
+ *      Extra information about event.
+ *
+ *      @type array pathnamehashes uploaded files path name hashes.
+ *      @type string content string.
+ * }
+ *
  * @package    core
  * @copyright  2013 Frédéric Massart
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 5670f52..0e7da1c 100644 (file)
@@ -29,6 +29,15 @@ defined('MOODLE_INTERNAL') || die();
  *
  * Class for event to be triggered when a new blog entry is associated with a context.
  *
+ * @property-read array $other {
+ *      Extra information about event.
+ *
+ *      @type string associatetype type of blog association, course/coursemodule.
+ *      @type int blogid id of blog.
+ *      @type int associateid id of associate.
+ *      @type string subject blog subject.
+ * }
+ *
  * @package    core
  * @copyright  2013 onwards Ankit Agarwal
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
diff --git a/lib/classes/event/blog_comment_created.php b/lib/classes/event/blog_comment_created.php
new file mode 100644 (file)
index 0000000..1ac2850
--- /dev/null
@@ -0,0 +1,53 @@
+<?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/>.
+
+/**
+ * blog comment created event.
+ *
+ * @package    core
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * blog comment created event.
+ *
+ * @package    core
+ * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class blog_comment_created extends \core\event\comment_created {
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/blog/index.php', array('entryid' => $this->other['itemid']));
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'User with id ' . $this->userid . ' added comment for blog with id ' . $this->other['itemid'];
+    }
+}
diff --git a/lib/classes/event/blog_comment_deleted.php b/lib/classes/event/blog_comment_deleted.php
new file mode 100644 (file)
index 0000000..cf8e7da