Merge branch 'MDL-36184-master' of git://github.com/damyon/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 27 Nov 2012 05:58:20 +0000 (13:58 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 27 Nov 2012 05:58:20 +0000 (13:58 +0800)
259 files changed:
.gitignore
admin/blocks.php
admin/cli/install.php
admin/filters.php
admin/index.php
admin/modules.php
admin/qbehaviours.php
admin/qtypes.php
admin/renderer.php
admin/settings/appearance.php
admin/tool/phpunit/cli/util.php
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/lib_test.php
backup/util/checks/tests/checks_test.php
blocks/navigation/styles.css
blocks/online_users/styles.css
cache/README.md
cache/classes/config.php
cache/classes/dummystore.php
cache/classes/factory.php
cache/classes/interfaces.php
cache/classes/loaders.php
cache/classes/store.php [new file with mode: 0644]
cache/disabledlib.php [new file with mode: 0644]
cache/lib.php
cache/locallib.php
cache/renderer.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
cache/tests/cache_test.php
cache/tests/fixtures/lib.php
cache/tests/locallib_test.php
calendar/lib.php
calendar/managesubscriptions_form.php
completion/criteria/completion_criteria_grade.php
composer.json [new file with mode: 0644]
config-dist.php
course/category.php
course/format/topics/lib.php
course/format/topics/styles.css
course/format/weeks/lib.php
course/format/weeks/styles.css
course/lib.php
course/search.php
course/yui/toolboxes/toolboxes.js
enrol/guest/pix/withoutpassword.png [new file with mode: 0644]
enrol/guest/pix/withoutpassword.svg [new file with mode: 0644]
enrol/guest/pix/withpassword.png [new file with mode: 0644]
enrol/guest/pix/withpassword.svg [new file with mode: 0644]
enrol/self/pix/withkey.png [new file with mode: 0644]
enrol/self/pix/withkey.svg [new file with mode: 0644]
enrol/self/pix/withoutkey.png [new file with mode: 0644]
enrol/self/pix/withoutkey.svg [new file with mode: 0644]
files/renderer.php
grade/lib.php
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/grader/quickedit_item.php
grade/report/grader/styles.css
grade/report/lib.php
install.php
install/lang/es_mx/install.php
lib/adminlib.php
lib/completionlib.php
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/editor/tinymce/adminlib.php
lib/form/yui/dateselector/dateselector.js
lib/formslib.php
lib/javascript-static.js
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.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/questionlib.php
lib/sessionlib.php
lib/setup.php
lib/setuplib.php
lib/tests/pagelib_test.php
lib/upgrade.txt
lib/upgradelib.php
lib/yui/formautosubmit/formautosubmit.js [new file with mode: 0644]
message/lib.php
message/renderer.php
mod/assign/adminlib.php
mod/assign/assignmentplugin.php
mod/assign/feedback/file/locallib.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/mod_form.php
mod/assign/settings.php
mod/book/locallib.php
mod/book/pix/add.png
mod/book/pix/add.svg [new file with mode: 0644]
mod/book/pix/nav_exit.png
mod/book/pix/nav_exit.svg [new file with mode: 0644]
mod/book/pix/nav_next.png
mod/book/pix/nav_next.svg [new file with mode: 0644]
mod/book/pix/nav_next_dis.png [new file with mode: 0644]
mod/book/pix/nav_next_dis.svg [new file with mode: 0644]
mod/book/pix/nav_prev.png
mod/book/pix/nav_prev.svg [new file with mode: 0644]
mod/book/pix/nav_prev_dis.png
mod/book/pix/nav_prev_dis.svg [new file with mode: 0644]
mod/book/styles.css
mod/book/tool/print/pix/book.png
mod/book/tool/print/pix/book.svg [new file with mode: 0644]
mod/book/tool/print/pix/chapter.png
mod/book/tool/print/pix/chapter.svg [new file with mode: 0644]
mod/book/view.php
mod/choice/lib.php
mod/data/lib.php
mod/data/view.php
mod/feedback/analysis_course.php
mod/forum/discuss.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/glossary/settings.php
mod/lesson/report.php
mod/lti/locallib.php
mod/quiz/accessmanager.php
mod/quiz/accessrule/accessrulebase.php
mod/quiz/accessrule/delaybetweenattempts/tests/rule_test.php
mod/quiz/accessrule/ipaddress/tests/rule_test.php
mod/quiz/accessrule/numattempts/tests/rule_test.php
mod/quiz/accessrule/openclosedate/rule.php
mod/quiz/accessrule/openclosedate/tests/rule_test.php
mod/quiz/accessrule/password/tests/rule_test.php
mod/quiz/accessrule/safebrowser/tests/rule_test.php
mod/quiz/accessrule/securewindow/tests/rule_test.php
mod/quiz/accessrule/timelimit/rule.php
mod/quiz/accessrule/timelimit/tests/rule_test.php
mod/quiz/accessrule/upgrade.txt
mod/quiz/attemptlib.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/backup/moodle2/restore_quiz_stepslib.php
mod/quiz/cronlib.php
mod/quiz/db/events.php
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/module.js
mod/quiz/overrideedit.php
mod/quiz/processattempt.php
mod/quiz/renderer.php
mod/quiz/startattempt.php
mod/quiz/tests/attempts_test.php [new file with mode: 0644]
mod/quiz/tests/generator/lib.php
mod/quiz/tests/generator_test.php [new file with mode: 0644]
mod/quiz/version.php
mod/scorm/request.js
mod/survey/download.php
pix/i/agg_mean.png [new file with mode: 0644]
pix/i/agg_mean.svg [new file with mode: 0644]
pix/i/agg_sum.png [new file with mode: 0644]
pix/i/agg_sum.svg [new file with mode: 0644]
pix/i/calc.png [new file with mode: 0644]
pix/i/calc.svg [new file with mode: 0644]
pix/i/courseevent.png [new file with mode: 0644]
pix/i/courseevent.svg [new file with mode: 0644]
pix/i/dragdrop.png
pix/i/dragdrop.svg
pix/i/enrolmentsuspended.png [new file with mode: 0644]
pix/i/enrolmentsuspended.svg [new file with mode: 0644]
pix/i/folder.png [new file with mode: 0644]
pix/i/folder.svg [new file with mode: 0644]
pix/i/groupevent.png [new file with mode: 0644]
pix/i/groupevent.svg [new file with mode: 0644]
pix/i/manual_item.png [new file with mode: 0644]
pix/i/manual_item.svg [new file with mode: 0644]
pix/i/siteevent.png [new file with mode: 0644]
pix/i/siteevent.svg [new file with mode: 0644]
pix/i/twoway.png [new file with mode: 0644]
pix/i/twoway.svg [new file with mode: 0644]
pix/i/userevent.png [new file with mode: 0644]
pix/i/userevent.svg [new file with mode: 0644]
pix/t/addcontact.png [new file with mode: 0644]
pix/t/addcontact.svg [new file with mode: 0644]
pix/t/block.png [new file with mode: 0644]
pix/t/block.svg [new file with mode: 0644]
pix/t/calc.png [new file with mode: 0644]
pix/t/calc.svg [new file with mode: 0644]
pix/t/calc_off.png [new file with mode: 0644]
pix/t/calc_off.svg [new file with mode: 0644]
pix/t/email.png [new file with mode: 0644]
pix/t/email.svg [new file with mode: 0644]
pix/t/emailno.png [new file with mode: 0644]
pix/t/emailno.svg [new file with mode: 0644]
pix/t/grades.png [new file with mode: 0644]
pix/t/grades.svg [new file with mode: 0644]
pix/t/lock.png [new file with mode: 0644]
pix/t/lock.svg [new file with mode: 0644]
pix/t/locked.png [new file with mode: 0644]
pix/t/locked.svg [new file with mode: 0644]
pix/t/locktime.png [new file with mode: 0644]
pix/t/locktime.svg [new file with mode: 0644]
pix/t/message.png [new file with mode: 0644]
pix/t/message.svg [new file with mode: 0644]
pix/t/messages.png [new file with mode: 0644]
pix/t/messages.svg [new file with mode: 0644]
pix/t/preview.png [new file with mode: 0644]
pix/t/preview.svg [new file with mode: 0644]
pix/t/print.png [new file with mode: 0644]
pix/t/print.svg [new file with mode: 0644]
pix/t/removecontact.png [new file with mode: 0644]
pix/t/removecontact.svg [new file with mode: 0644]
pix/t/sort.png [new file with mode: 0644]
pix/t/sort.svg [new file with mode: 0644]
pix/t/switch_whole.png [new file with mode: 0644]
pix/t/switch_whole.svg [new file with mode: 0644]
pix/t/unblock.png [new file with mode: 0644]
pix/t/unblock.svg [new file with mode: 0644]
pix/t/unlock.png [new file with mode: 0644]
pix/t/unlock.svg [new file with mode: 0644]
pix/t/unlocked.png [new file with mode: 0644]
pix/t/unlocked.svg [new file with mode: 0644]
pix/y/ln.png [deleted file]
question/previewlib.php
question/type/numerical/edit_numerical_form.php
theme/arialist/style/core.css
theme/base/style/admin.css
theme/base/style/blocks.css
theme/base/style/calendar.css
theme/base/style/core.css
theme/base/style/course.css
theme/base/style/filemanager.css
theme/base/style/grade.css
theme/base/style/message.css
theme/base/style/question.css
theme/boxxie/style/core.css
theme/magazine/style/core.css
theme/mymobile/config.php
theme/mymobile/javascript/custom.js
theme/mymobile/javascript/jquery.mobile-1.1.1.js [moved from theme/mymobile/javascript/jquery.mobile-1.1.0.js with 94% similarity]
theme/mymobile/renderers.php
theme/standard/style/blocks.css
theme/standard/style/calendar.css
theme/standard/style/core.css
theme/standard/style/course.css
theme/standard/style/css3.css
theme/upgrade.txt
user/filters/yesno.php
user/message.html
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 34e8e4e..e703ee8 100644 (file)
             $visible = '';
         } else if ($blocks[$blockid]->visible) {
             $visible = '<a href="blocks.php?hide='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strhide.'">'.
-                       '<img src="'.$OUTPUT->pix_url('i/hide') . '" class="icon" alt="'.$strhide.'" /></a>';
+                       '<img src="'.$OUTPUT->pix_url('t/hide') . '" class="iconsmall" alt="'.$strhide.'" /></a>';
         } else {
             $visible = '<a href="blocks.php?show='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strshow.'">'.
-                       '<img src="'.$OUTPUT->pix_url('i/show') . '" class="icon" alt="'.$strshow.'" /></a>';
+                       '<img src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.$strshow.'" /></a>';
             $class = ' class="dimmed_text"'; // Leading space required!
         }
 
             $undeletable = '';
         } else if (in_array($blockname, $undeletableblocktypes)) {
             $undeletable = '<a href="blocks.php?unprotect='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strunprotect.'">'.
-                       '<img src="'.$OUTPUT->pix_url('t/unlock') . '" class="icon" alt="'.$strunprotect.'" /></a>';
+                       '<img src="'.$OUTPUT->pix_url('t/unlock') . '" class="iconsmall" alt="'.$strunprotect.'" /></a>';
         } else {
             $undeletable = '<a href="blocks.php?protect='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strprotect.'">'.
-                       '<img src="'.$OUTPUT->pix_url('t/unlock_gray') . '" class="icon" alt="'.$strprotect.'" /></a>';
+                       '<img src="'.$OUTPUT->pix_url('t/lock') . '" class="iconsmall" alt="'.$strprotect.'" /></a>';
         }
 
         $row = array(
index 858571f..ca8aaef 100644 (file)
@@ -127,6 +127,9 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
 /** Used by library scripts to check they are being called by Moodle */
 define('MOODLE_INTERNAL', true);
 
+// Disables caching.. just in case.
+define('CACHE_DISABLE_ALL', true);
+
 // Check that PHP is of a sufficient version
 if (version_compare(phpversion(), "5.3.2") < 0) {
     $phpversion = phpversion();
index 5f58bc7..c102666 100644 (file)
@@ -247,15 +247,15 @@ function get_table_row($filterinfo, $isfirstrow, $islastactive, $applytostrings)
 
     // Re-order
     $updown = '';
-    $spacer = '<img src="' . $OUTPUT->pix_url('spacer') . '" class="iconsmall" alt="" /> ';
+    $spacer = '<img src="' . $OUTPUT->pix_url('spacer') . '" class="iconsmall" alt="" />';
     if ($filterinfo->active != TEXTFILTER_DISABLED) {
         if (!$isfirstrow) {
-            $updown .= $OUTPUT->action_icon(filters_action_url($filter, 'up'), new pix_icon('t/up', get_string('up')));
+            $updown .= $OUTPUT->action_icon(filters_action_url($filter, 'up'), new pix_icon('t/up', get_string('up'), '', array('class' => 'iconsmall')));
         } else {
             $updown .= $spacer;
         }
         if (!$islastactive) {
-            $updown .= $OUTPUT->action_icon(filters_action_url($filter, 'down'), new pix_icon('t/down', get_string('down')));
+            $updown .= $OUTPUT->action_icon(filters_action_url($filter, 'down'), new pix_icon('t/down', get_string('down'), '', array('class' => 'iconsmall')));
         } else {
             $updown .= $spacer;
         }
index 2f88126..d75e364 100644 (file)
@@ -193,6 +193,7 @@ if (empty($CFG->version)) {
 
 if ($version > $CFG->version) {  // upgrade
     purge_all_caches();
+
     $PAGE->set_pagelayout('maintenance');
     $PAGE->set_popup_notification_allowed(false);
 
@@ -376,6 +377,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 2290612..3ad0a72 100644 (file)
             $class   = '';
         } else if ($module->visible) {
             $visible = "<a href=\"modules.php?hide=$module->name&amp;sesskey=".sesskey()."\" title=\"$strhide\">".
-                       "<img src=\"" . $OUTPUT->pix_url('i/hide') . "\" class=\"icon\" alt=\"$strhide\" /></a>";
+                       "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strhide\" /></a>";
             $class   = '';
         } else {
             $visible = "<a href=\"modules.php?show=$module->name&amp;sesskey=".sesskey()."\" title=\"$strshow\">".
-                       "<img src=\"" . $OUTPUT->pix_url('i/show') . "\" class=\"icon\" alt=\"$strshow\" /></a>";
+                       "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strshow\" /></a>";
             $class =   ' class="dimmed_text"';
         }
         if ($module->name == "forum") {
index 1e72afa..3dd14a0 100644 (file)
@@ -258,7 +258,7 @@ foreach ($sortedbehaviours as $behaviour => $behaviourname) {
             $rowclass = 'dimmed_text';
         }
     } else {
-        $icons = $OUTPUT->spacer() . ' ';
+        $icons = $OUTPUT->spacer(array('class' => 'iconsmall'));
     }
 
     // Move icons.
@@ -284,10 +284,10 @@ echo $OUTPUT->footer();
 
 function question_behaviour_enable_disable_icons($behaviour, $enabled) {
     if ($enabled) {
-        return question_behaviour_icon_html('disable', $behaviour, 'i/hide',
+        return question_behaviour_icon_html('disable', $behaviour, 't/hide',
                 get_string('enabled', 'question'), get_string('disable'));
     } else {
-        return question_behaviour_icon_html('enable', $behaviour, 'i/show',
+        return question_behaviour_icon_html('enable', $behaviour, 't/show',
                 get_string('disabled', 'question'), get_string('enable'));
     }
 }
@@ -296,7 +296,7 @@ function question_behaviour_icon_html($action, $behaviour, $icon, $alt, $tip) {
     global $OUTPUT;
     return $OUTPUT->action_icon(new moodle_url('/admin/qbehaviours.php',
             array($action => $behaviour, 'sesskey' => sesskey())),
-            new pix_icon($icon, $alt, 'moodle', array('title' => '')),
-            null, array('title' => $tip)) . ' ';
+            new pix_icon($icon, $alt, 'moodle', array('title' => '', 'class' => 'iconsmall')),
+            null, array('title' => $tip));
 }
 
index a37725c..1b35c43 100644 (file)
@@ -257,7 +257,7 @@ foreach ($sortedqtypes as $qtypename => $localname) {
             $rowclass = 'dimmed_text';
         }
     } else {
-        $icons = $OUTPUT->spacer() . ' ';
+        $icons = $OUTPUT->spacer();
     }
 
     // Move icons.
@@ -294,10 +294,10 @@ echo $OUTPUT->footer();
 
 function question_types_enable_disable_icons($qtypename, $createable) {
     if ($createable) {
-        return question_type_icon_html('disable', $qtypename, 'i/hide',
+        return question_type_icon_html('disable', $qtypename, 't/hide',
                 get_string('enabled', 'question'), get_string('disable'));
     } else {
-        return question_type_icon_html('enable', $qtypename, 'i/show',
+        return question_type_icon_html('enable', $qtypename, 't/show',
                 get_string('disabled', 'question'), get_string('enable'));
     }
 }
@@ -306,7 +306,7 @@ function question_type_icon_html($action, $qtypename, $icon, $alt, $tip) {
     global $OUTPUT;
     return $OUTPUT->action_icon(new moodle_url('/admin/qtypes.php',
             array($action => $qtypename, 'sesskey' => sesskey())),
-            new pix_icon($icon, $alt, 'moodle', array('title' => '')),
-            null, array('title' => $tip)) . ' ';
+            new pix_icon($icon, $alt, 'moodle', array('title' => '', 'class' => 'iconsmall')),
+            null, array('title' => $tip));
 }
 
index 7ca6ea2..de78470 100644 (file)
@@ -1092,9 +1092,9 @@ class core_admin_renderer extends plugin_renderer_base {
                 $row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
 
                 if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) {
-                    $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'smallicon pluginicon'));
+                    $icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'icon pluginicon'));
                 } else {
-                    $icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon'));
+                    $icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
                 }
                 if ($plugin->get_status() === plugin_manager::PLUGIN_STATUS_MISSING) {
                     $msg = html_writer::tag('span', get_string('status_missing', 'core_plugin'), array('class' => 'notifyproblem'));
@@ -1102,7 +1102,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 } else {
                     $msg = '';
                 }
-                $pluginname  = html_writer::tag('div', $icon . ' ' . $plugin->displayname . ' ' . $msg, array('class' => 'displayname')).
+                $pluginname  = html_writer::tag('div', $icon . '' . $plugin->displayname . ' ' . $msg, array('class' => 'displayname')).
                                html_writer::tag('div', $plugin->component, array('class' => 'componentname'));
                 $pluginname  = new html_table_cell($pluginname);
 
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 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 c3b47c6..4fdf62e 100644 (file)
@@ -642,7 +642,7 @@ class moodle1_converter extends base_converter {
         }
         foreach ($matches[2] as $match) {
             $file = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
-            $files[] = urldecode($file);
+            $files[] = rawurldecode($file);
         }
 
         return array_unique($files);
@@ -659,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 20aca62..bf4935c 100644 (file)
@@ -443,23 +443,37 @@ 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() {
-        // This test covers MDL-36204
+
         $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 />';
+    <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(2, count($files));
+        $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() {
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 6d08f5e..9126897 100644 (file)
@@ -7,6 +7,7 @@
 .block_navigation .block_tree li > p.hasicon img {vertical-align:middle;position:absolute;left:0;top:-1px;width:16px;height:16px;}
 .block_navigation .block_tree li.item_with_icon.contains_branch > p img {left:16px;}
 .block_navigation .block_tree .type_activity > p.branch.hasicon,
+.block_navigation .block_tree .type_activity > p.emptybranch.hasicon,
 .block_navigation .block_tree li.item_with_icon.contains_branch > .tree_item {padding-left:37px;}
 
 .block_navigation .block_tree li ul {padding-left:0;margin:0;}
@@ -16,7 +17,9 @@
 
 .block_navigation .block_tree .tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 0;background-repeat: no-repeat;}
 .block_navigation .block_tree .tree_item.branch.navigation_node {background-image:none;padding-left:0;}
+.block_navigation .block_tree .type_activity > .tree_item.emptybranch,
 .block_navigation .block_tree .type_activity > .tree_item.branch {background-image:none;position:relative;}
+.block_navigation .block_tree .type_activity > .tree_item.hasicon.emptybranch img,
 .block_navigation .block_tree .type_activity > .tree_item.branch img {left: 16px;}
 .block_navigation .block_tree .root_node.leaf {padding-left:0px;}
 .block_navigation .block_tree .active_tree_node {font-weight:bold;}
index baa80f3..ef5757e 100644 (file)
@@ -1,7 +1,8 @@
 .block_online_users .content .list li.listentry {clear:both;}
 .block_online_users .content .list li.listentry .user {float:left;position:relative;}
-.block_online_users .content .list li.listentry .message {float:right;}
+.block_online_users .content .list li.listentry .user .userpicture { vertical-align: text-bottom;}
+.block_online_users .content .list li.listentry .message {float:right; margin-top: 3px;}
 .block_online_users .content .info {text-align:center;}
 
 .dir-rtl .block_online_users .content .list li.listentry .user {float:right;}
-.dir-rtl .block_online_users .content .list li.listentry .message {float:left;}
\ No newline at end of file
+.dir-rtl .block_online_users .content .list li.listentry .message {float:left;}
index 408e014..b6f4e4f 100644 (file)
@@ -56,6 +56,27 @@ If a data source had been specified in the definition, the following would be al
     $cache = cache::make('core', 'string');
     $component = $cache->get('component');
 
+Disabling the cache stores.
+There are times in code when you will want to disable the cache stores.
+While the cache API must still be functional in order for calls to it to work it is possible to disable the use of the cache stores separately so that you can be sure only the cache will function in all circumstances.
+
+    // Disable the cache store at the start of your script with:
+    define('CACHE_DISABLE_STORES', true);
+
+    // Disable the cache within your script when you want with:
+    cache_factory::disable_stores();
+    // If you disabled it using the above means you can re-enable it with:
+    cache_factory::reset();
+
+Disabling the cache entirely.
+Like above there are times when you want the cache to avoid initialising anything it doesn't absolutely need. Things such as installation and upgrade require this functionality.
+When the cache API is disabled it is still functional however special "disabled" classes will be used instead of the regular classes that make the Cache API tick.
+These disabled classes do the least work possible and through this means we avoid all manner of intialisation and configuration.
+Once disabled it cannot be re-enabled.
+
+    // To disable the cache entirely call the following:
+    define('CACHE_DISABLE_ALL', true);
+
 Cache API parts
 ---------------
 
@@ -89,6 +110,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 da3b966..fee8aea 100644 (file)
@@ -122,12 +122,15 @@ class cache_config {
     /**
      * Loads the configuration file and parses its contents into the expected structure.
      *
+     * @param array|false $configuration Can be used to force a configuration. Should only be used when truly required.
      * @return boolean
      */
-    public function load() {
+    public function load($configuration = false) {
         global $CFG;
 
-        $configuration = $this->include_configuration();
+        if ($configuration === false) {
+            $configuration = $this->include_configuration();
+        }
 
         $this->configstores = array();
         $this->configdefinitions = array();
@@ -182,7 +185,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'])) {
@@ -425,7 +428,8 @@ class cache_config {
      */
     public function get_stores_for_definition(cache_definition $definition) {
         // Check if MUC has been disabled.
-        if (defined('NO_CACHE_STORES') && NO_CACHE_STORES !== false) {
+        $factory = cache_factory::instance();
+        if ($factory->stores_disabled()) {
             // Yip its been disabled.
             // To facilitate this we are going to always return an empty array of stores to use.
             // This will force all cache instances to use the cachestore_dummy.
index d44dc8c..43d9229 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.
@@ -107,6 +107,11 @@ class cachestore_dummy implements cache_store {
      */
     public function initialise(cache_definition $definition) {
         // If the definition isn't persistent then we need to be persistent here.
+        // The reasoning behind this is that:
+        //   - If the definition is persistent then the cache loader is going to
+        //     store things in its persistent cache.
+        //   - If the definition is not persistent then the cache loader won't try to store anything
+        //     and we will need to store it here in order to make sure it is accessible.
         $this->persist = !$definition->should_be_persistent();
     }
 
@@ -135,30 +140,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 9a588df..1458cf0 100644 (file)
@@ -40,6 +40,21 @@ defined('MOODLE_INTERNAL') || die();
  */
 class cache_factory {
 
+    /** The cache has not been initialised yet. */
+    const STATE_UNINITIALISED = 0;
+    /** The cache is in the process of initialising itself. */
+    const STATE_INITIALISING = 1;
+    /** The cache is in the process of saving its configuration file. */
+    const STATE_SAVING = 2;
+    /** The cache is ready to use. */
+    const STATE_READY = 3;
+    /** The cache encountered an error while initialising. */
+    const STATE_ERROR_INITIALISING = 9;
+    /** The cache has been disabled. */
+    const STATE_DISABLED = 10;
+    /** The cache stores have been disabled */
+    const STATE_STORES_DISABLED = 11;
+
     /**
      * An instance of the cache_factory class created upon the first request.
      * @var cache_factory
@@ -82,6 +97,12 @@ class cache_factory {
      */
     protected $lockplugins = null;
 
+    /**
+     * The current state of the cache API.
+     * @var int
+     */
+    protected $state = 0;
+
     /**
      * Returns an instance of the cache_factor method.
      *
@@ -89,8 +110,22 @@ class cache_factory {
      * @return cache_factory
      */
     public static function instance($forcereload = false) {
+        global $CFG;
         if ($forcereload || self::$instance === null) {
-            self::$instance = new cache_factory();
+            // Initialise a new factory to facilitate our needs.
+            if (defined('CACHE_DISABLE_ALL') && CACHE_DISABLE_ALL !== false) {
+                // The cache has been disabled. Load disabledlib and start using the factory designed to handle this
+                // situation. It will use disabled alternatives where available.
+                require_once($CFG->dirroot.'/cache/disabledlib.php');
+                self::$instance = new cache_factory_disabled();
+            } else {
+                // We're using the regular factory.
+                self::$instance = new cache_factory();
+                if (defined('CACHE_DISABLE_STORES') && CACHE_DISABLE_STORES !== false) {
+                    // The cache stores have been disabled.
+                    self::$instance->set_state(self::STATE_STORES_DISABLED);;
+                }
+            }
         }
         return self::$instance;
     }
@@ -113,6 +148,8 @@ class cache_factory {
         $factory->configs = array();
         $factory->definitions = array();
         $factory->lockplugins = null; // MUST be null in order to force its regeneration.
+        // Reset the state to uninitialised.
+        $factory->state = self::STATE_UNINITIALISED;
     }
 
     /**
@@ -206,7 +243,7 @@ class cache_factory {
      * @param string $name The name of the store (must be unique remember)
      * @param array $details
      * @param cache_definition $definition The definition to instantiate it for.
-     * @return boolean
+     * @return boolean|cache_store
      */
     public function create_store_from_config($name, array $details, cache_definition $definition) {
         if (!array_key_exists($name, $this->stores)) {
@@ -253,9 +290,19 @@ class cache_factory {
             $class = 'cache_config_phpunittest';
         }
 
+        $error = false;
         if ($needtocreate) {
             // Create the default configuration.
-            $class::create_default_configuration();
+            // Update the state, we are now initialising the cache.
+            self::set_state(self::STATE_INITIALISING);
+            $configuration = $class::create_default_configuration();
+            if ($configuration !== true) {
+                // Failed to create the default configuration. Disable the cache stores and update the state.
+                self::set_state(self::STATE_ERROR_INITIALISING);
+                $this->configs[$class] = new $class;
+                $this->configs[$class]->load($configuration);
+                $error = true;
+            }
         }
 
         if (!array_key_exists($class, $this->configs)) {
@@ -264,6 +311,11 @@ class cache_factory {
             $this->configs[$class]->load();
         }
 
+        if (!$error) {
+            // The cache is now ready to use. Update the state.
+            self::set_state(self::STATE_READY);
+        }
+
         // Return the instance.
         return $this->configs[$class];
     }
@@ -338,4 +390,83 @@ class cache_factory {
         $class = $this->lockplugins[$type];
         return new $class($name, $config);
     }
+
+    /**
+     * Returns the current state of the cache API.
+     *
+     * @return int
+     */
+    public function get_state() {
+        return $this->state;
+    }
+
+    /**
+     * Updates the state fo the cache API.
+     *
+     * @param int $state
+     * @return bool
+     */
+    public function set_state($state) {
+        if ($state <= $this->state) {
+            return false;
+        }
+        $this->state = $state;
+        return true;
+    }
+
+    /**
+     * Returns true if the cache API has been disabled.
+     *
+     * @return bool
+     */
+    public function is_disabled() {
+        return $this->state === self::STATE_DISABLED;
+    }
+
+    /**
+     * Disables as much of the cache API as possible.
+     *
+     * All of the magic associated with the disabled cache is wrapped into this function.
+     * In switching out the factory for the disabled factory it gains full control over the initialisation of objects
+     * and can use all of the disabled alternatives.
+     * Simple!
+     *
+     * This function has been marked as protected so that it cannot be abused through the public API presently.
+     * Perhaps in the future we will allow this, however as per the build up to the first release containing
+     * MUC it was decided that this was just to risky and abusable.
+     */
+    protected static function disable() {
+        global $CFG;
+        require_once($CFG->dirroot.'/cache/disabledlib.php');
+        self::$instance = new cache_factory_disabled();
+    }
+
+    /**
+     * Returns true if the cache stores have been disabled.
+     *
+     * @return bool
+     */
+    public function stores_disabled() {
+        return $this->state === self::STATE_STORES_DISABLED || $this->is_disabled();
+    }
+
+    /**
+     * Disables cache stores.
+     *
+     * The cache API will continue to function however none of the actual stores will be used.
+     * Instead the dummy store will be provided for all cache requests.
+     * This is useful in situations where you cannot be sure any stores are working.
+     *
+     * In order to re-enable the cache you must call the cache factories static reset method:
+     * <code>
+     * // Disable the cache factory.
+     * cache_factory::disable_stores();
+     * // Re-enable the cache factory by resetting it.
+     * cache_factory::reset();
+     * </code>
+     */
+    public static function disable_stores() {
+        $factory = self::instance();
+        $factory->set_state(self::STATE_STORES_DISABLED);
+    }
 }
\ No newline at end of file
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;
+    }
+}
diff --git a/cache/disabledlib.php b/cache/disabledlib.php
new file mode 100644 (file)
index 0000000..18cc11e
--- /dev/null
@@ -0,0 +1,462 @@
+<?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 file contains classes that are used by the Cache API only when it is disabled.
+ *
+ * These classes are derivatives of other significant classes used by the Cache API customised specifically
+ * to only do what is absolutely necessary when initialising and using the Cache API when its been disabled.
+ *
+ * @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();
+
+/**
+ * Required as it is needed for cache_config_disabled which extends cache_config_writer.
+ */
+require_once($CFG->dirroot.'/cache/locallib.php');
+
+/**
+ * The cache loader class used when the Cache has been disabled.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_disabled extends cache {
+
+    /**
+     * Constructs the cache.
+     *
+     * @param cache_definition $definition
+     * @param cache_store $store
+     * @param null $loader Unused.
+     */
+    public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
+        $this->definition = $definition;
+        $this->store = $store;
+    }
+
+    /**
+     * Gets a key from the cache.
+     *
+     * @param int|string $key
+     * @param int $strictness Unused.
+     * @return bool
+     */
+    public function get($key, $strictness = IGNORE_MISSING) {
+        return false;
+    }
+
+    /**
+     * Gets many keys at once from the cache.
+     *
+     * @param array $keys
+     * @param int $strictness Unused.
+     * @return array
+     */
+    public function get_many(array $keys, $strictness = IGNORE_MISSING) {
+        $return = array();
+        foreach ($keys as $key) {
+            $return[$key] = false;
+        }
+        return $return;
+    }
+
+    /**
+     * Sets a key value pair in the cache.
+     *
+     * @param int|string $key Unused.
+     * @param mixed $data Unused.
+     * @return bool
+     */
+    public function set($key, $data) {
+        return false;
+    }
+
+    /**
+     * Sets many key value pairs in the cache at once.
+     *
+     * @param array $keyvaluearray Unused.
+     * @return int
+     */
+    public function set_many(array $keyvaluearray) {
+        return 0;
+    }
+
+    /**
+     * Deletes an item from the cache.
+     *
+     * @param int|string $key Unused.
+     * @param bool $recurse Unused.
+     * @return bool
+     */
+    public function delete($key, $recurse = true) {
+        return false;
+    }
+
+    /**
+     * Deletes many items at once from the cache.
+     *
+     * @param array $keys Unused.
+     * @param bool $recurse Unused.
+     * @return int
+     */
+    public function delete_many(array $keys, $recurse = true) {
+        return 0;
+    }
+
+    /**
+     * Checks if the cache has the requested key.
+     *
+     * @param int|string $key Unused.
+     * @return bool
+     */
+    public function has($key) {
+        return false;
+    }
+
+    /**
+     * Checks if the cache has all of the requested keys.
+     * @param array $keys Unused.
+     * @return bool
+     */
+    public function has_all(array $keys) {
+        return false;
+    }
+
+    /**
+     * Checks if the cache has any of the requested keys.
+     *
+     * @param array $keys Unused.
+     * @return bool
+     */
+    public function has_any(array $keys) {
+        return false;
+    }
+
+    /**
+     * Purges all items from the cache.
+     *
+     * @return bool
+     */
+    public function purge() {
+        return true;
+    }
+}
+
+/**
+ * The cache factory class used when the Cache has been disabled.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_factory_disabled extends cache_factory {
+
+    /**
+     * Returns an instance of the cache_factor method.
+     *
+     * @param bool $forcereload Unused.
+     * @return cache_factory
+     * @throws coding_exception
+     */
+    public static function instance($forcereload = false) {
+        throw new coding_exception('You must not call to this cache factory within your code.');
+    }
+
+    /**
+     * Creates a definition instance or returns the existing one if it has already been created.
+     *
+     * @param string $component
+     * @param string $area
+     * @param string $aggregate Unused.
+     * @return cache_definition
+     */
+    public function create_definition($component, $area, $aggregate = null) {
+        return cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
+    }
+
+    /**
+     * Common public method to create a cache instance given a definition.
+     *
+     * @param cache_definition $definition
+     * @return cache_application|cache_session|cache_store
+     * @throws coding_exception
+     */
+    public function create_cache(cache_definition $definition) {
+        return new cache_disabled($definition, $this->create_dummy_store($definition));
+    }
+
+    /**
+     * Creates a cache object given the parameters for a definition.
+     *
+     * @param string $component
+     * @param string $area
+     * @param array $identifiers
+     * @param string $aggregate
+     * @return cache_application|cache_session|cache_request
+     */
+    public function create_cache_from_definition($component, $area, array $identifiers = array(), $aggregate = null) {
+        $definition = $this->create_definition($component, $area, $aggregate);
+        $cache = $this->create_cache($definition, $identifiers);
+        return $cache;
+    }
+
+    /**
+     * Creates an ad-hoc cache from the given param.
+     *
+     * @param int $mode
+     * @param string $component
+     * @param string $area
+     * @param array $identifiers
+     * @param array $options An array of options, available options are:
+     *   - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
+     *   - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
+     *   - persistent : If set to true the cache will persist construction requests.
+     * @return cache_application|cache_session|cache_request
+     */
+    public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
+        $definition = cache_definition::load_adhoc($mode, $component, $area);
+        $cache = $this->create_cache($definition, $identifiers);
+        return $cache;
+    }
+
+    /**
+     * Creates a store instance given its name and configuration.
+     *
+     * @param string $name Unused.
+     * @param array $details Unused.
+     * @param cache_definition $definition
+     * @return boolean|cache_store
+     */
+    public function create_store_from_config($name, array $details, cache_definition $definition) {
+        return $this->create_dummy_store($definition);
+    }
+
+    /**
+     * Creates a cache config instance with the ability to write if required.
+     *
+     * @param bool $writer Unused.
+     * @return cache_config|cache_config_writer
+     */
+    public function create_config_instance($writer = false) {
+        $class = 'cache_config_disabled';
+        if (!array_key_exists($class, $this->configs)) {
+            self::set_state(self::STATE_INITIALISING);
+            $configuration = $class::create_default_configuration();
+            $this->configs[$class] = new $class;
+            $this->configs[$class]->load($configuration);
+        }
+        self::set_state(self::STATE_READY);
+
+        // Return the instance.
+        return $this->configs[$class];
+    }
+}
+
+/**
+ * The cache config class used when the Cache has been disabled.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_config_disabled extends cache_config_writer {
+
+    /**
+     * Returns an instance of the configuration writer.
+     *
+     * @return cache_config_disabled
+     */
+    public static function instance() {
+        $factory = cache_factory::instance();
+        return $factory->create_config_instance(true);
+    }
+
+    /**
+     * Saves the current configuration.
+     */
+    protected function config_save() {
+        // Nothing to do here.
+    }
+
+    /**
+     * Generates a configuration array suitable to be written to the config file.
+     *
+     * @return array
+     */
+    protected function generate_configuration_array() {
+        $configuration = array();
+        $configuration['stores'] = $this->configstores;
+        $configuration['modemappings'] = $this->configmodemappings;
+        $configuration['definitions'] = $this->configdefinitions;
+        $configuration['definitionmappings'] = $this->configdefinitionmappings;
+        $configuration['locks'] = $this->configlocks;
+        return $configuration;
+    }
+
+    /**
+     * Adds a plugin instance.
+     *
+     * @param string $name Unused.
+     * @param string $plugin Unused.
+     * @param array $configuration Unused.
+     * @return bool
+     * @throws cache_exception
+     */
+    public function add_store_instance($name, $plugin, array $configuration = array()) {
+        return false;
+    }
+
+    /**
+     * Sets the mode mappings.
+     *
+     * @param array $modemappings Unused.
+     * @return bool
+     * @throws cache_exception
+     */
+    public function set_mode_mappings(array $modemappings) {
+        return false;
+    }
+
+    /**
+     * Edits a give plugin instance.
+     *
+     * @param string $name Unused.
+     * @param string $plugin Unused.
+     * @param array $configuration Unused.
+     * @return bool
+     * @throws cache_exception
+     */
+    public function edit_store_instance($name, $plugin, $configuration) {
+        return false;
+    }
+
+    /**
+     * Deletes a store instance.
+     *
+     * @param string $name Unused.
+     * @return bool
+     * @throws cache_exception
+     */
+    public function delete_store_instance($name) {
+        return false;
+    }
+
+    /**
+     * Creates the default configuration and saves it.
+     *
+     * @return array
+     */
+    public static function create_default_configuration() {
+        global $CFG;
+
+        // HACK ALERT.
+        // We probably need to come up with a better way to create the default stores, or at least ensure 100% that the
+        // default store plugins are protected from deletion.
+        require_once($CFG->dirroot.'/cache/stores/file/lib.php');
+        require_once($CFG->dirroot.'/cache/stores/session/lib.php');
+        require_once($CFG->dirroot.'/cache/stores/static/lib.php');
+
+        $writer = new self;
+        $writer->configstores = array(
+            'default_application' => array(
+                'name' => 'default_application',
+                'plugin' => 'file',
+                'configuration' => array(),
+                'features' => cachestore_file::get_supported_features(),
+                'modes' => cache_store::MODE_APPLICATION,
+                'default' => true,
+            ),
+            'default_session' => array(
+                'name' => 'default_session',
+                'plugin' => 'session',
+                'configuration' => array(),
+                'features' => cachestore_session::get_supported_features(),
+                'modes' => cache_store::MODE_SESSION,
+                'default' => true,
+            ),
+            'default_request' => array(
+                'name' => 'default_request',
+                'plugin' => 'static',
+                'configuration' => array(),
+                'features' => cachestore_static::get_supported_features(),
+                'modes' => cache_store::MODE_REQUEST,
+                'default' => true,
+            )
+        );
+        $writer->configdefinitions = array();
+        $writer->configmodemappings = array(
+            array(
+                'mode' => cache_store::MODE_APPLICATION,
+                'store' => 'default_application',
+                'sort' => -1
+            ),
+            array(
+                'mode' => cache_store::MODE_SESSION,
+                'store' => 'default_session',
+                'sort' => -1
+            ),
+            array(
+                'mode' => cache_store::MODE_REQUEST,
+                'store' => 'default_request',
+                'sort' => -1
+            )
+        );
+        $writer->configlocks = array(
+            'default_file_lock' => array(
+                'name' => 'cachelock_file_default',
+                'type' => 'cachelock_file',
+                'dir' => 'filelocks',
+                'default' => true
+            )
+        );
+
+        return $writer->generate_configuration_array();
+    }
+
+    /**
+     * Updates the definition in the configuration from those found in the cache files.
+     *
+     * @param bool $coreonly Unused.
+     */
+    public static function update_definitions($coreonly = false) {
+        // Nothing to do here.
+    }
+
+    /**
+     * Locates all of the definition files.
+     *
+     * @param bool $coreonly Unused.
+     * @return array
+     */
+    protected static function locate_definitions($coreonly = false) {
+        return array();
+    }
+
+    /**
+     * Sets the mappings for a given definition.
+     *
+     * @param string $definition Unused.
+     * @param array $mappings Unused.
+     * @throws coding_exception
+     */
+    public function set_definition_mappings($definition, $mappings) {
+        // Nothing to do here.
+    }
+}
\ No newline at end of file
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..76888b7 100644 (file)
@@ -41,6 +41,14 @@ defined('MOODLE_INTERNAL') || die();
  */
 class cache_config_writer extends cache_config {
 
+    /**
+     * Switch that gets set to true when ever a cache_config_writer instance is saving the cache configuration file.
+     * If this is set to true when save is next called we must avoid the trying to save and instead return the
+     * generated config so that is may be used instead of the file.
+     * @var bool
+     */
+    protected static $creatingconfig = false;
+
     /**
      * Returns an instance of the configuration writer.
      *
@@ -53,6 +61,10 @@ class cache_config_writer extends cache_config {
 
     /**
      * Saves the current configuration.
+     *
+     * Exceptions within this function are tolerated but must be of type cache_exception.
+     * They are caught during initialisation and written to the error log. This is required in order to avoid
+     * infinite loop situations caused by the cache throwing exceptions during its initialisation.
      */
     protected function config_save() {
         global $CFG;
@@ -61,20 +73,15 @@ class cache_config_writer extends cache_config {
         if ($directory !== $CFG->dataroot && !file_exists($directory)) {
             $result = make_writable_directory($directory, false);
             if (!$result) {
-                throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory.');
+                throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory. Check the permissions on your moodledata directory.');
             }
         }
         if (!file_exists($directory) || !is_writable($directory)) {
-            throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable.');
+            throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable. Check the permissions on the moodledata/muc directory.');
         }
 
         // Prepare a configuration array to store.
-        $configuration = array();
-        $configuration['stores'] = $this->configstores;
-        $configuration['modemappings'] = $this->configmodemappings;
-        $configuration['definitions'] = $this->configdefinitions;
-        $configuration['definitionmappings'] = $this->configdefinitionmappings;
-        $configuration['locks'] = $this->configlocks;
+        $configuration = $this->generate_configuration_array();
 
         // Prepare the file content.
         $content = "<?php defined('MOODLE_INTERNAL') || die();\n \$configuration = ".var_export($configuration, true).";";
@@ -106,6 +113,20 @@ class cache_config_writer extends cache_config {
         }
     }
 
+    /**
+     * Generates a configuration array suitable to be written to the config file.
+     * @return array
+     */
+    protected function generate_configuration_array() {
+        $configuration = array();
+        $configuration['stores'] = $this->configstores;
+        $configuration['modemappings'] = $this->configmodemappings;
+        $configuration['definitions'] = $this->configdefinitions;
+        $configuration['definitionmappings'] = $this->configdefinitionmappings;
+        $configuration['locks'] = $this->configlocks;
+        return $configuration;
+    }
+
     /**
      * Adds a plugin instance.
      *
@@ -137,7 +158,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()) {
@@ -293,6 +314,9 @@ class cache_config_writer extends cache_config {
      *
      * This function calls config_save, however it is safe to continue using it afterwards as this function should only ever
      * be called when there is no configuration file already.
+     *
+     * @return true|array Returns true if the default configuration was successfully created.
+     *     Returns a configuration array if it could not be saved. This is a bad situation. Check your error logs.
      */
     public static function create_default_configuration() {
         global $CFG;
@@ -357,7 +381,16 @@ class cache_config_writer extends cache_config {
                 'default' => true
             )
         );
+
+        $factory = cache_factory::instance();
+        // We expect the cache to be initialising presently. If its not then something has gone wrong and likely
+        // we are now in a loop.
+        if ($factory->get_state() !== cache_factory::STATE_INITIALISING) {
+            return $writer->generate_configuration_array();
+        }
+        $factory->set_state(cache_factory::STATE_SAVING);
         $writer->config_save();
+        return true;
     }
 
     /**
@@ -790,7 +823,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 +881,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 2443a09..81e86b1 100644 (file)
@@ -86,7 +86,7 @@ class core_cache_renderer extends plugin_renderer_base {
 
             $info = '';
             if (!empty($store['default'])) {
-                $info = $this->output->pix_icon('i/info', $defaultstoreactions).' ';
+                $info = $this->output->pix_icon('i/info', $defaultstoreactions, '', array('class' => 'icon'));
             }
             $htmlactions = array();
             foreach ($actions as $action) {
index 19fdfb6..bcfbf40 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.
@@ -123,7 +123,7 @@ class cachestore_file implements cache_store, cache_is_key_aware {
             }
             if ($path !== false && !is_writable($path)) {
                 $path = false;
-                debugging('The given file cache store path is not writable. '.$path, DEBUG_DEVELOPER);
+                debugging('The file cache store path is not writable for `'.$name.'`', DEBUG_DEVELOPER);
             }
         } else {
             $path = make_cache_directory('cachestore_file/'.preg_replace('#[^a-zA-Z0-9\.\-_]+#', '', $name));
@@ -151,7 +151,7 @@ class cachestore_file implements cache_store, cache_is_key_aware {
      * @return bool
      */
     public function is_ready() {
-        return ($this->path !== null);
+        return $this->isready;
     }
 
     /**
@@ -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..2ec9a94 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+/**
+ * The session data store class.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class session_data_store extends cache_store {
+
+    /**
+     * Used for the actual storage.
+     * @var array
+     */
+    private static $sessionstore = null;
+
+    /**
+     * Returns a static store by reference... REFERENCE SUPER IMPORTANT.
+     *
+     * @param string $id
+     * @return array
+     */
+    protected static function &register_store_id($id) {
+        if (is_null(self::$sessionstore)) {
+            global $SESSION;
+            if (!isset($SESSION->cachestore_session)) {
+                $SESSION->cachestore_session = array();
+            }
+            self::$sessionstore =& $SESSION->cachestore_session;
+        }
+        if (!array_key_exists($id, self::$sessionstore)) {
+            self::$sessionstore[$id] = array();
+        }
+        return self::$sessionstore[$id];
+    }
+
+    /**
+     * Flushes the data belong to the given store id.
+     * @param string $id
+     */
+    protected static function flush_store_by_id($id) {
+        unset(self::$sessionstore[$id]);
+        self::$sessionstore[$id] = array();
+    }
+
+    /**
+     * Flushes the store of all data.
+     */
+    protected static function flush_store() {
+        $ids = array_keys(self::$sessionstore);
+        unset(self::$sessionstore);
+        self::$sessionstore = array();
+        foreach ($ids as $id) {
+            self::$sessionstore[$id] = array();
+        }
+    }
+}
+
 /**
  * The Session store class.
  *
  * @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 +169,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.
      *
@@ -370,60 +399,4 @@ class cachestore_session extends session_data_store implements cache_store, cach
     public function my_name() {
         return $this->name;
     }
-}
-
-/**
- * The session data store class.
- *
- * @copyright  2012 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-abstract class session_data_store {
-
-    /**
-     * Used for the actual storage.
-     * @var array
-     */
-    private static $sessionstore = null;
-
-    /**
-     * Returns a static store by reference... REFERENCE SUPER IMPORTANT.
-     *
-     * @param string $id
-     * @return array
-     */
-    protected static function &register_store_id($id) {
-        if (is_null(self::$sessionstore)) {
-            global $SESSION;
-            if (!isset($SESSION->cachestore_session)) {
-                $SESSION->cachestore_session = array();
-            }
-            self::$sessionstore =& $SESSION->cachestore_session;
-        }
-        if (!array_key_exists($id, self::$sessionstore)) {
-            self::$sessionstore[$id] = array();
-        }
-        return self::$sessionstore[$id];
-    }
-
-    /**
-     * Flushes the data belong to the given store id.
-     * @param string $id
-     */
-    protected static function flush_store_by_id($id) {
-        unset(self::$sessionstore[$id]);
-        self::$sessionstore[$id] = array();
-    }
-
-    /**
-     * Flushes the store of all data.
-     */
-    protected static function flush_store() {
-        $ids = array_keys(self::$sessionstore);
-        unset(self::$sessionstore);
-        self::$sessionstore = array();
-        foreach ($ids as $id) {
-            self::$sessionstore[$id] = array();
-        }
-    }
 }
\ No newline at end of file
index 7a7bd5a..dcbdbd7 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+/**
+ * The static data store class
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class static_data_store extends cache_store {
+
+    /**
+     * An array for storage.
+     * @var array
+     */
+    private static $staticstore = array();
+
+    /**
+     * Returns a static store by reference... REFERENCE SUPER IMPORTANT.
+     *
+     * @param string $id
+     * @return array
+     */
+    protected static function &register_store_id($id) {
+        if (!array_key_exists($id, self::$staticstore)) {
+            self::$staticstore[$id] = array();
+        }
+        return self::$staticstore[$id];
+    }
+
+    /**
+     * Flushes the store of all values for belonging to the store with the given id.
+     * @param string $id
+     */
+    protected static function flush_store_by_id($id) {
+        unset(self::$staticstore[$id]);
+        self::$staticstore[$id] = array();
+    }
+
+    /**
+     * Flushes all of the values from all stores.
+     *
+     * @copyright  2012 Sam Hemelryk
+     * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+     */
+    protected static function flush_store() {
+        $ids = array_keys(self::$staticstore);
+        unset(self::$staticstore);
+        self::$staticstore = array();
+        foreach ($ids as $id) {
+            self::$staticstore[$id] = array();
+        }
+    }
+}
+
 /**
  * The static store class.
  *
  * @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 +165,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.
      *
@@ -370,56 +395,4 @@ class cachestore_static extends static_data_store implements cache_store, cache_
     public function my_name() {
         return $this->name;
     }
-}
-
-/**
- * The static data store class
- *
- * @copyright  2012 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-abstract class static_data_store {
-
-    /**
-     * An array for storage.
-     * @var array
-     */
-    private static $staticstore = array();
-
-    /**
-     * Returns a static store by reference... REFERENCE SUPER IMPORTANT.
-     *
-     * @param string $id
-     * @return array
-     */
-    protected static function &register_store_id($id) {
-        if (!array_key_exists($id, self::$staticstore)) {
-            self::$staticstore[$id] = array();
-        }
-        return self::$staticstore[$id];
-    }
-
-    /**
-     * Flushes the store of all values for belonging to the store with the given id.
-     * @param string $id
-     */
-    protected static function flush_store_by_id($id) {
-        unset(self::$staticstore[$id]);
-        self::$staticstore[$id] = array();
-    }
-
-    /**
-     * Flushes all of the values from all stores.
-     *
-     * @copyright  2012 Sam Hemelryk
-     * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-     */
-    protected static function flush_store() {
-        $ids = array_keys(self::$staticstore);
-        unset(self::$staticstore);
-        self::$staticstore = array();
-        foreach ($ids as $id) {
-            self::$staticstore[$id] = array();
-        }
-    }
-}
+}
\ No newline at end of file
index a15a31c..6ad2152 100644 (file)
@@ -50,6 +50,14 @@ class cache_phpunit_tests extends advanced_testcase {
         cache_config_phpunittest::create_default_configuration();
     }
 
+    /**
+     * Final task is to reset the cache system
+     */
+    public static function tearDownAfterClass() {
+        parent::tearDownAfterClass();
+        cache_factory::reset();
+    }
+
     /**
      * Tests cache configuration
      */
@@ -664,4 +672,76 @@ class cache_phpunit_tests extends advanced_testcase {
         $instance = cache_config_phpunittest::instance();
         $this->assertInstanceOf('cache_config', $instance);
     }
+
+    /**
+     * Test disabling the cache stores.
+     */
+    public function test_disable_stores() {
+        $instance = cache_config_phpunittest::instance();
+        $instance->phpunit_add_definition('phpunit/disabletest', array(
+            'mode' => cache_store::MODE_APPLICATION,
+            'component' => 'phpunit',
+            'area' => 'disabletest'
+        ));
+        $cache = cache::make('phpunit', 'disabletest');
+        $this->assertInstanceOf('cache_phpunit_application', $cache);
+        $this->assertEquals('cachestore_file', $cache->phpunit_get_store_class());
+
+        $this->assertFalse($cache->get('test'));
+        $this->assertTrue($cache->set('test', 'test'));
+        $this->assertEquals('test', $cache->get('test'));
+
+        cache_factory::disable_stores();
+
+        $cache = cache::make('phpunit', 'disabletest');
+        $this->assertInstanceOf('cache_phpunit_application', $cache);
+        $this->assertEquals('cachestore_dummy', $cache->phpunit_get_store_class());
+
+        $this->assertFalse($cache->get('test'));
+        $this->assertTrue($cache->set('test', 'test'));
+        $this->assertEquals('test', $cache->get('test'));
+    }
+
+    /**
+     * Test disabling the cache.
+     */
+    public function test_disable() {
+        global $CFG;
+
+        $configfile = $CFG->dataroot.'/muc/config.php';
+
+        // That's right, we're deleting the config file.
+        $this->assertTrue(@unlink($configfile));
+
+        // Disable the cache
+        cache_phpunit_factory::phpunit_disable();
+
+        // Check we get the expected disabled factory.
+        $factory = cache_factory::instance();
+        $this->assertInstanceOf('cache_factory_disabled', $factory);
+
+        // Check we get the expected disabled config.
+        $config = $factory->create_config_instance();
+        $this->assertInstanceOf('cache_config_disabled', $config);
+
+        // Check we get the expected disabled caches.
+        $cache = cache::make('phpunit', 'disable');
+        $this->assertInstanceOf('cache_disabled', $cache);
+
+        $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable');
+        $this->assertInstanceOf('cache_disabled', $cache);
+
+        $this->assertFalse(file_exists($configfile));
+
+        $this->assertFalse($cache->get('test'));
+        $this->assertFalse($cache->set('test', 'test'));
+        $this->assertFalse($cache->delete('test'));
+        $this->assertTrue($cache->purge());
+
+        cache_factory::reset();
+
+        $factory = cache_factory::instance(true);
+        $config = $factory->create_config_instance();
+        $this->assertEquals('cache_config_phpunittest', get_class($config));
+    }
 }
