Merge branch 'wip-mdl-43028' of https://github.com/rajeshtaneja/moodle
authorDan Poltawski <dan@moodle.com>
Wed, 4 Dec 2013 06:57:24 +0000 (14:57 +0800)
committerDan Poltawski <dan@moodle.com>
Wed, 4 Dec 2013 06:57:24 +0000 (14:57 +0800)
389 files changed:
.shifter.json
admin/enrol.php
admin/mnet/access_control.php
admin/renderer.php
admin/settings/plugins.php
admin/settings/server.php
admin/tests/behat/behat_admin.php
admin/tests/behat/display_short_names.feature
admin/tool/behat/cli/util.php
admin/tool/behat/tests/behat/basic_actions.feature
admin/tool/behat/tests/behat/data_generators.feature
admin/tool/behat/tests/behat/manipulate_forms.feature
admin/tool/behat/version.php
admin/tool/capability/locallib.php
admin/tool/capability/renderer.php
admin/tool/customlang/db/upgrade.php
admin/tool/customlang/index.php
admin/tool/dbtransfer/locallib.php
admin/tool/generator/classes/backend.php
admin/tool/innodb/index.php
admin/tool/langimport/index.php
admin/tool/multilangupgrade/index.php
admin/tool/phpunit/webrunner.php
admin/tool/spamcleaner/index.php
admin/tool/uploadcourse/classes/processor.php
admin/tool/uploadcourse/tests/course_test.php
admin/tool/uploadcourse/tests/helper_test.php
admin/tool/uploadcourse/tests/processor_test.php
admin/tool/uploaduser/index.php
admin/tool/uploaduser/picture.php
admin/tool/xmldb/actions/check_foreign_keys/check_foreign_keys.class.php
admin/tool/xmldb/lang/en/tool_xmldb.php
auth/cas/db/upgrade.php
auth/db/auth.php
auth/imap/auth.php
auth/ldap/auth.php
auth/ldap/db/upgrade.php
auth/manual/db/upgrade.php
auth/mnet/db/upgrade.php
auth/pop3/auth.php
auth/upgrade.txt
backup/backup.class.php
backup/controller/backup_controller.class.php
backup/controller/restore_controller.class.php
backup/util/helper/backup_cron_helper.class.php
backup/util/plan/tests/step_test.php
backup/util/progress/core_backup_progress.class.php
backup/util/progress/tests/progress_test.php
backup/util/ui/base_moodleform.class.php
backup/util/ui/restore_ui_stage.class.php
blocks/activity_modules/tests/behat/block_activity_modules.feature [new file with mode: 0644]
blocks/community/db/upgrade.php
blocks/completionstatus/db/upgrade.php
blocks/course_list/block_course_list.php
blocks/course_summary/db/upgrade.php
blocks/html/db/upgrade.php
blocks/navigation/db/upgrade.php
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js
blocks/navigation/yui/src/navigation/js/navigation.js
blocks/rss_client/block_rss_client.php
blocks/section_links/db/upgrade.php
blocks/selfcompletion/db/upgrade.php
blocks/settings/db/upgrade.php
blocks/site_main_menu/block_site_main_menu.php
blocks/tags/block_tags.php
blocks/tests/behat/configure_block_throughout_site.feature
blocks/tests/behat/manage_blocks.feature
blog/index.php
blog/lib.php
blog/locallib.php
blog/tests/bloglib_test.php
blog/upgrade.txt [new file with mode: 0644]
cache/admin.php
cache/classes/definition.php
cache/classes/factory.php
cache/classes/helper.php
cache/classes/loaders.php
cache/locallib.php
calendar/lib.php
calendar/renderer.php
cohort/tests/behat/behat_cohort.php
cohort/tests/behat/upload_cohort_users.feature
composer.json
config-dist.php
course/classes/management/helper.php
course/classes/management_renderer.php
course/format/renderer.php
course/lib.php
course/tests/behat/behat_course.php
course/tests/behat/category_management.feature
course/tests/behat/course_controls.feature
course/tests/courselib_test.php
course/tests/externallib_test.php
course/tests/management_helper_test.php
course/yui/build/moodle-course-management/moodle-course-management-debug.js
course/yui/build/moodle-course-management/moodle-course-management-min.js
course/yui/build/moodle-course-management/moodle-course-management.js
course/yui/src/management/js/category.js
course/yui/src/management/js/console.js
course/yui/src/management/js/course.js
enrol/category/locallib.php
enrol/cohort/locallib.php
enrol/database/db/upgrade.php
enrol/database/lib.php
enrol/flatfile/db/upgrade.php
enrol/flatfile/lib.php
enrol/guest/db/upgrade.php
enrol/imsenterprise/db/upgrade.php
enrol/imsenterprise/lib.php
enrol/ldap/lib.php
enrol/manual/db/upgrade.php
enrol/manual/lib.php
enrol/meta/locallib.php
enrol/mnet/db/upgrade.php
enrol/paypal/db/upgrade.php
enrol/paypal/lib.php
enrol/self/db/upgrade.php
enrol/self/lib.php
enrol/self/version.php
filter/algebra/thirdpartylibs.xml [new file with mode: 0644]
filter/mediaplugin/db/upgrade.php
filter/tex/db/upgrade.php
filter/tex/filter.php
filter/tex/lang/en/filter_tex.php
filter/tex/latex.php
filter/tex/lib.php
filter/tex/pix.php
filter/tex/settings.php
filter/tex/texdebug.php
filter/tex/version.php
grade/grading/form/rubric/db/upgrade.php
grade/import/csv/index.php
grade/import/xml/import.php
grade/import/xml/index.php
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/grader/preferences.php
grade/report/lib.php
grade/report/upgrade.txt
group/tests/behat/id_uniqueness.feature
install/lang/hy/admin.php
install/lang/lt_uni/admin.php
install/lang/pt_br/admin.php
install/lang/tr/install.php
lang/en/admin.php
lang/en/blog.php
lang/en/cache.php
lang/en/calendar.php
lang/en/error.php
lang/en/message.php
lang/en/mnet.php
lang/en/moodle.php
lang/en/plugin.php
lang/en/repository.php
lib/accesslib.php
lib/adminlib.php
lib/ajax/getsiteadminbranch.php
lib/authlib.php
lib/badgeslib.php
lib/behat/classes/behat_command.php
lib/behat/classes/behat_config_manager.php
lib/behat/classes/behat_selectors.php
lib/behat/classes/util.php
lib/behat/lib.php
lib/classes/event/blog_association_created.php [new file with mode: 0644]
lib/classes/event/blog_entries_viewed.php [new file with mode: 0644]
lib/classes/event/course_module_viewed.php [new file with mode: 0644]
lib/classes/minify.php
lib/classes/php_time_limit.php [new file with mode: 0644]
lib/classes/plugininfo/base.php
lib/classes/plugininfo/calendartype.php
lib/classes/session/file.php
lib/classes/session/manager.php
lib/completionlib.php
lib/conditionlib.php
lib/cronlib.php
lib/db/upgrade.php
lib/editor/tinymce/db/upgrade.php
lib/editor/tinymce/plugins/managefiles/manage.php
lib/editor/tinymce/plugins/spellchecker/db/upgrade.php
lib/enrollib.php
lib/externallib.php
lib/filelib.php
lib/form/form.js
lib/formslib.php
lib/google/Google_Client.php
lib/google/readme_moodle.txt
lib/grouplib.php
lib/messagelib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/phpunit/classes/util.php
lib/setup.php
lib/setuplib.php
lib/statslib.php
lib/testing/classes/util.php
lib/tests/behat/behat_deprecated.php [new file with mode: 0644]
lib/tests/behat/behat_general.php
lib/tests/behat/behat_navigation.php
lib/tests/event_course_module_viewed.php [new file with mode: 0644]
lib/tests/filelib_test.php
lib/tests/fixtures/event_fixtures.php
lib/tests/grouplib_test.php
lib/tests/messagelib_test.php
lib/tests/minify_test.php
lib/tests/moodlelib_test.php
lib/tests/performance/filtersettingsperformancetester.php
lib/tests/setuplib_test.php
lib/tests/statslib_test.php
lib/upgrade.txt
lib/upgradelib.php
lib/webdavlib.php
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-maintenancemodetimer/moodle-core-maintenancemodetimer.js [new file with mode: 0644]
lib/yui/dragdrop/dragdrop.js
lib/yui/src/maintenancemodetimer/build.json [new file with mode: 0644]
lib/yui/src/maintenancemodetimer/js/maintenancemodetimer.js [new file with mode: 0644]
lib/yui/src/maintenancemodetimer/meta/maintenancemodetimer.json [new file with mode: 0644]
message/defaultoutputs.php
message/module.js
message/output/email/db/upgrade.php
message/output/jabber/db/upgrade.php
message/output/popup/db/upgrade.php
message/renderer.php
message/tests/behat/block_users.feature
message/tests/behat/display_history.feature
message/tests/behat/manage_contacts.feature
message/tests/behat/search_history.feature
mod/assign/db/upgrade.php
mod/assign/feedback/comments/db/upgrade.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/editpdf/db/upgrade.php
mod/assign/feedback/file/db/upgrade.php
mod/assign/feedback/file/importziplib.php
mod/assign/feedback/file/locallib.php
mod/assign/locallib.php
mod/assign/submission/comments/db/upgrade.php
mod/assign/submission/file/db/upgrade.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/db/upgrade.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/tests/upgradelib_test.php
mod/assign/upgradelib.php
mod/assignment/db/upgrade.php
mod/book/classes/event/course_module_viewed.php
mod/book/db/upgrade.php
mod/book/lang/en/book.php
mod/chat/chatd.php
mod/chat/db/upgrade.php
mod/chat/gui_header_js/jsupdated.php
mod/choice/classes/event/course_module_viewed.php
mod/choice/db/upgrade.php
mod/choice/lang/en/choice.php
mod/choice/view.php
mod/data/db/upgrade.php
mod/data/field/picture/field.class.php
mod/data/import.php
mod/feedback/classes/event/course_module_viewed.php
mod/feedback/db/upgrade.php
mod/feedback/lang/en/feedback.php
mod/feedback/view.php
mod/folder/db/upgrade.php
mod/folder/tests/generator/lib.php [new file with mode: 0644]
mod/folder/tests/generator_test.php [new file with mode: 0644]
mod/forum/db/upgrade.php
mod/forum/lib.php
mod/forum/tests/behat/edit_post_student.feature
mod/glossary/db/upgrade.php
mod/glossary/tests/behat/print_friendly_version.feature
mod/imscp/db/upgrade.php
mod/imscp/lib.php
mod/imscp/tests/generator/lib.php [new file with mode: 0644]
mod/imscp/tests/generator_test.php [new file with mode: 0644]
mod/imscp/tests/packages/singlescobasic.zip [new file with mode: 0644]
mod/label/db/upgrade.php
mod/lesson/db/upgrade.php
mod/lesson/lib.php
mod/lti/db/upgrade.php
mod/page/classes/event/course_module_viewed.php
mod/page/db/upgrade.php
mod/page/lang/en/page.php
mod/page/view.php
mod/quiz/attemptlib.php
mod/quiz/autosave.ajax.php
mod/quiz/db/log.php
mod/quiz/db/upgrade.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/mod_form.php
mod/quiz/override_form.php
mod/quiz/overrides.php
mod/quiz/renderer.php
mod/quiz/report/overview/db/upgrade.php
mod/quiz/report/overview/report.php
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/report/statistics/report.php
mod/quiz/report/statistics/statistics_graph.php
mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php
mod/quiz/styles.css
mod/quiz/version.php
mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-debug.js
mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave-min.js
mod/quiz/yui/build/moodle-mod_quiz-autosave/moodle-mod_quiz-autosave.js
mod/quiz/yui/src/autosave/js/autosave.js
mod/resource/db/upgrade.php
mod/scorm/backup/moodle2/restore_scorm_stepslib.php
mod/scorm/datamodels/scorm_12.js.php
mod/scorm/db/upgrade.php
mod/scorm/lib.php
mod/scorm/module.js
mod/scorm/tests/generator/lib.php [new file with mode: 0644]
mod/scorm/tests/generator_test.php [new file with mode: 0644]
mod/scorm/version.php
mod/scorm/view.js
mod/survey/db/upgrade.php
mod/url/db/upgrade.php
mod/wiki/db/upgrade.php
mod/wiki/tests/behat/page_history.feature
mod/workshop/classes/event/course_module_viewed.php
mod/workshop/db/upgrade.php
mod/workshop/form/accumulative/db/upgrade.php
mod/workshop/form/comments/db/upgrade.php
mod/workshop/form/numerrors/db/upgrade.php
mod/workshop/form/rubric/db/upgrade.php
mod/workshop/lang/en/workshop.php
mod/workshop/view.php
portfolio/boxnet/db/upgrade.php
portfolio/googledocs/db/upgrade.php
portfolio/picasa/db/upgrade.php
question/behaviour/manualgraded/db/upgrade.php
question/classes/statistics/questions/calculator.php
question/classes/statistics/responses/analyser.php
question/engine/upgrade/upgradelib.php
question/export_form.php
question/format.php
question/import_form.php
question/tests/behat/edit_questions.feature
question/tests/behat/preview_question.feature
question/type/calculated/db/upgrade.php
question/type/essay/db/upgrade.php
question/type/match/db/upgrade.php
question/type/multianswer/db/upgrade.php
question/type/multichoice/db/upgrade.php
question/type/multichoice/styles.css
question/type/numerical/db/upgrade.php
question/type/shortanswer/db/upgrade.php
question/type/shortanswer/question.php
question/type/shortanswer/tests/question_test.php
report/loglive/lib.php
report/security/index.php
report/security/locallib.php
repository/boxnet/db/upgrade.php
repository/boxnet/lib.php
repository/dropbox/db/upgrade.php
repository/dropbox/lib.php
repository/equella/lib.php
repository/filepicker.php
repository/googledocs/db/upgrade.php
repository/lib.php
repository/picasa/db/upgrade.php
repository/repository_ajax.php
repository/repository_callback.php
repository/upgrade.txt
theme/afterburner/db/upgrade.php
theme/base/style/admin.css
theme/base/style/core.css
theme/base/style/course.css
theme/base/style/question.css
theme/bootstrapbase/config.php
theme/bootstrapbase/layout/popup.php [new file with mode: 0644]
theme/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/course.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/style/moodle.css
theme/formal_white/db/upgrade.php
theme/yui_combo.php
user/editadvanced_form.php
user/renderer.php
user/selector/module.js
user/tests/behat/edituserpassword.feature [new file with mode: 0644]
version.php

index b453cc8..a394425 100644 (file)
@@ -1,4 +1,5 @@
 {
     "coverage": false,
-    "lint": "config"
+    "lint": "config",
+    "clean": true
 }
index 642d93b..5f26eff 100644 (file)
@@ -108,7 +108,7 @@ switch ($action) {
         echo $OUTPUT->header();
 
         // This may take a long time.
-        set_time_limit(0);
+        core_php_time_limit::raise();
 
         // Disable plugin to prevent concurrent cron execution.
         unset($enabled[$enrol]);
index 79c744c..375b3fa 100644 (file)
@@ -16,8 +16,6 @@ require_login();
 
 admin_externalpage_setup('ssoaccesscontrol');
 
-echo $OUTPUT->header();
-
 if (!extension_loaded('openssl')) {
     print_error('requiresopenssl', 'mnet');
 }
@@ -65,9 +63,11 @@ if (!empty($action) and confirm_sesskey()) {
 
             if (mnet_update_sso_access_control($idrec->username, $idrec->mnet_host_id, $accessctrl)) {
                 if ($accessctrl == 'allow') {
-                    redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('uset'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
-                } elseif ($accessctrl == 'deny') {
-                    redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
+                    redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('user' => $idrec->username,
+                        'host' => $mnethosts[$idrec->mnet_host_id])));
+                } else if ($accessctrl == 'deny') {
+                    redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user' => $idrec->username,
+                        'host' => $mnethosts[$idrec->mnet_host_id])));
                 }
             }
             break;
@@ -118,6 +118,8 @@ if ($form = data_submitted() and confirm_sesskey()) {
     exit;
 }
 
+echo $OUTPUT->header();
+
 // Explain
 echo $OUTPUT->box(get_string('ssoacldescr','mnet'));
 // Are the needed bits enabled?
index 0f84e00..f86097b 100644 (file)
@@ -1176,13 +1176,14 @@ class core_admin_renderer extends plugin_renderer_base {
             get_string('displayname', 'core_plugin'),
             get_string('source', 'core_plugin'),
             get_string('version', 'core_plugin'),
+            get_string('release', 'core_plugin'),
             get_string('availability', 'core_plugin'),
             get_string('actions', 'core_plugin'),
             get_string('notes','core_plugin'),
         );
-        $table->headspan = array(1, 1, 1, 1, 2, 1);
+        $table->headspan = array(1, 1, 1, 1, 1, 2, 1);
         $table->colclasses = array(
-            'pluginname', 'source', 'version', 'availability', 'settings', 'uninstall', 'notes'
+            'pluginname', 'source', 'version', 'release', 'availability', 'settings', 'uninstall', 'notes'
         );
 
         foreach ($plugininfo as $type => $plugins) {
@@ -1238,6 +1239,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 }
 
                 $version = new html_table_cell($plugin->versiondb);
+                $release = new html_table_cell($plugin->release);
 
                 $isenabled = $plugin->is_enabled();
                 if (is_null($isenabled)) {
@@ -1283,7 +1285,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $notes = new html_table_cell($requiredby.$updateinfo);
 
                 $row->cells = array(
-                    $pluginname, $source, $version, $availability, $settings, $uninstall, $notes
+                    $pluginname, $source, $version, $release, $availability, $settings, $uninstall, $notes
                 );
                 $table->data[] = $row;
             }
