Merge branch 'MDL-43232-master' of git://github.com/andrewnicols/moodle
authorSam Hemelryk <sam@moodle.com>
Mon, 9 Dec 2013 20:06:54 +0000 (09:06 +1300)
committerSam Hemelryk <sam@moodle.com>
Mon, 9 Dec 2013 20:06:54 +0000 (09:06 +1300)
261 files changed:
.jshintrc
admin/enrol.php
admin/index.php
admin/mnet/access_control.php
admin/renderer.php
admin/settings/appearance.php
admin/settings/plugins.php
admin/settings/server.php
admin/tests/behat/behat_admin.php
admin/tests/behat/display_short_names.feature
admin/tool/behat/cli/util.php
admin/tool/behat/tests/behat/data_generators.feature
admin/tool/behat/tests/tool_behat_test.php
admin/tool/customlang/index.php
admin/tool/dbtransfer/locallib.php
admin/tool/generator/classes/backend.php
admin/tool/innodb/index.php
admin/tool/langimport/index.php
admin/tool/multilangupgrade/index.php
admin/tool/phpunit/webrunner.php
admin/tool/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
auth/db/auth.php
auth/imap/auth.php
auth/ldap/auth.php
auth/pop3/auth.php
auth/upgrade.txt
auth/webservice/auth.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/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js
blocks/navigation/yui/src/navigation/js/navigation.js
blocks/rss_client/block_rss_client.php
blocks/site_main_menu/block_site_main_menu.php
blocks/tags/block_tags.php
blog/index.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
cohort/tests/behat/behat_cohort.php
cohort/tests/behat/upload_cohort_users.feature
composer.json
config-dist.php
course/format/renderer.php
course/lib.php
course/tests/behat/behat_course.php
course/tests/courselib_test.php
course/tests/externallib_test.php
enrol/category/locallib.php
enrol/cohort/locallib.php
enrol/database/lib.php
enrol/flatfile/lib.php
enrol/imsenterprise/lib.php
enrol/ldap/lib.php
enrol/manual/lib.php
enrol/meta/locallib.php
enrol/self/db/upgrade.php
enrol/self/lib.php
enrol/self/version.php
filter/algebra/thirdpartylibs.xml [new file with mode: 0644]
filter/tex/db/upgrade.php
filter/tex/filter.php
filter/tex/lang/en/filter_tex.php
filter/tex/latex.php
filter/tex/lib.php
filter/tex/pix.php
filter/tex/settings.php
filter/tex/texdebug.php
filter/tex/version.php
grade/import/csv/index.php
grade/import/xml/import.php
grade/import/xml/index.php
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/grader/preferences.php
grade/report/lib.php
grade/report/upgrade.txt
group/tests/behat/id_uniqueness.feature
install/lang/de/install.php
install/lang/hu/install.php
lang/en/admin.php
lang/en/blog.php
lang/en/cache.php
lang/en/message.php
lang/en/mnet.php
lang/en/moodle.php
lang/en/plugin.php
lang/en/repository.php
lib/accesslib.php
lib/adminlib.php
lib/ajax/getsiteadminbranch.php
lib/authlib.php
lib/badgeslib.php
lib/behat/classes/behat_command.php
lib/behat/classes/behat_config_manager.php
lib/behat/classes/util.php
lib/behat/lib.php
lib/classes/component.php
lib/classes/event/base.php
lib/classes/event/blog_association_created.php [new file with mode: 0644]
lib/classes/event/blog_entries_viewed.php [new file with mode: 0644]
lib/classes/event/course_module_viewed.php [new file with mode: 0644]
lib/classes/minify.php
lib/classes/php_time_limit.php [new file with mode: 0644]
lib/classes/plugininfo/base.php
lib/classes/session/file.php
lib/classes/session/manager.php
lib/classes/update/checker.php
lib/classes/update/deployer.php
lib/completionlib.php
lib/cronlib.php
lib/enrollib.php
lib/externallib.php
lib/filelib.php
lib/form/form.js
lib/formslib.php
lib/messagelib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/setup.php
lib/setuplib.php
lib/statslib.php
lib/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/event_test.php
lib/tests/filelib_test.php
lib/tests/fixtures/event_fixtures.php
lib/tests/messagelib_test.php
lib/tests/minify_test.php
lib/tests/performance/filtersettingsperformancetester.php
lib/tests/setuplib_test.php
lib/tests/statslib_test.php
lib/upgrade.txt
lib/upgradelib.php
lib/webdavlib.php
lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks.js
lib/yui/build/moodle-core-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/blocks/js/blocks.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]
mdeploy.php
message/defaultoutputs.php
message/module.js
message/renderer.php
message/tests/behat/block_users.feature
message/tests/behat/display_history.feature
message/tests/behat/manage_contacts.feature
message/tests/behat/search_history.feature
mod/assign/backup/moodle2/restore_assign_activity_task.class.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/file/importziplib.php
mod/assign/feedback/file/locallib.php
mod/assign/locallib.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/tests/upgradelib_test.php
mod/assign/upgradelib.php
mod/book/classes/event/course_module_viewed.php
mod/book/lang/en/book.php
mod/chat/chatd.php
mod/chat/gui_header_js/jsupdated.php
mod/choice/classes/event/course_module_viewed.php
mod/choice/lang/en/choice.php
mod/choice/view.php
mod/data/field/picture/field.class.php
mod/data/import.php
mod/feedback/classes/event/course_module_viewed.php
mod/feedback/lang/en/feedback.php
mod/feedback/view.php
mod/folder/renderer.php
mod/forum/lib.php
mod/forum/tests/behat/edit_post_student.feature
mod/lesson/lib.php
mod/page/classes/event/course_module_viewed.php
mod/page/lang/en/page.php
mod/page/view.php
mod/quiz/db/log.php
mod/quiz/lib.php
mod/quiz/report/overview/report.php
mod/quiz/report/statistics/statistics_graph.php
mod/quiz/version.php
mod/wiki/tests/behat/page_history.feature
mod/workshop/classes/event/course_module_viewed.php
mod/workshop/lang/en/workshop.php
mod/workshop/view.php
my/indexsys.php
question/classes/statistics/questions/calculator.php
question/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/multichoice/styles.css
report/security/index.php
report/security/locallib.php
repository/boxnet/lib.php
repository/dropbox/lib.php
repository/equella/lib.php
repository/filepicker.php
repository/filesystem/lib.php
repository/lib.php
repository/repository_ajax.php
repository/repository_callback.php
repository/upgrade.txt
tag/manage.php
theme/afterburner/style/afterburner_styles.css
theme/base/style/admin.css
theme/base/style/core.css
theme/base/style/question.css
theme/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/readme_moodle.txt
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-debug.js
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-min.js
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapcollapse.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapdropdown.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapengine.js
theme/clean/layout/secure.php
theme/yui_combo.php
user/editadvanced.php
user/editadvanced_form.php
user/profilesys.php
user/renderer.php
user/tests/behat/edituserpassword.feature [new file with mode: 0644]
version.php

index 06de646..89f05f1 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -42,7 +42,7 @@
     "sub":          false,
     "supernew":     false,
     "maxerr":       500,
-    "maxlen":       150,
+    "maxlen":       180,
     "passfail":     false,
     "latedef":      true
 }
index 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 ea9f37d..a07104c 100644 (file)
@@ -47,7 +47,9 @@ if (!function_exists('iconv')) {
 
 define('NO_OUTPUT_BUFFERING', true);
 
-if (empty($_GET['cache']) and empty($_POST['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey'])) {
+if ((isset($_GET['cache']) and $_GET['cache'] === '0')
+        or (isset($_POST['cache']) and $_POST['cache'] === '0')
+        or (!isset($_POST['cache']) and !isset($_GET['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey']))) {
     // Prevent caching at all cost when visiting this page directly,
     // we redirect to self once we known no upgrades are necessary.
     // Note: $_GET and $_POST are used here intentionally because our param cleaning is not loaded yet.
@@ -90,9 +92,7 @@ $newaddonreq    = optional_param('installaddonrequest', null, PARAM_RAW);
 
 // Set up PAGE.
 $url = new moodle_url('/admin/index.php');
-if ($cache) {
-    $url->param('cache', 1);
-}
+$url->param('cache', $cache);
 $PAGE->set_url($url);
 unset($url);
 
@@ -267,12 +267,13 @@ if (!$cache and $version > $CFG->version) {  // upgrade
     $PAGE->set_pagelayout('maintenance');
     $PAGE->set_popup_notification_allowed(false);
 
+    /** @var core_admin_renderer $output */
+    $output = $PAGE->get_renderer('core', 'admin');
+
     if (upgrade_stale_php_files_present()) {
         $PAGE->set_title($stradministration);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_stale_php_files_page();
         die();
     }
@@ -287,8 +288,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strdatabasechecking);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_confirm_page($a->newversion, $maturity, $testsite);
         die();
 
@@ -302,8 +301,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strcurrentrelease);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_environment_page($release, $envstatus, $environment_results);
         die();
 
@@ -315,23 +312,13 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strplugincheck);
         $PAGE->set_cacheable(false);
 
-        $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1));
-
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
-
-        // check plugin dependencies first
-        $failed = array();
-        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
-            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
-            die();
-        }
-        unset($failed);
+        $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0));
 
         if ($fetchupdates) {
-            // no sesskey support guaranteed here
-            if (empty($CFG->disableupdatenotifications)) {
-                \core\update\checker::instance()->fetch();
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            $updateschecker = \core\update\checker::instance();
+            if ($updateschecker->enabled()) {
+                $updateschecker->fetch();
             }
             redirect($reloadurl);
         }
@@ -342,6 +329,7 @@ if (!$cache and $version > $CFG->version) {  // upgrade
 
             $deploydata = $deployer->submitted_data();
             if (!empty($deploydata)) {
+                // No sesskey support guaranteed here, because sessions might not work yet.
                 echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
                 die();
             }
@@ -349,11 +337,22 @@ if (!$cache and $version > $CFG->version) {  // upgrade
 
         echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                 $version, $showallplugins, $reloadurl,
-                new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1)));
+                new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1, 'cache'=>0)));
         die();
 
     } else {
-        // Launch main upgrade
+        // Always verify plugin dependencies!
+        $failed = array();
+        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $PAGE->set_pagelayout('maintenance');
+            $PAGE->set_popup_notification_allowed(false);
+            $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0));
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
+            die();
+        }
+        unset($failed);
+
+        // Launch main upgrade.
         upgrade_core($version, true);
     }
 } else if ($version < $CFG->version) {
@@ -373,6 +372,10 @@ if (!$cache and $branch <> $CFG->branch) {  // Update the branch
 if (!$cache and moodle_needs_upgrading()) {
     if (!$PAGE->headerprinted) {
         // means core upgrade or installation was not already done
+
+        /** @var core_admin_renderer $output */
+        $output = $PAGE->get_renderer('core', 'admin');
+
         if (!$confirmplugins) {
             $strplugincheck = get_string('plugincheck');
 
@@ -384,40 +387,46 @@ if (!$cache and moodle_needs_upgrading()) {
             $PAGE->set_cacheable(false);
 
             if ($fetchupdates) {
-                // no sesskey support guaranteed here
-                \core\update\checker::instance()->fetch();
+                require_sesskey();
+                $updateschecker = \core\update\checker::instance();
+                if ($updateschecker->enabled()) {
+                    $updateschecker->fetch();
+                }
                 redirect($PAGE->url);
             }
 
-            $output = $PAGE->get_renderer('core', 'admin');
-
             $deployer = \core\update\deployer::instance();
             if ($deployer->enabled()) {
                 $deployer->initialize($PAGE->url, $PAGE->url);
 
                 $deploydata = $deployer->submitted_data();
                 if (!empty($deploydata)) {
+                    require_sesskey();
                     echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
                     die();
                 }
             }
 
-            // check plugin dependencies first
-            $failed = array();
-            if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
-                echo $output->unsatisfied_dependencies_page($version, $failed, $PAGE->url);
-                die();
-            }
-            unset($failed);
-
-            // dependencies check passed, let's rock!
+            // Show plugins info.
             echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                     $version, $showallplugins,
                     new moodle_url($PAGE->url),
-                    new moodle_url('/admin/index.php', array('confirmplugincheck'=>1)));
+                    new moodle_url('/admin/index.php', array('confirmplugincheck'=>1, 'cache'=>0)));
+            die();
+        }
+
+        // Make sure plugin dependencies are always checked.
+        $failed = array();
+        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $PAGE->set_pagelayout('maintenance');
+            $PAGE->set_popup_notification_allowed(false);
+            $reloadurl = new moodle_url('/admin/index.php', array('cache' => 0));
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
             die();
         }
+        unset($failed);
     }
+
     // install/upgrade all plugins and other parts
     upgrade_noncore(true);
 }
@@ -477,6 +486,17 @@ if (during_initial_install()) {
     upgrade_finished('upgradesettings.php');
 }
 
+if (has_capability('moodle/site:config', context_system::instance())) {
+    if ($fetchupdates) {
+        require_sesskey();
+        $updateschecker = \core\update\checker::instance();
+        if ($updateschecker->enabled()) {
+            $updateschecker->fetch();
+        }
+        redirect(new moodle_url('/admin/index.php', array('cache' => 0)));
+    }
+}
+
 // Now we can be sure everything was upgraded and caches work fine,
 // redirect if necessary to make sure caching is enabled.
 if (!$cache) {
@@ -564,12 +584,6 @@ $registered = $DB->count_records('registration_hubs', array('huburl' => HUB_MOOD
 
 admin_externalpage_setup('adminnotifications');
 
-if ($fetchupdates) {
-    require_sesskey();
-    $updateschecker->fetch();
-    redirect(new moodle_url('/admin/index.php'));
-}
-
 $output = $PAGE->get_renderer('core', 'admin');
 echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
         $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
index 79c744c..375b3fa 100644 (file)
@@ -16,8 +16,6 @@ require_login();
 
 admin_externalpage_setup('ssoaccesscontrol');
 
-echo $OUTPUT->header();
-
 if (!extension_loaded('openssl')) {
     print_error('requiresopenssl', 'mnet');
 }
@@ -65,9 +63,11 @@ if (!empty($action) and confirm_sesskey()) {
 
             if (mnet_update_sso_access_control($idrec->username, $idrec->mnet_host_id, $accessctrl)) {
                 if ($accessctrl == 'allow') {
-                    redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('uset'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
-                } elseif ($accessctrl == 'deny') {
-                    redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
+                    redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('user' => $idrec->username,
+                        'host' => $mnethosts[$idrec->mnet_host_id])));
+                } else if ($accessctrl == 'deny') {
+                    redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user' => $idrec->username,
+                        'host' => $mnethosts[$idrec->mnet_host_id])));
                 }
             }
             break;
@@ -118,6 +118,8 @@ if ($form = data_submitted() and confirm_sesskey()) {
     exit;
 }
 
+echo $OUTPUT->header();
+
 // Explain
 echo $OUTPUT->box(get_string('ssoacldescr','mnet'));
 // Are the needed bits enabled?
index 0f84e00..3ba44bb 100644 (file)
@@ -140,7 +140,7 @@ class core_admin_renderer extends plugin_renderer_base {
     public function upgrade_confirm_page($strnewversion, $maturity, $testsite) {
         $output = '';
 
-        $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1));
+        $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'cache' => 0));
         $continue = new single_button($continueurl, get_string('continue'), 'get');
         $cancelurl = new moodle_url('/admin/index.php');
 
