Merge branch 'wip-MDL-43026-master' of git://github.com/marinaglancy/moodle
authorDamyon Wiese <damyon@moodle.com>
Tue, 3 Dec 2013 08:11:48 +0000 (16:11 +0800)
committerDamyon Wiese <damyon@moodle.com>
Tue, 3 Dec 2013 08:11:48 +0000 (16:11 +0800)
168 files changed:
admin/enrol.php
admin/settings/appearance.php
admin/settings/server.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
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/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
composer.json
course/lib.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/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
lang/en/admin.php
lang/en/blog.php
lang/en/cache.php
lang/en/message.php
lang/en/moodle.php
lib/adminlib.php
lib/ajax/getsiteadminbranch.php
lib/authlib.php
lib/badgeslib.php
lib/behat/classes/util.php
lib/behat/lib.php
lib/classes/event/blog_association_created.php [new file with mode: 0644]
lib/classes/event/blog_entries_viewed.php [new file with mode: 0644]
lib/classes/event/course_module_viewed.php [new file with mode: 0644]
lib/classes/minify.php
lib/classes/php_time_limit.php [new file with mode: 0644]
lib/classes/session/file.php
lib/classes/session/manager.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/navigationlib.php
lib/setup.php
lib/setuplib.php
lib/statslib.php
lib/tests/event_course_module_viewed.php [new file with mode: 0644]
lib/tests/filelib_test.php
lib/tests/fixtures/event_fixtures.php
lib/tests/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/dragdrop/dragdrop.js
message/defaultoutputs.php
message/module.js
message/renderer.php
mod/assign/feedback/file/importziplib.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/forum/lib.php
mod/lesson/lib.php
mod/page/classes/event/course_module_viewed.php
mod/page/lang/en/page.php
mod/page/view.php
mod/quiz/lib.php
mod/quiz/report/overview/report.php
mod/workshop/classes/event/course_module_viewed.php
mod/workshop/lang/en/workshop.php
mod/workshop/view.php
my/indexsys.php
question/classes/statistics/questions/calculator.php
question/engine/upgrade/upgradelib.php
question/format.php
question/type/multichoice/styles.css
report/security/index.php
repository/filepicker.php
repository/repository_ajax.php
repository/repository_callback.php
tag/manage.php
theme/base/style/core.css
theme/base/style/question.css
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/style/moodle.css
user/editadvanced_form.php
user/profilesys.php
user/renderer.php
user/tests/behat/edituserpassword.feature [new file with mode: 0644]

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 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 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 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..162e8ba 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();
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 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 a252ec3..ef90f69 100644 (file)
@@ -73,6 +73,11 @@ 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;
+        $attributes = (array)$attributes;
+        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(
index 5550c33..b6e6822 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js differ
index f0d0522..e474ba8 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js differ
index 6dd4a48..368a23d 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js differ
index 6ac1510..0748b98 100644 (file)
@@ -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 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 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 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;
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..cfba41b 100644 (file)
@@ -22,7 +22,7 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-set_time_limit(0);
+core_php_time_limit::raise();
 require_once '../../../config.php';
 require_once $CFG->libdir . '/gradelib.php';
 require_once '../../lib.php';
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 515ddb5..cd99894 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.';
@@ -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 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 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 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..533402e 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;
diff --git a/lib/classes/event/blog_association_created.php b/lib/classes/event/blog_association_created.php
new file mode 100644 (file)
index 0000000..5670f52
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Event for when a new blog entry is associated with a context.
+ *
+ * @package    core
+ * @copyright  2013 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * blog_association_created
+ *
+ * Class for event to be triggered when a new blog entry is associated with a context.
+ *
+ * @package    core
+ * @copyright  2013 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class blog_association_created extends \core\event\base {
+
+    /**
+     * Set basic properties for the event.
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['objecttable'] = 'blog_association';
+        $this->data['crud'] = 'c';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventblogassociationadded', 'core_blog');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "Blog association added between entry id $this->other['blogid'] and $this->other['associatetype'] with id
+                $this->other['associateid']";
+    }
+
+    /**
+     * Returns relevant URL.
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/blog/index.php', array('entryid' => $this->objectid, 'userid' => $this->userid));
+    }
+
+    /**
+     * replace add_to_log() statement.
+     *
+     * @return array of parameters to be passed to legacy add_to_log() function.
+     */
+    protected function get_legacy_logdata() {
+        if ($this->other['associatetype'] === 'course') {
+            return array (SITEID, 'blog', 'add association', 'index.php?userid=' . $this->relateduserid. '&entryid=' .
+                    $this->other['blogid'], $this->other['subject'], 0, $this->relateduserid);
+        } else {
+            return array (SITEID, 'blog', 'add association', 'index.php?userid=' . $this->relateduserid. '&entryid=' .
+                    $this->other['blogid'], $this->other['subject'], $this->other['associateid'], $this->relateduserid);
+        }
+    }
+
+    /**
+     * Custom validations.
+     *
+     * @throws \coding_exception when validation fails.
+     * @return void
+     */
+    protected function validate_data() {
+        if (empty($this->other['associatetype']) || ($this->other['associatetype'] !== 'course'
+                && $this->other['associatetype'] !== 'coursemodule')) {
+            throw new \coding_exception('Invalid associatetype in event blog_association_created.');
+        } else if (!isset($this->other['blogid'])) {
+            throw new \coding_exception('Blog id must be set in event blog_association_created.');
+        } else if (!isset($this->other['associateid'])) {
+            throw new \coding_exception('Associate id must be set in event blog_association_created.');
+        } else if (!isset($this->other['subject'])) {
+            throw new \coding_exception('Subject must be set in event blog_association_created.');
+        }
+    }
+}
diff --git a/lib/classes/event/blog_entries_viewed.php b/lib/classes/event/blog_entries_viewed.php
new file mode 100644 (file)
index 0000000..bf4dbaf
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Event for when blog entries are viewed.
+ *
+ * @package    core
+ * @copyright  2013 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * blog_entries_viewed
+ *
+ * Class for event to be triggered when blog entries are viewed.
+ *
+ * @package    core
+ * @copyright  2013 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class blog_entries_viewed extends base {
+
+    /** @var array List of url params accepted*/
+    private $validparams = array('entryid', 'tagid', 'userid', 'modid', 'groupid', 'courseid', 'search', 'fromstart');
+
+    /**
+     * Set basic properties for the event.
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'r';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventblogentriesviewed', 'core_blog');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'Blog entries viewed';
+    }
+
+    /**
+     * Returns relevant URL.
+     * @return \moodle_url
+     */
+    public function get_url() {
+        $params = array();
+        foreach ($this->validparams as $param) {
+            if (!empty($this->other[$param])) {
+                $params[$param] = $this->other[$param];
+            }
+        }
+        return new \moodle_url('/blog/index.php', $params);
+    }
+
+    /**
+     * replace add_to_log() statement.
+     *
+     * @return array of parameters to be passed to legacy add_to_log() function.
+     */
+    protected function get_legacy_logdata() {
+        $params = array();
+        foreach ($this->validparams as $param) {
+            if (!empty($this->other[$param])) {
+                $params[$param] = $this->other[$param];
+            }
+        }
+        $url = new \moodle_url('index.php', $params);
+        return array (SITEID, 'blog', 'view', $url->out(), 'view blog entry');
+    }
+}
diff --git a/lib/classes/event/course_module_viewed.php b/lib/classes/event/course_module_viewed.php
new file mode 100644 (file)
index 0000000..7b45a25
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Course module viewed event.
+ *
+ * @package    core
+ * @copyright  2013 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Abstract Course module viewed event class.
+ *
+ * Class for event to be triggered when a course module is viewed.
+ *
+ * @package    core
+ * @copyright  2013 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class course_module_viewed extends base {
+
+    /**
+     * Init method.
+     *
+     * Please override this in extending class and specify objecttable.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "User with id '$this->userid' viewed course module '$this->objecttable' with instance id '$this->objectid'";
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventcoursemoduleviewed', 'core');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url("/mod/$this->objecttable/view.php", array('id' => $this->context->instanceid));
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        return array($this->courseid, $this->objecttable, 'view', 'view.php?id=' . $this->context->instanceid, $this->objectid,
+                     $this->context->instanceid);
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        // Make sure this class is never used without proper object details.
+        if (empty($this->objectid) || empty($this->objecttable)) {
+            throw new \coding_exception('course_module_viewed event must define objectid and object table.');
+        }
+    }
+
+}
index 19d734f..6ebb067 100644 (file)
@@ -87,7 +87,7 @@ EOD;
             $compressed[] = self::js($content);
         }
 
-        return implode("\n", $compressed);
+        return implode(";\n", $compressed);
     }
 
     /**
diff --git a/lib/classes/php_time_limit.php b/lib/classes/php_time_limit.php
new file mode 100644 (file)
index 0000000..b7be4f2
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * PHP time limit management.
+ *
+ * @package core
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Utility class to manage PHP time limit.
+ */
+class core_php_time_limit {
+    /**
+     * @var int Current end time of time limit (-1 if not set)
+     */
+    protected static $currentend = -1;
+
+    /**
+     * @var array Data for unit testing
+     */
+    protected static $unittestdata = array();
+
+    /**
+     * Sets the PHP time limit to a number of seconds from now.
+     *
+     * This function will always extend the time limit (in other words, if the time
+     * limit has already been set further in the future, it will do nothing).
+     *
+     * In order to support front-end servers which may time out silently if no
+     * output is displayed, you should ideally only call this function if you expect
+     * some output to be displayed at the same time. (I.e. if you call this function
+     * each time around a loop, also display some output each time around the loop,
+     * such as a progress bar update.)
+     *
+     * @param int $newlimit Limit in seconds from now (0 = infinite)
+     */
+    public static function raise($newlimit = 0) {
+        global $CFG;
+
+        // Special behaviour in unit tests so that we can check the value.
+        if (PHPUNIT_TEST) {
+            self::$unittestdata[] = $newlimit;
+        }
+
+        // If the time limit has already been set to 'infinite', ignore. Also do
+        // nothing in CLI scripts (including unit testing) which are set to
+        // infinite by default.
+        if (self::$currentend === 0 || CLI_SCRIPT) {
+            return;
+        }
+
+        // Maximum time limit can be set in config. This can be useful for front-end
+        // server systems; if the front-end server has a timeout without receiving
+        // data, it's helpful to set this timeout lower to ensure that a suitable
+        // error gets logged.
+        if (!empty($CFG->maxtimelimit)) {
+            $realtimeout = max(1, $CFG->maxtimelimit);
+            if ($newlimit === 0) {
+                $newlimit = $realtimeout;
+            } else {
+                $newlimit = min($newlimit, $realtimeout);
+            }
+        }
+
+        // If new time limit is infinite, just set that.
+        if ($newlimit === 0) {
+            self::$currentend = 0;
+            @set_time_limit(0);
+            return;
+        }
+
+        // Calculate time limits to make sure it's longer than previous.
+        $now = time();
+        $newend = $now + $newlimit;
+        if (self::$currentend !== -1 && self::$currentend > $newend) {
+            // Existing time limit is already longer, so do nothing.
+            return;
+        }
+
+        // Set time limit and update current value.
+        @set_time_limit($newlimit);
+        self::$currentend = $newend;
+    }
+
+    /**
+     * For unit testing, returns an array of the values set during test.
+     *
+     * @return array Array of values set
+     */
+    public static function get_and_clear_unit_test_data() {
+        $data = self::$unittestdata;
+        self::$unittestdata = array();
+        return $data;
+    }
+}
index 7ecb00c..47a678c 100644 (file)
@@ -65,7 +65,8 @@ class file extends handler {
         // Need to disable debugging since disk_free_space()
         // will fail on very large partitions (see MDL-19222).
         $freespace = @disk_free_space($this->sessiondir);
-        if (!($freespace > 2048) and $freespace !== false) {
+        // MDL-43039: disk_free_space() returns null if disabled.
+        if (!($freespace > 2048) and ($freespace !== false) and ($freespace !== null)) {
             throw new exception('sessiondiskfull', 'error');
         }
 
index c5e764c..314f9ed 100644 (file)
@@ -271,8 +271,9 @@ class manager {
                     // This should not happen, just log it, we MUST not produce any output here!
                     error_log("Cannot find session record $sid for user ".$_SESSION['USER']->id.", creating new session.");
                 }
+                // Prevent session fixation attacks.
+                session_regenerate_id(true);
             }
-            session_regenerate_id(true);
             $_SESSION = array();
         }
         unset($sid);
@@ -614,7 +615,7 @@ class manager {
         global $CFG, $DB;
 
         // This may take a long time...
-        set_time_limit(0);
+        \core_php_time_limit::raise();
 
         $maxlifetime = $CFG->sessiontimeout;
 
index 552398b..18a0e19 100644 (file)
@@ -49,7 +49,7 @@ function cron_run() {
         set_debugging(DEBUG_DEVELOPER, true);
     }
 
-    set_time_limit(0);
+    core_php_time_limit::raise();
     $starttime = microtime();
 
     // Increase memory limit
@@ -287,7 +287,7 @@ function cron_run() {
                         mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
                     }
                     // Reset possible changes by modules to time_limit. MDL-11597
-                    @set_time_limit(0);
+                    core_php_time_limit::raise();
                     mtrace("done.");
                 }
             }
@@ -314,7 +314,7 @@ function cron_run() {
                         $DB->set_field('block', 'lastcron', $timenow, array('id'=>$block->id));
                     }
                     // Reset possible changes by blocks to time_limit. MDL-11597
-                    @set_time_limit(0);
+                    core_php_time_limit::raise();
                     mtrace('done.');
                 }
             }