index 63d39ab..283d02a 100644 (file)
@@ -256,7 +256,10 @@ if ($hassiteconfig) {
 
     // Add common settings page
     $temp = new admin_settingpage('managerepositoriescommon', new lang_string('commonrepositorysettings', 'repository'));
-    $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120));
+    $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorygetfiletimeout', new lang_string('getfiletimeout', 'repository'), new lang_string('configgetfiletimeout', 'repository'), 30, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorysyncfiletimeout', new lang_string('syncfiletimeout', 'repository'), new lang_string('configsyncfiletimeout', 'repository'), 1, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorysyncimagetimeout', new lang_string('syncimagetimeout', 'repository'), new lang_string('configsyncimagetimeout', 'repository'), 3, PARAM_INT));
     $temp->add(new admin_setting_configcheckbox('repositoryallowexternallinks', new lang_string('allowexternallinks', 'repository'), new lang_string('configallowexternallinks', 'repository'), 1));
     $temp->add(new admin_setting_configcheckbox('legacyfilesinnewcourses', new lang_string('legacyfilesinnewcourses', 'admin'), new lang_string('legacyfilesinnewcourses_help', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('legacyfilesaddallowed', new lang_string('legacyfilesaddallowed', 'admin'), new lang_string('legacyfilesaddallowed_help', 'admin'), 1));
@@ -485,6 +488,16 @@ if ($hassiteconfig) {
     }
 }
 
+// Add Calendar type settings.
+if ($hassiteconfig) {
+    $ADMIN->add('modules', new admin_category('calendartype', new lang_string('calendartypes', 'calendar')));
+    foreach (core_component::get_plugin_list_with_file('calendartype', 'settings.php') as $plugin => $settingspath) {
+        $settings = new admin_settingpage('calendartype_' . $plugin . '_settings', new lang_string('pluginname', 'calendartype_' . $plugin), 'moodle/site:config');
+        include($settingspath);
+        $ADMIN->add('calendartype', $settings);
+    }
+}
+
 /// Add all local plugins - must be always last!
 if ($hassiteconfig) {
     $ADMIN->add('modules', new admin_category('localplugins', new lang_string('localplugins')));
index ea2491a..20f69b2 100644 (file)
@@ -93,6 +93,7 @@ $options = array(
     GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
     GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR');
 $temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'), new lang_string('configgetremoteaddrconf', 'admin'), 0, $options));
+
 $temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'), new lang_string('webproxyinfo', 'admin')));
 $temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'), new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
 $temp->add(new admin_setting_configtext('proxyport', new lang_string('proxyport', 'admin'), new lang_string('configproxyport', 'admin'), 0, PARAM_INT));
@@ -195,6 +196,8 @@ if (PHP_INT_SIZE === 8) {
 $temp->add(new admin_setting_configselect('extramemorylimit', new lang_string('extramemorylimit', 'admin'),
                                           new lang_string('configextramemorylimit', 'admin'), '512M',
                                           $memoryoptions));
+$temp->add(new admin_setting_configtext('maxtimelimit', new lang_string('maxtimelimit', 'admin'),
+        new lang_string('maxtimelimit_desc', 'admin'), 0, PARAM_INT));
 
 $temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
                                         new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
index 10d39eb..13d5f2f 100644 (file)
@@ -125,23 +125,4 @@ class behat_admin extends behat_base {
             $this->getSession()->wait($timeout, $javascript);
         }
     }
-
-    /**
-     * Goes to notification page ensuring site admin navigation is loaded.
-     *
-     * @Given /^I go to notifications page$/
-     * @return Given[]
-     */
-    public function i_go_to_notifications_page() {
-        if ($this->running_javascript()) {
-            return array(
-                new Given('I expand "' . get_string('administrationsite') . '" node'),
-                new Given('I follow "' . get_string('notifications') . '"')
-            );
-        } else {
-            return array(
-                new Given('I follow "' . get_string('administrationsite') . '"')
-            );
-        }
-    }
 }
index 86231b2..76cfda0 100644 (file)
@@ -15,7 +15,8 @@ Feature: Display extended course names
     And I should not see "C_shortname Course fullname"
 
   Scenario: Courses list with extended course names
-    Given I go to notifications page
+    Given I expand "Site administration" node
+    And I expand "Appearance" node
     And I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][contains(., 'Appearance')]" "xpath_element"
     And I check "Display extended course names"
     When I press "Save changes"
index 287a535..28c933a 100644 (file)
@@ -93,21 +93,17 @@ ini_set('log_errors', '1');
 // Getting $CFG data.
 require_once(__DIR__ . '/../../../../config.php');
 
-// CFG->behat_prefix must be set and with value different than CFG->prefix and phpunit_prefix.
-if (empty($CFG->behat_prefix) ||
-       ($CFG->behat_prefix == $CFG->prefix) ||
-       (!empty($CFG->phpunit_prefix) && $CFG->behat_prefix == $CFG->phpunit_prefix)) {
-    behat_error(BEHAT_EXITCODE_CONFIG,
-        'Define $CFG->behat_prefix in config.php with a value different than $CFG->prefix and $CFG->phpunit_prefix');
-}
-
-// CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot.
-if (empty($CFG->behat_dataroot) ||
-       ($CFG->behat_dataroot == $CFG->dataroot) ||
-       (!empty($CFG->phpunit_dataroot) && $CFG->behat_dataroot == $CFG->phpunit_dataroot)) {
-    behat_error(BEHAT_EXITCODE_CONFIG,
-        'Define $CFG->behat_dataroot in config.php with a value different than $CFG->dataroot and $CFG->phpunit_dataroot');
-}
+// When we use the utilities we don't know how the site
+// will be accessed, so if neither $CFG->behat_switchcompletely or
+// $CFG->behat_wwwroot are set we must think that the site will
+// be accessed using the built-in server which is set by default
+// to localhost:8000. We need to do this to prevent uses of the production
+// wwwroot when the site is being installed / dropped...
+$CFG->behat_wwwroot = behat_get_wwwroot();
+
+// Checking the integrity of the provided $CFG->behat_* vars
+// to prevent conflicts with production and phpunit environments.
+behat_check_config_vars();
 
 // Create behat_dataroot if it doesn't exists.
 if (!file_exists($CFG->behat_dataroot)) {
index c5dc276..5b1bd50 100644 (file)
@@ -18,7 +18,7 @@ Feature: Page contents assertions
     When I follow "Overview"
     And I wait until the page is ready
     And I wait "2" seconds
-    And I hover ".region-content .generaltable td span" "css_element"
+    And I hover "#region-main .generaltable td span" "css_element"
     Then I should see "I'm the description"
     And "Grouping" "select" in the "region-main" "region" should be visible
     And "Group" "select" should be visible
@@ -26,8 +26,8 @@ Feature: Page contents assertions
     And "Change password" "link" should not be visible
     And I should see "Filter groups by"
     And I should not see "Filter groupssss by"
-    And I should see "Group members" in the ".region-content table th.c1" "css_element"
-    And I should not see "Group membersssss" in the ".region-content table th.c1" "css_element"
+    And I should see "Group members" in the "#region-main table th.c1" "css_element"
+    And I should not see "Group membersssss" in the "#region-main table th.c1" "css_element"
     And I follow "Groups"
     And the "#groupeditform #showcreateorphangroupform" "css_element" should be enabled
     And the "#groupeditform #showeditgroupsettingsform" "css_element" should be disabled
index 45d1ed6..063e198 100644 (file)
@@ -1,4 +1,4 @@
-@tool @tool_behat
+@tool @tool_behat @_only_local
 Feature: Set up contextual data for tests
   In order to write tests quickly
   As a developer
@@ -143,13 +143,56 @@ Feature: Set up contextual data for tests
       | fullname | shortname |
       | Course 1 | C1 |
     And the following "activities" exists:
-      | activity | name | intro | course | idnumber |
-      | assign   | Test assignment name | Test assignment description | C1 | assign1 |
-      | data     | Test database name | Test database description | C1 | data1 |
+      | activity   | name                   | intro                         | course | idnumber    |
+      | assign     | Test assignment name   | Test assignment description   | C1     | assign1     |
+      | assignment | Test assignment22 name | Test assignment22 description | C1     | assignment1 |
+      | book       | Test book name         | Test book description         | C1     | book1       |
+      | chat       | Test chat name         | Test chat description         | C1     | chat1       |
+      | choice     | Test choice name       | Test choice description       | C1     | choice1     |
+      | data       | Test database name     | Test database description     | C1     | data1       |
+      | feedback   | Test feedback name     | Test feedback description     | C1     | feedback1   |
+      | folder     | Test folder name       | Test folder description       | C1     | folder1     |
+      | forum      | Test forum name        | Test forum description        | C1     | forum1      |
+      | glossary   | Test glossary name     | Test glossary description     | C1     | glossary1   |
+      | imscp      | Test imscp name        | Test imscp description        | C1     | imscp1      |
+      | label      | Test label name        | Test label description        | C1     | label1      |
+      | lesson     | Test lesson name       | Test lesson description       | C1     | lesson1     |
+      | lti        | Test lti name          | Test lti description          | C1     | lti1        |
+      | page       | Test page name         | Test page description         | C1     | page1       |
+      | quiz       | Test quiz name         | Test quiz description         | C1     | quiz1       |
+      | resource   | Test resource name     | Test resource description     | C1     | resource1   |
+      | scorm      | Test scorm name        | Test scorm description        | C1     | scorm1      |
+      | survey     | Test survey name       | Test survey description       | C1     | survey1     |
+      | url        | Test url name          | Test url description          | C1     | url1        |
+      | wiki       | Test wiki name         | Test wiki description         | C1     | wiki1       |
+      | workshop   | Test workshop name     | Test workshop description     | C1     | workshop1   |
     When I log in as "admin"
     And I follow "Course 1"
     Then I should see "Test assignment name"
+    # Assignment 2.2 module type is disabled by default
+    # And I should see "Test assignment22 name"
+    And I should see "Test book name"
+    And I should see "Test chat name"
+    And I should see "Test choice name"
     And I should see "Test database name"
+    # Feedback module type is disabled by default
+    # And I should see "Test feedback name"
+    And I should see "Test folder name"
+    And I should see "Test forum name"
+    And I should see "Test glossary name"
+    And I should see "Test imscp name"
+    # We don't see label name, we see only description:
+    And I should see "Test label description"
+    And I should see "Test lesson name"
+    And I should see "Test lti name"
+    And I should see "Test page name"
+    And I should see "Test quiz name"
+    And I should see "Test resource name"
+    And I should see "Test scorm name"
+    And I should see "Test survey name"
+    And I should see "Test url name"
+    And I should see "Test wiki name"
+    And I should see "Test workshop name"
     And I follow "Test assignment name"
     And I should see "Test assignment description"
 
index 9aa1c88..664bbd6 100644 (file)
@@ -32,5 +32,5 @@ Feature: Forms manipulation
     Then I should see "Close the quiz"
     And I should see "Group mode"
     And I should see "Grouping"
-    And I should not see "Show more..." in the "region-main-box" "region"
+    And I should not see "Show more..." in the "region-main" "region"
     And I should see "Show less..."
index b750aa7..12b96cd 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013110500;
+$plugin->version   = 2013110501;
 $plugin->requires  = 2013110500; // Requires Moodle 2.5.
 $plugin->component = 'tool_behat';
index 08cd8b3..b27a7ce 100644 (file)
@@ -74,9 +74,15 @@ function tool_capability_calculate_role_data($capability, array $roles) {
     // Put the contexts into a tree structure.
     foreach ($contexts as $conid => $con) {
         $context = context::instance_by_id($conid);
-        $parentcontext = $context->get_parent_context();
-        if ($parentcontext) {
-            $contexts[$parentcontext->id]->children[] = $conid;
+        try {
+            $parentcontext = $context->get_parent_context();
+            if ($parentcontext) { // Will be false if $context is the system context.
+                $contexts[$parentcontext->id]->children[] = $conid;
+            }
+        } catch (dml_missing_record_exception $e) {
+            // Ignore corrupt context tree structure here. Don't let it break
+            // showing the rest of the report.
+            continue;
         }
     }
 
index 586725a..dde7c3e 100644 (file)
@@ -87,7 +87,7 @@ class tool_capability_renderer extends plugin_renderer_base {
         $table->attributes['class'] = 'comparisontable';
         $table->head = array('&nbsp;');
         foreach ($roles as $role) {
-            $url = new moodle_url('/admin/roles/override.php', array('contextid' => $contextid, 'roleid' => $role->id));
+            $url = new moodle_url('/admin/roles/define.php', array('action' => 'view', 'roleid' => $role->id));
             $table->head[] = html_writer::div(html_writer::link($url, $role->localname));
         }
         $table->data = array();
index 63d8fb1..4832c0f 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 431a911..4d120ff 100644 (file)
@@ -55,7 +55,7 @@ if ($action === 'checkout') {
     $progressbar->create();         // prints the HTML code of the progress bar
 
     // we may need a bit of extra execution time and memory here
-    @set_time_limit(HOURSECS);
+    core_php_time_limit::raise(HOURSECS);
     raise_memory_limit(MEMORY_EXTRA);
     tool_customlang_utils::checkout($lng, $progressbar);
 
index c7e622f..f37f62a 100644 (file)
@@ -50,7 +50,7 @@ require_once($CFG->libdir.'/dtllib.php');
  * @return does not return, calls die()
  */
 function tool_dbtransfer_export_xml_database($description, $mdb) {
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     \core\session\manager::write_close(); // Release session.
 
@@ -77,7 +77,7 @@ function tool_dbtransfer_export_xml_database($description, $mdb) {
  * @return void
  */
 function tool_dbtransfer_transfer_database(moodle_database $sourcedb, moodle_database $targetdb, progress_trace $feedback = null) {
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     \core\session\manager::write_close(); // Release session.
 
index da931db..4299599 100644 (file)
@@ -177,7 +177,7 @@ abstract class tool_generator_backend {
 
         // Update time limit so PHP doesn't time out.
         if (!CLI_SCRIPT) {
-            set_time_limit(120);
+            core_php_time_limit::raise(120);
         }
     }
 
index 06f4f6a..6e99834 100644 (file)
@@ -57,7 +57,7 @@ if (data_submitted() and $confirm and confirm_sesskey()) {
 
     echo $OUTPUT->notification('Please be patient and wait for this to complete...', 'notifysuccess');
 
-    set_time_limit(0);
+    core_php_time_limit::raise();
 
     foreach ($rs as $table) {
         $DB->set_debug(true);
index 82b5cc2..36b416f 100644 (file)
@@ -69,7 +69,7 @@ $notice_ok    = array();
 $notice_error = array();
 
 if (($mode == INSTALLATION_OF_SELECTED_LANG) and confirm_sesskey() and !empty($pack)) {
-    set_time_limit(0);
+    core_php_time_limit::raise();
     make_temp_directory('');
     make_upload_directory('lang');
 
@@ -125,7 +125,7 @@ if ($mode == DELETION_OF_SELECTED_LANG and !empty($uninstalllang)) {
 }
 
 if ($mode == UPDATE_ALL_LANG) {
-    set_time_limit(0);
+    core_php_time_limit::raise();
 
     $installer = new lang_installer();
 
index 04dd691..6b4ce5c 100644 (file)
@@ -59,7 +59,7 @@ echo $OUTPUT->box_start();
 
 /// Turn off time limits, sometimes upgrades can be slow.
 
-@set_time_limit(0);
+core_php_time_limit::raise();
 
 echo '<strong>Progress:</strong>';
 $i = 0;
index f52bf6b..b565ae1 100644 (file)
@@ -38,7 +38,7 @@ if (!$CFG->debugdeveloper) {
     error('Not available on production sites, sorry.');
 }
 
-set_time_limit(60*30);
+core_php_time_limit::raise(60*30);
 
 $oldcwd = getcwd();
 $code = 0;
index 0835270..7ae6017 100644 (file)
@@ -178,13 +178,47 @@ function search_spammers($keywords) {
     $conditions6 = '( '.implode(' OR ', $keywordfull6).' )';
     $conditions7 = '( '.implode(' OR ', $keywordfull7).' )';
 
-    $sql  = "SELECT * FROM {user} WHERE deleted = 0 AND id <> :userid AND $conditions";  // Exclude oneself
-    $sql2 = "SELECT u.*, p.summary FROM {user} AS u, {post} AS p WHERE $conditions2 AND u.deleted = 0 AND u.id=p.userid AND u.id <> :userid";
-    $sql3 = "SELECT u.*, p.subject as postsubject FROM {user} AS u, {post} AS p WHERE $conditions3 AND u.deleted = 0 AND u.id=p.userid AND u.id <> :userid";
-    $sql4 = "SELECT u.*, c.content FROM {user} AS u, {comments} AS c WHERE $conditions4 AND u.deleted = 0 AND u.id=c.userid AND u.id <> :userid";
-    $sql5 = "SELECT u.*, m.fullmessage FROM {user} AS u, {message} AS m WHERE $conditions5 AND u.deleted = 0 AND u.id=m.useridfrom AND u.id <> :userid";
-    $sql6 = "SELECT u.*, fp.message FROM {user} AS u, {forum_posts} AS fp WHERE $conditions6 AND u.deleted = 0 AND u.id=fp.userid AND u.id <> :userid";
-    $sql7 = "SELECT u.*, fp.subject FROM {user} AS u, {forum_posts} AS fp WHERE $conditions7 AND u.deleted = 0 AND u.id=fp.userid AND u.id <> :userid";
+    $sql  = "SELECT *
+               FROM {user}
+              WHERE deleted = 0
+                    AND id <> :userid
+                    AND $conditions";  // Exclude oneself
+    $sql2 = "SELECT u.*, p.summary
+               FROM {user} u, {post} p
+              WHERE $conditions2
+                    AND u.deleted = 0
+                    AND u.id=p.userid
+                    AND u.id <> :userid";
+    $sql3 = "SELECT u.*, p.subject AS postsubject
+               FROM {user} u, {post} p
+              WHERE $conditions3
+                    AND u.deleted = 0
+                    AND u.id=p.userid
+                    AND u.id <> :userid";
+    $sql4 = "SELECT u.*, c.content
+               FROM {user} u, {comments} c
+               WHERE $conditions4
+                    AND u.deleted = 0
+                    AND u.id=c.userid
+                    AND u.id <> :userid";
+    $sql5 = "SELECT u.*, m.fullmessage
+               FROM {user} u, {message} m
+              WHERE $conditions5
+                    AND u.deleted = 0
+                    AND u.id=m.useridfrom
+                    AND u.id <> :userid";
+    $sql6 = "SELECT u.*, fp.message
+               FROM {user} u, {forum_posts} fp
+              WHERE $conditions6
+                    AND u.deleted = 0
+                    AND u.id=fp.userid
+                    AND u.id <> :userid";
+    $sql7 = "SELECT u.*, fp.subject
+               FROM {user} u, {forum_posts} fp
+              WHERE $conditions7
+                    AND u.deleted = 0
+                    AND u.id=fp.userid
+                    AND u.id <> :userid";
 
     $spamusers_desc = $DB->get_recordset_sql($sql, $params);
     $spamusers_blog = $DB->get_recordset_sql($sql2, $params);
index 7113ded..8963f7e 100644 (file)
@@ -195,7 +195,7 @@ class tool_uploadcourse_processor {
         $errors = 0;
 
         // We will most certainly need extra time and memory to process big files.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Loop over the CSV lines.
@@ -335,7 +335,7 @@ class tool_uploadcourse_processor {
         $tracker->start();
 
         // We might need extra time and memory depending on the number of rows to preview.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Loop over the CSV lines.
index 8f86212..fd33545 100644 (file)
@@ -651,9 +651,6 @@ class tool_uploadcourse_course_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_restore_file() {
@@ -703,9 +700,6 @@ class tool_uploadcourse_course_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_restore_invalid_file() {
index ae42a1e..41a838c 100644 (file)
@@ -204,9 +204,6 @@ class tool_uploadcourse_helper_testcase extends advanced_testcase {
         $this->assertEquals($dir, $dir2);
 
         $CFG->keeptempdirectoriesonbackup = $oldcfg;
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_get_role_ids() {
index 648fcc6..1dd20dc 100644 (file)
@@ -96,9 +96,6 @@ class tool_uploadcourse_processor_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_restore_restore_file() {
@@ -136,9 +133,6 @@ class tool_uploadcourse_processor_testcase extends advanced_testcase {
             }
         }
         $this->assertTrue($found);
-
-        // Restore the time limit to prevent warning.
-        set_time_limit(0);
     }
 
     public function test_shortname_template() {
index 82f7ccc..a45cb47 100644 (file)
@@ -36,7 +36,7 @@ require_once('user_form.php');
 $iid         = optional_param('iid', '', PARAM_INT);
 $previewrows = optional_param('previewrows', 10, PARAM_INT);
 
-@set_time_limit(60*60); // 1 hour should be enough
+core_php_time_limit::raise(60*60); // 1 hour should be enough
 raise_memory_limit(MEMORY_HUGE);
 
 require_login();
@@ -123,12 +123,11 @@ if (empty($iid)) {
         $content = $mform1->get_file_content('userfile');
 
         $readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter_name);
+        $csvloaderror = $cir->get_error();
         unset($content);
 
-        if ($readcount === false) {
-            print_error('csvloaderror', '', $returnurl);
-        } else if ($readcount == 0) {
-            print_error('csvemptyfile', 'error', $returnurl);
+        if (!is_null($csvloaderror)) {
+            print_error('csvloaderror', '', $returnurl, $csvloaderror);
         }
         // test if columns ok
         $filecolumns = uu_validate_user_upload_columns($cir, $STD_FIELDS, $PRF_FIELDS, $returnurl);
@@ -235,10 +234,10 @@ if ($formdata = $mform2->is_cancelled()) {
                     $user->$key['text']   = $value;
                     $user->$key['format'] = FORMAT_MOODLE;
                 } else {
-                    $user->$key = $value;
+                    $user->$key = trim($value);
                 }
             } else {
-                $user->$key = $value;
+                $user->$key = trim($value);
             }
 
             if (in_array($key, $upt->columns)) {
@@ -550,8 +549,6 @@ if ($formdata = $mform2->is_cancelled()) {
                         continue;
                     }
                     if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
-                        // this should never happen
-                        debugging("Could not find $column on the user objects", DEBUG_DEVELOPER);
                         continue;
                     }
                     if ($updatetype == UU_UPDATE_MISSING) {
@@ -966,7 +963,7 @@ if ($formdata = $mform2->is_cancelled()) {
                     $status = null;
 
                     if (isset($user->{'enrolstatus'.$i})) {
-                        $enrolstatus = trim($user->{'enrolstatus'.$i});
+                        $enrolstatus = $user->{'enrolstatus'.$i};
                         if ($enrolstatus == '') {
                             $status = null;
                         } else if ($enrolstatus === (string)ENROL_USER_ACTIVE) {
@@ -1109,7 +1106,7 @@ while ($linenum <= $previewrows and $fields = $cir->next()) {
     $rowcols = array();
     $rowcols['line'] = $linenum;
     foreach($fields as $key => $field) {
-        $rowcols[$filecolumns[$key]] = s($field);
+        $rowcols[$filecolumns[$key]] = s(trim($field));
     }
     $rowcols['status'] = array();
 
@@ -1135,7 +1132,7 @@ while ($linenum <= $previewrows and $fields = $cir->next()) {
     }
 
     if (isset($rowcols['city'])) {
-        $rowcols['city'] = trim($rowcols['city']);
+        $rowcols['city'] = $rowcols['city'];
     }
     // Check if rowcols have custom profile field with correct data and update error state.
     $noerror = uu_check_custom_profile_data($rowcols) && $noerror;
index fe3d8e5..03091aa 100644 (file)
@@ -72,7 +72,7 @@ if ($formdata = $mform->get_data()) {
         // Large files are likely to take their time and memory. Let PHP know
         // that we'll take longer, and that the process should be recycled soon
         // to free up memory.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Create a unique temporary directory, to process the zip file
index 67e886f..128b502 100644 (file)
@@ -53,6 +53,8 @@ class check_foreign_keys extends XMLDBCheckAction {
             'noviolatedforeignkeysfound' => 'tool_xmldb',
             'violatedforeignkeysfound' => 'tool_xmldb',
             'violations' => 'tool_xmldb',
+            'unknowntable' => 'tool_xmldb',
+            'unknownfield' => 'tool_xmldb',
         ));
     }
 
@@ -75,6 +77,19 @@ class check_foreign_keys extends XMLDBCheckAction {
                 }
                 $o.='            <li>' . $this->str['key'] . ': ' . $xmldb_key->readableInfo() . ' ';
 
+                $reftable = $xmldb_key->getRefTable();
+                if (!$dbman->table_exists($reftable)) {
+                    $o.='<font color="red">' . $this->str['unknowntable'] . '</font>';
+                    // Add the missing index to the list
+                    $violation = new stdClass();
+                    $violation->string = 'fkunknowntable';
+                    $violation->table = $xmldb_table;
+                    $violation->key = $xmldb_key;
+                    $violation->reftable = $reftable;
+                    $violatedkeys[] = $violation;
+                    continue;
+                }
+
                 // Work out the SQL to find key violations.
                 $keyfields = $xmldb_key->getFields();
                 $reffields = $xmldb_key->getRefFields();
@@ -82,6 +97,19 @@ class check_foreign_keys extends XMLDBCheckAction {
                 $nullnessconditions = array();
                 $params = array();
                 foreach ($keyfields as $i => $field) {
+                    if (!$dbman->field_exists($reftable, $reffields[$i])) {
+                        $o.='<font color="red">' . $this->str['unknownfield'] . '</font>';
+                        // Add the missing index to the list
+                        $violation = new stdClass();
+                        $violation->string = 'fkunknownfield';
+                        $violation->table = $xmldb_table;
+                        $violation->key = $xmldb_key;
+                        $violation->reftable = $reftable;
+                        $violation->reffield = $reffields[$i];
+                        $violatedkeys[] = $violation;
+                        continue 2;
+                    }
+
                     $joinconditions[] = 't1.' . $field . ' = t2.' . $reffields[$i];
                     $xmldb_field = $xmldb_table->getField($field);
                     $default = $xmldb_field->getDefault();
@@ -97,7 +125,7 @@ class check_foreign_keys extends XMLDBCheckAction {
                 }
                 $nullnessconditions[] = 't2.id IS NULL';
                 $sql = 'SELECT count(1) FROM {' . $xmldb_table->getName() .
-                        '} t1 LEFT JOIN {' . $xmldb_key->getRefTable() . '} t2 ON ' .
+                        '} t1 LEFT JOIN {' . $reftable . '} t2 ON ' .
                         implode(' AND ', $joinconditions) . ' WHERE ' .
                         implode(' AND ', $nullnessconditions);
 
@@ -109,6 +137,7 @@ class check_foreign_keys extends XMLDBCheckAction {
                     $o.='<font color="red">' . $this->str['violations'] . '</font>';
                     // Add the missing index to the list
                     $violation = new stdClass;
+                    $violation->string = 'fkviolationdetails';
                     $violation->table = $xmldb_table;
                     $violation->key = $xmldb_key;
                     $violation->numviolations = $violations;
@@ -145,8 +174,11 @@ class check_foreign_keys extends XMLDBCheckAction {
                 $violation->tablename = $violation->table->getName();
                 $violation->keyname = $violation->key->getName();
 
-                $r.= '            <li>' .get_string('fkviolationdetails', 'tool_xmldb', $violation) .
-                        '<pre>' . s($violation->sql) . '; ' . s($violation->sqlparams) . '</pre></li>';
+                $r.= '            <li>' .get_string($violation->string, 'tool_xmldb', $violation);
+                if (!empty($violation->sql)) {
+                    $r.= '<pre>' . s($violation->sql) . '; ' . s($violation->sqlparams) . '</pre>';
+                }
+                $r.= '</li>';
             }
             $r.= '        </ul>';
         } else {
index 040cf3a..f89293f 100644 (file)
@@ -106,6 +106,8 @@ $string['fieldsusedinindex'] = 'This field is used as index';
 $string['fieldsusedinkey'] = 'This field is used as key.';
 $string['filemodifiedoutfromeditor'] = 'Warning: File locally modified while using the XMLDB Editor. Saving will overwrite local changes.';
 $string['filenotwriteable'] = 'File not writeable';
+$string['fkunknownfield'] = 'Foreign key {$a->keyname} on table {$a->tablename} points to a non-existent field {$a->reffield} in referenced table {$a->reftable}.';
+$string['fkunknowntable'] = 'Foreign key {$a->keyname} on table {$a->tablename} points to a non-existent table {$a->reftable}.';
 $string['fkviolationdetails'] = 'Foreign key {$a->keyname} on table {$a->tablename} is violated by {$a->numviolations} out of {$a->numrows} rows.';
 $string['floatincorrectdecimals'] = 'Incorrect number of decimals for float field';
 $string['floatincorrectlength'] = 'Incorrect length for float field';
@@ -183,6 +185,8 @@ $string['selecttable'] = 'Select table:';
 $string['table'] = 'Table';
 $string['tablenameempty'] = 'The table name cannot be empty';
 $string['tables'] = 'Tables';
+$string['unknownfield'] = 'Refers to an unknown field';
+$string['unknowntable'] = 'Refers to an unknown table';
 $string['unload'] = 'Unload';
 $string['up'] = 'Up';
 $string['view'] = 'View';
index b82fcc2..d4e1414 100644 (file)
@@ -49,5 +49,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2013091700, 'auth', 'cas');
     }
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 333142a..d2087f7 100644 (file)
@@ -664,7 +664,7 @@ class auth_plugin_db extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        if ($this->is_internal()) {
+        if ($this->is_internal() || empty($this->config->changepasswordurl)) {
             // Standard form.
             return null;
         } else {
index 442f93e..ede7600 100644 (file)
@@ -120,7 +120,11 @@ class auth_plugin_imap extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        return new moodle_url($this->config->changepasswordurl);
+        if (!empty($this->config->changepasswordurl)) {
+            return new moodle_url($this->config->changepasswordurl);
+        } else {
+            return null;
+        }
     }
 
     /**
index f92c08d..a3fc3a8 100644 (file)
@@ -1603,7 +1603,11 @@ class auth_plugin_ldap extends auth_plugin_base {
      */
     function change_password_url() {
         if (empty($this->config->stdchangepassword)) {
-            return new moodle_url($this->config->changepasswordurl);
+            if (!empty($this->config->changepasswordurl)) {
+                return new moodle_url($this->config->changepasswordurl);
+            } else {
+                return null;
+            }
         } else {
             return null;
         }
index b118fa9..641357b 100644 (file)
@@ -39,5 +39,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2013052100, 'auth', 'ldap');
     }
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 41bfeb9..0170124 100644 (file)
@@ -44,5 +44,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index b2d49e2..14a8c56 100644 (file)
@@ -44,5 +44,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 6b4bdff..5e25aa9 100644 (file)
@@ -120,7 +120,11 @@ class auth_plugin_pop3 extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        return new moodle_url($this->config->changepasswordurl);
+        if (!empty($this->config->changepasswordurl)) {
+            return new moodle_url($this->config->changepasswordurl);
+        } else {
+            return null;
+        }
     }
 
     /**
index 6085397..8334fed 100644 (file)
@@ -1,6 +1,10 @@
 This files describes API changes in /auth/* - plugins,
 information provided here is intended especially for developers.
 
+=== 2.7 ===
+
+* If you are returning a url in method change_password_url() from config, please make sure it is set before trying to use it.
+
 === 2.6 ===
 
 * can_be_manually_set() - This function was introduced in the base class and returns false by default. If overriden by
index 6794645..82f59f1 100644 (file)
@@ -126,8 +126,8 @@ abstract class backup implements checksumable {
     const OPERATION_RESTORE ='restore';// We are performing one restore
 
     // Version (to keep CFG->backup_version (and release) updated automatically)
-    const VERSION = 2013110500;
-    const RELEASE = '2.6';
+    const VERSION = 2013111800;
+    const RELEASE = '2.7';
 }
 
 /*
index f38d263..345831c 100644 (file)
@@ -307,7 +307,7 @@ class backup_controller extends base_controller {
      */
     public function execute_plan() {
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         // If this is not a course backup, inform the plan we are not
         // including all the activities for sure. This will affect any
index 3349a18..f4d3958 100644 (file)
@@ -315,7 +315,7 @@ class restore_controller extends base_controller {
 
     public function execute_plan() {
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         // If this is not a course restore, inform the plan we are not
         // including all the activities for sure. This will affect any
@@ -349,7 +349,7 @@ class restore_controller extends base_controller {
             throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status);
         }
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         $this->precheck = restore_prechecks_helper::execute_prechecks($this, $droptemptablesafter);
         if (!array_key_exists('errors', $this->precheck)) { // No errors, can be executed
@@ -420,7 +420,7 @@ class restore_controller extends base_controller {
         require_once($CFG->dirroot . '/backup/util/helper/convert_helper.class.php');
 
         // Basic/initial prevention against time/memory limits
-        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
         raise_memory_limit(MEMORY_EXTRA);
         $this->progress->start_progress('Backup format conversion');
 
index 914e4ab..6e13b42 100644 (file)
@@ -111,7 +111,7 @@ abstract class backup_cron_automated_helper {
             cron_trace_time_and_memory();
 
             // This could take a while!
-            @set_time_limit(0);
+            core_php_time_limit::raise();
             raise_memory_limit(MEMORY_EXTRA);
 
             $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup($admin->timezone, $now);
index ed7a70b..44afd8d 100644 (file)
@@ -132,9 +132,6 @@ class backup_step_testcase extends advanced_testcase {
 
         // Remove the test dir and any content
         @remove_dir(dirname($file));
-
-        // Clear the time limit, otherwise PHPUnit complains.
-        set_time_limit(0);
     }
 
     /**
index d4a5012..7204e58 100644 (file)
@@ -31,9 +31,14 @@ abstract class core_backup_progress {
     const INDETERMINATE = -1;
 
     /**
+     * This value is set rather high to ensure there are no regressions from
+     * previous behaviour. For testing, it may be useful to set the
+     * frontendservertimeout config option to a lower value, such as 180
+     * seconds (default for some commercial products).
+     *
      * @var int The number of seconds that can pass without progress() calls.
      */
-    const TIME_LIMIT_WITHOUT_PROGRESS = 120;
+    const TIME_LIMIT_WITHOUT_PROGRESS = 3600;
 
     /**
      * @var int Time of last progress call.
@@ -115,7 +120,6 @@ abstract class core_backup_progress {
         $this->currents[] = 0;
         $this->parentcounts[] = $parentcount;
         $this->update_progress();
-        $lastprogresstime = $this->get_time();
     }
 
     /**
@@ -202,7 +206,9 @@ abstract class core_backup_progress {
         // Update progress.
         $this->count++;
         $this->lastprogresstime = $now;
-        set_time_limit(self::TIME_LIMIT_WITHOUT_PROGRESS);
+
+        // Update time limit before next progress display.
+        core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
         $this->update_progress();
     }
 
index 28f9f03..1e4f8c8 100644 (file)
@@ -56,9 +56,11 @@ class backup_progress_testcase extends basic_testcase {
 
         // Make some progress and check that the time limit gets added.
         $progress->step_time();
+        core_php_time_limit::get_and_clear_unit_test_data();
         $progress->progress(2);
         $this->assertTrue($progress->was_update_called());
-        $this->assertEquals(120, ini_get('max_execution_time'));
+        $this->assertEquals(array(core_backup_progress::TIME_LIMIT_WITHOUT_PROGRESS),
+                core_php_time_limit::get_and_clear_unit_test_data());
 
         // Check the new value.
         $this->assert_min_max(0.2, 0.2, $progress);
@@ -77,9 +79,6 @@ class backup_progress_testcase extends basic_testcase {
 
         // There was 1 progress call.
         $this->assertEquals(1, $progress->get_progress_count());
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
@@ -158,8 +157,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.8, 0.8, $progress);
         $progress->end_progress();
         $this->assertFalse($progress->is_in_progress_section());
-
-        set_time_limit(0);
     }
 
     /**
@@ -192,8 +189,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.4, 1.0, $progress);
         $progress->end_progress();
         $this->assert_min_max(1.0, 1.0, $progress);
-
-        set_time_limit(0);
     }
 
     /**
@@ -208,9 +203,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.01, 0.01, $progress);
         $progress->end_progress();
         $this->assert_min_max(0.01, 0.01, $progress);
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
@@ -231,8 +223,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.02, 0.02, $progress);
         $progress->end_progress();
         $this->assert_min_max(0.02, 0.02, $progress);
-
-        set_time_limit(0);
     }
 
     /**
@@ -329,9 +319,6 @@ class backup_progress_testcase extends basic_testcase {
         } catch (coding_exception $e) {
             $this->assertEquals(1, preg_match('~would exceed max~', $e->getMessage()));
         }
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
index a252ec3..762ea70 100644 (file)
@@ -73,6 +73,14 @@ abstract class base_moodleform extends moodleform {
      */
     function __construct(base_ui_stage $uistage, $action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true) {
         $this->uistage = $uistage;
+        // Add a class to the attributes to prevent the default collapsible behaviour.
+        if (!$attributes) {
+            $attributes = array();
+        }
+        $attributes['class'] = 'unresponsive';
+        if (!isset($attributes['enctype'])) {
+            $attributes['enctype'] = 'application/x-www-form-urlencoded'; // Enforce compatibility with our max_input_vars hack.
+        }
         parent::__construct($action, $customdata, $method, $target, $attributes, $editable);
     }
     /**
index 6275e60..ef559fd 100644 (file)
@@ -846,6 +846,7 @@ class restore_ui_stage_process extends restore_ui_stage {
         $html .= html_writer::start_tag('form', array(
             'action'    => $url->out_omit_querystring(),
             'class'     => 'backup-restore',
+            'enctype'   => 'application/x-www-form-urlencoded', // Enforce compatibility with our max_input_vars hack.
             'method'    => 'post'));
         foreach ($url->params() as $name => $value) {
             $html .= html_writer::empty_tag('input', array(
diff --git a/blocks/activity_modules/tests/behat/block_activity_modules.feature b/blocks/activity_modules/tests/behat/block_activity_modules.feature
new file mode 100644 (file)
index 0000000..ba76908
--- /dev/null
@@ -0,0 +1,176 @@
+@block @block_activity_modules @_only_local
+Feature: Block activity modules
+  In order to overview activity modules in a course
+  As a manager
+  I can add activities block in a course or on the frontpage
+
+  Background:
+    Given I log in as "admin"
+    And I expand "Site administration" node
+    And I expand "Plugins" node
+    And I expand "Activity modules" node
+    And I follow "Manage activities"
+    And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Feedback" "table_row"
+    And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Assignment (2.2)" "table_row"
+
+  Scenario: Add activities block on the frontpage
+    And the following "activities" exists:
+      | activity   | name                        | intro                              | course               | idnumber    |
+      | assign     | Frontpage assignment name   | Frontpage assignment description   | Acceptance test site | assign0     |
+      | assignment | Frontpage assignment22 name | Frontpage assignment22 description | Acceptance test site | assignment0 |
+      | book       | Frontpage book name         | Frontpage book description         | Acceptance test site | book0       |
+      | chat       | Frontpage chat name         | Frontpage chat description         | Acceptance test site | chat0       |
+      | choice     | Frontpage choice name       | Frontpage choice description       | Acceptance test site | choice0     |
+      | data       | Frontpage database name     | Frontpage database description     | Acceptance test site | data0       |
+      | feedback   | Frontpage feedback name     | Frontpage feedback description     | Acceptance test site | feedback0   |
+      | forum      | Frontpage forum name        | Frontpage forum description        | Acceptance test site | forum0      |
+      | label      | Frontpage label name        | Frontpage label description        | Acceptance test site | label0      |
+      | lti        | Frontpage lti name          | Frontpage lti description          | Acceptance test site | lti0        |
+      | page       | Frontpage page name         | Frontpage page description         | Acceptance test site | page0       |
+      | quiz       | Frontpage quiz name         | Frontpage quiz description         | Acceptance test site | quiz0       |
+      | resource   | Frontpage resource name     | Frontpage resource description     | Acceptance test site | resource0   |
+      | imscp      | Frontpage imscp name        | Frontpage imscp description        | Acceptance test site | imscp0      |
+      | folder     | Frontpage folder name       | Frontpage folder description       | Acceptance test site | folder0     |
+      | glossary   | Frontpage glossary name     | Frontpage glossary description     | Acceptance test site | glossary0   |
+      | scorm      | Frontpage scorm name        | Frontpage scorm description        | Acceptance test site | scorm0      |
+      | lesson     | Frontpage lesson name       | Frontpage lesson description       | Acceptance test site | lesson0     |
+      | survey     | Frontpage survey name       | Frontpage survey description       | Acceptance test site | survey0     |
+      | url        | Frontpage url name          | Frontpage url description          | Acceptance test site | url0        |
+      | wiki       | Frontpage wiki name         | Frontpage wiki description         | Acceptance test site | wiki0       |
+      | workshop   | Frontpage workshop name     | Frontpage workshop description     | Acceptance test site | workshop0   |
+
+    And I am on homepage
+    When I follow "Turn editing on"
+    And I add the "Activities" block
+    And I click on "Assignments" "link" in the "Activities" "block"
+    Then I should see "Frontpage assignment name"
+    And I am on homepage
+    And I click on "Assignments (2.2)" "link" in the "Activities" "block"
+    And I should see "Frontpage assignment22 name"
+    And I am on homepage
+    And I click on "Chats" "link" in the "Activities" "block"
+    And I should see "Frontpage chat name"
+    And I am on homepage
+    And I click on "Choices" "link" in the "Activities" "block"
+    And I should see "Frontpage choice name"
+    And I am on homepage
+    And I click on "Databases" "link" in the "Activities" "block"
+    And I should see "Frontpage database name"
+    And I am on homepage
+    And I click on "Feedback" "link" in the "Activities" "block"
+    And I should see "Frontpage feedback name"
+    And I am on homepage
+    And I click on "Forums" "link" in the "Activities" "block"
+    And I should see "Frontpage forum name"
+    And I am on homepage
+    And I click on "External Tools" "link" in the "Activities" "block"
+    And I should see "Frontpage lti name"
+    And I am on homepage
+    And I click on "Quizzes" "link" in the "Activities" "block"
+    And I should see "Frontpage quiz name"
+    And I am on homepage
+    And I click on "Glossaries" "link" in the "Activities" "block"
+    And I should see "Frontpage glossary name"
+    And I am on homepage
+    And I click on "SCORM packages" "link" in the "Activities" "block"
+    And I should see "Frontpage scorm name"
+    And I am on homepage
+    And I click on "Lessons" "link" in the "Activities" "block"
+    And I should see "Frontpage lesson name"
+    And I am on homepage
+    And I click on "Wikis" "link" in the "Activities" "block"
+    And I should see "Frontpage wiki name"
+    And I am on homepage
+    And I click on "Workshop" "link" in the "Activities" "block"
+    And I should see "Frontpage workshop name"
+    And I am on homepage
+    And I click on "Resources" "link" in the "Activities" "block"
+    And I should see "Frontpage book name"
+    And I should see "Frontpage page name"
+    And I should see "Frontpage resource name"
+    And I should see "Frontpage imscp name"
+    And I should see "Frontpage folder name"
+    And I should see "Frontpage url name"
+
+  Scenario: Add activities block in a course
+    Given the following "courses" exists:
+      | fullname | shortname | format |
+      | Course 1 | C1        | topics |
+    And the following "activities" exists:
+      | activity   | name                   | intro                         | course | idnumber    |
+      | assign     | Test assignment name   | Test assignment description   | C1     | assign1     |
+      | assignment | Test assignment22 name | Test assignment22 description | C1     | assignment1 |
+      | book       | Test book name         | Test book description         | C1     | book1       |
+      | chat       | Test chat name         | Test chat description         | C1     | chat1       |
+      | choice     | Test choice name       | Test choice description       | C1     | choice1     |
+      | data       | Test database name     | Test database description     | C1     | data1       |
+      | feedback   | Test feedback name     | Test feedback description     | C1     | feedback1   |
+      | folder     | Test folder name       | Test folder description       | C1     | folder1     |
+      | forum      | Test forum name        | Test forum description        | C1     | forum1      |
+      | glossary   | Test glossary name     | Test glossary description     | C1     | glossary1   |
+      | imscp      | Test imscp name        | Test imscp description        | C1     | imscp1      |
+      | label      | Test label name        | Test label description        | C1     | label1      |
+      | lesson     | Test lesson name       | Test lesson description       | C1     | lesson1     |
+      | lti        | Test lti name          | Test lti description          | C1     | lti1        |
+      | page       | Test page name         | Test page description         | C1     | page1       |
+      | quiz       | Test quiz name         | Test quiz description         | C1     | quiz1       |
+      | resource   | Test resource name     | Test resource description     | C1     | resource1   |
+      | scorm      | Test scorm name        | Test scorm description        | C1     | scorm1      |
+      | survey     | Test survey name       | Test survey description       | C1     | survey1     |
+      | url        | Test url name          | Test url description          | C1     | url1        |
+      | wiki       | Test wiki name         | Test wiki description         | C1     | wiki1       |
+      | workshop   | Test workshop name     | Test workshop description     | C1     | workshop1   |
+
+    When I follow "Courses"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add the "Activities" block
+    And I click on "Assignments" "link" in the "Activities" "block"
+    Then I should see "Test assignment name"
+    And I follow "Course 1"
+    And I click on "Assignments (2.2)" "link" in the "Activities" "block"
+    And I should see "Test assignment22 name"
+    And I follow "Course 1"
+    And I click on "Chats" "link" in the "Activities" "block"
+    And I should see "Test chat name"
+    And I follow "Course 1"
+    And I click on "Choices" "link" in the "Activities" "block"
+    And I should see "Test choice name"
+    And I follow "Course 1"
+    And I click on "Databases" "link" in the "Activities" "block"
+    And I should see "Test database name"
+    And I follow "Course 1"
+    And I click on "Feedback" "link" in the "Activities" "block"
+    And I should see "Test feedback name"
+    And I follow "Course 1"
+    And I click on "Forums" "link" in the "Activities" "block"
+    And I should see "Test forum name"
+    And I follow "Course 1"
+    And I click on "External Tools" "link" in the "Activities" "block"
+    And I should see "Test lti name"
+    And I follow "Course 1"
+    And I click on "Quizzes" "link" in the "Activities" "block"
+    And I should see "Test quiz name"
+    And I follow "Course 1"
+    And I click on "Glossaries" "link" in the "Activities" "block"
+    And I should see "Test glossary name"
+    And I follow "Course 1"
+    And I click on "SCORM packages" "link" in the "Activities" "block"
+    And I should see "Test scorm name"
+    And I follow "Course 1"
+    And I click on "Lessons" "link" in the "Activities" "block"
+    And I should see "Test lesson name"
+    And I follow "Course 1"
+    And I click on "Wikis" "link" in the "Activities" "block"
+    And I should see "Test wiki name"
+    And I follow "Course 1"
+    And I click on "Workshop" "link" in the "Activities" "block"
+    And I should see "Test workshop name"
+    And I follow "Course 1"
+    And I click on "Resources" "link" in the "Activities" "block"
+    And I should see "Test book name"
+    And I should see "Test page name"
+    And I should see "Test resource name"
+    And I should see "Test imscp name"
+    And I should see "Test folder name"
+    And I should see "Test url name"
index 39a165d..2e56d5c 100644 (file)
@@ -58,5 +58,8 @@ function xmldb_block_community_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 0fc7927..d161a36 100644 (file)
@@ -64,5 +64,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
\ No newline at end of file
index aaf6570..d459982 100644 (file)
@@ -35,7 +35,14 @@ class block_course_list extends block_list {
 
         if (empty($CFG->disablemycourses) and isloggedin() and !isguestuser() and
           !(has_capability('moodle/course:update', context_system::instance()) and $adminseesall)) {    // Just print My Courses
-            if ($courses = enrol_get_my_courses(NULL, 'visible DESC, fullname ASC')) {
+            // As this is producing navigation sort order should default to $CFG->navsortmycoursessort instead
+            // of using the default.
+            if (!empty($CFG->navsortmycoursessort)) {
+                $sortorder = 'visible DESC, ' . $CFG->navsortmycoursessort . ' ASC';
+            } else {
+                $sortorder = 'visible DESC, sortorder ASC';
+            }
+            if ($courses = enrol_get_my_courses(NULL, $sortorder)) {
                 foreach ($courses as $course) {
                     $coursecontext = context_course::instance($course->id);
                     $linkcss = $course->visible ? "" : " class=\"dimmed\" ";
index d18e85e..932bf2d 100644 (file)
@@ -69,5 +69,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
\ No newline at end of file
index 7bf440e..ba0cbe2 100644 (file)
@@ -44,5 +44,8 @@ function xmldb_block_html_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 8037799..36b093f 100644 (file)
@@ -70,5 +70,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
\ No newline at end of file
index d0542ac..b6e6822 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js differ
index ce342d8..e474ba8 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js differ
index 55e4047..368a23d 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js differ
index 97e8bdc..0748b98 100644 (file)
@@ -402,6 +402,15 @@ Y.extend(TREE, Y.Base, TREE.prototype, {
                 }
                 return val;
             }
+        },
+        /**
+         * The navigation tree block instance.
+         */
+        instance : {
+            value : false,
+            setter : function(val) {
+                return parseInt(val);
+            }
         }
     }
 });
@@ -581,7 +590,7 @@ BRANCH.prototype = {
         } else {
             e.stopPropagation();
         }
-        if (e.type === 'actionkey' && e.action === 'enter' && e.target.test('A')) {
+        if ((e.type === 'actionkey' && e.action === 'enter') || e.target.test('a')) {
             // No ajaxLoad for enter.
             this.node.setAttribute('data-expandable', '0');
             this.node.setAttribute('data-loaded', '1');
@@ -638,6 +647,12 @@ BRANCH.prototype = {
         this.node.setAttribute('data-loaded', '1');
         try {
             var object = Y.JSON.parse(outcome.responseText);
+            if (object.error) {
+                Y.use('moodle-core-notification-ajaxException', function () {
+                    return new M.core.ajaxException(object).show();
+                });
+                return false;
+            }
             if (object.children && object.children.length > 0) {
                 var coursecount = 0;
                 for (var i in object.children) {
@@ -661,9 +676,16 @@ BRANCH.prototype = {
                 return true;
             }
             Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
-        } catch (ex) {
-            // If we got here then there was an error parsing the result.
-            Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+        } catch (error) {
+            if (outcome && outcome.status && outcome.status > 0) {
+                // If we got here then there was an error parsing the result.
+                Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+                Y.use('moodle-core-notification-exception', function () {
+                    return new M.core.exception(error).show();
+                });
+            }
+
+            return false;
         }
         // The branch is empty so class it accordingly
         this.node.replaceClass('branch', 'emptybranch');
index ed070a7..2f49298 100644 (file)
             mtrace('    ' . $rec->url . ' ', '');
             // Fetch the rss feed, using standard simplepie caching
             // so feeds will be renewed only if cache has expired
-            @set_time_limit(60);
+            core_php_time_limit::raise(60);
 
             $feed =  new moodle_simplepie();
             // set timeout for longer than normal to be agressive at
index 3c838d8..42a472c 100644 (file)
@@ -80,5 +80,8 @@ function xmldb_block_section_links_upgrade($oldversion, $block) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 962b991..1070048 100644 (file)
@@ -69,5 +69,8 @@ function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
\ No newline at end of file
index 305e513..e06f9bb 100644 (file)
@@ -70,5 +70,8 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
\ No newline at end of file
index 7f650c4..762bb64 100644 (file)
@@ -89,6 +89,20 @@ class block_site_main_menu extends block_list {
                 }
                 if (!$ismoving) {
                     $actions = course_get_cm_edit_actions($mod, -1);
+
+                    // Add the action move.
+                    $modcontext = context_module::instance($mod->id);
+                    $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
+                    if ($hasmanageactivities) {
+                        $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
+                        $actions['move'] = new action_menu_link_primary(
+                            new moodle_url($baseurl, array('copy' => $mod->id)),
+                            new pix_icon('t/move', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
+                            null,
+                            array('title' => get_string('move'))
+                        );
+                    }
+
                     $editbuttons = html_writer::tag('div',
                         $courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
                         array('class' => 'buttons')
index 6b1a793..d1a0371 100644 (file)
@@ -81,6 +81,7 @@ class block_tags extends block_base {
         }
 
         $this->content = new stdClass;
+        $this->content->text = '';
         $this->content->footer = '';
 
         // Get a list of tags.
index bc1c316..3812f69 100644 (file)
@@ -34,4 +34,4 @@ Feature: Add and configure blocks throughout the site
     And I press "Save changes"
     And I follow "Course 1"
     # The first block matching the pattern should be top-left block
-    And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
+    And I should see "Comments" in the "//*[@id='region-pre' or @id='block-region-side-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
index dea4dcc..5f1189d 100644 (file)
@@ -81,4 +81,4 @@ Feature: Block appearances
       | Visible | Yes |
       | Region  | Right |
     And I press "Save changes"
-    And I should see "Comments" in the "#region-post" "css_element"
+    And I should see "Comments" in the "//*[@id='region-post' or @id='block-region-side-post']" "xpath_element"
index 61ef61d..efa99ea 100644 (file)
@@ -40,7 +40,7 @@ if (empty($CFG->enableblogs)) {
 
 //correct tagid if a text tag is provided as a param
 if (!empty($tag)) {
-    if ($tagrec = $DB->get_record_sql("SELECT * FROM {tag} WHERE ". $DB->sql_like('name', '?', false), array("%$tag%"))) {
+    if ($tagrec = $DB->get_record('tag', array('name' => $tag))) {
         $tagid = $tagrec->id;
     } else {
         unset($tagid);
@@ -223,5 +223,13 @@ $bloglisting = new blog_listing($blogheaders['filters']);
 $bloglisting->print_entries();
 
 echo $OUTPUT->footer();
-
-add_to_log($courseid, 'blog', 'view', 'index.php?entryid='.$entryid.'&amp;tagid='.@$tagid.'&amp;tag='.$tag, 'view blog entry');
+$eventparams = array(
+    'other' => array('entryid' => $entryid, 'tagid' => $tagid, 'userid' => $userid, 'modid' => $modid, 'groupid' => $groupid,
+                     'search' => $search, 'fromstart' => $start)
+);
+if (!empty($userid)) {
+    $eventparams['relateduserid'] = $userid;
+}
+$eventparams['other']['courseid'] = ($courseid === SITEID) ? 0 : $courseid;
+$event = \core\event\blog_entries_viewed::create($eventparams);
+$event->trigger();
index c7ee5bd..10317c7 100644 (file)
@@ -880,8 +880,12 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
             $tagrec = $DB->get_record('tag', array('id'=>$tagid));
             $PAGE->navbar->add($tagrec->name, $blogurl);
         } elseif (!empty($tag)) {
-            $blogurl->param('tag', $tag);
-            $PAGE->navbar->add(get_string('tagparam', 'blog', $tag), $blogurl);
+            if ($tagrec = $DB->get_record('tag', array('name' => $tag))) {
+                $tagid = $tagrec->id;
+                $headers['filters']['tag'] = $tagid;
+                $blogurl->param('tag', $tag);
+                $PAGE->navbar->add(get_string('tagparam', 'blog', $tag), $blogurl);
+            }
         }
 
         // Append Search info
index a4e4210..c6a1084 100644 (file)
@@ -339,46 +339,62 @@ class blog_entry implements renderable {
     }
 
     /**
-     * function to add all context associations to an entry
-     * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
+     * Function to add all context associations to an entry.
+     * TODO : Remove $action in 2.9 (MDL-41330)
+     *
+     * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
      */
-    public function add_associations($action='add') {
-        global $DB, $USER;
+    public function add_associations($action = null) {
+
+        if (!empty($action)) {
+            debugging('blog_entry->add_associations() does not accept any argument', DEBUG_DEVELOPER);
+        }
 
         $this->remove_associations();
 
         if (!empty($this->courseassoc)) {
-            $this->add_association($this->courseassoc, $action);
+            $this->add_association($this->courseassoc);
         }
 
         if (!empty($this->modassoc)) {
-            $this->add_association($this->modassoc, $action);
+            $this->add_association($this->modassoc);
         }
     }
 
     /**
-     * add a single association for a blog entry
-     * @param int contextid - id of context to associate with the blog entry
+     * Add a single association for a blog entry
+     * TODO : Remove $action in 2.9 (MDL-41330)
+     *
+     * @param int $contextid - id of context to associate with the blog entry.
+     * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
      */
-    public function add_association($contextid, $action='add') {
-        global $DB, $USER;
+    public function add_association($contextid, $action = null) {
+        global $DB;
+
+        if (!empty($action)) {
+            debugging('blog_entry->add_association() accepts only one argument', DEBUG_DEVELOPER);
+        }
 
         $assocobject = new StdClass;
         $assocobject->contextid = $contextid;
         $assocobject->blogid = $this->id;
-        $DB->insert_record('blog_association', $assocobject);
+        $id = $DB->insert_record('blog_association', $assocobject);
 
+        // Trigger an association created event.
         $context = context::instance_by_id($contextid);
-        $courseid = null;
-
+        $eventparam = array(
+            'objectid' => $id,
+            'other' => array('associateid' => $context->instanceid, 'subject' => $this->subject, 'blogid' => $this->id),
+            'relateduserid' => $this->userid
+        );
         if ($context->contextlevel == CONTEXT_COURSE) {
-            $courseid = $context->instanceid;
-            add_to_log($courseid, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+            $eventparam['other']['associatetype'] = 'course';
+
         } else if ($context->contextlevel == CONTEXT_MODULE) {
-            $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
-            $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module));
-            add_to_log($cm->course, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject, $cm->id, $this->userid);
+            $eventparam['other']['associatetype'] = 'coursemodule';
         }
+        $event = \core\event\blog_association_created::create($eventparam);
+        $event->trigger();
     }
 
     /**
index 689e3be..47c8a29 100644 (file)
@@ -250,5 +250,137 @@ class core_bloglib_testcase extends advanced_testcase {
         $this->assertEventLegacyLogData($arr, $event);
         $this->assertEventLegacyData($blog, $event);
     }
+
+
+    /**
+     * Tests for event blog_association_created.
+     */
+    public function test_blog_association_created_event() {
+        global $USER;
+
+        $this->setAdminUser();
+        $this->resetAfterTest();
+        $sitecontext = context_system::instance();
+        $coursecontext = context_course::instance($this->courseid);
+        $contextmodule = context_module::instance($this->cmid);
+
+        // Add blog associations with a course.
+        $blog = new blog_entry($this->postid);
+        $sink = $this->redirectEvents();
+        $blog->add_association($coursecontext->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $this->assertInstanceOf('\core\event\blog_association_created', $event);
+        $this->assertEquals($sitecontext->id, $event->contextid);
+        $this->assertEquals($blog->id, $event->other['blogid']);
+        $this->assertEquals($this->courseid, $event->other['associateid']);
+        $this->assertEquals('course', $event->other['associatetype']);
+        $this->assertEquals($blog->subject, $event->other['subject']);
+        $this->assertEquals($USER->id, $event->userid);
+        $this->assertEquals($this->userid, $event->relateduserid);
+        $this->assertEquals('blog_association', $event->objecttable);
+        $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+                     $blog->subject, 0, $this->userid);
+        $this->assertEventLegacyLogData($arr, $event);
+
+        // Add blog associations with a module.
+        $blog = new blog_entry($this->postid);
+        $sink = $this->redirectEvents();
+        $blog->add_association($contextmodule->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $this->assertEquals($blog->id, $event->other['blogid']);
+        $this->assertEquals($this->cmid, $event->other['associateid']);
+        $this->assertEquals('coursemodule', $event->other['associatetype']);
+        $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+                     $blog->subject, $this->cmid, $this->userid);
+        $this->assertEventLegacyLogData($arr, $event);
+    }
+
+    /**
+     * Tests for event blog_association_created validations.
+     */
+    public function test_blog_association_created_event_validations() {
+
+        $this->resetAfterTest();
+
+         // Make sure associatetype validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 2 , 'blogid' => 3, 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid associatetype', $e->getMessage());
+        }
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 2 , 'blogid' => 3, 'associatetype' => 'random', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid associatetype', $e->getMessage());
+        }
+        // Make sure associateid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('blogid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Associate id must be set', $e->getMessage());
+        }
+        // Make sure blogid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Blog id must be set', $e->getMessage());
+        }
+        // Make sure blogid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('blogid' => 3, 'associateid' => 3, 'associatetype' => 'course')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Subject must be set', $e->getMessage());
+        }
+    }
+
+    /**
+     * Tests for event blog_entries_viewed.
+     */
+    public function test_blog_entries_viewed_event() {
+
+        $this->setAdminUser();
+        $this->resetAfterTest();
+        $other = array('entryid' => $this->postid, 'tagid' => $this->tagid, 'userid' => $this->userid, 'modid' => $this->cmid,
+                       'groupid' => $this->groupid, 'courseid' => $this->courseid, 'search' => 'search', 'fromstart' => 2);
+
+        // Trigger event.
+        $sink = $this->redirectEvents();
+        $eventparams = array('other' => $other);
+        $eventinst = \core\event\blog_entries_viewed::create($eventparams);
+        $eventinst->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $url = new moodle_url('/blog/index.php', $other);
+        $url2 = new moodle_url('index.php', $other);
+        $this->assertEquals($url, $event->get_url());
+        $arr = array(SITEID, 'blog', 'view', $url2->out(), 'view blog entry');
+        $this->assertEventLegacyLogData($arr, $event);
+    }
 }
 
diff --git a/blog/upgrade.txt b/blog/upgrade.txt
new file mode 100644 (file)
index 0000000..8695b95
--- /dev/null
@@ -0,0 +1,4 @@
+=== 2.7 ===
+
+* blog_entry->add_association() does not accept any params.
+* blog_entry->add_associations() accepts only one param.
\ No newline at end of file
index f3e3acb..c9c35db 100644 (file)
@@ -282,6 +282,7 @@ if (!empty($action) && confirm_sesskey()) {
 
 $PAGE->set_title($title);
 $PAGE->set_heading($SITE->fullname);
+/* @var core_cache_renderer $renderer */
 $renderer = $PAGE->get_renderer('core_cache');
 
 echo $renderer->header();
index 281fa30..4463e0a 100644 (file)
@@ -963,4 +963,31 @@ class cache_definition {
     public function has_required_identifiers() {
         return (count($this->requireidentifiers) > 0);
     }
+
+    /**
+     * Returns the possible sharing options that can be used with this defintion.
+     *
+     * @return int
+     */
+    public function get_sharing_options() {
+        return $this->sharingoptions;
+    }
+
+    /**
+     * Returns the user entered sharing key for this definition.
+     *
+     * @return string
+     */
+    public function get_user_input_sharing_key() {
+        return $this->userinputsharingkey;
+    }
+
+    /**
+     * Returns the user selected sharing option for this definition.
+     *
+     * @return int
+     */
+    public function get_selected_sharing_option() {
+        return $this->selectedsharingoption;
+    }
 }
index 2843dae..f850e69 100644 (file)
@@ -235,14 +235,9 @@ class cache_factory {
      */
     public function create_cache(cache_definition $definition) {
         $class = $definition->get_cache_class();
-        if ($this->is_initialising()) {
-            // Do nothing we just want the dummy store.
-            $stores = array();
-        } else {
-            $stores = cache_helper::get_cache_stores($definition);
-        }
+        $stores = cache_helper::get_stores_suitable_for_definition($definition);
         if (count($stores) === 0) {
-            // Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
+            // Hmm still no stores, better provide a dummy store to mimic functionality. The dev will be none the wiser.
             $stores[] = $this->create_dummy_store($definition);
         }
         $loader = null;
index 96c6601..90076aa 100644 (file)
@@ -143,7 +143,7 @@ class cache_helper {
      *
      * @param array $stores
      * @param cache_definition $definition
-     * @return array
+     * @return cache_store[]
      */
     protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) {
         $return = array();
@@ -382,34 +382,40 @@ class cache_helper {
     /**
      * Record a cache hit in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $hits The number of hits to record (by default 1)
      */
-    public static function record_cache_hit($store, $definition) {
+    public static function record_cache_hit($store, $definition, $hits = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['hits']++;
+        self::$stats[$definition][$store]['hits'] += $hits;
     }
 
     /**
      * Record a cache miss in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $misses The number of misses to record (by default 1)
      */
-    public static function record_cache_miss($store, $definition) {
+    public static function record_cache_miss($store, $definition, $misses = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['misses']++;
+        self::$stats[$definition][$store]['misses'] += $misses;
     }
 
     /**
      * Record a cache set in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $sets The number of sets to record (by default 1)
      */
-    public static function record_cache_set($store, $definition) {
+    public static function record_cache_set($store, $definition, $sets = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['sets']++;
+        self::$stats[$definition][$store]['sets'] += $sets;
     }
 
     /**
@@ -672,4 +678,59 @@ class cache_helper {
             }
         }
     }
+
+    /**
+     * Returns an array of stores that would meet the requirements for every definition.
+     *
+     * These stores would be 100% suitable to map as defaults for cache modes.
+     *
+     * @return array[] An array of stores, keys are the store names.
+     */
+    public static function get_stores_suitable_for_mode_default() {
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
+        $requirements = 0;
+        foreach ($config->get_definitions() as $definition) {
+            $definition = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
+            $requirements = $requirements | $definition->get_requirements_bin();
+        }
+        $stores = array();
+        foreach ($config->get_all_stores() as $name => $store) {
+            if (!empty($store['features']) && ($store['features'] & $requirements)) {
+                $stores[$name] = $store;
+            }
+        }
+        return $stores;
+    }
+
+    /**
+     * Returns stores suitable for use with a given definition.
+     *
+     * @param cache_definition $definition
+     * @return cache_store[]
+     */
+    public static function get_stores_suitable_for_definition(cache_definition $definition) {
+        $factory = cache_factory::instance();
+        $stores = array();
+        if ($factory->is_initialising() || $factory->stores_disabled()) {
+            // No suitable stores here.
+            return $stores;
+        } else {
+            $stores = self::get_cache_stores($definition);
+            if (count($stores) === 0) {
+                // No suitable stores we found for the definition. We need to come up with a sensible default.
+                // If this has happened we can be sure that the user has mapped custom stores to either the
+                // mode of the definition. The first alternative to try is the system default for the mode.
+                // e.g. the default file store instance for application definitions.
+                $config = $factory->create_config_instance();
+                foreach ($config->get_stores($definition->get_mode()) as $name => $details) {
+                    if (!empty($details['default'])) {
+                        $stores[] = $factory->create_store_from_config($name, $details, $definition);
+                        break;
+                    }
+                }
+            }
+        }
+        return $stores;
+    }
 }
index 10ad620..994aef2 100644 (file)
@@ -463,6 +463,20 @@ class cache implements cache_loader {
             }
         }
 
+        if ($this->perfdebug) {
+            $hits = 0;
+            $misses = 0;
+            foreach ($fullresult as $value) {
+                if ($value === false) {
+                    $misses++;
+                } else {
+                    $hits++;
+                }
+            }
+            cache_helper::record_cache_hit($this->storetype, $this->definition->get_id(), $hits);
+            cache_helper::record_cache_miss($this->storetype, $this->definition->get_id(), $misses);
+        }
+
         // Return the result. Phew!
         return $fullresult;
     }
@@ -633,10 +647,11 @@ class cache implements cache_loader {
                 $this->static_acceleration_set($data[$key]['key'], $value);
             }
         }
-        if ($this->perfdebug) {
-            cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
+        $successfullyset = $this->store->set_many($data);
+        if ($this->perfdebug && $successfullyset) {
+            cache_helper::record_cache_set($this->storetype, $this->definition->get_id(), $successfullyset);
         }
-        return $this->store->set_many($data);
+        return $successfullyset;
     }
 
     /**
@@ -1937,7 +1952,19 @@ class cache_session extends cache {
         if ($hasmissingkeys && $strictness === MUST_EXIST) {
             throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
         }
-
+        if ($this->perfdebug) {
+            $hits = 0;
+            $misses = 0;
+            foreach ($return as $value) {
+                if ($value === false) {
+                    $misses++;
+                } else {
+                    $hits++;
+                }
+            }
+            cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id(), $hits);
+            cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id(), $misses);
+        }
         return $return;
 
     }
@@ -2011,10 +2038,11 @@ class cache_session extends cache {
                 'value' => $value
             );
         }
-        if ($this->perfdebug) {
-            cache_helper::record_cache_set($this->storetype, $definitionid);
+        $successfullyset = $this->get_store()->set_many($data);
+        if ($this->perfdebug && $successfullyset) {
+            cache_helper::record_cache_set($this->storetype, $definitionid, $successfullyset);
         }
-        return $this->get_store()->set_many($data);
+        return $successfullyset;
     }
 
     /**
index c72c52c..c7948cf 100644 (file)
@@ -778,63 +778,36 @@ abstract class cache_administration_helper extends cache_helper {
      * @return array
      */
     public static function get_definition_summaries() {
-        $instance = cache_config::instance();
-        $definitions = $instance->get_definitions();
-
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
         $storenames = array();
-        foreach ($instance->get_all_stores() as $key => $store) {
+        foreach ($config->get_all_stores() as $key => $store) {
             if (!empty($store['default'])) {
                 $storenames[$key] = new lang_string('store_'.$key, 'cache');
-            }
-        }
-
-        $modemappings = array();
-        foreach ($instance->get_mode_mappings() as $mapping) {
-            $mode = $mapping['mode'];
-            if (!array_key_exists($mode, $modemappings)) {
-                $modemappings[$mode] = array();
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $modemappings[$mode][] = $storenames[$mapping['store']];
             } else {
-                $modemappings[$mode][] = $mapping['store'];
+                $storenames[$store['name']] = $store['name'];
             }
         }
-
-        $definitionmappings = array();
-        foreach ($instance->get_definition_mappings() as $mapping) {
-            $definition = $mapping['definition'];
-            if (!array_key_exists($definition, $definitionmappings)) {
-                $definitionmappings[$definition] = array();
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $definitionmappings[$definition][] = $storenames[$mapping['store']];
-            } else {
-                $definitionmappings[$definition][] = $mapping['store'];
-            }
+        /* @var cache_definition[] $definitions */
+        $definitions = array();
+        foreach ($config->get_definitions() as $key => $definition) {
+            $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
         }
-
-        $return = array();
-
         foreach ($definitions as $id => $definition) {
-
             $mappings = array();
-            if (array_key_exists($id, $definitionmappings)) {
-                $mappings = $definitionmappings[$id];
-            } else if (empty($definition['mappingsonly'])) {
-                $mappings = $modemappings[$definition['mode']];
+            foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
+                $mappings[] = $storenames[$store->my_name()];
             }
-
             $return[$id] = array(
                 'id' => $id,
-                'name' => cache_helper::get_definition_name($definition),
-                'mode' => $definition['mode'],
-                'component' => $definition['component'],
-                'area' => $definition['area'],
+                'name' => $definition->get_name(),
+                'mode' => $definition->get_mode(),
+                'component' => $definition->get_component(),
+                'area' => $definition->get_area(),
                 'mappings' => $mappings,
-                'sharingoptions' => self::get_definition_sharing_options($definition['sharingoptions'], false),
-                'selectedsharingoption' => self::get_definition_sharing_options($definition['selectedsharingoption'], true),
-                'userinputsharingkey' => $definition['userinputsharingkey']
+                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
+                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
+                'userinputsharingkey' => $definition->get_user_input_sharing_key()
             );
         }
         return $return;
@@ -1126,7 +1099,10 @@ abstract class cache_administration_helper extends cache_helper {
      * @return array An array containing sub-arrays, one for each mode.
      */
     public static function get_default_mode_stores() {
+        global $OUTPUT;
         $instance = cache_config::instance();
+        $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
+        $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache'));
         $storenames = array();
         foreach ($instance->get_all_stores() as $key => $store) {
             if (!empty($store['default'])) {
@@ -1149,6 +1125,9 @@ abstract class cache_administration_helper extends cache_helper {
             } else {
                 $modemappings[$mode][$mapping['store']] = $mapping['store'];
             }
+            if (!array_key_exists($mapping['store'], $adequatestores)) {
+                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
+            }
         }
         return $modemappings;
     }
index c11141e..be9f906 100644 (file)
@@ -3025,7 +3025,7 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = nu
     $updatecount = 0;
 
     // Large calendars take a while...
-    set_time_limit(300);
+    core_php_time_limit::raise(300);
 
     // Mark all events in a subscription with a zero timestamp.
     if (!empty($subscriptionid)) {
index fd9b99e..1a87d58 100644 (file)
@@ -309,6 +309,7 @@ class core_calendar_renderer extends plugin_renderer_base {
         global $CFG;
 
         $event = calendar_add_event_metadata($event);
+        $context = $event->context;
 
         $anchor  = html_writer::tag('a', '', array('name'=>'event_'.$event->id));
 
@@ -331,7 +332,7 @@ class core_calendar_renderer extends plugin_renderer_base {
         if (!empty($event->referer)) {
             $table->data[0]->cells[1]->text .= html_writer::tag('div', $event->referer, array('class'=>'referer'));
         } else {
-            $table->data[0]->cells[1]->text .= html_writer::tag('div', $event->name, array('class'=>'name'));
+            $table->data[0]->cells[1]->text .= html_writer::tag('div', format_string($event->name, false, array('context' => $context)), array('class'=>'name'));
         }
         if (!empty($event->courselink)) {
             $table->data[0]->cells[1]->text .= html_writer::tag('div', $event->courselink, array('class'=>'course'));
@@ -355,7 +356,7 @@ class core_calendar_renderer extends plugin_renderer_base {
         $table->data[1]->cells[0] = new html_table_cell('&nbsp;');
         $table->data[1]->cells[0]->attributes['class'] .= 'side';
 
-        $table->data[1]->cells[1] = new html_table_cell($event->description);
+        $table->data[1]->cells[1] = new html_table_cell(format_text($event->description, $event->format, array('context' => $context)));
         $table->data[1]->cells[1]->attributes['class'] .= ' description';
         if (isset($event->cssclass)) {
             $table->data[1]->cells[1]->attributes['class'] .= ' '.$event->cssclass;
index 5349c53..887533b 100644 (file)
@@ -54,7 +54,7 @@ class behat_cohort extends behat_base {
         $userid = $DB->get_field('user', 'id', array('username' => $username));
 
         $steps = array(
-            new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" table row'),
+            new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" "table_row"'),
             new Given('I select "' . $userid . '" from "' . get_string('potusers', 'cohort') . '"'),
             new Given('I press "' . get_string('add') . '"'),
             new Given('I press "' . get_string('backtocohorts', 'cohort') . '"')
index 47b169a..5e6a5ad 100644 (file)
@@ -32,11 +32,11 @@ Feature: Upload users to a cohort
     And I press "Upload users"
     And I press "Continue"
     And I follow "Cohorts"
-    And I click on "Assign" "link" in the "Cohort 1" table row
+    And I click on "Assign" "link" in the "Cohort 1" "table_row"
     Then the "Current users" select box should contain "Tom Jones (tomjones@example.com)"
     And the "Current users" select box should contain "Bob Jones (bobjones@example.com)"
     And I press "Back to cohorts"
-    And I click on "Assign" "link" in the "Cohort 2" table row
+    And I click on "Assign" "link" in the "Cohort 2" "table_row"
     And the "Current users" select box should contain "Mary Smith (marysmith@example.com)"
     And the "Current users" select box should contain "Alice Smith (alicesmith@example.com)"
     And I am on homepage
index 45ff5a8..1810b79 100644 (file)
@@ -8,6 +8,6 @@
     "require-dev": {
         "phpunit/phpunit": "3.7.*",
         "phpunit/dbUnit": "1.2.*",
-        "moodlehq/behat-extension": "1.26.*"
+        "moodlehq/behat-extension": "1.27.0"
     }
 }
index 08a50a0..cafa530 100644 (file)
@@ -589,10 +589,16 @@ $CFG->admin = 'admin';
 // $CFG->behat_prefix = 'bht_';
 // $CFG->behat_dataroot = '/home/example/bht_moodledata';
 //
-// Behat uses http://localhost:8000 as default URL to run
-// the acceptance tests, you can override this value.
+// To set a seperate wwwroot for Behat to use, use $CFG->behat_wwwroot; this is set automatically
+// to http://localhost:8000 as it is the proposed PHP built-in server URL. Instead of that you can,
+// for example, use an alias, add a host to /etc/hosts or add a new virtual host having a URL
+// poiting to your production site and another one poiting to your test site. Note that you need
+// to ensure that this URL is not accessible from the www as the behat test site uses "sugar"
+// credentials (admin/admin) and can be easily hackable.
+//
 // Example:
 //   $CFG->behat_wwwroot = 'http://192.168.1.250:8000';
+//   $CFG->behat_wwwroot = 'http://localhost/moodlesitetesting';
 //
 // You can override default Moodle configuration for Behat and add your own
 // params; here you can add more profiles, use different Mink drivers than Selenium...
@@ -661,6 +667,11 @@ $CFG->admin = 'admin';
 // Example:
 //   $CFG->behat_extraallowedsettings = array('logsql', 'dblogerror');
 //
+// You should explicitly allow the usage of the deprecated behat steps, otherwise an exception will
+// be thrown when using them. The setting is disabled by default.
+// Example:
+//   $CFG->behat_usedeprecated = true;
+//
 //=========================================================================
 // 12. DEVELOPER DATA GENERATOR
 //=========================================================================
index 5f21e94..a26144d 100644 (file)
@@ -622,10 +622,6 @@ class helper {
         if (!$category->can_change_visibility()) {
             throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_change_visbility');
         }
-        if ((int)$category->get_parent_coursecat()->visible === 0) {
-            // You cannot mark a category visible if its parent is hidden.
-            return false;
-        }
         $category->show();
         return true;
     }
index 61973b1..688e304 100644 (file)
@@ -209,7 +209,8 @@ class core_course_management_renderer extends plugin_renderer_base {
             'name' => 'bcat[]',
             'value' => $category->id,
             'class' => 'bulk-action-checkbox',
-            'aria-label' => get_string('bulkactionselect', 'moodle', $text)
+            'aria-label' => get_string('bulkactionselect', 'moodle', $text),
+            'data-action' => 'select'
         );
 
         if (!$category->can_resort_subcategories() && !$category->has_manage_capability()) {
@@ -615,7 +616,8 @@ class core_course_management_renderer extends plugin_renderer_base {
             'name' => 'bc[]',
             'value' => $course->id,
             'class' => 'bulk-action-checkbox',
-            'aria-label' => get_string('bulkactionselect', 'moodle', $text)
+            'aria-label' => get_string('bulkactionselect', 'moodle', $text),
+            'data-action' => 'select'
         );
         if (!$category->has_manage_capability()) {
             // Very very hardcoded here.
index c296ec9..d4a44a2 100644 (file)
@@ -186,7 +186,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         if ($PAGE->user_is_editing() && has_capability('moodle/course:update', $context)) {
             $url = new moodle_url('/course/editsection.php', array('id'=>$section->id, 'sr'=>$sectionreturn));
             $o.= html_writer::link($url,
-                html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/edit'),
+                html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/settings'),
                     'class' => 'iconsmall edit', 'alt' => get_string('edit'))),
                 array('title' => get_string('editsummary')));
         }
index bbdad82..ce72a9c 100644 (file)
@@ -1595,14 +1595,6 @@ function set_coursemodule_visible($id, $visible) {
         }
     }
 
-    // Hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there.
-    $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
-    if ($grade_items) {
-        foreach ($grade_items as $grade_item) {
-            $grade_item->set_hidden(!$visible);
-        }
-    }
-
     // Updating visible and visibleold to keep them in sync. Only changing a section visibility will
     // affect visibleold to allow for an original visibility restore. See set_section_visible().
     $cminfo = new stdClass();
@@ -1611,6 +1603,22 @@ function set_coursemodule_visible($id, $visible) {
     $cminfo->visibleold = $visible;
     $DB->update_record('course_modules', $cminfo);
 
+    // Hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there.
+    // Note that this must be done after updating the row in course_modules, in case
+    // the modules grade_item_update function needs to access $cm->visible.
+    if (plugin_supports('mod', $modulename, FEATURE_CONTROLS_GRADE_VISIBILITY) &&
+            component_callback_exists('mod_' . $modulename, 'grade_item_update')) {
+        $instance = $DB->get_record($modulename, array('id' => $cm->instance), '*', MUST_EXIST);
+        component_callback('mod_' . $modulename, 'grade_item_update', array($instance));
+    } else {
+        $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
+        if ($grade_items) {
+            foreach ($grade_items as $grade_item) {
+                $grade_item->set_hidden(!$visible);
+            }
+        }
+    }
+
     rebuild_course_cache($cm->course, true);
     return true;
 }
index 3b32a94..87f8495 100644 (file)
@@ -85,18 +85,16 @@ class behat_course extends behat_base {
      * @return Given[]
      */
     public function i_go_to_the_courses_management_page() {
-        if ($this->running_javascript()) {
-            $scenario = array(new Given('I expand "' . get_string('administrationsite') . '" node'));
-        } else {
-            $scenario = array(new Given('I follow "' . get_string('administrationsite') . '"'));
-        }
-        $scenario[] = new Given('I expand "' . get_string('courses', 'admin') . '" node');
-        $scenario[] = new Given('I follow "' . get_string('coursemgmt', 'admin') . '"');
-        return $scenario;
+        return array(
+            new Given('I am on homepage'),
+            new Given('I expand "' . get_string('administrationsite') . '" node'),
+            new Given('I expand "' . get_string('courses', 'admin') . '" node'),
+            new Given('I follow "' . get_string('coursemgmt', 'admin') . '"')
+        );
     }
 
     /**
-     * Adds the selected activity/resource filling the form data with the specified field/value pairs.
+     * Adds the selected activity/resource filling the form data with the specified field/value pairs. Sections 0 and 1 are also allowed on frontpage.
      *
      * @When /^I add a "(?P<activity_or_resource_name_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)" and I fill the form with:$/
      * @param string $activity The activity name
@@ -114,7 +112,7 @@ class behat_course extends behat_base {
     }
 
     /**
-     * Opens the activity chooser and opens the activity/resource form page.
+     * Opens the activity chooser and opens the activity/resource form page. Sections 0 and 1 are also allowed on frontpage.
      *
      * @Given /^I add a "(?P<activity_or_resource_name_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)"$/
      * @throws ElementNotFoundException Thrown by behat_base::find
@@ -123,7 +121,19 @@ class behat_course extends behat_base {
      */
     public function i_add_to_section($activity, $section) {
 
-        $sectionxpath = "//li[@id='section-" . $section . "']";
+        if ($this->getSession()->getPage()->find('css', 'body#page-site-index') && (int)$section <= 1) {
+            // We are on the frontpage.
+            if ($section) {
+                // Section 1 represents the contents on the frontpage.
+                $sectionxpath = "//body[@id='page-site-index']/descendant::div[contains(concat(' ',normalize-space(@class),' '),' sitetopic ')]";
+            } else {
+                // Section 0 represents "Site main menu" block.
+                $sectionxpath = "//div[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]";
+            }
+        } else {
+            // We are inside the course.
+            $sectionxpath = "//li[@id='section-" . $section . "']";
+        }
 
         $activityliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(ucfirst($activity));
 
@@ -946,7 +956,7 @@ class behat_course extends behat_base {
     protected function get_management_category_listing_node_by_name($name, $link = false) {
         $selector = "//div[@id='category-listing']//li[contains(concat(' ', normalize-space(@class), ' '), ' listitem-category ')]//a[text()='{$name}']";
         if ($link === false) {
-            $selector .= "/ancestor::li[@data-id]";
+            $selector .= "/ancestor::li[@data-id][1]";
         }
         return $this->find('xpath', $selector);
     }
@@ -1001,39 +1011,70 @@ class behat_course extends behat_base {
     }
 
     /**
-     * Clicks on a category checkbox in the management interface.
+     * Clicks on a category checkbox in the management interface, if not checked.
      *
      * @Given /^I select category "(?P<name>[^"]*)" in the management interface$/
      * @param string $name
      */
     public function i_select_category_in_the_management_interface($name) {
         $node = $this->get_management_category_listing_node_by_name($name);
-        $node->checkField('bcat[]');
+        $node = $node->findField('bcat[]');
+        if (!$node->isChecked()) {
+            $node->click();
+        }
     }
 
     /**
-     * Clicks course checkbox in the management interface.
+     * Clicks on a category checkbox in the management interface, if checked.
+     *
+     * @Given /^I unselect category "(?P<name>[^"]*)" in the management interface$/
+     * @param string $name
+     */
+    public function i_unselect_category_in_the_management_interface($name) {
+        $node = $this->get_management_category_listing_node_by_name($name);
+        $node = $node->findField('bcat[]');
+        if ($node->isChecked()) {
+            $node->click();
+        }
+    }
+
+    /**
+     * Clicks course checkbox in the management interface, if not checked.
      *
      * @Given /^I select course "(?P<name>[^"]*)" in the management interface$/
      * @param string $name
      */
     public function i_select_course_in_the_management_interface($name) {
         $node = $this->get_management_course_listing_node_by_name($name);
-        $node->checkField('bc[]');
+        $node = $node->findField('bc[]');
+        if (!$node->isChecked()) {
+            $node->click();
+        }
+    }
+
+    /**
+     * Clicks course checkbox in the management interface, if checked.
+     *
+     * @Given /^I unselect course "(?P<name>[^"]*)" in the management interface$/
+     * @param string $name
+     */
+    public function i_unselect_course_in_the_management_interface($name) {
+        $node = $this->get_management_course_listing_node_by_name($name);
+        $node = $node->findField('bc[]');
+        if ($node->isChecked()) {
+            $node->click();
+        }
     }
 
     /**
      * Move selected categories to top level in the management interface.
      *
-     * @Given /^I move category "(?P<idnumber>[^"]*)" to top level in the management interface$/
-     * @param string $idnumber
+     * @Given /^I move category "(?P<name>[^"]*)" to top level in the management interface$/
+     * @param string $name
      * @return Given[]
      */
-    public function i_move_category_to_top_level_in_the_management_interface($idnumber) {
-        $id = $this->get_category_id($idnumber);
-        $selector = sprintf('.listitem-category[data-id="%d"] > div', $id);
-        $node = $this->find('css', $selector);
-        $node->checkField('bcat[]');
+    public function i_move_category_to_top_level_in_the_management_interface($name) {
+        $this->i_select_category_in_the_management_interface($name);
         return array(
             new Given('I select "' .  coursecat::get(0)->get_formatted_name() . '" from "menumovecategoriesto"'),
             new Given('I press "bulkmovecategories"'),
index 5b6693b..baed279 100644 (file)
@@ -231,7 +231,104 @@ Feature: Test category management actions
     And I click on category "Cat 1" in the management interface
     # Redirect
     Then I should see category "CAT3" as subcategory of "CAT1" in the management interface
-    And I move category "CAT3" to top level in the management interface
+    And I move category "Cat 3" to top level in the management interface
     # Redirect
     And I should not see category "CAT3" as subcategory of "CAT1" in the management interface
     Then I should see category "CAT2" as subcategory of "CAT1" in the management interface
+
+  @javascript
+  Scenario: Test bulk action is shown only when some category/course is selected
+    Given the following "categories" exists:
+      | name | category | idnumber |
+      | Cat 1 | 0 | CAT1 |
+      | Cat 2 | 0 | CAT2 |
+      | Cat 3 | 0 | CAT3 |
+    And the following "courses" exists:
+      | category | fullname | shortname | idnumber |
+      | CAT3 | Course 1 | Course 1 | C1 |
+      | CAT3 | Course 2 | Course 2 | C2 |
+
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see the "Course categories" management page
+    And I should see "Cat 1" in the "#category-listing ul.ml" "css_element"
+    And I should see "Cat 2" in the "#category-listing ul.ml" "css_element"
+    And I should see "Cat 3" in the "#category-listing ul.ml" "css_element"
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    When I select "allcategories" from "selectsortby"
+    Then the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And the "movecategoriesto" "select" should be disabled
+    And I select category "Cat 2" in the management interface
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I select "selectedcategories" from "selectsortby"
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I unselect category "Cat 2" in the management interface
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    And I select category "Cat 3" in the management interface
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I select category "Cat 2" in the management interface
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I unselect category "Cat 2" in the management interface
+    And I unselect category "Cat 3" in the management interface
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    And I click on category "Cat 1" in the management interface
+    # Redirect.
+    And I should see the "Course categories and courses" management page
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    And the "movecoursesto" "select" should be disabled
+    And I click on category "Cat 3" in the management interface
+    #Redirect
+    And I should see the "Course categories and courses" management page
+    And I should see "Course 1" in the "#course-listing ul.ml" "css_element"
+    And I should see "Course 2" in the "#course-listing ul.ml" "css_element"
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    And the "movecoursesto" "select" should be disabled
+    And I select course "Course 1" in the management interface
+    And the "movecoursesto" "select" should be enabled
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    And I select course "Course 2" in the management interface
+    And the "movecoursesto" "select" should be enabled
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
+    And I select category "Cat 3" in the management interface
+    And the "movecoursesto" "select" should be enabled
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I unselect course "Course 2" in the management interface
+    And the "movecoursesto" "select" should be enabled
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I unselect course "Course 1" in the management interface
+    And the "movecoursesto" "select" should be disabled
+    And the "movecategoriesto" "select" should be enabled
+    And the "resortcategoriesby" "select" should be enabled
+    And the "resortcoursesby" "select" should be enabled
+    And I unselect category "Cat 3" in the management interface
+    And the "movecoursesto" "select" should be disabled
+    And the "movecategoriesto" "select" should be disabled
+    And the "resortcategoriesby" "select" should be disabled
+    And the "resortcoursesby" "select" should be disabled
index 283346e..87adb97 100644 (file)
@@ -137,7 +137,7 @@ Feature: Course activity controls works as expected
     And "#section-2" "css_element" <should_see_other_sections> exists
     And I delete "Test forum name 1" activity
     And "#section-2" "css_element" <should_see_other_sections> exists
-    And I should not see "Test forum name 1" in the ".region-content" "css_element"
+    And I should not see "Test forum name 1" in the "#region-main" "css_element"
     And I duplicate "Test forum name 2" activity editing the new copy with:
       | Forum name | Edited test forum name 2 |
     And "#section-2" "css_element" <should_see_other_sections> exists
index 1935ec7..3f8d5b8 100644 (file)
@@ -1566,12 +1566,7 @@ class core_course_courselib_testcase extends advanced_testcase {
         // Catch the update event.
         $sink = $this->redirectEvents();
 
-        // Call remove_course_contents() which will trigger the course_content_deleted event.
-        // This function prints out data to the screen, which we do not want during a PHPUnit
-        // test, so use ob_start and ob_end_clean to prevent this.
-        ob_start();
-        remove_course_contents($course->id);
-        ob_end_clean();
+        remove_course_contents($course->id, false);
 
         // Capture the event.
         $events = $sink->get_events();
@@ -1720,9 +1715,6 @@ class core_course_courselib_testcase extends advanced_testcase {
         // Destroy the resource controller since we are done using it.
         $rc->destroy();
         unset($rc);
-
-        // Clear the time limit, otherwise PHPUnit complains.
-        set_time_limit(0);
     }
 
     /**
index 8693632..92a5f6c 100644 (file)
@@ -662,9 +662,6 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
 
         // Check that the course has been duplicated.
         $this->assertEquals($newcourse['shortname'], $duplicate['shortname']);
-
-        // Reset the timeouts.
-        set_time_limit(0);
     }
 
     /**
@@ -1026,9 +1023,6 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
                 $this->fail('Unknown CM found.');
             }
         }
-
-        // Reset the timeout (see MDL-38989).
-        set_time_limit(0);
     }
 
     /**
@@ -1080,9 +1074,6 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
                 $this->fail('Unknown CM found.');
             }
         }
-
-        // Reset the timeout (see MDL-38989).
-        set_time_limit(0);
     }
 
     /**
@@ -1124,10 +1115,6 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         // Check that course modules haven't changed, but that blocks have.
         $this->assertEquals($initialcmcount, $newcmcount);
         $this->assertEquals(($initialblockcount + 1), $newblockcount);
-
-
-        // Reset the timeout (see MDL-38989).
-        set_time_limit(0);
     }
 
     /**
@@ -1176,9 +1163,6 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
                 $this->fail('Unknown CM found: '.$cm->name);
             }
         }
-
-        // Reset the timeout (see MDL-38989).
-        set_time_limit(0);
     }
 
     /**
index f4cb41b..ee9a952 100644 (file)
@@ -191,8 +191,8 @@ class core_course_management_helper_test extends advanced_testcase {
 
         $parentassignment->assign(CAP_PREVENT);
         // Now we have capability on the category and subcategory but not the parent.
-        // Try to mark the subcategory as visible. This shouldn't be possible because its parent is hidden.
-        $this->assertFalse(\core_course\management\helper::action_category_show($subcategory));
+        // Try to mark the subcategory as visible. This should be possible although its parent is set to hidden.
+        $this->assertTrue(\core_course\management\helper::action_category_show($subcategory));
         $cat = coursecat::get($category->id);
         $subcat = coursecat::get($subcategory->id);
         $course = $DB->get_record('course', array('id' => $course->id), 'id, visible, visibleold', MUST_EXIST);
@@ -348,8 +348,8 @@ class core_course_management_helper_test extends advanced_testcase {
 
         $parentassignment->assign(CAP_PREVENT);
         // Now we have capability on the category and subcategory but not the parent.
-        // Try to mark the subcategory as visible. This shouldn't be possible because its parent is hidden.
-        $this->assertFalse(\core_course\management\helper::action_category_show($subcategory));
+        // Try to mark the subcategory as visible. This should be possible although its parent is set to hidden.
+        $this->assertTrue(\core_course\management\helper::action_category_show($subcategory));
         $cat = coursecat::get($category->id);
         $subcat = coursecat::get($subcategory->id);
         $course = $DB->get_record('course', array('id' => $course->id), 'id, visible, visibleold', MUST_EXIST);
index 1b26a57..d1ce9ef 100644 (file)
Binary files a/course/yui/build/moodle-course-management/moodle-course-management-debug.js and b/course/yui/build/moodle-course-management/moodle-course-management-debug.js differ
index 6597c67..3db2a29 100644 (file)
Binary files a/course/yui/build/moodle-course-management/moodle-course-management-min.js and b/course/yui/build/moodle-course-management/moodle-course-management-min.js differ
index 5d78e2d..9b29145 100644 (file)
Binary files a/course/yui/build/moodle-course-management/moodle-course-management.js and b/course/yui/build/moodle-course-management/moodle-course-management.js differ
index 2a608c3..7d56493 100644 (file)
@@ -136,6 +136,20 @@ Category.prototype = {
                 e.preventDefault();
                 this.collapse();
                 break;
+            case 'select':
+                var c = this.get('console'),
+                    movecategoryto = c.get('categorylisting').one('#menumovecategoriesto');
+                // If any category is selected and there are more then one categories.
+                if (movecategoryto) {
+                    if (c.isCategorySelected(e.currentTarget) &&
+                            c.get('categories').length > 1) {
+                        movecategoryto.removeAttribute('disabled');
+                    } else {
+                        movecategoryto.setAttribute('disabled', true);
+                    }
+                    c.handleBulkSortByaction();
+                }
+                break;
             default:
                 Y.log('Invalid AJAX action requested of managed category.', 'warn', 'moodle-course-management');
                 return false;
index 631dfc4..b0ff872 100644 (file)
@@ -250,6 +250,21 @@ Console.prototype = {
         if (!listing) {
             return false;
         }
+
+        // Disable category bulk actions as nothing will be selected on initialise.
+        var menumovecatto = listing.one('#menumovecategoriesto');
+        if (menumovecatto) {
+            menumovecatto.setAttribute('disabled', true);
+        }
+        var menuresortcategoriesby = listing.one('#menuresortcategoriesby');
+        if (menuresortcategoriesby) {
+            menuresortcategoriesby.setAttribute('disabled', true);
+        }
+        var menuresortcoursesby = listing.one('#menuresortcoursesby');
+        if (menuresortcoursesby) {
+            menuresortcoursesby.setAttribute('disabled', true);
+        }
+
         listing.all('.listitem[data-id]').each(function(node){
             this.set('categories', new Category({
                 node : node,
@@ -259,6 +274,8 @@ Console.prototype = {
         }, this);
         if (!this.categoriesinit) {
             this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'a[data-action]', this);
+            this.get('categorylisting').delegate('click', this.handleCategoryDelegation, 'input[name="bcat[]"]', this);
+            this.get('categorylisting').delegate('click', this.handleBulkSortByaction, '#menuselectsortby', this);
             this.categoriesinit = true;
             Y.log(count+' categories being managed', 'info', 'moodle-course-management');
         } else {
@@ -279,6 +296,13 @@ Console.prototype = {
         if (!listing) {
             return false;
         }
+
+        // Disable course move to bulk action as nothing will be selected on initialise.
+        var menumovecoursesto = listing.one('#menumovecoursesto');
+        if (menumovecoursesto) {
+            menumovecoursesto.setAttribute('disabled', true);
+        }
+
         listing.all('.listitem[data-id]').each(function(node){
             this.registerCourse(new Course({
                 node : node,
@@ -288,6 +312,7 @@ Console.prototype = {
             count++;
         }, this);
         listing.delegate('click', this.handleCourseDelegation, 'a[data-action]', this);
+        listing.delegate('click', this.handleCourseDelegation, 'input[name="bc[]"]', this);
         Y.log(count+' courses being managed', 'info', 'moodle-course-management');
     },
 
@@ -340,6 +365,118 @@ Console.prototype = {
         }
     },
 
+    /**
+     * Check if any course is selected.
+     *
+     * @method isCourseSelected
+     * @param {Node} checkboxnode Checkbox node on which action happened.
+     * @return bool
+     */
+    isCourseSelected : function(checkboxnode) {
+        var selected = false;
+
+        // If any course selected then show move to category select box.
+        if (checkboxnode && checkboxnode.get('checked')) {
+            selected = true;
+        } else {
+            var i,
+                course,
+                courses = this.get('courses'),
+                length = courses.length;
+            for (i = 0; i < length; i++) {
+                if (courses.hasOwnProperty(i)) {
+                    course = courses[i];
+                    if (course.get('node').one('input[name="bc[]"]').get('checked')) {
+                        selected = true;
+                        break;
+                    }
+                }
+            }
+        }
+        return selected;
+    },
+
+    /**
+     * Check if any category is selected.
+     *
+     * @method isCategorySelected
+     * @param {Node} checkboxnode Checkbox node on which action happened.
+     * @return bool
+     */
+    isCategorySelected : function(checkboxnode) {
+        var selected = false;
+
+        // If any category selected then show move to category select box.
+        if (checkboxnode && checkboxnode.get('checked')) {
+            selected = true;
+        } else {
+            var i,
+                category,
+                categories = this.get('categories'),
+                length = categories.length;
+            for (i = 0; i < length; i++) {
+                if (categories.hasOwnProperty(i)) {
+                    category = categories[i];
+                    if (category.get('node').one('input[name="bcat[]"]').get('checked')) {
+                        selected = true;
+                        break;
+                    }
+                }
+            }
+        }
+        return selected;
+    },
+
+    /**
+     * Handle bulk sort action.
+     *
+     * @method handleBulkSortByaction
+     * @protected
+     * @param {EventFacade} e
+     */
+    handleBulkSortByaction : function(e) {
+        var sortcategoryby = this.get('categorylisting').one('#menuresortcategoriesby'),
+            sortcourseby = this.get('categorylisting').one('#menuresortcoursesby'),
+            sortbybutton = this.get('categorylisting').one('input[name="bulksort"]');
+            sortby = e;
+
+        if (!sortby) {
+            sortby = this.get('categorylisting').one('#menuselectsortby');
+        } else {
+            if (e && e.currentTarget) {
+                sortby = e.currentTarget;
+            }
+        }
+
+        // If no sortby select found then return as we can't do anything.
+        if (!sortby) {
+            return;
+        }
+
+        if ((this.get('categories').length <= 1) || (!this.isCategorySelected() &&
+                (sortby.get("options").item(sortby.get('selectedIndex')).getAttribute('value') === 'selectedcategories'))) {
+            if (sortcategoryby) {
+                sortcategoryby.setAttribute('disabled', true);
+            }
+            if (sortcourseby) {
+                sortcourseby.setAttribute('disabled', true);
+            }
+            if (sortbybutton) {
+                sortbybutton.setAttribute('disabled', true);
+            }
+        } else {
+            if (sortcategoryby) {
+                sortcategoryby.removeAttribute('disabled');
+            }
+            if (sortcourseby) {
+                sortcourseby.removeAttribute('disabled');
+            }
+            if (sortbybutton) {
+                sortbybutton.removeAttribute('disabled');
+            }
+        }
+    },
+
     /**
      * Returns the category with the given ID.
      * @method getCategoryById
index 2b2df34..3e646c6 100644 (file)
@@ -110,6 +110,17 @@ Course.prototype = {
                 e.halt();
                 managementconsole.performAjaxAction('hidecourse', args, this.hide, this);
                 break;
+            case 'select':
+                var c = this.get('console'),
+                    movetonode = c.get('courselisting').one('#menumovecoursesto');
+                if (movetonode) {
+                    if (c.isCourseSelected(e.currentTarget)) {
+                        movetonode.removeAttribute('disabled');
+                    } else {
+                        movetonode.setAttribute('disabled', true);
+                    }
+                }
+                break;
             default:
                 Y.log('Invalid AJAX action requested of managed course.', 'warn', 'moodle-course-management');
                 return false;
index 404ff8f..2439955 100644 (file)
@@ -141,7 +141,7 @@ function enrol_category_sync_full(progress_trace $trace) {
     }
 
     // We may need a lot of time here.
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     $plugin = enrol_get_plugin('category');
 
index e07c20b..ec9f9cf 100644 (file)
@@ -167,7 +167,7 @@ function enrol_cohort_sync(progress_trace $trace, $courseid = NULL) {
     }
 
     // Unfortunately this may take a long time, this script can be interrupted without problems.
-    @set_time_limit(0);
+    core_php_time_limit::raise();
     raise_memory_limit(MEMORY_HUGE);
 
     $trace->output('Starting user enrolment synchronisation...');
index d7e3b19..24cccb6 100644 (file)
@@ -41,5 +41,8 @@ function xmldb_enrol_database_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index cde6d62..b2aa363 100644 (file)
@@ -307,7 +307,7 @@ class enrol_database_plugin extends enrol_plugin {
         }
 
         // We may need a lot of memory here.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         $table            = $this->get_config('remoteenroltable');
@@ -625,7 +625,7 @@ class enrol_database_plugin extends enrol_plugin {
         $trace->output('Starting course synchronisation...');
 
         // We may need a lot of memory here.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         if (!$extdb = $this->db_init()) {
index 6506866..fdfd851 100644 (file)
@@ -42,5 +42,8 @@ function xmldb_enrol_flatfile_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index e0117b9..d30a3a6 100644 (file)
@@ -229,7 +229,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
         global $CFG, $DB;
 
         // We may need more memory here.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         $filelocation = $this->get_config('location');
index 4771b11..4ddbf8a 100644 (file)
@@ -51,6 +51,9 @@ function xmldb_enrol_guest_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
 
index 12c50c0..3c6379d 100644 (file)
@@ -44,5 +44,8 @@ function xmldb_enrol_imsenterprise_upgrade($oldversion) {
     // Moodle v2.5.0 release upgrade line.
     // Put any upgrade step following this.
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 5528e55..34a0442 100644 (file)
@@ -94,7 +94,7 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
 
         $fileisnew = false;
         if ( file_exists($filename) ) {
-            @set_time_limit(0);
+            core_php_time_limit::raise();
             $starttime = time();
 
             $this->log_line('----------------------------------------------------------------------');
index fa84fe3..b4bf0b9 100644 (file)
@@ -147,7 +147,7 @@ class enrol_ldap_plugin extends enrol_plugin {
         }
 
         // We may need a lot of memory here
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         // Get enrolments for each type of role.
@@ -311,7 +311,7 @@ class enrol_ldap_plugin extends enrol_plugin {
         $ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'));
 
         // we may need a lot of memory here
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         $oneidnumber = null;
index f306b81..dc1b910 100644 (file)
@@ -54,6 +54,9 @@ function xmldb_enrol_manual_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
 
index 10e12e1..805ed05 100644 (file)
@@ -303,7 +303,7 @@ class enrol_manual_plugin extends enrol_plugin {
         }
 
         // Unfortunately this may take a long time, execution can be interrupted safely here.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         $trace->output('Verifying manual enrolment expiration...');
index 5c10d79..84b6107 100644 (file)
@@ -255,7 +255,7 @@ function enrol_meta_sync($courseid = NULL, $verbose = false) {
     }
 
     // unfortunately this may take a long time, execution can be interrupted safely
-    @set_time_limit(0);
+    core_php_time_limit::raise();
     raise_memory_limit(MEMORY_HUGE);
 
     if ($verbose) {
index ade5484..58287c1 100644 (file)
@@ -43,5 +43,8 @@ function xmldb_enrol_mnet_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 282f2a5..6ad8df4 100644 (file)
@@ -58,5 +58,8 @@ function xmldb_enrol_paypal_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index fda6302..f3762d7 100644 (file)
@@ -38,7 +38,7 @@ class enrol_paypal_plugin extends enrol_plugin {
         // 3-character ISO-4217: https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_currency_codes
         $codes = array(
             'AUD', 'BRL', 'CAD', 'CHF', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', 'ILS', 'JPY',
-            'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'SEK', 'SGD', 'THB', 'TRY', 'TWD', 'USD');
+            'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RUB', 'SEK', 'SGD', 'THB', 'TRY', 'TWD', 'USD');
         $currencies = array();
         foreach ($codes as $c) {
             $currencies[$c] = new lang_string($c, 'core_currencies');
index 6fee53d..08d3330 100644 (file)
@@ -53,6 +53,15 @@ function xmldb_enrol_self_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2013112100) {
+        // Set customint1 (group enrolment key) to 0 if it was not set (null).
+        $DB->execute("UPDATE {enrol} SET customint1 = 0 WHERE enrol = 'self' AND customint1 IS NULL");
+        upgrade_plugin_savepoint(true, 2013112100, 'enrol', 'self');
+    }
+
     return true;
 }
 
index fd99646..d64281a 100644 (file)
@@ -468,7 +468,7 @@ class enrol_self_plugin extends enrol_plugin {
         }
 
         // Unfortunately this may take a long time, execution can be interrupted safely here.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         $trace->output('Verifying self-enrolments...');
index 6c6a35d..9e271c4 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013110500;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013112100;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013110500;        // Requires this Moodle version
 $plugin->component = 'enrol_self';      // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 600;
diff --git a/filter/algebra/thirdpartylibs.xml b/filter/algebra/thirdpartylibs.xml
new file mode 100644 (file)
index 0000000..93970af
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<libraries>
+  <library>
+      <location>AlgParser.pm</location>
+      <name>WeBWorK</name>
+      <license>GPL</license>
+      <version>2.4.9+</version>
+      <licenseversion>2.0+</licenseversion>
+  </library>
+</libraries>
index c8378cf..117f622 100644 (file)
@@ -66,5 +66,8 @@ function xmldb_filter_mediaplugin_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
     return true;
 }
index 6125a65..b0d4578 100644 (file)
@@ -45,5 +45,25 @@ function xmldb_filter_tex_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.6.0 release upgrade line.
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2013120300) {
+        $settings = array(
+                'density', 'latexbackground', 'convertformat', 'pathlatex',
+                'convertformat', 'pathconvert', 'pathdvips', 'latexpreamble');
+
+        // Move tex settings to config_pluins and delete entries from the config table.
+        foreach ($settings as $setting) {
+            $existingkey = 'filter_tex_'.$setting;
+            if (array_key_exists($existingkey, $CFG)) {
+                set_config($setting, $CFG->{$existingkey}, 'filter_tex');
+                unset_config($existingkey);
+            }
+        }
+
+        upgrade_plugin_savepoint(true, 2013120300, 'filter', 'tex');
+    }
+
     return true;
 }
index 0999df2..6e593f3 100644 (file)
@@ -181,7 +181,8 @@ class filter_tex extends moodle_text_filter {
                 $texcache->timemodified = time();
                 $DB->insert_record("cache_filters", $texcache, false);
             }
-            $filename = $md5 . ".{$CFG->filter_tex_convertformat}";
+            $convertformat = get_config('filter_tex', 'convertformat');
+            $filename = $md5.".{$convertformat}";
             $text = str_replace( $matches[0][$i], filter_text_image($filename, $texexp, 0, 0, $align, $alt), $text);
         }
         return $text;
index 8290d7b..49fe7d1 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['configconvertformat'] = 'If <i>latex</i>, <i>dvips</i> and <i>convert</i> are available, the images are created using the specified format. If it is not, mimeTeX will be used and it will create GIF images.';
+$string['convertformat'] = '<i>Convert</i> output format';
+$string['latexpreamble'] = 'LaTeX preamble';
+$string['latexsettings'] = 'LaTeX renderer Settings';
 $string['filtername'] = 'TeX notation';
+$string['pathconvert'] = 'Path of <i>convert</i> binary';
+$string['pathdvips'] = 'Path of <i>dvips</i> binary';
+$string['pathlatex'] = 'Path of <i>latex</i> binary';
+$string['pathmimetex'] = 'Path of <i>mimetex</i> binary';
+$string['pathmimetexdesc'] = 'Moodle will use its own mimetex binary unless another valid path is specified.';
 $string['source'] = 'TeX source';
index fe0baf9..a79a33e 100644 (file)
@@ -44,7 +44,7 @@
 
             // $fontsize don't affects to formula's size. $density can change size
             $doc =  "\\documentclass[{$fontsize}pt]{article}\n";
-            $doc .=  $CFG->filter_tex_latexpreamble;
+            $doc .= get_config('filter_tex', 'latexpreamble');
             $doc .= "\\pagestyle{empty}\n";
             $doc .= "\\begin{document}\n";
 //dlnsk            $doc .= "$ {$formula} $\n";
@@ -90,7 +90,8 @@
             global $CFG;
 
             // quick check - will this work?
-            if (empty($CFG->filter_tex_pathlatex)) {
+            $pathlatex = get_config('filter_tex', 'pathlatex');
+            if (empty($pathlatex)) {
                 return false;
             }
 
             $tex = "{$this->temp_dir}/$filename.tex";
             $dvi = "{$this->temp_dir}/$filename.dvi";
             $ps  = "{$this->temp_dir}/$filename.ps";
-            $img = "{$this->temp_dir}/$filename.{$CFG->filter_tex_convertformat}";
+            $convertformat = get_config('filter_tex', 'convertformat');
+            $img = "{$this->temp_dir}/$filename.{$convertformat}";
 
             // turn the latex doc into a .tex file in the temp area
             $fh = fopen( $tex, 'w' );
             fclose( $fh );
 
             // run latex on document
-            $command = "{$CFG->filter_tex_pathlatex} --interaction=nonstopmode --halt-on-error $tex";
+            $command = "{$pathlatex} --interaction=nonstopmode --halt-on-error $tex";
             chdir( $this->temp_dir );
             if ($this->execute($command, $log)) { // It allways False on Windows
 //                return false;
             }
 
             // run dvips (.dvi to .ps)
-            $command = "{$CFG->filter_tex_pathdvips} -E $dvi -o $ps";
+            $pathdvips = get_config('filter_tex', 'pathdvips');
+            $command = "{$pathdvips} -E $dvi -o $ps";
             if ($this->execute($command, $log )) {
                 return false;
             }
             } else {
                 $bg_opt = "";
             }
-            $command = "{$CFG->filter_tex_pathconvert} -density $density -trim $bg_opt $ps $img";
+            $pathconvert = get_config('filter_tex', 'pathconvert');
+            $command = "{$pathconvert} -density $density -trim $bg_opt $ps $img";
             if ($this->execute($command, $log )) {
                 return false;
             }
             unlink( "{$this->temp_dir}/$filename.tex" );
             unlink( "{$this->temp_dir}/$filename.dvi" );
             unlink( "{$this->temp_dir}/$filename.ps" );
-            unlink( "{$this->temp_dir}/$filename.{$CFG->filter_tex_convertformat}" );
+            $convertformat = get_config('filter_tex', 'convertformat');
+            unlink( "{$this->temp_dir}/$filename.{$convertformat}" );
             unlink( "{$this->temp_dir}/$filename.aux" );
             unlink( "{$this->temp_dir}/$filename.log" );
             return;
index c6b5989..b77dfae 100644 (file)
@@ -42,6 +42,14 @@ function filter_tex_get_executable($debug=false) {
         return "$CFG->dirroot/filter/tex/mimetex.exe";
     }
 
+    if ($pathmimetex = get_config('filter_tex', 'pathmimetex')) {
+        if (is_executable($pathmimetex)) {
+            return $pathmimetex;
+        } else {
+            print_error('mimetexnotexecutable', 'error');
+        }
+    }
+
     $custom_commandpath = "$CFG->dirroot/filter/tex/mimetex";
     if (file_exists($custom_commandpath)) {
         if (is_executable($custom_commandpath)) {
@@ -111,16 +119,20 @@ function filter_tex_updatedcallback($name) {
     $DB->delete_records('cache_filters', array('filter'=>'tex'));
     $DB->delete_records('cache_filters', array('filter'=>'algebra'));
 
-    if (!isset($CFG->filter_tex_pathlatex)) {
+    $pathlatex = get_config('filter_tex', 'pathlatex');
+    if ($pathlatex === false) {
         // detailed settings not present yet
         return;
     }
 
-    if (!(is_file($CFG->filter_tex_pathlatex) && is_executable($CFG->filter_tex_pathlatex) &&
-          is_file($CFG->filter_tex_pathdvips) && is_executable($CFG->filter_tex_pathdvips) &&
-          is_file($CFG->filter_tex_pathconvert) && is_executable($CFG->filter_tex_pathconvert))) {
+    $pathdvips = get_config('filter_tex', 'pathdvips');
+    $pathconvert = get_config('filter_tex', 'pathconvert');
+
+    if (!(is_file($pathlatex) && is_executable($pathlatex) &&
+          is_file($pathdvips) && is_executable($pathdvips) &&
+          is_file($pathconvert) && is_executable($pathconvert))) {
         // LaTeX, dvips or convert are not available, and mimetex can only produce GIFs so...
-        set_config('filter_tex_convertformat', 'gif');
+        set_config('convertformat', 'gif', 'filter_tex');
     }
 }
 
index 4ae2cd0..4249e7f 100644 (file)
@@ -32,7 +32,8 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
     }
 
     if (!file_exists($pathname)) {
-        $md5 = str_replace(".{$CFG->filter_tex_convertformat}",'',$image);
+        $convertformat = get_config('filter_tex', 'convertformat');
+        $md5 = str_replace(".{$convertformat}", '', $image);
         if ($texcache = $DB->get_record('cache_filters', array('filter'=>'tex', 'md5key'=>$md5))) {
             if (!file_exists($CFG->dataroot.'/filter/tex')) {
                 make_upload_directory('filter/tex');
@@ -40,8 +41,8 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
             // try and render with latex first
             $latex = new latex();
-            $density = $CFG->filter_tex_density;
-            $background = $CFG->filter_tex_latexbackground;
+            $density = get_config('filter_tex', 'density');
+            $background = get_config('filter_tex', 'latexbackground');
             $texexp = $texcache->rawtext; // the entities are now decoded before inserting to DB
             $latex_path = $latex->render($texexp, $md5, 12, $density, $background);
             if ($latex_path) {
index c0244e8..6c79a21 100644 (file)
@@ -30,11 +30,11 @@ if ($ADMIN->fulltree) {
     require_once($CFG->dirroot.'/filter/tex/lib.php');
 
     $items = array();
-    $items[] = new admin_setting_heading('filter_tex_latexheading', get_string('latexsettings', 'admin'), '');
-    $items[] = new admin_setting_configtextarea('filter_tex_latexpreamble', get_string('latexpreamble','admin'),
+    $items[] = new admin_setting_heading('filter_tex/latexheading', get_string('latexsettings', 'filter_tex'), '');
+    $items[] = new admin_setting_configtextarea('filter_tex/latexpreamble', get_string('latexpreamble','filter_tex'),
                    '', "\\usepackage[latin1]{inputenc}\n\\usepackage{amsmath}\n\\usepackage{amsfonts}\n\\RequirePackage{amsmath,amssymb,latexsym}\n");
-    $items[] = new admin_setting_configtext('filter_tex_latexbackground', get_string('backgroundcolour', 'admin'), '', '#FFFFFF');
-    $items[] = new admin_setting_configtext('filter_tex_density', get_string('density', 'admin'), '', '120', PARAM_INT);
+    $items[] = new admin_setting_configtext('filter_tex/latexbackground', get_string('backgroundcolour', 'admin'), '', '#FFFFFF');
+    $items[] = new admin_setting_configtext('filter_tex/density', get_string('density', 'admin'), '', '120', PARAM_INT);
 
     if (PHP_OS=='Linux') {
         $default_filter_tex_pathlatex   = "/usr/bin/latex";
@@ -60,18 +60,19 @@ if ($ADMIN->fulltree) {
         $default_filter_tex_pathconvert = '';
     }
 
-    $items[] = new admin_setting_configexecutable('filter_tex_pathlatex', get_string('pathlatex', 'admin'), '', $default_filter_tex_pathlatex);
-    $items[] = new admin_setting_configexecutable('filter_tex_pathdvips', get_string('pathdvips', 'admin'), '', $default_filter_tex_pathdvips);
-    $items[] = new admin_setting_configexecutable('filter_tex_pathconvert', get_string('pathconvert', 'admin'), '', $default_filter_tex_pathconvert);
+    $items[] = new admin_setting_configexecutable('filter_tex/pathlatex', get_string('pathlatex', 'filter_tex'), '', $default_filter_tex_pathlatex);
+    $items[] = new admin_setting_configexecutable('filter_tex/pathdvips', get_string('pathdvips', 'filter_tex'), '', $default_filter_tex_pathdvips);
+    $items[] = new admin_setting_configexecutable('filter_tex/pathconvert', get_string('pathconvert', 'filter_tex'), '', $default_filter_tex_pathconvert);
+    $items[] = new admin_setting_configexecutable('filter_tex/pathmimetex', get_string('pathmimetex', 'filter_tex'), get_string('pathmimetexdesc', 'filter_tex'), '');
 
     // Even if we offer GIF and PNG formats here, in the update callback we check whether
     // all the paths actually point to executables. If they don't, we force the setting
     // to GIF, as that's the only format mimeTeX can produce.
     $formats = array('gif' => 'GIF', 'png' => 'PNG');
-    $items[] = new admin_setting_configselect('filter_tex_convertformat', get_string('convertformat', 'admin'), get_string('configconvertformat', 'admin'), 'gif', $formats);
+    $items[] = new admin_setting_configselect('filter_tex/convertformat', get_string('convertformat', 'filter_tex'), get_string('configconvertformat', 'filter_tex'), 'gif', $formats);
 
     foreach ($items as $item) {
         $item->set_updatedcallback('filter_tex_updatedcallback');
         $settings->add($item);
     }
-}
\ No newline at end of file
+}
index 0817d7e..c56df3e 100644 (file)
         // first check if it is likely to work at all
         $output .= "<h3>Checking executables</h3>\n";
         $executables_exist = true;
-        if (is_file($CFG->filter_tex_pathlatex)) {
-            $output .= "latex executable ($CFG->filter_tex_pathlatex) is readable<br />\n";
+        $pathlatex = get_config('filter_tex', 'pathlatex');
+        if (is_file($pathlatex)) {
+            $output .= "latex executable ($pathlatex) is readable<br />\n";
         }
         else {
             $executables_exist = false;
-            $output .= "<b>Error:</b> latex executable ($CFG->filter_tex_pathlatex) is not readable<br />\n";
+            $output .= "<b>Error:</b> latex executable ($pathlatex) is not readable<br />\n";
         }
-        if (is_file($CFG->filter_tex_pathdvips)) {
-            $output .= "dvips executable ($CFG->filter_tex_pathdvips) is readable<br />\n";
+        $pathdvips = get_config('filter_tex', 'pathdvips');
+        if (is_file($pathdvips)) {
+            $output .= "dvips executable ($pathdvips) is readable<br />\n";
         }
         else {
             $executables_exist = false;
-            $output .= "<b>Error:</b> dvips executable ($CFG->filter_tex_pathdvips) is not readable<br />\n";
+            $output .= "<b>Error:</b> dvips executable ($pathdvips) is not readable<br />\n";
         }
-        if (is_file($CFG->filter_tex_pathconvert)) {
-            $output .= "convert executable ($CFG->filter_tex_pathconvert) is readable<br />\n";
+        $pathconvert = get_config('filter_tex', 'pathconvert');
+        if (is_file($pathconvert)) {
+            $output .= "convert executable ($pathconvert) is readable<br />\n";
         }
         else {
             $executables_exist = false;
-            $output .= "<b>Error:</b> convert executable ($CFG->filter_tex_pathconvert) is not readable<br />\n";
+            $output .= "<b>Error:</b> convert executable ($pathconvert) is not readable<br />\n";
         }
 
         // knowing that it might work..
         $tex = "$latex->temp_dir/$md5.tex";
         $dvi = "$latex->temp_dir/$md5.dvi";
         $ps = "$latex->temp_dir/$md5.ps";
-        $img = "$latex->temp_dir/$md5.{$CFG->filter_tex_convertformat}";
+        $convertformat = get_config('filter_tex', 'convertformat');
+        $img = "$latex->temp_dir/$md5.{$convertformat}";
 
         // put the expression as a file into the temp area
         $expression = html_entity_decode($expression);
         chdir($latex->temp_dir);
 
         // step 1: latex command
-        $cmd = "$CFG->filter_tex_pathlatex --interaction=nonstopmode --halt-on-error $tex";
+        $cmd = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
         $output .= execute($cmd);
 
         // step 2: dvips command
-        $cmd = "$CFG->filter_tex_pathdvips -E $dvi -o $ps";
+        $cmd = "$pathdvips -E $dvi -o $ps";
         $output .= execute($cmd);
 
         // step 3: convert command
-        $cmd = "$CFG->filter_tex_pathconvert -density 240 -trim $ps $img ";
+        $cmd = "$pathconvert -density 240 -trim $ps $img ";
         $output .= execute($cmd);
 
         if (!$graphic) {
             echo $output;
         } else if (file_exists($img)){
-            send_file($img, "$md5.{$CFG->filter_tex_convertformat}");
+            send_file($img, "$md5.{$convertformat}");
         } else {
             echo "Error creating image, see command execution output for more details.";
         }
index 10a6576..5104f39 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013110500;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013120300;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013110500;        // Requires this Moodle version
 $plugin->component = 'filter_tex';      // Full name of the plugin (used for diagnostics)