Merge branch 'MDL-30700-23' of git://github.com/damyon/moodle into MOODLE_23_STABLE
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Thu, 13 Dec 2012 09:32:38 +0000 (10:32 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Thu, 13 Dec 2012 09:32:38 +0000 (10:32 +0100)
68 files changed:
.gitignore
admin/tool/phpunit/cli/util.php
admin/tool/uploaduser/index.php
auth/ldap/auth.php
backup/converter/moodle1/handlerlib.php
backup/converter/moodle1/lib.php
backup/moodle2/restore_stepslib.php
backup/util/loggers/file_logger.class.php
backup/util/loggers/output_indented_logger.class.php
backup/util/loggers/output_text_logger.class.php
blocks/community/block_community.php
blocks/glossary_random/block_glossary_random.php
blocks/html/backup/moodle1/lib.php [new file with mode: 0644]
blocks/online_users/block_online_users.php
blocks/rss_client/backup/moodle1/lib.php [new file with mode: 0644]
composer.json [new file with mode: 0644]
course/lib.php
course/rest.php
enrol/README.txt
enrol/manual/yui/quickenrolment/quickenrolment.js
enrol/paypal/ipn.php
grade/edit/outcome/edit_form.php
lang/en/error.php
lib/datalib.php
lib/dml/pdo_moodle_database.php
lib/dml/tests/dml_test.php
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/excellib.class.php
lib/filelib.php
lib/filestorage/tests/file_storage_test.php
lib/moodlelib.php
lib/navigationlib.php
lib/phpunit/classes/hint_resultprinter.php
lib/phpunit/readme.md
lib/sessionlib.php
lib/setuplib.php
lib/tablelib.php
lib/tests/moodlelib_test.php
lib/tests/pagelib_test.php
login/token.php
message/lib.php
mod/assign/gradingbatchoperationsform.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/renderer.php
mod/assign/submission/comments/locallib.php
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/upgradelib.php
mod/lesson/format.php
mod/quiz/summary.php
mod/scorm/lib.php
mod/scorm/locallib.php
mod/wiki/pagelib.php
mod/wiki/parser/utils.php
mod/workshop/lib.php
question/format.php
question/format/blackboard_six/formatbase.php
question/format/blackboard_six/formatpool.php
question/format/blackboard_six/formatqti.php
question/format/blackboard_six/tests/blackboardformatpool_test.php
question/format/blackboard_six/tests/blackboardsixformatqti_test.php
question/format/xml/format.php
question/type/calculated/questiontype.php
question/type/multianswer/renderer.php
report/log/locallib.php
theme/base/style/question.css
webservice/externallib.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 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 380db45..0d97168 100644 (file)
@@ -900,7 +900,11 @@ if ($formdata = $mform2->is_cancelled()) {
                     $newgroupdata = new stdClass();
                     $newgroupdata->name = $addgroup;
                     $newgroupdata->courseid = $ccache[$shortname]->id;
-                    if ($ccache[$shortname]->groups[$addgroup]->id = groups_create_group($newgroupdata)){
+                    $newgroupdata->description = '';
+                    $gid = groups_create_group($newgroupdata);
+                    if ($gid){
+                        $ccache[$shortname]->groups[$addgroup] = new stdClass();
+                        $ccache[$shortname]->groups[$addgroup]->id   = $gid;
                         $ccache[$shortname]->groups[$addgroup]->name = $newgroupdata->name;
                     } else {
                         $upt->track('enrolments', get_string('unknowngroup', 'error', s($addgroup)), 'error');
index 88bb10d..1a6bac8 100644 (file)
@@ -685,7 +685,7 @@ class auth_plugin_ldap extends auth_plugin_base {
 /// User removal
         // Find users in DB that aren't in ldap -- to be removed!
         // this is still not as scalable (but how often do we mass delete?)
-        if ($this->config->removeuser !== AUTH_REMOVEUSER_KEEP) {
+        if ($this->config->removeuser != AUTH_REMOVEUSER_KEEP) {
             $sql = 'SELECT u.*
                       FROM {user} u
                       LEFT JOIN {tmp_extuser} e ON (u.username = e.username AND u.mnethostid = e.mnethostid)
index 51cad64..a2744c2 100644 (file)
@@ -88,10 +88,13 @@ abstract class moodle1_handlers_factory {
         foreach ($plugins as $name => $dir) {
             $handlerfile  = $dir . '/backup/moodle1/lib.php';
             $handlerclass = "moodle1_{$type}_{$name}_handler";
-            if (!file_exists($handlerfile)) {
+            if (file_exists($handlerfile)) {
+                require_once($handlerfile);
+            } elseif ($type == 'block') {
+                $handlerclass = "moodle1_block_generic_handler";
+            } else {
                 continue;
             }
-            require_once($handlerfile);
 
             if (!class_exists($handlerclass)) {
                 throw new moodle1_convert_exception('missing_handler_class', $handlerclass);
@@ -1989,9 +1992,93 @@ abstract class moodle1_resource_successor_handler extends moodle1_mod_handler {
  */
 abstract class moodle1_block_handler extends moodle1_plugin_handler {
 
+    public function get_paths() {
+        $blockname = strtoupper($this->pluginname);
+        return array(
+            new convert_path('block', "/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/{$blockname}"),
+        );
+    }
+
+    public function process_block(array $data) {
+        $newdata = $this->convert_common_block_data($data);
+
+        $this->write_block_xml($newdata, $data);
+        $this->write_inforef_xml($newdata, $data);
+        $this->write_roles_xml($newdata, $data);
+
+        return $data;
+    }
+
+    protected function convert_common_block_data(array $olddata) {
+        $newdata = array();
+
+        $newdata['blockname'] = $olddata['name'];
+        $newdata['parentcontextid'] = $this->converter->get_contextid(CONTEXT_COURSE, 0);
+        $newdata['showinsubcontexts'] = 0;
+        $newdata['pagetypepattern'] = $olddata['pagetype'].='-*';
+        $newdata['subpagepattern'] = null;
+        $newdata['defaultregion'] = ($olddata['position']=='l')?'side-pre':'side-post';
+        $newdata['defaultweight'] = $olddata['weight'];
+        $newdata['configdata'] = $this->convert_configdata($olddata);
+
+        return $newdata;
+    }
+
+    protected function convert_configdata(array $olddata) {
+        return $olddata['configdata'];
+    }
+
+    protected function write_block_xml($newdata, $data) {
+        $contextid = $this->converter->get_contextid(CONTEXT_BLOCK, $data['id']);
+
+        $this->open_xml_writer("course/blocks/{$data['name']}_{$data['id']}/block.xml");
+        $this->xmlwriter->begin_tag('block', array('id' => $data['id'], 'contextid' => $contextid));
+
+        foreach ($newdata as $field => $value) {
+            $this->xmlwriter->full_tag($field, $value);
+        }
+
+        $this->xmlwriter->begin_tag('block_positions');
+        $this->xmlwriter->begin_tag('block_position', array('id' => 1));
+        $this->xmlwriter->full_tag('contextid', $newdata['parentcontextid']);
+        $this->xmlwriter->full_tag('pagetype', $data['pagetype']);
+        $this->xmlwriter->full_tag('subpage', '');
+        $this->xmlwriter->full_tag('visible', $data['visible']);
+        $this->xmlwriter->full_tag('region', $newdata['defaultregion']);
+        $this->xmlwriter->full_tag('weight', $newdata['defaultweight']);
+        $this->xmlwriter->end_tag('block_position');
+        $this->xmlwriter->end_tag('block_positions');
+        $this->xmlwriter->end_tag('block');
+        $this->close_xml_writer();
+    }
+
+    protected function write_inforef_xml($newdata, $data) {
+        $this->open_xml_writer("course/blocks/{$data['name']}_{$data['id']}/inforef.xml");
+        $this->xmlwriter->begin_tag('inforef');
+        // Subclasses may provide inforef contents if needed
+        $this->xmlwriter->end_tag('inforef');
+        $this->close_xml_writer();
+    }
+
+    protected function write_roles_xml($newdata, $data) {
+        // This is an empty shell, as the moodle1 converter doesn't handle user data.
+        $this->open_xml_writer("course/blocks/{$data['name']}_{$data['id']}/roles.xml");
+        $this->xmlwriter->begin_tag('roles');
+        $this->xmlwriter->full_tag('role_overrides', '');
+        $this->xmlwriter->full_tag('role_assignments', '');
+        $this->xmlwriter->end_tag('roles');
+        $this->close_xml_writer();
+    }
 }
 
 
+/**
+ * Base class for block generic handler
+ */
+class moodle1_block_generic_handler extends moodle1_block_handler {
+
+}
+
 /**
  * Base class for the activity modules' subplugins
  */
index bdfb47c..9edda48 100644 (file)
@@ -254,7 +254,7 @@ class moodle1_converter extends base_converter {
             $path = '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentblock;
 
         } else if (strpos($path, '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK') === 0) {
-            $path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentmod, $path);
+            $path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentblock, $path);
         }
 
         if ($path !== $data['path']) {
@@ -350,12 +350,12 @@ class moodle1_converter extends base_converter {
         }
 
         if ($path === '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK') {
-            $this->currentmod = null;
+            $this->currentblock = null;
             $forbidden = true;
 
         } else if (strpos($path, '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK') === 0) {
             // expand the BLOCK paths so that they contain the module name
-            $path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentmod, $path);
+            $path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentblock, $path);
         }
 
         if (empty($this->pathelements[$path])) {
@@ -397,7 +397,7 @@ class moodle1_converter extends base_converter {
             $path = '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentblock;
 
         } else if (strpos($path, '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK') === 0) {
-            $path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentmod, $path);
+            $path = str_replace('/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK', '/MOODLE_BACKUP/COURSE/BLOCKS/BLOCK/' . $this->currentblock, $path);
         }
 
         if (empty($this->pathelements[$path])) {
index 4b1e09b..3abea43 100644 (file)
@@ -2913,9 +2913,6 @@ class restore_create_categories_and_questions extends restore_structure_step {
             $data->penalty = 1;
         }
 
-        $data->timecreated  = $this->apply_date_offset($data->timecreated);
-        $data->timemodified = $this->apply_date_offset($data->timemodified);
-
         $userid = $this->get_mappingid('user', $data->createdby);
         $data->createdby = $userid ? $userid : $this->task->get_userid();
 
index e4ab4b1..5c05380 100644 (file)
@@ -75,7 +75,7 @@ class file_logger extends base_logger {
         if (substr($this->fullpath, -5) !== '.html') {
             $content = $prefix . str_repeat('  ', $depth) . $message . PHP_EOL;
         } else {
-            $content = $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES) . '<br/>' . PHP_EOL;
+            $content = $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
         }
         if (false === fwrite($this->fhandle, $content)) {
             throw new base_logger_exception('error_writing_file', $this->fullpath);
index 8bb6811..2eaea5b 100644 (file)
@@ -38,7 +38,7 @@ class output_indented_logger extends base_logger {
         if (defined('STDOUT')) {
             echo $prefix . str_repeat('  ', $depth) . $message . PHP_EOL;
         } else {
-            echo $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES) . '<br/>' . PHP_EOL;
+            echo $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
         }
         flush();
         return true;
index fe61536..9d13dfd 100644 (file)
@@ -37,7 +37,7 @@ class output_text_logger extends base_logger {
         if (defined('STDOUT')) {
             echo $prefix . $message . PHP_EOL;
         } else {
-            echo $prefix . htmlentities($message, ENT_QUOTES) . '<br/>' . PHP_EOL;
+            echo $prefix . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
         }
         flush();
         return true;
index 35e3f2e..bef1b69 100644 (file)
@@ -69,7 +69,7 @@ class block_community extends block_list {
         }
 
         $icon = html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('i/group'),
-                    'class' => 'icon', 'alt' => get_string('addcourse', 'block_community')));
+                    'class' => 'icon', 'alt' => ""));
         $addcourseurl = new moodle_url('/blocks/community/communitycourse.php',
                         array('add' => true, 'courseid' => $this->page->course->id));
         $searchlink = html_writer::tag('a', $icon . '&nbsp;' . get_string('addcourse', 'block_community'),
index 102a167..c06d078 100644 (file)
@@ -137,6 +137,7 @@ class block_glossary_random extends block_base {
             $this->config->cache = '';
             $this->instance_config_commit();
 
+            $this->content = new stdClass();
             $this->content->text   = get_string('notyetconfigured','block_glossary_random');
             $this->content->footer = '';
             return $this->content;
diff --git a/blocks/html/backup/moodle1/lib.php b/blocks/html/backup/moodle1/lib.php
new file mode 100644 (file)
index 0000000..d4a491f
--- /dev/null
@@ -0,0 +1,46 @@
+<?php\r
+\r
+/**\r
+ * Provides support for the conversion of moodle1 backup to the moodle2 format\r
+ *\r
+ * @package    block_html\r
+ * @copyright  2012 Paul Nicholls\r
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\r
+ */\r
+\r
+defined('MOODLE_INTERNAL') || die();\r
+\r
+/**\r
+ * Block conversion handler for html\r
+ */\r
+class moodle1_block_html_handler extends moodle1_block_handler {\r
+    private $fileman = null;\r
+    protected function convert_configdata(array $olddata) {\r
+        $instanceid = $olddata['id'];\r
+        $contextid  = $this->converter->get_contextid(CONTEXT_BLOCK, $olddata['id']);\r
+        $configdata = unserialize(base64_decode($olddata['configdata']));\r
+\r
+        // get a fresh new file manager for this instance\r
+        $this->fileman = $this->converter->get_file_manager($contextid, 'block_html');\r
+\r
+        // convert course files embedded in the block content\r
+        $this->fileman->filearea = 'content';\r
+        $this->fileman->itemid   = 0;\r
+        $configdata->text = moodle1_converter::migrate_referenced_files($configdata->text, $this->fileman);\r
+        $configdata->format = FORMAT_HTML;\r
+\r
+        return base64_encode(serialize($configdata));\r
+    }\r
+\r
+    protected function write_inforef_xml($newdata, $data) {\r
+        $this->open_xml_writer("course/blocks/{$data['name']}_{$data['id']}/inforef.xml");\r
+        $this->xmlwriter->begin_tag('inforef');\r
+        $this->xmlwriter->begin_tag('fileref');\r
+        foreach ($this->fileman->get_fileids() as $fileid) {\r
+            $this->write_xml('file', array('id' => $fileid));\r
+        }\r
+        $this->xmlwriter->end_tag('fileref');\r
+        $this->xmlwriter->end_tag('inforef');\r
+        $this->close_xml_writer();\r
+    }\r
+}
\ No newline at end of file
index 2efe9a2..77f8a69 100644 (file)
@@ -151,12 +151,13 @@ class block_online_users extends block_base {
                 $timeago = format_time($now - $user->lastaccess); //bruno to calculate correctly on frontpage
 
                 if (isguestuser($user)) {
-                    $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16));
+                    $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16, 'alttext'=>false));
                     $this->content->text .= get_string('guestuser').'</div>';
 
                 } else {
-                    $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16));
-                    $this->content->text .= '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->page->course->id.'" title="'.$timeago.'">'.$user->fullname.'</a></div>';
+                    $this->content->text .= '<div class="user">';
+                    $this->content->text .= '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->page->course->id.'" title="'.$timeago.'">';
+                    $this->content->text .= $OUTPUT->user_picture($user, array('size'=>16, 'alttext'=>false, 'link'=>false)) .$user->fullname.'</a></div>';
                 }
                 if ($canshowicon and ($USER->id != $user->id) and !isguestuser($user)) {  // Only when logged in and messaging active etc
                     $anchortagcontents = '<img class="iconsmall" src="'.$OUTPUT->pix_url('t/message') . '" alt="'. get_string('messageselectadd') .'" />';
diff --git a/blocks/rss_client/backup/moodle1/lib.php b/blocks/rss_client/backup/moodle1/lib.php
new file mode 100644 (file)
index 0000000..95da634
--- /dev/null
@@ -0,0 +1,34 @@
+<?php\r
+\r
+/**\r
+ * Provides support for the conversion of moodle1 backup to the moodle2 format\r
+ *\r
+ * @package    block_rss_client\r
+ * @copyright  2012 Paul Nicholls\r
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\r
+ */\r
+\r
+defined('MOODLE_INTERNAL') || die();\r
+\r
+/**\r
+ * Block conversion handler for rss_client\r
+ */\r
+class moodle1_block_rss_client_handler extends moodle1_block_handler {\r
+    public function process_block(array $data) {\r
+        parent::process_block($data);\r
+        $instanceid = $data['id'];\r
+        $contextid = $this->converter->get_contextid(CONTEXT_BLOCK, $data['id']);\r
+\r
+        // Moodle 1.9 backups do not include sufficient data to restore feeds, so we need an empty shell rss_client.xml\r
+        // for the restore process to find\r
+        $this->open_xml_writer("course/blocks/{$data['name']}_{$instanceid}/rss_client.xml");\r
+        $this->xmlwriter->begin_tag('block', array('id' => $instanceid, 'contextid' => $contextid, 'blockname' => 'rss_client'));\r
+        $this->xmlwriter->begin_tag('rss_client', array('id' => $instanceid));\r
+        $this->xmlwriter->full_tag('feeds', '');\r
+        $this->xmlwriter->end_tag('rss_client');\r
+        $this->xmlwriter->end_tag('block');\r
+        $this->close_xml_writer();\r
+\r
+        return $data;\r
+    }\r
+}
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644 (file)
index 0000000..5f618de
--- /dev/null
@@ -0,0 +1,6 @@
+{
+    "require-dev": {
+        "phpunit/phpunit": "3.7.*",
+        "phpunit/dbUnit": "1.2.*"
+    }
+}
\ No newline at end of file
index 26961c3..b9f93bc 100644 (file)
@@ -600,7 +600,7 @@ function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
         $firstField = format_string($courses[$log->course], true, array('context' => $coursecontext));
         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
         $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action, $log->info);
-        $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);\r
+        $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
         $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action.' ('.$actionurl.')', $log->info);
         $text = implode("\t", $row);
         echo $text." \n";
@@ -1710,14 +1710,25 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                         } else {
                             $extraclass = '';
                         }
-                        echo "
-<form class='togglecompletion$extraclass' method='post' action='".$CFG->wwwroot."/course/togglecompletion.php'><div>
-<input type='hidden' name='id' value='{$mod->id}' />
-<input type='hidden' name='modulename' value='".s($mod->name)."' />
-<input type='hidden' name='sesskey' value='".sesskey()."' />
-<input type='hidden' name='completionstate' value='$newstate' />
-<input type='image' src='$imgsrc' alt='$imgalt' title='$imgtitle' />
-</div></form>";
+                        echo html_writer::start_tag('form', array(
+                                'class' => 'togglecompletion' . $extraclass,
+                                'method' => 'post',
+                                'action' => $CFG->wwwroot . '/course/togglecompletion.php'));
+                        echo html_writer::start_tag('div');
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'id', 'value' => $mod->id));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'modulename',
+                                'value' => $mod->name));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'completionstate',
+                                'value' => $newstate));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'image', 'src' => $imgsrc, 'alt' => $imgalt, 'title' => $imgtitle));
+                        echo html_writer::end_tag('div');
+                        echo html_writer::end_tag('form');
                     } else {
                         // In auto mode, or when editing, the icon is just an image
                         echo "<span class='autocompletion'>";
@@ -1869,7 +1880,7 @@ function print_section_add_menus($course, $section, $modnames, $vertical=false,
         // The module chooser link
         $modchooser = html_writer::start_tag('div', array('class' => 'mdl-right'));
         $modchooser.= html_writer::start_tag('div', array('class' => 'section-modchooser'));
-        $icon = $OUTPUT->pix_icon('t/add', $straddeither);
+        $icon = $OUTPUT->pix_icon('t/add', '');
         $span = html_writer::tag('span', $straddeither, array('class' => 'section-modchooser-text'));
         $modchooser .= html_writer::tag('span', $icon . $span, array('class' => 'section-modchooser-link'));
         $modchooser.= html_writer::end_tag('div');
index c013097..a7b55c9 100644 (file)
@@ -150,6 +150,7 @@ switch($requestmethod) {
                         break;
                     case 'updatetitle':
                         require_capability('moodle/course:manageactivities', $modcontext);
+                        require_once($CFG->libdir . '/gradelib.php');
                         $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST);
                         $module = new stdClass();
                         $module->id = $cm->instance;
@@ -167,6 +168,12 @@ switch($requestmethod) {
                             $module->name = $cm->name;
                         }
 
+                        // Attempt to update the grade item if relevant
+                        $grademodule = $DB->get_record($cm->modname, array('id' => $cm->instance));
+                        $grademodule->cmidnumber = $cm->idnumber;
+                        $grademodule->modname = $cm->modname;
+                        grade_update_mod_grades($grademodule);
+
                         // We need to return strings after they've been through filters for multilang
                         $stringoptions = new stdClass;
                         $stringoptions->context = $coursecontext;
index 70ce970..d6a1887 100644 (file)
@@ -3,102 +3,7 @@ ENROLMENT MODULES
 
 (Yes, that's the correct English spelling  ;-) )
 
-enrol.class.php contains a simple 'factory' method that
-will instantiate your class when called. For an example
-of a complete class, take a look at the 'manual' class.
-
-Each plugin is in a subfolder here.
-
-Except for the configuration methods, most methods
-defined in the API are optional -- callers will use
-method_exists() to determine whether your plugin offers
-the functionality they are after.
-
-
-Mandatory methods
-=================
-
-  config_form()
-  process_config()
-
-
-Login-time methods
-==================
-
-  Before Moodle 1.7
-  -----------------
-
-      get_student_courses()
-      get_teacher_courses()
-
-  You probably will want to offer at least get_student_courses().
-
-  These methods are triggered when a user logs in successfully,
-  and they are expected to populate $USER->student and
-  $USER->teacher arrays and maintain (add/delete) entries from
-  user_students and user_teachers.
-
-  These methods are relevant for most plugins, and are the main
-  interest for plugins that work with a read-only backend such
-  as LDAP or a database.
-
-  Note that with the multi-enrol infrastructure two things have
-  changed. We now have an 'enrol' field in those tables, and
-  each plugin must maintain only its own enrolment records.
-  Conversely, the $USER->student and ->teacher arrays have the
-  enrolment type as value, like
-
-     $USER->student = array ( $courseid => $plugintype );
-
-
-  Moodle 1.7 and later
-  --------------------
-
-      setup_enrolments()
-
-  With the advent of roles, there could well not be students and
-  teachers any more, so enrolment plugins have to be more flexible
-  about how they map outside data to the internal roles.
-
-  This one method should do everything, calling functions from
-  lib/accesslib.php as necessary to set up relationships.
-
-
-Interactive enrolment methods
-=============================
-
-  print_entry()
-  check_entry()
-  check_group_entry()
-  get_access_icons()
-
-These methods are for enrolment plugins that allow for user
-driven enrolment. These methods are relevant for plugins
-that implement payment gateways (credit card, paypal),
-as well as "magic password" schemes.
-
-Only one interactive enrolment method can be active for
-a given course. The site default can be set from
-Admin->Enrolment, and then individual courses can be
-set to specific interactive enrolment methods.
-
-
-Cron
-====
-
-If your class offers a cron() method, it will be invoked by
-the standard Moodle cron every time it is called. Note that if the
-tasks are not lightweight you must control how frequently they
-execute, perhaps offering a config option.
-
-For really heavy cron processing, an alternative is to have
-a separate script to be called separately. Currently the LDAP
-and DB plugins have external scripts.
-
-
-Guilty Parties
---------------
-
-Martin Dougiamas and Shane Elliott, Moodle.com
-Martin Langhoff and Patrick Li, Catalyst IT
+All enrolment modules must extend base class enrol_plugin
+which is defined in lib/enrollib.php. You can find documentation
+of each method in the base class.
 
index c1963aa..b4c7692 100644 (file)
@@ -149,6 +149,11 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             }
 
             this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url(collapsedimage, 'moodle'));
+            this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).once('click', function() {
+                // We want to do this just once, the first time the controls are shown.
+                this.populateStartDates();
+                this.populateDuration();
+            }, this);
             this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).on('click', function(){
                 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).toggleClass(CSS.ACTIVE);
                 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).toggleClass(CSS.HIDDEN);