\ No newline at end of file
index 9bebea8..180d768 100644 (file)
@@ -41,6 +41,19 @@ class cache_config_phpunittest extends cache_config_writer {
      * @param array $properties
      */
     public function phpunit_add_definition($area, array $properties) {
+        if (!array_key_exists('overrideclass', $properties)) {
+            switch ($properties['mode']) {
+                case cache_store::MODE_APPLICATION:
+                    $properties['overrideclass'] = 'cache_phpunit_application';
+                    break;
+                case cache_store::MDOE_SESSION:
+                    $properties['overrideclass'] = 'cache_phpunit_session';
+                    break;
+                case cache_store::MODE_REQUEST:
+                    $properties['overrideclass'] = 'cache_phpunit_request';
+                    break;
+            }
+        }
         $this->configdefinitions[$area] = $properties;
     }
 
@@ -137,6 +150,64 @@ class cache_phpunit_dummy_datasource implements cache_data_source {
     }
 }
 
+/**
+ * PHPUnit application cache loader.
+ *
+ * Used to expose things we could not otherwise see within an application cache.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_phpunit_application extends cache_application {
+
+    /**
+     * Returns the class of the store immediately associated with this cache.
+     * @return string
+     */
+    public function phpunit_get_store_class() {
+        return get_class($this->get_store());
+    }
+
+}
+
+/**
+ * PHPUnit session cache loader.
+ *
+ * Used to expose things we could not otherwise see within an session cache.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_phpunit_session extends cache_session {
+
+    /**
+     * Returns the class of the store immediately associated with this cache.
+     * @return string
+     */
+    public function phpunit_get_store_class() {
+        return get_class($this->get_store());
+    }
+}
+
+/**
+ * PHPUnit request cache loader.
+ *
+ * Used to expose things we could not otherwise see within an request cache.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_phpunit_request extends cache_request {
+
+    /**
+     * Returns the class of the store immediately associated with this cache.
+     * @return string
+     */
+    public function phpunit_get_store_class() {
+        return get_class($this->get_store());
+    }
+}
+
 /**
  * Dummy overridden cache loader class that we can use to test overriding loader functionality.
  *
@@ -145,4 +216,21 @@ class cache_phpunit_dummy_datasource implements cache_data_source {
  */
 class cache_phpunit_dummy_overrideclass extends cache_application {
     // Satisfying the code pre-checker is just part of my day job.
+}
+
+/**
+ * Cache PHPUnit specific factory.
+ *
+ * @copyright  2012 Sam Hemelryk
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_phpunit_factory extends cache_factory {
+    /**
+     * Exposes the cache_factory's disable method.
+     *
+     * Perhaps one day that method will be made public, for the time being it is protected.
+     */
+    public static function phpunit_disable() {
+        parent::disable();
+    }
 }