@@ -170,7 +170,7 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->environment_check_table($envstatus, $environment_results);
 
         if (!$envstatus) {
-            $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1));
+            $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1, 'cache' => 0));
 
         } else {
             $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess');
@@ -179,7 +179,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $output .= $this->box(get_string('langpackwillbeupdated', 'admin'), 'generalbox', 'notice');
             }
 
-            $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1)));
+            $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)));
         }
 
         $output .= $this->footer();
@@ -711,7 +711,7 @@ class core_admin_renderer extends plugin_renderer_base {
         }
 
         $updateinfo .= $this->container_start('checkforupdates');
-        $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 1));
+        $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0));
         $updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin'));
         if ($fetch) {
             $updateinfo .= $this->container(get_string('checkforupdateslast', 'core_plugin',
@@ -962,7 +962,7 @@ class core_admin_renderer extends plugin_renderer_base {
             $out .= $this->output->heading(get_string('nonehighlighted', 'core_plugin'));
             if (empty($options['full'])) {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)),
                     get_string('nonehighlightedinfo', 'core_plugin'));
             }
             $out .= $this->output->container_end();
@@ -972,11 +972,11 @@ class core_admin_renderer extends plugin_renderer_base {
             $out .= $this->output->heading(get_string('somehighlighted', 'core_plugin', $sumofhighlighted));
             if (empty($options['full'])) {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)),
                     get_string('somehighlightedinfo', 'core_plugin'));
             } else {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0, 'cache' => 0)),
                     get_string('somehighlightedonly', 'core_plugin'));
             }
             $out .= $this->output->container_end();
@@ -1176,13 +1176,14 @@ class core_admin_renderer extends plugin_renderer_base {
             get_string('displayname', 'core_plugin'),
             get_string('source', 'core_plugin'),
             get_string('version', 'core_plugin'),
+            get_string('release', 'core_plugin'),
             get_string('availability', 'core_plugin'),
             get_string('actions', 'core_plugin'),
             get_string('notes','core_plugin'),
         );
-        $table->headspan = array(1, 1, 1, 1, 2, 1);
+        $table->headspan = array(1, 1, 1, 1, 1, 2, 1);
         $table->colclasses = array(
-            'pluginname', 'source', 'version', 'availability', 'settings', 'uninstall', 'notes'
+            'pluginname', 'source', 'version', 'release', 'availability', 'settings', 'uninstall', 'notes'
         );
 
         foreach ($plugininfo as $type => $plugins) {
@@ -1238,6 +1239,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 }
 
                 $version = new html_table_cell($plugin->versiondb);
+                $release = new html_table_cell($plugin->release);
 
                 $isenabled = $plugin->is_enabled();
                 if (is_null($isenabled)) {
@@ -1283,7 +1285,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $notes = new html_table_cell($requiredby.$updateinfo);
 
                 $row->cells = array(
-                    $pluginname, $source, $version, $availability, $settings, $uninstall, $notes
+                    $pluginname, $source, $version, $release, $availability, $settings, $uninstall, $notes
                 );
                 $table->data[] = $row;
             }
index 1ee3a7a..6505c96 100644 (file)
@@ -2,7 +2,12 @@
 
 // This file defines settingpages and externalpages under the "appearance" category
 
-if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
+$capabilities = array(
+    'moodle/my:configsyspages',
+    'moodle/tag:manage'
+);
+
+if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // speedup for non-admins, add all caps used on this page
 
     $ADMIN->add('appearance', new admin_category('themes', new lang_string('themes')));
     // "themesettings" settingpage
@@ -186,10 +191,12 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configcheckbox('doctonewwindow', new lang_string('doctonewwindow', 'admin'), new lang_string('configdoctonewwindow', 'admin'), 0));
     $ADMIN->add('appearance', $temp);
 
-    $temp = new admin_externalpage('mypage', new lang_string('mypage', 'admin'), $CFG->wwwroot . '/my/indexsys.php');
+    $temp = new admin_externalpage('mypage', new lang_string('mypage', 'admin'), $CFG->wwwroot . '/my/indexsys.php',
+            'moodle/my:configsyspages');
     $ADMIN->add('appearance', $temp);
 