@@ -158,10 +163,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                     this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/expanded', 'moodle'));
                 }
             }, this);
-
             this.populateAssignableRoles();
-            this.populateStartDates();
-            this.populateDuration();
         },
         populateAssignableRoles : function() {
             this.on('assignablerolesloaded', function(){
@@ -200,9 +202,10 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             var select = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.DURATION+' select');
             var defaultvalue = this.get(UEP.DEFAULTDURATION);
             var index = 0, count = 0;
+            var durationdays = M.util.get_string('durationdays', 'enrol', '{a}');
             for (var i = 1; i <= 365; i++) {
                 count++;
-                var option = create('<option value="'+i+'">'+M.util.get_string('durationdays', 'enrol', i)+'</option>');
+                var option = create('<option value="'+i+'">'+durationdays.replace('{a}', i)+'</option>');
                 if (i == defaultvalue) {
                     index = count;
                 }
index 530cb85..86951db 100644 (file)
@@ -172,7 +172,7 @@ if (strlen($result) > 0) {
 
         }
 
-        if ($data->business != $plugin->get_config('paypalbusiness')) {   // Check that the email is the one we want it to be
+        if (textlib::strtolower($data->business) !== textlib::strtolower($plugin->get_config('paypalbusiness'))) {   // Check that the email is the one we want it to be
             message_paypal_error_to_admin("Business email is {$data->business} (not ".
                     $plugin->get_config('paypalbusiness').")", $data);
             die;
index fe2c682..2cd24c5 100644 (file)
@@ -121,7 +121,7 @@ class edit_outcome_form extends moodleform {
             if (empty($courseid)) {
                 $mform->hardFreeze('standard');
 
-            } else if (empty($outcome->courseid) and !has_capability('moodle/grade:manage', get_context_instance(CONTEXT_SYSTEM))) {
+            } else if (!has_capability('moodle/grade:manage', get_context_instance(CONTEXT_SYSTEM))) {
                 $mform->hardFreeze('standard');
 
             } else if ($coursecount and empty($outcome->courseid)) {
index 7ad032c..9640c74 100644 (file)
@@ -432,6 +432,7 @@ $string['refoundtoorigi'] = 'Refunded to original amount: {$a}';
 $string['remotedownloaderror'] = 'Download of component to your server failed, please verify proxy settings, PHP cURL extension is highly recommended.<br /><br />You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.';
 $string['remotedownloadnotallowed'] = 'Download of components to your server isn\'t allowed (allow_url_fopen is disabled).<br /><br />You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.';
 $string['reportnotavailable'] = 'This type of report is only available for the site course';
+$string['requirecorrectaccess'] = 'Invalid url or port.';
 $string['requireloginerror'] = 'Course or activity not accessible.';
 $string['restorechecksumfailed'] = 'Some problem happened with the restore information stored in your session. Please check your PHP memory/DB package size limits. Restore stopped.';
 $string['restore_path_element_missingmethod'] = 'Restore method {$a} is missing. It must be defined by a developer.';
index dde0f20..d17e3e1 100644 (file)
@@ -1694,7 +1694,7 @@ function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user
     $timenow = time();
     $info = $info;
     if (!empty($url)) { // could break doing html_entity_decode on an empty var.
-        $url = html_entity_decode($url);
+        $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
     } else {
         $url = '';
     }
index 4aae9ce..61b161c 100644 (file)
@@ -172,7 +172,7 @@ abstract class pdo_moodle_database extends moodle_database {
      * Function to print/save/ignore debugging messages related to SQL queries.
      */
     protected function debug_query($sql, $params = null) {
-        echo '<hr /> (', $this->get_dbtype(), '): ',  htmlentities($sql);
+        echo '<hr /> (', $this->get_dbtype(), '): ',  htmlentities($sql, ENT_QUOTES, 'UTF-8');
         if($params) {
             echo ' (parameters ';
             print_r($params);
index e056b53..39481ad 100644 (file)
@@ -3813,7 +3813,7 @@ class dml_testcase extends database_driver_testcase {
         $this->assertEquals("Firstname Surname", $DB->get_field_sql($sql, $params));
     }
 
-    function sql_sql_order_by_text() {
+    function test_sql_order_by_text() {
         $DB = $this->tdb;
         $dbman = $DB->get_manager();
 
index 2209291..0b72fa9 100644 (file)
@@ -687,6 +687,7 @@ $string['table_dlg:bordercolor'] = 'Border color';
 $string['table_dlg:caption'] = 'Table caption';
 $string['table_dlg:cell_all'] = 'Update all cells in table';
 $string['table_dlg:cell_cell'] = 'Update current cell';
+$string['table_dlg:cell_col'] = 'Update all cells in column';
 $string['table_dlg:cell_limit'] = 'You\'ve exceeded the maximum number of cells of {$cells}.';
 $string['table_dlg:cell_row'] = 'Update all cells in row';
 $string['table_dlg:cell_title'] = 'Table cell properties';
index 44315ca..69205d9 100644 (file)
@@ -74,8 +74,8 @@ class MoodleExcelWorkbook {
      * @param string $name Name of the sheet
      * @return object MoodleExcelWorksheet
      */
-    function &add_worksheet($name = '') {
-    /// Create the Moodle Worksheet. Returns one pointer to it
+    function add_worksheet($name = '') {
+        // Create the Moodle Worksheet. Returns one pointer to it
         $ws = new MoodleExcelWorksheet ($name, $this->pear_excel_workbook, $this->latin_output);
         return $ws;
     }
@@ -138,6 +138,9 @@ class MoodleExcelWorksheet {
      */
     function MoodleExcelWorksheet($name, &$workbook, $latin_output=false) {
 
+        // Replace any characters in the name that Excel cannot cope with.
+        $name = strtr($name, '[]*/\?:', '       ');
+
         if (strlen($name) > 31) {
             // Excel does not seem able to cope with sheet names > 31 chars.
             // With $latin_output = false, it does not cope at all.
index fd3c612..ce5c160 100644 (file)
@@ -2250,7 +2250,7 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
             $options = new stdClass();
             $options->newlines = false;
             $options->noclean = true;
-            $text = htmlentities($pathisstring ? $path : implode('', file($path)));
+            $text = htmlentities($pathisstring ? $path : implode('', file($path)), ENT_QUOTES, 'UTF-8');
             $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
 
             readstring_accel($output, $mimetype, false);
index dfc4fa0..049acfe 100644 (file)
@@ -67,6 +67,10 @@ class filestoragelib_testcase extends advanced_testcase {
     public function test_get_file_preview(stored_file $file) {
         global $CFG;
 
+        if (empty($CFG->gdversion)) {
+            $this->markTestSkipped('GD extension is disabled');
+        }
+
         $this->resetAfterTest(true);
         $fs = get_file_storage();
 
index 32697fa..bf3bdc1 100644 (file)
@@ -1128,7 +1128,7 @@ function clean_param($param, $type) {
 
         case PARAM_TIMEZONE:    //can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'
             $param = fix_utf8($param);
-            $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3]|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
+            $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3](\.0)?|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
             if (preg_match($timezonepattern, $param)) {
                 return $param;
             } else {
index 046307b..f092010 100644 (file)
@@ -3515,9 +3515,9 @@ class settings_navigation extends navigation_node {
                         continue;
                     }
                     if ($type->modclass == MOD_CLASS_RESOURCE) {
-                        $resources[html_entity_decode($type->type)] = $type->typestr;
+                        $resources[html_entity_decode($type->type, ENT_QUOTES, 'UTF-8')] = $type->typestr;
                     } else {
-                        $activities[html_entity_decode($type->type)] = $type->typestr;
+                        $activities[html_entity_decode($type->type, ENT_QUOTES, 'UTF-8')] = $type->typestr;
                     }
                 }
             } else {
@@ -4491,7 +4491,7 @@ class navigation_json {
         }
 
         if ($child->forcetitle || $child->title !== $child->text) {
-            $attributes['title'] = htmlentities($child->title);
+            $attributes['title'] = htmlentities($child->title, ENT_QUOTES, 'UTF-8');
         }
         if (array_key_exists($child->key.':'.$child->type, $this->expandable)) {
             $attributes['expandable'] = $child->key;
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 5af9c06..0bf70c4 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 --dev`
+
+
+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 be15e14..b40927d 100644 (file)
@@ -1069,6 +1069,10 @@ function get_moodle_cookie() {
 function session_set_user($user) {
     $_SESSION['USER'] = $user;
     unset($_SESSION['USER']->description); // conserve memory
+    if (isset($_SESSION['USER']->lang)) {
+        // Make sure it is a valid lang pack name.
+        $_SESSION['USER']->lang = clean_param($_SESSION['USER']->lang, PARAM_LANG);
+    }
     sesskey(); // init session key
 
     if (PHPUNIT_TEST) {
index 51f8d82..9b03ff0 100644 (file)
@@ -356,7 +356,7 @@ function default_exception_handler($ex) {
     }
 
     if (is_early_init($info->backtrace)) {
-        echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
+        echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo, $info->errorcode);
     } else {
         try {
             if ($DB) {
@@ -370,7 +370,7 @@ function default_exception_handler($ex) {
             // so we just print at least something instead of "Exception thrown without a stack frame in Unknown on line 0":-(
             if (CLI_SCRIPT or AJAX_SCRIPT) {
                 // just ignore the error and send something back using the safest method
-                echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
+                echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo, $info->errorcode);
             } else {
                 echo bootstrap_renderer::early_error_content($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
                 $outinfo = get_exception_info($out_ex);
@@ -761,6 +761,20 @@ function initialise_fullme() {
             if (!defined('NO_MOODLE_COOKIES')) {
                 define('NO_MOODLE_COOKIES', true);
             }
+            // The login/token.php script should call the correct url/port.
+            if (defined('REQUIRE_CORRECT_ACCESS') && REQUIRE_CORRECT_ACCESS) {
+                $wwwrootport = empty($wwwroot['port'])?'':$wwwroot['port'];
+                $calledurl = $rurl['host'];
+                if (!empty($rurl['port'])) {
+                    $calledurl .=  ':'. $rurl['port'];
+                }
+                $correcturl = $wwwroot['host'];
+                if (!empty($wwwrootport)) {
+                    $correcturl .=  ':'. $wwwrootport;
+                }
+                throw new moodle_exception('requirecorrectaccess', 'error', '', null,
+                    'You called ' . $calledurl .', you should have called ' . $correcturl);
+            }
             redirect($CFG->wwwroot, get_string('wwwrootmismatch', 'error', $CFG->wwwroot), 3);
         }
     }
@@ -1497,7 +1511,7 @@ width: 80%; -moz-border-radius: 20px; padding: 15px">
      * @param string $debuginfo extra information for developers
      * @return string
      */
-    public static function early_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
+    public static function early_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null, $errorcode = null) {
         global $CFG;
 
         if (CLI_SCRIPT) {
@@ -1525,6 +1539,7 @@ width: 80%; -moz-border-radius: 20px; padding: 15px">
                     $e->stacktrace = format_backtrace($backtrace, true);
                 }
             }
+            $e->errorcode  = $errorcode;
             @header('Content-Type: application/json; charset=utf-8');
             echo json_encode($e);
             return;
index 4204a79..62bb077 100644 (file)
@@ -1504,7 +1504,7 @@ class table_spreadsheet_export_format_parent extends table_default_export_format
     }
 
     function start_table($sheettitle) {
-        $this->worksheet =& $this->workbook->add_worksheet($sheettitle);
+        $this->worksheet = $this->workbook->add_worksheet($sheettitle);
         $this->rownum=0;
     }
 
index c26bab7..f5045c3 100644 (file)
@@ -979,9 +979,23 @@ class moodlelib_testcase extends advanced_testcase {
             '0'                              => '0',
             '0.0'                            => '0.0',
             '0.5'                            => '0.5',
+            '9.0'                            => '9.0',
+            '-9.0'                           => '-9.0',
+            '+9.0'                           => '+9.0',
+            '9.5'                            => '9.5',
+            '-9.5'                           => '-9.5',
+            '+9.5'                           => '+9.5',
+            '12.0'                           => '12.0',
+            '-12.0'                          => '-12.0',
+            '+12.0'                          => '+12.0',
+            '12.5'                           => '12.5',
             '-12.5'                          => '-12.5',
             '+12.5'                          => '+12.5',
+            '13.0'                           => '13.0',
+            '-13.0'                          => '-13.0',
+            '+13.0'                          => '+13.0',
             '13.5'                           => '',
+            '+13.5'                          => '',
             '-13.5'                          => '',
             '0.2'                            => '');
 
index 04814d0..0594466 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 9194de2..ad3b2c6 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 define('AJAX_SCRIPT', true);
+define('REQUIRE_CORRECT_ACCESS', true);
 define('NO_MOODLE_COOKIES', true);
 
 require_once(dirname(dirname(__FILE__)) . '/config.php');
index cac237b..1fc1cd3 100644 (file)
@@ -77,17 +77,6 @@ define('MESSAGE_PERMITTED_MASK', 0x0c); // 1100
  */
 define('MESSAGE_DEFAULT_PERMITTED', 'permitted');
 
-//TODO: defaults must be initialised via settings - this is a bad hack! (skodak)
-if (!isset($CFG->message_contacts_refresh)) {  // Refresh the contacts list every 60 seconds
-    $CFG->message_contacts_refresh = 60;
-}
-if (!isset($CFG->message_chat_refresh)) {      // Look for new comments every 5 seconds
-    $CFG->message_chat_refresh = 5;
-}
-if (!isset($CFG->message_offline_time)) {
-    $CFG->message_offline_time = 300;
-}
-
 /**
  * Print the selector that allows the user to view their contacts, course participants, their recent
  * conversations etc
@@ -185,7 +174,18 @@ function message_print_participants($context, $courseid, $contactselecturl=null,
     }
 
     $countparticipants = count_enrolled_users($context);
-    $participants = get_enrolled_users($context, '', 0, 'u.*', '', $page*MESSAGE_CONTACTS_PER_PAGE, MESSAGE_CONTACTS_PER_PAGE);
+
+    list($esql, $params) = get_enrolled_sql($context);
+    $params['mcuserid'] = $USER->id;
+    $ufields = user_picture::fields('u');
+
+    $sql = "SELECT $ufields, mc.id as contactlistid, mc.blocked
+              FROM {user} u
+              JOIN ($esql) je ON je.id = u.id
+              LEFT JOIN {message_contacts} mc ON mc.contactid = u.id AND mc.userid = :mcuserid
+             WHERE u.deleted = 0";
+
+    $participants = $DB->get_records_sql($sql, $params, $page * MESSAGE_CONTACTS_PER_PAGE, MESSAGE_CONTACTS_PER_PAGE);
 
     $pagingbar = new paging_bar($countparticipants, $page, MESSAGE_CONTACTS_PER_PAGE, $PAGE->url, 'page');
     echo $OUTPUT->render($pagingbar);
@@ -196,11 +196,23 @@ function message_print_participants($context, $courseid, $contactselecturl=null,
     echo html_writer::tag('td', $titletodisplay, array('colspan' => 3, 'class' => 'heading'));
     echo html_writer::end_tag('tr');
 
-    //todo these need to come from somewhere if the course participants list is to show users with unread messages
-    $iscontact = true;
-    $isblocked = false;
     foreach ($participants as $participant) {
         if ($participant->id != $USER->id) {
+
+            $iscontact = false;
+            $isblocked = false;
+            if ( $participant->contactlistid )  {
+                if ($participant->blocked == 0) {
+                    // Is contact. Is not blocked.
+                    $iscontact = true;
+                    $isblocked = false;
+                } else {
+                    // Is blocked.
+                    $iscontact = false;
+                    $isblocked = true;
+                }
+            }
+
             $participant->messagecount = 0;//todo it would be nice if the course participant could report new messages
             message_print_contactlist_user($participant, $iscontact, $isblocked, $contactselecturl, $showactionlinks, $user2);
         }
index 2ae00a2..a71e9ce 100644 (file)
@@ -58,7 +58,7 @@ class mod_assign_grading_batch_operations_form extends moodleform {
         $mform->addElement('hidden', 'returnaction', 'grading');
 
         $objs = array();
-        $objs[] =& $mform->createElement('select', 'operation', '', $options);
+        $objs[] =& $mform->createElement('select', 'operation', get_string('chooseoperation', 'assign'), $options);
         $objs[] =& $mform->createElement('submit', 'submit', get_string('go'));
         $mform->addElement('group', 'actionsgrp', get_string('batchoperationsdescription', 'assign'), $objs, ' ', false);
 
index 45fa21a..78ae6ca 100644 (file)
@@ -65,6 +65,8 @@ $string['batchoperationlock'] = 'lock submissions';
 $string['batchoperationunlock'] = 'unlock submissions';
 $string['batchoperationreverttodraft'] = 'revert submissions to draft';
 $string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.';
+$string['choosegradingaction'] = 'Grading action';
+$string['chooseoperation'] = 'Choose operation';
 $string['comment'] = 'Comment';
 $string['conversionexception'] = 'Could not convert assignment. Exception was: {$a}.';
 $string['configshowrecentsubmissions'] = 'Everyone can see notifications of submissions in recent activity reports.';
index da09984..9ae8c82 100644 (file)
@@ -968,6 +968,8 @@ class assign {
 
         static $scalegrades = array();
 
+        $o = '';
+
         if ($this->get_instance()->grade >= 0) {
             // Normal number
             if ($editing && $this->get_instance()->grade > 0) {
@@ -976,15 +978,18 @@ class assign {
                 } else {
                     $displaygrade = format_float($grade);
                 }
-                $o = '<input type="text" name="quickgrade_' . $userid . '" value="' . $displaygrade . '" size="6" maxlength="10" class="quickgrade"/>';
+                $o .= '<input type="text" name="quickgrade_' . $userid . '" value="' . $displaygrade . '" size="6" maxlength="10" class="quickgrade"/>';
                 $o .= '&nbsp;/&nbsp;' . format_float($this->get_instance()->grade,2);
                 $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
                 return $o;
             } else {
+                $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
                 if ($grade == -1 || $grade === null) {
-                    return '-';
+                    $o .= '-';
+                    return $o;
                 } else {
-                    return format_float(($grade),2) .'&nbsp;/&nbsp;'. format_float($this->get_instance()->grade,2);
+                    $o .= format_float(($grade),2) .'&nbsp;/&nbsp;'. format_float($this->get_instance()->grade,2);
+                    return $o;
                 }
             }
 
@@ -994,11 +999,12 @@ class assign {
                 if ($scale = $DB->get_record('scale', array('id'=>-($this->get_instance()->grade)))) {
                     $this->cache['scale'] = make_menu_from_list($scale->scale);
                 } else {
-                    return '-';
+                    $o .= '-';
+                    return $o;
                 }
             }
             if ($editing) {
-                $o = '<select name="quickgrade_' . $userid . '" class="quickgrade">';
+                $o .= '<select name="quickgrade_' . $userid . '" class="quickgrade">';
                 $o .= '<option value="-1">' . get_string('nograde') . '</option>';
                 foreach ($this->cache['scale'] as $optionid => $option) {
                     $selected = '';
@@ -1013,9 +1019,11 @@ class assign {
             } else {
                 $scaleid = (int)$grade;
                 if (isset($this->cache['scale'][$scaleid])) {
-                    return $this->cache['scale'][$scaleid];
+                    $o .= $this->cache['scale'][$scaleid];
+                    return $o;
                 }
-                return '-';
+                $o .= '-';
+                return $o;
             }
         }
     }
@@ -1048,22 +1056,32 @@ class assign {
     /**
      * Load a count of users submissions in the current module that require grading
      * This means the submission modification time is more recent than the
-     * grading modification time.
+     * grading modification time and the status is SUBMITTED.
      *
      * @return int number of matching submissions
      */
     public function count_submissions_need_grading() {
         global $DB;
 
-        $params = array($this->get_course_module()->instance);
+        $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
 
-        return $DB->count_records_sql("SELECT COUNT('x')
-                                       FROM {assign_submission} s
-                                       LEFT JOIN {assign_grades} g ON s.assignment = g.assignment AND s.userid = g.userid
-                                       WHERE s.assignment = ?
-                                           AND s.timemodified IS NOT NULL
-                                           AND (s.timemodified > g.timemodified OR g.timemodified IS NULL)",
-                                       $params);
+        $params['assignid'] = $this->get_instance()->id;
+        $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+
+        $sql = 'SELECT COUNT(s.userid)
+                   FROM {assign_submission} s
+                   LEFT JOIN {assign_grades} g ON
+                        s.assignment = g.assignment AND
+                        s.userid = g.userid
+                   JOIN(' . $esql . ') AS e ON e.id = s.userid
+                   WHERE
+                        s.assignment = :assignid AND
+                        s.timemodified IS NOT NULL AND
+                        s.status = :submitted AND
+                        (s.timemodified > g.timemodified OR g.timemodified IS NULL)';
+
+        return $DB->count_records_sql($sql, $params);
     }
 
     /**
@@ -1074,10 +1092,26 @@ class assign {
      */
     public function count_submissions_with_status($status) {
         global $DB;
-        return $DB->count_records_sql("SELECT COUNT('x')
-                                     FROM {assign_submission}
-                                    WHERE assignment = ? AND
-                                          status = ?", array($this->get_course_module()->instance, $status));
+
+        $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
+
+        $params['assignid'] = $this->get_instance()->id;
+        $params['submissionstatus'] = $status;
+
+        $sql = 'SELECT COUNT(s.userid)
+                   FROM {assign_submission} s
+                   LEFT JOIN {assign_grades} g ON
+                        s.assignment = g.assignment AND
+                        s.userid = g.userid
+                   JOIN(' . $esql . ') AS e ON e.id = s.userid
+                   WHERE
+                        s.assignment = :assignid AND
+                        s.timemodified IS NOT NULL AND
+                        s.status = :submissionstatus AND
+                        (s.timemodified > g.timemodified OR g.timemodified IS NULL)';
+
+        return $DB->count_records_sql($sql, $params);
     }
 
     /**
@@ -1130,22 +1164,32 @@ class assign {
      * @return array The submission objects indexed by id
      */
     private function get_all_submissions( $sort="", $dir="DESC") {
-        global $CFG, $DB;
+        global $DB;
+
+        $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
+
+        $params['assignid'] = $this->get_instance()->id;
+
+        $sql = 'SELECT s.*, u.lastname, u.firstname, u.username
+                   FROM {assign_submission} s
+                   JOIN {user} u ON s.userid = u.id
+                   JOIN(' . $esql . ') AS e ON e.id = s.userid
+                   WHERE
+                        s.assignment = :assignid AND
+                        s.timemodified IS NOT NULL';
 
         if ($sort == "lastname" or $sort == "firstname") {
             $sort = "u.$sort $dir";
         } else if (empty($sort)) {
-            $sort = "a.timemodified DESC";
+            $sort = "s.timemodified DESC";
         } else {
-            $sort = "a.$sort $dir";
+            $sort = "s.$sort $dir";
         }
 
-        return $DB->get_records_sql("SELECT a.*
-                                       FROM {assign_submission} a, {user} u
-                                      WHERE u.id = a.userid
-                                            AND a.assignment = ?
-                                   ORDER BY $sort", array($this->get_instance()->id));
+        $sql .= ' ORDER BY ' . $sort;
 
+        return $DB->get_records_sql($sql, $params);
     }
 
     /**
@@ -1534,9 +1578,7 @@ class assign {
             if ((groups_is_member($groupid,$userid) or !$groupmode or !$groupid)) {
                 // get the plugins to add their own files to the zip
 
-                $user = $DB->get_record("user", array("id"=>$userid),'id,username,firstname,lastname', MUST_EXIST);
-
-                $prefix = clean_filename(fullname($user) . "_" .$userid . "_");
+                $prefix = clean_filename(fullname($submission) . "_" .$userid . "_");
 
                 foreach ($this->submissionplugins as $plugin) {
                     if ($plugin->is_enabled() && $plugin->is_visible()) {
@@ -1601,12 +1643,7 @@ class assign {
             $submission->userid       = $userid;
             $submission->timecreated = time();
             $submission->timemodified = $submission->timecreated;
-
-            if ($this->get_instance()->submissiondrafts) {
-                $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
-            } else {
-                $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
-            }
+            $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
             $sid = $DB->insert_record('assign_submission', $submission);
             $submission->id = $sid;
             return $submission;
@@ -1801,6 +1838,7 @@ class assign {
         }
 
         $gradingactions = new url_select($links);
+        $gradingactions->set_label(get_string('choosegradingaction', 'assign'));
 
         $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
 
@@ -2094,6 +2132,9 @@ class assign {
             $showedit = has_capability('mod/assign:submit', $this->context) &&
                          $this->submissions_open() && ($this->is_any_submission_plugin_enabled()) && $showlinks;
             $showsubmit = $submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT) && $showlinks;
+            if (!$this->get_instance()->submissiondrafts) {
+                $showsubmit = false;
+            }
             $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($user->id);
 
             $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate,
@@ -2620,19 +2661,16 @@ class assign {
         // gets a list of possible users and look for values based upon that.
         foreach ($participants as $userid => $unused) {
             $modified = optional_param('grademodified_' . $userid, -1, PARAM_INT);
-            if ($modified >= 0) {
-                // gather the userid, updated grade and last modified value
-                $record = new stdClass();
-                $record->userid = $userid;
-                $record->grade = unformat_float(required_param('quickgrade_' . $record->userid, PARAM_TEXT));
-                $record->lastmodified = $modified;
-                $record->gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
-                $users[$userid] = $record;
+            // Gather the userid, updated grade and last modified value.
+            $record = new stdClass();
+            $record->userid = $userid;
+            $gradevalue = optional_param('quickgrade_' . $userid, '', PARAM_TEXT);
+            if($modified >= 0) {
+                $record->grade = unformat_float(optional_param('quickgrade_' . $record->userid, -1, PARAM_TEXT));
             }
-        }
-        if (empty($users)) {
-            // Quick check to see whether we have any users to update and we don't
-            return get_string('quickgradingchangessaved', 'assign'); // Technical lie
+            $record->lastmodified = $modified;
+            $record->gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
+            $users[$userid] = $record;
         }
 
         list($userids, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED);
@@ -2847,13 +2885,18 @@ class assign {
         }
         if ($data = $mform->get_data()) {
             $submission = $this->get_user_submission($USER->id, true); //create the submission if needed & its id
+            if ($this->get_instance()->submissiondrafts) {
+                $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
+            } else {
+                $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+            }
+
             $grade = $this->get_user_grade($USER->id, false); // get the grade to check if it is locked
             if ($grade && $grade->locked) {
                 print_error('submissionslocked', 'assign');
                 return true;
             }
 
-
             $allempty = true;
             $pluginerror = false;
             foreach ($this->submissionplugins as $plugin) {
@@ -2993,6 +3036,11 @@ class assign {
                 $grademenu = make_grades_menu($this->get_instance()->grade);
                 if (count($grademenu) > 0) {
                     $gradingelement = $mform->addElement('select', 'grade', get_string('grade').':', $grademenu);
+
+                    // The grade is already formatted with format_float so it needs to be converted back to an integer.
+                    if (!empty($data->grade)) {
+                        $data->grade = (int)unformat_float($data->grade);
+                    }
                     $mform->setType('grade', PARAM_INT);
                     if ($gradingdisabled) {
                         $gradingelement->freeze();
index 993db48..107eae8 100644 (file)
@@ -452,7 +452,11 @@ class mod_assign_renderer extends plugin_renderer_base {
             $t->data[] = $row;
 
             foreach ($status->submissionplugins as $plugin) {
-                if ($plugin->is_enabled() && $plugin->is_visible() && !$plugin->is_empty($status->submission)) {
+                $pluginshowsummary = !$plugin->is_empty($status->submission) || !$plugin->allow_submissions();
+                if ($plugin->is_enabled() &&
+                    $plugin->is_visible() &&
+                    $pluginshowsummary) {
+
                     $row = new html_table_row();
                     $cell1 = new html_table_cell($plugin->get_name());
                     $pluginsubmission = new assign_submission_plugin_submission($plugin, $status->submission, assign_submission_plugin_submission::SUMMARY, $status->coursemoduleid, $status->returnaction, $status->returnparams);
index eb87938..1d91186 100644 (file)
@@ -78,12 +78,13 @@ class assign_submission_comments extends assign_submission_plugin {
     }
 
     /**
-     * Always return false because at a minimum there is the comments control
+     * Always return true because the submission comments are not part of the submission form.
+     *
      * @param stdClass $submission
      * @return bool
      */
     public function is_empty(stdClass $submission) {
-        return false;
+        return true;
     }
 
   /**
index 3f95f34..ed1e234 100644 (file)
@@ -87,10 +87,13 @@ class assign_submission_file extends assign_submission_plugin {
         $mform->disabledIf('assignsubmission_file_maxfiles', 'assignsubmission_file_enabled', 'eq', 0);
 
         $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes, get_config('assignsubmission_file', 'maxbytes'));
+
+        // Remove the option for 0 bytes.
+        unset($choices[0]);
         if ($COURSE->maxbytes == 0) {
-            $choices[0] = get_string('siteuploadlimit', 'assignsubmission_file');
+            $choices = array(0=>get_string('siteuploadlimit', 'assignsubmission_file')) + $choices;
         } else {
-            $choices[0] = get_string('courseuploadlimit') . ' (' . display_size($COURSE->maxbytes) . ')';
+            $choices = array(0=>get_string('courseuploadlimit') . ' (' . display_size($COURSE->maxbytes) . ')') + $choices;
         }
         $settings[] = array('type' => 'select',
                             'name' => 'maxsubmissionsizebytes',
index cfb95ad..7d5b624 100644 (file)
@@ -200,7 +200,12 @@ class assign_submission_onlinetext extends assign_submission_plugin {
         $showviewlink = true;
 
         if ($onlinetextsubmission) {
-            $text = format_text($onlinetextsubmission->onlinetext, $onlinetextsubmission->onlineformat, array('context'=>$this->assignment->get_context()));
+            $text = $this->assignment->render_editor_content(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
+                                                             $onlinetextsubmission->submission,
+                                                             $this->get_type(),
+                                                             'onlinetext',
+                                                             'assignsubmission_onlinetext');
+
             $shorttext = shorten_text($text, 140);
             if ($text != $shorttext) {
                 return $shorttext . get_string('numwords', 'assignsubmission_onlinetext', count_words($text));
@@ -376,7 +381,9 @@ class assign_submission_onlinetext extends assign_submission_plugin {
      * @return bool
      */
     public function is_empty(stdClass $submission) {
-        return $this->view($submission) == '';
+        $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
+
+        return empty($onlinetextsubmission->onlinetext);
     }
 
     /**
index 8e7df84..f50aac0 100644 (file)
@@ -134,6 +134,8 @@ class assign_upgrade_manager {
                     if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) {
                         $rollback = true;
                     }
+                } else {
+                    $plugin->disable();
                 }
             }
             foreach ($newassignment->get_feedback_plugins() as $plugin) {
@@ -142,6 +144,8 @@ class assign_upgrade_manager {
                     if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) {
                         $rollback = true;
                     }
+                } else {
+                    $plugin->disable();
                 }
             }
 
index 9eb2c18..d751340 100644 (file)
@@ -387,14 +387,6 @@ class qformat_default {
                     $newpage->contents = $question->questiontext;
                     $newpage->contentsformat = isset($question->questionformat) ? $question->questionformat : FORMAT_HTML;
 
-                    // Sometimes, questiontext is not a simple text, but one array
-                    // containing both text and format, so we need to support here
-                    // that case with the following dirty patch. MDL-35147
-                    if (is_array($question->questiontext)) {
-                        $newpage->contents = isset($question->questiontext['text']) ? $question->questiontext['text'] : '';
-                        $newpage->contentsformat = isset($question->questiontext['format']) ? $question->questiontext['format'] : FORMAT_HTML;
-                    }
-
                     // set up page links
                     if ($pageid) {
                         // the new page follows on from this page
index 303638f..ae99ee5 100644 (file)
@@ -36,9 +36,13 @@ $attemptobj = quiz_attempt::create($attemptid);
 // Check login.
 require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
 
-// If this is not our own attempt, display an error.
+// Check that this attempt belongs to this user.
 if ($attemptobj->get_userid() != $USER->id) {
-    print_error('notyourattempt', 'quiz', $attemptobj->view_url());
+    if ($attemptobj->has_capability('mod/quiz:viewreports')) {
+        redirect($attemptobj->review_url(null));
+    } else {
+        throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'notyourattempt');
+    }
 }
 
 // Check capabilites.
index c3de373..dd410bb 100644 (file)
@@ -272,8 +272,6 @@ function scorm_delete_instance($id) {
             }
         }
         $DB->delete_records('scorm_scoes', array('scorm'=>$scorm->id));
-    } else {
-        $result = false;
     }
     if (! $DB->delete_records('scorm', array('id'=>$scorm->id))) {
         $result = false;
index 19e6061..c2f1c40 100644 (file)
@@ -250,9 +250,6 @@ function scorm_parse($scorm, $full) {
         }
 
     } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL and $cfg_scorm->allowtypeexternal) {
-        if (!$full and $scorm->sha1hash === sha1($scorm->reference)) {
-            return;
-        }
         require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php");
         // SCORM only, AICC can not be external
         if (!scorm_parse_scorm($scorm, $scorm->reference)) {
@@ -847,13 +844,13 @@ function scorm_simple_play($scorm, $user, $context, $cmid) {
 
     $result = false;
 
-    if ($scorm->scormtype != SCORM_TYPE_LOCAL && $scorm->updatefreq == SCORM_UPDATE_EVERYTIME) {
-        scorm_parse($scorm, false);
-    }
     if (has_capability('mod/scorm:viewreport', $context)) { //if this user can view reports, don't skipview so they can see links to reports.
         return $result;
     }
 
+    if ($scorm->scormtype != SCORM_TYPE_LOCAL && $scorm->updatefreq == SCORM_UPDATE_EVERYTIME) {
+        scorm_parse($scorm, false);
+    }
     $scoes = $DB->get_records_select('scorm_scoes', 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true), array($scorm->id), 'id', 'id');
 
     if ($scoes) {
index ee8dc2e..afea1a3 100644 (file)
@@ -657,7 +657,7 @@ class page_wiki_comments extends page_wiki {
                     $parsedcontent = wiki_parse_content('nwiki', $comment->content, $options);
                 }
 
-                $cell4->text = format_text(html_entity_decode($parsedcontent['parsed_text']), FORMAT_HTML);
+                $cell4->text = format_text(html_entity_decode($parsedcontent['parsed_text'], ENT_QUOTES, 'UTF-8'), FORMAT_HTML);
             } else {
                 $cell4->text = format_text($comment->content, FORMAT_HTML);
             }
index eb14bc0..f777f28 100644 (file)
@@ -14,9 +14,9 @@ require_once($CFG->dirroot . "/lib/outputcomponents.php");
 class parser_utils {
         
     public static function h($tag, $text = null, $options = array(), $escape_text = false) {
-        $tag = htmlentities($tag);
+        $tag = htmlentities($tag, ENT_COMPAT, 'UTF-8');
         if(!empty($text) && $escape_text) {
-                $text = htmlentities($text);
+                $text = htmlentities($text, ENT_COMPAT, 'UTF-8');
             }
         return html_writer::tag($tag, $text, $options);
     }
index 1508b5b..cf37a23 100644 (file)
@@ -1035,7 +1035,7 @@ function workshop_get_extra_capabilities() {
  * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
  * {@link workshop_update_grades()}.
  *
- * @param stdClass $workshop instance object with extra cmidnumber and modname property
+ * @param stdClass $workshop instance object with extra cmidnumber property
  * @param stdClass $submissiongrades data for the first grade item
  * @param stdClass $assessmentgrades data for the second grade item
  * @return void
index 13c1232..811e9bc 100644 (file)
@@ -413,20 +413,11 @@ class qformat_default {
                     'maxfiles' => -1,
                     'maxbytes' => 0,
                 );
-            if (is_array($question->questiontext)) {
-                // Importing images from draftfile.
-                $questiontext = $question->questiontext;
-                $question->questiontext = $questiontext['text'];
-            }
-            if (is_array($question->generalfeedback)) {
-                $generalfeedback = $question->generalfeedback;
-                $question->generalfeedback = $generalfeedback['text'];
-            }
 
             $question->id = $DB->insert_record('question', $question);
 
-            if (!empty($questiontext['itemid'])) {
-                $question->questiontext = file_save_draft_area_files($questiontext['itemid'],
+            if (isset($question->questiontextitemid)) {
+                $question->questiontext = file_save_draft_area_files($question->questiontextitemid,
                         $this->importcontext->id, 'question', 'questiontext', $question->id,
                         $fileoptions, $question->questiontext);
             } else if (isset($question->questiontextfiles)) {
@@ -435,8 +426,8 @@ class qformat_default {
                             $this->importcontext, 'question', 'questiontext', $question->id, $file);
                 }
             }
-            if (!empty($generalfeedback['itemid'])) {
-                $question->generalfeedback = file_save_draft_area_files($generalfeedback['itemid'],
+            if (isset($question->generalfeedbackitemid)) {
+                $question->generalfeedback = file_save_draft_area_files($question->generalfeedbackitemid,
                         $this->importcontext->id, 'question', 'generalfeedback', $question->id,
                         $fileoptions, $question->generalfeedback);
             } else if (isset($question->generalfeedbackfiles)) {
index da08e91..4424f5f 100644 (file)
@@ -148,16 +148,4 @@ class qformat_blackboard_six_base extends qformat_based_on_xml {
     public function cleaned_text_field($text) {
         return $this->text_field($this->cleaninput($text));
     }
-
-    /**
-     * Convert the question text to plain text.
-     * We need to overwrite this function because questiontext is an array.
-     */
-    protected function format_question_text($question) {
-        global $DB;
-        $formatoptions = new stdClass();
-        $formatoptions->noclean = true;
-        return html_to_text(format_text($question->questiontext['text'],
-                $question->questiontext['format'], $formatoptions), 0, false);
-    }
 }
index a6c942c..7911305 100644 (file)
@@ -88,12 +88,16 @@ class qformat_blackboard_six_pool extends qformat_blackboard_six_base {
                 array('#', 'BODY', 0, '#', 'TEXT', 0, '#'),
                 '', true, get_string('importnotext', 'qformat_blackboard_six'));
 
-        $question->questiontext = $this->cleaned_text_field($text);
-        $question->questiontextformat = FORMAT_HTML; // Needed because add_blank_combined_feedback uses it.
+        $questiontext = $this->cleaned_text_field($text);
+        $question->questiontext = $questiontext['text'];
+        $question->questiontextformat = $questiontext['format']; // Needed because add_blank_combined_feedback uses it.
+        if (isset($questiontext['itemid'])) {
+            $question->questiontextitemid = $questiontext['itemid'];
+        }
 
         // Put name in question object. We must ensure it is not empty and it is less than 250 chars.
         $id = $this->getpath($questiondata, array('@', 'id'), '',  true);
-        $question->name = $this->create_default_question_name($question->questiontext['text'],
+        $question->name = $this->create_default_question_name($question->questiontext,
                 get_string('defaultname', 'qformat_blackboard_six' , $id));
 
         $question->generalfeedback = '';
@@ -453,7 +457,7 @@ class qformat_blackboard_six_pool extends qformat_blackboard_six_base {
                 $subanswercount++;
             }
             if ($subquestioncount < 2 || $subanswercount < 3) {
-                    $this->error(get_string('notenoughtsubans', 'qformat_blackboard_six', $question->questiontext['text']));
+                    $this->error(get_string('notenoughtsubans', 'qformat_blackboard_six', $question->questiontext));
             } else {
                 $questions[] = $question;
             }
index 37545fd..6b9edda 100644 (file)
@@ -506,11 +506,13 @@ class qformat_blackboard_six_qti extends qformat_blackboard_six_base {
     public function process_common($quest) {
         $question = $this->defaultquestion();
         $text = $quest->QUESTION_BLOCK->text;
-
-        $question->questiontext = $this->cleaned_text_field($text);
-        $question->questiontextformat = FORMAT_HTML; // Needed because add_blank_combined_feedback uses it.
-
-        $question->name = $this->create_default_question_name($question->questiontext['text'],
+        $questiontext = $this->cleaned_text_field($text);
+        $question->questiontext = $questiontext['text'];
+        $question->questiontextformat = $questiontext['format']; // Needed because add_blank_combined_feedback uses it.
+        if (isset($questiontext['itemid'])) {
+            $question->questiontextitemid = $questiontext['itemid'];
+        }
+        $question->name = $this->create_default_question_name($question->questiontext,
                 get_string('defaultname', 'qformat_blackboard_six' , $quest->id));
         $question->generalfeedback = '';
         $question->generalfeedbackformat = FORMAT_HTML;
@@ -863,7 +865,7 @@ class qformat_blackboard_six_qti extends qformat_blackboard_six_base {
             $subanswercount++;
         }
         if ($subquestioncount < 2 || $subanswercount < 3) {
-                $this->error(get_string('notenoughtsubans', 'qformat_blackboard_six', $question->questiontext['text']));
+                $this->error(get_string('notenoughtsubans', 'qformat_blackboard_six', $question->questiontext));
         } else {
             $questions[] = $question;
         }
index 22cadb7..fdfa5e4 100644 (file)
@@ -58,10 +58,7 @@ class qformat_blackboard_six_pool_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'match';
         $expectedq->name = 'Classify the animals.';
-        $expectedq->questiontext = array(
-                'text' => '<i>Classify the animals.</i>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<i>Classify the animals.</i>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array('text' => '',
                 'format' => FORMAT_HTML);
@@ -98,10 +95,7 @@ class qformat_blackboard_six_pool_test extends question_testcase {
         $expectedq->qtype = 'multichoice';
         $expectedq->single = 1;
         $expectedq->name = 'What\'s between orange and green in the spectrum?';
-        $expectedq->questiontext = array(
-                'text' =>'<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array('text' => 'You gave the right answer.',
                 'format' => FORMAT_HTML);
@@ -161,10 +155,7 @@ class qformat_blackboard_six_pool_test extends question_testcase {
         $expectedq->qtype = 'multichoice';
         $expectedq->single = 0;
         $expectedq->name = 'What\'s between orange and green in the spectrum?';
-        $expectedq->questiontext = array(
-                'text' => '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array(
                 'text' => 'You gave the right answer.',
@@ -237,10 +228,7 @@ class qformat_blackboard_six_pool_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'truefalse';
         $expectedq->name = '42 is the Absolute Answer to everything.';
-        $expectedq->questiontext = array(
-                'text' => '<span style="font-size:12pt">42 is the Absolute Answer to everything.</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">42 is the Absolute Answer to everything.</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = '';
         $expectedq->generalfeedbackformat = FORMAT_HTML;
@@ -270,10 +258,7 @@ class qformat_blackboard_six_pool_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'shortanswer';
         $expectedq->name = 'Name an amphibian: __________.';
-        $expectedq->questiontext = array(
-                'text' => '<span style="font-size:12pt">Name an amphibian: __________.</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">Name an amphibian: __________.</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = '';
         $expectedq->generalfeedbackformat = FORMAT_HTML;
@@ -308,10 +293,7 @@ class qformat_blackboard_six_pool_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'essay';
         $expectedq->name = 'How are you?';
-        $expectedq->questiontext = array(
-                'text' => 'How are you?',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = 'How are you?';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = '';
         $expectedq->generalfeedbackformat = FORMAT_HTML;
index 68df181..9d80e9b 100644 (file)
@@ -55,10 +55,7 @@ class qformat_blackboard_six_qti_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'match';
         $expectedq->name = 'Classify the animals.';
-        $expectedq->questiontext = array(
-                'text' => 'Classify the animals.',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = 'Classify the animals.';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array('text' => '',
                 'format' => FORMAT_HTML, 'files' => array());
@@ -94,10 +91,7 @@ class qformat_blackboard_six_qti_test extends question_testcase {
         $expectedq->qtype = 'multichoice';
         $expectedq->single = 1;
         $expectedq->name = 'What\'s between orange and green in the spectrum?';
-        $expectedq->questiontext = array(
-                'text' => '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array('text' => '',
                 'format' => FORMAT_HTML, 'files' => array());
@@ -157,10 +151,7 @@ class qformat_blackboard_six_qti_test extends question_testcase {
         $expectedq->qtype = 'multichoice';
         $expectedq->single = 0;
         $expectedq->name = 'What\'s between orange and green in the spectrum?';
-        $expectedq->questiontext = array(
-                'text' => '<i>What\'s between orange and green in the spectrum?</i>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<i>What\'s between orange and green in the spectrum?</i>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array(
                 'text' => '',
@@ -236,10 +227,7 @@ class qformat_blackboard_six_qti_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'truefalse';
         $expectedq->name = '42 is the Absolute Answer to everything.';
-        $expectedq->questiontext = array(
-                'text' => '<span style="font-size:12pt">42 is the Absolute Answer to everything.</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">42 is the Absolute Answer to everything.</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = '';
         $expectedq->generalfeedbackformat = FORMAT_HTML;
@@ -270,10 +258,7 @@ class qformat_blackboard_six_qti_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'shortanswer';
         $expectedq->name = 'Name an amphibian: __________.';
-        $expectedq->questiontext = array(
-                'text' => '<span style="font-size:12pt">Name an amphibian: __________.</span>',
-                'format' => FORMAT_HTML,
-            );
+        $expectedq->questiontext = '<span style="font-size:12pt">Name an amphibian: __________.</span>';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = '';
         $expectedq->generalfeedbackformat = FORMAT_HTML;
@@ -308,10 +293,7 @@ class qformat_blackboard_six_qti_test extends question_testcase {
         $expectedq = new stdClass();
         $expectedq->qtype = 'essay';
         $expectedq->name = 'How are you?';
-        $expectedq->questiontext = array(
-                'text' => 'How are you?',
-                'format' => FORMAT_HTML
-            );
+        $expectedq->questiontext = 'How are you?';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = '';
         $expectedq->generalfeedbackformat = FORMAT_HTML;
index 45ec077..64f0dbf 100644 (file)
@@ -905,17 +905,18 @@ class qformat_xml extends qformat_default {
      */
     protected function readquestions($lines) {
         // We just need it as one big string
-        $text = implode($lines, ' ');
-        unset($lines);
+        $lines = implode('', $lines);
 
         // This converts xml to big nasty data structure
         // the 0 means keep white space as it is (important for markdown format)
         try {
-            $xml = xmlize($text, 0, 'UTF-8', true);
+            $xml = xmlize($lines, 0, 'UTF-8', true);
         } catch (xml_format_exception $e) {
             $this->error($e->getMessage(), '');
             return false;
         }
+        unset($lines); // No need to keep this in memory.
+
         // Set up array to hold all our questions
         $questions = array();
 
index c0ab753..0a91a53 100644 (file)
@@ -1227,7 +1227,7 @@ class qtype_calculated extends question_type {
                 echo $OUTPUT->notification(get_string('notvalidnumber', 'qtype_calculated', $a));
                 $val = 1.0;
             }
-            if ($val < 0) {
+            if ($val <= 0) { // MDL-36025 Use parentheses for "-0"
                 $str = str_replace('{'.$name.'}', '('.$val.')', $str);
             } else {
                 $str = str_replace('{'.$name.'}', $val, $str);
@@ -1255,6 +1255,7 @@ class qtype_calculated extends question_type {
         } else if ($formula === '*') {
             $str = '*';
         } else {
+            $str = null;
             eval('$str = '.$formula.';');
         }
         return $str;
@@ -1870,6 +1871,11 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
     // Exchange formula variables with the correct values...
     $answer = question_bank::get_qtype('calculated')->substitute_variables_and_eval(
             $formula, $individualdata);
+    if (!is_numeric($answer)) {
+        // Something went wrong, so just return NaN.
+        $calculated->answer = NAN;
+        return $calculated;
+    }
     if ('1' == $answerformat) { /* Answer is to have $answerlength decimals */
         /*** Adjust to the correct number of decimals ***/
         if (stripos($answer, 'e')>0) {
index 5afe4ff..a24a4fd 100644 (file)
@@ -118,10 +118,6 @@ abstract class qtype_multianswer_subq_renderer_base extends qtype_renderer {
     protected function feedback_popup(question_graded_automatically $subq,
             $fraction, $feedbacktext, $rightanswer, question_display_options $options) {
 
-        if (!$options->feedback) {
-            return '';
-        }
-
         $feedback = array();
         if ($options->correctness) {
             if (is_null($fraction)) {
@@ -132,7 +128,7 @@ abstract class qtype_multianswer_subq_renderer_base extends qtype_renderer {
             $feedback[] = $state->default_string(true);
         }
 
-        if ($feedbacktext) { // Note $options->feedback is already checked above.
+        if ($options->feedback && $feedbacktext) {
             $feedback[] = $feedbacktext;
         }
 
@@ -141,7 +137,8 @@ abstract class qtype_multianswer_subq_renderer_base extends qtype_renderer {
         }
 
         $subfraction = '';
-        if ($options->marks >= question_display_options::MARK_AND_MAX && $subq->maxmark > 0) {
+        if ($options->marks >= question_display_options::MARK_AND_MAX && $subq->maxmark > 0
+                && (!is_null($fraction) || $feedback)) {
             $a = new stdClass();
             $a->mark = format_float($fraction * $subq->maxmark, $options->markdp);
             $a->max =  format_float($subq->maxmark, $options->markdp);
index 9544e91..276a170 100644 (file)
@@ -281,7 +281,10 @@ function report_log_print_mnet_selector_form($hostid, $course, $selecteduser=0,
     $timemidnight = $today = usergetmidnight($timenow);
 
     // Put today up the top of the list
-    $dates = array("$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate) );
+    $dates = array(
+        "0" => get_string('alldays'),
+        "$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate)
+    );
 
     if (!$course->startdate or ($course->startdate > $timenow)) {
         $course->startdate = $course->timecreated;
@@ -295,7 +298,7 @@ function report_log_print_mnet_selector_form($hostid, $course, $selecteduser=0,
         $numdates++;
     }
 
-    if ($selecteddate == "today") {
+    if ($selecteddate === "today") {
         $selecteddate = $today;
     }
 
@@ -355,7 +358,7 @@ function report_log_print_mnet_selector_form($hostid, $course, $selecteduser=0,
     }
 
     echo html_writer::label(get_string('date'), 'menudate', false, array('class' => 'accesshide'));
-    echo html_writer::select($dates, "date", $selecteddate, get_string("alldays"));
+    echo html_writer::select($dates, "date", $selecteddate, false);
     echo html_writer::label(get_string('showreports'), 'menumodid', false, array('class' => 'accesshide'));
     echo html_writer::select($activities, "modid", $selectedactivity, get_string("allactivities"));
     echo html_writer::label(get_string('actions'), 'menumodaction', false, array('class' => 'accesshide'));
index 463e4db..640b90f 100644 (file)
@@ -32,6 +32,8 @@
 #categoryquestions { margin: 0; }
 #categoryquestions td,
 #categoryquestions th { padding: 0 0.2em; }
+#categoryquestions th { text-align: left; font-weight: normal; }
+.dir-rtl #categoryquestions th { text-align: right; }
 .questionbank .singleselect { margin: 0; }
 
 /* Question editing form */
index 7b006d6..4a6f2d0 100644 (file)
@@ -173,7 +173,8 @@ class core_webservice_external extends external_api {
                     new external_single_structure(
                         array(
                             'name' => new external_value(PARAM_RAW, 'function name'),
-                            'version' => new external_value(PARAM_FLOAT, 'The version number of moodle site/local plugin linked to the function')
+                            'version' => new external_value(PARAM_TEXT,
+                                        'The version number of the component to which the function belongs')
                         ), 'functions that are available')
                     ),
                 'downloadfiles'  => new external_value(PARAM_INT, '1 if users are allowed to download files, 0 if not', VALUE_OPTIONAL),
@@ -237,4 +238,4 @@ class moodle_webservice_external extends external_api {
     public static function get_siteinfo_returns() {
         return core_webservice_external::get_site_info_returns();
     }
-}
\ No newline at end of file
+}