\ No newline at end of file
index 3e6ba79..004a537 100644 (file)
@@ -50,6 +50,14 @@ class cache_config_writer_phpunit_tests extends advanced_testcase {
         cache_config_phpunittest::create_default_configuration();
     }
 
+    /**
+     * Final task is to reset the cache system
+     */
+    public static function tearDownAfterClass() {
+        parent::tearDownAfterClass();
+        cache_factory::reset();
+    }
+
     /**
      * Test getting an instance. Pretty basic.
      */
@@ -297,6 +305,14 @@ class cache_administration_helper_phpunit_tests extends advanced_testcase {
         cache_config_phpunittest::create_default_configuration();
     }
 
+    /**
+     * Final task is to reset the cache system
+     */
+    public static function tearDownAfterClass() {
+        parent::tearDownAfterClass();
+        cache_factory::reset();
+    }
+
     /**
      * Test the numerous summaries the helper can produce.
      */
index 14b4999..2549493 100644 (file)
@@ -322,13 +322,13 @@ function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_y
                     $popupalt  = $event->modulename;
                     $component = $event->modulename;
                 } else if ($event->courseid == SITEID) {                                // Site event
-                    $popupicon = 'c/site';
+                    $popupicon = 'i/siteevent';
                 } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) {      // Course event
-                    $popupicon = 'c/course';
+                    $popupicon = 'i/courseevent';
                 } else if ($event->groupid) {                                      // Group event
-                    $popupicon = 'c/group';
+                    $popupicon = 'i/groupevent';
                 } else if ($event->userid) {                                       // User event
-                    $popupicon = 'c/user';
+                    $popupicon = 'i/userevent';
                 }
 
                 $dayhref->set_anchor('event_'.$event->id);