-    $temp = new admin_externalpage('profilepage', new lang_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php');
+    $temp = new admin_externalpage('profilepage', new lang_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php',
+            'moodle/my:configsyspages');
     $ADMIN->add('appearance', $temp);
 
     // coursecontact is the person responsible for course - usually manages enrolments, receives notification, etc.
@@ -219,7 +226,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $ADMIN->add('appearance', $temp);
 
     // link to tag management interface
-    $ADMIN->add('appearance', new admin_externalpage('managetags', new lang_string('managetags', 'tag'), "$CFG->wwwroot/tag/manage.php"));
+    $ADMIN->add('appearance', new admin_externalpage('managetags', new lang_string('managetags', 'tag'), $CFG->wwwroot.'/tag/manage.php', 'moodle/tag:manage'));
 
     $temp = new admin_settingpage('additionalhtml', new lang_string('additionalhtml', 'admin'));
     $temp->add(new admin_setting_heading('additionalhtml_heading', new lang_string('additionalhtml_heading', 'admin'), new lang_string('additionalhtml_desc', 'admin')));
index 5391cf7..283d02a 100644 (file)
@@ -256,7 +256,10 @@ if ($hassiteconfig) {
 
     // Add common settings page
     $temp = new admin_settingpage('managerepositoriescommon', new lang_string('commonrepositorysettings', 'repository'));
-    $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120));
+    $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorygetfiletimeout', new lang_string('getfiletimeout', 'repository'), new lang_string('configgetfiletimeout', 'repository'), 30, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorysyncfiletimeout', new lang_string('syncfiletimeout', 'repository'), new lang_string('configsyncfiletimeout', 'repository'), 1, PARAM_INT));
+    $temp->add(new admin_setting_configtext('repositorysyncimagetimeout', new lang_string('syncimagetimeout', 'repository'), new lang_string('configsyncimagetimeout', 'repository'), 3, PARAM_INT));
     $temp->add(new admin_setting_configcheckbox('repositoryallowexternallinks', new lang_string('allowexternallinks', 'repository'), new lang_string('configallowexternallinks', 'repository'), 1));
     $temp->add(new admin_setting_configcheckbox('legacyfilesinnewcourses', new lang_string('legacyfilesinnewcourses', 'admin'), new lang_string('legacyfilesinnewcourses_help', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('legacyfilesaddallowed', new lang_string('legacyfilesaddallowed', 'admin'), new lang_string('legacyfilesaddallowed_help', 'admin'), 1));
index ea2491a..20f69b2 100644 (file)
@@ -93,6 +93,7 @@ $options = array(
     GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR => 'HTTP_CLIENT, REMOTE_ADDR',
     GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR|GETREMOTEADDR_SKIP_HTTP_CLIENT_IP => 'REMOTE_ADDR');
 $temp->add(new admin_setting_configselect('getremoteaddrconf', new lang_string('getremoteaddrconf', 'admin'), new lang_string('configgetremoteaddrconf', 'admin'), 0, $options));
+
 $temp->add(new admin_setting_heading('webproxy', new lang_string('webproxy', 'admin'), new lang_string('webproxyinfo', 'admin')));
 $temp->add(new admin_setting_configtext('proxyhost', new lang_string('proxyhost', 'admin'), new lang_string('configproxyhost', 'admin'), '', PARAM_HOST));
 $temp->add(new admin_setting_configtext('proxyport', new lang_string('proxyport', 'admin'), new lang_string('configproxyport', 'admin'), 0, PARAM_INT));
@@ -195,6 +196,8 @@ if (PHP_INT_SIZE === 8) {
 $temp->add(new admin_setting_configselect('extramemorylimit', new lang_string('extramemorylimit', 'admin'),
                                           new lang_string('configextramemorylimit', 'admin'), '512M',
                                           $memoryoptions));
+$temp->add(new admin_setting_configtext('maxtimelimit', new lang_string('maxtimelimit', 'admin'),
+        new lang_string('maxtimelimit_desc', 'admin'), 0, PARAM_INT));
 
 $temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
                                         new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
index 10d39eb..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 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 c8fa488..09db5e6 100644 (file)
@@ -150,6 +150,11 @@ class tool_behat_testcase extends advanced_testcase {
             $this->markTestSkipped('Behat not installed.');
         }
 
+        // It is possible that it has no value.
+        if (empty($CFG->behat_wwwroot)) {
+            $CFG->behat_wwwroot = behat_get_wwwroot();
+        }
+
         // To avoid user value at config.php level.
         unset($CFG->behat_config);
 
@@ -183,6 +188,8 @@ class tool_behat_testcase extends advanced_testcase {
         // Lists.
         $this->assertContains('- feature1', $contents);
         $this->assertContains('- feature3', $contents);
+
+        unset($CFG->behat_wwwroot);
     }
 
 }
index 431a911..4d120ff 100644 (file)
@@ -55,7 +55,7 @@ if ($action === 'checkout') {
     $progressbar->create();         // prints the HTML code of the progress bar
 
     // we may need a bit of extra execution time and memory here
-    @set_time_limit(HOURSECS);
+    core_php_time_limit::raise(HOURSECS);
     raise_memory_limit(MEMORY_EXTRA);
     tool_customlang_utils::checkout($lng, $progressbar);
 
index c7e622f..f37f62a 100644 (file)
@@ -50,7 +50,7 @@ require_once($CFG->libdir.'/dtllib.php');
  * @return does not return, calls die()
  */
 function tool_dbtransfer_export_xml_database($description, $mdb) {
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     \core\session\manager::write_close(); // Release session.
 
@@ -77,7 +77,7 @@ function tool_dbtransfer_export_xml_database($description, $mdb) {
  * @return void
  */
 function tool_dbtransfer_transfer_database(moodle_database $sourcedb, moodle_database $targetdb, progress_trace $feedback = null) {
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     \core\session\manager::write_close(); // Release session.
 
index da931db..4299599 100644 (file)
@@ -177,7 +177,7 @@ abstract class tool_generator_backend {
 
         // Update time limit so PHP doesn't time out.
         if (!CLI_SCRIPT) {
-            set_time_limit(120);
+            core_php_time_limit::raise(120);
         }
     }
 
index 06f4f6a..6e99834 100644 (file)
@@ -57,7 +57,7 @@ if (data_submitted() and $confirm and confirm_sesskey()) {
 
     echo $OUTPUT->notification('Please be patient and wait for this to complete...', 'notifysuccess');
 
-    set_time_limit(0);
+    core_php_time_limit::raise();
 
     foreach ($rs as $table) {
         $DB->set_debug(true);
index 82b5cc2..36b416f 100644 (file)
@@ -69,7 +69,7 @@ $notice_ok    = array();
 $notice_error = array();
 
 if (($mode == INSTALLATION_OF_SELECTED_LANG) and confirm_sesskey() and !empty($pack)) {
-    set_time_limit(0);
+    core_php_time_limit::raise();
     make_temp_directory('');
     make_upload_directory('lang');
 
@@ -125,7 +125,7 @@ if ($mode == DELETION_OF_SELECTED_LANG and !empty($uninstalllang)) {
 }
 
 if ($mode == UPDATE_ALL_LANG) {
-    set_time_limit(0);
+    core_php_time_limit::raise();
 
     $installer = new lang_installer();
 
index 04dd691..6b4ce5c 100644 (file)
@@ -59,7 +59,7 @@ echo $OUTPUT->box_start();
 
 /// Turn off time limits, sometimes upgrades can be slow.
 
-@set_time_limit(0);
+core_php_time_limit::raise();
 
 echo '<strong>Progress:</strong>';
 $i = 0;
index f52bf6b..b565ae1 100644 (file)
@@ -38,7 +38,7 @@ if (!$CFG->debugdeveloper) {
     error('Not available on production sites, sorry.');
 }
 
-set_time_limit(60*30);
+core_php_time_limit::raise(60*30);
 
 $oldcwd = getcwd();
 $code = 0;
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 f6a178e..a45cb47 100644 (file)
@@ -36,7 +36,7 @@ require_once('user_form.php');
 $iid         = optional_param('iid', '', PARAM_INT);
 $previewrows = optional_param('previewrows', 10, PARAM_INT);
 
-@set_time_limit(60*60); // 1 hour should be enough
+core_php_time_limit::raise(60*60); // 1 hour should be enough
 raise_memory_limit(MEMORY_HUGE);
 
 require_login();
@@ -549,8 +549,6 @@ if ($formdata = $mform2->is_cancelled()) {
                         continue;
                     }
                     if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
-                        // this should never happen
-                        debugging("Could not find $column on the user objects", DEBUG_DEVELOPER);
                         continue;
                     }
                     if ($updatetype == UU_UPDATE_MISSING) {
index fe3d8e5..03091aa 100644 (file)
@@ -72,7 +72,7 @@ if ($formdata = $mform->get_data()) {
         // Large files are likely to take their time and memory. Let PHP know
         // that we'll take longer, and that the process should be recycled soon
         // to free up memory.
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_EXTRA);
 
         // Create a unique temporary directory, to process the zip file
index 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 6b4bdff..5e25aa9 100644 (file)
@@ -120,7 +120,11 @@ class auth_plugin_pop3 extends auth_plugin_base {
      * @return moodle_url
      */
     function change_password_url() {
-        return new moodle_url($this->config->changepasswordurl);
+        if (!empty($this->config->changepasswordurl)) {
+            return new moodle_url($this->config->changepasswordurl);
+        } else {
+            return null;
+        }
     }
 
     /**
index 6085397..8334fed 100644 (file)
@@ -1,6 +1,10 @@
 This files describes API changes in /auth/* - plugins,
 information provided here is intended especially for developers.
 
+=== 2.7 ===
+
+* If you are returning a url in method change_password_url() from config, please make sure it is set before trying to use it.
+
 === 2.6 ===
 
 * can_be_manually_set() - This function was introduced in the base class and returns false by default. If overriden by
index 1fc1e99..1fc5694 100644 (file)
@@ -90,10 +90,12 @@ class auth_plugin_webservice extends auth_plugin_base {
     /**
      * Returns true if this authentication plugin is 'internal'.
      *
+     * Webserice auth doesn't use password fields, it uses only tokens.
+     *
      * @return bool
      */
     function is_internal() {
-        return true;
+        return false;
     }
 
     /**
index 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 74c80f6..7204e58 100644 (file)
@@ -31,9 +31,14 @@ abstract class core_backup_progress {
     const INDETERMINATE = -1;
 
     /**
+     * This value is set rather high to ensure there are no regressions from
+     * previous behaviour. For testing, it may be useful to set the
+     * frontendservertimeout config option to a lower value, such as 180
+     * seconds (default for some commercial products).
+     *
      * @var int The number of seconds that can pass without progress() calls.
      */
-    const TIME_LIMIT_WITHOUT_PROGRESS = 120;
+    const TIME_LIMIT_WITHOUT_PROGRESS = 3600;
 
     /**
      * @var int Time of last progress call.
@@ -201,7 +206,9 @@ abstract class core_backup_progress {
         // Update progress.
         $this->count++;
         $this->lastprogresstime = $now;
-        set_time_limit(self::TIME_LIMIT_WITHOUT_PROGRESS);
+
+        // Update time limit before next progress display.
+        core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
         $this->update_progress();
     }
 
index 28f9f03..1e4f8c8 100644 (file)
@@ -56,9 +56,11 @@ class backup_progress_testcase extends basic_testcase {
 
         // Make some progress and check that the time limit gets added.
         $progress->step_time();
+        core_php_time_limit::get_and_clear_unit_test_data();
         $progress->progress(2);
         $this->assertTrue($progress->was_update_called());
-        $this->assertEquals(120, ini_get('max_execution_time'));
+        $this->assertEquals(array(core_backup_progress::TIME_LIMIT_WITHOUT_PROGRESS),
+                core_php_time_limit::get_and_clear_unit_test_data());
 
         // Check the new value.
         $this->assert_min_max(0.2, 0.2, $progress);
@@ -77,9 +79,6 @@ class backup_progress_testcase extends basic_testcase {
 
         // There was 1 progress call.
         $this->assertEquals(1, $progress->get_progress_count());
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
@@ -158,8 +157,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.8, 0.8, $progress);
         $progress->end_progress();
         $this->assertFalse($progress->is_in_progress_section());
-
-        set_time_limit(0);
     }
 
     /**
@@ -192,8 +189,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.4, 1.0, $progress);
         $progress->end_progress();
         $this->assert_min_max(1.0, 1.0, $progress);
-
-        set_time_limit(0);
     }
 
     /**
@@ -208,9 +203,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.01, 0.01, $progress);
         $progress->end_progress();
         $this->assert_min_max(0.01, 0.01, $progress);
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
@@ -231,8 +223,6 @@ class backup_progress_testcase extends basic_testcase {
         $this->assert_min_max(0.02, 0.02, $progress);
         $progress->end_progress();
         $this->assert_min_max(0.02, 0.02, $progress);
-
-        set_time_limit(0);
     }
 
     /**
@@ -329,9 +319,6 @@ class backup_progress_testcase extends basic_testcase {
         } catch (coding_exception $e) {
             $this->assertEquals(1, preg_match('~would exceed max~', $e->getMessage()));
         }
-
-        // Clear the time limit, otherwise phpunit complains.
-        set_time_limit(0);
     }
 
     /**
index 8c51dd5..9866705 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 5550c33..7a116ee 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js differ
index f0d0522..c48a7c3 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js differ
index 6dd4a48..0ecd3b9 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js differ
index 6ac1510..7db6ca9 100644 (file)
@@ -409,7 +409,7 @@ Y.extend(TREE, Y.Base, TREE.prototype, {
         instance : {
             value : false,
             setter : function(val) {
-                return parseInt(val);
+                return parseInt(val, 10);
             }
         }
     }
@@ -590,7 +590,7 @@ BRANCH.prototype = {
         } else {
             e.stopPropagation();
         }
-        if (e.type === 'actionkey' && e.action === 'enter' && e.target.test('A')) {
+        if ((e.type === 'actionkey' && e.action === 'enter') || e.target.test('a')) {
             // No ajaxLoad for enter.
             this.node.setAttribute('data-expandable', '0');
             this.node.setAttribute('data-loaded', '1');
@@ -647,6 +647,12 @@ BRANCH.prototype = {
         this.node.setAttribute('data-loaded', '1');
         try {
             var object = Y.JSON.parse(outcome.responseText);
+            if (object.error) {
+                Y.use('moodle-core-notification-ajaxException', function () {
+                    return new M.core.ajaxException(object).show();
+                });
+                return false;
+            }
             if (object.children && object.children.length > 0) {
                 var coursecount = 0;
                 for (var i in object.children) {
@@ -670,9 +676,16 @@ BRANCH.prototype = {
                 return true;
             }
             Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
-        } catch (ex) {
-            // If we got here then there was an error parsing the result.
-            Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+        } catch (error) {
+            if (outcome && outcome.status && outcome.status > 0) {
+                // If we got here then there was an error parsing the result.
+                Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+                Y.use('moodle-core-notification-exception', function () {
+                    return new M.core.exception(error).show();
+                });
+            }
+
+            return false;
         }
         // The branch is empty so class it accordingly
         this.node.replaceClass('branch', 'emptybranch');
index ed070a7..2f49298 100644 (file)
             mtrace('    ' . $rec->url . ' ', '');
             // Fetch the rss feed, using standard simplepie caching
             // so feeds will be renewed only if cache has expired
-            @set_time_limit(60);
+            core_php_time_limit::raise(60);
 
             $feed =  new moodle_simplepie();
             // set timeout for longer than normal to be agressive at
index 7f650c4..762bb64 100644 (file)
@@ -89,6 +89,20 @@ class block_site_main_menu extends block_list {
                 }
                 if (!$ismoving) {
                     $actions = course_get_cm_edit_actions($mod, -1);
+
+                    // Add the action move.
+                    $modcontext = context_module::instance($mod->id);
+                    $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
+                    if ($hasmanageactivities) {
+                        $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
+                        $actions['move'] = new action_menu_link_primary(
+                            new moodle_url($baseurl, array('copy' => $mod->id)),
+                            new pix_icon('t/move', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
+                            null,
+                            array('title' => get_string('move'))
+                        );
+                    }
+
                     $editbuttons = html_writer::tag('div',
                         $courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
                         array('class' => 'buttons')
index 6b1a793..d1a0371 100644 (file)
@@ -81,6 +81,7 @@ class block_tags extends block_base {
         }
 
         $this->content = new stdClass;
+        $this->content->text = '';
         $this->content->footer = '';
 
         // Get a list of tags.
index 6b3d5db..efa99ea 100644 (file)
@@ -223,5 +223,13 @@ $bloglisting = new blog_listing($blogheaders['filters']);
 $bloglisting->print_entries();
 
 echo $OUTPUT->footer();
-
-add_to_log($courseid, 'blog', 'view', 'index.php?entryid='.$entryid.'&amp;tagid='.@$tagid.'&amp;tag='.$tag, 'view blog entry');
+$eventparams = array(
+    'other' => array('entryid' => $entryid, 'tagid' => $tagid, 'userid' => $userid, 'modid' => $modid, 'groupid' => $groupid,
+                     'search' => $search, 'fromstart' => $start)
+);
+if (!empty($userid)) {
+    $eventparams['relateduserid'] = $userid;
+}
+$eventparams['other']['courseid'] = ($courseid === SITEID) ? 0 : $courseid;
+$event = \core\event\blog_entries_viewed::create($eventparams);
+$event->trigger();
index a4e4210..c6a1084 100644 (file)
@@ -339,46 +339,62 @@ class blog_entry implements renderable {
     }
 
     /**
-     * function to add all context associations to an entry
-     * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
+     * Function to add all context associations to an entry.
+     * TODO : Remove $action in 2.9 (MDL-41330)
+     *
+     * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
      */
-    public function add_associations($action='add') {
-        global $DB, $USER;
+    public function add_associations($action = null) {
+
+        if (!empty($action)) {
+            debugging('blog_entry->add_associations() does not accept any argument', DEBUG_DEVELOPER);
+        }
 
         $this->remove_associations();
 
         if (!empty($this->courseassoc)) {
-            $this->add_association($this->courseassoc, $action);
+            $this->add_association($this->courseassoc);
         }
 
         if (!empty($this->modassoc)) {
-            $this->add_association($this->modassoc, $action);
+            $this->add_association($this->modassoc);
         }
     }
 
     /**
-     * add a single association for a blog entry
-     * @param int contextid - id of context to associate with the blog entry
+     * Add a single association for a blog entry
+     * TODO : Remove $action in 2.9 (MDL-41330)
+     *
+     * @param int $contextid - id of context to associate with the blog entry.
+     * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
      */
-    public function add_association($contextid, $action='add') {
-        global $DB, $USER;
+    public function add_association($contextid, $action = null) {
+        global $DB;
+
+        if (!empty($action)) {
+            debugging('blog_entry->add_association() accepts only one argument', DEBUG_DEVELOPER);
+        }
 
         $assocobject = new StdClass;
         $assocobject->contextid = $contextid;
         $assocobject->blogid = $this->id;
-        $DB->insert_record('blog_association', $assocobject);
+        $id = $DB->insert_record('blog_association', $assocobject);
 
+        // Trigger an association created event.
         $context = context::instance_by_id($contextid);
-        $courseid = null;
-
+        $eventparam = array(
+            'objectid' => $id,
+            'other' => array('associateid' => $context->instanceid, 'subject' => $this->subject, 'blogid' => $this->id),
+            'relateduserid' => $this->userid
+        );
         if ($context->contextlevel == CONTEXT_COURSE) {
-            $courseid = $context->instanceid;
-            add_to_log($courseid, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+            $eventparam['other']['associatetype'] = 'course';
+
         } else if ($context->contextlevel == CONTEXT_MODULE) {
-            $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
-            $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module));
-            add_to_log($cm->course, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject, $cm->id, $this->userid);
+            $eventparam['other']['associatetype'] = 'coursemodule';
         }
+        $event = \core\event\blog_association_created::create($eventparam);
+        $event->trigger();
     }
 
     /**
index 689e3be..47c8a29 100644 (file)
@@ -250,5 +250,137 @@ class core_bloglib_testcase extends advanced_testcase {
         $this->assertEventLegacyLogData($arr, $event);
         $this->assertEventLegacyData($blog, $event);
     }
+
+
+    /**
+     * Tests for event blog_association_created.
+     */
+    public function test_blog_association_created_event() {
+        global $USER;
+
+        $this->setAdminUser();
+        $this->resetAfterTest();
+        $sitecontext = context_system::instance();
+        $coursecontext = context_course::instance($this->courseid);
+        $contextmodule = context_module::instance($this->cmid);
+
+        // Add blog associations with a course.
+        $blog = new blog_entry($this->postid);
+        $sink = $this->redirectEvents();
+        $blog->add_association($coursecontext->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $this->assertInstanceOf('\core\event\blog_association_created', $event);
+        $this->assertEquals($sitecontext->id, $event->contextid);
+        $this->assertEquals($blog->id, $event->other['blogid']);
+        $this->assertEquals($this->courseid, $event->other['associateid']);
+        $this->assertEquals('course', $event->other['associatetype']);
+        $this->assertEquals($blog->subject, $event->other['subject']);
+        $this->assertEquals($USER->id, $event->userid);
+        $this->assertEquals($this->userid, $event->relateduserid);
+        $this->assertEquals('blog_association', $event->objecttable);
+        $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+                     $blog->subject, 0, $this->userid);
+        $this->assertEventLegacyLogData($arr, $event);
+
+        // Add blog associations with a module.
+        $blog = new blog_entry($this->postid);
+        $sink = $this->redirectEvents();
+        $blog->add_association($contextmodule->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $this->assertEquals($blog->id, $event->other['blogid']);
+        $this->assertEquals($this->cmid, $event->other['associateid']);
+        $this->assertEquals('coursemodule', $event->other['associatetype']);
+        $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+                     $blog->subject, $this->cmid, $this->userid);
+        $this->assertEventLegacyLogData($arr, $event);
+    }
+
+    /**
+     * Tests for event blog_association_created validations.
+     */
+    public function test_blog_association_created_event_validations() {
+
+        $this->resetAfterTest();
+
+         // Make sure associatetype validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 2 , 'blogid' => 3, 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid associatetype', $e->getMessage());
+        }
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 2 , 'blogid' => 3, 'associatetype' => 'random', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Invalid associatetype', $e->getMessage());
+        }
+        // Make sure associateid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('blogid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Associate id must be set', $e->getMessage());
+        }
+        // Make sure blogid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('associateid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Blog id must be set', $e->getMessage());
+        }
+        // Make sure blogid validations work.
+        try {
+            \core\event\blog_association_created::create(array(
+                'contextid' => 1,
+                'objectid' => 3,
+                'other' => array('blogid' => 3, 'associateid' => 3, 'associatetype' => 'course')));
+        } catch (coding_exception $e) {
+            $this->assertContains('Subject must be set', $e->getMessage());
+        }
+    }
+
+    /**
+     * Tests for event blog_entries_viewed.
+     */
+    public function test_blog_entries_viewed_event() {
+
+        $this->setAdminUser();
+        $this->resetAfterTest();
+        $other = array('entryid' => $this->postid, 'tagid' => $this->tagid, 'userid' => $this->userid, 'modid' => $this->cmid,
+                       'groupid' => $this->groupid, 'courseid' => $this->courseid, 'search' => 'search', 'fromstart' => 2);
+
+        // Trigger event.
+        $sink = $this->redirectEvents();
+        $eventparams = array('other' => $other);
+        $eventinst = \core\event\blog_entries_viewed::create($eventparams);
+        $eventinst->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+        $sink->close();
+
+        // Validate event data.
+        $url = new moodle_url('/blog/index.php', $other);
+        $url2 = new moodle_url('index.php', $other);
+        $this->assertEquals($url, $event->get_url());
+        $arr = array(SITEID, 'blog', 'view', $url2->out(), 'view blog entry');
+        $this->assertEventLegacyLogData($arr, $event);
+    }
 }
 
diff --git a/blog/upgrade.txt b/blog/upgrade.txt
new file mode 100644 (file)
index 0000000..8695b95
--- /dev/null
@@ -0,0 +1,4 @@
+=== 2.7 ===
+
+* blog_entry->add_association() does not accept any params.
+* blog_entry->add_associations() accepts only one param.
\ No newline at end of file
index f3e3acb..c9c35db 100644 (file)
@@ -282,6 +282,7 @@ if (!empty($action) && confirm_sesskey()) {
 
 $PAGE->set_title($title);
 $PAGE->set_heading($SITE->fullname);
+/* @var core_cache_renderer $renderer */
 $renderer = $PAGE->get_renderer('core_cache');
 
 echo $renderer->header();
index 281fa30..4463e0a 100644 (file)
@@ -963,4 +963,31 @@ class cache_definition {
     public function has_required_identifiers() {
         return (count($this->requireidentifiers) > 0);
     }
+
+    /**
+     * Returns the possible sharing options that can be used with this defintion.
+     *
+     * @return int
+     */
+    public function get_sharing_options() {
+        return $this->sharingoptions;
+    }
+
+    /**
+     * Returns the user entered sharing key for this definition.
+     *
+     * @return string
+     */
+    public function get_user_input_sharing_key() {
+        return $this->userinputsharingkey;
+    }
+
+    /**
+     * Returns the user selected sharing option for this definition.
+     *
+     * @return int
+     */
+    public function get_selected_sharing_option() {
+        return $this->selectedsharingoption;
+    }
 }
index 2843dae..f850e69 100644 (file)
@@ -235,14 +235,9 @@ class cache_factory {
      */
     public function create_cache(cache_definition $definition) {
         $class = $definition->get_cache_class();
-        if ($this->is_initialising()) {
-            // Do nothing we just want the dummy store.
-            $stores = array();
-        } else {
-            $stores = cache_helper::get_cache_stores($definition);
-        }
+        $stores = cache_helper::get_stores_suitable_for_definition($definition);
         if (count($stores) === 0) {
-            // Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
+            // Hmm still no stores, better provide a dummy store to mimic functionality. The dev will be none the wiser.
             $stores[] = $this->create_dummy_store($definition);
         }
         $loader = null;
index 96c6601..90076aa 100644 (file)
@@ -143,7 +143,7 @@ class cache_helper {
      *
      * @param array $stores
      * @param cache_definition $definition
-     * @return array
+     * @return cache_store[]
      */
     protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) {
         $return = array();
@@ -382,34 +382,40 @@ class cache_helper {
     /**
      * Record a cache hit in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $hits The number of hits to record (by default 1)
      */
-    public static function record_cache_hit($store, $definition) {
+    public static function record_cache_hit($store, $definition, $hits = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['hits']++;
+        self::$stats[$definition][$store]['hits'] += $hits;
     }
 
     /**
      * Record a cache miss in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $misses The number of misses to record (by default 1)
      */
-    public static function record_cache_miss($store, $definition) {
+    public static function record_cache_miss($store, $definition, $misses = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['misses']++;
+        self::$stats[$definition][$store]['misses'] += $misses;
     }
 
     /**
      * Record a cache set in the stats for the given store and definition.
      *
+     * @internal
      * @param string $store
      * @param string $definition
+     * @param int $sets The number of sets to record (by default 1)
      */
-    public static function record_cache_set($store, $definition) {
+    public static function record_cache_set($store, $definition, $sets = 1) {
         self::ensure_ready_for_stats($store, $definition);
-        self::$stats[$definition][$store]['sets']++;
+        self::$stats[$definition][$store]['sets'] += $sets;
     }
 
     /**
@@ -672,4 +678,59 @@ class cache_helper {
             }
         }
     }
+
+    /**
+     * Returns an array of stores that would meet the requirements for every definition.
+     *
+     * These stores would be 100% suitable to map as defaults for cache modes.
+     *
+     * @return array[] An array of stores, keys are the store names.
+     */
+    public static function get_stores_suitable_for_mode_default() {
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
+        $requirements = 0;
+        foreach ($config->get_definitions() as $definition) {
+            $definition = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
+            $requirements = $requirements | $definition->get_requirements_bin();
+        }
+        $stores = array();
+        foreach ($config->get_all_stores() as $name => $store) {
+            if (!empty($store['features']) && ($store['features'] & $requirements)) {
+                $stores[$name] = $store;
+            }
+        }
+        return $stores;
+    }
+
+    /**
+     * Returns stores suitable for use with a given definition.
+     *
+     * @param cache_definition $definition
+     * @return cache_store[]
+     */
+    public static function get_stores_suitable_for_definition(cache_definition $definition) {
+        $factory = cache_factory::instance();
+        $stores = array();
+        if ($factory->is_initialising() || $factory->stores_disabled()) {
+            // No suitable stores here.
+            return $stores;
+        } else {
+            $stores = self::get_cache_stores($definition);
+            if (count($stores) === 0) {
+                // No suitable stores we found for the definition. We need to come up with a sensible default.
+                // If this has happened we can be sure that the user has mapped custom stores to either the
+                // mode of the definition. The first alternative to try is the system default for the mode.
+                // e.g. the default file store instance for application definitions.
+                $config = $factory->create_config_instance();
+                foreach ($config->get_stores($definition->get_mode()) as $name => $details) {
+                    if (!empty($details['default'])) {
+                        $stores[] = $factory->create_store_from_config($name, $details, $definition);
+                        break;
+                    }
+                }
+            }
+        }
+        return $stores;
+    }
 }
index 10ad620..994aef2 100644 (file)
@@ -463,6 +463,20 @@ class cache implements cache_loader {
             }
         }
 
+        if ($this->perfdebug) {
+            $hits = 0;
+            $misses = 0;
+            foreach ($fullresult as $value) {
+                if ($value === false) {
+                    $misses++;
+                } else {
+                    $hits++;
+                }
+            }
+            cache_helper::record_cache_hit($this->storetype, $this->definition->get_id(), $hits);
+            cache_helper::record_cache_miss($this->storetype, $this->definition->get_id(), $misses);
+        }
+
         // Return the result. Phew!
         return $fullresult;
     }
@@ -633,10 +647,11 @@ class cache implements cache_loader {
                 $this->static_acceleration_set($data[$key]['key'], $value);
             }
         }
-        if ($this->perfdebug) {
-            cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
+        $successfullyset = $this->store->set_many($data);
+        if ($this->perfdebug && $successfullyset) {
+            cache_helper::record_cache_set($this->storetype, $this->definition->get_id(), $successfullyset);
         }
-        return $this->store->set_many($data);
+        return $successfullyset;
     }
 
     /**
@@ -1937,7 +1952,19 @@ class cache_session extends cache {
         if ($hasmissingkeys && $strictness === MUST_EXIST) {
             throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
         }
-
+        if ($this->perfdebug) {
+            $hits = 0;
+            $misses = 0;
+            foreach ($return as $value) {
+                if ($value === false) {
+                    $misses++;
+                } else {
+                    $hits++;
+                }
+            }
+            cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id(), $hits);
+            cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id(), $misses);
+        }
         return $return;
 
     }
@@ -2011,10 +2038,11 @@ class cache_session extends cache {
                 'value' => $value
             );
         }
-        if ($this->perfdebug) {
-            cache_helper::record_cache_set($this->storetype, $definitionid);
+        $successfullyset = $this->get_store()->set_many($data);
+        if ($this->perfdebug && $successfullyset) {
+            cache_helper::record_cache_set($this->storetype, $definitionid, $successfullyset);
         }
-        return $this->get_store()->set_many($data);
+        return $successfullyset;
     }
 
     /**
index c72c52c..c7948cf 100644 (file)
@@ -778,63 +778,36 @@ abstract class cache_administration_helper extends cache_helper {
      * @return array
      */
     public static function get_definition_summaries() {
-        $instance = cache_config::instance();
-        $definitions = $instance->get_definitions();
-
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
         $storenames = array();
-        foreach ($instance->get_all_stores() as $key => $store) {
+        foreach ($config->get_all_stores() as $key => $store) {
             if (!empty($store['default'])) {
                 $storenames[$key] = new lang_string('store_'.$key, 'cache');
-            }
-        }
-
-        $modemappings = array();
-        foreach ($instance->get_mode_mappings() as $mapping) {
-            $mode = $mapping['mode'];
-            if (!array_key_exists($mode, $modemappings)) {
-                $modemappings[$mode] = array();
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $modemappings[$mode][] = $storenames[$mapping['store']];
             } else {
-                $modemappings[$mode][] = $mapping['store'];
+                $storenames[$store['name']] = $store['name'];
             }
         }
-
-        $definitionmappings = array();
-        foreach ($instance->get_definition_mappings() as $mapping) {
-            $definition = $mapping['definition'];
-            if (!array_key_exists($definition, $definitionmappings)) {
-                $definitionmappings[$definition] = array();
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $definitionmappings[$definition][] = $storenames[$mapping['store']];
-            } else {
-                $definitionmappings[$definition][] = $mapping['store'];
-            }
+        /* @var cache_definition[] $definitions */
+        $definitions = array();
+        foreach ($config->get_definitions() as $key => $definition) {
+            $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
         }
-
-        $return = array();
-
         foreach ($definitions as $id => $definition) {
-
             $mappings = array();
-            if (array_key_exists($id, $definitionmappings)) {
-                $mappings = $definitionmappings[$id];
-            } else if (empty($definition['mappingsonly'])) {
-                $mappings = $modemappings[$definition['mode']];
+            foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
+                $mappings[] = $storenames[$store->my_name()];
             }
-
             $return[$id] = array(
                 'id' => $id,
-                'name' => cache_helper::get_definition_name($definition),
-                'mode' => $definition['mode'],
-                'component' => $definition['component'],
-                'area' => $definition['area'],
+                'name' => $definition->get_name(),
+                'mode' => $definition->get_mode(),
+                'component' => $definition->get_component(),
+                'area' => $definition->get_area(),
                 'mappings' => $mappings,
-                'sharingoptions' => self::get_definition_sharing_options($definition['sharingoptions'], false),
-                'selectedsharingoption' => self::get_definition_sharing_options($definition['selectedsharingoption'], true),
-                'userinputsharingkey' => $definition['userinputsharingkey']
+                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
+                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
+                'userinputsharingkey' => $definition->get_user_input_sharing_key()
             );
         }
         return $return;
@@ -1126,7 +1099,10 @@ abstract class cache_administration_helper extends cache_helper {
      * @return array An array containing sub-arrays, one for each mode.
      */
     public static function get_default_mode_stores() {
+        global $OUTPUT;
         $instance = cache_config::instance();
+        $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
+        $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache'));
         $storenames = array();
         foreach ($instance->get_all_stores() as $key => $store) {
             if (!empty($store['default'])) {
@@ -1149,6 +1125,9 @@ abstract class cache_administration_helper extends cache_helper {
             } else {
                 $modemappings[$mode][$mapping['store']] = $mapping['store'];
             }
+            if (!array_key_exists($mapping['store'], $adequatestores)) {
+                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
+            }
         }
         return $modemappings;
     }
index c11141e..be9f906 100644 (file)
@@ -3025,7 +3025,7 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = nu
     $updatecount = 0;
 
     // Large calendars take a while...
-    set_time_limit(300);
+    core_php_time_limit::raise(300);
 
     // Mark all events in a subscription with a zero timestamp.
     if (!empty($subscriptionid)) {
index 5349c53..887533b 100644 (file)
@@ -54,7 +54,7 @@ class behat_cohort extends behat_base {
         $userid = $DB->get_field('user', 'id', array('username' => $username));
 
         $steps = array(
-            new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" table row'),
+            new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" "table_row"'),
             new Given('I select "' . $userid . '" from "' . get_string('potusers', 'cohort') . '"'),
             new Given('I press "' . get_string('add') . '"'),
             new Given('I press "' . get_string('backtocohorts', 'cohort') . '"')
index 47b169a..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 a0d5ea3..1810b79 100644 (file)
@@ -8,6 +8,6 @@
     "require-dev": {
         "phpunit/phpunit": "3.7.*",
         "phpunit/dbUnit": "1.2.*",
-        "moodlehq/behat-extension": "1.26.3"
+        "moodlehq/behat-extension": "1.27.0"
     }
 }
index 08a50a0..e7f1631 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,16 @@ $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;
+//
+// Including feature files from directories outside the dirroot is possible if required. The setting
+// requires that the running user has executable permissions on all parent directories in the paths.
+// Example:
+//   $CFG->behat_additionalfeatures = array('/home/developer/code/wipfeatures');
+//
 //=========================================================================
 // 12. DEVELOPER DATA GENERATOR
 //=========================================================================
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 a35cddd..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));
 
index 05970da..3f8d5b8 100644 (file)
@@ -1715,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 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 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 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 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 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 79bc999..08d3330 100644 (file)
@@ -56,6 +56,12 @@ function xmldb_enrol_self_upgrade($oldversion) {
     // 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 659c7f5..b0d4578 100644 (file)
@@ -48,5 +48,22 @@ function xmldb_filter_tex_upgrade($oldversion) {
     // 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)
index 8cdc537..07d6e2e 100644 (file)
@@ -85,7 +85,7 @@ if (!$iid) {
         // 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);
 
         // Use current (non-conflicting) time stamp.
@@ -183,7 +183,7 @@ if ($formdata = $mform2->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);
 
     $csvimport->init();
index 92c69fa..7151a35 100644 (file)
@@ -43,7 +43,7 @@ require_capability('gradeimport/xml:view', $context);
 // 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);
 
 $text = download_file_content($url);
index de16555..f9a2a5a 100644 (file)
@@ -47,7 +47,7 @@ if ($data = $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);
 
     if ($text = $mform->get_file_content('userfile')) {
index a0b082a..b3d812a 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-require_once '../../../config.php';
-require_once $CFG->libdir.'/gradelib.php';
-require_once $CFG->dirroot.'/grade/lib.php';
-require_once $CFG->dirroot.'/grade/report/grader/lib.php';
+require_once('../../../config.php');
+require_once($CFG->libdir.'/gradelib.php');
+require_once($CFG->dirroot.'/user/renderer.php');
+require_once($CFG->dirroot.'/grade/lib.php');
+require_once($CFG->dirroot.'/grade/report/grader/lib.php');
 
 $courseid      = required_param('id', PARAM_INT);        // course id
 $page          = optional_param('page', 0, PARAM_INT);   // active page
@@ -36,12 +37,23 @@ $action        = optional_param('action', 0, PARAM_ALPHAEXT);
 $move          = optional_param('move', 0, PARAM_INT);
 $type          = optional_param('type', 0, PARAM_ALPHA);
 $target        = optional_param('target', 0, PARAM_ALPHANUM);
-$toggle        = optional_param('toggle', NULL, PARAM_INT);
+$toggle        = optional_param('toggle', null, PARAM_INT);
 $toggle_type   = optional_param('toggle_type', 0, PARAM_ALPHANUM);
 
+$graderreportsifirst  = optional_param('sifirst', null, PARAM_ALPHA);
+$graderreportsilast   = optional_param('silast', null, PARAM_ALPHA);
+
+// The report object is recreated each time, save search information to SESSION object for future use.
+if (isset($graderreportsifirst)) {
+    $SESSION->gradereport['filterfirstname'] = $graderreportsifirst;
+}
+if (isset($graderreportsilast)) {
+    $SESSION->gradereport['filtersurname'] = $graderreportsilast;
+}
+
 $PAGE->set_url(new moodle_url('/grade/report/grader/index.php', array('id'=>$courseid)));
 
-/// basic access checks
+// basic access checks
 if (!$course = $DB->get_record('course', array('id' => $courseid))) {
     print_error('nocourseid');
 }
@@ -51,16 +63,16 @@ $context = context_course::instance($course->id);
 require_capability('gradereport/grader:view', $context);
 require_capability('moodle/grade:viewall', $context);
 
-/// return tracking object
+// return tracking object
 $gpr = new grade_plugin_return(array('type'=>'report', 'plugin'=>'grader', 'courseid'=>$courseid, 'page'=>$page));
 
-/// last selected report session tracking
+// last selected report session tracking
 if (!isset($USER->grade_last_report)) {
     $USER->grade_last_report = array();
 }
 $USER->grade_last_report[$course->id] = 'grader';
 
-/// Build editing on/off buttons
+// Build editing on/off buttons
 
 if (!isset($USER->gradeediting)) {
     $USER->gradeediting = array();
@@ -112,12 +124,13 @@ if (!empty($target) && !empty($action) && confirm_sesskey()) {
 
 $reportname = get_string('pluginname', 'gradereport_grader');
 
-/// Print header
+// Print header
 print_grade_page_head($COURSE->id, 'report', 'grader', $reportname, false, $buttons);
 
 //Initialise the grader report object that produces the table
 //the class grade_report_grader_ajax was removed as part of MDL-21562
 $report = new grade_report_grader($courseid, $gpr, $context, $page, $sortitemid);
+$numusers = $report->get_numusers(true, true);
 
 // make sure separate group does not prevent view
 if ($report->currentgroup == -2) {
@@ -126,7 +139,7 @@ if ($report->currentgroup == -2) {
     exit;
 }
 
-/// processing posted grades & feedback here
+// processing posted grades & feedback here
 if ($data = data_submitted() and confirm_sesskey() and has_capability('moodle/grade:edit', $context)) {
     $warnings = $report->process_data($data);
 } else {
@@ -135,14 +148,19 @@ if ($data = data_submitted() and confirm_sesskey() and has_capability('moodle/gr
 
 // final grades MUST be loaded after the processing
 $report->load_users();
-$numusers = $report->get_numusers();
 $report->load_final_grades();
-
 echo $report->group_selector;
-echo '<div class="clearer"></div>';
+
+// User search
+$url = new moodle_url('/grade/report/grader/index.php', array('id' => $course->id));
+$firstinitial = isset($SESSION->gradereport['filterfirstname']) ? $SESSION->gradereport['filterfirstname'] : '';
+$lastinitial  = isset($SESSION->gradereport['filtersurname']) ? $SESSION->gradereport['filtersurname'] : '';
+$totalusers = $report->get_numusers(true, false);
+$renderer = $PAGE->get_renderer('core_user');
+echo $renderer->user_search($url, $firstinitial, $lastinitial, $numusers, $totalusers, $report->currentgroupname);
 
 //show warnings if any
-foreach($warnings as $warning) {
+foreach ($warnings as $warning) {
     echo $OUTPUT->notification($warning);
 }
 
@@ -152,11 +170,16 @@ if (!empty($studentsperpage)) {
     echo $OUTPUT->paging_bar($numusers, $report->page, $studentsperpage, $report->pbarurl);
 }
 
-$reporthtml = $report->get_grade_table();
+$displayaverages = true;
+if ($numusers == 0) {
+    $displayaverages = false;
+}
+
+$reporthtml = $report->get_grade_table($displayaverages);
 
 // print submit button
 if ($USER->gradeediting[$course->id] && ($report->get_pref('showquickfeedback') || $report->get_pref('quickgrading'))) {
-    echo '<form action="index.php" method="post">';
+    echo '<form action="index.php" enctype="application/x-www-form-urlencoded" method="post">'; // Enforce compatibility with our max_input_vars hack.
     echo '<div>';
     echo '<input type="hidden" value="'.s($courseid).'" name="id" />';
     echo '<input type="hidden" value="'.sesskey().'" name="sesskey" />';
index a6dcf42..f68ba22 100644 (file)
@@ -28,7 +28,8 @@ require_once($CFG->libdir.'/tablelib.php');
 /**
  * Class providing an API for the grader report building and displaying.
  * @uses grade_report
- * @package gradereport_grader
+ * @copyright 2007 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class grade_report_grader extends grade_report {
     /**
@@ -43,7 +44,7 @@ class grade_report_grader extends grade_report {
      */
     public $gradeserror = array();
 
-//// SQL-RELATED
+    // SQL-RELATED
 
     /**
      * The id of the grade_item by which this report will be sorted.
@@ -83,11 +84,10 @@ class grade_report_grader extends grade_report {
 
     /**
      * Capability check caching
-     * */
+     * @var boolean $canviewhidden
+     */
     public $canviewhidden;
 
-    var $preferencespage=false;
-
     /**
      * Length at which feedback will be truncated (to the nearest word) and an ellipsis be added.
      * TODO replace this by a report preference
@@ -145,7 +145,7 @@ class grade_report_grader extends grade_report {
         $this->pbarurl = new moodle_url('/grade/report/grader/index.php', array('id' => $this->courseid));
 
         $this->setup_groups();
-
+        $this->setup_users();
         $this->setup_sortitemid();
     }
 
@@ -175,6 +175,7 @@ class grade_report_grader extends grade_report {
 
         // always initialize all arrays
         $queue = array();
+
         $this->load_users();
         $this->load_final_grades();
 
@@ -230,7 +231,7 @@ class grade_report_grader extends grade_report {
                         if ($this->get_pref('quickgrading')) {
                             $oldvalue->feedback = preg_replace("/\r\n|\r|\n/", "", $oldvalue->feedback);
                         }
-                        if (($oldvalue->feedback === $postedvalue) or ($oldvalue->feedback === NULL and empty($postedvalue))) {
+                        if (($oldvalue->feedback === $postedvalue) or ($oldvalue->feedback === null and empty($postedvalue))) {
                             continue;
                         }
                     }
@@ -255,9 +256,7 @@ class grade_report_grader extends grade_report {
 
                         $errorstr = '';
                         // Warn if the grade is out of bounds.
-                        if (is_null($finalgrade)) {
-                            // ok
-                        } else {
+                        if (!is_null($finalgrade)) {
                             $bounded = $gradeitem->bounded_grade($finalgrade);
                             if ($bounded > $finalgrade) {
                                 $errorstr = 'lessthanmin';
@@ -278,7 +277,7 @@ class grade_report_grader extends grade_report {
                         $finalgrade = false;
                         $trimmed = trim($postedvalue);
                         if (empty($trimmed)) {
-                             $feedback = NULL;
+                             $feedback = null;
                         } else {
                              $feedback = $postedvalue;
                         }
@@ -289,7 +288,7 @@ class grade_report_grader extends grade_report {
                         // note: we can not use $this->currentgroup because it would fail badly
                         //       when having two browser windows each with different group
                         $sharinggroup = false;
-                        foreach($mygroups as $groupid) {
+                        foreach ($mygroups as $groupid) {
                             if (groups_is_member($groupid, $userid)) {
                                 $sharinggroup = true;
                                 break;
@@ -376,7 +375,7 @@ class grade_report_grader extends grade_report {
 
             if (isset($SESSION->gradeuserreport->sortitemid)) {
                 $this->sortitemid = $SESSION->gradeuserreport->sortitemid;
-            }else{
+            } else {
                 $this->sortitemid = 'lastname';
             }
 
@@ -397,6 +396,7 @@ class grade_report_grader extends grade_report {
         if (!empty($this->users)) {
             return;
         }
+        $this->setup_users();
 
         // Limit to users with a gradeable role.
         list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0');
@@ -412,8 +412,8 @@ class grade_report_grader extends grade_report {
 
         // If the user has clicked one of the sort asc/desc arrows.
         if (is_numeric($this->sortitemid)) {
-            $params = array_merge(array('gitemid' => $this->sortitemid), $gradebookrolesparams, $this->groupwheresql_params, $enrolledparams,
-                $relatedctxparams);
+            $params = array_merge(array('gitemid' => $this->sortitemid), $gradebookrolesparams, $this->userwheresql_params,
+                    $this->groupwheresql_params, $enrolledparams, $relatedctxparams);
 
             $sortjoin = "LEFT JOIN {grade_grades} g ON g.userid = u.id AND g.itemid = $this->sortitemid";
             $sort = "g.finalgrade $this->sortorder";
@@ -435,7 +435,7 @@ class grade_report_grader extends grade_report {
                     break;
             }
 
-            $params = array_merge($gradebookrolesparams, $this->groupwheresql_params, $enrolledparams, $relatedctxparams);
+            $params = array_merge($gradebookrolesparams, $this->userwheresql_params, $this->groupwheresql_params, $enrolledparams, $relatedctxparams);
         }
 
         $sql = "SELECT $userfields
@@ -450,6 +450,7 @@ class grade_report_grader extends grade_report {
                               AND ra.contextid $relatedctxsql
                        ) rainner ON rainner.userid = u.id
                    AND u.deleted = 0
+                   $this->userwheresql
                    $this->groupwheresql
               ORDER BY $sort";
         $studentsperpage = $this->get_students_per_page();
@@ -489,7 +490,6 @@ class grade_report_grader extends grade_report {
                 }
             }
         }
-
         return $this->users;
     }
 
@@ -504,6 +504,10 @@ class grade_report_grader extends grade_report {
             return;
         }
 
+        if (empty($this->users)) {
+            return;
+        }
+
         // please note that we must fetch all grade_grades fields if we want to construct grade_grade object from it!
         $params = array_merge(array('courseid'=>$this->courseid), $this->userselect_params);
         $sql = "SELECT g.*
@@ -513,7 +517,6 @@ class grade_report_grader extends grade_report {
 
         $userids = array_keys($this->users);
 
-
         if ($grades = $DB->get_records_sql($sql, $params)) {
             foreach ($grades as $graderec) {
                 if (in_array($graderec->userid, $userids) and array_key_exists($graderec->itemid, $this->gtree->get_items())) { // some items may not be present!!
@@ -525,7 +528,7 @@ class grade_report_grader extends grade_report {
 
         // prefil grades that do not exist yet
         foreach ($userids as $userid) {
-            foreach ($this->gtree->get_items() as $itemid=>$unused) {
+            foreach ($this->gtree->get_items() as $itemid => $unused) {
                 if (!isset($this->grades[$userid][$itemid])) {
                     $this->grades[$userid][$itemid] = new grade_grade();
                     $this->grades[$userid][$itemid]->itemid = $itemid;
@@ -537,6 +540,7 @@ class grade_report_grader extends grade_report {
     }
 
     /**
+     * Gets html toggle
      * @deprecated since Moodle 2.4 as it appears not to be used any more.
      */
     public function get_toggles_html() {
@@ -544,7 +548,9 @@ class grade_report_grader extends grade_report {
     }
 
     /**
+     * Prints html toggle
      * @deprecated since 2.4 as it appears not to be used any more.
+     * @param unknown $type
      */
     public function print_toggle($type) {
         throw new coding_exception('print_toggle() can not be used any more');
@@ -555,9 +561,10 @@ class grade_report_grader extends grade_report {
      * This consists of student names and icons, links to user reports and id numbers, as well
      * as header cells for these columns. It also includes the fillers required for the
      * categories displayed on the right side of the report.
+     * @param boolean $displayaverages whether to display average rows in the table
      * @return array Array of html_table_row objects
      */
-    public function get_left_rows() {
+    public function get_left_rows($displayaverages) {
         global $CFG, $USER, $OUTPUT;
 
         $rows = array();
@@ -644,7 +651,8 @@ class grade_report_grader extends grade_report {
                 if (empty($suspendedstring)) {
                     $suspendedstring = get_string('userenrolmentsuspended', 'grades');
                 }
-                $usercell->text .= html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/enrolmentsuspended'), 'title'=>$suspendedstring, 'alt'=>$suspendedstring, 'class'=>'usersuspendedicon'));
+                $usercell->text .= html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/enrolmentsuspended'), 'title'=>$suspendedstring,
+                        'alt'=>$suspendedstring, 'class'=>'usersuspendedicon'));
             }
 
             $userrow->cells[] = $usercell;
@@ -674,17 +682,20 @@ class grade_report_grader extends grade_report {
         }
 
         $rows = $this->get_left_range_row($rows, $colspan);
-        $rows = $this->get_left_avg_row($rows, $colspan, true);
-        $rows = $this->get_left_avg_row($rows, $colspan);
+        if ($displayaverages) {
+            $rows = $this->get_left_avg_row($rows, $colspan, true);
+            $rows = $this->get_left_avg_row($rows, $colspan);
+        }
 
         return $rows;
     }
 
     /**
      * Builds and returns the rows that will make up the right part of the grader report
+     * @param boolean $displayaverages whether to display average rows in the table
      * @return array Array of html_table_row objects
      */
-    public function get_right_rows() {
+    public function get_right_rows($displayaverages) {
         global $CFG, $USER, $OUTPUT, $DB, $PAGE;
 
         $rows = array();
@@ -706,12 +717,7 @@ class grade_report_grader extends grade_report {
         );
         $jsscales = array();
 
-        foreach ($this->gtree->get_levels() as $key=>$row) {
-            if ($key == 0) {
-                // do not display course grade category
-                // continue;
-            }
-
+        foreach ($this->gtree->get_levels() as $key => $row) {
             $headingrow = new html_table_row();
             $headingrow->attributes['class'] = 'heading_name_row';
 
@@ -738,7 +744,7 @@ class grade_report_grader extends grade_report {
                     $catlevel = '';
                 }
 
-// Element is a filler
+                // Element is a filler
                 if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') {
                     $fillercell = new html_table_cell();
                     $fillercell->attributes['class'] = $type . ' ' . $catlevel;
@@ -747,9 +753,8 @@ class grade_report_grader extends grade_report {
                     $fillercell->header = true;
                     $fillercell->scope = 'col';
                     $headingrow->cells[] = $fillercell;
-                }
-// Element is a category
-                else if ($type == 'category') {
+                } else if ($type == 'category') {
+                    // Element is a category
                     $categorycell = new html_table_cell();
                     $categorycell->attributes['class'] = 'category ' . $catlevel;
                     $categorycell->colspan = $colspan;
@@ -764,12 +769,8 @@ class grade_report_grader extends grade_report {
                     }
 
                     $headingrow->cells[] = $categorycell;
-                }
-// Element is a grade_item
-                else {
-                    //$itemmodule = $element['object']->itemmodule;
-                    //$iteminstance = $element['object']->iteminstance;
-
+                } else {
+                    // Element is a grade_item
                     if ($element['object']->id == $this->sortitemid) {
                         if ($this->sortorder == 'ASC') {
                             $arrow = $this->get_sort_arrow('up', $sortlink);
@@ -806,7 +807,7 @@ class grade_report_grader extends grade_report {
         $scaleslist = array();
         $tabindices = array();
 
-        foreach ($this->gtree->get_items() as $itemid=>$item) {
+        foreach ($this->gtree->get_items() as $itemid => $item) {
             $scale = null;
             if (!empty($item->scaleid)) {
                 $scaleslist[] = $item->scaleid;
@@ -839,14 +840,13 @@ class grade_report_grader extends grade_report {
                 unset($hidingaffected);
             }
 
-
             $itemrow = new html_table_row();
             $itemrow->id = 'user_'.$userid;
             $itemrow->attributes['class'] = $rowclasses[$this->rowcount % 2];
 
             $jsarguments['users'][$userid] = fullname($user);
 
-            foreach ($this->gtree->items as $itemid=>$unused) {
+            foreach ($this->gtree->items as $itemid => $unused) {
                 $item =& $this->gtree->items[$itemid];
                 $grade = $this->grades[$userid][$item->id];
 
@@ -865,13 +865,13 @@ class grade_report_grader extends grade_report {
                     $gradeval = $grade->finalgrade;
                 }
                 if (!empty($grade->finalgrade)) {
-                    $gradevalforJS = null;
+                    $gradevalforjs = null;
                     if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
-                        $gradevalforJS = (int)$gradeval;
+                        $gradevalforjs = (int)$gradeval;
                     } else {
-                        $gradevalforJS = format_float($gradeval, $decimalpoints);
+                        $gradevalforjs = format_float($gradeval, $decimalpoints);
                     }
-                    $jsarguments['grades'][] = array('user'=>$userid, 'item'=>$itemid, 'grade'=>$gradevalforJS);
+                    $jsarguments['grades'][] = array('user'=>$userid, 'item'=>$itemid, 'grade'=>$gradevalforjs);
                 }
 
                 // MDL-11274
@@ -879,7 +879,7 @@ class grade_report_grader extends grade_report {
                 if (!$this->canviewhidden and $grade->is_hidden()) {
                     if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$item->is_course_item()) {
                         // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records
-                        $itemcell->text = html_writer::tag('span', userdate($grade->get_datesubmitted(),get_string('strftimedatetimeshort')), array('class'=>'datesubmitted'));
+                        $itemcell->text = html_writer::tag('span', userdate($grade->get_datesubmitted(), get_string('strftimedatetimeshort')), array('class'=>'datesubmitted'));
                     } else {
                         $itemcell->text = '-';
                     }
@@ -902,13 +902,10 @@ class grade_report_grader extends grade_report {
                     $itemcell->attributes['class'] .= ' overridden';
                 }
 
-                if ($grade->is_excluded()) {
-                    // $itemcell->attributes['class'] .= ' excluded';
-                }
-
                 if (!empty($grade->feedback)) {
                     //should we be truncating feedback? ie $short_feedback = shorten_text($feedback, $this->feedback_trunc_length);
-                    $jsarguments['feedback'][] = array('user'=>$userid, 'item'=>$itemid, 'content'=>wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >'));
+                    $jsarguments['feedback'][] = array('user'=>$userid, 'item'=>$itemid, 'content'=>wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)),
+                            34, '<br/ >'));
                 }
 
                 if ($grade->is_excluded()) {
@@ -928,7 +925,7 @@ class grade_report_grader extends grade_report {
                 $gradepass = ' gradefail ';
                 if ($grade->is_passed($item)) {
                     $gradepass = ' gradepass ';
-                } elseif (is_null($grade->is_passed($item))) {
+                } else if (is_null($grade->is_passed($item))) {
                     $gradepass = '';
                 }
 
@@ -965,7 +962,7 @@ class grade_report_grader extends grade_report {
                             $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id'=>'grade_'.$userid.'_'.$item->id);
                             $itemcell->text .= html_writer::label(get_string('typescale', 'grades'), $attributes['id'], false, array('class' => 'accesshide'));
                             $itemcell->text .= html_writer::select($scaleopt, 'grade['.$userid.']['.$item->id.']', $gradeval, array(-1=>$nogradestr), $attributes);
-                        } elseif(!empty($scale)) {
+                        } else if (!empty($scale)) {
                             $scales = explode(",", $scale->scale);
 
                             // invalid grade if gradeval < 1
@@ -975,8 +972,6 @@ class grade_report_grader extends grade_report {
                                 $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale
                                 $itemcell->text .= html_writer::tag('span', $scales[$gradeval-1], array('class'=>"gradevalue$hidden$gradepass"));
                             }
-                        } else {
-                            // no such scale, throw error?
                         }
 
                     } else if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type
@@ -993,7 +988,6 @@ class grade_report_grader extends grade_report {
                         }
                     }
 
-
                     // If quickfeedback is on, print an input element
                     if ($this->get_pref('showquickfeedback') and $grade->is_editable()) {
                         $feedbacklabel = fullname($user) . ' ' . $item->itemname;
@@ -1019,7 +1013,8 @@ class grade_report_grader extends grade_report {
                     if ($item->needsupdate) {
                         $itemcell->text .= html_writer::tag('span', get_string('error'), array('class'=>"gradingerror$hidden$gradepass"));
                     } else {
-                        $itemcell->text .= html_writer::tag('span', grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null), array('class'=>"gradevalue$hidden$gradepass"));
+                        $itemcell->text .= html_writer::tag('span', grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null),
+                                array('class'=>"gradevalue$hidden$gradepass"));
                         if ($this->get_pref('showanalysisicon')) {
                             $itemcell->text .= $this->gtree->get_grade_analysis_icon($grade);
                         }
@@ -1039,7 +1034,7 @@ class grade_report_grader extends grade_report {
             $jsarguments['cfg']['ajaxenabled'] = true;
             $jsarguments['cfg']['scales'] = array();
             foreach ($jsscales as $scale) {
-                $jsarguments['cfg']['scales'][$scale->id] = explode(',',$scale->scale);
+                $jsarguments['cfg']['scales'][$scale->id] = explode(',', $scale->scale);
             }
             $jsarguments['cfg']['feedbacktrunclength'] =  $this->feedback_trunc_length;
 
@@ -1056,12 +1051,14 @@ class grade_report_grader extends grade_report {
             'requires'  => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io-queue', 'json-parse', 'overlay')
         );
         $PAGE->requires->js_init_call('M.gradereport_grader.init_report', $jsarguments, false, $module);
-        $PAGE->requires->strings_for_js(array('addfeedback','feedback', 'grade'), 'grades');
-        $PAGE->requires->strings_for_js(array('ajaxchoosescale','ajaxclicktoclose','ajaxerror','ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader');
+        $PAGE->requires->strings_for_js(array('addfeedback', 'feedback', 'grade'), 'grades');
+        $PAGE->requires->strings_for_js(array('ajaxchoosescale', 'ajaxclicktoclose', 'ajaxerror', 'ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader');
 
         $rows = $this->get_right_range_row($rows);
-        $rows = $this->get_right_avg_row($rows, true);
-        $rows = $this->get_right_avg_row($rows);
+        if ($displayaverages) {
+            $rows = $this->get_right_avg_row($rows, true);
+            $rows = $this->get_right_avg_row($rows);
+        }
 
         return $rows;
     }
@@ -1070,18 +1067,18 @@ class grade_report_grader extends grade_report {
      * Depending on the style of report (fixedstudents vs traditional one-table),
      * arranges the rows of data in one or two tables, and returns the output of
      * these tables in HTML
+     * @param boolean $displayaverages whether to display average rows in the table
      * @return string HTML
      */
-    public function get_grade_table() {
+    public function get_grade_table($displayaverages = false) {
         global $OUTPUT;
         $fixedstudents = $this->is_fixed_students();
 
-        $leftrows = $this->get_left_rows();
-        $rightrows = $this->get_right_rows();
+        $leftrows = $this->get_left_rows($displayaverages);
+        $rightrows = $this->get_right_rows($displayaverages);
 
         $html = '';
 
-
         if ($fixedstudents) {
             $fixedcolumntable = new html_table();
             $fixedcolumntable->id = 'fixed_column';
@@ -1124,7 +1121,7 @@ class grade_report_grader extends grade_report {
             $controlscell = new html_table_cell();
             $controlscell->attributes['class'] = 'header controls';
             $controlscell->colspan = $colspan;
-            $controlscell->text = $this->get_lang_string('controls','grades');
+            $controlscell->text = $this->get_lang_string('controls', 'grades');
 
             $controlsrow->cells[] = $controlscell;
             $rows[] = $controlsrow;
@@ -1149,7 +1146,7 @@ class grade_report_grader extends grade_report {
             $rangecell->colspan = $colspan;
             $rangecell->header = true;
             $rangecell->scope = 'row';
-            $rangecell->text = $this->get_lang_string('range','grades');
+            $rangecell->text = $this->get_lang_string('range', 'grades');
             $rangerow->cells[] = $rangecell;
             $rows[] = $rangerow;
         }
@@ -1219,7 +1216,7 @@ class grade_report_grader extends grade_report {
             $iconsrow = new html_table_row();
             $iconsrow->attributes['class'] = 'controls';
 
-            foreach ($this->gtree->items as $itemid=>$unused) {
+            foreach ($this->gtree->items as $itemid => $unused) {
                 // emulate grade element
                 $item = $this->gtree->get_item($itemid);
 
@@ -1249,7 +1246,7 @@ class grade_report_grader extends grade_report {
             $rangerow = new html_table_row();
             $rangerow->attributes['class'] = 'heading range';
 
-            foreach ($this->gtree->items as $itemid=>$unused) {
+            foreach ($this->gtree->items as $itemid => $unused) {
                 $item =& $this->gtree->items[$itemid];
                 $itemcell = new html_table_cell();
                 $itemcell->attributes['class'] .= ' range i'. $itemid;
@@ -1365,7 +1362,7 @@ class grade_report_grader extends grade_report {
             $avgrow = new html_table_row();
             $avgrow->attributes['class'] = 'avg';
 
-            foreach ($this->gtree->items as $itemid=>$unused) {
+            foreach ($this->gtree->items as $itemid => $unused) {
                 $item =& $this->gtree->items[$itemid];
 
                 if ($item->needsupdate) {
@@ -1444,7 +1441,7 @@ class grade_report_grader extends grade_report {
      * figures out the state of the object and builds then returns a div
      * with the icons needed for the grader report.
      *
-     * @param array $object
+     * @param array $element
      * @return string HTML
      */
     protected function get_icons($element) {
@@ -1471,7 +1468,7 @@ class grade_report_grader extends grade_report {
             }
 
             if ($this->get_pref('showeyecons')) {
-               $showhideicon = $this->gtree->get_hiding_icon($element, $this->gpr);
+                $showhideicon = $this->gtree->get_hiding_icon($element, $this->gpr);
             }
 
             if ($this->get_pref('showlocks')) {
@@ -1490,7 +1487,7 @@ class grade_report_grader extends grade_report {
 
     /**
      * Given a category element returns collapsing +/- icon if available
-     * @param object $object
+     * @param object $element
      * @return string HTML
      */
     protected function get_collapsing_icon($element) {
@@ -1522,6 +1519,12 @@ class grade_report_grader extends grade_report {
         return $icon;
     }
 
+    /**
+     * Processes a single action against a category, grade_item or grade.
+     * @param string $target eid ({type}{id}, e.g. c4 for category4)
+     * @param string $action Which action to take (edit, delete etc...)
+     * @return
+     */
     public function process_action($target, $action) {
         return self::do_process_action($target, $action);
     }
@@ -1651,51 +1654,10 @@ class grade_report_grader extends grade_report {
     /**
      * Returns the maximum number of students to be displayed on each page
      *
-     * Takes into account the 'studentsperpage' user preference and the 'max_input_vars'
-     * PHP setting. Too many fields is only a problem when submitting grades but
-     * we respect 'max_input_vars' even when viewing grades to prevent students disappearing
-     * when toggling editing on and off.
-     *
      * @return int The maximum number of students to display per page
      */
     public function get_students_per_page() {
-        global $USER;
-        static $studentsperpage = null;
-
-        if ($studentsperpage === null) {
-            $originalstudentsperpage = $studentsperpage = $this->get_pref('studentsperpage');
-
-            // Will this number of students result in more fields that we are allowed?
-            $maxinputvars = ini_get('max_input_vars');
-            if ($maxinputvars !== false) {
-                // We can't do anything about there being more grade items than max_input_vars,
-                // but we can decrease number of students per page if there are >= max_input_vars
-                $fieldsperstudent = 0; // The number of fields output per student
-
-                if ($this->get_pref('quickgrading') || $this->get_pref('showquickfeedback')) {
-                    // Each array (grade, feedback) will gain one element
-                    $fieldsperstudent ++;
-                }
-
-                $fieldsrequired = $studentsperpage * $fieldsperstudent;
-                if ($fieldsrequired >= $maxinputvars) {
-                    $studentsperpage = $maxinputvars - 1; // Subtract one to be on the safe side
-                    if ($studentsperpage<1) {
-                        // Make sure students per page doesn't fall below 1, though if your
-                        // max_input_vars is only 1 you've got bigger problems!
-                        $studentsperpage = 1;
-                    }
-
-                    $a = new stdClass();
-                    $a->originalstudentsperpage = $originalstudentsperpage;
-                    $a->studentsperpage = $studentsperpage;
-                    $a->maxinputvars = $maxinputvars;
-                    debugging(get_string('studentsperpagereduced', 'grades', $a));
-                }
-            }
-        }
-
-        return $studentsperpage;
+        return $this->get_pref('studentsperpage');
     }
 }
 
index 6d2d717..1fc07f0 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-set_time_limit(0);
 require_once '../../../config.php';
 require_once $CFG->libdir . '/gradelib.php';
 require_once '../../lib.php';
+core_php_time_limit::raise();
 
 $courseid      = required_param('id', PARAM_INT);
 
index 512a8c2..3e757cd 100644 (file)
@@ -26,7 +26,8 @@ require_once($CFG->libdir.'/gradelib.php');
 
 /**
  * An abstract class containing variables and methods used by all or most reports.
- * @package core_grades
+ * @copyright 2007 Moodle Pty Ltd (http://moodle.com)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 abstract class grade_report {
     /**
@@ -94,7 +95,7 @@ abstract class grade_report {
      */
     public $lang_strings = array();
 
-//// GROUP VARIABLES (including SQL)
+    // GROUP VARIABLES (including SQL)
 
     /**
      * The current group being displayed.
@@ -102,11 +103,17 @@ abstract class grade_report {
      */
     public $currentgroup;
 
+    /**
+     * The current groupname being displayed.
+     * @var string $currentgroupname
+     */
+    public $currentgroupname;
+
     /**
      * Current course group mode
      * @var int $groupmode
      */
-    var $groupmode;
+    public $groupmode;
 
     /**
      * A HTML select element used to select the current group.
@@ -132,6 +139,19 @@ abstract class grade_report {
      */
     protected $groupwheresql_params = array();
 
+    // USER VARIABLES (including SQL).
+
+    /**
+     * An SQL constraint to append to the queries used by this object to build the report.
+     * @var string $userwheresql
+     */
+    protected $userwheresql;
+
+    /**
+     * The ordered params for $userwheresql
+     * @var array $userwheresql_params
+     */
+    protected $userwheresql_params = array();
 
     /**
      * Constructor. Sets local copies of user preferences and initialises grade_tree.
@@ -147,7 +167,6 @@ abstract class grade_report {
             print_error('norolesdefined', 'grades');
         }
 
-
         $this->courseid  = $courseid;
         if ($this->courseid == $COURSE->id) {
             $this->course = $COURSE;
@@ -173,7 +192,7 @@ abstract class grade_report {
      * the value of that preference. If the preference has already been fetched before,
      * the saved value is returned. If the preference is not set at the User level, the $CFG equivalent
      * is given (site default).
-     * @static (Can be called statically, but then doesn't benefit from caching)
+     * Can be called statically, but then doesn't benefit from caching
      * @param string $pref The name of the preference (do not include the grade_report_ prefix)
      * @param int $objectid An optional itemid or categoryid to check for a more fine-grained preference
      * @return mixed The value of the preference
@@ -187,10 +206,10 @@ abstract class grade_report {
 
         if (!isset($this) OR get_class($this) != 'grade_report') {
             if (!empty($objectid)) {
-                $retval = get_user_preferences($fullprefname . $objectid, grade_report::get_pref($pref));
-            } elseif (isset($CFG->$fullprefname)) {
+                $retval = get_user_preferences($fullprefname . $objectid, self::get_pref($pref));
+            } else if (isset($CFG->$fullprefname)) {
                 $retval = get_user_preferences($fullprefname, $CFG->$fullprefname);
-            } elseif (isset($CFG->$shortprefname)) {
+            } else if (isset($CFG->$shortprefname)) {
                 $retval = get_user_preferences($fullprefname, $CFG->$shortprefname);
             } else {
                 $retval = null;
@@ -220,8 +239,7 @@ abstract class grade_report {
     /**
      * Uses set_user_preferences() to update the value of a user preference. If 'default' is given as the value,
      * the preference will be removed in favour of a higher-level preference.
-     * @static
-     * @param string $pref_name The name of the preference.
+     * @param string $pref The name of the preference.
      * @param mixed $pref_value The value of the preference.
      * @param int $itemid An optional itemid to which the preference will be assigned
      * @return bool Success or failure.
@@ -241,7 +259,7 @@ abstract class grade_report {
      * @param array $data
      * @return mixed True or array of errors
      */
-    abstract function process_data($data);
+    abstract public function process_data($data);
 
     /**
      * Processes a single action against a category, grade_item or grade.
@@ -249,7 +267,7 @@ abstract class grade_report {
      * @param string $action Which action to take (edit, delete etc...)
      * @return
      */
-    abstract function process_action($target, $action);
+    abstract public function process_action($target, $action);
 
     /**
      * First checks the cached language strings, then returns match if found, or uses get_string()
@@ -268,11 +286,12 @@ abstract class grade_report {
     /**
      * Fetches and returns a count of all the users that will be shown on this page.
      * @param boolean $groups include groups limit
+     * @param boolean $users include users limit - default false, used for searching purposes
      * @return int Count of users
      */
-    public function get_numusers($groups=true) {
-        global $DB;
-
+    public function get_numusers($groups = true, $users = false) {
+        global $CFG, $DB;
+        $userwheresql = "";
         $groupsql      = "";
         $groupwheresql = "";
 
@@ -287,13 +306,18 @@ abstract class grade_report {
 
         $params = array_merge($gradebookrolesparams, $enrolledparams, $relatedctxparams);
 
+        if ($users) {
+            $userwheresql = $this->userwheresql;
+            $params       = array_merge($params, $this->userwheresql_params);
+        }
+
         if ($groups) {
             $groupsql      = $this->groupsql;
             $groupwheresql = $this->groupwheresql;
             $params        = array_merge($params, $this->groupwheresql_params);
         }
 
-        $countsql = "SELECT COUNT(DISTINCT u.id)
+        $sql = "SELECT DISTINCT u.id
                        FROM {user} u
                        JOIN ($enrolledsql) je
                             ON je.id = u.id
@@ -302,16 +326,36 @@ abstract class grade_report {
                        $groupsql
                       WHERE ra.roleid $gradebookrolessql
                             AND u.deleted = 0
+                            $userwheresql
                             $groupwheresql
                             AND ra.contextid $relatedctxsql";
-        return $DB->count_records_sql($countsql, $params);
+        $selectedusers = $DB->get_records_sql($sql, $params);
+
+        $count = 0;
+        // Check if user's enrolment is active and should be displayed.
+        if (!empty($selectedusers)) {
+            $coursecontext = $this->context->get_course_context(true);
+
+            $useractiveenrolments = get_enrolled_users($coursecontext, '', 0, 'u.*',  null, 0, 0, true);
+
+            $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
+            $showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
+            $showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $coursecontext);
+
+            foreach ($selectedusers as $id => $value) {
+                if (!$showonlyactiveenrol || ($showonlyactiveenrol && array_key_exists($id, $useractiveenrolments))) {
+                    $count++;
+                }
+            }
+        }
+        return $count;
     }
 
     /**
      * Sets up this object's group variables, mainly to restrict the selection of users to display.
      */
     protected function setup_groups() {
-        /// find out current groups mode
+        // find out current groups mode
         if ($this->groupmode = groups_get_course_groupmode($this->course)) {
             $this->currentgroup = groups_get_course_group($this->course, true);
             $this->group_selector = groups_print_course_menu($this->course, $this->pbarurl, true);
@@ -321,6 +365,8 @@ abstract class grade_report {
             }
 
             if ($this->currentgroup) {
+                $group = groups_get_group($this->currentgroup);
+                $this->currentgroupname     = $group->name;
                 $this->groupsql             = " JOIN {groups_members} gm ON gm.userid = u.id ";
                 $this->groupwheresql        = " AND gm.groupid = :gr_grpid ";
                 $this->groupwheresql_params = array('gr_grpid'=>$this->currentgroup);
@@ -328,11 +374,28 @@ abstract class grade_report {
         }
     }
 
+    /**
+     * Sets up this report's user criteria to restrict the selection of users to display.
+     */
+    public function setup_users() {
+        global $SESSION, $DB;
+
+        $this->userwheresql = "";
+        $this->userwheresql_params = array();
+        if (isset($SESSION->gradereport['filterfirstname']) && !empty($SESSION->gradereport['filterfirstname'])) {
+            $this->userwheresql .= ' AND '.$DB->sql_like('u.firstname', ':firstname', false, false);
+            $this->userwheresql_params['firstname'] = $SESSION->gradereport['filterfirstname'].'%';
+        }
+        if (isset($SESSION->gradereport['filtersurname']) && !empty($SESSION->gradereport['filtersurname'])) {
+            $this->userwheresql .= ' AND '.$DB->sql_like('u.lastname', ':lastname', false, false);
+            $this->userwheresql_params['lastname'] = $SESSION->gradereport['filtersurname'].'%';
+        }
+    }
+
     /**
      * Returns an arrow icon inside an <a> tag, for the purpose of sorting a column.
      * @param string $direction
-     * @param moodle_url $sort_link
-     * @param string HTML
+     * @param moodle_url $sortlink
      */
     protected function get_sort_arrow($direction='move', $sortlink=null) {
         global $OUTPUT;
@@ -377,7 +440,7 @@ abstract class grade_report {
             $previous_courseid = $courseid;
         }
 
-        if( !$hiding_affected ) {
+        if (!$hiding_affected) {
             $items = grade_item::fetch_all(array('courseid'=>$courseid));
             $grades = array();
             $sql = "SELECT g.*
@@ -390,7 +453,7 @@ abstract class grade_report {
                 }
                 unset($gradesrecords);
             }
-            foreach ($items as $itemid=>$unused) {
+            foreach ($items as $itemid => $unused) {
                 if (!isset($grades[$itemid])) {
                     $grade_grade = new grade_grade();
                     $grade_grade->userid = $this->user->id;
@@ -404,21 +467,19 @@ abstract class grade_report {
 
         //if the item definitely depends on a hidden item
         if (array_key_exists($course_item->id, $hiding_affected['altered'])) {
-            if( !$this->showtotalsifcontainhidden[$courseid] ) {
+            if (!$this->showtotalsifcontainhidden[$courseid]) {
                 //hide the grade
                 $finalgrade = null;
-            }
-            else {
+            } else {
                 //use reprocessed marks that exclude hidden items
                 $finalgrade = $hiding_affected['altered'][$course_item->id];
             }
         } else if (!empty($hiding_affected['unknown'][$course_item->id])) {
             //not sure whether or not this item depends on a hidden item
-            if( !$this->showtotalsifcontainhidden[$courseid] ) {
+            if (!$this->showtotalsifcontainhidden[$courseid]) {
                 //hide the grade
                 $finalgrade = null;
-            }
-            else {
+            } else {
                 //use reprocessed marks that exclude hidden items
                 $finalgrade = $hiding_affected['unknown'][$course_item->id];
             }
index 9ff8f36..ae7b48d 100644 (file)
@@ -4,6 +4,18 @@ information provided here is intended especially for developers.
 === 2.6 ===
 * grade_report_grader::get_toggles_html() and grade_report_grader::print_toggle()
   can not be used any more
+* class grade_report get_numusers($groups = true, $users = false)
+  An extra parameter has been added to restrict count to those users being
+displayed
+* class grade_report_grader get_left_rows($displayaverages)
+  A parameter has been added to indicate whether averages are applicable and
+should be displayed on the report
+* class grade_report_grader get_right_rows($displayaverages)
+  A parameter has been added to indicate whether averages are applicable and
+should be displayed on the report
+* class grade_report_grader get_grade_table($displayaverages = false)
+  A parameter has been added to indicate whether averages are applicable and
+should be displayed on the report
 
 === 2.3.5, 2.4.2 ===
 * class_grade_report::showtotalsifcontainhidden has been switched from a single integer value to an array.
index b87cf85..8c109df 100644 (file)
@@ -60,7 +60,7 @@ Feature: Uniqueness of Group ID number
     And I fill the moodle form with:
       | Grouping ID number | GG2 |
     And I press "Save changes"
-    And I click on "Edit" "link" in the "Grouping 1" table row
+    And I click on "Edit" "link" in the "Grouping 1" "table_row"
     And I fill the moodle form with:
       | Grouping ID number | GG2 |
     And I press "Save changes"
index 188257e..7108b6a 100644 (file)
@@ -83,6 +83,6 @@ $string['welcomep30'] = 'Diese Version von <strong>{$a->installername}</strong>
 $string['welcomep40'] = 'Das Paket enthält: <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
 $string['welcomep50'] = 'Die Nutzung dieser Anwendungen ist lizenzrechtlich geprüft. Alle Anwendungen von <strong>{$a->installername}</strong> sind
 <a href="http://www.opensource.org/docs/definition_plain.html"> Open Source </a> und unterliegen der <a href="http://www.gnu.org/copyleft/gpl.html"> GPL</a> Lizenz.';
-$string['welcomep60'] = 'Die folgenden Webseiten führen Sie in einfachen Schritten durch die Konfiguration und Installation von <strong>Moodle</strong> auf Ihrem Computer. Sie können die vorgeschlagenen Einstellungen übernehmen oder an Ihre Bedürfnisse anpassen.';
+$string['welcomep60'] = 'Die folgenden Webseiten führen Sie in einfachen Schritten durch die Installation von <strong>Moodle</strong> auf Ihrem Computer. Sie können die vorgeschlagenen Einstellungen übernehmen oder an eigene Bedürfnisse anpassen.';
 $string['welcomep70'] = 'Klicken Sie auf die Taste \'Weiter\', um mit der Installation von Moodle fortzufahren.';
 $string['wwwroot'] = 'Webadresse';
index 39fdf50..a205c16 100644 (file)
@@ -55,7 +55,7 @@ $string['paths'] = 'Útvonalak';
 $string['pathserrcreatedataroot'] = 'A telepítő nem tudja létrehozni az adatkönyvtárat ({$a->dataroot}).';
 $string['pathshead'] = 'Útvonalak megerősítése';
 $string['pathsrodataroot'] = 'Az adatok gyökérkönyvtára nem írható.';
-$string['pathsroparentdataroot'] = 'A felettes könyvtás ({$a->parent}) nem írható. A telepítő nem tudja létrehozni az adatkönyvtárat ({$a->dataroot}).';
+$string['pathsroparentdataroot'] = 'A felettes könyvtár ({$a->parent}) nem írható. A telepítő nem tudja létrehozni az adatkönyvtárat ({$a->dataroot}).';
 $string['pathssubadmindir'] = 'Egy pár webes gazdagép esetén az /admin speciális URL pl. a vezérlőpanel eléréséhez. Ez ütközik a Moodle admin oldalainak standard helyével. Javítás: a telepítésben nevezze át a rendszergazda könyvtárát, az új nevet pedig írja be ide. Például: <em>moodleadmin</em>. Ezzel helyrehozhatók a Moodle rendszergazdai ugrópontjai.';
 $string['pathssubdataroot'] = 'Szüksége van egy helyre, ahol a Moodle mentheti a feltöltött állományokat. Ez a könyvtár a webszerver felhasználója (általában \'nobody\' vagy \'apache\') számára legyen mind olvasható, MIND ÍRHATÓ. Ha nem létezik, a telepítő megpróbálja létrehozni.';
 $string['pathssubdirroot'] = 'Teljes útvonal a Moodle telepítéséhez. ';
index 515ddb5..a6bc7af 100644 (file)
@@ -151,7 +151,6 @@ $string['configcalendarexportsalt'] = 'This random text is used for improving of
 $string['configclamactlikevirus'] = 'Treat files like viruses';
 $string['configclamdonothing'] = 'Treat files as OK';
 $string['configclamfailureonupload'] = 'If you have configured clam to scan uploaded files, but it is configured incorrectly or fails to run for some unknown reason, how should it behave?  If you choose \'Treat files like viruses\', they\'ll be moved into the quarantine area, or deleted. If you choose \'Treat files as OK\', the files will be moved to the destination directory like normal. Either way, admins will be alerted that clam has failed.  If you choose \'Treat files like viruses\' and for some reason clam fails to run (usually because you have entered an invalid pathtoclam), ALL files that are uploaded will be moved to the given quarantine area, or deleted. Be careful with this setting.';
-$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['configcookiehttponly'] = 'Enables new PHP 5.2.0 feature - browsers are instructed to send cookie with real http requests only, cookies should not be accessible by scripting languages. This is not supported in all browsers and it may not be fully compatible with current code. It helps to prevent some types of XSS attacks.';
 $string['configcookiesecure'] = 'If server is accepting only https connections it is recommended to enable sending of secure cookies. If enabled please make sure that web server is not accepting http:// or set up permanent redirection to https:// address. When <em>wwwroot</em> address does not start with https:// this setting is turned off automatically.';
 $string['configcountry'] = 'If you set a country here, then this country will be selected by default on new user accounts.  To force users to choose a country, just leave this unset.';
@@ -355,7 +354,6 @@ $string['configyuicomboloading'] = 'This options enables combined file loading o
 $string['confirmation'] = 'Confirmation';
 $string['confirmdeletecomments'] = 'You are about to delete comments, are you sure?';
 $string['confirmed'] = 'Confirmed';
-$string['convertformat'] = '<i>convert</i> output format';
 $string['cookiehttponly'] = 'Only http cookies';
 $string['cookiesecure'] = 'Secure cookies only';
 $string['country'] = 'Default country';
@@ -622,8 +620,6 @@ $string['langmenu'] = 'Display language menu';
 $string['langpackwillbeupdated'] = 'NOTE: Moodle will try to download updates for your language packs during the upgrade.';
 $string['langstringcache'] = 'Cache all language strings';
 $string['languagesettings'] = 'Language settings';
-$string['latexpreamble'] = 'LaTeX preamble';
-$string['latexsettings'] = 'LaTeX renderer Settings';
 $string['latinexcelexport'] = 'Excel encoding';
 $string['legacyfilesaddallowed'] = 'Allow adding to legacy course files';
 $string['legacyfilesaddallowed_help'] = 'If a course has legacy course files, allow new files and folders to be added to it.';
@@ -671,7 +667,7 @@ $string['loginpasswordautocomplete_help'] = 'Having this off will let users save
 $string['loglifetime'] = 'Keep logs for';
 $string['longtimewarning'] = '<b>Please note that this process can take a long time.</b>';
 $string['maintenancemode'] = 'In maintenance mode';
-$string['maintenancemodeisscheduled'] = 'Site is switching to maintenance mode in {$a} minutes';
+$string['maintenancemodeisscheduled'] = 'This site will be switched to maintenance mode in {$a->min} mins {$a->sec} secs';
 $string['maintfileopenerror'] = 'Error opening maintenance files!';
 $string['maintinprogress'] = 'Maintenance is in progress...';
 $string['manageformats'] = 'Manage course formats';
@@ -767,6 +763,8 @@ $string['nohttpsformobilewarning'] = 'It is recommended to enable HTTPS with a v
 $string['nomissingstrings'] = 'No missing strings';
 $string['nonewsettings'] = 'No new settings were added during this upgrade.';
 $string['nonexistentbookmark'] = 'The bookmark you requested does not exist.';
+$string['maxtimelimit'] = 'Maximum time limit';
+$string['maxtimelimit_desc'] = 'To restrict the maximum PHP execution time that Moodle will allow without any output being displayed, enter a value in seconds here. 0 means that Moodle default restrictions are used. If you have a front-end server with its own time limit, set this value lower to receive PHP errors in logs. Does not apply to CLI scripts.';
 $string['noresults'] = 'No results found.';
 $string['noroles'] = 'No roles';
 $string['notifications'] = 'Notifications';
@@ -785,9 +783,6 @@ $string['order3'] = 'Third';
 $string['order4'] = 'Fourth';
 $string['passwordpolicy'] = 'Password policy';
 $string['passwordresettime'] = 'Maximum time to validate password reset request';
-$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['pathtoclam'] = 'clam AV path';
 $string['pathtodot'] = 'Path to dot';
 $string['pathtodot_help'] = 'Path to dot. Probably something like /usr/bin/dot. To be able to generate graphics from DOT files, you must have installed the dot executable and point to it here. Note that, for now, this only used by the profiling features (Development->Profiling) built into Moodle.';
index 5937859..a7d5d29 100644 (file)
@@ -86,6 +86,8 @@ $string['entrybodyonlydesc'] = 'Entry description';
 $string['entryerrornotyours'] = 'This entry is not yours';
 $string['entrysaved'] = 'Your entry has been saved';
 $string['entrytitle'] = 'Entry title';
+$string['eventblogentriesviewed'] = 'Blog entries viewed';
+$string['eventblogassociationcreated'] = 'Blog association created';
 $string['evententryadded'] = 'Blog entry added';
 $string['evententrydeleted'] = 'Blog entry deleted';
 $string['evententryupdated'] = 'Blog entry updated';
index 461edde..6d4eab7 100644 (file)
@@ -92,6 +92,7 @@ $string['ex_unabletolock'] = 'Unable to acquire a lock for caching.';
 $string['ex_unmetstorerequirements'] = 'You are unable to use this store at the present time. Please refer to the documentation to determine its requirements.';
 $string['gethit'] = 'Get - Hit';
 $string['getmiss'] = 'Get - Miss';
+$string['inadequatestoreformapping'] = 'This store doesn\'t meet the requirements for all known definitions. Definitions for which this store is inadequate will be given the original default store instead of the selected store.';
 $string['invalidlock'] = 'Invalid lock';
 $string['invalidplugin'] = 'Invalid plugin';
 $string['invalidstore'] = 'Invalid cache store provided';
index a816753..3689531 100644 (file)
@@ -51,6 +51,7 @@ $string['discussion'] = 'Discussion';
 $string['emailmessages'] = 'Email messages when I am offline';
 $string['emailtagline'] = 'This is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.';
 $string['emptysearchstring'] = 'You must search for something';
+$string['enabled'] = 'Enabled';
 $string['errorcallingprocessor'] = 'Error calling defined output';
 $string['errortranslatingdefault'] = 'Error translating default setting provided by plugin, using system defaults instead.';
 $string['forced'] = 'Forced';
index 36cd958..26966ec 100644 (file)
@@ -213,8 +213,8 @@ $string['serviceswesubscribeto'] = 'Services on {$a} that we subscribe to.';
 $string['settings'] = 'Settings';
 $string['showlocal'] = 'Show local users';
 $string['showremote'] = 'Show remote users';
-$string['ssl_acl_allow'] = 'SSO ACL: Allow user {$a->user} from {$a->host}';
-$string['ssl_acl_deny'] = 'SSO ACL: Deny user {$a->user} from {$a->host}';
+$string['ssl_acl_allow'] = 'SSO ACL: Allow user \'{$a->user}\' from \'{$a->host}\'';
+$string['ssl_acl_deny'] = 'SSO ACL: Deny user \'{$a->user}\' from \'{$a->host}\'';
 $string['ssoaccesscontrol'] = 'SSO access control';
 $string['ssoacldescr'] = 'Use this page to grant/deny access to specific users from remote MNet hosts. This is functional when you are offering SSO services to remote users. To control your <em>local</em> users\' ability to roam to other MNet hosts, use the roles system to grant them the <em>mnetlogintoremote</em> capability.';
 $string['ssoaclneeds'] = 'For this functionality to work, you must have Networking on, plus the MNet authentication plugin enabled.';
index ccae4de..387d439 100644 (file)
@@ -717,6 +717,7 @@ $string['eventcoursedeleted'] = 'Course deleted';
 $string['eventcoursemodulecreated'] = 'Course module created';
 $string['eventcoursemoduledeleted'] = 'Course module deleted';
 $string['eventcoursemoduleupdated'] = 'Course module updated';
+$string['eventcoursemoduleviewed'] = 'Course module viewed';
 $string['eventcourseresetended'] = 'Course reset ended';
 $string['eventcourseresetstarted'] = 'Course reset started';
 $string['eventcourserestored'] = 'Course restored';
index f1e7958..20bebb3 100644 (file)
@@ -64,6 +64,7 @@ $string['plugindisable'] = 'Disable';
 $string['plugindisabled'] = 'Disabled';
 $string['pluginenable'] = 'Enable';
 $string['pluginenabled'] = 'Enabled';
+$string['release'] = 'Release';
 $string['requiredby'] = 'Required by: {$a}';
 $string['requires'] = 'Requires';
 $string['rootdir'] = 'Directory';
index db2c118..287c25b 100644 (file)
@@ -61,7 +61,10 @@ $string['close'] = 'Close';
 $string['commonrepositorysettings'] = 'Common repository settings';
 $string['configallowexternallinks'] = 'This option enables all users to choose whether or not external media is copied into Moodle or not. If this is off then media is always copied into Moodle (this is usually best for overall data integrity and security).  If this is on then users can choose each time they add media to a text.';
 $string['configcacheexpire'] = 'The amount of time that file listings are cached locally (in seconds) when browsing external repositories.';
+$string['configgetfiletimeout'] = 'Timeout in seconds for downloading the external file into moodle.';
 $string['configsaved'] = 'Configuration saved!';
+$string['configsyncfiletimeout'] = 'Timeout in seconds for syncronising the external file size.';
+$string['configsyncimagetimeout'] = 'Timeout in seconds for downloading an image file from external repository during syncronisation.';
 $string['confirmdelete'] = 'Are you sure you want to delete this repository - {$a}? If you choose "Continue and download", file references to external contents will be downloaded to moodle, but it could take long time to process.';
 $string['confirmdeletefile'] = 'Are you sure you want to delete this file?';
 $string['confirmrenamefile'] = 'Are you sure you want to rename/move this file? There are {$a} alias/shortcut files that use this file as their source. If you proceed then those aliases will be converted to true copies.';
@@ -122,6 +125,7 @@ $string['folderexists'] = 'Folder name already being used, please use another na
 $string['foldernotfound'] = 'Folder not found';
 $string['folderrecurse'] = 'Folder can not be moved to it\'s own subfolder';
 $string['getfile'] = 'Select this file';
+$string['getfiletimeout'] = 'Get file timeout';
 $string['hidden'] = 'Hidden';
 $string['help'] = 'Help';
 $string['choosealink'] = 'Choose a link...';
@@ -210,6 +214,8 @@ $string['siteinstances'] = 'Repositories instances of the site';
 $string['size'] = 'Size';
 $string['submit'] = 'Submit';
 $string['sync'] = 'Sync';
+$string['syncfiletimeout'] = 'Sync file timeout';
+$string['syncimagetimeout'] = 'Sync image timeout';
 $string['thumbview'] = 'View as icons';
 $string['title'] = 'Choose a file...';
 $string['type'] = 'Type';
index 45f07b4..f035ae7 100644 (file)
@@ -1113,12 +1113,12 @@ function get_empty_accessdata() {
 function get_user_accessdata($userid, $preloadonly=false) {
     global $CFG, $ACCESSLIB_PRIVATE, $USER;
 
-    if (!empty($USER->acces['rdef']) and empty($ACCESSLIB_PRIVATE->rolepermissions)) {
+    if (!empty($USER->access['rdef']) and empty($ACCESSLIB_PRIVATE->rolepermissions)) {
         // share rdef from USER session with rolepermissions cache in order to conserve memory
-        foreach($USER->acces['rdef'] as $k=>$v) {
-            $ACCESSLIB_PRIVATE->rolepermissions[$k] =& $USER->acces['rdef'][$k];
+        foreach ($USER->access['rdef'] as $k=>$v) {
+            $ACCESSLIB_PRIVATE->rolepermissions[$k] =& $USER->access['rdef'][$k];
         }
-        $ACCESSLIB_PRIVATE->accessdatabyuser[$USER->id] = $USER->acces;
+        $ACCESSLIB_PRIVATE->accessdatabyuser[$USER->id] = $USER->access;
     }
 
     if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
index 074ad64..449a88e 100644 (file)
@@ -126,7 +126,7 @@ function uninstall_plugin($type, $name) {
     global $CFG, $DB, $OUTPUT;
 
     // This may take a long time.
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     // Recursively uninstall all subplugins first.
     $subplugintypes = core_component::get_plugin_types_with_subplugins();
@@ -6728,7 +6728,7 @@ function db_replace($search, $replace) {
                         'block_instances', '');
 
     // Turn off time limits, sometimes upgrades can be slow.
-    @set_time_limit(0);
+    core_php_time_limit::raise();
 
     if (!$tables = $DB->get_tables() ) {    // No tables yet at all.
         return false;
index a57959b..9f7d940 100644 (file)
@@ -39,14 +39,24 @@ if ($branchtype !== navigation_node::TYPE_SITE_ADMIN) {
     die('Wrong node type passed.');
 }
 
+// Start capturing output in case of broken plugins.
+ob_start();
+
 $PAGE->set_context(context_system::instance());
 $PAGE->set_url('/lib/ajax/getsiteadminbranch.php', array('type'=>$branchtype));
 
 $sitenavigation = new settings_navigation_ajax($PAGE);
 
-// Set XML headers.
-header('Content-type: text/plain; charset=utf-8');
-// Convert and output the branch as XML.
+// Convert and output the branch as JSON.
 $converter = new navigation_json();
 $branch = $sitenavigation->get('root');
-echo $converter->convert($branch);
+
+$output = ob_get_contents();
+ob_end_clean();
+if ($CFG->debugdeveloper && !empty($output)) {
+    throw new coding_exception('Unexpected output whilst building the administration tree. ' .
+            'This could be caused by trailing whitespace. Output received: ' .
+            var_export($output, true));
+} else {
+    echo $converter->convert($branch);
+}
index 7363742..3e06093 100644 (file)
@@ -159,6 +159,8 @@ class auth_plugin_base {
      *
      * This method is used if can_change_password() returns true.
      * This method is called only when user is logged in, it may use global $USER.
+     * If you are using a plugin config variable in this method, please make sure it is set before using it,
+     * as this method can be called even if the plugin is disabled, in which case the config values won't be set.
      *
      * @return moodle_url url of the profile page or null if standard used
      */
index 8219cdb..57c86fa 100644 (file)
@@ -426,7 +426,7 @@ class badge {
         $awards = 0;
 
         // Raise timelimit as this could take a while for big web sites.
-        set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
         // For site level badges, get all active site users who can earn this badge and haven't got it yet.
index fb149ad..20762ae 100644 (file)
@@ -108,7 +108,7 @@ class behat_command {
 
         // We don't check the PHP version if $CFG->behat_switchcompletely has been enabled.
         // Here we are in CLI.
-        if (empty($CFG->behat_switchcompletely) && $checkphp && version_compare(PHP_VERSION, '5.4.0', '<')) {
+        if (empty($CFG->behat_switchcompletely) && empty($CFG->behat_wwwroot) && $checkphp && version_compare(PHP_VERSION, '5.4.0', '<')) {
             behat_error(BEHAT_EXITCODE_REQUIREMENT, 'PHP 5.4 is required. See config-dist.php for possible alternatives');
         }
 
@@ -148,6 +148,7 @@ class behat_command {
         }
 
         // Checking behat dataroot existence otherwise echo about admin/tool/behat/cli/init.php.
+        $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
         if (empty($CFG->behat_dataroot) || !is_dir($CFG->behat_dataroot) || !is_writable($CFG->behat_dataroot)) {
             self::output_msg(get_string('runclitool', 'tool_behat', 'php admin/tool/behat/cli/init.php'));
             return BEHAT_EXITCODE_CONFIG;
index 11fbf4d..d559ff4 100644 (file)
@@ -82,6 +82,11 @@ class behat_config_manager {
             $features = array_values($featurespaths);
         }
 
+        // Optionally include features from additional directories.
+        if (!empty($CFG->behat_additionalfeatures)) {
+            $features = array_merge($features, array_map("realpath", $CFG->behat_additionalfeatures));
+        }
+
         // Gets all the components with steps definitions.
         $stepsdefinitions = array();
         $steps = self::get_components_steps_definitions();
@@ -93,6 +98,11 @@ class behat_config_manager {
             }
         }
 
+        // We don't want the deprecated steps definitions here.
+        if (!$testsrunner) {
+            unset($stepsdefinitions['behat_deprecated']);
+        }
+
         // Behat config file specifing the main context class,
         // the required Behat extensions and Moodle test wwwroot.
         $contents = self::get_config_file_contents($features, $stepsdefinitions);
index d09c7df..c8504fe 100644 (file)
@@ -69,6 +69,11 @@ class behat_util extends testing_util {
             throw new coding_exception('This method can be only used by Behat CLI tool');
         }
 
+        $tables = $DB->get_tables(false);
+        if (!empty($tables)) {
+            behat_error(BEHAT_EXITCODE_INSTALLED);
+        }
+
         // New dataroot.
         self::reset_dataroot();
 
index 0d9f2e2..667125e 100644 (file)
@@ -33,6 +33,7 @@ define('BEHAT_EXITCODE_PERMISSIONS', 252);
 define('BEHAT_EXITCODE_REINSTALL', 253);
 define('BEHAT_EXITCODE_INSTALL', 254);
 define('BEHAT_EXITCODE_COMPOSER', 255);
+define('BEHAT_EXITCODE_INSTALLED', 256);
 
 /**
  * Exits with an error code
@@ -62,6 +63,9 @@ function behat_error($errorcode, $text = '') {
             $path = testing_cli_argument_path('/admin/tool/behat/cli/init.php');
             $text = "Install Behat before enabling it, use:\n php ".$path;
             break;
+        case BEHAT_EXITCODE_INSTALLED:
+            $text = "The Behat site is already installed";
+            break;
         default:
             $text = 'Unknown error ' . $errorcode . ' ' . $text;
             break;
@@ -167,3 +171,98 @@ function behat_clean_init_config() {
     }
 
 }
+
+/**
+ * Checks that the behat config vars are properly set.
+ *
+ * @return void Stops execution with error code if something goes wrong.
+ */
+function behat_check_config_vars() {
+    global $CFG;
+
+    // CFG->behat_prefix must be set and with value different than CFG->prefix and phpunit_prefix.
+    if (empty($CFG->behat_prefix) ||
+           ($CFG->behat_prefix == $CFG->prefix) ||
+           (!empty($CFG->phpunit_prefix) && $CFG->behat_prefix == $CFG->phpunit_prefix)) {
+        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_wwwroot must be different than CFG->wwwroot if it is set, it may not be set as
+    // it can take the default value and we should also consider that will have the same value than
+    // $CFG->wwwroot if $CFG->behat_switchcompletely is set.
+    if (!empty($CFG->behat_wwwroot) && $CFG->behat_wwwroot == $CFG->wwwroot && empty($CFG->behat_switchcompletely)) {
+        behat_error(BEHAT_EXITCODE_CONFIG,
+            'Define $CFG->behat_wwwroot in config.php with a value different than $CFG->wwwroot');
+    }
+
+    // CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot.
+    $CFG->dataroot = realpath($CFG->dataroot);
+    if (!empty($CFG->behat_dataroot) && is_dir($CFG->behat_dataroot)) {
+        $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
+    }
+    if (empty($CFG->behat_dataroot) ||
+           ($CFG->behat_dataroot == $CFG->dataroot) ||
+           (!empty($CFG->phpunit_dataroot) && is_dir($CFG->phpunit_dataroot)
+                && $CFG->behat_dataroot == realpath($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');
+    }
+
+}
+
+/**
+ * Returns a URL based on the priorities between $CFG->behat_* vars.
+ *
+ * 1.- Switch completely wins and overwrites behat_wwwroot
+ * 2.- behat_wwwroot alternatively
+ * 3.- http://localhost:8000 if there is nothing else
+ *
+ * @return string
+ */
+function behat_get_wwwroot() {
+    global $CFG;
+
+    if (!empty($CFG->behat_switchcompletely)) {
+        return $CFG->wwwroot;
+    } else if (!empty($CFG->behat_wwwroot)) {
+        return $CFG->behat_wwwroot;
+    }
+
+    return 'http://localhost:8000';
+}
+
+/**
+ * Checks if the URL requested by the user matches the provided argument
+ *
+ * @param string $url
+ * @return bool Returns true if it matches.
+ */
+function behat_is_requested_url($url) {
+
+    $parsedurl = parse_url($url . '/');
+    $parsedurl['port'] = isset($parsedurl['port']) ? $parsedurl['port'] : 80;
+    $parsedurl['path'] = rtrim($parsedurl['path'], '/');
+
+    // Removing the port.
+    $pos = strpos($_SERVER['HTTP_HOST'], ':');
+    if ($pos !== false) {
+        $requestedhost = substr($_SERVER['HTTP_HOST'], 0, $pos);
+    } else {
+        $requestedhost = $_SERVER['HTTP_HOST'];
+    }
+
+    // The path should also match.
+    if (empty($parsedurl['path'])) {
+        $matchespath = true;
+    } else if (strpos($_SERVER['SCRIPT_NAME'], $parsedurl['path']) === 0) {
+        $matchespath = true;
+    }
+
+    // The host and the port should match
+    if ($parsedurl['host'] == $requestedhost && $parsedurl['port'] == $_SERVER['SERVER_PORT'] && !empty($matchespath)) {
+        return true;
+    }
+
+    return false;
+}