Merge branch 'wip-MDL-36551-master' of git://github.com/abgreeve/moodle
authorSam Hemelryk <sam@moodle.com>
Mon, 26 Nov 2012 01:44:40 +0000 (14:44 +1300)
committerSam Hemelryk <sam@moodle.com>
Mon, 26 Nov 2012 01:44:40 +0000 (14:44 +1300)
228 files changed:
.gitignore
admin/index.php
admin/settings/appearance.php
admin/settings/server.php
admin/tool/phpunit/cli/util.php
admin/user.php
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/lib_test.php
backup/moodle2/restore_stepslib.php
backup/util/checks/tests/checks_test.php
blocks/course_overview/renderer.php
blocks/course_overview/styles.css
blocks/dock.js
blocks/glossary_random/block_glossary_random.php
blocks/site_main_menu/styles.css [new file with mode: 0644]
cache/README.md
cache/classes/config.php
cache/classes/dummystore.php
cache/classes/helper.php
cache/classes/interfaces.php
cache/classes/loaders.php
cache/classes/store.php [new file with mode: 0644]
cache/lib.php
cache/locallib.php
cache/stores/file/lib.php
cache/stores/memcache/lib.php
cache/stores/memcached/lib.php
cache/stores/mongodb/lib.php
cache/stores/session/lib.php
cache/stores/static/lib.php
calendar/lib.php
calendar/managesubscriptions_form.php
calendar/renderer.php
composer.json [new file with mode: 0644]
config-dist.php
course/format/renderer.php
course/format/scorm/lib.php
course/lib.php
course/yui/toolboxes/toolboxes.js
enrol/flatfile/lib.php
enrol/renderer.php
grade/import/csv/index.php
grade/import/grade_import_form.php
grade/report/grader/index.php
grade/report/grader/lib.php
install/lang/es_mx/install.php
lang/en/admin.php
lang/en/calendar.php
lang/en/message.php
lib/adminlib.php
lib/bennu/iCalendar_components.php
lib/bennu/readme_moodle.txt
lib/db/install.xml
lib/db/upgrade.php
lib/dml/moodle_database.php
lib/dml/mssql_native_moodle_database.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/oci_native_moodle_database.php
lib/dml/pgsql_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_database.php
lib/eaccelerator.class.php [deleted file]
lib/filelib.php
lib/form/yui/dateselector/dateselector.js
lib/formslib.php
lib/javascript-static.js
lib/memcached.class.php [deleted file]
lib/messagelib.php
lib/modinfolib.php
lib/moodlelib.php
lib/outputrenderers.php
lib/phpunit/classes/data_generator.php
lib/phpunit/classes/hint_resultprinter.php
lib/phpunit/classes/module_generator.php
lib/phpunit/readme.md
lib/phpunit/tests/generator_test.php
lib/pluginlib.php
lib/sessionlib.php
lib/setup.php
lib/setuplib.php
lib/tablelib.php
lib/tests/accesslib_test.php
lib/tests/outputcomponents_test.php
lib/tests/pagelib_test.php
lib/tests/setuplib_test.php
lib/yui/chooserdialogue/chooserdialogue.js
message/lib.php
mod/assign/assignmentplugin.php
mod/assign/feedback/file/locallib.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/pix/icon.svg
mod/assignment/pix/icon.svg
mod/book/pix/icon.svg
mod/chat/pix/icon.svg
mod/choice/pix/icon.svg
mod/data/field.php
mod/data/lib.php
mod/data/pix/icon.svg
mod/data/preset.php
mod/data/view.php
mod/feedback/pix/icon.svg
mod/folder/pix/icon.svg
mod/forum/discuss.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/pix/icon.svg
mod/glossary/pix/icon.svg
mod/imscp/pix/icon.svg
mod/label/pix/icon.svg
mod/lesson/format.php
mod/lesson/lib.php
mod/lesson/mod_form.php
mod/lesson/pix/icon.svg
mod/lti/pix/icon.svg
mod/page/pix/icon.svg
mod/quiz/pix/icon.svg
mod/quiz/renderer.php
mod/quiz/styles.css
mod/resource/pix/icon.svg
mod/scorm/loadSCO.php
mod/scorm/mod_form.php
mod/scorm/pix/icon.svg
mod/scorm/request.js
mod/survey/pix/icon.svg
mod/url/pix/icon.svg
mod/wiki/pagelib.php
mod/wiki/pix/icon.svg
mod/workshop/pix/icon.svg
mod/workshop/renderer.php
pix/docs.png [new file with mode: 0644]
pix/docs.svg [new file with mode: 0644]
pix/help.svg
pix/i/assignroles.svg
pix/i/backup.svg
pix/i/checkpermissions.svg
pix/i/cohort.svg
pix/i/down.png [new file with mode: 0644]
pix/i/down.svg [new file with mode: 0644]
pix/i/dragdrop.png
pix/i/dragdrop.svg
pix/i/edit.svg
pix/i/enrolusers.svg
pix/i/export.svg
pix/i/filter.svg
pix/i/grades.svg
pix/i/group.svg
pix/i/hide.svg
pix/i/import.svg
pix/i/info.svg
pix/i/item.svg
pix/i/marked.svg
pix/i/marker.svg
pix/i/move_2d.svg
pix/i/navigationitem.svg
pix/i/outcomes.svg
pix/i/permissions.svg
pix/i/publish.svg
pix/i/report.svg
pix/i/repository.svg
pix/i/restore.svg
pix/i/return.svg
pix/i/rss.svg
pix/i/scales.svg
pix/i/settings.svg
pix/i/show.svg
pix/i/switchrole.svg
pix/i/test.svg
pix/i/up.png [new file with mode: 0644]
pix/i/up.svg [new file with mode: 0644]
pix/i/user.svg
pix/i/users.svg
pix/i/withsubcat.svg
pix/t/add.svg
pix/t/assignroles.svg
pix/t/backup.svg
pix/t/block_to_dock.svg
pix/t/block_to_dock_rtl.svg
pix/t/cohort.svg
pix/t/collapsed.svg
pix/t/collapsed_empty.svg
pix/t/collapsed_empty_rtl.svg
pix/t/collapsed_rtl.svg
pix/t/copy.svg
pix/t/delete.svg
pix/t/dock_to_block.svg
pix/t/dock_to_block_rtl.svg
pix/t/dockclose.svg
pix/t/down.png
pix/t/down.svg
pix/t/edit.svg
pix/t/editstring.svg
pix/t/enrolusers.svg
pix/t/expanded.svg
pix/t/groupn.svg
pix/t/groups.svg
pix/t/groupv.svg
pix/t/hide.svg
pix/t/left.svg
pix/t/move.svg
pix/t/restore.svg
pix/t/right.svg
pix/t/show.svg
pix/t/sort_asc.png [new file with mode: 0644]
pix/t/sort_asc.svg [new file with mode: 0644]
pix/t/sort_desc.png [new file with mode: 0644]
pix/t/sort_desc.svg [new file with mode: 0644]
pix/t/switch_minus.svg
pix/t/switch_plus.svg
pix/t/up.png
pix/t/up.svg
question/editlib.php
question/format/xml/format.php
question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php
question/type/multichoice/question.php
question/type/multichoice/tests/question_test.php
repository/filepicker.js
theme/base/cli/svgtool.php [new file with mode: 0644]
theme/base/style/admin.css
theme/base/style/core.css
theme/formal_white/layout/frontpage.php
theme/formal_white/layout/general.php
theme/formal_white/layout/report.php
theme/formal_white/style/formal_white.css
theme/formal_white/style/frame.css
theme/standard/style/blocks.css
theme/standard/style/css3.css
theme/upgrade.txt
version.php

index e95a47a..e27bed2 100644 (file)
@@ -25,4 +25,8 @@ CVS
 /.project
 /.buildpath
 /.cache
-phpunit.xml
\ No newline at end of file
+phpunit.xml
+# Composer support - only composer.json is to be in git, the rest is installed in each checkout.
+composer.phar
+composer.lock
+/vendor/
index 2f88126..bbd528d 100644 (file)
@@ -376,6 +376,10 @@ if (during_initial_install()) {
         }
     }
 
+    // Cleanup SESSION to make sure other code does not complain in the future.
+    unset($SESSION->has_timed_out);
+    unset($SESSION->wantsurl);
+
     // at this stage there can be only one admin unless more were added by install - users may change username, so do not rely on that
     $adminids = explode(',', $CFG->siteadmins);
     $adminuser = get_complete_user_data('id', reset($adminids));
index 7ea3454..99a5eea 100644 (file)
@@ -71,23 +71,21 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $ADMIN->add('appearance', $temp);
 
     // blog
-    if (!empty($CFG->enableblogs)) {
-        $temp = new admin_settingpage('blog', new lang_string('blog','blog'));
-        $temp->add(new admin_setting_configcheckbox('useblogassociations', new lang_string('useblogassociations', 'blog'), new lang_string('configuseblogassociations','blog'), 1));
-        $temp->add(new admin_setting_bloglevel('bloglevel', new lang_string('bloglevel', 'admin'), new lang_string('configbloglevel', 'admin'), 4, array(BLOG_GLOBAL_LEVEL => new lang_string('worldblogs','blog'),
-                                                                                                                                               BLOG_SITE_LEVEL => new lang_string('siteblogs','blog'),
-                                                                                                                                               BLOG_USER_LEVEL => new lang_string('personalblogs','blog'))));
-        $temp->add(new admin_setting_configcheckbox('useexternalblogs', new lang_string('useexternalblogs', 'blog'), new lang_string('configuseexternalblogs','blog'), 1));
-        $temp->add(new admin_setting_configselect('externalblogcrontime', new lang_string('externalblogcrontime', 'blog'), new lang_string('configexternalblogcrontime', 'blog'), 86400,
-            array(43200 => new lang_string('numhours', '', 12),
-                  86400 => new lang_string('numhours', '', 24),
-                  172800 => new lang_string('numdays', '', 2),
-                  604800 => new lang_string('numdays', '', 7))));
-        $temp->add(new admin_setting_configtext('maxexternalblogsperuser', new lang_string('maxexternalblogsperuser','blog'), new lang_string('configmaxexternalblogsperuser', 'blog'), 1));
-        $temp->add(new admin_setting_configcheckbox('blogusecomments', new lang_string('enablecomments', 'admin'), new lang_string('configenablecomments', 'admin'), 1));
-        $temp->add(new admin_setting_configcheckbox('blogshowcommentscount', new lang_string('showcommentscount', 'admin'), new lang_string('configshowcommentscount', 'admin'), 1));
-        $ADMIN->add('appearance', $temp);
-    }
+    $temp = new admin_settingpage('blog', new lang_string('blog','blog'), 'moodle/site:config', empty($CFG->enableblogs));
+    $temp->add(new admin_setting_configcheckbox('useblogassociations', new lang_string('useblogassociations', 'blog'), new lang_string('configuseblogassociations','blog'), 1));
+    $temp->add(new admin_setting_bloglevel('bloglevel', new lang_string('bloglevel', 'admin'), new lang_string('configbloglevel', 'admin'), 4, array(BLOG_GLOBAL_LEVEL => new lang_string('worldblogs','blog'),
+                                                                                                                                           BLOG_SITE_LEVEL => new lang_string('siteblogs','blog'),
+                                                                                                                                           BLOG_USER_LEVEL => new lang_string('personalblogs','blog'))));
+    $temp->add(new admin_setting_configcheckbox('useexternalblogs', new lang_string('useexternalblogs', 'blog'), new lang_string('configuseexternalblogs','blog'), 1));
+    $temp->add(new admin_setting_configselect('externalblogcrontime', new lang_string('externalblogcrontime', 'blog'), new lang_string('configexternalblogcrontime', 'blog'), 86400,
+        array(43200 => new lang_string('numhours', '', 12),
+              86400 => new lang_string('numhours', '', 24),
+              172800 => new lang_string('numdays', '', 2),
+              604800 => new lang_string('numdays', '', 7))));
+    $temp->add(new admin_setting_configtext('maxexternalblogsperuser', new lang_string('maxexternalblogsperuser','blog'), new lang_string('configmaxexternalblogsperuser', 'blog'), 1));
+    $temp->add(new admin_setting_configcheckbox('blogusecomments', new lang_string('enablecomments', 'admin'), new lang_string('configenablecomments', 'admin'), 1));
+    $temp->add(new admin_setting_configcheckbox('blogshowcommentscount', new lang_string('showcommentscount', 'admin'), new lang_string('configshowcommentscount', 'admin'), 1));
+    $ADMIN->add('appearance', $temp);
 
     // Navigation settings
     $temp = new admin_settingpage('navigation', new lang_string('navigation'));
index 4e773cd..3ba7201 100644 (file)
@@ -193,29 +193,6 @@ $temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache'
 
 $temp->add(new admin_setting_configtext('curltimeoutkbitrate', new lang_string('curltimeoutkbitrate', 'admin'),
                                         new lang_string('curltimeoutkbitrate_help', 'admin'), 56, PARAM_INT));
-/* //TODO: we need to fix code instead of relying on slow rcache, enable this once we have some code that is actually using it
-$temp->add(new admin_setting_special_selectsetup('cachetype', new lang_string('cachetype', 'admin'),
-                                          new lang_string('configcachetype', 'admin'), '',
-                                          array( '' => new lang_string('none'),
-                                                 'internal' => 'internal',
-                                                 'memcached' => 'memcached',
-                                                 'eaccelerator' => 'eaccelerator')));
-// NOTE: $CFG->rcache is forced to bool in lib/setup.php
-$temp->add(new admin_setting_special_selectsetup('rcache', new lang_string('rcache', 'admin'),
-                                          new lang_string('configrcache', 'admin'), 0,
-                                          array( '0' => new lang_string('no'),
-                                                 '1' => new lang_string('yes'))));
-$temp->add(new admin_setting_configtext('rcachettl', new lang_string('rcachettl', 'admin'),
-                                        new lang_string('configrcachettl', 'admin'), 10));
-$temp->add(new admin_setting_configtext('intcachemax', new lang_string('intcachemax', 'admin'),
-                                        new lang_string('configintcachemax', 'admin'), 10));
-$temp->add(new admin_setting_configtext('memcachedhosts', new lang_string('memcachedhosts', 'admin'),
-                                        new lang_string('configmemcachedhosts', 'admin'), ''));
-$temp->add(new admin_setting_configselect('memcachedpconn', new lang_string('memcachedpconn', 'admin'),
-                                          new lang_string('configmemcachedpconn', 'admin'), 0,
-                                          array( '0' => new lang_string('no'),
-                                                 '1' => new lang_string('yes'))));
-*/
 
 $ADMIN->add('server', $temp);
 
index 5afd3ba..5cfd128 100644 (file)
@@ -39,7 +39,6 @@ list($options, $unrecognized) = cli_get_params(
         'buildconfig'           => false,
         'buildcomponentconfigs' => false,
         'diag'                  => false,
-        'phpunitdir'            => false,
         'run'                   => false,
         'help'                  => false,
     ),
@@ -48,24 +47,12 @@ list($options, $unrecognized) = cli_get_params(
     )
 );
 