@@ -607,14 +607,14 @@ function calendar_add_event_metadata($event) {
         $context = context_course::instance($module->course);
         $fullname = format_string($coursecache[$module->course]->fullname, true, array('context' => $context));
 
-        $event->icon = '<img height="16" width="16" src="'.$icon.'" alt="'.$eventtype.'" title="'.$modulename.'" style="vertical-align: middle;" />';
+        $event->icon = '<img src="'.$icon.'" alt="'.$eventtype.'" title="'.$modulename.'" class="icon" />';
         $event->referer = '<a href="'.$CFG->wwwroot.'/mod/'.$event->modulename.'/view.php?id='.$module->id.'">'.$event->name.'</a>';
         $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$module->course.'">'.$fullname.'</a>';
         $event->cmid = $module->id;
 
 
     } else if($event->courseid == SITEID) {                              // Site event
-        $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/site') . '" alt="'.get_string('globalevent', 'calendar').'" style="vertical-align: middle;" />';
+        $event->icon = '<img src="'.$OUTPUT->pix_url('i/siteevent') . '" alt="'.get_string('globalevent', 'calendar').'" class="icon" />';
         $event->cssclass = 'calendar_event_global';
     } else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) {          // Course event
         calendar_get_course_cached($coursecache, $event->courseid);
@@ -622,14 +622,14 @@ function calendar_add_event_metadata($event) {
         $context = context_course::instance($event->courseid);
         $fullname = format_string($coursecache[$event->courseid]->fullname, true, array('context' => $context));
 
-        $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/course') . '" alt="'.get_string('courseevent', 'calendar').'" style="vertical-align: middle;" />';
+        $event->icon = '<img src="'.$OUTPUT->pix_url('i/courseevent') . '" alt="'.get_string('courseevent', 'calendar').'" class="icon" />';
         $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$event->courseid.'">'.$fullname.'</a>';
         $event->cssclass = 'calendar_event_course';
     } else if ($event->groupid) {                                    // Group event
-        $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/group') . '" alt="'.get_string('groupevent', 'calendar').'" style="vertical-align: middle;" />';
+        $event->icon = '<img src="'.$OUTPUT->pix_url('i/groupevent') . '" alt="'.get_string('groupevent', 'calendar').'" class="icon" />';
         $event->cssclass = 'calendar_event_group';
     } else if($event->userid) {                                      // User event
-        $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/user') . '" alt="'.get_string('userevent', 'calendar').'" style="vertical-align: middle;" />';
+        $event->icon = '<img src="'.$OUTPUT->pix_url('i/userevent') . '" alt="'.get_string('userevent', 'calendar').'" class="icon" />';
         $event->cssclass = 'calendar_event_user';
     }
     return $event;
@@ -1153,7 +1153,7 @@ function calendar_get_block_upcoming($events, $linkhref = NULL) {
             continue;
         }
         $events[$i] = calendar_add_event_metadata($events[$i]);
-        $content .= '<div class="event"><span class="icon c0">'.$events[$i]->icon.'</span> ';
+        $content .= '<div class="event"><span class="icon c0">'.$events[$i]->icon.'</span>';
         if (!empty($events[$i]->referer)) {
             // That's an activity event, so let's provide the hyperlink
             $content .= $events[$i]->referer;
@@ -2696,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('siteevents', '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');
@@ -2718,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;
 
@@ -2798,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 731bbd3..caf3400 100644 (file)
@@ -69,7 +69,7 @@ class calendar_addsubscription_form extends moodleform {
         $mform->setType('pollinterval', PARAM_INT);
 
         // Import file
-        $mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar'));
+        $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);
@@ -80,7 +80,7 @@ class calendar_addsubscription_form extends moodleform {
         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();
@@ -105,19 +105,30 @@ class calendar_addsubscription_form extends moodleform {
      * @return array
      */
     public function validation($data, $files) {
+        global $USER;
+
         $errors = parent::validation($data, $files);
 
-        if (empty($data['url']) && empty($data['importfile'])) {
-            if (!empty($data['importfrom']) && $data['importfrom'] == CALENDAR_IMPORT_FROM_FILE) {
+        if ($data['importfrom'] == CALENDAR_IMPORT_FROM_FILE) {
+            if (empty($data['importfile'])) {
                 $errors['importfile'] = get_string('errorrequiredurlorfile', 'calendar');
             } else {
-                $errors['url'] = get_string('errorrequiredurlorfile', 'calendar');
-            }
-        } else if (!empty($data['url'])) {
-            if (clean_param($data['url'], PARAM_URL) !== $data['url']) {
-                $errors['url']  = get_string('invalidurl', 'error');
+                // Make sure the file area is not empty and contains only one file.
+                $draftitemid = $data['importfile'];
+                $fs = get_file_storage();
+                $usercontext = context_user::instance($USER->id);
+                $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id DESC', false);
+                if (count($files) !== 1) {
+                    $errors['importfile'] = get_string('errorrequiredurlorfile', 'calendar');
+                }
             }
+        } else if (($data['importfrom'] == CALENDAR_IMPORT_FROM_URL) && (clean_param($data['url'], PARAM_URL) !== $data['url'])) {
+            $errors['url']  = get_string('invalidurl', 'error');
+        } else {
+            // Shouldn't happen.
+            $errors['url'] = get_string('errorrequiredurlorfile', 'calendar');
         }
+
         return $errors;
     }
 
index 56a4d0f..d0f976b 100644 (file)
@@ -45,7 +45,7 @@ class completion_criteria_grade extends completion_criteria {
     /**
      * Finds and returns a data_object instance based on params.
      *
-     * @param array $params associative array varname => value of various 
+     * @param array $params associative array varname => value of various
      * parameters used to fetch data_object
      * @return data_object data_object instance or false if none found.
      */
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 43b1132..33bc709 100644 (file)
@@ -439,8 +439,11 @@ if (!$courses) {
         $movetocategories[$category->id] = get_string('moveselectedcoursesto');
         echo '<tr><td colspan="3" align="right">';
         echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
-        echo html_writer::select($movetocategories, 'moveto', $category->id, null, array('id'=>'movetoid'));
-        $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('movecourses', 'movetoid', false));
+        echo html_writer::select($movetocategories, 'moveto', $category->id, null, array('id'=>'movetoid', 'class' => 'autosubmit'));
+        $PAGE->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => 'movetoid', 'nothing' => $category->id))
+        );
         echo '<input type="hidden" name="id" value="'.$category->id.'" />';
         echo '</td></tr>';
     }
index 0f783e1..f26cafd 100644 (file)
@@ -213,8 +213,12 @@ class format_topics extends format_base {
         }
         if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
             $courseconfig = get_config('moodlecourse');
+            $max = $courseconfig->maxsections;
+            if (!isset($max) || !is_numeric($max)) {
+                $max = 52;
+            }
             $sectionmenu = array();
-            for ($i = 0; $i <= $courseconfig->maxsections; $i++) {
+            for ($i = 0; $i <= $max; $i++) {
                 $sectionmenu[$i] = "$i";
             }
             $courseformatoptionsedit = array(
index 3460a0a..432ad96 100644 (file)
@@ -1,7 +1,10 @@
 .course-content ul.topics {margin:0;}
-.course-content ul.topics li.section {list-style: none;margin:5px 0 0 0;padding:0;}
+.course-content ul.topics li.section {list-style: none;margin:0 0 5px 0;padding:0;}
 .course-content ul.topics li.section .content {margin:0 40px;}
-.course-content ul.topics li.section .left {width:40px;float:left;text-align:center;padding-top: 4px;}
-.course-content ul.topics li.section .right {width:40px;float:right;text-align:center;padding-top: 4px;}
+.course-content ul.topics li.section .left {float:left;}
+.course-content ul.topics li.section .right {float:right;}
+.course-content ul.topics li.section .left,
+.course-content ul.topics li.section .right {width:40px;text-align:center;padding: 6px 0;}
+.course-content ul.topics li.section .right img.icon { padding: 0 0 4px 0;}
 .course-content ul.topics li.section .left .section-handle img.icon { padding:0; vertical-align: baseline; }
 .jumpmenu {text-align:center;}
index ba1f54f..a5c1153 100644 (file)
@@ -219,7 +219,11 @@ class format_weeks extends format_base {
         if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
             $courseconfig = get_config('moodlecourse');
             $sectionmenu = array();
-            for ($i = 0; $i <= $courseconfig->maxsections; $i++) {
+            $max = $courseconfig->maxsections;
+            if (!isset($max) || !is_numeric($max)) {
+                $max = 52;
+            }
+            for ($i = 0; $i <= $max; $i++) {
                 $sectionmenu[$i] = "$i";
             }
             $courseformatoptionsedit = array(
index 17cf4cd..80d3479 100644 (file)
@@ -1,7 +1,10 @@
 .course-content ul.weeks {margin:0;}
-.course-content ul.weeks li.section {list-style: none;margin:5px 0 0 0;padding:0;}
+.course-content ul.weeks li.section {list-style: none;margin:0 0 5px 0;padding:0;}
 .course-content ul.weeks li.section .content {margin:0 40px;}
-.course-content ul.weeks li.section .left {width:40px;float:left;text-align:center;padding-top: 4px;}
-.course-content ul.weeks li.section .right {width:40px;float:right;text-align:center;padding-top: 4px;}
+.course-content ul.weeks li.section .left {float:left;}
+.course-content ul.weeks li.section .right {float:right;}
+.course-content ul.weeks li.section .left,
+.course-content ul.weeks li.section .right {width:40px;text-align:center;padding: 6px 0;}
+.course-content ul.weeks li.section .right img.icon { padding: 0 0 4px 0;}
 .course-content ul.weeks li.section .left .section-handle img.icon { padding:0; vertical-align: baseline; }
 .jumpmenu {text-align:center;}
\ No newline at end of file
index d00d393..2e17095 100644 (file)
@@ -1484,6 +1484,9 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                 $altname = get_accesshide(' '.$altname);
             }
 
+            // Start the div for the activity title, excluding the edit icons.
+            echo html_writer::start_tag('div', array('class' => 'activityinstance'));
+
             // We may be displaying this just in order to show information
             // about visibility, without the actual link
             $contentpart = '';
@@ -1498,51 +1501,49 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                         $linkclasses .= ' conditionalhidden';
                         $textclasses .= ' conditionalhidden';
                     }
-                    $accesstext = '<span class="accesshide">'.
-                        get_string('hiddenfromstudents').': </span>';
+                    $accesstext = get_accesshide(get_string('hiddenfromstudents').': ');
                 } else {
                     $accesstext = '';
                 }
                 if ($linkclasses) {
-                    $linkcss = 'class="activityinstance ' . trim($linkclasses) . '" ';
+                    $linkcss = trim($linkclasses) . ' ';
                 } else {
-                    $linkcss = 'class="activityinstance"';
+                    $linkcss = '';
                 }
                 if ($textclasses) {
-                    $textcss = 'class="' . trim($textclasses) . '" ';
+                    $textcss = trim($textclasses) . ' ';
                 } else {
                     $textcss = '';
                 }
 
                 // Get on-click attribute value if specified
                 $onclick = $mod->get_on_click();
-                if ($onclick) {
-                    $onclick = ' onclick="' . $onclick . '"';
+
+                $groupinglabel = '';
+                if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
+                    $groupings = groups_get_all_groupings($course->id);
+                    $groupinglabel = html_writer::tag('span', '('.format_string($groupings[$mod->groupingid]->name).')',
+                            array('class' => 'groupinglabel'));
                 }
 
                 if ($url = $mod->get_url()) {
-                    // Display link itself
-                    echo '<a ' . $linkcss . $mod->extra . $onclick .
-                            ' href="' . $url . '"><img src="' . $mod->get_icon_url() .
-                            '" class="iconlarge activityicon" alt="' . $mod->modfullname . '" />' .
-                            $accesstext . '<span class="instancename">' .
-                            $instancename . $altname . '</span></a>';
-
-                    // If specified, display extra content after link
+                    // Display link itself.
+                    $activitylink = html_writer::empty_tag('img', array('src' => $mod->get_icon_url(),
+                            'class' => 'iconlarge activityicon', 'alt' => $mod->modfullname)) . $accesstext .
+                            html_writer::tag('span', $instancename . $altname, array('class' => 'instancename'));
+                    echo html_writer::link($url, $activitylink, array('class' => $linkcss, 'onclick' => $onclick)) .
+                            $groupinglabel;
+
+                    // If specified, display extra content after link.
                     if ($content) {
-                        $contentpart = '<div class="' . trim('contentafterlink' . $textclasses) .
-                                '">' . $content . '</div>';
+                        $contentpart = html_writer::tag('div', $content, array('class' =>
+                                trim('contentafterlink ' . $textclasses)));
                     }
                 } else {
-                    // No link, so display only content
-                    $contentpart = '<div ' . $textcss . $mod->extra . '>' .
-                            $accesstext . $content . '</div>';
+                    // No link, so display only content.
+                    $contentpart = html_writer::tag('div', $accesstext . $content, array('class' => $textcss));
                 }
 
-                if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
-                    $groupings = groups_get_all_groupings($course->id);
-                    echo " <span class=\"groupinglabel\">(".format_string($groupings[$mod->groupingid]->name).')</span>';
-                }
             } else {
                 $textclasses = $extraclasses;
                 $textclasses .= ' dimmed_text';
@@ -1573,6 +1574,9 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
             // Module can put text after the link (e.g. forum unread)
             echo $mod->get_after_link();
 
+            // Closing the tag which contains everything but edit icons. $contentpart should not be part of this.
+            echo html_writer::end_tag('div');
+
             // If there is content but NO link (eg label), then display the
             // content here (BEFORE any icons). In this case cons must be
             // displayed after the content so that it makes more sense visually
@@ -2285,7 +2289,7 @@ function print_category_info($category, $depth=0, $showcourses = false) {
                 $courseicon = '';
                 if ($icons = enrol_get_course_info_icons($course)) {
                     foreach ($icons as $pix_icon) {
-                        $courseicon = $OUTPUT->render($pix_icon).' ';
+                        $courseicon = $OUTPUT->render($pix_icon);
                     }
                 }
 
@@ -2764,7 +2768,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
@@ -2772,25 +2776,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 {
@@ -3343,7 +3353,10 @@ function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $movesele
         );
     }
 
-    $output = html_writer::start_tag('span', array('class' => 'commands'));
+    // The space added before the <span> is a ugly hack but required to set the CSS property white-space: nowrap
+    // and having it to work without attaching the preceding text along with it. Hopefully the refactoring of
+    // the course page HTML will allow this to be removed.
+    $output = ' ' . html_writer::start_tag('span', array('class' => 'commands'));
     foreach ($actions as $action) {
         if ($action instanceof renderable) {
             $output .= $OUTPUT->render($action);
index 393ab0b..5f91966 100644 (file)
@@ -378,8 +378,11 @@ if ($courses) {
         echo "<input type=\"button\" onclick=\"checknone()\" value=\"$strdeselectall\" />\n";
         // Select box should only show categories in which user has min capability to move course.
         echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
-        echo html_writer::select($usercatlist, 'moveto', '', array(''=>get_string('moveselectedcoursesto')), array('id'=>'movetoid'));
-        $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('movecourses', 'movetoid', false));
+        echo html_writer::select($usercatlist, 'moveto', '', array(''=>get_string('moveselectedcoursesto')), array('id'=>'movetoid', 'class' => 'autosubmit'));
+        $PAGE->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => 'movetoid', 'nothing' => false))
+        );
         echo "</td>\n</tr>\n";
         echo "</table>\n</form>";
 
@@ -432,4 +435,4 @@ function print_navigation_bar($totalcount, $page, $perpage, $encodedsearch, $mod
         echo "<a href=\"search.php?search=$encodedsearch".$modulelink."&amp;perpage=".$defaultperpage."\">".get_string("showperpage", "", $defaultperpage)."</a>";
         echo "</p></center>";
     }
-}
\ No newline at end of file
+}
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');
         },
         /**
diff --git a/enrol/guest/pix/withoutpassword.png b/enrol/guest/pix/withoutpassword.png
new file mode 100644 (file)
index 0000000..f803c86
Binary files /dev/null and b/enrol/guest/pix/withoutpassword.png differ
diff --git a/enrol/guest/pix/withoutpassword.svg b/enrol/guest/pix/withoutpassword.svg
new file mode 100644 (file)
index 0000000..9fc0081
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<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="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M5.2,10.3L0,13.1V16h16v-3l-5.2-2.7C10.3,10,10.2,9.4,10.6,9c0,0,1.4-1.8,1.4-3.8C12,2.3,10.2,0,8,0\r
+       S4,2.3,4,5.2C4,7.2,5.4,9,5.4,9C5.8,9.5,5.7,10,5.2,10.3z"/>\r
+</svg>\r
diff --git a/enrol/guest/pix/withpassword.png b/enrol/guest/pix/withpassword.png
new file mode 100644 (file)
index 0000000..63198b1
Binary files /dev/null and b/enrol/guest/pix/withpassword.png differ
diff --git a/enrol/guest/pix/withpassword.svg b/enrol/guest/pix/withpassword.svg
new file mode 100644 (file)
index 0000000..e4e850b
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<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="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M3.4,9C3.4,9,2,7.2,2,5.2C2,2.3,3.8,0,6,0s4,2.3,4,5.2c0,0.1,0,0.1,0,0.2C8.8,6.2,8,7.6,8,9.2\r
+       c0,0.1,0,0.2,0,0.4l-4,4V16H0v-4l3.2-1.7C3.7,10,3.8,9.5,3.4,9z M11.8,13.6L10.4,15H10v0.4l-0.6,0.6L9,16h3v-2.3\r
+       C11.9,13.6,11.8,13.6,11.8,13.6z M16,9.2c0,1.9-1.6,3.5-3.5,3.5c-0.4,0-0.7-0.1-1-0.2L10,14H9v1l0,0H8v1l0,0H5v-2l4.1-4.1\r
+       C9,9.6,9,9.4,9,9.2c0-1.9,1.6-3.5,3.5-3.5S16,7.2,16,9.2z M14.5,8c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1s0.4,1,1,1C14,9,14.5,8.5,14.5,8\r
+       z"/>\r
+</svg>\r
diff --git a/enrol/self/pix/withkey.png b/enrol/self/pix/withkey.png
new file mode 100644 (file)
index 0000000..f2a2d16
Binary files /dev/null and b/enrol/self/pix/withkey.png differ
diff --git a/enrol/self/pix/withkey.svg b/enrol/self/pix/withkey.svg
new file mode 100644 (file)
index 0000000..6dc0520
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<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="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M9,16H1c-0.5,0-1-0.5-1-1V1c0-0.5,0.5-1,1-1h8v2H3C2.4,2,2,2.4,2,3v10c0,0.6,0.4,1,1,1h6V16z M16,5.2\r
+       c0,1.9-1.6,3.5-3.5,3.5c-0.4,0-0.7-0.1-1-0.2L10,10H9v1l0,0H8v1l0,0H5v-2l4.1-4.1C9,5.7,9,5.5,9,5.2c0-1.9,1.6-3.5,3.5-3.5\r
+       S16,3.3,16,5.2z M14.5,4c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1s0.4,1,1,1C14,5,14.5,4.6,14.5,4z"/>\r
+</svg>\r
diff --git a/enrol/self/pix/withoutkey.png b/enrol/self/pix/withoutkey.png
new file mode 100644 (file)
index 0000000..b00cdfd
Binary files /dev/null and b/enrol/self/pix/withoutkey.png differ
diff --git a/enrol/self/pix/withoutkey.svg b/enrol/self/pix/withoutkey.svg
new file mode 100644 (file)
index 0000000..0994de6
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<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="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M9,16H1c-0.5,0-1-0.5-1-1V1c0-0.5,0.5-1,1-1h8v2H3C2.4,2,2,2.4,2,3v10c0,0.6,0.4,1,1,1h6V16z M15,6.5\r
+       h-4.7l1-1c0.4-0.4,0.4-1,0-1.4l-0.7-0.7c-0.4-0.4-1-0.4-1.4,0l-4,4c-0.4,0.4-0.4,1,0,1.4l4,4c0.4,0.4,1,0.4,1.4,0l0.7-0.7\r
+       c0.4-0.4,0.4-1,0-1.4l-1.1-1.1H15c0.5,0,1-0.4,1-1v-1C16,7,15.5,6.5,15,6.5z"/>\r
+</svg>\r
index 8d7c34e..2a5e351 100644 (file)
@@ -220,9 +220,9 @@ class core_files_renderer extends plugin_renderer_base {
         <div class="fm-content-wrapper">
             <div class="fp-content"></div>
             <div class="fm-empty-container">
-                <span class="dndupload-message">'.$strdndenabledinbox.'<br/><span class="dndupload-arrow"></span></span>
+                <div class="dndupload-message">'.$strdndenabledinbox.'<br/><div class="dndupload-arrow"></div></div>
             </div>
-            <div class="dndupload-target">'.$strdroptoupload.'<br/><span class="dndupload-arrow"></span></div>
+            <div class="dndupload-target">'.$strdroptoupload.'<br/><div class="dndupload-arrow"></div></div>
             <div class="dndupload-uploadinprogress">'.$icon_progress.'</div>
         </div>
         <div class="filemanager-updating">'.$icon_progress.'</div>
index 645d73a..6dd6748 100644 (file)
@@ -1182,7 +1182,7 @@ class grade_structure {
                                 '" alt="'.s($stroutcome).'"/>';
                     } else {
                         $strmanual = get_string('manualitem', 'grades');
-                        return '<img src="'.$OUTPUT->pix_url('t/manual_item') . '" '.
+                        return '<img src="'.$OUTPUT->pix_url('i/manual_item') . '" '.
                                 'class="icon itemicon" title="'.s($strmanual).
                                 '" alt="'.s($strmanual).'"/>';
                     }
@@ -1191,7 +1191,7 @@ class grade_structure {
 
             case 'category':
                 $strcat = get_string('category', 'grades');
-                return '<img src="'.$OUTPUT->pix_url(file_folder_icon()) . '" class="icon itemicon" ' .
+                return '<img src="'.$OUTPUT->pix_url('i/folder') . '" class="icon itemicon" ' .
                         'title="'.s($strcat).'" alt="'.s($strcat).'" />';
         }
 
@@ -1573,7 +1573,8 @@ class grade_structure {
             $strparamobj->itemname = $element['object']->grade_item->itemname;
             $strnonunlockable = get_string('nonunlockableverbose', 'grades', $strparamobj);
 
-            $action = $OUTPUT->pix_icon('t/unlock_gray', $strnonunlockable);
+            $action = html_writer::tag('span', $OUTPUT->pix_icon('t/locked', $strnonunlockable),
+                    array('class' => 'action-icon'));
 
         } else if ($element['object']->is_locked()) {
             $type = 'unlock';
@@ -1639,7 +1640,7 @@ class grade_structure {
 
                 $url = new moodle_url('/grade/edit/tree/calculation.php', array('courseid' => $this->courseid, 'id' => $object->id));
                 $url = $gpr->add_url_params($url);
-                return $OUTPUT->action_icon($url, new pix_icon($icon, $streditcalculation)) . "\n";
+                return $OUTPUT->action_icon($url, new pix_icon($icon, $streditcalculation));
             }
         }
 
@@ -2274,7 +2275,7 @@ function grade_button($type, $courseid, $object) {
         $url = new moodle_url('edit.php', array('courseid' => $courseid, 'id' => $object->id));
     }
 
-    return $OUTPUT->action_icon($url, new pix_icon('t/'.$type, ${'str'.$type}));
+    return $OUTPUT->action_icon($url, new pix_icon('t/'.$type, ${'str'.$type}, '', array('class' => 'iconsmall')));
 
 }
 
index 36e493e..a0b082a 100644 (file)
@@ -140,7 +140,6 @@ $report->load_final_grades();
 
 echo $report->group_selector;
 echo '<div class="clearer"></div>';
-// echo $report->get_toggles_html();
 
 //show warnings if any
 foreach($warnings as $warning) {
index 2f9d0d4..089b168 100644 (file)
@@ -517,10 +517,11 @@ class grade_report_grader extends grade_report {
     /**
      * Builds and returns a div with on/off toggles.
      * @return string HTML code
+     * @deprecated since 2.4 as it appears not to be used any more.
      */
     public function get_toggles_html() {
         global $CFG, $USER, $COURSE, $OUTPUT;
-
+        debugging('Call to deprecated function grade_report_grader::get_toggles_html().', DEBUG_DEVELOPER);
         $html = '';
         if ($USER->gradeediting[$this->courseid]) {
             if (has_capability('moodle/grade:manage', $this->context) or has_capability('moodle/grade:hide', $this->context)) {
@@ -557,10 +558,11 @@ class grade_report_grader extends grade_report {
     * @param string $type The type of toggle
     * @param bool $return Whether to return the HTML string rather than printing it
     * @return void
+    * @deprecated since 2.4 as it appears not to be used any more.
     */
     public function print_toggle($type) {
         global $CFG, $OUTPUT;
-
+        debugging('Call to deprecated function grade_report_grader::print_toggle().', DEBUG_DEVELOPER);
         $icons = array('eyecons' => 't/hide',
                        'calculations' => 't/calc',
                        'locks' => 't/lock',
@@ -1673,6 +1675,8 @@ class grade_report_grader extends grade_report {
         $strsortdesc  = $this->get_lang_string('sortdesc', 'grades');
         $strfirstname = $this->get_lang_string('firstname');
         $strlastname  = $this->get_lang_string('lastname');
+        $iconasc = $OUTPUT->pix_icon('t/sort_asc', $strsortasc, '', array('class' => 'iconsmall sorticon'));
+        $icondesc = $OUTPUT->pix_icon('t/sort_desc', $strsortdesc, '', array('class' => 'iconsmall sorticon'));
 
         $firstlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'firstname')), $strfirstname);
         $lastlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'lastname')), $strlastname);
@@ -1681,9 +1685,9 @@ class grade_report_grader extends grade_report {
 
         if ($this->sortitemid === 'lastname') {
             if ($this->sortorder == 'ASC') {
-                $arrows['studentname'] .= print_arrow('up', $strsortasc, true);
+                $arrows['studentname'] .= $iconasc;
             } else {
-                $arrows['studentname'] .= print_arrow('down', $strsortdesc, true);
+                $arrows['studentname'] .= $icondesc;
             }
         }
 
@@ -1691,9 +1695,9 @@ class grade_report_grader extends grade_report {
 
         if ($this->sortitemid === 'firstname') {
             if ($this->sortorder == 'ASC') {
-                $arrows['studentname'] .= print_arrow('up', $strsortasc, true);
+                $arrows['studentname'] .= $iconasc;
             } else {
-                $arrows['studentname'] .= print_arrow('down', $strsortdesc, true);
+                $arrows['studentname'] .= $icondesc;
             }
         }
 
@@ -1704,9 +1708,9 @@ class grade_report_grader extends grade_report {
 
             if ($field == $this->sortitemid) {
                 if ($this->sortorder == 'ASC') {
-                    $arrows[$field] .= print_arrow('up', $strsortasc, true);
+                    $arrows[$field] .= $iconasc;
                 } else {
-                    $arrows[$field] .= print_arrow('down', $strsortdesc, true);
+                    $arrows[$field] .= $icondesc;
                 }
             }
         }
index 076c5cb..cd18dc7 100644 (file)
@@ -94,7 +94,6 @@ print_grade_page_head($COURSE->id, 'report', 'grader', $reportname);
 
 echo $report->group_selector;
 echo '<div class="clearer"></div>';
-// echo $report->get_toggles_html();
 
 //show warnings if any
 foreach($warnings as $warning) {
index c562c82..62c5ad6 100644 (file)
@@ -11,6 +11,14 @@ width:20px;
 height:20px;
 }
 
+.gradestable th img {
+    vertical-align: text-bottom;
+    padding-bottom: 0;
+}
+.gradestable th .grade_icons { margin-top: .3em; }
+.gradestable th img.sorticon { margin-left: .3em; }
+.dir-rtl .gradestable th img.sorticon { margin-left: 0; margin-right: .3em; }
+
 table#user-grades .catlevel2 {
 background-color:#f9f9f9;
 }
@@ -133,9 +141,8 @@ border-width:0 0 1px 1px;
 }
 
 table#user-grades th.categoryitem,
-table#user-grades th.courseitem,
 table#user-grades td.topleft {
-vertical-align:top;
+vertical-align: bottom;
 border-style:solid;
 border-width:0 1px;
 }