@@ -493,7 +493,7 @@ function cron_run() {
                     }
                 }
             }
-            @set_time_limit(0);
+            core_php_time_limit::raise();
         } else {
             mtrace('Next stats run after:'. userdate($timetocheck));
         }
@@ -575,7 +575,7 @@ function cron_execute_plugin_type($plugintype, $description = null) {
                 round(microtime(true) - $pre_time, 2) . " seconds)");
 
         set_config('lastcron', time(), $component);
-        @set_time_limit(0);
+        core_php_time_limit::raise();
     }
 
     if ($description) {
index df880d8..30b930a 100644 (file)
@@ -2036,7 +2036,7 @@ abstract class enrol_plugin {
         // Unfortunately this may take a long time, it should not be interrupted,
         // otherwise users get duplicate notification.
 
-        @set_time_limit(0);
+        core_php_time_limit::raise();
         raise_memory_limit(MEMORY_HUGE);
 
 
index 698a9ef..6f7a001 100644 (file)
@@ -149,7 +149,7 @@ class external_api {
      */
     public static function set_timeout($seconds=360) {
         $seconds = ($seconds < 300) ? 300 : $seconds;
-        set_time_limit($seconds);
+        core_php_time_limit::raise($seconds);
     }
 
     /**
index a32b36f..ebdd06c 100644 (file)
@@ -2693,7 +2693,7 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
 
         fseek($handle, $ranges[0][1]);
         while (!feof($handle) && $length > 0) {
-            @set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
+            core_php_time_limit::raise(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
             $buffer = fread($handle, ($chunksize < $length ? $chunksize : $length));
             echo $buffer;
             flush();
@@ -2722,7 +2722,7 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
             echo $range[0];
             fseek($handle, $range[1]);
             while (!feof($handle) && $length > 0) {
-                @set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
+                core_php_time_limit::raise(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
                 $buffer = fread($handle, ($chunksize < $length ? $chunksize : $length));
                 echo $buffer;
                 flush();
@@ -3082,6 +3082,10 @@ class curl {
      * private callback function
      * Formatting HTTP Response Header
      *
+     * We only keep the last headers returned. For example during a redirect the
+     * redirect headers will not appear in {@link self::getResponse()}, if you need
+     * to use those headers, refer to {@link self::get_raw_response()}.
+     *
      * @param resource $ch Apparently not used
      * @param string $header
      * @return int The strlen of the header
@@ -3090,15 +3094,17 @@ class curl {
         $this->rawresponse[] = $header;
 
         if (trim($header, "\r\n") === '') {
-            if ($this->responsefinished) {
-                // Multiple headers means redirect, keep just the latest one.
-                $this->response = array();
-                return strlen($header);
-            }
+            // This must be the last header.
             $this->responsefinished = true;
         }
 
         if (strlen($header) > 2) {
+            if ($this->responsefinished) {
+                // We still have headers after the supposedly last header, we must be
+                // in a redirect so let's empty the response to keep the last headers.
+                $this->responsefinished = false;
+                $this->response = array();
+            }
             list($key, $value) = explode(" ", rtrim($header, "\r\n"), 2);
             $key = rtrim($key, ':');
             if (!empty($this->response[$key])) {
@@ -3402,9 +3408,6 @@ class curl {
                     }
                 }
 
-                $this->responsefinished = false;
-                $this->response = array();
-
                 curl_setopt($curl, CURLOPT_URL, $redirecturl);
                 $ret = curl_exec($curl);
 
index 2d754fc..6c571c6 100644 (file)
@@ -399,6 +399,72 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
                     hide : false
                 }
             },
+            /**
+             * Lock the given field if the field value is in the given set of values.
+             *
+             * @param elements
+             * @param values
+             * @returns {{lock: boolean, hide: boolean}}
+             * @private
+             */
+            _dependency_in : function(elements, values) {
+                // A pipe (|) is used as a value separator
+                // when multiple values have to be passed on at the same time.
+                values = values.split('|');
+                var lock = false;
+                var hidden_val = false;
+                var options, v, selected, value;
+                elements.each(function(){
+                    if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
+                        return;
+                    } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        hidden_val = (values.indexOf(this.get('value')) > -1);
+                        return;
+                    } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
+                        lock = lock || hidden_val;
+                        return;
+                    }
+                    if (this.getAttribute('class').toLowerCase() == 'filepickerhidden') {
+                        // Check for filepicker status.
+                        var elementname = this.getAttribute('name');
+                        if (elementname && M.form_filepicker.instances[elementname].fileadded) {
+                            lock = false;
+                        } else {
+                            lock = true;
+                        }
+                    } else if (this.get('nodeName').toUpperCase() === 'SELECT' && this.get('multiple') === true) {
+                        // Multiple selects can have one or more value assigned.
+                        selected = [];
+                        options = this.get('options');
+                        options.each(function() {
+                            if (this.get('selected')) {
+                                selected[selected.length] = this.get('value');
+                            }
+                        });
+                        if (selected.length > 0 && selected.length === values.length) {
+                            for (var i in selected) {
+                                v = selected[i];
+                                if (values.indexOf(v) > -1) {
+                                    lock = true;
+                                } else {
+                                    lock = false;
+                                    return;
+                                }
+                            }
+                        } else {
+                            lock = false;
+                        }
+                    } else {
+                        value = this.get('value');
+                        lock = lock || (values.indexOf(value) > -1);
+                    }
+                });
+                return {
+                    lock : lock,
+                    hide : false
+                }
+            },
             _dependency_hide : function(elements, value) {
                 return {
                     lock : false,
index 71e213a..c5818dd 100644 (file)
@@ -261,10 +261,11 @@ abstract class moodleform {
         $submission = array();
         if ($method == 'post') {
             if (!empty($_POST)) {
-                $submission = $this->_get_post_params();
+                $submission = $_POST;
             }
         } else {
-            $submission = array_merge_recursive($_GET, $this->_get_post_params()); // Emulate handling of parameters in xxxx_param().
+            $submission = $_GET;
+            merge_query_params($submission, $_POST); // Emulate handling of parameters in xxxx_param().
         }
 
         // following trick is needed to enable proper sesskey checks when using GET forms
@@ -284,34 +285,12 @@ abstract class moodleform {
     }
 
     /**
-     * Internal method. Gets all POST variables, bypassing max_input_vars limit if needed.
-     *
-     * @return array All POST variables as an array, in the same format as $_POST.
+     * Internal method - should not be used anywhere.
+     * @deprecated since 2.6
+     * @return array $_POST.
      */
     protected function _get_post_params() {
-        $enctype = $this->_form->getAttribute('enctype');
-        $max = (int)ini_get('max_input_vars');
-
-        if (empty($max) || count($_POST, COUNT_RECURSIVE) < $max || (!empty($enctype) && $enctype == 'multipart/form-data')) {
-            return $_POST;
-        }
-
-        // Large POST request with enctype supported by php://input.
-        // Parse php://input in chunks to bypass max_input_vars limit, which also applies to parse_str().
-        $allvalues = array();
-        $values = array();
-        $str = file_get_contents("php://input");
-        $delim = '&';
-
-        $fun = create_function('$p', 'return implode("'.$delim.'", $p);');
-        $chunks = array_map($fun, array_chunk(explode($delim, $str), $max));
-
-        foreach ($chunks as $chunk) {
-            parse_str($chunk, $values);
-            $allvalues = array_merge_recursive($allvalues, $values);
-        }
-
-        return $allvalues;
+        return $_POST;
     }
 
     /**
index 426c593..51c71c4 100644 (file)
@@ -57,6 +57,14 @@ function message_send($eventdata) {
     //new message ID to return
     $messageid = false;
 
+    // Fetch default (site) preferences
+    $defaultpreferences = get_message_output_default_preferences();
+    $preferencebase = $eventdata->component.'_'.$eventdata->name;
+    // If message provider is disabled then don't do any processing.
+    if (!empty($defaultpreferences->{$preferencebase.'_disable'})) {
+        return $messageid;
+    }
+
     //TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
     $DB->transactions_forbidden();
 
@@ -150,12 +158,9 @@ function message_send($eventdata) {
 
     // Fetch enabled processors
     $processors = get_message_processors(true);
-    // Fetch default (site) preferences
-    $defaultpreferences = get_message_output_default_preferences();
 
     // Preset variables
     $processorlist = array();
-    $preferencebase = $eventdata->component.'_'.$eventdata->name;
     // Fill in the array of processors to be used based on default and user preferences
     foreach ($processors as $processor) {
         // Skip adding processors for internal user, if processor doesn't support sending message to internal user.
index 17a499c..6e69ce4 100644 (file)
@@ -2077,7 +2077,7 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
         $select = array('id'=>$courseid);
     } else {
         $select = array();
-        @set_time_limit(0);  // this could take a while!   MDL-10954
+        core_php_time_limit::raise();  // this could take a while!   MDL-10954
     }
 
     $rs = $DB->get_recordset("course", $select,'','id,'.join(',', course_modinfo::$cachedfields));
index d1c873f..7e61808 100644 (file)
@@ -1850,9 +1850,6 @@ class global_navigation extends navigation_node {
             }
             foreach ($modinfo->sections[$section->section] as $cmid) {
                 $cm = $modinfo->cms[$cmid];
-                if (!$cm->uservisible) {
-                    continue;
-                }
                 $activity = new stdClass;
                 $activity->id = $cm->id;
                 $activity->course = $course->id;
@@ -1870,7 +1867,7 @@ class global_navigation extends navigation_node {
                     $activity->display = false;
                 } else {
                     $activity->url = $cm->get_url()->out();
-                    $activity->display = true;
+                    $activity->display = $cm->uservisible ? true : false;
                     if (self::module_extends_navigation($cm->modname)) {
                         $activity->nodetype = navigation_node::NODETYPE_BRANCH;
                     }
@@ -2009,9 +2006,6 @@ class global_navigation extends navigation_node {
             return null;
         }
         $cm = $modinfo->cms[$this->page->cm->id];
-        if (!$cm->uservisible) {
-            return null;
-        }
         if ($cm->icon) {
             $icon = new pix_icon($cm->icon, get_string('modulename', $cm->modname), $cm->iconcomponent);
         } else {
@@ -2021,7 +2015,11 @@ class global_navigation extends navigation_node {
         $activitynode = $coursenode->add(format_string($cm->name), $url, navigation_node::TYPE_ACTIVITY, null, $cm->id, $icon);
         $activitynode->title(get_string('modulename', $cm->modname));
         $activitynode->hidden = (!$cm->visible);
-        if (!$url) {
+        if (!$cm->uservisible) {
+            // Do not show any error here, let the page handle exception that activity is not visible for the current user.
+            // Also there may be no exception at all in case when teacher is logged in as student.
+            $activitynode->display = false;
+        } else if (!$url) {
             // Don't show activities that don't have links!
             $activitynode->display = false;
         } else if (self::module_extends_navigation($cm->modname)) {
index 40ae817..89c6592 100644 (file)
@@ -793,6 +793,10 @@ if (!empty($CFG->profilingenabled)) {
     profiling_start();
 }
 
+// Hack to get around max_input_vars restrictions,
+// we need to do this after session init to have some basic DDoS protection.
+workaround_max_input_vars();
+
 // Process theme change in the URL.
 if (!empty($CFG->allowthemechangeonurl) and !empty($_GET['theme'])) {
     // we have to use _GET directly because we do not want this to interfere with _POST
index abda23d..a076035 100644 (file)
@@ -933,6 +933,108 @@ function setup_get_remote_url() {
     return $rurl;
 }
 
+/**
+ * Try to work around the 'max_input_vars' restriction if necessary.
+ */
+function workaround_max_input_vars() {
+    // Make sure this gets executed only once from lib/setup.php!
+    static $executed = false;
+    if ($executed) {
+        debugging('workaround_max_input_vars() must be called only once!');
+        return;
+    }
+    $executed = true;
+
+    if (!isset($_SERVER["CONTENT_TYPE"]) or strpos($_SERVER["CONTENT_TYPE"], 'multipart/form-data') !== false) {
+        // Not a post or 'multipart/form-data' which is not compatible with "php://input" reading.
+        return;
+    }
+
+    if (!isloggedin() or isguestuser()) {
+        // Only real users post huge forms.
+        return;
+    }
+
+    $max = (int)ini_get('max_input_vars');
+
+    if ($max <= 0) {
+        // Most probably PHP < 5.3.9 that does not implement this limit.
+        return;
+    }
+
+    if ($max >= 200000) {
+        // This value should be ok for all our forms, by setting it in php.ini
+        // admins may prevent any unexpected regressions caused by this hack.
+
+        // Note there is no need to worry about DDoS caused by making this limit very high
+        // because there are very many easier ways to DDoS any Moodle server.
+        return;
+    }
+
+    if (count($_POST, COUNT_RECURSIVE) < $max) {
+        return;
+    }
+
+    // Large POST request with enctype supported by php://input.
+    // Parse php://input in chunks to bypass max_input_vars limit, which also applies to parse_str().
+    $str = file_get_contents("php://input");
+    if ($str === false or $str === '') {
+        // Some weird error.
+        return;
+    }
+
+    $delim = '&';
+    $fun = create_function('$p', 'return implode("'.$delim.'", $p);');
+    $chunks = array_map($fun, array_chunk(explode($delim, $str), $max));
+
+    foreach ($chunks as $chunk) {
+        $values = array();
+        parse_str($chunk, $values);
+
+        if (ini_get_bool('magic_quotes_gpc')) {
+            // Use the same logic as lib/setup.php to work around deprecated magic quotes.
+            $values = array_map('stripslashes_deep', $values);
+        }
+
+        merge_query_params($_POST, $values);
+        merge_query_params($_REQUEST, $values);
+    }
+}
+
+/**
+ * Merge parsed POST chunks.
+ *
+ * NOTE: this is not perfect, but it should work in most cases hopefully.
+ *
+ * @param array $target
+ * @param array $values
+ */
+function merge_query_params(array &$target, array $values) {
+    if (isset($values[0]) and isset($target[0])) {
+        // This looks like a split [] array, lets verify the keys are continuous starting with 0.
+        $keys1 = array_keys($values);
+        $keys2 = array_keys($target);
+        if ($keys1 === array_keys($keys1) and $keys2 === array_keys($keys2)) {
+            foreach ($values as $v) {
+                $target[] = $v;
+            }
+            return;
+        }
+    }
+    foreach ($values as $k => $v) {
+        if (!isset($target[$k])) {
+            $target[$k] = $v;
+            continue;
+        }
+        if (is_array($target[$k]) and is_array($v)) {
+            merge_query_params($target[$k], $v);
+            continue;
+        }
+        // We should not get here unless there are duplicates in params.
+        $target[$k] = $v;
+    }
+}
+
 /**
  * Initializes our performance info early.
  *
index 1f72d68..84e9a5a 100644 (file)
@@ -208,7 +208,7 @@ function stats_cron_daily($maxdays=1) {
         }
 
         $days++;
-        @set_time_limit($timeout - 200);
+        core_php_time_limit::raise($timeout - 200);
 
         if ($days > 1) {
             // move the lock
@@ -677,7 +677,7 @@ function stats_cron_weekly() {
 
     $weeks = 0;
     while ($now > $nextstartweek) {
-        @set_time_limit($timeout - 200);
+        core_php_time_limit::raise($timeout - 200);
         $weeks++;
 
         if ($weeks > 1) {
@@ -820,7 +820,7 @@ function stats_cron_monthly() {
 
     $months = 0;
     while ($now > $nextstartmonth) {
-        @set_time_limit($timeout - 200);
+        core_php_time_limit::raise($timeout - 200);
         $months++;
 
         if ($months > 1) {
diff --git a/lib/tests/event_course_module_viewed.php b/lib/tests/event_course_module_viewed.php
new file mode 100644 (file)
index 0000000..7977642
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Tests for base course module viewed event.
+ *
+ * @package    core
+ * @category   phpunit
+ * @copyright  2013 Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+require_once(__DIR__.'/fixtures/event_fixtures.php');
+
+/**
+ * Class core_event_course_module_viewed_testcase
+ *
+ * Tests for event \core\event\course_module_viewed
+ */
+class core_event_course_module_viewed_testcase extends advanced_testcase {
+
+    /**
+     * Test event properties and methods.
+     */
+    public function test_event_attributes() {
+
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $record = new stdClass();
+        $record->course = $course->id;
+        $feed = $this->getDataGenerator()->create_module('feedback', $record);
+        $cm = get_coursemodule_from_instance('feedback', $feed->id);
+        $context = context_module::instance($cm->id);
+
+        // Trigger the page view event.
+        $sink = $this->redirectEvents();
+        $pageevent = \core_tests\event\course_module_viewed::create(array(
+            'context' => $context,
+            'courseid' => $course->id,
+            'objectid' => $feed->id
+        ));
+        $pageevent->trigger();
+        $result = $sink->get_events();
+        $event = reset($result);
+        $sink->close();
+
+        // Test event data.
+        $legacydata = array($course->id, 'feedback', 'view', 'view.php?id=' . $cm->id, $feed->id, $cm->id);
+        $this->assertEventLegacyLogData($legacydata, $event);
+        $this->assertSame('feedback', $event->objecttable);
+        $url = new moodle_url('/mod/feedback/view.php', array('id' => $cm->id));
+        $this->assertEquals($url, $event->get_url());
+
+    }
+
+    /**
+     * Test custom validations of the event.
+     */
+    public function test_event_validations() {
+
+        // Make sure objecttable and object id is always set.
+        try {
+            \core_tests\event\course_module_viewed_noinit::create(array(
+                'contextid' => 1,
+                'courseid' => 2,
+                'objectid' => 3 ));
+        } catch (coding_exception $e) {
+            $this->assertContains("course_module_viewed event must define objectid and object table.", $e->getMessage());
+        }
+
+        try {
+            \core_tests\event\course_module_viewed::create(array(
+                'contextid' => 1,
+                'courseid' => 2,
+            ));
+        } catch (coding_exception $e) {
+            $this->assertContains("course_module_viewed event must define objectid and object table.", $e->getMessage());
+        }
+    }
+}
index 3f9f041..657a50e 100644 (file)
@@ -177,12 +177,12 @@ class core_filelib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test curl class.
+     * Test curl basics.
      */
-    public function test_curl_class() {
+    public function test_curl_basics() {
         global $CFG;
 
-        // Test https success.
+        // Test HTTP success.
         $testhtml = $this->getExternalTestFileUrl('/test.html');
 
         $curl = new curl();
@@ -210,11 +210,24 @@ class core_filelib_testcase extends advanced_testcase {
         $this->assertSame($contents, file_get_contents($tofile));
         @unlink($tofile);
 
+        // Test 404 request.
+        $curl = new curl();
+        $contents = $curl->get($this->getExternalTestFileUrl('/i.do.not.exist'));
+        $response = $curl->getResponse();
+        $this->assertSame('404 Not Found', reset($response));
+        $this->assertSame(0, $curl->get_errno());
+    }
+
+    public function test_curl_redirects() {
+        global $CFG;
+
         // Test full URL redirects.
         $testurl = $this->getExternalTestFileUrl('/test_redir.php');
 
         $curl = new curl();
         $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(2, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -222,12 +235,16 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(2, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
 
         $curl = new curl();
         $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
+        $response = $curl->getResponse();
+        $this->assertSame('302 Found', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(302, $curl->info['http_code']);
         $this->assertSame('', $contents);
@@ -235,6 +252,8 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
+        $response = $curl->getResponse();
+        $this->assertSame('302 Found', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(302, $curl->info['http_code']);
         $this->assertSame('', $contents);
@@ -291,12 +310,16 @@ class core_filelib_testcase extends advanced_testcase {
         $this->assertFileExists($tofile);
         $this->assertSame('done', file_get_contents($tofile));
         @unlink($tofile);
+    }
 
+    public function test_curl_relative_redirects() {
         // Test relative location redirects.
         $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
 
         $curl = new curl();
         $contents = $curl->get($testurl);
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -304,6 +327,8 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get($testurl);
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -313,6 +338,8 @@ class core_filelib_testcase extends advanced_testcase {
 
         $curl = new curl();
         $contents = $curl->get("$testurl?type=301");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -320,12 +347,16 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?type=301");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
 
         $curl = new curl();
         $contents = $curl->get("$testurl?type=302");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -333,12 +364,16 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?type=302");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
 
         $curl = new curl();
         $contents = $curl->get("$testurl?type=303");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -346,12 +381,16 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?type=303");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
 
         $curl = new curl();
         $contents = $curl->get("$testurl?type=307");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -359,12 +398,16 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?type=307");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
 
         $curl = new curl();
         $contents = $curl->get("$testurl?type=308");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
@@ -372,10 +415,18 @@ class core_filelib_testcase extends advanced_testcase {
         $curl = new curl();
         $curl->emulateredirects = true;
         $contents = $curl->get("$testurl?type=308");
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame(1, $curl->info['redirect_count']);
         $this->assertSame('done', $contents);
 
+    }
+
+    public function test_curl_proxybypass() {
+        global $CFG;
+        $testurl = $this->getExternalTestFileUrl('/test.html');
+
         $oldproxy = $CFG->proxyhost;
         $oldproxybypass = $CFG->proxybypass;
 
@@ -383,15 +434,15 @@ class core_filelib_testcase extends advanced_testcase {
         $CFG->proxyhost = 'i.do.not.exist';
         $CFG->proxybypass = '';
         $curl = new curl();
-        $contents = $curl->get($testhtml);
+        $contents = $curl->get($testurl);
         $this->assertNotEquals(0, $curl->get_errno());
         $this->assertNotEquals('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
 
         // Test with proxy bypass.
-        $testhtmlhost = parse_url($testhtml, PHP_URL_HOST);
-        $CFG->proxybypass = $testhtmlhost;
+        $testurlhost = parse_url($testurl, PHP_URL_HOST);
+        $CFG->proxybypass = $testurlhost;
         $curl = new curl();
-        $contents = $curl->get($testhtml);
+        $contents = $curl->get($testurl);
         $this->assertSame(0, $curl->get_errno());
         $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
 
@@ -399,6 +450,27 @@ class core_filelib_testcase extends advanced_testcase {
         $CFG->proxybypass = $oldproxybypass;
     }
 
+    public function test_curl_post() {
+        $testurl = $this->getExternalTestFileUrl('/test_post.php');
+
+        // Test post request.
+        $curl = new curl();
+        $contents = $curl->post($testurl, 'data=moodletest');
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
+        $this->assertSame(0, $curl->get_errno());
+        $this->assertSame('OK', $contents);
+
+        // Test 100 requests.
+        $curl = new curl();
+        $curl->setHeader('Expect: 100-continue');
+        $contents = $curl->post($testurl, 'data=moodletest');
+        $response = $curl->getResponse();
+        $this->assertSame('200 OK', reset($response));
+        $this->assertSame(0, $curl->get_errno());
+        $this->assertSame('OK', $contents);
+    }
+
     /**
      * Testing prepare draft area
      *
index cce023d..9cc8252 100644 (file)
@@ -228,3 +228,25 @@ class noname_event extends \core\event\base {
 class content_viewed extends \core\event\content_viewed {
 }
 
+
+/**
+ * Class course_module_viewed.
+ *
+ * Wrapper for testing \core\event\course_module_viewed.
+ */
+class course_module_viewed extends \core\event\course_module_viewed {
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'feedback';
+    }
+}
+
+/**
+ * Class course_module_viewed_noinit.
+ *
+ * Wrapper for testing \core\event\course_module_viewed.
+ */
+class course_module_viewed_noinit extends \core\event\course_module_viewed {
+}
+
index 4f95515..46c710b 100644 (file)
@@ -27,6 +27,47 @@ defined('MOODLE_INTERNAL') || die();
 
 class core_messagelib_testcase extends advanced_testcase {
 
+    public function test_message_provider_disabled() {
+        $this->resetAfterTest();
+        $this->preventResetByRollback();
+        unset_config('noemailever');
+
+        // Disable instantmessage provider.
+        $disableprovidersetting = 'moodle_instantmessage_disable';
+        set_config($disableprovidersetting, 1, 'message');
+        $preferences = get_message_output_default_preferences();
+        $this->assertTrue($preferences->$disableprovidersetting == 1);
+
+        $message = new stdClass();
+        $message->component         = 'moodle';
+        $message->name              = 'instantmessage';
+        $message->userfrom          = get_admin();
+        $message->userto            = $this->getDataGenerator()->create_user();;
+        $message->subject           = 'message subject 1';
+        $message->fullmessage       = 'message body';
+        $message->fullmessageformat = FORMAT_MARKDOWN;
+        $message->fullmessagehtml   = '<p>message body</p>';
+        $message->smallmessage      = 'small message';
+
+        // Check message is not sent.
+        $sink = $this->redirectEmails();
+        $this->assertTrue(phpunit_util::is_redirecting_phpmailer());
+        message_send($message);
+        $emails = $sink->get_messages();
+        $this->assertEmpty($emails);
+
+        // Check message is sent.
+        set_config($disableprovidersetting, 0, 'message');
+        $preferences = get_message_output_default_preferences();
+        $this->assertTrue($preferences->$disableprovidersetting == 0);
+
+        $sink = $this->redirectEmails();
+        $this->assertTrue(phpunit_util::is_redirecting_phpmailer());
+        message_send($message);
+        $emails = $sink->get_messages();
+        $email = reset($emails);
+        $this->assertEquals($email->subject, 'message subject 1');
+    }
     public function test_message_get_providers_for_user() {
         global $CFG, $DB;
 
index d170d55..75de3b8 100644 (file)
@@ -110,11 +110,11 @@ function hm()
 
         $files = array($testfile1, $testfile2);
 
-        $this->assertSame("function hm(){}\nfunction oh(){}", core_minify::js_files($files));
+        $this->assertSame("function hm(){};\nfunction oh(){}", core_minify::js_files($files));
 
         $files = array($testfile1, $testfile2, $testfile3);
 
-        $this->assertStringStartsWith("function hm(){}\nfunction oh(){}\n\n\n// Cannot read JS file ", @core_minify::js_files($files));
+        $this->assertStringStartsWith("function hm(){};\nfunction oh(){};\n\n\n// Cannot read JS file ", @core_minify::js_files($files));
 
         unlink($testfile1);
         unlink($testfile2);
index 6eec7b6..ae9fea6 100644 (file)
@@ -138,7 +138,7 @@ function simple_get_record_by_id($context) {
 }
 
 function run_tests($function, $contexts, $numcalls, $basetime) {
-    set_time_limit(120);
+    core_php_time_limit::raise(120);
     $startime = microtime(true);
     for ($j = 0; $j < $numcalls; $j++) {
         $function($contexts[array_rand($contexts)]);
@@ -158,7 +158,7 @@ function print_result_line($duration, $basetime, $numcalls, $action1, $action2 =
 
 function populate_test_database($syscontext, $numcategories, $numcourses, $nummodules, $numoverrides, $numconfigs) {
     global $DB, $OUTPUT;
-    set_time_limit(600);
+    core_php_time_limit::raise(600);
     $syscontext->id = $DB->insert_record('context', $syscontext);
 
     // Category contexts.
index 2131107..a17971d 100644 (file)
@@ -213,4 +213,82 @@ class core_setuplib_testcase extends advanced_testcase {
         $this->assertFileExists($timestampfile);
         $this->assertTimeCurrent(filemtime($timestampfile));
     }
+
+    public function test_merge_query_params() {
+        $original = array(
+            'id' => '1',
+            'course' => '2',
+            'action' => 'delete',
+            'grade' => array(
+                0 => 'a',
+                1 => 'b',
+                2 => 'c',
+            ),
+            'items' => array(
+                'a' => 'aa',
+                'b' => 'bb',
+            ),
+            'mix' => array(
+                0 => '2',
+            ),
+            'numerical' => array(
+                '2' => array('a' => 'b'),
+                '1' => '2',
+            ),
+        );
+
+        $chunk = array(
+            'numerical' => array(
+                '0' => 'z',
+                '2' => array('d' => 'e'),
+            ),
+            'action' => 'create',
+            'next' => '2',
+            'grade' => array(
+                0 => 'e',
+                1 => 'f',
+                2 => 'g',
+            ),
+            'mix' => 'mix',
+        );
+
+        $expected = array(
+            'id' => '1',
+            'course' => '2',
+            'action' => 'create',
+            'grade' => array(
+                0 => 'a',
+                1 => 'b',
+                2 => 'c',
+                3 => 'e',
+                4 => 'f',
+                5 => 'g',
+            ),
+            'items' => array(
+                'a' => 'aa',
+                'b' => 'bb',
+            ),
+            'mix' => 'mix',
+            'numerical' => array(
+                '2' => array('a' => 'b', 'd' => 'e'),
+                '1' => '2',
+                '0' => 'z',
+            ),
+            'next' => '2',
+        );
+
+        $array = $original;
+        merge_query_params($array, $chunk);
+
+        $this->assertSame($expected, $array);
+        $this->assertNotSame($original, $array);
+
+        $query = "id=1&course=2&action=create&grade%5B%5D=a&grade%5B%5D=b&grade%5B%5D=c&grade%5B%5D=e&grade%5B%5D=f&grade%5B%5D=g&items%5Ba%5D=aa&items%5Bb%5D=bb&mix=mix&numerical%5B2%5D%5Ba%5D=b&numerical%5B2%5D%5Bd%5D=e&numerical%5B1%5D=2&numerical%5B0%5D=z&next=2";
+        $decoded = array();
+        parse_str($query, $decoded);
+        $this->assertSame($expected, $decoded);
+
+        // Prove that we cannot use array_merge_recursive() instead.
+        $this->assertNotSame($expected, array_merge_recursive($original, $chunk));
+    }
 }
index 50fd289..8e45f22 100644 (file)
@@ -111,11 +111,6 @@ class core_statslib_testcase extends advanced_testcase {
         $this->resetAfterTest();
     }
 
-    protected function tearDown() {
-        // Reset the timeouts.
-        set_time_limit(0);
-    }
-
     /**
      * Function to setup database.
      *
index 6fd732a..5b93690 100644 (file)
@@ -61,6 +61,9 @@ information provided here is intended especially for developers.
   new class boxnet_client(). Note that the method names and return values have changed.
 * Settings pages are now possible for Calendar type plugins. Calendar type plugins that require a settings page to
   work properly will need to set their requires version to a number that is equal to or grater than the 2.6.1 release version.
+* The admin/tool/generator tool was overhauled to use testing data generators and the previous interface to create
+  test data was removed (it was not working correctly anyway). If you were using this tool you will probably need to
+  update your code.
 
 DEPRECATIONS:
 Various previously deprecated functions have now been altered to throw DEBUG_DEVELOPER debugging notices
index 7f22c4d..0ec6e24 100644 (file)
@@ -156,9 +156,9 @@ function upgrade_set_timeout($max_execution_time=300) {
 
     if (CLI_SCRIPT) {
         // there is no point in timing out of CLI scripts, admins can stop them if necessary
-        set_time_limit(0);
+        core_php_time_limit::raise();
     } else {
-        set_time_limit($max_execution_time);
+        core_php_time_limit::raise($max_execution_time);
     }
     set_config('upgraderunning', $expected_end); // keep upgrade locked until this time
 }
@@ -401,7 +401,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
 
     foreach ($plugs as $plug=>$fullplug) {
         // Reset time so that it works when installing a large number of plugins
-        set_time_limit(600);
+        core_php_time_limit::raise(600);
         $component = clean_param($type.'_'.$plug, PARAM_COMPONENT); // standardised plugin name
 
         // check plugin dir is valid name
@@ -1463,7 +1463,7 @@ function install_core($version, $verbose) {
     make_writable_directory($CFG->dataroot.'/muc', true);
 
     try {
-        set_time_limit(600);
+        core_php_time_limit::raise(600);
         print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag
 
         $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml");
@@ -1521,7 +1521,7 @@ function upgrade_core($version, $verbose) {
         // Pre-upgrade scripts for local hack workarounds.
         $preupgradefile = "$CFG->dirroot/local/preupgrade.php";
         if (file_exists($preupgradefile)) {
-            set_time_limit(0);
+            core_php_time_limit::raise();
             require($preupgradefile);
             // Reset upgrade timeout to default.
             upgrade_set_timeout();
index 96b4458..1f7b641 100644 (file)
@@ -161,7 +161,7 @@ class webdav_client {
         // let's try to open a socket
         $this->_error_log('open a socket connection');
         $this->sock = fsockopen($this->_socket . $this->_server, $this->_port, $this->_errno, $this->_errstr, $this->_socket_timeout);
-        set_time_limit(30);
+        core_php_time_limit::raise(30);
         if (is_resource($this->sock)) {
             socket_set_blocking($this->sock, true);
             $this->_connection_closed = false;
index e8d0895..773373c 100644 (file)
@@ -59,7 +59,8 @@ YUI.add('moodle-core-dragdrop', function(Y) {
                 .setAttribute('title', title)
                 .setAttribute('tabIndex', 0)
                 .setAttribute('data-draggroups', this.groups)
-                .setAttribute('aria-grabbed', 'false');
+                .setAttribute('aria-grabbed', 'false')
+                .setAttribute('role', 'button');
             dragelement.appendChild(dragicon);
             dragelement.addClass(MOVEICON.cssclass);
 
index 58513ea..3e95a30 100644 (file)
@@ -41,6 +41,15 @@ if (($form = data_submitted()) && confirm_sesskey()) {
     // Prepare default message outputs settings
     foreach ( $providers as $provider) {
         $componentproviderbase = $provider->component.'_'.$provider->name;
+        $disableprovidersetting = $componentproviderbase.'_disable';
+        $providerdisabled = false;
+        if (!isset($form->$disableprovidersetting)) {
+            $providerdisabled = true;
+            $preferences[$disableprovidersetting] = 1;
+        } else {
+            $preferences[$disableprovidersetting] = 0;
+        }
+
         foreach (array('permitted', 'loggedin', 'loggedoff') as $setting){
             $value = null;
             $componentprovidersetting = $componentproviderbase.'_'.$setting;
@@ -56,13 +65,13 @@ if (($form = data_submitted()) && confirm_sesskey()) {
                     }
                     // Ensure that loggedin loggedoff options are set correctly
                     // for this permission
-                    if ($value == 'forced') {
-                        $form->{$componentproviderbase.'_loggedin'}[$processor->name] = 1;
-                        $form->{$componentproviderbase.'_loggedoff'}[$processor->name] = 1;
-                    } else if ($value == 'disallowed') {
+                    if (($value == 'disallowed') || $providerdisabled) {
                         // It might be better to unset them, but I can't figure out why that cause error
                         $form->{$componentproviderbase.'_loggedin'}[$processor->name] = 0;
                         $form->{$componentproviderbase.'_loggedoff'}[$processor->name] = 0;
+                    } else if ($value == 'forced') {
+                        $form->{$componentproviderbase.'_loggedin'}[$processor->name] = 1;
+                        $form->{$componentproviderbase.'_loggedoff'}[$processor->name] = 1;
                     }
                     // record the site preference
                     $preferences[$processor->name.'_provider_'.$componentprovidersetting] = $value;
index dc4ee7a..6fd3bf4 100644 (file)
@@ -54,6 +54,12 @@ M.core_message.init_defaultoutputs = function(Y) {
                 // set initial layout
                 node.simulate("change");
             }, this);
+
+            Y.all('#defaultmessageoutputs input.messagedisable').each(function(node) {
+                // Attach event listener
+                node.on('change', defaultoutputs.changeProviderState);
+                node.simulate("change");
+            }, this);
         },
 
         changeState : function(e) {
@@ -83,6 +89,26 @@ M.core_message.init_defaultoutputs = function(Y) {
                     node.setAttribute('checked', 1)
                 }
             }, this);
+        },
+
+        changeProviderState : function(e) {
+            var isenabled = e.target.get('checked') || undefined;
+            var parentnode = e.target.ancestor('tr');
+            if (!isenabled) {
+                parentnode.all('select').each(function(node) {
+                    node.set('value', 'disallowed');
+                    node.setAttribute('disabled', 1);
+                    defaultoutputs.updateCheckboxes(node.ancestor('td'), 1, 0);
+                }, this);
+                parentnode.addClass('dimmed_text');
+            } else {
+                parentnode.all('select').each(function(node) {
+                    node.removeAttribute('disabled');
+                    node.set('value', 'permitted');
+                    defaultoutputs.updateCheckboxes(node.ancestor('td'), 0, 0);
+                }, this);
+                parentnode.removeClass('dimmed_text');
+            }
         }
     }
 
index 82e8e68..846a795 100644 (file)
@@ -134,6 +134,9 @@ class core_message_renderer extends plugin_renderer_base {
         foreach ($processors as $processor) {
             $table->head[]  = get_string('pluginname', 'message_'.$processor->name);
         }
+        // Add enable/disable to head
+        $table->head[] = get_string('enabled', 'core_message');
+
         // Generate the matrix of settings for each provider and processor
         foreach ($providers as $provider) {
             $row = new html_table_row();
@@ -143,14 +146,16 @@ class core_message_renderer extends plugin_renderer_base {
             // Provider Name
             $providername = get_string('messageprovider:'.$provider->name, $provider->component);
             $row->cells[] = new html_table_cell($providername);
-
+            $providersettingprefix = $provider->component.'_'.$provider->name.'_';
+            $disableprovidersetting = $providersettingprefix.'disable';
+            $providerdisabled = !empty($preferences->$disableprovidersetting);
             // Settings for each processor
             foreach ($processors as $processor) {
                 $cellcontent = '';
                 foreach (array('permitted', 'loggedin', 'loggedoff') as $setting) {
                     // pepare element and preference names
-                    $elementname = $provider->component.'_'.$provider->name.'_'.$setting.'['.$processor->name.']';
-                    $preferencebase = $provider->component.'_'.$provider->name.'_'.$setting;
+                    $elementname = $providersettingprefix.$setting.'['.$processor->name.']';
+                    $preferencebase = $providersettingprefix.$setting;
                     // prepare language bits
                     $processorname = get_string('pluginname', 'message_'.$processor->name);
                     $statename = get_string($setting, 'message');
@@ -164,7 +169,9 @@ class core_message_renderer extends plugin_renderer_base {
                         // determine the current setting or use default
                         $select = MESSAGE_DEFAULT_PERMITTED;
                         $preference = $processor->name.'_provider_'.$preferencebase;
-                        if (array_key_exists($preference, $preferences)) {
+                        if ($providerdisabled) {
+                            $select = MESSAGE_DISALLOWED;
+                        } else if (array_key_exists($preference, $preferences)) {
                             $select = $preferences->{$preference};
                         }
                         // dropdown menu
@@ -193,6 +200,10 @@ class core_message_renderer extends plugin_renderer_base {
                 }
                 $row->cells[] = new html_table_cell($cellcontent);
             }
+            $disableprovider = html_writer::checkbox($disableprovidersetting, 1, !$providerdisabled, '',
+                    array('id' => $disableprovidersetting, 'class' => 'messagedisable'));
+            $disableprovider = html_writer::tag('div', $disableprovider);
+            $row->cells[] = new html_table_cell($disableprovider);
             $table->data[] = $row;
         }
 
@@ -240,6 +251,7 @@ class core_message_renderer extends plugin_renderer_base {
         $numprocs = count($processors);
         // Display the messaging options table(s)
         foreach ($components as $component) {
+            $provideradded = false;
             $table = new html_table();
             $table->attributes['class'] = 'generaltable';
             $table->data = array();
@@ -249,18 +261,18 @@ class core_message_renderer extends plugin_renderer_base {
                 $componentname = get_string('coresystem');
             }
             $table->head = array($componentname);
-
             foreach ($readyprocessors as $processor) {
                 $table->head[]  = get_string('pluginname', 'message_'.$processor->name);
             }
-
             // Populate the table with rows
-            foreach ( $providers as $provider) {
-                if( $provider->component != $component) {
+            foreach ($providers as $provider) {
+                $preferencebase = $provider->component.'_'.$provider->name;
+                // If provider component is not same or provider disabled then don't show.
+                if (($provider->component != $component) ||
+                        (!empty($defaultpreferences->{$preferencebase.'_disable'}))) {
                     continue;
                 }
-                $preferencebase = $provider->component.'_'.$provider->name;
-
+                $provideradded = true;
                 $headerrow = new html_table_row();
                 $providername = get_string('messageprovider:'.$provider->name, $provider->component);
                 $providercell = new html_table_cell($providername);
@@ -331,9 +343,12 @@ class core_message_renderer extends plugin_renderer_base {
                     $table->data[] = $optionrow;
                 }
             }
-            $output .= html_writer::start_tag('div', array('class' => 'messagesettingcomponent'));
-            $output .= html_writer::table($table);
-            $output .= html_writer::end_tag('div');
+            // Add settings only if provider added for component.
+            if ($provideradded) {
+                $output .= html_writer::start_tag('div', array('class' => 'messagesettingcomponent'));
+                $output .= html_writer::table($table);
+                $output .= html_writer::end_tag('div');
+            }
         }
 
         $output .= html_writer::end_tag('fieldset');
index c3fd443..c59477f 100644 (file)
@@ -167,7 +167,7 @@ class assignfeedback_file_zip_importer {
         raise_memory_limit(MEMORY_EXTRA);
 
         $packer = get_file_packer('application/zip');
-        @set_time_limit(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME);
+        core_php_time_limit::raise(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME);
 
         return $packer->extract_to_storage($zipfile,
                                     $contextid,
@@ -218,7 +218,7 @@ class assignfeedback_file_zip_importer {
     public function import_zip_files($assignment, $fileplugin) {
         global $CFG, $PAGE, $DB;
 
-        @set_time_limit(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME);
+        core_php_time_limit::raise(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME);
         $packer = get_file_packer('application/zip');
 
         $feedbackfilesupdated = 0;
index 2df6e74..2c71b5c 100644 (file)
@@ -40,11 +40,6 @@ require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
  */
 class mod_assign_upgradelib_testcase extends mod_assign_base_testcase {
 
-    protected function tearDown() {
-        // Reset the timeouts.
-        set_time_limit(0);
-    }
-
     public function test_upgrade_upload_assignment() {
         global $DB, $CFG;
 
index 3b416a5..1b26b56 100644 (file)
@@ -61,7 +61,7 @@ class assign_upgrade_manager {
               return false;
         }
 
-        @set_time_limit(ASSIGN_MAX_UPGRADE_TIME_SECS);
+        core_php_time_limit::raise(ASSIGN_MAX_UPGRADE_TIME_SECS);
 
         // Get the module details.
         $oldmodule = $DB->get_record('modules', array('name'=>'assignment'), '*', MUST_EXIST);
index f8eef44..20848d8 100644 (file)
@@ -32,44 +32,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2013 Frédéric Massart
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class course_module_viewed extends \core\event\content_viewed {
-
-    /**
-     * Returns description of what happened.
-     *
-     * @return string
-     */
-    public function get_description() {
-        return 'User with id ' . $this->userid . ' viewed book activity with instance id ' . $this->objectid;
-    }
-
-    /**
-     * Return the legacy event log data.
-     *
-     * @return array|null
-     */
-    protected function get_legacy_logdata() {
-        return array($this->courseid, 'book', 'view', 'view.php?id=' . $this->context->instanceid, $this->objectid,
-            $this->context->instanceid);
-    }
-
-    /**
-     * Return localised event name.
-     *
-     * @return string
-     */
-    public static function get_name() {
-        return get_string('event_course_module_viewed', 'mod_book');
-    }
-
-    /**
-     * Get URL related to the action.
-     *
-     * @return \moodle_url
-     */
-    public function get_url() {
-        return new \moodle_url('/mod/book/view.php', array('id' => $this->context->instanceid));
-    }
+class course_module_viewed extends \core\event\course_module_viewed {
 
     /**
      * Init method.
@@ -81,17 +44,4 @@ class course_module_viewed extends \core\event\content_viewed {
         $this->data['level'] = self::LEVEL_PARTICIPATING;
         $this->data['objecttable'] = 'book';
     }
-
-    /**
-     * Custom validation.
-     *
-     * @throws \coding_exception
-     * @return void
-     */
-    protected function validate_data() {
-        // Hack to please the parent class. 'view' was the key used in old add_to_log().
-        $this->data['other']['content'] = 'view';
-        parent::validate_data();
-    }
-
 }
index 4acbc52..f35e4e2 100644 (file)
@@ -51,7 +51,6 @@ $string['event_chapter_deleted'] = 'Chapter deleted';
 $string['event_chapter_updated'] = 'Chapter updated';
 $string['event_chapter_viewed'] = 'Chapter viewed';
 $string['event_instances_list_viewed'] = 'Instances list viewed';
-$string['event_course_module_viewed'] = 'Course module viewed';
 $string['subchapter'] = 'Subchapter';
 $string['nocontent'] = 'No content has been added to this book yet.';
 $string['numbering'] = 'Chapter formatting';
index 567a95d..9f56fbb 100644 (file)
@@ -62,7 +62,7 @@ if(!empty($safemode)) {
     die("Error: Cannot run with PHP safe_mode = On. Turn off safe_mode in php.ini.\n");
 }
 
-@set_time_limit (0);
+core_php_time_limit::raise(0);
 error_reporting(E_ALL);
 
 function chat_empty_connection() {