-if ($options['phpunitdir']) {
-    // nasty skodak's hack for testing of future PHPUnit versions - intentionally not documented
-    if (!file_exists($options['phpunitdir'])) {
-        cli_error('Invalid custom PHPUnit lib location');
-    }
-    $files = scandir($options['phpunitdir']);
-    foreach ($files as $file) {
-        $path = $options['phpunitdir'].'/'.$file;
-        if (!is_dir($path) or strpos($file, '.') === 0) {
-            continue;
-        }
-        ini_set('include_path', $path . PATH_SEPARATOR . ini_get('include_path'));
-    }
-    unset($files);
-    unset($file);
+if (file_exists(__DIR__.'/../../../../vendor/autoload.php')) {
+    // Composer packages present.
+    require_once(__DIR__.'/../../../../vendor/autoload.php');
 }
 
-// verify PHPUnit libs are loaded
+// Verify PHPUnit libs can be loaded.
 if (!include_once('PHPUnit/Autoload.php')) {
     phpunit_bootstrap_error(PHPUNIT_EXITCODE_PHPUNITMISSING);
 }
@@ -75,7 +62,7 @@ if ($options['run']) {
     unset($unrecognized);
 
     foreach ($_SERVER['argv'] as $k=>$v) {
-        if (strpos($v, '--run') === 0 or strpos($v, '--phpunitdir') === 0) {
+        if (strpos($v, '--run') === 0) {
             unset($_SERVER['argv'][$k]);
             $_SERVER['argc'] = $_SERVER['argc'] - 1;
         }
index 5abb3b0..97f1abb 100644 (file)
         } else {
             $columndir = $dir == "ASC" ? "DESC":"ASC";
             if ($column == "lastaccess") {
-                $columnicon = $dir == "ASC" ? "up":"down";
+                $columnicon = ($dir == "ASC") ? "sort_desc" : "sort_asc";
             } else {
-                $columnicon = $dir == "ASC" ? "down":"up";
+                $columnicon = ($dir == "ASC") ? "sort_asc" : "sort_desc";
             }
-            $columnicon = " <img src=\"" . $OUTPUT->pix_url('t/' . $columnicon) . "\" alt=\"\" />";
+            $columnicon = "<img class='iconsort' src=\"" . $OUTPUT->pix_url('t/' . $columnicon) . "\" alt=\"\" />";
 
         }
         $$column = "<a href=\"user.php?sort=$column&amp;dir=$columndir\">".$string[$column]."</a>$columnicon";
index 46d7514..4fdf62e 100644 (file)
@@ -641,7 +641,8 @@ class moodle1_converter extends base_converter {
             return $files;
         }
         foreach ($matches[2] as $match) {
-            $files[] = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
+            $file = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
+            $files[] = rawurldecode($file);
         }
 
         return array_unique($files);
@@ -658,9 +659,16 @@ class moodle1_converter extends base_converter {
     public static function rewrite_filephp_usage($text, array $files) {
 
         foreach ($files as $file) {
+            // Expect URLs properly encoded by default.
+            $parts   = explode('/', $file);
+            $encoded = implode('/', array_map('rawurlencode', $parts));
+            $fileref = '$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $encoded);
+            $text    = str_replace($fileref.'$@FORCEDOWNLOAD@$', '@@PLUGINFILE@@'.$encoded.'?forcedownload=1', $text);
+            $text    = str_replace($fileref, '@@PLUGINFILE@@'.$encoded, $text);
+            // Add support for URLs without any encoding.
             $fileref = '$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $file);
-            $text    = str_replace($fileref.'$@FORCEDOWNLOAD@$', '@@PLUGINFILE@@'.$file.'?forcedownload=1', $text);
-            $text    = str_replace($fileref, '@@PLUGINFILE@@'.$file, $text);
+            $text    = str_replace($fileref.'$@FORCEDOWNLOAD@$', '@@PLUGINFILE@@'.$encoded.'?forcedownload=1', $text);
+            $text    = str_replace($fileref, '@@PLUGINFILE@@'.$encoded, $text);
         }
 
         return $text;
index a97720e..bf4935c 100644 (file)
@@ -443,12 +443,39 @@ as it is parsed from the backup file. <br /><br /><img border="0" width="110" vs
         $this->assertTrue(in_array('/pics/news.gif', $files));
         $this->assertTrue(in_array('/MANUAL.DOC', $files));
 
-        $text = moodle1_converter::rewrite_filephp_usage($text, array('/pics/news.gif', '/another/file/notused.txt'), $files);
+        $text = moodle1_converter::rewrite_filephp_usage($text, array('/pics/news.gif', '/another/file/notused.txt'));
         $this->assertEquals($text, 'This is a text containing links to file.php
 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="@@PLUGINFILE@@/pics/news.gif" /><a href="@@PLUGINFILE@@/pics/news.gif?forcedownload=1">download image</a><br />
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />');
     }
 
+    public function test_referenced_files_urlencoded() {
+
+        $text = 'This is a text containing links to file.php
+as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif" /><a href="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif$@FORCEDOWNLOAD@$">no space</a><br />
+    <br /><a href=\'$@FILEPHP@$$@SLASH@$pics$@SLASH@$news%20with%20spaces.gif$@FORCEDOWNLOAD@$\'>with urlencoded spaces</a><br />
+<a href="$@FILEPHP@$$@SLASH@$illegal%20pics%2Bmovies$@SLASH@$romeo%2Bjuliet.avi">Download the full AVI for free! (space and plus encoded)</a>
+<a href="$@FILEPHP@$$@SLASH@$illegal pics+movies$@SLASH@$romeo+juliet.avi">Download the full AVI for free! (none encoded)</a>
+<a href="$@FILEPHP@$$@SLASH@$illegal%20pics+movies$@SLASH@$romeo+juliet.avi">Download the full AVI for free! (only space encoded)</a>
+<a href="$@FILEPHP@$$@SLASH@$illegal pics%2Bmovies$@SLASH@$romeo%2Bjuliet.avi">Download the full AVI for free! (only plus)</a>';
+
+        $files = moodle1_converter::find_referenced_files($text);
+        $this->assertEquals(gettype($files), 'array');
+        $this->assertEquals(3, count($files));
+        $this->assertTrue(in_array('/pics/news.gif', $files));
+        $this->assertTrue(in_array('/pics/news with spaces.gif', $files));
+        $this->assertTrue(in_array('/illegal pics+movies/romeo+juliet.avi', $files));
+
+        $text = moodle1_converter::rewrite_filephp_usage($text, $files);
+        $this->assertEquals('This is a text containing links to file.php
+as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="@@PLUGINFILE@@/pics/news.gif" /><a href="@@PLUGINFILE@@/pics/news.gif?forcedownload=1">no space</a><br />
+    <br /><a href=\'@@PLUGINFILE@@/pics/news%20with%20spaces.gif?forcedownload=1\'>with urlencoded spaces</a><br />
+<a href="@@PLUGINFILE@@/illegal%20pics%2Bmovies/romeo%2Bjuliet.avi">Download the full AVI for free! (space and plus encoded)</a>
+<a href="@@PLUGINFILE@@/illegal%20pics%2Bmovies/romeo%2Bjuliet.avi">Download the full AVI for free! (none encoded)</a>
+<a href="$@FILEPHP@$$@SLASH@$illegal%20pics+movies$@SLASH@$romeo+juliet.avi">Download the full AVI for free! (only space encoded)</a>
+<a href="$@FILEPHP@$$@SLASH@$illegal pics%2Bmovies$@SLASH@$romeo%2Bjuliet.avi">Download the full AVI for free! (only plus)</a>', $text);
+    }
+
     public function test_question_bank_conversion() {
         global $CFG;
 
index af81bfa..fe4f504 100644 (file)
@@ -3128,6 +3128,22 @@ class restore_create_categories_and_questions extends restore_structure_step {
                        AND ' . $DB->sql_compare_text('hint', 255) . ' = ' . $DB->sql_compare_text('?', 255);
             $params = array($newquestionid, $data->hint);
             $newitemid = $DB->get_field_sql($sql, $params);
+
+            // Not able to find the hint, let's try cleaning the hint text
+            // of all the question's hints in DB as slower fallback. MDL-33863.
+            if (!$newitemid) {
+                $potentialhints = $DB->get_records('question_hints',
+                        array('questionid' => $newquestionid), '', 'id, hint');
+                foreach ($potentialhints as $potentialhint) {
+                    // Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
+                    $cleanhint = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $potentialhint->hint); // Clean CTRL chars.
+                    $cleanhint = preg_replace("/\r\n|\r/", "\n", $cleanhint); // Normalize line ending.
+                    if ($cleanhint === $data->hint) {
+                        $newitemid = $data->id;
+                    }
+                }
+            }
+
             // If we haven't found the newitemid, something has gone really wrong, question in DB
             // is missing hints, exception
             if (!$newitemid) {
index e9905c4..6bcbda4 100644 (file)
@@ -50,7 +50,7 @@ class backup_check_testcase extends advanced_testcase {
         $coursemodule = $DB->get_record('course_modules', array('id'=>$page->cmid));
 
         $this->moduleid  = $coursemodule->id;
-        $this->sectionid = $DB->get_field("course_sections", 'id', array("section"=>$coursemodule->section, "course"=>$course->id));
+        $this->sectionid = $coursemodule->section;
         $this->courseid  = $coursemodule->course;
         $this->userid = 2; // admin
 
index a83e51f..9e5a93a 100644 (file)
@@ -149,7 +149,7 @@ class block_course_overview_renderer extends plugin_renderer_base {
             $output .= html_writer::start_tag('div', array('class' => 'activity_overview'));
             $url = new moodle_url("/mod/$module/index.php", array('id' => $cid));
             $modulename = get_string('modulename', $module);
-            $icontext = html_writer::link($url, $this->output->pix_icon('icon', $modulename, 'mod_'.$module, array('class'=>'icon')).' ');
+            $icontext = html_writer::link($url, $this->output->pix_icon('icon', $modulename, 'mod_'.$module, array('class'=>'iconlarge')));
             if (get_string_manager()->string_exists("activityoverview", $module)) {
                 $icontext .= get_string("activityoverview", $module);
             } else {
index fe3e0e3..6718d06 100644 (file)
@@ -4,12 +4,15 @@
 }
 
 .block_course_overview .content {
-    margin-left: 20px;
+    margin: 0 20px;
+}
+.block_course_overview .content .notice {
+    margin: 5px 0;
 }
 
 .block_course_overview .coursebox {
-    padding: 15px 0 10px 10px;
-    width: 98%;
+    padding: 15px;
+    width: auto;
 }
 
 .block_course_overview .profilepicture {
@@ -36,8 +39,7 @@
 
 .block_course_overview .content h2.title {
     float: left;
-    margin-bottom: 0;
-    margin-top: 0;
+    margin: 0 0 .5em 0;
     position: relative;
 }
 .dir-rtl .block_course_overview .content h2.title {
 .block_course_overview .activity_overview {
     padding: 2px;
 }
+.block_course_overview .activity_overview img.iconlarge { vertical-align: text-bottom; margin-right: 6px; }
+.dir-rtl .block_course_overview .activity_overview img.iconlarge { margin-left: 6px; margin-right: 0;}
 
 .block_course_overview .singleselect {
     text-align: left;
     border-width: 2px;
     border-style: dashed;
 }
-
-.block_course_overview .collapsibleregioninner .name {margin-right: 20px;}
-.block_course_overview .collapsibleregioninner .info,
-.block_course_overview .collapsibleregioninner .details {margin-right: 25px;}
\ No newline at end of file
index ca685d5..6e56390 100644 (file)
@@ -536,7 +536,7 @@ M.core_dock.fixTitleOrientation = function(item, title, text) {
     });
 
     // Positioning is different when in RTL mode.
-    if (Y.one(document.body).hasClass('dir-rtl')) {
+    if (right_to_left()) {
         title.setStyle('left', width/2 - height);
     } else {
         title.setStyle('right', width/2 - height);
@@ -837,7 +837,11 @@ M.core_dock.genericblock.prototype = {
 
         // Must set the image src seperatly of we get an error with XML strict headers
         var moveto = Y.Node.create('<input type="image" class="moveto customcommand requiresjs" alt="'+M.str.block.addtodock+'" title="'+M.str.block.addtodock+'" />');
-        moveto.setAttribute('src', M.util.image_url('t/block_to_dock', 'moodle'));
+        var icon = 't/block_to_dock';
+        if (right_to_left()) {
+            icon = 't/block_to_dock_rtl';
+        }
+        moveto.setAttribute('src', M.util.image_url(icon, 'moodle'));
         moveto.on('movetodock|click', this.move_to_dock, this, commands);
 
         var blockaction = node.one('.block_action');
@@ -903,7 +907,11 @@ M.core_dock.genericblock.prototype = {
 
         // Must set the image src seperatly of we get an error with XML strict headers
         var movetoimg = Y.Node.create('<img alt="'+M.str.block.undockitem+'" title="'+M.str.block.undockitem+'" />');
-        movetoimg.setAttribute('src', M.util.image_url('t/dock_to_block', 'moodle'));
+        var icon = 't/dock_to_block';
+        if (right_to_left()) {
+            icon = 't/dock_to_block_rtl';
+        }
+        movetoimg.setAttribute('src', M.util.image_url(icon, 'moodle'));
         var moveto = Y.Node.create('<a class="moveto customcommand requiresjs"></a>').append(movetoimg);
         if (location.href.match(/\?/)) {
             moveto.set('href', location.href+'&dock='+this.id);
index 0128f1b..916780d 100644 (file)
@@ -129,11 +129,6 @@ class block_glossary_random extends block_base {
         $course = $this->page->course;
         $modinfo = get_fast_modinfo($course);
         $glossaryid = $this->config->glossary;
-        $cm = $modinfo->instances['glossary'][$glossaryid];
-
-        if (!has_capability('mod/glossary:view', context_module::instance($cm->id))) {
-            return '';
-        }
 
         if (!isset($modinfo->instances['glossary'][$glossaryid])) {
             // we can get here if the glossary has been deleted, so
@@ -147,6 +142,12 @@ class block_glossary_random extends block_base {
             return $this->content;
         }
 
+        $cm = $modinfo->instances['glossary'][$glossaryid];
+
+        if (!has_capability('mod/glossary:view', context_module::instance($cm->id))) {
+            return '';
+        }
+
         if (empty($this->config->cache)) {
             $this->config->cache = '';
         }
diff --git a/blocks/site_main_menu/styles.css b/blocks/site_main_menu/styles.css
new file mode 100644 (file)
index 0000000..271eca5
--- /dev/null
@@ -0,0 +1,7 @@
+.block_site_main_menu li { clear: both; }
+.block_site_main_menu li .column { width: 100%; }
+.block_site_main_menu li .buttons { float: right; margin-top: 3px;}
+.dir-rtl .block_site_main_menu li .buttons { float: left; }
+.block_site_main_menu li .buttons a img{ vertical-align: text-bottom; margin: 0 3px;}
+.block_site_main_menu .footer { margin-top: 1em; }
+.block_site_main_menu .section_add_menus noscript div { display: inline;}
index 408e014..fc8309d 100644 (file)
@@ -89,6 +89,7 @@ The following points highlight things you should know about stores.
 ** Data guarantee - Data is guaranteed to exist in the cache once it is set there. It is never cleaned up to free space or because it has not been recently used.
 ** Multiple identifiers - Rather than a single string key, the parts that make up the key are passed as an array.
 ** Native TTL support - When required, the store supports native ttl and doesn't require the cache API to manage ttl of things given to the store.
+* There are two reserved store names, base and dummy. These are both used internally.
 
 ### Definition
 _Definitions were not a part of the previous proposal._
index 682ef18..e6d3416 100644 (file)
@@ -182,7 +182,7 @@ class cache_config {
             if (!class_exists($class)) {
                 continue;
             }
-            if (!array_key_exists('cache_store', class_implements($class))) {
+            if (!array_key_exists('cache_store', class_parents($class))) {
                 continue;
             }
             if (!array_key_exists('configuration', $store) || !is_array($store['configuration'])) {
@@ -344,6 +344,61 @@ class cache_config {
         return $this->configdefinitions;
     }
 
+    /**
+     * Returns the definitions mapped into the given store name.
+     *
+     * @param string $storename
+     * @return array Associative array of definitions, id=>definition
+     */
+    public static function get_definitions_by_store($storename) {
+        $definitions = array();
+
+        $config = cache_config::instance();
+        $stores = $config->get_all_stores();
+        if (!array_key_exists($storename, $stores)) {
+            // The store does not exist.
+            return false;
+        }
+
+        $defmappings = $config->get_definition_mappings();
+        // Create an associative array for the definition mappings.
+        $thedefmappings = array();
+        foreach ($defmappings as $defmapping) {
+            $thedefmappings[$defmapping['definition']] = $defmapping;
+        }
+
+        // Search for matches in default mappings.
+        $defs = $config->get_definitions();
+        foreach($config->get_mode_mappings() as $modemapping) {
+            if ($modemapping['store'] !== $storename) {
+                continue;
+            }
+            foreach($defs as $id => $definition) {
+                if ($definition['mode'] !== $modemapping['mode']) {
+                    continue;
+                }
+                // Exclude custom definitions mapping: they will be managed few lines below.
+                if (array_key_exists($id, $thedefmappings)) {
+                    continue;
+                }
+                $definitions[$id] = $definition;
+            }
+        }
+
+        // Search for matches in the custom definitions mapping
+        foreach ($defmappings as $defmapping) {
+            if ($defmapping['store'] !== $storename) {
+                continue;
+            }
+            $definition = $config->get_definition_by_id($defmapping['definition']);
+            if ($definition) {
+                $definitions[$defmapping['definition']] = $definition;
+            }
+        }
+
+        return $definitions;
+    }
+
     /**
      * Returns all of the stores that are suitable for the given mode and requirements.
      *
index d44dc8c..9a69d67 100644 (file)
@@ -37,7 +37,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_dummy implements cache_store {
+class cachestore_dummy extends cache_store {
 
     /**
      * The name of this store.
@@ -135,30 +135,6 @@ class cachestore_dummy implements cache_store {
         return true;
     }
 
-    /**
-     * Returns true if this store supports data guarantee.
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return false;
-    }
-
-    /**
-     * Returns true if this store supports multiple identifiers.
-     * @return bool
-     */
-    public function supports_multiple_identifiers() {
-        return false;
-    }
-
-    /**
-     * Returns true if this store supports a native ttl.
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return true;
-    }
-
     /**
      * Returns the data for the given key
      * @param string $key
index 8c56eeb..4439a56 100644 (file)
@@ -270,6 +270,8 @@ class cache_helper {
     /**
      * Purges the cache for a specific definition.
      *
+     * @todo MDL-36660: Change the signature: $aggregate must be added.
+     *
      * @param string $component
      * @param string $area
      * @param array $identifiers
@@ -278,6 +280,14 @@ class cache_helper {
     public static function purge_by_definition($component, $area, array $identifiers = array()) {
         // Create the cache.
         $cache = cache::make($component, $area, $identifiers);
+        // Initialise, in case of a store.
+        if ($cache instanceof cache_store) {
+            $factory = cache_factory::instance();
+            // TODO MDL-36660: Providing $aggregate is required for purging purposes: $definition->get_id()
+            $definition = $factory->create_definition($component, $area, null);
+            $definition->set_identifiers($identifiers);
+            $cache->initialise($definition);
+        }
         // Purge baby, purge.
         $cache->purge();
         return true;
@@ -295,8 +305,13 @@ class cache_helper {
         foreach ($instance->get_definitions() as $name => $definitionarr) {
             $definition = cache_definition::load($name, $definitionarr);
             if ($definition->invalidates_on_event($event)) {
-                // Purge the cache.
+                // Create the cache.
                 $cache = $factory->create_cache($definition);
+                // Initialise, in case of a store.
+                if ($cache instanceof cache_store) {
+                    $cache->initialise($definition);
+                }
+                // Purge the cache.
                 $cache->purge();
 
                 // We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
@@ -389,16 +404,9 @@ class cache_helper {
      */
     public static function purge_all() {
         $config = cache_config::instance();
-        $stores = $config->get_all_stores();
-        $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, 'core', 'cache_purge');
-        foreach ($stores as $store) {
-            $class = $store['class'];
-            $instance = new $class($store['name'], $store['configuration']);
-            if (!$instance->is_ready()) {
-                continue;
-            }
-            $instance->initialise($definition);
-            $instance->purge();
+
+        foreach ($config->get_all_stores() as $store) {
+            self::purge_store($store['name']);
         }
     }
 
@@ -410,21 +418,32 @@ class cache_helper {
      */
     public static function purge_store($storename) {
         $config = cache_config::instance();
-        foreach ($config->get_all_stores() as $store) {
-            if ($store['name'] !== $storename) {
-                continue;
-            }
-            $class = $store['class'];
+
+        $stores = $config->get_all_stores();
+        if (!array_key_exists($storename, $stores)) {
+            // The store does not exist.
+            return false;
+        }
+
+        $store = $stores[$storename];
+        $class = $store['class'];
+
+        // Found the store: is it ready?
+        $instance = new $class($store['name'], $store['configuration']);
+        if (!$instance->is_ready()) {
+            unset($instance);
+            return false;
+        }
+
+        foreach ($config->get_definitions_by_store($storename) as $id => $definition) {
+            $definition = cache_definition::load($id, $definition);
             $instance = new $class($store['name'], $store['configuration']);
-            if (!$instance->is_ready()) {
-                continue;
-            }
-            $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, 'core', 'cache_purge');
             $instance->initialise($definition);
             $instance->purge();
-            return true;
+            unset($instance);
         }
-        return false;
+
+        return true;
     }
 
     /**
index 983bc6b..d548349 100644 (file)
@@ -230,227 +230,6 @@ interface cache_loader_with_locking {
     public function release_lock($key);
 }
 
-/**
- * Cache store.
- *
- * This interface outlines the requirements for a cache store plugin.
- * It must be implemented by all such plugins and provides a reference to interacting with cache stores.
- *
- * Must be implemented by all cache store plugins.
- *
- * @package    core
- * @category   cache
- * @copyright  2012 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-interface cache_store {
-
-    /**#@+
-     * Constants for features a cache store can support
-     */
-    /**
-     * Supports multi-part keys
-     */
-    const SUPPORTS_MULTIPLE_IDENTIFIERS = 1;
-    /**
-     * Ensures data remains in the cache once set.
-     */
-    const SUPPORTS_DATA_GUARANTEE = 2;
-    /**
-     * Supports a native ttl system.
-     */
-    const SUPPORTS_NATIVE_TTL = 4;
-    /**#@-*/
-
-    /**#@+
-     * Constants for the modes of a cache store
-     */
-    /**
-     * Application caches. These are shared caches.
-     */
-    const MODE_APPLICATION = 1;
-    /**
-     * Session caches. Just access to the PHP session.
-     */
-    const MODE_SESSION = 2;
-    /**
-     * Request caches. Static caches really.
-     */
-    const MODE_REQUEST = 4;
-    /**#@-*/
-
-    /**
-     * Static method to check if the store requirements are met.
-     *
-     * @return bool True if the stores software/hardware requirements have been met and it can be used. False otherwise.
-     */
-    public static function are_requirements_met();
-
-    /**
-     * Static method to check if a store is usable with the given mode.
-     *
-     * @param int $mode One of cache_store::MODE_*
-     */
-    public static function is_supported_mode($mode);
-
-    /**
-     * Returns the supported features as a binary flag.
-     *
-     * @param array $configuration The configuration of a store to consider specifically.
-     * @return int The supported features.
-     */
-    public static function get_supported_features(array $configuration = array());
-
-    /**
-     * Returns the supported modes as a binary flag.
-     *
-     * @param array $configuration The configuration of a store to consider specifically.
-     * @return int The supported modes.
-     */
-    public static function get_supported_modes(array $configuration = array());
-
-    /**
-     * Returns true if this cache store instance supports multiple identifiers.
-     *
-     * @return bool
-     */
-    public function supports_multiple_identifiers();
-
-    /**
-     * Returns true if this cache store instance promotes data guarantee.
-     *
-     * @return bool
-     */
-    public function supports_data_guarantee();
-
-    /**
-     * Returns true if this cache store instance supports ttl natively.
-     *
-     * @return bool
-     */
-    public function supports_native_ttl();
-
-    /**
-     * Used to control the ability to add an instance of this store through the admin interfaces.
-     *
-     * @return bool True if the user can add an instance, false otherwise.
-     */
-    public static function can_add_instance();
-
-    /**
-     * Constructs an instance of the cache store.
-     *
-     * This method should not create connections or perform and processing, it should be used
-     *
-     * @param string $name The name of the cache store
-     * @param array $configuration The configuration for this store instance.
-     */
-    public function __construct($name, array $configuration = array());
-
-    /**
-     * Returns the name of this store instance.
-     * @return string
-     */
-    public function my_name();
-
-    /**
-     * Initialises a new instance of the cache store given the definition the instance is to be used for.
-     *
-     * This function should prepare any given connections etc.
-     *
-     * @param cache_definition $definition
-     */
-    public function initialise(cache_definition $definition);
-
-    /**
-     * Returns true if this cache store instance has been initialised.
-     * @return bool
-     */
-    public function is_initialised();
-
-    /**
-     * Returns true if this cache store instance is ready to use.
-     * @return bool
-     */
-    public function is_ready();
-
-    /**
-     * Retrieves an item from the cache store given its key.
-     *
-     * @param string $key The key to retrieve
-     * @return mixed The data that was associated with the key, or false if the key did not exist.
-     */
-    public function get($key);
-
-    /**
-     * Retrieves several items from the cache store in a single transaction.
-     *
-     * If not all of the items are available in the cache then the data value for those that are missing will be set to false.
-     *
-     * @param array $keys The array of keys to retrieve
-     * @return array An array of items from the cache. There will be an item for each key, those that were not in the store will
-     *      be set to false.
-     */
-    public function get_many($keys);
-
-    /**
-     * Sets an item in the cache given its key and data value.
-     *
-     * @param string $key The key to use.
-     * @param mixed $data The data to set.
-     * @return bool True if the operation was a success false otherwise.
-     */
-    public function set($key, $data);
-
-    /**
-     * Sets many items in the cache in a single transaction.
-     *
-     * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
-     *      keys, 'key' and 'value'.
-     * @return int The number of items successfully set. It is up to the developer to check this matches the number of items
-     *      sent ... if they care that is.
-     */
-    public function set_many(array $keyvaluearray);
-
-    /**
-     * Deletes an item from the cache store.
-     *
-     * @param string $key The key to delete.
-     * @return bool Returns true if the operation was a success, false otherwise.
-     */
-    public function delete($key);
-
-    /**
-     * Deletes several keys from the cache in a single action.
-     *
-     * @param array $keys The keys to delete
-     * @return int The number of items successfully deleted.
-     */
-    public function delete_many(array $keys);
-
-    /**
-     * Purges the cache deleting all items within it.
-     *
-     * @return boolean True on success. False otherwise.
-     */
-    public function purge();
-
-    /**
-     * Performs any necessary clean up when the store instance is being deleted.
-     */
-    public function cleanup();
-
-    /**
-     * Generates an instance of the cache store that can be used for testing.
-     *
-     * Returns an instance of the cache store, or false if one cannot be created.
-     *
-     * @param cache_definition $definition
-     * @return cache_store|false
-     */
-    public static function initialise_test_instance(cache_definition $definition);
-}
-
 /**
  * Cache store feature: locking
  *
@@ -559,6 +338,35 @@ interface cache_is_key_aware {
     public function has_all(array $keys);
 }
 
+/**
+ * Cache store feature: configurable.
+ *
+ * This feature should be implemented by all cache stores that are configurable when adding an instance.
+ * It requires the implementation of methods required to convert form data into the a configuration array for the
+ * store instance, and then the reverse converting configuration data into an array that can be used to set the
+ * data for the edit form.
+ *
+ * Can be implemented by classes already implementing cache_store.
+ */
+interface cache_is_configurable {
+
+    /**
+     * Given the data from the add instance form this function creates a configuration array.
+     *
+     * @param stdClass $data
+     * @return array
+     */
+    public static function config_get_configuration_array($data);
+
+    /**
+     * Allows the cache store to set its data against the edit form before it is shown to the user.
+     *
+     * @param moodleform $editform
+     * @param array $config
+     */
+    public static function config_set_edit_form_data(moodleform $editform, array $config);
+}
+
 /**
  * Cache Data Source.
  *
index 433e9b7..74014c4 100644 (file)
@@ -638,19 +638,22 @@ class cache implements cache_loader {
     public function has($key, $tryloadifpossible = false) {
         $parsedkey = $this->parse_key($key);
         if ($this->is_in_persist_cache($parsedkey)) {
+            // Hoorah, that was easy. It exists in the persist cache so we definitely have it.
             return true;
         }
-        if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
-            if ($this->store_supports_key_awareness() && !$this->store->has($parsedkey)) {
-                return false;
-            }
+        if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
+            // The data has a TTL and the store doesn't support it natively.
+            // We must fetch the data and expect a ttl wrapper.
             $data = $this->store->get($parsedkey);
-            if (!$this->store_supports_native_ttl()) {
-                $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
-            } else {
-                $has = ($data !== false);
-            }
+            $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
+        } else if (!$this->store_supports_key_awareness()) {
+            // The store doesn't support key awareness, get the data and check it manually... puke.
+            // Either no TTL is set of the store supports its handling natively.
+            $data = $this->store->get($parsedkey);
+            $has = ($data !== false);
         } else {
+            // The store supports key awareness, this is easy!
+            // Either no TTL is set of the store supports its handling natively.
             $has = $this->store->has($parsedkey);
         }
         if (!$has && $tryloadifpossible) {
diff --git a/cache/classes/store.php b/cache/classes/store.php
new file mode 100644 (file)
index 0000000..75cd8d8
--- /dev/null
@@ -0,0 +1,266 @@
+<?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/>.
+
+/**
+ * Cache store - base class
+ *
+ * This file is part of Moodle's cache API, affectionately called MUC.
+ * It contains the components that are required in order to use caching.
+ *
+ * @package    core
+ * @category   cache
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Cache store interface.
+ *
+ * This interface defines the static methods that must be implemented by every cache store plugin.
+ * To ensure plugins implement this class the abstract cache_store class implements this interface.
+ *
+ * @package    core
+ * @category   cache
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface cache_store_interface {
+    /**
+     * Static method to check if the store requirements are met.
+     *
+     * @return bool True if the stores software/hardware requirements have been met and it can be used. False otherwise.
+     */
+    public static function are_requirements_met();
+
+    /**
+     * Static method to check if a store is usable with the given mode.
+     *
+     * @param int $mode One of cache_store::MODE_*
+     */
+    public static function is_supported_mode($mode);
+
+    /**
+     * Returns the supported features as a binary flag.
+     *
+     * @param array $configuration The configuration of a store to consider specifically.
+     * @return int The supported features.
+     */
+    public static function get_supported_features(array $configuration = array());
+
+    /**
+     * Returns the supported modes as a binary flag.
+     *
+     * @param array $configuration The configuration of a store to consider specifically.
+     * @return int The supported modes.
+     */
+    public static function get_supported_modes(array $configuration = array());
+
+    /**
+     * Generates an instance of the cache store that can be used for testing.
+     *
+     * Returns an instance of the cache store, or false if one cannot be created.
+     *
+     * @param cache_definition $definition
+     * @return cache_store|false
+     */
+    public static function initialise_test_instance(cache_definition $definition);
+}
+
+/**
+ * Abstract cache store class.
+ *
+ * All cache store plugins must extend this base class.
+ * It lays down the foundation for what is required of a cache store plugin.
+ *
+ * @since 2.4
+ * @package    core
+ * @category   cache
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class cache_store implements cache_store_interface {
+
+    // Constants for features a cache store can support
+
+    /**
+     * Supports multi-part keys
+     */
+    const SUPPORTS_MULTIPLE_IDENTIFIERS = 1;
+    /**
+     * Ensures data remains in the cache once set.
+     */
+    const SUPPORTS_DATA_GUARANTEE = 2;
+    /**
+     * Supports a native ttl system.
+     */
+    const SUPPORTS_NATIVE_TTL = 4;
+
+    // Constants for the modes of a cache store
+
+    /**
+     * Application caches. These are shared caches.
+     */
+    const MODE_APPLICATION = 1;
+    /**
+     * Session caches. Just access to the PHP session.
+     */
+    const MODE_SESSION = 2;
+    /**
+     * Request caches. Static caches really.
+     */
+    const MODE_REQUEST = 4;
+
+    /**
+     * Constructs an instance of the cache store.
+     *
+     * This method should not create connections or perform and processing, it should be used
+     *
+     * @param string $name The name of the cache store
+     * @param array $configuration The configuration for this store instance.
+     */
+    abstract public function __construct($name, array $configuration = array());
+
+    /**
+     * Returns the name of this store instance.
+     * @return string
+     */
+    abstract public function my_name();
+
+    /**
+     * Initialises a new instance of the cache store given the definition the instance is to be used for.
+     *
+     * This function should prepare any given connections etc.
+     *
+     * @param cache_definition $definition
+     */
+    abstract public function initialise(cache_definition $definition);
+
+    /**
+     * Returns true if this cache store instance has been initialised.
+     * @return bool
+     */
+    abstract public function is_initialised();
+
+    /**
+     * Returns true if this cache store instance is ready to use.
+     * @return bool
+     */
+    abstract public function is_ready();
+
+    /**
+     * Retrieves an item from the cache store given its key.
+     *
+     * @param string $key The key to retrieve
+     * @return mixed The data that was associated with the key, or false if the key did not exist.
+     */
+    abstract public function get($key);
+
+    /**
+     * Retrieves several items from the cache store in a single transaction.
+     *
+     * If not all of the items are available in the cache then the data value for those that are missing will be set to false.
+     *
+     * @param array $keys The array of keys to retrieve
+     * @return array An array of items from the cache. There will be an item for each key, those that were not in the store will
+     *      be set to false.
+     */
+    abstract public function get_many($keys);
+
+    /**
+     * Sets an item in the cache given its key and data value.
+     *
+     * @param string $key The key to use.
+     * @param mixed $data The data to set.
+     * @return bool True if the operation was a success false otherwise.
+     */
+    abstract public function set($key, $data);
+
+    /**
+     * Sets many items in the cache in a single transaction.
+     *
+     * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
+     *      keys, 'key' and 'value'.
+     * @return int The number of items successfully set. It is up to the developer to check this matches the number of items
+     *      sent ... if they care that is.
+     */
+    abstract public function set_many(array $keyvaluearray);
+
+    /**
+     * Deletes an item from the cache store.
+     *
+     * @param string $key The key to delete.
+     * @return bool Returns true if the operation was a success, false otherwise.
+     */
+    abstract public function delete($key);
+
+    /**
+     * Deletes several keys from the cache in a single action.
+     *
+     * @param array $keys The keys to delete
+     * @return int The number of items successfully deleted.
+     */
+    abstract public function delete_many(array $keys);
+
+    /**
+     * Purges the cache deleting all items within it.
+     *
+     * @return boolean True on success. False otherwise.
+     */
+    abstract public function purge();
+
+    /**
+     * Performs any necessary clean up when the store instance is being deleted.
+     */
+    abstract public function cleanup();
+
+    /**
+     * Returns true if the user can add an instance of the store plugin.
+     *
+     * @return bool
+     */
+    public static function can_add_instance() {
+        return true;
+    }
+
+    /**
+     * Returns true if the store instance guarantees data.
+     *
+     * @return bool
+     */
+    public function supports_data_guarantee() {
+        return $this::get_supported_features() & self::SUPPORTS_DATA_GUARANTEE;
+    }
+
+    /**
+     * Returns true if the store instance supports multiple identifiers.
+     *
+     * @return bool
+     */
+    public function supports_multiple_identifiers() {
+        return $this::get_supported_features() & self::SUPPORTS_MULTIPLE_IDENTIFIERS;
+    }
+
+    /**
+     * Returns true if the store instance supports native ttl.
+     *
+     * @return bool
+     */
+    public function supports_native_ttl() {
+        return $this::supports_data_guarantee() & self::SUPPORTS_NATIVE_TTL;
+    }
+}
index b5e23ad..001cd43 100644 (file)
@@ -36,6 +36,7 @@ require_once($CFG->dirroot.'/cache/classes/config.php');
 require_once($CFG->dirroot.'/cache/classes/helper.php');
 require_once($CFG->dirroot.'/cache/classes/factory.php');
 require_once($CFG->dirroot.'/cache/classes/loaders.php');
+require_once($CFG->dirroot.'/cache/classes/store.php');
 require_once($CFG->dirroot.'/cache/classes/definition.php');
 
 /**
index e8e43a3..2b8087c 100644 (file)
@@ -137,7 +137,7 @@ class cache_config_writer extends cache_config {
             }
         }
         $reflection = new ReflectionClass($class);
-        if (!$reflection->implementsInterface('cache_store')) {
+        if (!$reflection->isSubclassOf('cache_store')) {
             throw new cache_exception('Invalid cache plugin specified. The plugin does not extend the required class.');
         }
         if (!$class::are_requirements_met()) {
@@ -790,7 +790,7 @@ abstract class cache_administration_helper extends cache_helper {
         // If it has a customised add instance form then it is going to want to.
         $storeclass = 'cachestore_'.$plugin;
         $storedata = $stores[$store];
-        if (array_key_exists('configuration', $storedata) && method_exists($storeclass, 'config_set_edit_form_data')) {
+        if (array_key_exists('configuration', $storedata) && array_key_exists('cache_is_configurable', class_implements($storeclass))) {
             $storeclass::config_set_edit_form_data($editform, $storedata['configuration']);
         }
         return $editform;
@@ -848,12 +848,11 @@ abstract class cache_administration_helper extends cache_helper {
         }
         require_once($file);
         $class = 'cachestore_'.$data->plugin;
-        $method = 'config_get_configuration_array';
         if (!class_exists($class)) {
             throw new coding_exception('Invalid cache plugin provided.');
         }
-        if (method_exists($class, $method)) {
-            return call_user_func(array($class, $method), $data);
+        if (array_key_exists('cache_is_configurable', class_implements($class))) {
+            return $class::config_get_configuration_array($data);
         }
         return array();
     }
index 19fdfb6..1d9bb42 100644 (file)
@@ -37,7 +37,7 @@
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_file implements cache_store, cache_is_key_aware {
+class cachestore_file extends cache_store implements cache_is_key_aware, cache_is_configurable  {
 
     /**
      * The name of the store.
@@ -204,33 +204,6 @@ class cachestore_file implements cache_store, cache_is_key_aware {
         return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION);
     }
 
-    /**
-     * Returns true if the store instance supports multiple identifiers.
-     *
-     * @return bool
-     */
-    public function supports_multiple_identifiers() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance guarantees data.
-     *
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return true;
-    }
-
-    /**
-     * Returns true if the store instance supports native ttl.
-     *
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return true;
-    }
-
     /**
      * Initialises the cache.
      *
@@ -593,15 +566,6 @@ class cachestore_file implements cache_store, cache_is_key_aware {
         return true;
     }
 
-    /**
-     * Returns true if the user can add an instance of the store plugin.
-     *
-     * @return bool
-     */
-    public static function can_add_instance() {
-        return true;
-    }
-
     /**
      * Performs any necessary clean up when the store instance is being deleted.
      *
index d62fac7..80c5a33 100644 (file)
@@ -37,7 +37,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_memcache implements cache_store {
+class cachestore_memcache extends cache_store implements cache_is_configurable {
 
     /**
      * The name of the store
@@ -173,33 +173,6 @@ class cachestore_memcache implements cache_store {
         return self::SUPPORTS_NATIVE_TTL;
     }
 
-    /**
-     * Returns true if the store instance supports multiple identifiers.
-     *
-     * @return bool
-     */
-    public function supports_multiple_identifiers() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance guarantees data.
-     *
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance supports native ttl.
-     *
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return true;
-    }
-
     /**
      * Returns the supported modes as a combined int.
      *
@@ -343,15 +316,6 @@ class cachestore_memcache implements cache_store {
         $editform->set_data($data);
     }
 
-    /**
-     * Returns true if the user can add an instance of the store plugin.
-     *
-     * @return bool
-     */
-    public static function can_add_instance() {
-        return true;
-    }
-
     /**
      * Performs any necessary clean up when the store instance is being deleted.
      */
index 419e640..44a11b9 100644 (file)
@@ -43,8 +43,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_memcached implements cache_store {
-
+class cachestore_memcached extends cache_store implements cache_is_configurable {
     /**
      * The name of the store
      * @var store
@@ -199,33 +198,6 @@ class cachestore_memcached implements cache_store {
         return self::SUPPORTS_NATIVE_TTL;
     }
 
-    /**
-     * Returns true if the store instance supports multiple identifiers.
-     *
-     * @return bool
-     */
-    public function supports_multiple_identifiers() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance guarantees data.
-     *
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance supports native ttl.
-     *
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return true;
-    }
-
     /**
      * Returns the supported modes as a combined int.
      *
@@ -426,15 +398,6 @@ class cachestore_memcached implements cache_store {
         $editform->set_data($data);
     }
 
-    /**
-     * Returns true if the user can add an instance of the store plugin.
-     *
-     * @return bool
-     */
-    public static function can_add_instance() {
-        return true;
-    }
-
     /**
      * Performs any necessary clean up when the store instance is being deleted.
      */
index 1931bdb..4ee7752 100644 (file)
@@ -37,7 +37,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_mongodb implements cache_store {
+class cachestore_mongodb extends cache_store implements cache_is_configurable {
 
     /**
      * The name of the store
@@ -141,14 +141,6 @@ class cachestore_mongodb implements cache_store {
         return class_exists('Mongo');
     }
 
-    /**
-     * Returns true if the user can add an instance of this store.
-     * @return bool
-     */
-    public static function can_add_instance() {
-        return true;
-    }
-
     /**
      * Returns the supported features.
      * @param array $configuration
@@ -218,14 +210,6 @@ class cachestore_mongodb implements cache_store {
         return ($mode == self::MODE_APPLICATION || $mode == self::MODE_SESSION);
     }
 
-    /**
-     * Returns true if this store guarantees its data is there once set.
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return true;
-    }
-
     /**
      * Returns true if this store is making use of multiple identifiers.
      * @return bool
@@ -234,14 +218,6 @@ class cachestore_mongodb implements cache_store {
         return $this->extendedmode;
     }
 
-    /**
-     * Returns true if this store supports native TTL.
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return false;
-    }
-
     /**
      * Retrieves an item from the cache store given its key.
      *
index ba82c17..a652f9c 100644 (file)
@@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_session extends session_data_store implements cache_store, cache_is_key_aware {
+class cachestore_session extends session_data_store implements cache_is_key_aware {
 
     /**
      * The name of the store
@@ -113,33 +113,6 @@ class cachestore_session extends session_data_store implements cache_store, cach
         return ($mode === self::MODE_SESSION);
     }
 
-    /**
-     * Returns true if the store instance guarantees data.
-     *
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return true;
-    }
-
-    /**
-     * Returns true if the store instance supports multiple identifiers.
-     *
-     * @return bool
-     */
-    public function supports_multiple_identifiers() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance supports native ttl.
-     *
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return true;
-    }
-
     /**
      * Initialises the cache.
      *
@@ -378,7 +351,7 @@ class cachestore_session extends session_data_store implements cache_store, cach
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-abstract class session_data_store {
+abstract class session_data_store extends cache_store {
 
     /**
      * Used for the actual storage.
index 7a7bd5a..03d455a 100644 (file)
@@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class cachestore_static extends static_data_store implements cache_store, cache_is_key_aware {
+class cachestore_static extends static_data_store implements cache_is_key_aware {
 
     /**
      * The name of the store
@@ -113,33 +113,6 @@ class cachestore_static extends static_data_store implements cache_store, cache_
         return ($mode === self::MODE_REQUEST);
     }
 
-    /**
-     * Returns true if the store instance guarantees data.
-     *
-     * @return bool
-     */
-    public function supports_data_guarantee() {
-        return true;
-    }
-
-    /**
-     * Returns true if the store instance supports multiple identifiers.
-     *
-     * @return bool
-     */
-    public function supports_multiple_identifiers() {
-        return false;
-    }
-
-    /**
-     * Returns true if the store instance supports native ttl.
-     *
-     * @return bool
-     */
-    public function supports_native_ttl() {
-        return true;
-    }
-
     /**
      * Initialises the cache.
      *
@@ -378,7 +351,7 @@ class cachestore_static extends static_data_store implements cache_store, cache_
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-abstract class static_data_store {
+abstract class static_data_store extends cache_store {
 
     /**
      * An array for storage.
index 02f541b..8401de3 100644 (file)
@@ -2112,24 +2112,22 @@ class calendar_event {
             if ($usingeditor) {
                 switch ($this->properties->eventtype) {
                     case 'user':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->courseid = 0;
+                        $this->properties->course = 0;
                         $this->properties->groupid = 0;
                         $this->properties->userid = $USER->id;
                         break;
                     case 'site':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->courseid = SITEID;
+                        $this->properties->course = SITEID;
                         $this->properties->groupid = 0;
                         $this->properties->userid = $USER->id;
                         break;
                     case 'course':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->groupid = 0;
                         $this->properties->userid = $USER->id;
                         break;
                     case 'group':
-                        $this->editorcontext = $this->properties->context;
                         $this->properties->userid = $USER->id;
                         break;
                     default:
@@ -2139,6 +2137,13 @@ class calendar_event {
                         break;
                 }
 
+                // If we are actually using the editor, we recalculate the context because some default values
+                // were set when calculate_context() was called from the constructor.
+                if ($usingeditor) {
+                    $this->properties->context = $this->calculate_context($this->properties);
+                    $this->editorcontext = $this->properties->context;
+                }
+
                 $editor = $this->properties->description;
                 $this->properties->format = $this->properties->description['format'];
                 $this->properties->description = $this->properties->description['text'];
@@ -2157,7 +2162,6 @@ class calendar_event {
                                                 $this->editoroptions,
                                                 $editor['text'],
                                                 $this->editoroptions['forcehttps']);
-
                 $DB->set_field('event', 'description', $this->properties->description, array('id'=>$this->properties->id));
             }
 
@@ -2692,13 +2696,13 @@ function calendar_get_eventtype_choices($courseid) {
     calendar_get_allowed_types($allowed, $courseid);
 
     if ($allowed->user) {
-        $choices[0] = get_string('userevents', 'calendar');
+        $choices['user'] = get_string('userevents', 'calendar');
     }
     if ($allowed->site) {
-        $choices[SITEID] = get_string('globalevents', 'calendar');
+        $choices['site'] = get_string('siteevents', 'calendar');
     }
     if (!empty($allowed->courses)) {
-        $choices[$courseid] = get_string('courseevents', 'calendar');
+        $choices['course'] = get_string('courseevents', 'calendar');
     }
     if (!empty($allowed->groups) and is_array($allowed->groups)) {
         $choices['group'] = get_string('group');
@@ -2714,11 +2718,15 @@ function calendar_get_eventtype_choices($courseid) {
  * @return int The insert ID, if any.
  */
 function calendar_add_subscription($sub) {
-    global $DB, $USER;
+    global $DB, $USER, $SITE;
 
-    $sub->courseid = $sub->eventtype;
-    if ($sub->eventtype == 'group') {
+    if ($sub->eventtype === 'site') {
+        $sub->courseid = $SITE->id;
+    } else if ($sub->eventtype === 'group' || $sub->eventtype === 'course') {
         $sub->courseid = $sub->course;
+    } else {
+        // User events.
+        $sub->courseid = 0;
     }
     $sub->userid = $USER->id;
 
@@ -2794,10 +2802,10 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid = null)
         $eventrecord->userid = $sub->userid;
         $eventrecord->groupid = $sub->groupid;
         $eventrecord->courseid = $sub->courseid;
+        $eventrecord->eventtype = $sub->eventtype;
     } else {
-        $eventrecord->userid = $USER->id;
-        $eventrecord->groupid = 0; // TODO: ???
-        $eventrecord->courseid = $courseid;
+        // We should never do anything with an event without a subscription reference.
+        return 0;
     }
 
     if ($updaterecord = $DB->get_record('event', array('uuid' => $eventrecord->uuid))) {
index fcd7979..a5ac01f 100644 (file)
@@ -61,12 +61,6 @@ class calendar_addsubscription_form extends moodleform {
         // Cannot set as PARAM_URL since we need to allow webcal:// protocol.
         $mform->setType('url', PARAM_RAW);
 
-        // Import file
-        $mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar'));
-
-        $mform->disabledIf('url',  'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE);
-        $mform->disabledIf('importfile', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_URL);
-
         // Poll interval
         $choices = calendar_get_pollinterval_choices();
         $mform->addElement('select', 'pollinterval', get_string('pollinterval', 'calendar'), $choices);
@@ -74,11 +68,19 @@ class calendar_addsubscription_form extends moodleform {
         $mform->addHelpButton('pollinterval', 'pollinterval', 'calendar');
         $mform->setType('pollinterval', PARAM_INT);
 
+        // Import file
+        $mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar'), null, array('accepted_types' => '.ics'));
+
+        // Disable appropriate elements depending on import from value.
+        $mform->disabledIf('pollinterval', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE);
+        $mform->disabledIf('url',  'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE);
+        $mform->disabledIf('importfile', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_URL);
+
         // Eventtype: 0 = user, 1 = global, anything else = course ID.
         list($choices, $groups) = calendar_get_eventtype_choices($courseid);
         $mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $choices);
         $mform->addRule('eventtype', get_string('required'), 'required');
-        $mform->setType('eventtype', PARAM_INT);
+        $mform->setType('eventtype', PARAM_ALPHA);
 
         if (!empty($groups) and is_array($groups)) {
             $groupoptions = array();
index 842ecc4..5a18073 100644 (file)
@@ -800,6 +800,7 @@ class core_calendar_renderer extends plugin_renderer_base {
                 if ($k == $subscription->pollinterval) {
                     $attributes['selected'] = 'selected';
                 }
+                $attributes['value'] = $k;
                 $html .= html_writer::tag('option', $v, $attributes);
             }
             $html .= html_writer::end_tag('select');
diff --git a/composer.json b/composer.json
new file mode 100644 (file)
index 0000000..94bf886
--- /dev/null
@@ -0,0 +1,6 @@
+{
+    "require": {
+        "phpunit/phpunit": "3.7.*",
+        "phpunit/dbUnit": "1.2.*"
+    }
+}
\ No newline at end of file
index 2e38635..cb1aa0d 100644 (file)
@@ -246,6 +246,11 @@ $CFG->admin = 'admin';
 // logs in. The site front page will always show the same (logged-out) view.
 //     $CFG->disablemycourses = true;
 //
+// By default all user sessions should be using locking, uncomment
+// the following setting to prevent locking for guests and not-logged-in
+// accounts. This may improve performance significantly.
+//     $CFG->sessionlockloggedinonly = 1;
+//
 // If this setting is set to true, then Moodle will track the IP of the
 // current user to make sure it hasn't changed during a session.  This
 // will prevent the possibility of sessions being hijacked via XSS, but it
index 1856c62..befde23 100644 (file)
@@ -245,7 +245,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
                 $strmoveup = get_string('moveup');
 
                 $controls[] = html_writer::link($url,
-                    html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/up'),
+                    html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/up'),
                     'class' => 'icon up', 'alt' => $strmoveup)),
                     array('title' => $strmoveup, 'class' => 'moveup'));
             }
@@ -257,7 +257,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
                 $strmovedown =  get_string('movedown');
 
                 $controls[] = html_writer::link($url,
-                    html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/down'),
+                    html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/down'),
                     'class' => 'icon down', 'alt' => $strmovedown)),
                     array('title' => $strmovedown, 'class' => 'movedown'));
             }
index 0bb518d..60e9a26 100644 (file)
@@ -74,4 +74,28 @@ class format_scorm extends format_base {
             BLOCK_POS_RIGHT => array('news_items', 'recent_activity', 'calendar_upcoming')
         );
     }
+
+    /**
+     * Allows course format to execute code on moodle_page::set_course()
+     *
+     * If user is on course view page and there is no scorm module added to the course
+     * and the user has 'moodle/course:update' capability, redirect to create module
+     * form. This function is executed before the output starts
+     *
+     * @param moodle_page $page instance of page calling set_course
+     */
+    public function page_set_course(moodle_page $page) {
+        global $PAGE;
+        if ($PAGE == $page && $page->has_set_url() &&
+                $page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+            $modinfo = get_fast_modinfo($this->courseid);
+            if (empty($modinfo->instances['scorm'])
+                    && has_capability('moodle/course:update', context_course::instance($this->courseid))) {
+                // Redirect to create a new activity
+                $url = new moodle_url('/course/modedit.php',
+                        array('course' => $this->courseid, 'section' => 0, 'add' => 'scorm'));
+                redirect($url);
+            }
+        }
+    }
 }
index 7d2ed99..9f5fce1 100644 (file)
@@ -1427,15 +1427,16 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             $modcontext = context_module::instance($mod->id);
             $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
             $accessiblebutdim = false;
+            $conditionalhidden = false;
             if ($canviewhidden) {
                 $accessiblebutdim = !$mod->visible;
                 if (!empty($CFG->enableavailability)) {
-                    $accessiblebutdim = $accessiblebutdim ||
-                        $mod->availablefrom > time() ||
+                    $conditionalhidden = $mod->availablefrom > time() ||
                         ($mod->availableuntil && $mod->availableuntil < time()) ||
                         count($mod->conditionsgrade) > 0 ||
                         count($mod->conditionscompletion) > 0;
                 }
+                $accessiblebutdim = $conditionalhidden || $accessiblebutdim;
             }
 
             $liclasses = array();
@@ -1491,8 +1492,12 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 $linkclasses = '';
                 $textclasses = '';
                 if ($accessiblebutdim) {
-                    $linkclasses .= ' dimmed conditionalhidden';
-                    $textclasses .= ' dimmed_text conditionalhidden';
+                    $linkclasses .= ' dimmed';
+                    $textclasses .= ' dimmed_text';
+                    if ($conditionalhidden) {
+                        $linkclasses .= ' conditionalhidden';
+                        $textclasses .= ' conditionalhidden';
+                    }
                     $accesstext = '<span class="accesshide">'.
                         get_string('hiddenfromstudents').': </span>';
                 } else {
@@ -2759,7 +2764,7 @@ function course_create_sections_if_missing($courseorid, $sections) {
  * Updates both tables {course_sections} and {course_modules}
  *
  * @param int|stdClass $courseorid course id or course object
- * @param int $modid id of the module already existing in course_modules table
+ * @param int $cmid id of the module already existing in course_modules table
  * @param int $sectionnum relative number of the section (field course_sections.section)
  *     If section does not exist it will be created
  * @param int|stdClass $beforemod id or object with field id corresponding to the module
@@ -2767,25 +2772,31 @@ function course_create_sections_if_missing($courseorid, $sections) {
  *     end of the section
  * @return int The course_sections ID where the module is inserted
  */
-function course_add_cm_to_section($courseorid, $modid, $sectionnum, $beforemod = null) {
+function course_add_cm_to_section($courseorid, $cmid, $sectionnum, $beforemod = null) {
     global $DB, $COURSE;
     if (is_object($beforemod)) {
         $beforemod = $beforemod->id;
     }
+    if (is_object($courseorid)) {
+        $courseid = $courseorid->id;
+    } else {
+        $courseid = $courseorid;
+    }
     course_create_sections_if_missing($courseorid, $sectionnum);
-    $section = get_fast_modinfo($courseorid)->get_section_info($sectionnum);
+    // Do not try to use modinfo here, there is no guarantee it is valid!
+    $section = $DB->get_record('course_sections', array('course'=>$courseid, 'section'=>$sectionnum), '*', MUST_EXIST);
     $modarray = explode(",", trim($section->sequence));
     if (empty($section->sequence)) {
-        $newsequence = "$modid";
+        $newsequence = "$cmid";
     } else if ($beforemod && ($key = array_keys($modarray, $beforemod))) {
-        $insertarray = array($modid, $beforemod);
+        $insertarray = array($cmid, $beforemod);
         array_splice($modarray, $key[0], 1, $insertarray);
         $newsequence = implode(",", $modarray);
     } else {
-        $newsequence = "$section->sequence,$modid";
+        $newsequence = "$section->sequence,$cmid";
     }
     $DB->set_field("course_sections", "sequence", $newsequence, array("id" => $section->id));
-    $DB->set_field('course_modules', 'section', $section->id, array('id' => $modid));
+    $DB->set_field('course_modules', 'section', $section->id, array('id' => $cmid));
     if (is_object($courseorid)) {
         rebuild_course_cache($courseorid->id, true);
     } else {
index eca0983..5c8c896 100644 (file)
@@ -479,7 +479,6 @@ YUI.add('moodle-course-toolboxes', function(Y) {
                 .setAttribute('href', newlink)
                 .setAttribute('title', left_string);
             anchor.appendChild(newicon);
-            anchor.on('click', this.move_left, this);
             moveright.insert(anchor, 'before');
         },
         /**
index 3c839d0..7e01825 100644 (file)
@@ -151,7 +151,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
             if(! @unlink($filename)) {
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = get_admin();
                 $eventdata->userto            = get_admin();
@@ -169,7 +169,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
                 // Send mail to admin
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = get_admin();
                 $eventdata->userto            = get_admin();
@@ -280,7 +280,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
 
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = $teacher;
                 $eventdata->userto            = $user;
@@ -303,7 +303,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
 
                     $eventdata = new stdClass();
                     $eventdata->modulename        = 'moodle';
-                    $eventdata->component         = 'course';
+                    $eventdata->component         = 'enrol_flatfile';
                     $eventdata->name              = 'flatfile_enrolment';
                     $eventdata->userfrom          = $user;
                     $eventdata->userto            = $teacher;
index aa71854..05e43f5 100644 (file)
@@ -512,7 +512,7 @@ class course_enrolment_table extends html_table implements renderable {
                     } else {
                         $link = html_writer::link(new moodle_url($url, array(self::SORTVAR=>$n)), $fields[$name][$n]);
                         if ($this->sort == $n) {
-                            $link .= ' '.html_writer::link(new moodle_url($url, array(self::SORTVAR=>$n, self::SORTDIRECTIONVAR=>$this->get_field_sort_direction($n))), $this->get_direction_icon($output, $n));
+                            $link .= html_writer::link(new moodle_url($url, array(self::SORTVAR=>$n, self::SORTDIRECTIONVAR=>$this->get_field_sort_direction($n))), $this->get_direction_icon($output, $n));
                         }
                         $bits[] = html_writer::tag('span', $link, array('class'=>'subheading_'.$n));
 
@@ -525,7 +525,7 @@ class course_enrolment_table extends html_table implements renderable {
                 } else {
                     $newlabel  = html_writer::link(new moodle_url($url, array(self::SORTVAR=>$name)), $fields[$name]);
                     if ($this->sort == $name) {
-                        $newlabel .= ' '.html_writer::link(new moodle_url($url, array(self::SORTVAR=>$name, self::SORTDIRECTIONVAR=>$this->get_field_sort_direction($name))), $this->get_direction_icon($output, $name));
+                        $newlabel .= html_writer::link(new moodle_url($url, array(self::SORTVAR=>$name, self::SORTDIRECTIONVAR=>$this->get_field_sort_direction($name))), $this->get_direction_icon($output, $name));
                     }
                 }
             }
@@ -633,9 +633,11 @@ class course_enrolment_table extends html_table implements renderable {
             $direction = $this->sortdirection;
         }
         if ($direction === 'ASC') {
-            return html_writer::empty_tag('img', array('alt'=>'', 'src'=>$output->pix_url('t/down')));
+            return html_writer::empty_tag('img', array('alt' => '', 'class' => 'iconsort',
+                'src' => $output->pix_url('t/sort_asc')));
         } else {
-            return html_writer::empty_tag('img', array('alt'=>'', 'src'=>$output->pix_url('t/up')));
+            return html_writer::empty_tag('img', array('alt' => '', 'class' => 'iconsort',
+                'src' => $output->pix_url('t/sort_desc')));
         }
     }
 
index 7896f39..36784b3 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-require_once '../../../config.php';
-require_once $CFG->libdir.'/gradelib.php';
-require_once $CFG->dirroot.'/grade/lib.php';
-require_once '../grade_import_form.php';
-require_once '../lib.php';
+require_once("../../../config.php");
+require_once($CFG->libdir.'/gradelib.php');
+require_once($CFG->dirroot.'/grade/lib.php');
+require_once($CFG->dirroot. '/grade/import/grade_import_form.php');
+require_once($CFG->dirroot.'/grade/import/lib.php');
+require_once($CFG->libdir . '/csvlib.class.php');
 
 $id            = required_param('id', PARAM_INT); // course id
 $separator     = optional_param('separator', '', PARAM_ALPHA);
 $verbosescales = optional_param('verbosescales', 1, PARAM_BOOL);
+$iid           = optional_param('iid', null, PARAM_INT);
+$importcode    = optional_param('importcode', '', PARAM_FILE);
 
 $url = new moodle_url('/grade/import/csv/index.php', array('id'=>$id));
 if ($separator !== '') {
@@ -34,8 +37,6 @@ if ($verbosescales !== 1) {
 }
 $PAGE->set_url($url);
 
-define('GRADE_CSV_LINE_LENGTH', 4096);
-
 if (!$course = $DB->get_record('course', array('id'=>$id))) {
     print_error('nocourseid');
 }
@@ -48,33 +49,14 @@ require_capability('gradeimport/csv:view', $context);
 $separatemode = (groups_get_course_groupmode($COURSE) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context));
 $currentgroup = groups_get_course_group($course);
 
-// sort out delimiter
-if (isset($CFG->CSV_DELIMITER)) {
-    $csv_delimiter = $CFG->CSV_DELIMITER;
-
-    if (isset($CFG->CSV_ENCODE)) {
-        $csv_encode = '/\&\#' . $CFG->CSV_ENCODE . '/';
-    }
-} else if ($separator == 'tab') {
-    $csv_delimiter = "\t";
-    $csv_encode = "";
-} else {
-    $csv_delimiter = ",";
-    $csv_encode = '/\&\#44/';
-}
-
 print_grade_page_head($course->id, 'import', 'csv', get_string('importcsv', 'grades'));
 
-// set up import form
-$mform = new grade_import_form(null, array('includeseparator'=>!isset($CFG->CSV_DELIMITER), 'verbosescales'=>true));
-
-// set up grade import mapping form
-$header = '';
+// Set up the grade import mapping form.
 $gradeitems = array();
 if ($id) {
     if ($grade_items = grade_item::fetch_all(array('courseid'=>$id))) {
         foreach ($grade_items as $grade_item) {
-            // skip course type and category type
+            // Skip course type and category type.
             if ($grade_item->itemtype == 'course' || $grade_item->itemtype == 'category') {
                 continue;
             }
@@ -90,96 +72,84 @@ if ($id) {
     }
 }
 
-if ($importcode = optional_param('importcode', '', PARAM_FILE)) {
-    $filename = $CFG->tempdir.'/gradeimport/cvs/'.$USER->id.'/'.$importcode;
-    $fp = fopen($filename, "r");
-    $headers = fgets($fp, GRADE_CSV_LINE_LENGTH);
-    $header = explode($csv_delimiter, $headers);
-    fclose($fp);
-}
+// Set up the import form.
+$mform = new grade_import_form(null, array('includeseparator'=>true, 'verbosescales'=>true));
 
-$mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header));
+// If the csv file hasn't been imported yet then look for a form submission or
+// show the initial submission form.
+if (!$iid) {
+    // If the import form has been submitted.
+    if ($formdata = $mform->get_data()) {
 
-// if import form is submitted
-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);
+        raise_memory_limit(MEMORY_EXTRA);
 
-    // 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);
-    raise_memory_limit(MEMORY_EXTRA);
+        // Use current (non-conflicting) time stamp.
+        $importcode = get_new_importcode();
 
-    // use current (non-conflicting) time stamp
-    $importcode = get_new_importcode();
-    $filename = make_temp_directory('gradeimport/cvs/'.$USER->id);
-    $filename = $filename.'/'.$importcode;
-
-    $text = $mform->get_file_content('userfile');
-    // trim utf-8 bom
-    /// normalize line endings and do the encoding conversion
-    $text = textlib::convert($text, $formdata->encoding);
-    $text = textlib::trim_utf8_bom($text);
-    // Fix mac/dos newlines
-    $text = preg_replace('!\r\n?!',"\n",$text);
-    $fp = fopen($filename, "w");
-    fwrite($fp,$text);
-    fclose($fp);
-
-    if (!$fp = fopen($filename, "r")) {
-        print_error('cannotopenfile');
-    }
+        $text = $mform->get_file_content('userfile');
+        $iid = csv_import_reader::get_new_iid('grade');
+        $csvimport = new csv_import_reader($iid, 'grade');
 
-    // --- get header (field names) ---
-    $header = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH));
+        $csvimport->load_csv_content($text, $formdata->encoding, $separator);
 
-    // print some preview
-    $numlines = 0; // 0 preview lines displayed
+        // --- get header (field names) ---
+        $header = $csvimport->get_columns();
 
-    echo $OUTPUT->heading(get_string('importpreview', 'grades'));
-    echo '<table>';
-    echo '<tr>';
-    foreach ($header as $h) {
-        $h = clean_param($h, PARAM_RAW);
-        echo '<th>'.$h.'</th>';
-    }
-    echo '</tr>';
-    while (!feof ($fp) && $numlines <= $formdata->previewrows) {
-        $lines = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH));
-        echo '<tr>';
-        foreach ($lines as $line) {
-            echo '<td>'.$line.'</td>';
-        }
-        $numlines ++;
-        echo '</tr>';
-    }
-    echo '</table>';
+        // Print a preview of the data.
+        $numlines = 0; // 0 lines previewed so far.
 
-    // display the mapping form with header info processed
-    $mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header));
-    $mform2->set_data(array('importcode'=>$importcode, 'id'=>$id, 'verbosescales'=>$verbosescales, 'separator'=>$separator));
-    $mform2->display();
+        echo $OUTPUT->heading(get_string('importpreview', 'grades'));
 
-//} else if (($formdata = data_submitted()) && !empty($formdata->map)) {
+        foreach ($header as $i => $h) {
+            $h = trim($h); // Remove whitespace.
+            $h = clean_param($h, PARAM_RAW); // Clean the header.
+            $header[$i] = $h;
+        }
 
-// else if grade import mapping form is submitted
-} else if ($formdata = $mform2->get_data()) {
+        $table = new html_table();
+        $table->head = $header;
+        $csvimport->init();
+        $previewdata = array();
+        while ($numlines <= $formdata->previewrows) {
+            $lines = $csvimport->next();
+            if ($lines) {
+                $previewdata[] = $lines;
+            }
+            $numlines ++;
+        }
+        $table->data = $previewdata;
+        echo html_writer::table($table);
+    } else {
+        // Display the standard upload file form.
+        groups_print_course_menu($course, 'index.php?id='.$id);
+        echo html_writer::start_tag('div', array('class' => 'clearer'));
+        echo html_writer::end_tag('div');
+
+        $mform->display();
+        echo $OUTPUT->footer();
+        die();
+    }
+}
 
-    $importcode = clean_param($formdata->importcode, PARAM_FILE);
-    $filename = $CFG->tempdir.'/gradeimport/cvs/'.$USER->id.'/'.$importcode;
+// Data has already been submitted so we can use the $iid to retrieve it.
+$csvimport = new csv_import_reader($iid, 'grade');
+$header = $csvimport->get_columns();
 
-    if (!file_exists($filename)) {
-        print_error('cannotuploadfile');
-    }
+// we create a form to handle mapping data from the file to the database.
+$mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header));
+$mform2->set_data(array('iid' => $iid, 'id' => $id, 'importcode'=>$importcode, 'verbosescales' => $verbosescales));
 
-    if ($fp = fopen($filename, "r")) {
-        // --- get header (field names) ---
-        $header = explode($csv_delimiter, clean_param(fgets($fp,GRADE_CSV_LINE_LENGTH), PARAM_RAW));
+// Here, if we have data, we process the fields and enter the information into the database.
+if ($formdata = $mform2->get_data()) {
 
-        foreach ($header as $i => $h) {
-            $h = trim($h); $header[$i] = $h; // remove whitespace
-        }
-    } else {
-        print_error('cannotopenfile');
+    foreach ($header as $i => $h) {
+        $h = trim($h); // Remove whitespace.
+        $h = clean_param($h, PARAM_RAW); // Clean the header.
+        $header[$i] = $h;
     }
 
     $map = array();
@@ -204,8 +174,6 @@ if ($formdata = $mform->get_data()) {
                 $maperrors[$j] = true;
             } else {
                 // collision
-                fclose($fp);
-                unlink($filename); // needs to be uploaded again, sorry
                 print_error('cannotmapfield', '', '', $j);
             }
         }
@@ -217,186 +185,160 @@ if ($formdata = $mform->get_data()) {
     @set_time_limit(0);
     raise_memory_limit(MEMORY_EXTRA);
 
-    // we only operate if file is readable
-    if ($fp = fopen($filename, "r")) {
-
-        // read the first line makes sure this doesn't get read again
-        $header = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH));
+    $csvimport->init();
 
-        $newgradeitems = array(); // temporary array to keep track of what new headers are processed
-        $status = true;
+    $newgradeitems = array(); // temporary array to keep track of what new headers are processed
+    $status = true;
 
-        while (!feof ($fp)) {
-            // add something
-            $line = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH));
+    while ($line = $csvimport->next()) {
+        if(count($line) <= 1){
+            // there is no data on this line, move on
+            continue;
+        }
 
-            if(count($line) <= 1){
-                // there is no data on this line, move on
-                continue;
+        // array to hold all grades to be inserted
+        $newgrades = array();
+        // array to hold all feedback
+        $newfeedbacks = array();
+        // each line is a student record
+        foreach ($line as $key => $value) {
+
+            $value = clean_param($value, PARAM_RAW);
+            $value = trim($value);
+
+            /*
+             * the options are
+             * 1) userid, useridnumber, usermail, username - used to identify user row
+             * 2) new - new grade item
+             * 3) id - id of the old grade item to map onto
+             * 3) feedback_id - feedback for grade item id
+             */
+
+            $t = explode("_", $map[$key]);
+            $t0 = $t[0];
+            if (isset($t[1])) {
+                $t1 = (int)$t[1];
+            } else {
+                $t1 = '';
             }
 
-            // array to hold all grades to be inserted
-            $newgrades = array();
-            // array to hold all feedback
-            $newfeedbacks = array();
-            // each line is a student record
-            foreach ($line as $key => $value) {
-                //decode encoded commas
-                $value = clean_param($value, PARAM_RAW);
-                $value = trim($value);
-                if (!empty($csv_encode)) {
-                    $value = preg_replace($csv_encode, $csv_delimiter, $value);
-                }
+            switch ($t0) {
+                case 'userid': //
+                    if (!$user = $DB->get_record('user', array('id' => $value))) {
+                        // user not found, abort whole import
+                        import_cleanup($importcode);
+                        echo $OUTPUT->notification("user mapping error, could not find user with id \"$value\"");
+                        $status = false;
+                        break 3;
+                    }
+                    $studentid = $value;
+                break;
+                case 'useridnumber':
+                    if (!$user = $DB->get_record('user', array('idnumber' => $value))) {
+                         // user not found, abort whole import
+                        import_cleanup($importcode);
+                        echo $OUTPUT->notification("user mapping error, could not find user with idnumber \"$value\"");
+                        $status = false;
+                        break 3;
+                    }
+                    $studentid = $user->id;
+                break;
+                case 'useremail':
+                    if (!$user = $DB->get_record('user', array('email' => $value))) {
+                        import_cleanup($importcode);
+                        echo $OUTPUT->notification("user mapping error, could not find user with email address \"$value\"");
+                        $status = false;
+                        break 3;
+                    }
+                    $studentid = $user->id;
+                break;
+                case 'username':
+                    if (!$user = $DB->get_record('user', array('username' => $value))) {
+                        import_cleanup($importcode);
+                        echo $OUTPUT->notification("user mapping error, could not find user with username \"$value\"");
+                        $status = false;
+                        break 3;
+                    }
+                    $studentid = $user->id;
+                break;
+                case 'new':
+                    // first check if header is already in temp database
 
-                /*
-                 * the options are
-                 * 1) userid, useridnumber, usermail, username - used to identify user row
-                 * 2) new - new grade item
-                 * 3) id - id of the old grade item to map onto
-                 * 3) feedback_id - feedback for grade item id
-                 */
-
-                $t = explode("_", $map[$key]);
-                $t0 = $t[0];
-                if (isset($t[1])) {
-                    $t1 = (int)$t[1];
-                } else {
-                    $t1 = '';
-                }
+                    if (empty($newgradeitems[$key])) {
 
-                switch ($t0) {
-                    case 'userid': //
-                        if (!$user = $DB->get_record('user', array('id' => $value))) {
-                            // user not found, abort whole import
-                            import_cleanup($importcode);
-                            echo $OUTPUT->notification("user mapping error, could not find user with id \"$value\"");
+                        $newgradeitem = new stdClass();
+                        $newgradeitem->itemname = $header[$key];
+                        $newgradeitem->importcode = $importcode;
+                        $newgradeitem->importer   = $USER->id;
+
+                        // insert into new grade item buffer
+                        $newgradeitems[$key] = $DB->insert_record('grade_import_newitem', $newgradeitem);
+                    }
+                    $newgrade = new stdClass();
+                    $newgrade->newgradeitem = $newgradeitems[$key];
+
+                    // if the user has a grade for this grade item
+                    if (trim($value) != '-') {
+                        // instead of omitting the grade we could insert one with finalgrade set to 0
+                        // we do not have access to grade item min grade
+                        $newgrade->finalgrade   = $value;
+                        $newgrades[] = $newgrade;
+                    }
+                break;
+                case 'feedback':
+                    if ($t1) {
+                        // case of an id, only maps id of a grade_item
+                        // this was idnumber
+                        if (!$gradeitem = new grade_item(array('id'=>$t1, 'courseid'=>$course->id))) {
+                            // supplied bad mapping, should not be possible since user
+                            // had to pick mapping
                             $status = false;
-                            break 3;
-                        }
-                        $studentid = $value;
-                    break;
-                    case 'useridnumber':
-                        if (!$user = $DB->get_record('user', array('idnumber' => $value))) {
-                             // user not found, abort whole import
                             import_cleanup($importcode);
-                            echo $OUTPUT->notification("user mapping error, could not find user with idnumber \"$value\"");
-                            $status = false;
+                            echo $OUTPUT->notification(get_string('importfailed', 'grades'));
                             break 3;
                         }
-                        $studentid = $user->id;
-                    break;
-                    case 'useremail':
-                        if (!$user = $DB->get_record('user', array('email' => $value))) {
-                            import_cleanup($importcode);
-                            echo $OUTPUT->notification("user mapping error, could not find user with email address \"$value\"");
+
+                        // t1 is the id of the grade item
+                        $feedback = new stdClass();
+                        $feedback->itemid   = $t1;
+                        $feedback->feedback = $value;
+                        $newfeedbacks[] = $feedback;
+                    }
+                break;
+                default:
+                    // existing grade items
+                    if (!empty($map[$key])) {
+                        // case of an id, only maps id of a grade_item
+                        // this was idnumber
+                        if (!$gradeitem = new grade_item(array('id'=>$map[$key], 'courseid'=>$course->id))) {
+                            // supplied bad mapping, should not be possible since user
+                            // had to pick mapping
                             $status = false;
+                            import_cleanup($importcode);
+                            echo $OUTPUT->notification(get_string('importfailed', 'grades'));
                             break 3;
                         }
-                        $studentid = $user->id;
-                    break;
-                    case 'username':
-                        if (!$user = $DB->get_record('user', array('username' => $value))) {
-                            import_cleanup($importcode);
-                            echo $OUTPUT->notification("user mapping error, could not find user with username \"$value\"");
+
+                        // check if grade item is locked if so, abort
+                        if ($gradeitem->is_locked()) {
                             $status = false;
+                            import_cleanup($importcode);
+                            echo $OUTPUT->notification(get_string('gradeitemlocked', 'grades'));
                             break 3;
                         }
-                        $studentid = $user->id;
-                    break;
-                    case 'new':
-                        // first check if header is already in temp database
-
-                        if (empty($newgradeitems[$key])) {
 
-                            $newgradeitem = new stdClass();
-                            $newgradeitem->itemname = $header[$key];
-                            $newgradeitem->importcode = $importcode;
-                            $newgradeitem->importer   = $USER->id;
-
-                            // insert into new grade item buffer
-                            $newgradeitems[$key] = $DB->insert_record('grade_import_newitem', $newgradeitem);
-                        }
                         $newgrade = new stdClass();
-                        $newgrade->newgradeitem = $newgradeitems[$key];
-
-                        // if the user has a grade for this grade item
-                        if (trim($value) != '-') {
-                            // instead of omitting the grade we could insert one with finalgrade set to 0
-                            // we do not have access to grade item min grade
-                            $newgrade->finalgrade   = $value;
-                            $newgrades[] = $newgrade;
-                        }
-                    break;
-                    case 'feedback':
-                        if ($t1) {
-                            // case of an id, only maps id of a grade_item
-                            // this was idnumber
-                            if (!$gradeitem = new grade_item(array('id'=>$t1, 'courseid'=>$course->id))) {
-                                // supplied bad mapping, should not be possible since user
-                                // had to pick mapping
-                                $status = false;
-                                import_cleanup($importcode);
-                                echo $OUTPUT->notification(get_string('importfailed', 'grades'));
-                                break 3;
-                            }
-
-                            // t1 is the id of the grade item
-                            $feedback = new stdClass();
-                            $feedback->itemid   = $t1;
-                            $feedback->feedback = $value;
-                            $newfeedbacks[] = $feedback;
-                        }
-                    break;
-                    default:
-                        // existing grade items
-                        if (!empty($map[$key])) {
-                            // case of an id, only maps id of a grade_item
-                            // this was idnumber
-                            if (!$gradeitem = new grade_item(array('id'=>$map[$key], 'courseid'=>$course->id))) {
-                                // supplied bad mapping, should not be possible since user
-                                // had to pick mapping
-                                $status = false;
-                                import_cleanup($importcode);
-                                echo $OUTPUT->notification(get_string('importfailed', 'grades'));
-                                break 3;
-                            }
-
-                            // check if grade item is locked if so, abort
-                            if ($gradeitem->is_locked()) {
-                                $status = false;
-                                import_cleanup($importcode);
-                                echo $OUTPUT->notification(get_string('gradeitemlocked', 'grades'));
-                                break 3;
-                            }
-
-                            $newgrade = new stdClass();
-                            $newgrade->itemid     = $gradeitem->id;
-                            if ($gradeitem->gradetype == GRADE_TYPE_SCALE and $verbosescales) {
-                                if ($value === '' or $value == '-') {
-                                    $value = null; // no grade
-                                } else {
-                                    $scale = $gradeitem->load_scale();
-                                    $scales = explode(',', $scale->scale);
-                                    $scales = array_map('trim', $scales); //hack - trim whitespace around scale options
-                                    array_unshift($scales, '-'); // scales start at key 1
-                                    $key = array_search($value, $scales);
-                                    if ($key === false) {
-                                        echo "<br/>t0 is $t0";
-                                        echo "<br/>grade is $value";
-                                        $status = false;
-                                        import_cleanup($importcode);
-                                        echo $OUTPUT->notification(get_string('badgrade', 'grades'));
-                                        break 3;
-                                    }
-                                    $value = $key;
-                                }
-                                $newgrade->finalgrade = $value;
+                        $newgrade->itemid     = $gradeitem->id;
+                        if ($gradeitem->gradetype == GRADE_TYPE_SCALE and $verbosescales) {
+                            if ($value === '' or $value == '-') {
+                                $value = null; // no grade
                             } else {
-                                if ($value === '' or $value == '-') {
-                                    $value = null; // no grade
-
-                                } else if (!is_numeric($value)) {
-                                // non numeric grade value supplied, possibly mapped wrong column
+                                $scale = $gradeitem->load_scale();
+                                $scales = explode(',', $scale->scale);
+                                $scales = array_map('trim', $scales); //hack - trim whitespace around scale options
+                                array_unshift($scales, '-'); // scales start at key 1
+                                $key = array_search($value, $scales);
+                                if ($key === false) {
                                     echo "<br/>t0 is $t0";
                                     echo "<br/>grade is $value";
                                     $status = false;
@@ -404,94 +346,98 @@ if ($formdata = $mform->get_data()) {
                                     echo $OUTPUT->notification(get_string('badgrade', 'grades'));
                                     break 3;
                                 }
-                                $newgrade->finalgrade = $value;
+                                $value = $key;
                             }
-                            $newgrades[] = $newgrade;
-                        } // otherwise, we ignore this column altogether
-                          // because user has chosen to ignore them (e.g. institution, address etc)
-                    break;
-                }
-            }
-
-            // no user mapping supplied at all, or user mapping failed
-            if (empty($studentid) || !is_numeric($studentid)) {
-                // user not found, abort whole import
-                $status = false;
-                import_cleanup($importcode);
-                echo $OUTPUT->notification('user mapping error, could not find user!');
+                            $newgrade->finalgrade = $value;
+                        } else {
+                            if ($value === '' or $value == '-') {
+                                $value = null; // no grade
+
+                            } else if (!is_numeric($value)) {
+                            // non numeric grade value supplied, possibly mapped wrong column
+                                echo "<br/>t0 is $t0";
+                                echo "<br/>grade is $value";
+                                $status = false;
+                                import_cleanup($importcode);
+                                echo $OUTPUT->notification(get_string('badgrade', 'grades'));
+                                break 3;
+                            }
+                            $newgrade->finalgrade = $value;
+                        }
+                        $newgrades[] = $newgrade;
+                    } // otherwise, we ignore this column altogether
+                      // because user has chosen to ignore them (e.g. institution, address etc)
                 break;
             }
+        }
 
-            if ($separatemode and !groups_is_member($currentgroup, $studentid)) {
-                // not allowed to import into this group, abort
-                $status = false;
-                import_cleanup($importcode);
-                echo $OUTPUT->notification('user not member of current group, can not update!');
-                break;
-            }
+        // no user mapping supplied at all, or user mapping failed
+        if (empty($studentid) || !is_numeric($studentid)) {
+            // user not found, abort whole import
+            $status = false;
+            import_cleanup($importcode);
+            echo $OUTPUT->notification('user mapping error, could not find user!');
+            break;
+        }
 
-            // insert results of this students into buffer
-            if ($status and !empty($newgrades)) {
+        if ($separatemode and !groups_is_member($currentgroup, $studentid)) {
+            // not allowed to import into this group, abort
+            $status = false;
+            import_cleanup($importcode);
+            echo $OUTPUT->notification('user not member of current group, can not update!');
+            break;
+        }
 
-                foreach ($newgrades as $newgrade) {
+        // insert results of this students into buffer
+        if ($status and !empty($newgrades)) {
 
-                    // check if grade_grade is locked and if so, abort
-                    if (!empty($newgrade->itemid) and $grade_grade = new grade_grade(array('itemid'=>$newgrade->itemid, 'userid'=>$studentid))) {
-                        if ($grade_grade->is_locked()) {
-                            // individual grade locked
-                            $status = false;
-                            import_cleanup($importcode);
-                            echo $OUTPUT->notification(get_string('gradelocked', 'grades'));
-                            break 2;
-                        }
-                    }
+            foreach ($newgrades as $newgrade) {
 
-                    $newgrade->importcode = $importcode;
-                    $newgrade->userid     = $studentid;
-                    $newgrade->importer   = $USER->id;
-                    $DB->insert_record('grade_import_values', $newgrade);
-                }
-            }
-
-            // updating/inserting all comments here
-            if ($status and !empty($newfeedbacks)) {
-                foreach ($newfeedbacks as $newfeedback) {
-                    $sql = "SELECT *
-                              FROM {grade_import_values}
-                             WHERE importcode=? AND userid=? AND itemid=? AND importer=?";
-                    if ($feedback = $DB->get_record_sql($sql, array($importcode, $studentid, $newfeedback->itemid, $USER->id))) {
-                        $newfeedback->id = $feedback->id;
-                        $DB->update_record('grade_import_values', $newfeedback);
-
-                    } else {
-                        // the grade item for this is not updated
-                        $newfeedback->importcode = $importcode;
-                        $newfeedback->userid     = $studentid;
-                        $newfeedback->importer   = $USER->id;
-                        $DB->insert_record('grade_import_values', $newfeedback);
+                // check if grade_grade is locked and if so, abort
+                if (!empty($newgrade->itemid) and $grade_grade = new grade_grade(array('itemid'=>$newgrade->itemid, 'userid'=>$studentid))) {
+                    if ($grade_grade->is_locked()) {
+                        // individual grade locked
+                        $status = false;
+                        import_cleanup($importcode);
+                        echo $OUTPUT->notification(get_string('gradelocked', 'grades'));
+                        break 2;
                     }
                 }
+
+                $newgrade->importcode = $importcode;
+                $newgrade->userid     = $studentid;
+                $newgrade->importer   = $USER->id;
+                $DB->insert_record('grade_import_values', $newgrade);
             }
         }
 
-        /// at this stage if things are all ok, we commit the changes from temp table
-        if ($status) {
-            grade_import_commit($course->id, $importcode);
+        // updating/inserting all comments here
+        if ($status and !empty($newfeedbacks)) {
+            foreach ($newfeedbacks as $newfeedback) {
+                $sql = "SELECT *
+                          FROM {grade_import_values}
+                         WHERE importcode=? AND userid=? AND itemid=? AND importer=?";
+                if ($feedback = $DB->get_record_sql($sql, array($importcode, $studentid, $newfeedback->itemid, $USER->id))) {
+                    $newfeedback->id = $feedback->id;
+                    $DB->update_record('grade_import_values', $newfeedback);
+
+                } else {
+                    // the grade item for this is not updated
+                    $newfeedback->importcode = $importcode;
+                    $newfeedback->userid     = $studentid;
+                    $newfeedback->importer   = $USER->id;
+                    $DB->insert_record('grade_import_values', $newfeedback);
+                }
+            }
         }
-        // temporary file can go now
-        fclose($fp);
-        unlink($filename);
-    } else {
-        print_error('cannotreadfile');
     }
 
+    /// at this stage if things are all ok, we commit the changes from temp table
+    if ($status) {
+        grade_import_commit($course->id, $importcode);
+    }
 } else {
-    groups_print_course_menu($course, 'index.php?id='.$id);
-    echo '<div class="clearer"></div>';
-
-    // display the standard upload file form
-    $mform->display();
-}
-
-echo $OUTPUT->footer();
-
+    // If data hasn't been submitted then display the data mapping form.
+    $mform2->display();
+    echo $OUTPUT->footer();
+}
\ No newline at end of file
index 76514b3..9723be2 100644 (file)
@@ -118,11 +118,11 @@ class grade_import_mapping_form extends moodleform {
         $mform->setType('map', PARAM_INT);
         $mform->addElement('hidden', 'id');
         $mform->setType('id', PARAM_INT);
+        $mform->addElement('hidden', 'iid');
+        $mform->setType('iid', PARAM_INT);
         $mform->addElement('hidden', 'importcode');
         $mform->setType('importcode', PARAM_FILE);
         $mform->addElement('hidden', 'verbosescales', 1);
-        $mform->setType('separator', PARAM_ALPHA);
-        $mform->addElement('hidden', 'separator', 'comma');
         $mform->setType('verbosescales', PARAM_INT);
         $mform->addElement('hidden', 'groupid', groups_get_course_group($COURSE));
         $mform->setType('groupid', PARAM_INT);
index a26537b..36e493e 100644 (file)
@@ -107,7 +107,7 @@ grade_regrade_final_grades($courseid);
 
 // Perform actions
 if (!empty($target) && !empty($action) && confirm_sesskey()) {
-    grade_report_grader::process_action($target, $action);
+    grade_report_grader::do_process_action($target, $action);
 }
 
 $reportname = get_string('pluginname', 'gradereport_grader');
index fccb042..2f9d0d4 100644 (file)
@@ -1584,13 +1584,17 @@ class grade_report_grader extends grade_report {
         return $icon;
     }
 
+    public function process_action($target, $action) {
+        return self::do_process_action($target, $action);
+    }
+
     /**
      * 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) {
+    public static function do_process_action($target, $action) {
         // TODO: this code should be in some grade_tree static method
         $targettype = substr($target, 0, 1);
         $targetid = substr($target, 1);
index 7de314e..f129f7e 100644 (file)
@@ -48,12 +48,12 @@ $string['environmenthead'] = 'Comprobando su entorno';
 $string['environmentsub2'] = 'Cada versión de Moodle tiene algún requisito mínimo de la versión de PHP y un número obligatorio de extensiones de PHP. Una comprobación del entorno completo se realiza antes de cada instalación y actualización. Por favor, póngase en contacto con el administrador del servidor si no sabe cómo instalar la nueva versión o habilitar las extensiones PHP.';
 $string['errorsinenvironment'] = '¡La comprobación del entorno falló!';
 $string['installation'] = 'Instalación';
-$string['langdownloaderror'] = 'El idioma "{$a}" no pudo ser instalado. El proceso de instalación continuará en inglés.';
+$string['langdownloaderror'] = 'El idioma "{$a}" no pudo ser instalado. El proceso de instalación continuará en Inglés.';
 $string['memorylimithelp'] = '<p>El límite de memoria PHP en su servidor es actualmente {$a}.</p>
 
 <p>Esto puede ocasionar que Moodle tenga problemas de memoria más adelante, especialmente si usted tiene activados muchos módulos y/o muchos usuarios.</p>
 
-<p>Recomendamos que configure PHP con el límite más alto posible, e.g. 40M.
+<p>Recomendamos que configure PHP con el límite más alto posible, por ejemplo: 40M.
 Hay varias formas de hacer esto:</p>
 <ol>
 <li>Si puede hacerlo, recompile PHP con <i>--enable-memory-limit</i>.
index dbf755f..42835f0 100644 (file)
@@ -90,7 +90,6 @@ $string['bookmarkthispage'] = 'bookmark this page';
 $string['cachejs'] = 'Cache Javascript';
 $string['cachejs_help'] = 'Javascript caching and compression greatly improves page loading performance. it is strongly recommended for production sites. Developers will probably want to disable this feature.';
 $string['cachetext'] = 'Text cache lifetime';
-$string['cachetype'] = 'Cache type';
 $string['calendarexportsalt'] = 'Calendar export salt';
 $string['calendarsettings'] = 'Calendar';
 $string['calendar_weekend'] = 'Weekend days';
@@ -143,7 +142,6 @@ $string['configautolang'] = 'Detect default language from browser setting, if di
 $string['configautologinguests'] = 'Should visitors be logged in as guests automatically when entering courses with guest access?';
 $string['configbloglevel'] = 'This setting allows you to restrict the level to which user blogs can be viewed on this site.  Note that they specify the maximum context of the VIEWER not the poster or the types of blog posts.  Blogs can also be disabled completely if you don\'t want them at all.';
 $string['configcachetext'] = 'For larger sites or sites that use text filters, this setting can really speed things up.  Copies of texts will be retained in their processed form for the time specified here.  Setting this too small may actually slow things down slightly,  but setting it too large may mean texts take too long to refresh (with new links, for example).';
-$string['configcachetype'] = 'Select a type of cache for Moodle to use. This will only configure the cache, remember to enable rcache so that the cache is used for something. Use <strong>only</strong> if you need to reduce the load on the database system -- otherwise Moodle will actually run slower. Medium-traffic sites may see benefits using \'internal\'. A single webserver with eAccelerator or Turckmmcache installed <em>with the shared memory options enabled</em> should try \'eaccelerator\'. If you have a multiple-server setup, and you have one or more memcached daemons running and the PHP-memcached extension, select \'memcached\' and configure the memached options below. <br /><strong>Note:</strong> make sure you test performance under load and tune accordingly -- the caches can make your site slower. In high-traffic situations, eAccelerator and memcached can yield the most benefits, but have the higher costs in CPU usage on the webserver.';
 $string['configcalendarexportsalt'] = 'This random text is used for improving of security of authentication tokens used for exporting of calendars. Please note that all current tokens are invalidated if you change this hash salt.';
 $string['configclamactlikevirus'] = 'Treat files like viruses';
 $string['configclamdonothing'] = 'Treat files as OK';
@@ -229,7 +227,6 @@ $string['configgradebookroles'] = 'This setting allows you to control who appear
 $string['configgradeexport'] = 'Choose which gradebook export formats are your primary methods for exporting grades.  Chosen plugins will then set and use a "last exported" field for every grade.  For example, this might result in exported records being identified as being "new" or "updated".  If you are not sure about this then leave everything unchecked.';
 $string['confighiddenuserfields'] = 'Select which user information fields you wish to hide from other users other than course teachers/admins. This will increase student privacy. Hold CTRL key to select multiple fields.';
 $string['configidnumber'] = 'This option specifies whether (a) Users are not be asked for an ID number at all, (b) Users are asked for an ID number but can leave it blank or (c) Users are asked for an ID Number and cannot leave it blank. If given the User\'s ID number is displayed in their Profile.';
-$string['configintcachemax'] = 'For internal cache only. Maximum number of records to keep in the cache. Recommended value: 50. Use lower values to reduce memory usage.';
 $string['configintro'] = 'On this page you can specify a number of configuration variables that help make Moodle work properly on your server.  Don\'t worry too much about it - the defaults will usually work fine and you can always come back to this page later and change these settings.';
 $string['configintroadmin'] = 'On this page you should configure your main administrator account which will have complete control over the site. Make sure you give it a secure username and password as well as a valid email address.  You can create more admin accounts later on.';
 $string['configintrosite'] = 'This page allows you to configure the front page and name of this new site.  You can come back here later to change these settings any time using the Administration menus.';
@@ -248,8 +245,6 @@ $string['configmaxbytes'] = 'This specifies a maximum size that uploaded files c
 $string['configmaxconsecutiveidentchars'] = 'Passwords must not have more than this number of consecutive identical characters. Use 0 to disable this check.';
 $string['configmaxeditingtime'] = 'This specifies the amount of time people have to re-edit forum postings, glossary comments etc.  Usually 30 minutes is a good value.';
 $string['configmaxevents'] = 'Events to Lookahead';
-$string['configmemcachedhosts'] = 'For memcached. Comma-separated list of hosts that are running the memcached daemon. Use IP addresses to avoid DNS latency. memcached does not behave well if you add/remove hosts on a running setup.';
-$string['configmemcachedpconn'] = 'For memcached. Use persistent connections. Use carefully -- it can make Apache/PHP crash after a restart of the memcached daemon.';
 $string['configmessaging'] = 'Should the messaging system between site users be enabled?';
 $string['configmessagingallowemailoverride'] = 'Allow users to have email message notifications sent to an email address other than the email address in their profile';
 $string['configmessaginghidereadnotifications'] = 'Hide read notifications of events like forum posts when viewing messaging history';
@@ -286,8 +281,6 @@ $string['configproxyport'] = 'If this server needs to use a proxy computer, then
 $string['configproxytype'] = 'Type of web proxy (PHP5 and cURL extension required for SOCKS5 support).';
 $string['configproxyuser'] = 'Username needed to access internet through proxy if required, empty if none (PHP cURL extension required).';
 $string['configquarantinedir'] = 'If you want clam AV to move infected files to a quarantine directory, enter it here. It must be writable by the webserver.  If you leave this blank, or if you enter a directory that doesn\'t exist or isn\'t writable, infected files will be deleted.  Do not include a trailing slash.';
-$string['configrcache'] = 'Use the cache to store database records. Remember to set \'cachetype\' as well!';
-$string['configrcachettl'] = 'Time-to-live for cached records, in seconds. Use a short (&lt;15) value here.';
 $string['configrecaptchaprivatekey'] = 'String of characters used to communicate between your Moodle server and the recaptcha server. Obtain one for this site by visiting http://www.google.com/recaptcha';
 $string['configrecaptchapublickey'] = 'String of characters used to display the reCAPTCHA element in the signup form. Generated by http://www.google.com/recaptcha';
 $string['configrequestcategoryselection'] = 'Allow the selection of a category when requesting a course.';
@@ -597,7 +590,6 @@ $string['includemoduleuserdata'] = 'Include module user data';
 $string['incompatibleblocks'] = 'Incompatible blocks';
 $string['installhijacked'] = 'Installation must be finished from the original IP address, sorry.';
 $string['installsessionerror'] = 'Can not initialise PHP session, please verify that your browser accepts cookies.';
-$string['intcachemax'] = 'Int. cache max';
 $string['intlrecommended'] = 'Intl extension is used to improve internationalization support, such as locale aware sorting.';
 $string['invalidsection'] = 'Invalid section.';
 $string['invaliduserchangeme'] = 'Username "changeme" is reserved -- you cannot create an account with it.';
@@ -681,8 +673,6 @@ $string['mediapluginswf'] = 'Enable .swf filter';
 $string['mediapluginswfnote'] = 'As a default security measure, normal users should not be allowed to embed swf flash files.';
 $string['mediapluginwmv'] = 'Enable .wmv filter';
 $string['mediapluginyoutube'] = 'Enable YouTube links filter';
-$string['memcachedhosts'] = 'memcached hosts';
-$string['memcachedpconn'] = 'memcached use persistent connections';
 $string['messaging'] = 'Enable messaging system';
 $string['messagingallowemailoverride'] = 'Notification email override';
 $string['messaginghidereadnotifications'] = 'Hide read notifications';
@@ -866,8 +856,6 @@ $string['questioncwqpfscheck'] = 'One or more \'random\' questions in a quiz are
 $string['questioncwqpfsok'] = 'Good. There are no \'random\' questions in your quizzes that are set up to select questions from a mixture of shared and unshared question categories.';
 $string['questiontype'] = 'Question type';
 $string['questiontypes'] = 'Question types';
-$string['rcache'] = 'Record cache';
-$string['rcachettl'] = 'Record cache TTL';
 $string['recaptchaprivatekey'] = 'ReCAPTCHA private key';
 $string['recaptchapublickey'] = 'ReCAPTCHA public key';
 $string['register'] = 'Register your site';
index 23e8ecf..54ee712 100644 (file)
@@ -161,6 +161,7 @@ $string['showglobalevents'] = 'Show global events';
 $string['showgroupsevents'] = 'Show group events';
 $string['showuserevents'] = 'Show user events';
 $string['shown'] = 'shown';
+$string['siteevents'] = 'Site events';
 $string['spanningevents'] = 'Events underway';
 $string['subscriptions'] = 'Subscriptions';
 $string['subscriptionname'] = 'Calendar name';
index 5594c8c..1a2c751 100644 (file)
@@ -40,7 +40,6 @@ $string['blocknoncontacts'] = 'Prevent non-contacts from messaging me';
 $string['contactlistempty'] = 'Your contact list is empty';
 $string['contacts'] = 'Contacts';
 $string['context'] = 'context';
-$string['couldnotfindpreference'] = 'Could not load preference {$a}. Does the component and name you supplied to message_send() match a row in message_providers? Message providers must appear in the database so users can configure how they will be notified when they receive messages.';
 $string['defaultmessageoutputs'] = 'Default message outputs';
 $string['defaults'] = 'Defaults';
 $string['deletemessagesdays'] = 'Number of days before old messages are automatically deleted';
index e5b4760..9bb8e77 100644 (file)
@@ -3390,13 +3390,16 @@ class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
     public function write_setting($data) {
         global $DB, $SITE;
         $record = new stdClass();
-        $record->id            = SITEID;
+        $record->id            = $SITE->id;
         $record->{$this->name} = ($data == '1' ? 1 : 0);
         $record->timemodified  = time();
         // update $SITE
         $SITE->{$this->name} = $data;
         course_get_format($SITE)->update_course_format_options($record);
-        return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
+        $DB->update_record('course', $record);
+        // There is something wrong in cache updates somewhere, let's reset everything.
+        format_base::reset_course_cache();
+        return '';
     }
 }
 
@@ -3450,13 +3453,16 @@ class admin_setting_sitesettext extends admin_setting_configtext {
         }
 
         $record = new stdClass();
-        $record->id            = SITEID;
+        $record->id            = $SITE->id;
         $record->{$this->name} = $data;
         $record->timemodified  = time();
         // update $SITE
         $SITE->{$this->name} = $data;
         course_get_format($SITE)->update_course_format_options($record);
-        return ($DB->update_record('course', $record) ? '' : get_string('dbupdatefailed', 'error'));
+        $DB->update_record('course', $record);
+        // There is something wrong in cache updates somewhere, let's reset everything.
+        format_base::reset_course_cache();
+        return '';
     }
 }
 
@@ -3493,12 +3499,15 @@ class admin_setting_special_frontpagedesc extends admin_setting {
     public function write_setting($data) {
         global $DB, $SITE;
         $record = new stdClass();
-        $record->id            = SITEID;
+        $record->id            = $SITE->id;
         $record->{$this->name} = $data;
         $record->timemodified  = time();
         $SITE->{$this->name} = $data;
         course_get_format($SITE)->update_course_format_options($record);
-        return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
+        $DB->update_record('course', $record);
+        // There is something wrong in cache updates somewhere, let's reset everything.
+        format_base::reset_course_cache();
+        return '';
     }
 
     /**
@@ -5075,16 +5084,16 @@ class admin_setting_manageenrols extends admin_setting {
                 if ($updowncount > 1) {
                     $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
                     $updown .= "<a href=\"$aurl\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" /></a>&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" class=\"iconsmall\" /></a>&nbsp;";
                 } else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
                 }
                 if ($updowncount < $enrolcount) {
                     $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
                     $updown .= "<a href=\"$aurl\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" /></a>";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" class=\"iconsmall\" /></a>";
                 } else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
                 }
                 ++$updowncount;
             }
@@ -5606,17 +5615,17 @@ class admin_setting_manageauths extends admin_setting {
             if ($enabled) {
                 if ($updowncount > 1) {
                     $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" /></a>&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
                 }
                 if ($updowncount < $authcount) {
                     $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" /></a>";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
                 }
                 ++ $updowncount;
             }
@@ -5773,17 +5782,17 @@ class admin_setting_manageeditors extends admin_setting {
             if ($enabled) {
                 if ($updowncount > 1) {
                     $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" /></a>&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
                 }
                 if ($updowncount < $editorcount) {
                     $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" /></a>";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
                 }
                 ++ $updowncount;
             }
@@ -6000,7 +6009,7 @@ class admin_setting_manageformats extends admin_setting {
 
         $cnt = 0;
         $defaultformat = get_config('moodlecourse', 'format');
-        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon'));
+        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
         foreach ($formats as $format) {
             $url = new moodle_url('/admin/courseformats.php',
                     array('sesskey' => sesskey(), 'format' => $format->name));
@@ -6021,13 +6030,13 @@ class admin_setting_manageformats extends admin_setting {
             $updown = '';
             if ($cnt) {
                 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
-                    $OUTPUT->pix_icon('t/up', $txt->up, 'moodle')). '&nbsp;';
+                    $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
             } else {
                 $updown .= $spacer;
             }
             if ($cnt < count($formats) - 1) {
                 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
-                    $OUTPUT->pix_icon('t/down', $txt->down, 'moodle'));
+                    $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
             } else {
                 $updown .= $spacer;
             }
@@ -6862,18 +6871,19 @@ class admin_setting_managerepository extends admin_setting {
 
                 // Display up/down link
                 $updown = '';
-                $spacer = $OUTPUT->spacer(array('height'=>15, 'width'=>15)); // should be done with CSS instead
+                // Should be done with CSS instead.
+                $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
 
                 if ($updowncount > 1) {
                     $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" /></a>&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
                 }
                 else {
                     $updown .= $spacer;
                 }
                 if ($updowncount < $totalinstances) {
                     $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" /></a>";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
                 }
                 else {
                     $updown .= $spacer;
index 3c2936b..1f6bf3d 100644 (file)
@@ -232,7 +232,7 @@ class iCalendar_component {
     
     function unserialize($string) {
         $string = rfc2445_unfold($string); // Unfold any long lines
-        $lines = explode(RFC2445_CRLF, $string); // Create an array of lines
+        $lines = preg_split("<".RFC2445_CRLF."|\n|\r>", $string, 0, PREG_SPLIT_NO_EMPTY); // Create an array of lines.
         
         $components = array(); // Initialise a stack of components
         $this->clear_errors();
index 1c6154b..582b265 100644 (file)
@@ -2,4 +2,5 @@ Description of Bennu library import - customised library by author, this version
 
 modifications:
 1/ removed ereg functions deprecated as of php 5.3 (18 Nov 2009)
-2/ replaced mbstring functions with moodle textlib (28 Nov 2011)
\ No newline at end of file
+2/ replaced mbstring functions with moodle textlib (28 Nov 2011)
+3/ replaced explode in iCalendar_component::unserialize() with preg_split to support various line breaks (20 Nov 2012)
\ No newline at end of file
index 5bd64ec..f60af6c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20121112" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20121116" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="courseid"/>
         <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="url" NEXT="groupid"/>
         <FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="courseid" NEXT="userid"/>
-        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="groupid" NEXT="pollinterval"/>
-        <FIELD NAME="pollinterval" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Frequency of checks for new/changed events" PREVIOUS="userid" NEXT="lastupdated"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="groupid" NEXT="eventtype"/>
+        <FIELD NAME="eventtype" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="The type of the event" PREVIOUS="userid" NEXT="pollinterval"/>
+        <FIELD NAME="pollinterval" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Frequency of checks for new/changed events" PREVIOUS="eventtype" NEXT="lastupdated"/>
         <FIELD NAME="lastupdated" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" PREVIOUS="pollinterval" NEXT="name"/>
         <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="lastupdated"/>
       </FIELDS>
index 65c3046..7242366 100644 (file)
@@ -1473,6 +1473,33 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012111200.01);
     }
 
+    if ($oldversion < 2012111601.01) {
+        // Clea up after old shared memory caching support.
+        unset_config('cachetype');
+        unset_config('rcache');
+        unset_config('rcachettl');
+        unset_config('intcachemax');
+        unset_config('memcachedhosts');
+        unset_config('memcachedpconn');
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2012111601.01);
+    }
+
+    if ($oldversion < 2012112100.00) {
+
+        // Define field eventtype to be added to event_subscriptions.
+        $table = new xmldb_table('event_subscriptions');
+        $field = new xmldb_field('eventtype', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, 'userid');
+
+        // Conditionally launch add field eventtype.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2012112100.00);
+    }
 
     return true;
 }
index c81dcf6..6ffbcbe 100644 (file)
@@ -341,11 +341,13 @@ abstract class moodle_database {
             }
             $this->force_transaction_rollback();
         }
-        if ($this->used_for_db_sessions) {
-            // this is needed because we need to save session to db before closing it
+        // Always terminate sessions here to make it consistent,
+        // this is needed because we need to save session to db before closing it.
+        if (function_exists('session_get_instance')) {
             session_get_instance()->write_close();
-            $this->used_for_db_sessions = false;
         }
+        $this->used_for_db_sessions = false;
+
         if ($this->temptables) {
             $this->temptables->dispose();
             $this->temptables = null;
index 323a2fe..1104518 100644 (file)
@@ -1296,6 +1296,10 @@ s only returning name of SQL substring function, it now requires all parameters.
         if (!$this->session_lock_supported()) {
             return;
         }
+        if (!$this->used_for_db_sessions) {
+            return;
+        }
+
         parent::release_session_lock($rowid);
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
index 44f3a92..fa823e8 100644 (file)
@@ -1451,6 +1451,10 @@ class mysqli_native_moodle_database extends moodle_database {
     }
 
     public function release_session_lock($rowid) {
+        if (!$this->used_for_db_sessions) {
+            return;
+        }
+
         parent::release_session_lock($rowid);
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
         $sql = "SELECT RELEASE_LOCK('$fullname')";
index b62ac9b..eaafb54 100644 (file)
@@ -1649,6 +1649,10 @@ class oci_native_moodle_database extends moodle_database {
         if (!$this->session_lock_supported()) {
             return;
         }
+        if (!$this->used_for_db_sessions) {
+            return;
+        }
+
         parent::release_session_lock($rowid);
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
index ab3650f..9c43a16 100644 (file)
@@ -1289,6 +1289,10 @@ class pgsql_native_moodle_database extends moodle_database {
         if (!$this->session_lock_supported()) {
             return;
         }
+        if (!$this->used_for_db_sessions) {
+            return;
+        }
+
         parent::release_session_lock($rowid);
 
         $sql = "SELECT pg_advisory_unlock($rowid)";
index 168c682..30ceb2c 100644 (file)
@@ -1347,6 +1347,10 @@ class sqlsrv_native_moodle_database extends moodle_database {
         if (!$this->session_lock_supported()) {
             return;
         }
+        if (!$this->used_for_db_sessions) {
+            return;
+        }
+
         parent::release_session_lock($rowid);
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
diff --git a/lib/eaccelerator.class.php b/lib/eaccelerator.class.php
deleted file mode 100644 (file)
index bde70b9..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-<?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/>.
-
-/**
- * This class abstracts eaccelerator/turckmmcache
- * API to provide
- *
- * - get()
- * - set()
- * - delete()
- * - getforfill()
- * - releaseforfill()
- *
- * Note: do NOT store booleans here. For compatibility with
- * memcached, a false value is indistinguisable from a
- * "not found in cache" response.
- *
- * @package    core
- * @subpackage lib
- * @copyright  Martin Langhoff <martin@catalyst.net.nz>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- *
- * @copyright Martin Langhoff <martin@catalyst.net.nz>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package moodlecore
- */
-class eaccelerator {
-
-    /**
-     * @todo Document this function
-     *
-     * @global object
-     */
-    function eaccelerator() {
-        global $CFG;
-        if ( function_exists('eaccelerator_get')) {
-            $this->mode = 'eaccelerator';
-        } elseif (function_exists('mmcache_get')) {
-            $this->mode = 'mmcache';
-        } else {
-            debugging("\$CFG->eaccelerator is set to true but the required functions are not available. You need to have either eaccelerator or turckmmcache extensions installed, compiled with the shmem keys option enabled.");
-        }
-
-        $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
-    }
-
-    /**
-     * The status of the eaccelerator, if it has been established
-     * this will return true
-     *
-     * @return bool
-     */
-    function status() {
-        if (isset($this->mode)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @todo Document this function
-     *
-     * @param string $key
-     * @param string $value
-     * @param int $ttl
-     * @return mixed
-     */
-    function set($key, $value, $ttl=0) {
-        $set    = $this->mode . '_put';
-        $unlock = $this->mode . '_unlock';
-
-        // we may have acquired a lock via getforfill
-        // release if it exists
-        @$unlock($this->prefix . $key . '_forfill');
-
-        return $set($this->prefix . $key, serialize($value), $ttl);
-    }
-
-    /**
-     * @todo Document this function
-     *
-     * @param string $key
-     * @return string|bool String if success else false
-     */
-    function get($key) {
-        $fn = $this->mode . '_get';
-        $rec = $fn($this->prefix . $key);
-        if (is_null($rec)) {
-            return false;
-        }
-        return unserialize($rec);
-    }
-
-    /**
-     * @todo Document this function
-     *
-     * @param string $key
-     * @return mixed
-     */
-    function delete($key) {
-        $fn = $this->mode . '_rm';
-        return $fn($this->prefix . $key);
-    }
-
-    /**
-     * In the simple case, this function will
-     * get the cached value if available. If the entry
-     * is not cached, it will try to get an exclusive
-     * lock that announces that this process will
-     * populate the cache.
-     *
-     * If we fail to get the lock -- this means another
-     * process is doing it.
-     * so we wait (block) for a few microseconds while we wait for
-     * the cache to be filled or the lock to timeout.
-     *
-     * If you get a false from this call, you _must_
-     * populate the cache ASAP or indicate that
-     * you won't by calling releaseforfill().
-     *
-     * This technique forces serialisation and so helps deal
-     * with thundering herd scenarios where a lot of clients
-     * ask the for the same idempotent (and costly) operation.
-     * The implementation is based on suggestions in this message
-     * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
-     *
-     * @param $key string
-     * @return mixed on cache hit, false otherwise
-     */
-    function getforfill ($key) {
-        $get    = $this->mode . '_get';
-        $lock   = $this->mode . '_lock';
-
-        $rec = $get($this->prefix . $key);
-        if (!is_null($rec)) {
-            return unserialize($rec);
-        }
-        if ($lock($this->prefix . $key . '_forfill')) {
-            // we obtained the _forfill lock
-            // our caller will compute and set the value
-            return false;
-        }
-        // someone else has the lock
-        // "block" till we can get the value
-        // actually, loop .05s waiting for it
-        for ($n=0;$n<5;$n++) {
-            usleep(10000);
-            $rec = $get($this->prefix . $key);
-            if (!is_null($rec)) {
-                return unserialize($rec);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Release the exclusive lock obtained by
-     * getforfill(). See getforfill()
-     * for more details.
-     *
-     * @param $key string
-     * @return bool
-     */
-    function releaseforfill ($key) {
-        $unlock = $this->mode . '_unlock';
-        return $unlock($this->prefix . $key . '_forfill');
-    }
-}
-
-?>
\ No newline at end of file
index 96dede0..6c935ef 100644 (file)
@@ -3685,7 +3685,6 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
             if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'eventtype'=>'site'))) {
                 send_file_not_found();
             }
-            // Check that we got an event and that it's userid is that of the user
 
             // Get the file and serve if successful
             $filename = array_pop($args);
@@ -3733,8 +3732,8 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
                 require_login($course);
             }
 
-            // Must be able to at least view the course
-            if (!is_enrolled($context) and !is_viewing($context)) {
+            // Must be able to at least view the course. This does not apply to the front page.
+            if ($course->id != SITEID && (!is_enrolled($context)) && (!is_viewing($context))) {
                 //TODO: hmm, do we really want to block guests here?
                 send_file_not_found();
             }
@@ -3755,10 +3754,10 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
                 if (!has_capability('moodle/site:accessallgroups', $context) && !groups_is_member($event->groupid, $USER->id)) {
                     send_file_not_found();
                 }
-            } else if ($event->eventtype === 'course') {
-                //ok
+            } else if ($event->eventtype === 'course' || $event->eventtype === 'site') {
+                // Ok. Please note that the event type 'site' still uses a course context.
             } else {
-                // some other type
+                // Some other type.
                 send_file_not_found();
             }
 
index 86fa632..3a94c45 100644 (file)
@@ -115,7 +115,6 @@ YUI.add('moodle-form-dateselector', function(Y) {
             M.form.dateselector.currentowner = this;
             M.form.dateselector.calendar.cfg.setProperty('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1));
             M.form.dateselector.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31));
-            M.form.dateselector.panel.set('constrain', this.get('node').ancestor('form'));
             M.form.dateselector.panel.show();
             M.form.dateselector.fix_position();
             setTimeout(function(){M.form.dateselector.cancel_any_timeout()}, 100);
@@ -184,7 +183,6 @@ YUI.add('moodle-form-dateselector', function(Y) {
         initPanel : function(config) {
             this.panel = new Y.Overlay({
                 visible : false,
-                constrain : true,
                 bodyContent : Y.Node.create('<div id="dateselector-calendar-content"></div>'),
                 id : 'dateselector-calendar-panel'
             });
@@ -245,7 +243,6 @@ YUI.add('moodle-form-dateselector', function(Y) {
         },
         fix_position : function() {
             if (this.currentowner) {
-                this.panel.set('constrain', Y.one(document.body));
                 this.panel.set('align', {
                     node:this.currentowner.get('node').one('select'),
                     points:[Y.WidgetPositionAlign.BL, Y.WidgetPositionAlign.TL]
index 5e39e77..e5e1830 100644 (file)
@@ -2227,15 +2227,15 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
         // switch next two lines for ol li containers for form items.
         //        $this->_elementTemplates=array('default'=>"\n\t\t".'<li class="fitem"><label>{label}{help}<!-- BEGIN required -->{req}<!-- END required --></label><div class="qfelement<!-- BEGIN error --> error<!-- END error --> {type}"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></li>');
         $this->_elementTemplates = array(
-        'default'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</label></div><div class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
+        'default'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div><div class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
 
         'actionbuttons'=>"\n\t\t".'<div id="{id}" class="fitem fitem_actionbuttons fitem_{type}"><div class="felement {type}">{element}</div></div>',
 
-        'fieldset'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</label></div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>',
+        'fieldset'=>"\n\t\t".'<div id="{id}" class="fitem {advanced}<!-- BEGIN required --> required<!-- END required --> fitem_{type}"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>',
 
-        'static'=>"\n\t\t".'<div class="fitem {advanced}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</label></div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}&nbsp;</div></div>',
+        'static'=>"\n\t\t".'<div class="fitem {advanced}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg}{help} </label></div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}&nbsp;</div></div>',
 
-'warning'=>"\n\t\t".'<div class="fitem {advanced}">{element}</div>',
+        'warning'=>"\n\t\t".'<div class="fitem {advanced}">{element}</div>',
 
         'nodisplay'=>'');
 
index fa0dfc0..6044ea1 100644 (file)
@@ -111,7 +111,7 @@ M.util.CollapsibleRegion = function(Y, id, userpref, strtooltip) {
     // Get the height of the div at this point before we shrink it if required
     var height = this.div.get('offsetHeight');
     var collapsedimage = 't/collapsed'; // ltr mode
-    if ( Y.one(document.body).hasClass('dir-rtl') ) {
+    if (right_to_left()) {
         collapsedimage = 't/collapsed_rtl';
     } else {
         collapsedimage = 't/collapsed';
@@ -140,7 +140,7 @@ M.util.CollapsibleRegion = function(Y, id, userpref, strtooltip) {
     animation.on('end', function() {
         this.div.toggleClass('collapsed');
         var collapsedimage = 't/collapsed'; // ltr mode
-        if ( Y.one(document.body).hasClass('dir-rtl') ) {
+        if (right_to_left()) {
             collapsedimage = 't/collapsed_rtl';
             } else {
             collapsedimage = 't/collapsed';
@@ -1209,6 +1209,20 @@ function getElementsByClassName(oElm, strTagName, name) {
     return (arrReturnElements)
 }
 
+/**
+ * Return whether we are in right to left mode or not.
+ *
+ * @return boolean
+ */
+function right_to_left() {
+    var body = Y.one('body');
+    var rtl = false;
+    if (body && body.hasClass('dir-rtl')) {
+        rtl = true;
+    }
+    return rtl;
+}
+
 function openpopup(event, args) {
 
     if (event) {
diff --git a/lib/memcached.class.php b/lib/memcached.class.php
deleted file mode 100644 (file)
index 35ac0f4..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * @package    core
- * @subpackage lib
- * @copyright  Martin Langhoff <martin@catalyst.net.nz>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * This class abstracts PHP's PECL memcached
- * API to provide
- *
- * - get()
- * - set()
- * - delete()
- * - getforfill()
- * - releaseforfill()
- *
- * Author: Martin Langhoff <martin@catalyst.net.nz>
- *
- * Note: do NOT store booleans here. With memcached, a false value
- * is indistinguisable from a "not found in cache" response.
- *
- * @package   moodlecore
- * @copyright Martin Langhoff <martin@catalyst.net.nz>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- **/
-class memcached {
-
-    function memcached() {
-        global $CFG;
-
-        if (!function_exists('memcache_connect')) {
-            debugging("Memcached is set to true but the memcached extension is not installed");
-            return false;
-        }
-        $this->_cache = new Memcache;
-
-        $hosts = explode(',', $CFG->memcachedhosts);
-        if (count($hosts) === 1 && !empty($CFG->memcachedpconn)) {
-            // the faster pconnect is only available
-            // for single-server setups
-            // NOTE: PHP-PECL client is buggy and pconnect()
-            // will segfault if the server is unavailable
-            $this->_cache->pconnect($hosts[0]);
-        } else {
-            // multi-host setup will share key space
-            foreach ($hosts as $host) {
-                $host = trim($host);
-                $this->_cache->addServer($host);
-            }
-        }
-
-        $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
-    }
-
-    function status() {
-        if (is_object($this->_cache)) {
-            return true;
-        }
-        return false;
-    }
-
-    function set($key, $value, $ttl=0) {
-
-        // we may have acquired a lock via getforfill
-        // release if it exists
-        @$this->_cache->delete($this->prefix . $key . '_forfill');
-
-        return $this->_cache->set($this->prefix . $key, $value, false);
-    }
-
-    function get($key) {
-        $rec = $this->_cache->get($this->prefix . $key);
-        return $rec;
-    }
-
-    function delete($key) {
-        return $this->_cache->delete($this->prefix . $key);
-    }
-
-    /**
-     * In the simple case, this function will
-     * get the cached value if available. If the entry
-     * is not cached, it will try to get an exclusive
-     * lock that announces that this process will
-     * populate the cache.
-     *
-     * If we fail to get the lock -- this means another
-     * process is doing it.
-     * so we wait (block) for a few microseconds while we wait for
-     * the cache to be filled or the lock to timeout.
-     *
-     * If you get a false from this call, you _must_
-     * populate the cache ASAP or indicate that
-     * you won't by calling releaseforfill().
-     *
-     * This technique forces serialisation and so helps deal
-     * with thundering herd scenarios where a lot of clients
-     * ask the for the same idempotent (and costly) operation.
-     * The implementation is based on suggestions in this message
-     * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
-     *
-     * @param $key string
-     * @return mixed on cache hit, NULL otherwise
-     */
-    function getforfill ($key) {
-
-        $rec = $this->_cache->get($this->prefix . $key);
-        if ($rec) {
-            return $rec;
-        }
-        if ($this->_cache->add($this->prefix . $key . '_forfill', 'true', false, 1)) {
-            // we obtained the _forfill lock
-            // our caller will compute and set the value
-            return false;
-        }
-        // someone else has the lock
-        // "block" till we can get the value
-        // actually, loop .05s waiting for it
-        for ($n=0;$n<5;$n++) {
-            usleep(10000);
-            $rec = $this->_cache->get($this->prefix . $key);
-            if ($rec) {
-                return $rec;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Release the exclusive lock obtained by
-     * getforfill(). See getforfill()
-     * for more details.
-     *
-     * @param $key string
-     * @return bool
-     */
-    function releaseforfill ($key) {
-        return $this->_cache->delete($this->prefix . $key . '_forfill');
-    }
-}
index 3ab00ea..d464106 100644 (file)
@@ -155,10 +155,11 @@ function message_send($eventdata) {
         if (isset($defaultpreferences->{$defaultpreference})) {
             $permitted = $defaultpreferences->{$defaultpreference};
         } else {
-            //MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
-            //exist in the message_provider table (thus there is no default settings for them)
-            $preferrormsg = get_string('couldnotfindpreference', 'message', $defaultpreference);
-            throw new coding_exception($preferrormsg,'blah');
+            // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
+            // exist in the message_provider table (thus there is no default settings for them).
+            $preferrormsg = "Could not load preference $defaultpreference. Make sure the component and name you supplied
+                    to message_send() are valid.";
+            throw new coding_exception($preferrormsg);
         }
 
         // Find out if user has configured this output
index ffe26a7..a9f4b24 100644 (file)
@@ -1358,7 +1358,7 @@ function get_fast_modinfo($courseorid, $userid = 0, $resetonly = false) {
  * @param boolean $clearonly - only clear the modinfo fields, gets rebuild automatically on the fly
  */
 function rebuild_course_cache($courseid=0, $clearonly=false) {
-    global $COURSE, $DB, $CFG;
+    global $COURSE, $SITE, $DB, $CFG;
 
     // Destroy navigation caches
     navigation_cache::destroy_volatile_caches();
@@ -1382,6 +1382,10 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
             $COURSE->modinfo = null;
             $COURSE->sectioncache = null;
         }
+        if ($courseid == $SITE->id) {
+            $SITE->modinfo = null;
+            $SITE->sectioncache = null;
+        }
         // reset the fast modinfo cache
         get_fast_modinfo($courseid, 0, true);
         return;
@@ -1408,6 +1412,10 @@ function rebuild_course_cache($courseid=0, $clearonly=false) {
             $COURSE->modinfo = $modinfo;
             $COURSE->sectioncache = $sectioncache;
         }
+        if ($course->id == $SITE->id) {
+            $SITE->modinfo = $modinfo;
+            $SITE->sectioncache = $sectioncache;
+        }
     }
     $rs->close();
     // reset the fast modinfo cache
index 7279079..9236a11 100644 (file)
@@ -1589,7 +1589,7 @@ function set_cache_flag($type, $name, $value, $expiry=NULL) {
 
     if ($f = $DB->get_record('cache_flags', array('name'=>$name, 'flagtype'=>$type), '*', IGNORE_MULTIPLE)) { // this is a potential problem in DEBUG_DEVELOPER
         if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) {
-            return true; //no need to update; helps rcache too
+            return true; //no need to update
         }
         $f->value        = $value;
         $f->expiry       = $expiry;
@@ -2810,7 +2810,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $
     }
 
     // Redirect to the login page if session has expired, only with dbsessions enabled (MDL-35029) to maintain current behaviour.
-    if (!empty($SESSION->has_timed_out) && !$preventredirect && !empty($CFG->dbsessions)) {
+    if ((!isloggedin() or isguestuser()) && !empty($SESSION->has_timed_out) && !$preventredirect && !empty($CFG->dbsessions)) {
         if ($setwantsurltome) {
             $SESSION->wantsurl = qualified_me();
         }
@@ -10482,15 +10482,6 @@ function get_performance_info() {
         $info['txt'] .= "Session: {$info['sessionsize']} ";
     }
 
-/*    if (isset($rcache->hits) && isset($rcache->misses)) {
-        $info['rcachehits'] = $rcache->hits;
-        $info['rcachemisses'] = $rcache->misses;
-        $info['html'] .= '<span class="rcache">Record cache hit/miss ratio : '.
-            "{$rcache->hits}/{$rcache->misses}</span> ";
-        $info['txt'] .= 'rcache: '.
-            "{$rcache->hits}/{$rcache->misses} ";
-    }*/
-
     if ($stats = cache_helper::get_stats()) {
         $html = '<span class="cachesused">';
         $html .= '<span class="cache-stats-heading">Caches interaction by definition then store</span>';
index 45d9618..55d38f1 100644 (file)
@@ -1488,7 +1488,7 @@ class core_renderer extends renderer_base {
         $output .= html_writer::select($select->options, $select->name, $select->selected, $select->nothing, $select->attributes);
 
         $go = html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('go')));
-        $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style'=>'inline'));
+        $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('class' => 'inline'));
 
         $nothing = empty($select->nothing) ? false : key($select->nothing);
         $this->page->requires->js_init_call('M.util.init_select_autosubmit', array($select->formid, $select->attributes['id'], $nothing));
@@ -1612,7 +1612,7 @@ class core_renderer extends renderer_base {
 
         if (!$select->showbutton) {
             $go = html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('go')));
-            $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style'=>'inline'));
+            $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('class' => 'inline'));
             $nothing = empty($select->nothing) ? false : key($select->nothing);
             $output .= $this->page->requires->js_init_call('M.util.init_select_autosubmit', array($select->formid, $select->attributes['id'], $nothing));
         } else {
index 93f28f9..7e43c10 100644 (file)
@@ -566,4 +566,39 @@ EOD;
 
         return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST);
     }
+
+    /**
+     * Simplified enrolment of user to course using default options.
+     *
+     * It is strongly recommended to use only this method for 'manual' and 'self' plugins only!!!
+     *
+     * @param int $userid
+     * @param int $courseid
+     * @param int $roleid optional role id, use only with manual plugin
+     * @param string $enrol name of enrol plugin,
+     *     there must be exactly one instance in course,
+     *     it must support enrol_user() method.
+     * @return bool success
+     */
+    public function enrol_user($userid, $courseid, $roleid = null, $enrol = 'manual') {
+        global $DB;
+
+        if (!$plugin = enrol_get_plugin($enrol)) {
+            return false;
+        }
+
+        $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'enrol'=>$enrol));
+        if (count($instances) != 1) {
+            return false;
+        }
+        $instance = reset($instances);
+
+        if (is_null($roleid) and $instance->roleid) {
+            $roleid = $instance->roleid;
+        }
+
+        $plugin->enrol_user($instance, $userid, $roleid);
+
+        return true;
+    }
 }
index 33ec793..4f68206 100644 (file)
@@ -88,10 +88,28 @@ class Hint_ResultPrinter extends PHPUnit_TextUI_ResultPrinter {
             $file = substr($file, strlen($cwd)+1);
         }
 
-        $executable = 'phpunit';
-        if (phpunit_bootstrap_is_cygwin()) {
-            $file = str_replace('\\', '/', $file);
-            $executable = 'phpunit.bat';
+        $executable = null;
+
+        if (isset($_SERVER['argv'][0])) {
+            if (preg_match('/phpunit(\.bat|\.cmd)?$/', $_SERVER['argv'][0])) {
+                $executable = $_SERVER['argv'][0];
+                for($i=1;$i<count($_SERVER['argv']);$i++) {
+                    if (!isset($_SERVER['argv'][$i])) {
+                        break;
+                    }
+                    if (in_array($_SERVER['argv'][$i], array('--colors', '--verbose', '-v', '--debug', '--strict'))) {
+                        $executable .= ' '.$_SERVER['argv'][$i];
+                    }
+                }
+            }
+        }
+
+        if (!$executable) {
+            $executable = 'phpunit';
+            if (phpunit_bootstrap_is_cygwin()) {
+                $file = str_replace('\\', '/', $file);
+                $executable = 'phpunit.bat';
+            }
         }
 
         $this->write("\nTo re-run:\n $executable $testName $file\n");
index aae6919..ffa20f9 100644 (file)
@@ -83,6 +83,7 @@ abstract class phpunit_module_generator {
 
         $modulename = $this->get_modulename();
         $sectionnum = isset($options['section']) ? $options['section'] : 0;
+        unset($options['section']); // Prevent confusion, it would be overridden later in course_add_cm_to_section() anyway.
 
         $cm = new stdClass();
         $cm->course             = $courseid;
index 5af9c06..132c4db 100644 (file)
@@ -5,22 +5,35 @@ PHPUnit testing support in Moodle
 Documentation
 -------------
 * [Moodle Dev wiki](http://docs.moodle.org/dev/PHPUnit)
-* [PHPUnit online documentaion](http://www.phpunit.de/manual/current/en/)
+* [PHPUnit online documentation](http://www.phpunit.de/manual/current/en/)
+* [Composer dependency manager](http://getcomposer.org/)
 
 
-Installation
-------------
+Composer installation
+---------------------
+Composer is a new dependency manager for PHP projects.
+It installs PHP libraries into /vendor/ subdirectory inside your moodle dirroot.
+
+1. install Composer - http://getcomposer.org/doc/00-intro.md
+2. go to your moodle dirroot and execute `php composer.phar install`
+
+
+PEAR installation (not recommended)
+-----------------------------------
+PEAR is a framework and distribution system for reusable PHP components.
+The packages installed via PEAR are available in all PHP projects.
+
 1. install PEAR package manager - see [PEAR Manual](http://pear.php.net/manual/en/installation.php)
 2. install PHPUnit package and phpunit/DbUnit extension - see [PHPUnit installation documentation](http://www.phpunit.de/manual/current/en/installation.html)
 3. edit main config.php - add `$CFG->phpunit_prefix` and `$CFG->phpunit_dataroot` - see config-dist.php
-4. execute `php admin/tool/phpunit/cli/init.php` to initialise the test environemnt, repeat it after every upgrade or installation of plugins
+4. execute `php admin/tool/phpunit/cli/init.php` to initialise the test environment, repeat it after every upgrade or installation of plugins
 
 
 Test execution
 --------------
-* execute `phpunit` from dirroot directory
-* you can execute a single test case class using class name followed by path to test file `phpunit core_phpunit_basic_testcase lib/tests/phpunit_test.php`
-* it is also possible to create custom configuration files in xml format and use `phpunit -c mytestsuites.xml`
+* execute `vendor/bin/phpunit` (or `phpunit` if you use PEAR) from dirroot directory
+* you can execute a single test case class using class name followed by path to test file `vendor/bin/phpunit core_phpunit_basic_testcase lib/tests/phpunit_test.php`
+* it is also possible to create custom configuration files in xml format and use `vendor/bin/phpunit -c mytestsuites.xml`
 
 
 How to add more tests?
index 1a3efc3..0213875 100644 (file)
@@ -100,6 +100,13 @@ class core_phpunit_generator_testcase extends advanced_testcase {
 
         $page = $generator->create_module('page', array('course'=>$SITE->id));
         $this->assertNotEmpty($page);
+        $cm = get_coursemodule_from_instance('page', $page->id, $SITE->id, true);
+        $this->assertEquals(0, $cm->sectionnum);
+
+        $page = $generator->create_module('page', array('course'=>$SITE->id), array('section'=>3));
+        $this->assertNotEmpty($page);
+        $cm = get_coursemodule_from_instance('page', $page->id, $SITE->id, true);
+        $this->assertEquals(3, $cm->sectionnum);
     }
 
     public function test_create_block() {
@@ -114,4 +121,86 @@ class core_phpunit_generator_testcase extends advanced_testcase {
         $page = $generator->create_block('online_users');
         $this->assertNotEmpty($page);
     }
+
+    public function test_enrol_user() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $selfplugin = enrol_get_plugin('self');
+        $this->assertNotEmpty($selfplugin);
+
+        $manualplugin = enrol_get_plugin('manual');
+        $this->assertNotEmpty($manualplugin);
+
+        // Prepare some data.
+
+        $studentrole = $DB->get_record('role', array('shortname'=>'student'));
+        $this->assertNotEmpty($studentrole);
+        $teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
+        $this->assertNotEmpty($teacherrole);
+
+        $course1 = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $course3 = $this->getDataGenerator()->create_course();
+
+        $context1 = context_course::instance($course1->id);
+        $context2 = context_course::instance($course2->id);
+        $context3 = context_course::instance($course3->id);
+
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+
+        $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'self')));
+        $instance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'self'), '*', MUST_EXIST);
+        $instance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'self'), '*', MUST_EXIST);
+        $instance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'self'), '*', MUST_EXIST);
+
+        $this->assertEquals($studentrole->id, $instance1->roleid);
+        $this->assertEquals($studentrole->id, $instance2->roleid);
+        $this->assertEquals($studentrole->id, $instance3->roleid);
+
+        $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'manual')));
+        $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $maninstance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
+        $maninstance3->roleid = $teacherrole->id;
+        $DB->update_record('enrol', $maninstance3, array('id'=>$maninstance3->id));
+
+        $this->assertEquals($studentrole->id, $maninstance1->roleid);
+        $this->assertEquals($studentrole->id, $maninstance2->roleid);
+        $this->assertEquals($teacherrole->id, $maninstance3->roleid);
+
+        $result = $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
+        $this->assertTrue($result);
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id)));
+
+        $result = $this->getDataGenerator()->enrol_user($user1->id, $course2->id, $teacherrole->id, 'manual');
+        $this->assertTrue($result);
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance2->id, 'userid'=>$user1->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user1->id, 'roleid'=>$teacherrole->id)));
+
+        $result = $this->getDataGenerator()->enrol_user($user1->id, $course3->id, 0, 'manual');
+        $this->assertTrue($result);
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance3->id, 'userid'=>$user1->id)));
+        $this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user1->id)));
+
+
+        $result = $this->getDataGenerator()->enrol_user($user2->id, $course1->id, null, 'self');
+        $this->assertTrue($result);
+        $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$instance1->id, 'userid'=>$user2->id)));
+        $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id)));
+
+
+        $selfplugin->add_instance($course2, array('status'=>ENROL_INSTANCE_ENABLED, 'roleid'=>$teacherrole->id));
+        $result = $this->getDataGenerator()->enrol_user($user2->id, $course2->id, null, 'self');
+        $this->assertFalse($result);
+
+        $DB->delete_records('enrol', array('enrol'=>'self', 'courseid'=>$course3->id));
+        $result = $this->getDataGenerator()->enrol_user($user2->id, $course3->id, null, 'self');
+        $this->assertFalse($result);
+
+    }
 }
index a449bc6..7383d7b 100644 (file)
@@ -993,7 +993,7 @@ class available_update_checker {
         if (!empty($CFG->config_php_settings['alternativeupdateproviderurl'])) {
             return $CFG->config_php_settings['alternativeupdateproviderurl'];
         } else {
-            return 'http://download.moodle.org/api/1.1/updates.php';
+            return 'https://download.moodle.org/api/1.1/updates.php';
         }
     }
 
index 1e7e927..123c70e 100644 (file)
@@ -41,6 +41,12 @@ function session_get_instance() {
 
     static $session = null;
 
+    if (!defined('NO_MOODLE_COOKIES')) {
+        // Moodle session was not initialised yet in lib/setup.php.
+        $session = new emergency_session();
+        return $session;
+    }
+
     if (is_null($session)) {
         if (empty($CFG->sessiontimeout)) {
             $CFG->sessiontimeout = 7200;
@@ -467,6 +473,9 @@ class database_session extends session_stub {
     /** @var bool $failed session read/init failed, do not write back to DB */
     protected $failed   = false;
 
+    /** @var string hash of the session data content */
+    protected $lasthash = null;
+
     public function __construct() {
         global $DB;
         $this->database = $DB;
@@ -484,7 +493,7 @@ class database_session extends session_stub {
 
     /**
      * Check for existing session with id $sid
-     * @param unknown_type $sid
+     * @param string $sid
      * @return boolean true if session found.
      */
     public function session_exists($sid){
@@ -572,7 +581,8 @@ class database_session extends session_stub {
         }
 
         try {
-            if (!$record = $this->database->get_record('sessions', array('sid'=>$sid))) {
+            // Do not fetch full record yet, wait until it is locked.
+            if (!$record = $this->database->get_record('sessions', array('sid'=>$sid), 'id, userid')) {
                 $record = new stdClass();
                 $record->state        = 0;
                 $record->sid          = $sid;
@@ -590,7 +600,14 @@ class database_session extends session_stub {
         }
 
         try {
-            $this->database->get_session_lock($record->id, SESSION_ACQUIRE_LOCK_TIMEOUT);
+            if (!empty($CFG->sessionlockloggedinonly) and (isguestuser($record->userid) or empty($record->userid))) {
+                // No session locking for guests and not-logged-in users,
+                // these users mostly read stuff, there should not be any major
+                // session race conditions. Hopefully they do not access other
+                // pages while being logged-in.
+            } else {
+                $this->database->get_session_lock($record->id, SESSION_ACQUIRE_LOCK_TIMEOUT);
+            }
         } catch (Exception $ex) {
             // This is a fatal error, better inform users.
             // It should not happen very often - all pages that need long time to execute
@@ -600,6 +617,13 @@ class database_session extends session_stub {
             throw $ex;
         }
 
+        // Finally read the full session data because we know we have the lock now.
+        if (!$record = $this->database->get_record('sessions', array('id'=>$record->id))) {
+            error_log('Cannot read session record');
+            $this->failed = true;
+            return '';
+        }
+
         // verify timeout
         if ($record->timemodified + $CFG->sessiontimeout < time()) {
             $ignoretimeout = false;
@@ -650,7 +674,13 @@ class database_session extends session_stub {
             }
         }
 
-        $data = is_null($record->sessdata) ? '' : base64_decode($record->sessdata);
+        if (is_null($record->sessdata)) {
+            $data = '';
+            $this->lasthash = sha1('');
+        } else {
+            $data = base64_decode($record->sessdata);
+            $this->lasthash = sha1($record->sessdata);
+        }
 
         unset($record->sessdata); // conserve memory
         $this->record = $record;
@@ -688,16 +718,28 @@ class database_session extends session_stub {
         }
 
         if (isset($this->record->id)) {
-            $this->record->sessdata     = base64_encode($session_data); // there might be some binary mess :-(
+            $data = base64_encode($session_data);  // There might be some binary mess :-(
+
+            // Skip db update if nothing changed,
+            // do not update the timemodified each second.
+            $hash = sha1($data);
+            if ($this->lasthash === $hash
+                and $this->record->userid == $userid
+                and (time() - $this->record->timemodified < 20)
+                and $this->record->lastip == getremoteaddr()
+            ) {
+                // No need to update anything!
+                return true;
+            }
+
+            $this->record->sessdata     = $data;
             $this->record->userid       = $userid;
             $this->record->timemodified = time();
             $this->record->lastip       = getremoteaddr();
 
-            // TODO: verify session changed before doing update,
-            //       also make sure the timemodified field is changed only every 10s if nothing else changes  MDL-20462
-
             try {
                 $this->database->update_record_raw('sessions', $this->record);
+                $this->lasthash = $hash;
             } catch (dml_exception $ex) {
                 if ($this->database->get_dbfamily() === 'mysql') {
                     try {
@@ -725,7 +767,9 @@ class database_session extends session_stub {
                 $record->timecreated  = $record->timemodified = time();
                 $record->firstip      = $record->lastip = getremoteaddr();
                 $record->id           = $this->database->insert_record_raw('sessions', $record);
-                $this->record = $record;
+
+                $this->record = $this->database->get_record('sessions', array('id'=>$record->id));
+                $this->lasthash = sha1($record->sessdata);
 
                 $this->database->get_session_lock($this->record->id, SESSION_ACQUIRE_LOCK_TIMEOUT);
             } catch (Exception $ex) {
@@ -759,6 +803,8 @@ class database_session extends session_stub {
             $this->record = null;
         }
 
+        $this->lasthash = null;
+
         return true;
     }
 
index 90c5c73..1b53e75 100644 (file)
@@ -344,13 +344,6 @@ global $COURSE;
  */
 global $OUTPUT;
 
-/**
- * Shared memory cache.
- * @global object $MCACHE
- * @name $MCACHE
- */
-global $MCACHE;
-
 /**
  * Cache used within grouplib to cache data within current request only.
  *
@@ -592,42 +585,6 @@ if (!empty($CFG->version) and $CFG->version < 2007101509) {
     die;
 }
 
-// Shared-Memory cache init -- will set $MCACHE
-// $MCACHE is a global object that offers at least add(), set() and delete()
-// with similar semantics to the memcached PHP API http://php.net/memcache
-// Ensure we define rcache - so we can later check for it
-// with a really fast and unambiguous $CFG->rcache === false
-if (!empty($CFG->cachetype)) {
-    if (empty($CFG->rcache)) {
-        $CFG->rcache = false;
-    } else {
-        $CFG->rcache = true;
-    }
-
-    // do not try to initialize if cache disabled
-    if (!$CFG->rcache) {
-        $CFG->cachetype = '';
-    }
-
-    if ($CFG->cachetype === 'memcached' && !empty($CFG->memcachedhosts)) {
-        if (!init_memcached()) {
-            debugging("Error initialising memcached");
-            $CFG->cachetype = '';
-            $CFG->rcache = false;
-        }
-    } else if ($CFG->cachetype === 'eaccelerator') {
-        if (!init_eaccelerator()) {
-            debugging("Error initialising eaccelerator cache");
-            $CFG->cachetype = '';
-            $CFG->rcache = false;
-        }
-    }
-
-} else { // just make sure it is defined
-    $CFG->cachetype = '';
-    $CFG->rcache    = false;
-}
-
 // Calculate and set $CFG->ostype to be used everywhere. Possible values are:
 // - WINDOWS: for any Windows flavour.
 // - UNIX: for the rest
index 33894ad..d100654 100644 (file)
@@ -1292,41 +1292,6 @@ function make_cache_directory($directory, $exceptiononerror = true) {
     return make_writable_directory("$CFG->cachedir/$directory", $exceptiononerror);
 }
 
-
-/**
- * Initialises an Memcached instance
- * @global memcached $MCACHE
- * @return boolean Returns true if an mcached instance could be successfully initialised
- */
-function init_memcached() {
-    global $CFG, $MCACHE;
-
-    include_once($CFG->libdir . '/memcached.class.php');
-    $MCACHE = new memcached;
-    if ($MCACHE->status()) {
-        return true;
-    }
-    unset($MCACHE);
-    return false;
-}
-
-/**
- * Initialises an eAccelerator instance
- * @global eaccelerator $MCACHE
- * @return boolean Returns true if an eAccelerator instance could be successfully initialised
- */
-function init_eaccelerator() {
-    global $CFG, $MCACHE;
-
-    include_once($CFG->libdir . '/eaccelerator.class.php');
-    $MCACHE = new eaccelerator;
-    if ($MCACHE->status()) {
-        return true;
-    }
-    unset($MCACHE);
-    return false;
-}
-
 /**
  * Checks if current user is a web crawler.
  *
@@ -1349,12 +1314,18 @@ function is_web_crawler() {
             return true;
         } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) {  // Zoomspider
             return true;
-        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) {  // MSN Search
+        } else if (stripos($_SERVER['HTTP_USER_AGENT'], 'msnbot') !== false ) {  // MSN Search
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'bingbot') !== false ) {  // Bing
             return true;
         } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yandex') !== false ) {
             return true;
         } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'AltaVista') !== false ) {
             return true;
+        } else if (stripos($_SERVER['HTTP_USER_AGENT'], 'baiduspider') !== false ) {  // Baidu
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Teoma') !== false ) {  // Ask.com
+            return true;
         }
     }
     return false;
index 4281232..bfb9df9 100644 (file)
@@ -1153,10 +1153,10 @@ class flexible_table {
 
         if ($order == SORT_ASC) {
             return html_writer::empty_tag('img',
-                    array('src' => $OUTPUT->pix_url('t/down'), 'alt' => get_string('asc')));
+                    array('src' => $OUTPUT->pix_url('t/sort_asc'), 'alt' => get_string('asc'), 'class' => 'iconsort'));
         } else {
             return html_writer::empty_tag('img',
-                    array('src' => $OUTPUT->pix_url('t/up'), 'alt' => get_string('desc')));
+                    array('src' => $OUTPUT->pix_url('t/sort_desc'), 'alt' => get_string('desc'), 'class' => 'iconsort'));
         }
     }
 
index 9695adb..becf244 100644 (file)
@@ -1769,7 +1769,8 @@ class accesslib_testcase extends advanced_testcase {
 
         context_helper::reset_caches();
         context_helper::preload_course($SITE->id);
-        $this->assertEquals(7, context_inspection::test_context_cache_size()); // depends on number of default blocks
+        $numfrontpagemodules = $DB->count_records('course_modules', array('course' => $SITE->id));
+        $this->assertEquals(6 + $numfrontpagemodules, context_inspection::test_context_cache_size()); // depends on number of default blocks
 
         // ====== assign_capability(), unassign_capability() ====================
 
@@ -2074,7 +2075,8 @@ class accesslib_testcase extends advanced_testcase {
         load_all_capabilities();
         $context = context_course::instance($testcourses[2]);
         $page = $DB->get_record('page', array('course'=>$testcourses[2]));
-        $pagecontext = context_module::instance($page->id);
+        $pagecm = get_coursemodule_from_instance('page', $page->id);
+        $pagecontext = context_module::instance($pagecm->id);
 
         $context->mark_dirty();
         $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path]));
@@ -2312,7 +2314,8 @@ class accesslib_testcase extends advanced_testcase {
 
         context_helper::reset_caches();
         preload_course_contexts($SITE->id);
-        $this->assertEquals(context_inspection::test_context_cache_size(), 1);
+        $this->assertEquals(1 + $DB->count_records('course_modules', array('course' => $SITE->id)),
+                context_inspection::test_context_cache_size());
 
         context_helper::reset_caches();
         list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx');
@@ -2358,11 +2361,11 @@ class accesslib_testcase extends advanced_testcase {
         $url = get_context_url($coursecontext);
         $this->assertFalse($url instanceof modole_url);
 
-        $page = $DB->get_record('page', array('id'=>$testpages[7]));
-        $context = context_module::instance($page->id);
+        $pagecm = get_coursemodule_from_instance('page', $testpages[7]);
+        $context = context_module::instance($pagecm->id);
         $coursecontext = get_course_context($context);
         $this->assertEquals($coursecontext->contextlevel, CONTEXT_COURSE);
-        $this->assertEquals(get_courseid_from_context($context), $page->course);
+        $this->assertEquals(get_courseid_from_context($context), $pagecm->course);
 
         $caps = fetch_context_capabilities($systemcontext);
         $this->assertTrue(is_array($caps));
index c3b796b..60ba0b9 100644 (file)
@@ -151,7 +151,7 @@ class user_picture_testcase extends advanced_testcase {
         // try legacy picture == 1
         $user1->picture = 1;
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=1', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=1', $up1->get_url($page, $renderer)->out(false));
         $user1->picture = 11;
 
         // try valid user with picture when user context is not cached - 1 query expected
@@ -159,7 +159,7 @@ class user_picture_testcase extends advanced_testcase {
         $reads = $DB->perf_get_reads();
         $up1 = new user_picture($user1);
         $this->assertEquals($reads, $DB->perf_get_reads());
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
         $this->assertEquals($reads+1, $DB->perf_get_reads());
 
         // try valid user with contextid hint - no queries expected
@@ -168,7 +168,7 @@ class user_picture_testcase extends advanced_testcase {
         $reads = $DB->perf_get_reads();
         $up1 = new user_picture($user1);
         $this->assertEquals($reads, $DB->perf_get_reads());
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
         $this->assertEquals($reads, $DB->perf_get_reads());
 
         // try valid user without image - no queries expected
@@ -217,13 +217,13 @@ class user_picture_testcase extends advanced_testcase {
         $this->assertEquals('http://www.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=http%3A%2F%2Fwww.example.com%2Fmoodle%2Fpix%2Fu%2Ff2.png', $up2->get_url($page, $renderer)->out(false));
         // uploaded image takes precedence before gravatar
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 
         // https version
         $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
 
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->httpswwwroot.'/pluginfile.php/15/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->httpswwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/standard/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 
         $up3 = new user_picture($user3);
         $this->assertEquals($CFG->httpswwwroot.'/theme/image.php/standard/core/1/u/f2', $up3->get_url($page, $renderer)->out(false));
@@ -261,7 +261,7 @@ class user_picture_testcase extends advanced_testcase {
         $renderer = $page->get_renderer('core');
 
         $up1 = new user_picture($user1);
-        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/15/user/icon/formal_white/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
+        $this->assertEquals($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/formal_white/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 
         $up2 = new user_picture($user2);
         $this->assertEquals($CFG->wwwroot.'/theme/image.php/formal_white/core/1/u/f2', $up2->get_url($page, $renderer)->out(false));
index 243f2df..a7158e7 100644 (file)
@@ -187,7 +187,10 @@ class moodle_page_test extends advanced_testcase {
     }
 
     public function test_pagetype_defaults_to_script() {
+        global $SCRIPT;
         // Exercise SUT and validate
+        $SCRIPT = '/index.php';
+        $this->testpage->initialise_default_pagetype();
         $this->assertEquals('site-index', $this->testpage->pagetype);
     }
 
index 7008e58..30a257b 100644 (file)
@@ -71,4 +71,51 @@ class core_setuplib_testcase extends basic_testcase {
         $this->assertEquals($CFG->wwwroot . '/lib/tests/setuplib_test.php',
                 get_docs_url('%%WWWROOT%%/lib/tests/setuplib_test.php'));
     }
+
+    public function test_is_web_crawler() {
+        $browsers = array(
+            'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))',
+            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:18.0) Gecko/18.0 Firefox/18.0',
+            'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412 (KHTML, like Gecko) Safari/412',
+            'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10',
+            'Opera/9.0 (Windows NT 5.1; U; en)',
+            'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17 –Nexus',
+            'Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5',
+        );
+        $crawlers = array(
+            // Google
+            'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
+            'Googlebot/2.1 (+http://www.googlebot.com/bot.html)',
+            'Googlebot-Image/1.0',
+            // Yahoo
+            'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)',
+            // Bing
+            'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)',
+            'Mozilla/5.0 (compatible; bingbot/2.0 +http://www.bing.com/bingbot.htm)',
+            // MSN
+            'msnbot/2.1',
+            // Yandex
+            'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)',
+            'Mozilla/5.0 (compatible; YandexImages/3.0; +http://yandex.com/bots)',
+            // AltaVista
+            'AltaVista V2.0B crawler@evreka.com',
+            // ZoomSpider
+            'ZoomSpider - wrensoft.com [ZSEBOT]',
+            // Baidu
+            'Baiduspider+(+http://www.baidu.com/search/spider_jp.html)',
+            'Baiduspider+(+http://www.baidu.com/search/spider.htm)',
+            'BaiDuSpider',
+            // Ask.com
+            'User-Agent: Mozilla/2.0 (compatible; Ask Jeeves/Teoma)',
+        );
+
+        foreach ($browsers as $agent) {
+            $_SERVER['HTTP_USER_AGENT'] = $agent;
+            $this->assertFalse(is_web_crawler());
+        }
+        foreach ($crawlers as $agent) {
+            $_SERVER['HTTP_USER_AGENT'] = $agent;
+            $this->assertTrue(is_web_crawler(), "$agent should be considered a search engine");
+        }
+    }
 }
index 1c877b8..4960847 100644 (file)
@@ -43,8 +43,7 @@ YUI.add('moodle-core-chooserdialogue', function(Y) {
                 draggable : true,
                 visible : false, // Hide by default
                 zindex : 100, // Display in front of other items
-                lightbox : true, // This dialogue should be modal
-                shim : true
+                lightbox : true // This dialogue should be modal
             };
 
             // Override with additional options
index 964123a..84be3a2 100644 (file)
@@ -1210,7 +1210,7 @@ function message_print_search_results($frm, $showicontext=false, $currentuser=nu
                 echo html_writer::end_tag('td');
 
                 echo html_writer::start_tag('td', array('class' => 'summary'));
-                echo message_get_fragment($message->fullmessage, $keywords);
+                echo message_get_fragment($message->smallmessage, $keywords);
                 echo html_writer::start_tag('div', array('class' => 'link'));
 
                 //If the user clicks the context link display message sender on the left
@@ -1595,10 +1595,10 @@ function message_search($searchterms, $fromme=true, $tome=true, $courseid='none'
     ///    c.  Messages to and from user
 
     if ($courseid == SITEID) { /// admin is searching all messages
-        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message_read} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
-        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
 
@@ -1623,10 +1623,10 @@ function message_search($searchterms, $fromme=true, $tome=true, $courseid='none'
             $params['userid'] = $userid;
         }
 
-        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message_read} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
-        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
 
index baf8f54..1d50651 100644 (file)
@@ -220,8 +220,25 @@ abstract class assign_plugin {
         return $this->get_config('enabled');
     }
 
+
+    /**
+     * Get any additional fields for the submission/grading form for this assignment.
+     *
+     * @param mixed $submissionorgrade submission|grade - For submission plugins this is the submission data,
+     *                                                    for feedback plugins it is the grade data
+     * @param MoodleQuickForm $mform - This is the form
+     * @param stdClass $data - This is the form data that can be modified for example by a filemanager element
+     * @param int $userid - This is the userid for the current submission.
+     *                      This is passed separately as there may not yet be a submission or grade.
+     * @return boolean - true if we added anything to the form
+     */
+    public function get_form_elements_for_user($submissionorgrade, MoodleQuickForm $mform, stdClass $data, $userid) {
+        return $this->get_form_elements($submissionorgrade, $mform, $data);
+    }
+
     /**
      * Get any additional fields for the submission/grading form for this assignment.
+     * This function is retained for backwards compatibility - new plugins should override {@link get_form_elements_for_user()}.
      *
      * @param mixed $submissionorgrade submission|grade - For submission plugins this is the submission data, for feedback plugins it is the grade data
      * @param MoodleQuickForm $mform - This is the form
index f2d0c2b..f921401 100644 (file)
@@ -125,17 +125,24 @@ class assign_feedback_file extends assign_feedback_plugin {
      * @param stdClass $grade
      * @param MoodleQuickForm $mform
      * @param stdClass $data
+     * @param int $userid The userid we are currently grading
      * @return bool true if elements were added to the form
      */
-    public function get_form_elements($grade, MoodleQuickForm $mform, stdClass $data) {
+    public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
 
         $fileoptions = $this->get_file_options();
         $gradeid = $grade ? $grade->id : 0;
+        $elementname = 'files_' . $userid;
 
+        $data = file_prepare_standard_filemanager($data,
+                                                  $elementname,
+                                                  $fileoptions,
+                                                  $this->assignment->get_context(),
+                                                  'assignfeedback_file',
+                                                  ASSIGNFEEDBACK_FILE_FILEAREA,
+                                                  $gradeid);
 
-        $data = file_prepare_standard_filemanager($data, 'files', $fileoptions, $this->assignment->get_context(), 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $gradeid);
-
-        $mform->addElement('filemanager', 'files_filemanager', '', null, $fileoptions);
+        $mform->addElement('filemanager', $elementname . '_filemanager', '', null, $fileoptions);
 
         return true;
     }
@@ -187,8 +194,11 @@ class assign_feedback_file extends assign_feedback_plugin {
     public function save(stdClass $grade, stdClass $data) {
         $fileoptions = $this->get_file_options();
 
+        $userid = $grade->userid;
+        $elementname = 'files_' . $userid;
+
         $data = file_postupdate_standard_filemanager($data,
-                                                     'files',
+                                                     $elementname,
                                                      $fileoptions,
                                                      $this->assignment->get_context(),
                                                      'assignfeedback_file',
index d0985e8..c7b041c 100644 (file)
@@ -101,6 +101,7 @@ $string['download all submissions'] = 'Download all submissions in a zip file.';
 $string['duedate'] = 'Due date';
 $string['duedate_help'] = 'This is when the assignment is due. Submissions will still be allowed after this date but any assignments submitted after this date are marked as late. To prevent submissions after a certain date - set the assignment cut off date.';
 $string['duedateno'] = 'No due date';
+$string['submissionempty'] = 'Nothing was submitted';
 $string['duedatereached'] = 'The due date for this assignment has now passed';
 $string['duedatevalidation'] = 'Due date must be after the allow submissions from date.';
 $string['editsubmission'] = 'Edit my submission';
index a6dd9bc..83cc090 100644 (file)
@@ -320,11 +320,12 @@ class assign {
 
         $o = '';
         $mform = null;
+        $notices = array();
 
-        // handle form submissions first
+        // Handle form submissions first.
         if ($action == 'savesubmission') {
             $action = 'editsubmission';
-            if ($this->process_save_submission($mform)) {
+            if ($this->process_save_submission($mform, $notices)) {
                 $action = 'view';
             }
         } else if ($action == 'lock') {
@@ -384,7 +385,7 @@ class assign {
         $returnparams = array('rownum'=>optional_param('rownum', 0, PARAM_INT));
         $this->register_return_link($action, $returnparams);
 
-        // now show the right view page
+        // Now show the right view page.
         if ($action == 'previousgrade') {
             $mform = null;
             $o .= $this->view_single_grade_page($mform, -1);
@@ -401,7 +402,7 @@ class assign {
         } else if ($action == 'viewpluginassignsubmission') {
             $o .= $this->view_plugin_content('assignsubmission');
         } else if ($action == 'editsubmission') {
-            $o .= $this->view_edit_submission_page($mform);
+            $o .= $this->view_edit_submission_page($mform, $notices);
         } else if ($action == 'grading') {
             $o .= $this->view_grading_page();
         } else if ($action == 'downloadall') {
@@ -808,13 +809,14 @@ class assign {
      * @param mixed $grade stdClass|null
      * @param MoodleQuickForm $mform
      * @param stdClass $data
+     * @param int $userid - The userid we are grading
      * @return void
      */
-    private function add_plugin_grade_elements($grade, MoodleQuickForm $mform, stdClass $data) {
+    private function add_plugin_grade_elements($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
         foreach ($this->feedbackplugins as $plugin) {
             if ($plugin->is_enabled() && $plugin->is_visible()) {
                 $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
-                if (!$plugin->get_form_elements($grade, $mform, $data)) {
+                if (!$plugin->get_form_elements_for_user($grade, $mform, $data, $userid)) {
                     $mform->removeElement('header_' . $plugin->get_type());
                 }
             }
@@ -2399,9 +2401,10 @@ class assign {
      * View edit submissions page.
      *
      * @param moodleform $mform
+     * @param array $notices A list of notices to display at the top of the edit submission form (e.g. from plugins).
      * @return void
      */
-    private function view_edit_submission_page($mform) {
+    private function view_edit_submission_page($mform, $notices) {
         global $CFG;
 
         $o = '';
@@ -2425,6 +2428,10 @@ class assign {
             $mform = new mod_assign_submission_form(null, array($this, $data));
         }
 
+        foreach ($notices as $notice) {
+            $o .= $this->get_renderer()->notification($notice);
+        }
+
         $o .= $this->get_renderer()->render(new assign_form('editsubmissionform',$mform));
 
         $o .= $this->view_footer();
@@ -3698,15 +3705,16 @@ class assign {
      * save assignment submission
      *
      * @param  moodleform $mform
+     * @param  array $notices Any error messages that should be shown to the user at the top of the edit submission form.
      * @return bool
      */
-    private function process_save_submission(&$mform) {
+    private function process_save_submission(&$mform, &$notices) {
         global $USER, $CFG;
 
-        // Include submission form
+        // Include submission form.
         require_once($CFG->dirroot . '/mod/assign/submission_form.php');
 
-        // Need submit permission to submit an assignment
+        // Need submit permission to submit an assignment.
         require_capability('mod/assign:submit', $this->context);
         require_sesskey();
 
@@ -3734,13 +3742,25 @@ class assign {
             }
 
 
+            $allempty = true;
+            $pluginerror = false;
             foreach ($this->submissionplugins as $plugin) {
                 if ($plugin->is_enabled()) {
                     if (!$plugin->save($submission, $data)) {
-                        print_error($plugin->get_error());
+                        $notices[] = $plugin->get_error();
+                        $pluginerror = true;
+                    }
+                    if (!$allempty || !$plugin->is_empty($submission)) {
+                        $allempty = false;
                     }
                 }
             }
+            if ($pluginerror || $allempty) {
+                if ($allempty) {
+                    $notices[] = get_string('submissionempty', 'mod_assign');
+                }
+                return false;
+            }
 
             $this->update_submission($submission, $USER->id, true, $this->get_instance()->teamsubmission);
 
@@ -3926,8 +3946,8 @@ class assign {
 
         $mform->addElement('static', 'progress', '', get_string('gradingstudentprogress', 'assign', array('index'=>$rownum+1, 'count'=>count($useridlist))));
 
-        // plugins
-        $this->add_plugin_grade_elements($grade, $mform, $data);
+        // Let feedback plugins add elements to the grading form.
+        $this->add_plugin_grade_elements($grade, $mform, $data, $userid);
 
         // hidden params
         $mform->addElement('hidden', 'id', $this->get_course_module()->id);
@@ -3966,7 +3986,9 @@ class assign {
         if (!$last) {
             $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign'));
         }
-        $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
+        if (!empty($buttonarray)) {
+            $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
+        }
     }
 
 
@@ -3976,13 +3998,14 @@ class assign {
      * @param mixed $submission stdClass|null
      * @param MoodleQuickForm $mform
      * @param stdClass $data
+     * @param int $userid The current userid (same as $USER->id)
      * @return void
      */
-    private function add_plugin_submission_elements($submission, MoodleQuickForm $mform, stdClass $data) {
+    private function add_plugin_submission_elements($submission, MoodleQuickForm $mform, stdClass $data, $userid) {
         foreach ($this->submissionplugins as $plugin) {
             if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
                 $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
-                if (!$plugin->get_form_elements($submission, $mform, $data)) {
+                if (!$plugin->get_form_elements_for_user($submission, $mform, $data, $userid)) {
                     $mform->removeElement('header_' . $plugin->get_type());
                 }
             }
@@ -4064,7 +4087,7 @@ class assign {
             $mform->addRule('submissionstatement', get_string('required'), 'required', null, 'client');
         }
 
-        $this->add_plugin_submission_elements($submission, $mform, $data);
+        $this->add_plugin_submission_elements($submission, $mform, $data, $USER->id);
 
         // hidden params
         $mform->addElement('hidden', 'id', $this->get_course_module()->id);
@@ -4095,9 +4118,9 @@ class assign {
         }
 
         if ($this->get_instance()->teamsubmission) {
-            $submission = $this->get_group_submission($USER->id, 0, false);
+            $submission = $this->get_group_submission($userid, 0, false);
         } else {
-            $submission = $this->get_user_submission($USER->id, false);
+            $submission = $this->get_user_submission($userid, false);
         }
 
         if (!$submission) {
index dd137ed..41a7889 100644 (file)
@@ -6,7 +6,7 @@
 <svg version="1.1"\r
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
         x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" style="overflow:visible;enable-background:new 0 0 24 24;"\r
-        xml:space="preserve">\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
 <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="14.0054" y1="0" x2="14.0054" y2="20.0005">\r
index dd137ed..41a7889 100644 (file)
@@ -6,7 +6,7 @@
 <svg version="1.1"\r
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
         x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" style="overflow:visible;enable-background:new 0 0 24 24;"\r
-        xml:space="preserve">\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
 <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="14.0054" y1="0" x2="14.0054" y2="20.0005">\r
index 1d94b5c..740a351 100644 (file)
@@ -6,7 +6,7 @@
 <svg version="1.1"\r
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
         x="0px" y="0px" width="24px" height="24px" viewBox="-2 0 24 24" style="overflow:visible;enable-background:new -2 0 24 24;"\r
-        xml:space="preserve">\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
 <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="12.0005" y1="0" x2="12.0005" y2="24.0005">\r
index 45905a7..9dd304b 100644 (file)
@@ -6,7 +6,7 @@
 <svg version="1.1"\r
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
         x="0px" y="0px" width="24px" height="24px" viewBox="-0.1 -0.1 24 24"\r
-        style="overflow:visible;enable-background:new -0.1 -0.1 24 24;" xml:space="preserve">\r
+        style="overflow:visible;enable-background:new -0.1 -0.1 24 24;" xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
 <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="10.9429" y1="0" x2="10.9429" y2="19.7881">\r