@@ -160,10 +167,10 @@ border-width:0 1px 0 0;
 }
 
 table#user-grades th.item ,
-table#user-grades th.categoryitem ,
+table#user-grades th.categoryitem,
 table#user-grades th.courseitem {
 border-bottom-color:#000;
-vertical-align:top;
+vertical-align:bottom;
 border-style:solid;
 border-width:1px;
 }
@@ -186,7 +193,9 @@ margin-right:10px;
 
 table#user-grades .quickfeedback {
 border:1px dashed #000;
+margin-left: 10px;
 }
+.dir-rtl table#user-grades .quickfeedback { margin-left: 0; margin-right: 10px;}
 
 .path-grade-report-grader #siteconfiglink {
 text-align:right;
@@ -214,7 +223,7 @@ border-color:#cecece;
 }
 
 .path-grade-report-grader th {
-padding:2px 10px 0;
+padding:2px 10px;
 }
 
 .path-grade-report-grader span.inclusion-links {
@@ -255,7 +264,7 @@ padding:0;
 table#user-grades th.categoryitem,
 table#user-grades th.courseitem,
 .path-grade-report-grader table td.topleft {
-vertical-align:top;
+vertical-align:bottom;
 border-color:#cecece #cecece #000;
 border-style:solid;
 border-width:0 1px 1px;
@@ -269,7 +278,7 @@ table#user-grades td.topleft {
 background-color:#fff;
 }
 
-.path-grade-report-grader th.user img {
+.path-grade-report-grader th.user img.userpicture {
 border:3px double #cecece;
 vertical-align:top;
 width:2.7em;
@@ -316,8 +325,6 @@ padding:0;
 
 .path-grade-report-grader td input.text {
 border:1px solid #666;
-margin-left:10px;
-margin-right:10px;
 }
 
 .path-grade-report-grader td input.submit {
@@ -463,8 +470,6 @@ width:2000px;
 float:right;
 }
 
-.path-grade-report-grader .action-icon {margin-left:0.3em;}
-
 .path-grade-report-grader .gradestable th.user,
 .path-grade-report-grader .gradestable th.range,
 .path-grade-report-grader .flexible th,
@@ -573,9 +578,12 @@ background-color:#f3ead8;
 }
 
 .path-grade-report-grader table th.usersuspended img.usersuspendedicon {
-    vertical-align:middle;
+    vertical-align: text-bottom;
+    margin-left: .45em;
 }
 
+.path-grade-report-grader .grade_icons { margin-bottom: .3em;}
+
 .path-grade-report-grader .yui-overlay {
     background-color: #FFEE69;
     border-color: #D4C237 #A6982B #A6982B;
index 2475550..90ef60f 100644 (file)
@@ -333,10 +333,11 @@ abstract class grade_report {
      */
     protected function get_sort_arrow($direction='move', $sortlink=null) {
         global $OUTPUT;
+        $pix = array('up' => 't/sort_desc', 'down' => 't/sort_asc', 'move' => 't/sort');
         $matrix = array('up' => 'desc', 'down' => 'asc', 'move' => 'desc');
         $strsort = $this->get_lang_string('sort' . $matrix[$direction]);
 
-        $arrow = print_arrow($direction, $strsort, true);
+        $arrow = $OUTPUT->pix_icon($pix[$direction], $strsort, '', array('class' => 'sorticon'));
         return html_writer::link($sortlink, $arrow, array('title'=>$strsort));
     }
 
index e36c49b..8c5bcf7 100644 (file)
@@ -45,6 +45,7 @@ if (file_exists($configfile)) {
 
 define('CLI_SCRIPT', false); // prevents some warnings later
 define('AJAX_SCRIPT', false); // prevents some warnings later
+define('CACHE_DISABLE_ALL', true); // Disables caching.. just in case.
 
 // Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
 // This is a quick hack.  Ideally we should ask the admin for a value.  See MDL-22625 for more on this.
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 9bb8e77..ba9a666 100644 (file)
@@ -3818,7 +3818,7 @@ class admin_settings_num_course_sections extends admin_setting_configselect {
     /** Lazy-load the available choices for the select box */
     public function load_choices() {
         $max = get_config('moodlecourse', 'maxsections');
-        if (empty($max)) {
+        if (!isset($max) || !is_numeric($max)) {
             $max = 52;
         }
         for ($i = 0; $i <= $max; $i++) {
@@ -5063,13 +5063,13 @@ class admin_setting_manageenrols extends admin_setting {
             if (isset($active_enrols[$enrol])) {
                 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
                 $hideshow = "<a href=\"$aurl\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/hide') . "\" class=\"icon\" alt=\"$strdisable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
                 $enabled = true;
                 $displayname = "<span>$name</span>";
             } else if (isset($enrols_available[$enrol])) {
                 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
                 $hideshow = "<a href=\"$aurl\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/show') . "\" class=\"icon\" alt=\"$strenable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
                 $enabled = false;
                 $displayname = "<span class=\"dimmed_text\">$name</span>";
             } else {
@@ -5597,14 +5597,14 @@ class admin_setting_manageauths extends admin_setting {
             // hide/show link
             if (in_array($auth, $authsenabled)) {
                 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/hide') . "\" class=\"icon\" alt=\"disable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
                 // $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\"><input type=\"checkbox\" checked /></a>";
                 $enabled = true;
                 $displayname = "<span>$name</span>";
             }
             else {
                 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/show') . "\" class=\"icon\" alt=\"enable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
                 // $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\"><input type=\"checkbox\" /></a>";
                 $enabled = false;
                 $displayname = "<span class=\"dimmed_text\">$name</span>";
@@ -5764,14 +5764,14 @@ class admin_setting_manageeditors extends admin_setting {
         // hide/show link
             if (in_array($editor, $active_editors)) {
                 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/hide') . "\" class=\"icon\" alt=\"disable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
                 // $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\"><input type=\"checkbox\" checked /></a>";
                 $enabled = true;
                 $displayname = "<span>$name</span>";
             }
             else {
                 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/show') . "\" class=\"icon\" alt=\"enable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
                 // $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\"><input type=\"checkbox\" /></a>";
                 $enabled = false;
                 $displayname = "<span class=\"dimmed_text\">$name</span>";
@@ -5896,14 +5896,14 @@ class admin_setting_managelicenses extends admin_setting {
 
             if ($value->enabled == 1) {
                 $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname,
-                    html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('i/hide'), 'class'=>'icon', 'alt'=>'disable')));
+                    html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/hide'), 'class'=>'iconsmall', 'alt'=>'disable')));
             } else {
                 $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname,
-                    html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('i/show'), 'class'=>'icon', 'alt'=>'enable')));
+                    html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/show'), 'class'=>'iconsmall', 'alt'=>'enable')));
             }
 
             if ($value->shortname == $CFG->sitedefaultlicense) {
-                $displayname .= ' '.html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('i/lock'), 'class'=>'icon', 'alt'=>get_string('default'), 'title'=>get_string('default')));
+                $displayname .= ' '.html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/locked'), 'class'=>'iconsmall', 'alt'=>get_string('default'), 'title'=>get_string('default')));
                 $hideshow = '';
             }
 
@@ -6020,12 +6020,12 @@ class admin_setting_manageformats extends admin_setting {
                     $hideshow = $txt->default;
                 } else {
                     $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
-                            $OUTPUT->pix_icon('i/hide', $txt->disable, 'moodle', array('class' => 'icon')));
+                            $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
                 }
             } else {
                 $strformatname = html_writer::tag('span', $format->displayname, array('class' => 'dimmed_text'));
                 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
-                    $OUTPUT->pix_icon('i/show', $txt->enable, 'moodle', array('class' => 'icon')));
+                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
             }
             $updown = '';
             if ($cnt) {
@@ -7710,11 +7710,11 @@ class admin_setting_managewebserviceprotocols extends admin_setting {
             // hide/show link
             if (in_array($protocol, $active_protocols)) {
                 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/hide') . "\" class=\"icon\" alt=\"$strdisable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
                 $displayname = "<span>$name</span>";
             } else {
                 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
-                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/show') . "\" class=\"icon\" alt=\"$strenable\" /></a>";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
                 $displayname = "<span class=\"dimmed_text\">$name</span>";
             }
 
index 05d24d4..1dd79dd 100644 (file)
@@ -259,9 +259,9 @@ class completion_info {
         global $PAGE, $OUTPUT;
         $result = '';
         if ($this->is_enabled() && !$PAGE->user_is_editing() && isloggedin() && !isguestuser()) {
-            $result .= '<span id = "completionprogressid" class="completionprogress">'.get_string('yourprogress','completion').' ';
-            $result .= $OUTPUT->help_icon('completionicons', 'completion');
-            $result .= '</span>';
+            $result .= html_writer::tag('div', get_string('yourprogress','completion') .
+                    $OUTPUT->help_icon('completionicons', 'completion'), array('id' => 'completionprogressid',
+                    'class' => 'completionprogress'));
         }
         return $result;
     }
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 d630e21..7242366 100644 (file)
@@ -1486,6 +1486,20 @@ function xmldb_main_upgrade($oldversion) {
         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;
index 013319c..09fcada 100644 (file)
@@ -199,20 +199,20 @@ class tiynce_subplugins_settings extends admin_setting {
                 $displayname = html_writer::tag('span', $name, array('class'=>'error'));
             } else if ($plugininfo->is_enabled()) {
                 $url = new moodle_url('/lib/editor/tinymce/subplugins.php', array('sesskey'=>sesskey(), 'return'=>'settings', 'disable'=>$name));
-                $hideshow = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/hide'), 'class'=>'icon', 'alt'=>$strdisable));
+                $hideshow = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/hide'), 'class'=>'iconsmall', 'alt'=>$strdisable));
                 $hideshow = html_writer::link($url, $hideshow);
                 $displayname = html_writer::tag('span', $namestr);
             } else {
                 $url = new moodle_url('/lib/editor/tinymce/subplugins.php', array('sesskey'=>sesskey(), 'return'=>'settings', 'enable'=>$name));
-                $hideshow = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/show'), 'class'=>'icon', 'alt'=>$strenable));
+                $hideshow = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/show'), 'class'=>'iconsmall', 'alt'=>$strenable));
                 $hideshow = html_writer::link($url, $hideshow);
                 $displayname = html_writer::tag('span', $namestr, array('class'=>'dimmed_text'));
             }
 
             if ($PAGE->theme->resolve_image_location('icon', 'tinymce_' . $name, false)) {
-                $icon = $OUTPUT->pix_icon('icon', '', 'tinymce_' . $name, array('class' => 'smallicon pluginicon'));
+                $icon = $OUTPUT->pix_icon('icon', '', 'tinymce_' . $name, array('class' => 'icon pluginicon'));
             } else {
-                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'smallicon pluginicon noicon'));
+                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
             }
             $displayname  = $icon . ' ' . $displayname;
 
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 e5e1830..d64b619 100644 (file)
@@ -1631,7 +1631,7 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
                     $value = '';
                     // If we have a default value then export it.
                     if (isset($this->_defaultValues[$varname])) {
-                        $value = array($varname => $this->_defaultValues[$varname]);
+                        $value = $this->prepare_fixed_value($varname, $this->_defaultValues[$varname]);
                     }
                 } else {
                     $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
@@ -1662,6 +1662,29 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
         return $unfiltered;
     }
 
+    /**
+     * This is a bit of a hack, and it duplicates the code in
+     * HTML_QuickForm_element::_prepareValue, but I could not think of a way or
+     * reliably calling that code. (Think about date selectors, for example.)
+     * @param string $name the element name.
+     * @param mixed $value the fixed value to set.
+     * @return mixed the appropriate array to add to the $unfiltered array.
+     */
+    protected function prepare_fixed_value($name, $value) {
+        if (null === $value) {
+            return null;
+        } else {
+            if (!strpos($name, '[')) {
+                return array($name => $value);
+            } else {
+                $valueAry = array();
+                $myIndex  = "['" . str_replace(array(']', '['), array('', "']['"), $name) . "']";
+                eval("\$valueAry$myIndex = \$value;");
+                return $valueAry;
+            }
+        }
+    }
+
     /**
      * Adds a validation rule for the given field
      *
index 6044ea1..574deb7 100644 (file)
@@ -340,11 +340,16 @@ M.util.init_maximised_embed = function(Y, id) {
         if (Y.Lang.isString(el)) {
             el = Y.one('#' + el);
         }
-        var val = el.getStyle(prop);
-        if (val == 'auto') {
-            val = el.getComputedStyle(prop);
+        // Ensure element exists.
+        if (el) {
+            var val = el.getStyle(prop);
+            if (val == 'auto') {
+                val = el.getComputedStyle(prop);
+            }
+            return parseInt(val);
+        } else {
+            return 0;
         }
-        return parseInt(val);
     };
 
     var resize_object = function() {
@@ -376,8 +381,15 @@ M.util.init_maximised_embed = function(Y, id) {
 
 /**
  * Attach handler to single_select
+ *
+ * This code was deprecated in Moodle 2.4 and will be removed in Moodle 2.6
+ *
+ * Please see lib/yui/formautosubmit/formautosubmit.js for its replacement
  */
 M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
+    if (M.cfg.developerdebug) {
+        Y.log("You are using a deprecated function call (M.util.init_select_autosubmit). Please look at rewriting your call to use moodle-core-formautosubmit");
+    }
     Y.use('event-key', function() {
         var select = Y.one('#'+selectid);
         if (select) {
@@ -451,6 +463,9 @@ M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
  * This function has accessability issues and also does not use the formid passed through as a parameter.
  */
 M.util.init_url_select = function(Y, formid, selectid, nothing) {
+    if (M.cfg.developerdebug) {
+        Y.log("You are using a deprecated function call (M.util.init_url_select). Please look at rewriting your call to use moodle-core-formautosubmit");
+    }
     YUI().use('node', function(Y) {
         Y.on('change', function() {
             if ((nothing == false && Y.Lang.isBoolean(nothing)) || Y.one('#'+selectid).get('value') != nothing) {
@@ -1456,14 +1471,20 @@ M.util.help_popups = {
 M.util.help_icon = {
     Y : null,
     instance : null,
-    add : function(Y, properties) {
-        this.Y = Y;
-        properties.node = Y.one('#'+properties.id);
-        if (properties.node) {
-            properties.node.on('click', this.display, this, properties);
+    initialised : false,
+    setup : function(Y) {
+        if (this.initialised) {
+            // Exit early if we have already completed setup
+            return;
         }
+        this.Y = Y;
+        Y.one('body').delegate('click', this.display, 'span.helplink a', this);
+        this.initialised = true;
+    },
+    add : function(Y, properties) {
+        this.setup(Y);
     },
-    display : function(event, args) {
+    display : function(event) {
         event.preventDefault();
         if (M.util.help_icon.instance === null) {
             var Y = M.util.help_icon.Y;
@@ -1493,7 +1514,7 @@ M.util.help_icon = {
                         //  Hide the menu if the user clicks outside of its content
                         boundingBox.get("ownerDocument").on("mousedown", function (event) {
                             var oTarget = event.target;
-                            var menuButton = Y.one("#"+args.id);
+                            var menuButton = this.helplink;
 
                             if (!oTarget.compareTo(menuButton) &&
                                 !menuButton.contains(oTarget) &&
@@ -1510,28 +1531,24 @@ M.util.help_icon = {
                         this.overlay.hide();
                     },
 
-                    display : function(event, args) {
-                        if (Y.one('html').get('dir') == 'rtl') {
-                            var overlayPosition = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.LC];
+                    display : function(event) {
+                        var overlayPosition;
+                        this.helplink = event.target.ancestor('span.helplink a', true);
+                        if (Y.one('html').get('dir') === 'rtl') {
+                            overlayPosition = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.LC];
                         } else {
-                            var overlayPosition = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC];
+                            overlayPosition = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC];
                         }
 
-                        this.helplink = args.node;
-
                         this.overlay.set('bodyContent', Y.Node.create('<img src="'+M.cfg.loadingicon+'" class="spinner" />'));
-                        this.overlay.set("align", {node:args.node, points: overlayPosition});
-
-                        var fullurl = args.url;
-                        if (!args.url.match(/https?:\/\//)) {
-                            fullurl = M.cfg.wwwroot + args.url;
-                        }
-
-                        var ajaxurl = fullurl + '&ajax=1';
+                        this.overlay.set("align", {node:this.helplink, points: overlayPosition});
 
                         var cfg = {
                             method: 'get',
                             context : this,
+                            data : {
+                                ajax : 1
+                            },
                             on: {
                                 success: function(id, o, node) {
                                     this.display_callback(o.responseText);
@@ -1546,7 +1563,7 @@ M.util.help_icon = {
                             }
                         };
 
-                        Y.io(ajaxurl, cfg);
+                        Y.io(this.helplink.get('href'), cfg);
                         this.overlay.show();
                     },
 
@@ -1562,10 +1579,10 @@ M.util.help_icon = {
                 };
                 help_content_overlay.init();
                 M.util.help_icon.instance = help_content_overlay;
-                M.util.help_icon.instance.display(event, args);
+                M.util.help_icon.instance.display(event);
             });
         } else {
-            M.util.help_icon.instance.display(event, args);
+            M.util.help_icon.instance.display(event);
         }
     },
     init : function(Y) {
@@ -1891,7 +1908,7 @@ M.util.load_flowplayer = function() {
                                 object.width = width;
                                 object.height = height;
                             }
-                               }
+                        }
                     }
                 });
             }
index 62648b8..9236a11 100644 (file)
@@ -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();
         }
index 0fd2496..1f2fa1d 100644 (file)
@@ -3567,7 +3567,7 @@ class settings_navigation extends navigation_node {
         if ($course->legacyfiles == 2 and has_capability('moodle/course:managefiles', $coursecontext)) {
             // hidden in new courses and courses where legacy files were turned off
             $url = new moodle_url('/files/index.php', array('contextid'=>$coursecontext->id));
-            $coursenode->add(get_string('courselegacyfiles'), $url, self::TYPE_SETTING, null, 'coursefiles', new pix_icon('i/files', ''));
+            $coursenode->add(get_string('courselegacyfiles'), $url, self::TYPE_SETTING, null, 'coursefiles', new pix_icon('i/folder', ''));
 
         }
 
@@ -4182,7 +4182,7 @@ class settings_navigation extends navigation_node {
         if ($course->legacyfiles == 2 and has_capability('moodle/course:managefiles', $this->context)) {
             //hiden in new installs
             $url = new moodle_url('/files/index.php', array('contextid'=>$coursecontext->id, 'itemid'=>0, 'component' => 'course', 'filearea'=>'legacy'));
-            $frontpage->add(get_string('sitelegacyfiles'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/files', ''));
+            $frontpage->add(get_string('sitelegacyfiles'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/folder', ''));
         }
         return $frontpage;
     }
index 55d38f1..9d11581 100644 (file)
@@ -1476,6 +1476,11 @@ class core_renderer extends renderer_base {
             $select->attributes['title'] = $select->tooltip;
         }
 
+        $select->attributes['class'] = 'autosubmit';
+        if ($select->class) {
+            $select->attributes['class'] .= ' ' . $select->class;
+        }
+
         if ($select->label) {
             $output .= html_writer::label($select->label, $select->attributes['id'], false, $select->labelattributes);
         }
@@ -1491,7 +1496,10 @@ class core_renderer extends renderer_base {
         $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));
+        $this->page->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => $select->attributes['id'], 'nothing' => $nothing))
+        );
 
         // then div wrapper for xhtml strictness
         $output = html_writer::tag('div', $output);
@@ -1557,6 +1565,11 @@ class core_renderer extends renderer_base {
             $output .= html_writer::label($select->label, $select->attributes['id'], false, $select->labelattributes);
         }
 
+        $select->attributes['class'] = 'autosubmit';
+        if ($select->class) {
+            $select->attributes['class'] .= ' ' . $select->class;
+        }
+
         if ($select->helpicon instanceof help_icon) {
             $output .= $this->render($select->helpicon);
         } else if ($select->helpicon instanceof old_help_icon) {
@@ -1614,7 +1627,10 @@ class core_renderer extends renderer_base {
             $go = html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('go')));
             $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));
+            $this->page->requires->yui_module('moodle-core-formautosubmit',
+                'M.core.init_formautosubmit',
+                array(array('selectid' => $select->attributes['id'], 'nothing' => $nothing))
+            );
         } else {
             $output .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>$select->showbutton));
         }
@@ -1944,11 +1960,9 @@ class core_renderer extends renderer_base {
         $title = get_string('helpprefix2', '', trim($title, ". \t"));
 
         $attributes = array('href'=>$url, 'title'=>$title, 'aria-haspopup' => 'true');
-        $id = html_writer::random_id('helpicon');
-        $attributes['id'] = $id;
         $output = html_writer::tag('a', $output, $attributes);
 
-        $this->page->requires->js_init_call('M.util.help_icon.add', array(array('id'=>$id, 'url'=>$url->out(false))));
+        $this->page->requires->js_init_call('M.util.help_icon.setup');
         $this->page->requires->string_for_js('close', 'form');
 
         // and finally span
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 eaed39a..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() {
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 41e1bc4..71f922f 100644 (file)
@@ -1804,10 +1804,10 @@ function question_pluginfile($course, $context, $component, $filearea, $args, $f
         send_file_not_found();
     }
 
-    list($context, $course, $cm) = get_context_info_array($context->id);
-    require_login($course, false, $cm);
-
     if ($filearea === 'export') {
+        list($context, $course, $cm) = get_context_info_array($context->id);
+        require_login($course, false, $cm);
+
         require_once($CFG->dirroot . '/question/editlib.php');
         $contexts = new question_edit_contexts($context);
         // check export capability
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 1b53e75..3037136 100644 (file)
@@ -138,12 +138,20 @@ if (!defined('PHPUNIT_TEST')) {
     define('PHPUNIT_TEST', false);
 }
 
+// When set to true MUC (Moodle caching) will be disabled as much as possible.
+// A special cache factory will be used to handle this situation and will use special "disabled" equivalents objects.
+// This ensure we don't attempt to read or create the config file, don't use stores, don't provide persistence or
+// storage of any kind.
+if (!defined('CACHE_DISABLE_ALL')) {
+    define('CACHE_DISABLE_ALL', false);
+}
+
 // When set to true MUC (Moodle caching) will not use any of the defined or default stores.
 // The Cache API will continue to function however this will force the use of the cachestore_dummy so all requests
 // will be interacting with a static property and will never go to the proper cache stores.
 // Useful if you need to avoid the stores for one reason or another.
-if (!defined('NO_CACHE_STORES')) {
-    define('NO_CACHE_STORES', false);
+if (!defined('CACHE_DISABLE_STORES')) {
+    define('CACHE_DISABLE_STORES', false);
 }
 
 // Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
index d100654..01c2f58 100644 (file)
@@ -1444,7 +1444,12 @@ border-color:black; background-color:#ffffee; border-style:solid; border-radius:
 width: 80%; -moz-border-radius: 20px; padding: 15px">
 ' . $message . '
 </div>';
-        if (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER) {
+        // Check whether debug is set.
+        $debug = (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER);
+        // Also check we have it set in the config file. This occurs if the method to read the config table from the
+        // database fails, reading from the config table is the first database interaction we have.
+        $debug = $debug || (!empty($CFG->config_php_settings['debug'])  && $CFG->config_php_settings['debug'] >= DEBUG_DEVELOPER );
+        if ($debug) {
             if (!empty($debuginfo)) {
                 $debuginfo = s($debuginfo); // removes all nasty JS
                 $debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines
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 3bee4ba..783300b 100644 (file)
@@ -21,6 +21,8 @@ information provided here is intended especially for developers.
 YUI changes:
 * moodle-enrol-notification has been renamed to moodle-core-notification
 * YUI2 code must now use 2in3, see http://yuilibrary.com/yui/docs/yui/yui-yui2.html
+* M.util.init_select_autosubmit() and M.util.init_url_select() have been deprecated. Code using this should be updated
+  to use moodle-core-formautosubmit
 
 Unit testing changes:
 * output debugging() is not sent to standard output any more,
index 7bd0d97..8adb97a 100644 (file)
@@ -1419,6 +1419,10 @@ function install_core($version, $verbose) {
     global $CFG, $DB;
 
     try {
+        // Disable the use of cache stores here. We will reset the factory after we've performed the installation.
+        // This ensures that we don't permanently cache anything during installation.
+        cache_factory::disable_stores();
+
         set_time_limit(600);
         print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag
 
@@ -1442,6 +1446,9 @@ function install_core($version, $verbose) {
         admin_apply_default_settings(NULL, true);
 
         print_upgrade_part_end(null, true, $verbose);
+
+        // Reset the cache, this returns it to a normal operation state.
+        cache_factory::reset();
     } catch (exception $ex) {
         upgrade_handle_exception($ex);
     }
@@ -1463,6 +1470,9 @@ function upgrade_core($version, $verbose) {
     try {
         // Reset caches before any output
         purge_all_caches();
+        // Disable the use of cache stores here. We will reset the factory after we've performed the installation.
+        // This ensures that we don't permanently cache anything during installation.
+        cache_factory::disable_stores();
 
         // Upgrade current language pack if we can
         upgrade_language_pack();
@@ -1495,7 +1505,9 @@ function upgrade_core($version, $verbose) {
         // Update core definitions.
         cache_helper::update_definitions(true);
 
-        // Reset caches again, just to be sure
+        // Reset the cache, this returns it to a normal operation state.
+        cache_factory::reset();
+        // Purge caches again, just to be sure we arn't holding onto old stuff now.
         purge_all_caches();
 
         // Clean up contexts - more and more stuff depends on existence of paths and contexts
@@ -1523,11 +1535,18 @@ function upgrade_noncore($verbose) {
 
     // upgrade all plugins types
     try {
+        // Disable the use of cache stores here. We will reset the factory after we've performed the installation.
+        // This ensures that we don't permanently cache anything during installation.
+        cache_factory::disable_stores();
+
         $plugintypes = get_plugin_types();
         foreach ($plugintypes as $type=>$location) {
             upgrade_plugins($type, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose);
         }
+        // Update cache definitions. Involves scanning each plugin for any changes.
         cache_helper::update_definitions();
+        // Reset the cache system to a normal state.
+        cache_factory::reset();
     } catch (Exception $ex) {
         upgrade_handle_exception($ex);
     }
diff --git a/lib/yui/formautosubmit/formautosubmit.js b/lib/yui/formautosubmit/formautosubmit.js
new file mode 100644 (file)
index 0000000..01d6668
--- /dev/null
@@ -0,0 +1,108 @@
+YUI.add('moodle-core-formautosubmit',
+    function(Y) {
+        // The CSS selectors we use
+        var CSS = {
+            AUTOSUBMIT : 'autosubmit'
+        };
+
+        var FORMAUTOSUBMITNAME = 'core-formautosubmit';
+
+        var FORMAUTOSUBMIT = function() {
+            FORMAUTOSUBMIT.superclass.constructor.apply(this, arguments);
+        }
+
+        //  We only want to initialize the module fully once
+        var INITIALIZED = false;
+
+        Y.extend(FORMAUTOSUBMIT, Y.Base, {
+
+            /**
+              * Initialize the module
+              */
+            initializer : function(config) {
+                // We only apply the delegation once
+                if (!INITIALIZED) {
+                    INITIALIZED = true;
+                    var applyto = Y.one('body');
+
+                    // We don't listen for change events by default as using the keyboard triggers these too.
+                    applyto.delegate('key', this.process_changes, 'press:13', 'select.' + CSS.AUTOSUBMIT, this);
+                    applyto.delegate('click', this.process_changes, 'select.' + CSS.AUTOSUBMIT, this);
+
+                    if (Y.UA.os == 'macintosh' && Y.UA.webkit) {
+                        // Macintosh webkit browsers like change events, but non-macintosh webkit browsers don't.
+                        applyto.delegate('change', this.process_changes, 'select.' + CSS.AUTOSUBMIT, this);
+                    }
+                    if (Y.UA.ios) {
+                        // IOS doesn't trigger click events because it's touch-based.
+                        applyto.delegate('change', this.process_changes, 'select.' + CSS.AUTOSUBMIT, this);
+                    }
+                }
+
+                // Assign this select items 'nothing' value and lastindex (current value)
+                var thisselect = Y.one('select#' + this.get('selectid'));
+                thisselect.setData('nothing', this.get('nothing'));
+                thisselect.setData('startindex', thisselect.get('selectedIndex'));
+            },
+
+            /**
+             * Check whether the select element was changed
+             */
+            check_changed : function(e) {
+                var select = e.target.ancestor('select.' + CSS.AUTOSUBMIT, true);
+                if (!select) {
+                    return false;
+                }
+
+                var nothing = select.getData('nothing');
+                var startindex = select.getData('startindex');
+                var currentindex = select.get('selectedIndex');
+
+                var previousindex = select.getAttribute('data-previousindex');
+                select.setAttribute('data-previousindex', currentindex);
+                if (!previousindex) {
+                    previousindex = startindex;
+                }
+
+                // Check whether the field has changed, and is not the 'nothing' value
+                if ((nothing===false || select.get('value') != nothing) && startindex != select.get('selectedIndex') && currentindex != previousindex) {
+                    return select;
+                }
+                return false;
+            },
+
+            /**
+             * Process any changes
+             */
+            process_changes : function(e) {
+                var select = this.check_changed(e);
+                if (select) {
+                    var form = select.ancestor('form', true);
+                    form.submit();
+                }
+            }
+        },
+        {
+            NAME : FORMAUTOSUBMITNAME,
+            ATTRS : {
+                selectid : {
+                    'value' : ''
+                },
+                nothing : {
+                    'value' : ''
+                },
+                ignorechangeevent : {
+                    'value' : false
+                }
+            }
+        });
+
+        M.core = M.core || {};
+        M.core.init_formautosubmit = M.core.init_formautosubmit || function(config) {
+            return new FORMAUTOSUBMIT(config);
+        };
+    },
+    '@VERSION@', {
+        requires : ['base', 'event-key']
+    }
+);
index 84be3a2..14d5a68 100644 (file)
@@ -1270,7 +1270,10 @@ function message_print_user ($user=false, $iscontact=false, $isblocked=false, $i
         echo $OUTPUT->user_picture($USER, array('size' => 20, 'courseid' => SITEID));
     } else {
         echo $OUTPUT->user_picture($user, array('size' => 20, 'courseid' => SITEID));
-        echo '&nbsp;';
+
+        $link = new moodle_url("/message/index.php?id=$user->id");
+        echo $OUTPUT->action_link($link, fullname($user), null, array('title' =>
+                get_string('sendmessageto', 'message', fullname($user))));
 
         $return = false;
         $script = null;
@@ -1279,27 +1282,12 @@ function message_print_user ($user=false, $iscontact=false, $isblocked=false, $i
         } else {
             message_contact_link($user->id, 'add', $return, $script, $includeicontext);
         }
-        echo '&nbsp;';
+
         if ($isblocked) {
             message_contact_link($user->id, 'unblock', $return, $script, $includeicontext);
         } else {
             message_contact_link($user->id, 'block', $return, $script, $includeicontext);
         }
-
-        $popupoptions = array(
-                'height' => MESSAGE_DISCUSSION_HEIGHT,
-                'width' => MESSAGE_DISCUSSION_WIDTH,
-                'menubar' => false,
-                'location' => false,
-                'status' => true,
-                'scrollbars' => true,
-                'resizable' => true);
-
-        $link = new moodle_url("/message/index.php?id=$user->id");
-        //$action = new popup_action('click', $link, "message_$user->id", $popupoptions);
-        $action = null;
-        echo $OUTPUT->action_link($link, fullname($user), $action, array('title' => get_string('sendmessageto', 'message', fullname($user))));
-
     }
 }
 
@@ -1351,14 +1339,14 @@ function message_contact_link($userid, $linktype='add', $return=false, $script=n
                 $iconpath = 't/block';
                 break;
             case 'unblock':
-                $iconpath = 't/userblue';
+                $iconpath = 't/unblock';
                 break;
             case 'remove':
-                $iconpath = 'i/cross_red_big';
+                $iconpath = 't/removecontact';
                 break;
             case 'add':
             default:
-                $iconpath = 't/addgreen';
+                $iconpath = 't/addcontact';
         }
 
         $img = '<img src="'.$OUTPUT->pix_url($iconpath).'" class="iconsmall" alt="'.$safealttext.'" />';
@@ -1405,9 +1393,9 @@ function message_history_link($userid1, $userid2, $return=false, $keywords='', $
     }
 
     if ($linktext == 'icon') {  // Icon only
-        $fulllink = '<img src="'.$OUTPUT->pix_url('t/log') . '" class="iconsmall" alt="'.$strmessagehistory.'" />';
+        $fulllink = '<img src="'.$OUTPUT->pix_url('t/messages') . '" class="iconsmall" alt="'.$strmessagehistory.'" />';
     } else if ($linktext == 'both') {  // Icon and standard name
-        $fulllink = '<img src="'.$OUTPUT->pix_url('t/log') . '" class="iconsmall" alt="" />';
+        $fulllink = '<img src="'.$OUTPUT->pix_url('t/messages') . '" class="iconsmall" alt="" />';
         $fulllink .= '&nbsp;'.$strmessagehistory;
     } else if ($linktext) {    // Custom name
         $fulllink = $linktext;
@@ -1831,8 +1819,7 @@ function message_print_message_history($user1,$user2,$search='',$messagelimit=0,
     echo html_writer::end_tag('td');
 
     echo html_writer::start_tag('td', array('align' => 'center'));
-    echo html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/left'), 'alt' => get_string('from')));
-    echo html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/right'), 'alt' => get_string('to')));
+    echo html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('i/twoway'), 'alt' => ''));
     echo html_writer::end_tag('td');
 
     echo html_writer::start_tag('td', array('align' => 'center', 'id' => 'user2'));
index f92022a..ba82093 100644 (file)
@@ -74,8 +74,8 @@ class core_message_renderer extends plugin_renderer_base {
             } else if ($processor->enabled) {
                 $url = new moodle_url('/admin/message.php', array('disable' => $processor->id, 'sesskey' => sesskey()));
                 $enable->text = html_writer::link($url, html_writer::empty_tag('img',
-                    array('src'   => $this->output->pix_url('i/hide'),
-                          'class' => 'icon',
+                    array('src'   => $this->output->pix_url('t/hide'),
+                          'class' => 'iconsmall',
                           'title' => get_string('outputenabled', 'message'),
                           'alt'   => get_string('outputenabled', 'message'),
                     )
@@ -84,8 +84,8 @@ class core_message_renderer extends plugin_renderer_base {
                 $name->attributes['class'] = 'dimmed_text';
                 $url = new moodle_url('/admin/message.php', array('enable' => $processor->id, 'sesskey' => sesskey()));
                 $enable->text = html_writer::link($url, html_writer::empty_tag('img',
-                    array('src'   => $this->output->pix_url('i/show'),
-                          'class' => 'icon',
+                    array('src'   => $this->output->pix_url('t/show'),
+                          'class' => 'iconsmall',
                           'title' => get_string('outputdisabled', 'message'),
                           'alt'   => get_string('outputdisabled', 'message'),
                     )
index fcc5311..a84c4cb 100644 (file)
@@ -187,16 +187,16 @@ class assign_plugin_manager {
             $visible = !get_config($this->subtype . '_' . $plugin, 'disabled');
 
             if ($visible) {
-                $row[] = $this->format_icon_link('hide', $plugin, 'i/hide', get_string('disable'));
+                $row[] = $this->format_icon_link('hide', $plugin, 't/hide', get_string('disable'));
             } else {
-                $row[] = $this->format_icon_link('show', $plugin, 'i/show', get_string('enable'));
+                $row[] = $this->format_icon_link('show', $plugin, 't/show', get_string('enable'));
             }
 
             $movelinks = '';
             if (!$idx == 0) {
                 $movelinks .= $this->format_icon_link('moveup', $plugin, 't/up', get_string('up'));
             } else {
-                $movelinks .= $OUTPUT->spacer(array('width'=>15));
+                $movelinks .= $OUTPUT->spacer(array('width'=>16));
             }
             if ($idx != count($plugins) - 1) {
                 $movelinks .= $this->format_icon_link('movedown', $plugin, 't/down', get_string('down'));
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..6ed295e 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,15 @@ class assign_feedback_file extends assign_feedback_plugin {
     public function save(stdClass $grade, stdClass $data) {
         $fileoptions = $this->get_file_options();
 
+        // The element name may have been for a different user.
+        foreach ($data as $key => $value) {
+            if (strpos($key, 'files_') === 0 && strpos($key, '_filemanager')) {
+                $elementname = substr($key, 0, strpos($key, '_filemanager'));
+            }
+        }
+
         $data = file_postupdate_standard_filemanager($data,
-                                                     'files',
+                                                     $elementname,
                                                      $fileoptions,
                                                      $this->assignment->get_context(),
                                                      'assignfeedback_file',
index d0985e8..91a8957 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';
@@ -222,6 +223,8 @@ $string['quickgradingchangessaved'] = 'The grade changes were saved';
 $string['quickgrading_help'] = 'Quick grading allows you to assign grades (and outcomes) directly in the submissions table. Quick grading is not compatible with advanced grading and is not recommended when there are multiple markers.';
 $string['requiresubmissionstatement'] = 'Require that students accept the submission statement';
 $string['requiresubmissionstatement_help'] = 'Require that students accept the submission statement for all assignment submissions for this entire Moodle installation. If this setting is not enabled, then submission statements can be enabled or disabled in the settings for each assignment.';
+$string['requiresubmissionstatementassignment'] = 'Require that students accept the submission statement';
+$string['requiresubmissionstatementassignment_help'] = 'Require that students accept the submission statement for all submissions to this assignment.';
 $string['requireallteammemberssubmit'] = 'Require all group members submit';
 $string['requireallteammemberssubmit_help'] = 'If enabled, all members of the student group must click the submit button for this assignment before the group submission will be considered as submitted. If disabled, the group submission will be considered as submitted as soon as any member of the student group clicks the submit button.';
 $string['recordid'] = 'Identifier';
@@ -270,6 +273,7 @@ $string['submissionsclosed'] = 'Submissions closed';
 $string['submissionsettings'] = 'Submission settings';
 $string['submissionstatement'] = 'Submission statement';
 $string['submissionstatement_help'] = 'Assignment submission confirmation statement';
+$string['submissionstatementdefault'] = 'This assignment is my own work, except where I have acknowledged the use of the works of other people.';
 $string['submissionstatementacceptedlog'] = 'Submission statement accepted by user {$a}';
 $string['submissionstatus_draft'] = 'Draft (not submitted)';
 $string['submissionstatusheading'] = 'Submission status';
index c9a78b6..9a13f6d 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_'&n