Merge branch 'MDL-27631' of git://github.com/mkassaei/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 10 Aug 2011 01:00:17 +0000 (03:00 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 10 Aug 2011 01:00:17 +0000 (03:00 +0200)
61 files changed:
admin/cli/install.php
admin/cli/install_database.php
admin/cron.php
admin/index.php
admin/settings/server.php
backup/moodle2/restore_stepslib.php
backup/util/helper/backup_cron_helper.class.php
blocks/rss_client/editfeed.php
calendar/preferences.php
cohort/index.php
install.php
lang/en/moodle.org.php
lang/en/moodle.php
lib/datalib.php
lib/db/upgrade.php
lib/dml/moodle_database.php
lib/dml/pgsql_native_moodle_database.php
lib/filebrowser/file_info_context_course.php
lib/filebrowser/file_info_context_module.php
lib/grouplib.php
lib/javascript-static.js
lib/moodlelib.php
lib/sessionlib.php
lib/simpletest/portfolio_testclass.php
lib/simpletest/testrepositorylib.php
lib/weblib.php
mod/assignment/simpletest/test_assignment_portfolio_callers.php
mod/chat/simpletest/test_chat_portfolio_callers.php
mod/data/simpletest/test_data_portfolio_callers.php
mod/forum/simpletest/test_forum_portfolio_callers.php
mod/glossary/simpletest/test_glossary_portfolio_callers.php
mod/quiz/edit.php
mod/quiz/editlib.php
mod/quiz/lib.php
mod/quiz/report/statistics/responseanalysis.php
mod/quiz/review.php
mod/scorm/datamodels/scormlib.php
mod/scorm/db/upgrade.php
mod/scorm/lang/en/scorm.php
mod/scorm/rd.js [deleted file]
mod/workshop/assessment.php
mod/workshop/exassessment.php
mod/workshop/excompare.php
mod/workshop/lang/en/workshop.php
mod/workshop/lib.php
mod/workshop/locallib.php
mod/workshop/renderer.php
mod/workshop/styles.css
mod/workshop/submission.php
mod/workshop/view.php
portfolio/boxnet/simpletest/testportfoliopluginboxnet.php
question/edit.php
question/editlib.php
question/type/edit_question_form.php
question/type/match/questiontype.php
question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php
question/type/multichoice/questiontype.php
question/type/questionbase.php
question/type/questiontypebase.php
theme/formal_white/style/formal_white.css
theme/yui_combo.php

index 1c37f54..0dd5102 100644 (file)
@@ -115,12 +115,12 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
 define('MOODLE_INTERNAL', true);
 
 // Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.2.8") < 0) {
+if (version_compare(phpversion(), "5.3.2") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
-    echo "Sorry, Moodle 2.0 requires PHP 5.2.8 or later (currently using version $phpversion).\n";
-    echo "Please upgrade your server software or install latest Moodle 1.9.x instead.";
-    die;
+    fwrite(STDERR, "Moodle 2.1 or later requires at least PHP 5.3.2 (currently using version $phpversion).\n");
+    fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
+    exit(1);
 }
 
 // set up configuration
index 9e55c9c..44412ec 100644 (file)
@@ -55,12 +55,12 @@ Example:
 ";
 
 // Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.2.8") < 0) {
+if (version_compare(phpversion(), "5.3.2") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
-    fwrite(STDERR, "Sorry, Moodle 2.0 requires PHP 5.2.8 or later (currently using version $phpversion).\n");
-    fwrite(STDERR, "Please upgrade your server software or install latest Moodle 1.9.x instead.\n");
-    die(1);
+    fwrite(STDERR, "Moodle 2.1 or later requires at least PHP 5.3.2 (currently using version $phpversion).\n");
+    fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
+    exit(1);
 }
 
 // Nothing to do if config.php does not exist
@@ -68,7 +68,7 @@ $configfile = dirname(dirname(dirname(__FILE__))).'/config.php';
 if (!file_exists($configfile)) {
     fwrite(STDERR, 'config.php does not exist, can not continue'); // do not localize
     fwrite(STDERR, "\n");
-    die(1);
+    exit(1);
 }
 
 // Include necessary libs
index e131e9b..5b473a3 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+if (defined('STDIN')) {
+    fwrite(STDERR, "ERROR: This script no longer supports CLI, please use admin/cli/cron.php instead\n");
+    exit(1);
+}
+
 // This is a fake CLI script, it is a really ugly hack which emulates
 // CLI via web interface, please do not use this hack elsewhere
 define('CLI_SCRIPT', true);
index 77c0404..2329248 100644 (file)
@@ -30,11 +30,23 @@ if (!file_exists('../config.php')) {
 }
 
 // Check that PHP is of a sufficient version as soon as possible
-if (version_compare(phpversion(), '5.2.0') < 0) {
+if (version_compare(phpversion(), '5.3.2') < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it to later place
-    echo "Sorry, Moodle 2.0 requires PHP 5.2.8 or later (currently using version $phpversion). ";
-    echo "Please upgrade your server software or use latest Moodle 1.9.x instead.";
+    echo "Moodle 2.1 or later requires at least PHP 5.3.2 (currently using version $phpversion).<br />";
+    echo "Please upgrade your server software or install older Moodle version.";
+    die;
+}
+
+// make sure iconv is available and actually works
+if (!function_exists('iconv')) {
+    // this should not happen, this must be very borked install
+    echo 'Moodle requires the iconv PHP extension. Please install or enable the iconv extension.';
+    die();
+}
+if (iconv('UTF-8', 'UTF-8//IGNORE', 'abc') !== 'abc') {
+    // known to be broken in mid-2011 MAMP installations
+    echo 'Broken iconv PHP extension detected, installation/upgrade can not continue.';
     die;
 }
 
@@ -121,7 +133,7 @@ if (!core_tables_exist()) {
         echo $OUTPUT->box($copyrightnotice, 'copyrightnotice');
         echo '<br />';
         $continue = new single_button(new moodle_url('/admin/index.php', array('lang'=>$CFG->lang, 'agreelicense'=>1)), get_string('continue'), 'get');
-        echo $OUTPUT->confirm(get_string('doyouagree'), $continue, "http://docs.moodle.org/en/License");
+        echo $OUTPUT->confirm(get_string('doyouagree'), $continue, "http://docs.moodle.org/dev/License");
         echo $OUTPUT->footer();
         die;
     }
@@ -455,8 +467,8 @@ if (!empty($CFG->maintenance_enabled)) {
 $copyrighttext = '<a href="http://moodle.org/">Moodle</a> '.
                  '<a href="http://docs.moodle.org/dev/Releases" title="'.$CFG->version.'">'.$CFG->release.'</a><br />'.
                  'Copyright &copy; 1999 onwards, Martin Dougiamas<br />'.
-                 'and <a href="http://docs.moodle.org/en/Credits">many other contributors</a>.<br />'.
-                 '<a href="http://docs.moodle.org/en/License">GNU Public License</a>';
+                 'and <a href="http://docs.moodle.org/dev/Credits">many other contributors</a>.<br />'.
+                 '<a href="http://docs.moodle.org/dev/License">GNU Public License</a>';
 echo $OUTPUT->box($copyrighttext, 'copyright');
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
index e8d7ea7..0348cd3 100644 (file)
@@ -48,8 +48,8 @@ $temp->add(new admin_setting_configselect('sessiontimeout', get_string('sessiont
                                                                                                                                                       900 => get_string('numminutes', '', 15),
                                                                                                                                                       300 => get_string('numminutes', '', 5))));
 $temp->add(new admin_setting_configtext('sessioncookie', get_string('sessioncookie', 'admin'), get_string('configsessioncookie', 'admin'), '', PARAM_ALPHANUM));
-$temp->add(new admin_setting_configtext('sessioncookiepath', get_string('sessioncookiepath', 'admin'), get_string('configsessioncookiepath', 'admin'), '/', PARAM_LOCALURL));
-$temp->add(new admin_setting_configtext('sessioncookiedomain', get_string('sessioncookiedomain', 'admin'), get_string('configsessioncookiedomain', 'admin'), '', PARAM_TEXT, 50));
+$temp->add(new admin_setting_configtext('sessioncookiepath', get_string('sessioncookiepath', 'admin'), get_string('configsessioncookiepath', 'admin'), '', PARAM_RAW));
+$temp->add(new admin_setting_configtext('sessioncookiedomain', get_string('sessioncookiedomain', 'admin'), get_string('configsessioncookiedomain', 'admin'), '', PARAM_RAW, 50));
 $ADMIN->add('server', $temp);
 
 
index 643c478..0b02c01 100644 (file)
@@ -2637,6 +2637,12 @@ class restore_create_question_files extends restore_execution_step {
                                               $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true);
             restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'hint',
                                               $oldctxid, $this->task->get_userid(), 'question_hint', null, $newctxid, true);
+            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'correctfeedback',
+                                              $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true);
+            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'partiallycorrectfeedback',
+                                              $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true);
+            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'incorrectfeedback',
+                                              $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true);
             // Add qtype dependent files
             $components = backup_qtype_plugin::get_components_and_fileareas($question->qtype);
             foreach ($components as $component => $fileareas) {
index ea07094..be15523 100644 (file)
@@ -312,7 +312,7 @@ abstract class backup_cron_automated_helper {
 
             $settings = array(
                 'users' => 'backup_auto_users',
-                'role_assignments' => 'backup_auto_users',
+                'role_assignments' => 'backup_auto_role_assignments',
                 'user_files' => 'backup_auto_user_files',
                 'activities' => 'backup_auto_activities',
                 'blocks' => 'backup_auto_blocks',
index 38eeb48..5711917 100644 (file)
@@ -140,7 +140,9 @@ class feed_edit_form extends moodleform {
                 return $url;
             }
 
-            return $rss->subscribe_url();
+            // return URL without quoting..
+            $discoveredurl = new moodle_url($rss->subscribe_url());
+            return $discoveredurl->out(false);
     }
 }
 
index a36c9f3..165466d 100644 (file)
@@ -9,7 +9,7 @@ require_once($CFG->dirroot.'/calendar/preferences_form.php');
 $courseid = required_param('course', PARAM_INT);
 $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
 
-$PAGE->set_url('/calendar/preferences.php', array('id' => $courseid));
+$PAGE->set_url(new moodle_url('/calendar/preferences.php', array('course' => $courseid)));
 $PAGE->set_pagelayout('standard');
 
 require_login($course);
@@ -37,10 +37,13 @@ $prefs->maxevents  = get_user_preferences('calendar_maxevents', $defaultmaxevent
 $prefs->lookahead  = get_user_preferences('calendar_lookahead', $defaultlookahead);
 $prefs->persistflt = get_user_preferences('calendar_persistflt', 0);
 
-$form = new calendar_preferences_form();
+$form = new calendar_preferences_form($PAGE->url);
 $form->set_data($prefs);
 
-if ($data = $form->get_data() && confirm_sesskey()) {
+if ($form->is_cancelled()) {
+    redirect($viewurl);
+} else if ($form->is_submitted() && $form->is_validated() && confirm_sesskey()) {
+    $data = $form->get_data();
     if ($data->timeformat != CALENDAR_TF_12 && $data->timeformat != CALENDAR_TF_24) {
         $data->timeformat = '';
     }
@@ -68,7 +71,7 @@ if ($data = $form->get_data() && confirm_sesskey()) {
 $strcalendar = get_string('calendar', 'calendar');
 $strpreferences = get_string('calendarpreferences', 'calendar');
 
-$PAGE->navbar->add($strpreferences, new moodle_url('/calendar/view.php'));
+$PAGE->navbar->add($strpreferences);
 $PAGE->set_pagelayout('admin');
 $PAGE->set_title("$course->shortname: $strcalendar: $strpreferences");
 $PAGE->set_heading($course->fullname);
index 0324c5a..62c4729 100644 (file)
@@ -47,6 +47,7 @@ if ($context->contextlevel == CONTEXT_COURSECAT) {
 }
 
 $manager = has_capability('moodle/cohort:manage', $context);
+$canassign = has_capability('moodle/cohort:assign', $context);
 if (!$manager) {
     require_capability('moodle/cohort:view', $context);
 }
@@ -84,18 +85,17 @@ foreach($cohorts as $cohort) {
         $line[] = get_string('pluginname', $cohort->component);
     }
 
-    if ($manager) {
-        if (empty($cohort->component)) {
-            $buttons = html_writer::link(new moodle_url('/cohort/edit.php', array('id'=>$cohort->id, 'delete'=>1)), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/delete'), 'alt'=>get_string('delete'), 'class'=>'iconsmall')));
-            $buttons .= ' ' . html_writer::link(new moodle_url('/cohort/edit.php', array('id'=>$cohort->id)), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/edit'), 'alt'=>get_string('edit'), 'class'=>'iconsmall')));
-            $buttons .= ' ' . html_writer::link(new moodle_url('/cohort/assign.php', array('id'=>$cohort->id)), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/users'), 'alt'=>get_string('assign', 'core_cohort'), 'class'=>'iconsmall')));
-        } else {
-            $buttons = '';
+    $buttons = array();
+    if (empty($cohort->component)) {
+        if ($manager) {
+            $buttons[] = html_writer::link(new moodle_url('/cohort/edit.php', array('id'=>$cohort->id, 'delete'=>1)), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/delete'), 'alt'=>get_string('delete'), 'class'=>'iconsmall')));
+            $buttons[] =  html_writer::link(new moodle_url('/cohort/edit.php', array('id'=>$cohort->id)), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/edit'), 'alt'=>get_string('edit'), 'class'=>'iconsmall')));
+        }
+        if ($manager or $canassign) {
+            $buttons[] = html_writer::link(new moodle_url('/cohort/assign.php', array('id'=>$cohort->id)), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/users'), 'alt'=>get_string('assign', 'core_cohort'), 'class'=>'iconsmall')));
         }
-    } else {
-        $buttons = '';
     }
-    $line[] = $buttons;
+    $line[] = implode(' ', $buttons);
 
     $data[] = $line;
 }
index 3ff618b..14b0665 100644 (file)
@@ -62,8 +62,20 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
 if (version_compare(phpversion(), "5.2.0") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN not move it after installib
-    echo "Sorry, Moodle 2.0 requires PHP 5.2.8 or later (currently using version $phpversion).<br />";
-    echo "Please upgrade your server software or install latest Moodle 1.9.x instead.";
+    echo "Moodle 2.1 or later requires at least PHP 5.3.2 (currently using version $phpversion).<br />";
+    echo "Please upgrade your server software or install older Moodle version.";
+    die;
+}
+
+// make sure iconv is available and actually works
+if (!function_exists('iconv')) {
+    // this should not happen, this must be very borked install
+    echo 'Moodle requires the iconv PHP extension. Please install or enable the iconv extension.';
+    die();
+}
+if (iconv('UTF-8', 'UTF-8//IGNORE', 'abc') !== 'abc') {
+    // known to be broken in mid-2011 MAMP installations
+    echo 'Broken iconv PHP extension detected, installation can not continue.';
     die;
 }
 
@@ -169,7 +181,7 @@ $memlimit = @ini_get('memory_limit');
 if (!empty($memlimit) and $memlimit != -1) {
     if (get_real_size($memlimit) < get_real_size($minrequiredmemory)) {
         // do NOT localise - lang strings would not work here and we CAN not move it to later place
-        echo "Sorry, Moodle 2.0 requires at least {$minrequiredmemory}B of PHP memory.<br />";
+        echo "Moodle requires at least {$minrequiredmemory}B of PHP memory.<br />";
         echo "Please contact server administrator to fix PHP.ini memory settings.";
         die;
     }
@@ -481,7 +493,7 @@ if ($config->stage == INSTALL_DATABASETYPE) {
 
 
 if ($config->stage == INSTALL_ENVIRONMENT or $config->stage == INSTALL_PATHS) {
-    $version_fail = (version_compare(phpversion(), "5.2.8") < 0);
+    $version_fail = (version_compare(phpversion(), "5.3.2") < 0);
     $curl_fail    = ($lang !== 'en' and !extension_loaded('curl')); // needed for lang pack download
     $zip_fail     = ($lang !== 'en' and !extension_loaded('zip'));  // needed for lang pack download
 
@@ -494,7 +506,7 @@ if ($config->stage == INSTALL_ENVIRONMENT or $config->stage == INSTALL_PATHS) {
 
         echo '<div id="envresult"><dl>';
         if ($version_fail) {
-            $a = (object)array('needed'=>'5.2.8', 'current'=>phpversion());
+            $a = (object)array('needed'=>'5.3.2', 'current'=>phpversion());
             echo '<dt>'.get_string('phpversion', 'install').'</dt><dd>'.get_string('environmentrequireversion', 'admin', $a).'</dd>';
         }
         if ($curl_fail) {
index ab06c92..a5526ce 100644 (file)
@@ -76,7 +76,7 @@ $string['downloadcore'] = 'Our main method of distribution is via these standard
 $string['downloadcoretitle'] = 'Standard Moodle packages';
 $string['downloadcvs'] = 'Another way to get the core source code is by using CVS to connect directly to one of our mirrors of the code repository used by Moodle developers.  This makes upgrading very easy, even if you have made local changes to the source code.';
 $string['downloadcvstitle'] = 'Moodle via CVS';
-$string['downloadintro'] = 'Moodle is open source under the <a href="http://docs.moodle.org/en/License">GPL licence</a>.  Everything we produce is available for you to download and use for free.';
+$string['downloadintro'] = 'Moodle is open source under the <a href="http://docs.moodle.org/dev/License">GPL licence</a>.  Everything we produce is available for you to download and use for free.';
 $string['downloadlang'] = 'In current versions of Moodle, adding support for new languages has been automated from the administration menu within your site.  However, for some older versions of Moodle, new language packs need to be downloaded manually.';
 $string['downloadlangtitle'] = 'Language packs';
 $string['downloadlogotitle'] = 'Moodle logos';
index 50f5ebc..07bb2e9 100644 (file)
@@ -735,7 +735,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the Moodle License information page for full details:
-http://docs.moodle.org/en/License';
+http://docs.moodle.org/dev/License';
 $string['gpllicense'] = 'GPL license';
 $string['gpl3'] = 'Copyright (C) 1999 onwards Martin Dougiamas (http://moodle.com)
 
@@ -749,7 +749,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the Moodle License information page for full details:
-http://docs.moodle.org/en/License';
+http://docs.moodle.org/dev/License';
 $string['grade'] = 'Grade';
 $string['grades'] = 'Grades';
 $string['group'] = 'Group';
index 8aa1f84..478156c 100644 (file)
@@ -847,7 +847,7 @@ function get_categories($parent='none', $sort=NULL, $shallow=true) {
                   FROM {course_categories} cc
                $ccjoin
                   JOIN {course_categories} ccp
-                       ON (cc.path LIKE ".$DB->sql_concat('ccp.path',"'%'").")
+                       ON ((cc.parent = ccp.id) OR (cc.path LIKE ".$DB->sql_concat('ccp.path',"'/%'")."))
                  WHERE ccp.id=?
                 $sort";
         $params = array($parent);
index 1935084..b80caa2 100644 (file)
@@ -2121,7 +2121,7 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
                 $instanceids[] = $blockinstance->id;
                 // If we have more than 1000 block instances now remove all block positions
                 // and empty the array
-                if (count($contextids) > 1000) {
+                if (count($instanceids) > 1000) {
                     $instanceidstring = join(',',$instanceids);
                     $DB->delete_records_select('block_positions', 'blockinstanceid IN ('.$instanceidstring.')');
                     $instanceids = array();
@@ -2131,8 +2131,10 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
 
         upgrade_cleanup_unwanted_block_contexts($contextids);
 
-        $instanceidstring = join(',',$instanceids);
-        $DB->delete_records_select('block_positions', 'blockinstanceid IN ('.$instanceidstring.')');
+        if ($instanceids) {
+            $instanceidstring = join(',',$instanceids);
+            $DB->delete_records_select('block_positions', 'blockinstanceid IN ('.$instanceidstring.')');
+        }
 
         unset($allblockinstances);
         unset($contextids);
index 83885c3..db88a9e 100644 (file)
@@ -502,19 +502,24 @@ abstract class moodle_database {
         $where = array();
         $params = array();
 
-        $columns = $this->get_columns($table);
-        foreach ($conditions as $key=>$value) {
-            if (!isset($columns[$key])) {
-                $a = new stdClass();
-                $a->fieldname = $key;
-                $a->tablename = $table;
-                throw new dml_exception('ddlfieldnotexist', $a);
-            }
-            $column = $columns[$key];
-            if ($column->meta_type == 'X') {
-                //ok so the column is a text column. sorry no text columns in the where clause conditions
-                throw new dml_exception('textconditionsnotallowed', $conditions);
+        if (debugging()) {
+            $columns = $this->get_columns($table);
+            foreach ($conditions as $key=>$value) {
+                if (!isset($columns[$key])) {
+                    $a = new stdClass();
+                    $a->fieldname = $key;
+                    $a->tablename = $table;
+                    throw new dml_exception('ddlfieldnotexist', $a);
+                }
+                $column = $columns[$key];
+                if ($column->meta_type == 'X') {
+                    //ok so the column is a text column. sorry no text columns in the where clause conditions
+                    throw new dml_exception('textconditionsnotallowed', $conditions);
+                }
             }
+        }
+
+        foreach ($conditions as $key=>$value) {
             if (is_int($key)) {
                 throw new dml_exception('invalidnumkey');
             }
index 4f82eda..9fa697f 100644 (file)
@@ -277,12 +277,12 @@ class pgsql_native_moodle_database extends moodle_database {
             return $this->tables;
         }
         $this->tables = array();
-        $prefix = str_replace('_', '\\\\_', $this->prefix);
+        $prefix = str_replace('_', '|_', $this->prefix);
         // Get them from information_schema instead of catalog as far as
         // we want to get only own session temp objects (catalog returns all)
         $sql = "SELECT table_name
                   FROM information_schema.tables
-                 WHERE table_name LIKE '$prefix%'
+                 WHERE table_name LIKE '$prefix%' ESCAPE '|'
                    AND table_type IN ('BASE TABLE', 'LOCAL TEMPORARY')";
         $this->query_start($sql, null, SQL_QUERY_AUX);
         $result = pg_query($this->pgsql, $sql);
index df8b0c4..14f5283 100644 (file)
@@ -53,10 +53,20 @@ class file_info_context_course extends file_info {
      * @param $filename
      */
     public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
+        // try to emulate require_login() tests here
+        if (!isloggedin()) {
+            return null;
+        }
+
         if (!$this->course->visible and !has_capability('moodle/course:viewhiddencourses', $this->context)) {
             return null;
         }
 
+        if (!is_viewing($this->context) and !is_enrolled($this->context)) {
+            // no peaking here if not enrolled or inspector
+            return null;
+        }
+
         if (empty($component)) {
             return $this;
         }
index e72e315..a8149c6 100644 (file)
@@ -75,11 +75,28 @@ class file_info_context_module extends file_info {
      * @param $filename
      */
     public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
-        if (!is_enrolled($this->context) and !is_viewing($this->context)) {
+        // try to emulate require_login() tests here
+        if (!isloggedin()) {
+            return null;
+        }
+
+        $coursecontext = get_course_context($this->context);
+        if (!$this->course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+            return null;
+        }
+
+        if (!is_viewing($this->context) and !is_enrolled($this->context)) {
             // no peaking here if not enrolled or inspector
             return null;
         }
 
+        $modinfo = get_fast_modinfo($this->course);
+        $cminfo = $modinfo->get_cm($this->cm->id);
+        if (!$cminfo->uservisible) {
+            // activity hidden sorry
+            return null;
+        }
+
         if (empty($component)) {
             return $this;
         }
index 120de12..708d242 100644 (file)
@@ -717,7 +717,7 @@ function groups_course_module_visible($cm, $userid=null) {
  * @return void
  */
 function _group_verify_activegroup($courseid, $groupmode, $groupingid, array $allowedgroups) {
-    global $SESSION;
+    global $SESSION, $USER;
 
     // init activegroup array if necessary
     if (!isset($SESSION->activegroup)) {
@@ -742,7 +742,11 @@ function _group_verify_activegroup($courseid, $groupmode, $groupingid, array $al
             $SESSION->activegroup[$courseid][$groupmode][$groupingid] = 0; // all groups by default if user has accessallgroups
 
         } else if ($allowedgroups) {
-            $firstgroup = reset($allowedgroups);
+            if ($groupmode != SEPARATEGROUPS and $mygroups = groups_get_all_groups($courseid, $USER->id, $groupingid)) {
+                $firstgroup = reset($mygroups);
+            } else {
+                $firstgroup = reset($allowedgroups);
+            }
             $SESSION->activegroup[$courseid][$groupmode][$groupingid] = $firstgroup->id;
 
         } else {
index 2d6476a..536c141 100644 (file)
@@ -361,21 +361,29 @@ M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
             // Make sure we have the form
             if (form) {
                 // Create a function to handle our change event
-                var processchange = function(e, lastindex) {
-                    if ((nothing===false || select.get('value') != nothing) && lastindex != select.get('selectedIndex')) {
+                var processchange = function(e, paramobject) {
+                    if ((nothing===false || select.get('value') != nothing) && paramobject.lastindex != select.get('selectedIndex')) {
+                        //prevent event bubbling and detach handlers to prevent multiple submissions caused by double clicking
+                        e.halt();
+                        paramobject.eventkeypress.detach();
+                        paramobject.eventblur.detach();
+                        paramobject.eventchangeorblur.detach();
+
                         this.submit();
                     }
                 };
                 // Attach the change event to the keypress, blur, and click actions.
                 // We don't use the change event because IE fires it on every arrow up/down
                 // event.... usability
-                Y.on('key', processchange, select, 'press:13', form, select.get('selectedIndex'));
-                select.on('blur', processchange, form, select.get('selectedIndex'));
+                var paramobject = new Object();
+                paramobject.lastindex = select.get('selectedIndex');
+                paramobject.eventkeypress = Y.on('key', processchange, select, 'press:13', form, paramobject);
+                paramobject.eventblur = select.on('blur', processchange, form, paramobject);
                 //little hack for chrome that need onChange event instead of onClick - see MDL-23224
                 if (Y.UA.webkit) {
-                    select.on('change', processchange, form, select.get('selectedIndex'));
+                    paramobject.eventchangeorblur = select.on('change', processchange, form, paramobject);
                 } else {
-                    select.on('click', processchange, form, select.get('selectedIndex'));
+                    paramobject.eventchangeorblur = select.on('click', processchange, form, paramobject);
                 }
             }
         }
index 6cf7236..e797bf6 100644 (file)
@@ -3509,6 +3509,9 @@ function delete_user($user) {
     // last course access not necessary either
     $DB->delete_records('user_lastaccess', array('userid'=>$user->id));
 
+    // force logout - may fail if file based sessions used, sorry
+    session_kill_user($user->id);
+
     // now do a final accesslib cleanup - removes all role assignments in user context and context itself
     delete_context(CONTEXT_USER, $user->id);
 
index becd7d7..53f0315 100644 (file)
@@ -279,11 +279,40 @@ abstract class session_stub implements moodle_session {
         if (!isset($CFG->sessioncookie)) {
             $CFG->sessioncookie = '';
         }
+
+        // make sure cookie domain makes sense for this wwwroot
         if (!isset($CFG->sessioncookiedomain)) {
             $CFG->sessioncookiedomain = '';
+        } else if ($CFG->sessioncookiedomain !== '') {
+            $host = parse_url($CFG->wwwroot, PHP_URL_HOST);
+            if ($CFG->sessioncookiedomain !== $host) {
+                if (substr($CFG->sessioncookiedomain, 0, 1) === '.') {
+                    if (!preg_match('|^.*'.preg_quote($CFG->sessioncookiedomain, '|').'$|', $host)) {
+                        // invalid domain - it must be end part of host
+                        $CFG->sessioncookiedomain = '';
+                    }
+                } else {
+                    if (!preg_match('|^.*\.'.preg_quote($CFG->sessioncookiedomain, '|').'$|', $host)) {
+                        // invalid domain - it must be end part of host
+                        $CFG->sessioncookiedomain = '';
+                    }
+                }
+            }
         }
+
+        // make sure the cookiepath is valid for this wwwroot or autodetect if not specified
         if (!isset($CFG->sessioncookiepath)) {
-            $CFG->sessioncookiepath = '/';
+            $CFG->sessioncookiepath = '';
+        }
+        if ($CFG->sessioncookiepath !== '/') {
+            $path = parse_url($CFG->wwwroot, PHP_URL_PATH).'/';
+            if ($CFG->sessioncookiepath === '') {
+                $CFG->sessioncookiepath = $path;
+            } else {
+                if (strpos($path, $CFG->sessioncookiepath) !== 0 or substr($CFG->sessioncookiepath, -1) !== '/') {
+                    $CFG->sessioncookiepath = $path;
+                }
+            }
         }
 
         //discard session ID from POST, GET and globals to tighten security,
index e02e7e5..9100c19 100644 (file)
@@ -38,7 +38,6 @@ if (!defined('MOODLE_INTERNAL')) {
 require_once("$CFG->libdir/portfoliolib.php");
 require_once("$CFG->libdir/portfolio/exporter.php");
 require_once("$CFG->libdir/portfolio/plugin.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 
 class portfolio_plugin_test extends portfolio_plugin_push_base {
     public function expected_time($callertime){
index d4a44d1..4a23924 100644 (file)
@@ -36,7 +36,6 @@ if (!defined('MOODLE_INTERNAL')) {
 }
 
 require_once("$CFG->dirroot/repository/lib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 
 // Generate a mock class for each plugin subclass present
 $repository_plugins = get_list_of_plugins('repository');
index 1232659..d314ef2 100644 (file)
@@ -1528,7 +1528,7 @@ function purify_html($text, $options = array()) {
         $config->set('Core.ConvertDocumentToFragment', true);
         $config->set('Core.Encoding', 'UTF-8');
         $config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
-        $config->set('URI.AllowedSchemes', array('http'=>true, 'https'=>true, 'ftp'=>true, 'irc'=>true, 'nntp'=>true, 'news'=>true, 'rtsp'=>true, 'teamspeak'=>true, 'gopher'=>true, 'mms'=>true));
+        $config->set('URI.AllowedSchemes', array('http'=>true, 'https'=>true, 'ftp'=>true, 'irc'=>true, 'nntp'=>true, 'news'=>true, 'rtsp'=>true, 'teamspeak'=>true, 'gopher'=>true, 'mms'=>true, 'mailto'=>true));
         $config->set('Attr.AllowedFrameTargets', array('_blank'));
 
         if (!empty($CFG->allowobjectembed)) {
index 05b0e1e..f9ce0fa 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 require_once("$CFG->libdir/simpletest/portfolio_testclass.php");
 require_once("$CFG->dirroot/mod/assignment/lib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 require_once("$CFG->dirroot/mod/assignment/locallib.php");
 
 Mock::generate('assignment_portfolio_caller', 'mock_caller');
index c8f1696..1069a43 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 require_once("$CFG->libdir/simpletest/portfolio_testclass.php");
 require_once("$CFG->dirroot/mod/chat/lib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 
 /*
  * TODO: The portfolio unit tests were obselete and did not work.
index 626ef7f..4ea08e4 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 require_once("$CFG->libdir/simpletest/portfolio_testclass.php");
 require_once("$CFG->dirroot/mod/data/lib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 require_once("$CFG->dirroot/mod/data/locallib.php");
 
 /*
index d6d6da0..503ef15 100644 (file)
@@ -26,7 +26,6 @@
 
 require_once("$CFG->libdir/simpletest/portfolio_testclass.php");
 require_once("$CFG->dirroot/mod/forum/lib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 
 /*
  * TODO: The portfolio unit tests were obselete and did not work.
index 066260b..78ac42a 100644 (file)
@@ -2,7 +2,6 @@
 require_once("$CFG->libdir/simpletest/portfolio_testclass.php");
 require_once("$CFG->dirroot/mod/glossary/lib.php");
 require_once("$CFG->dirroot/mod/glossary/locallib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 
 /*
  * TODO: The portfolio unit tests were obselete and did not work.
index df4668a..15a9d8f 100644 (file)
@@ -453,8 +453,7 @@ echo '<div id="module" class="module">';
 echo '<div class="bd">';
 $questionbank->display('editq',
         $pagevars['qpage'],
-        $pagevars['qperpage'], $pagevars['qsortorder'],
-        $pagevars['qsortorderdecoded'],
+        $pagevars['qperpage'],
         $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'],
         $pagevars['showquestiontext']);
 echo '</div>';
index a6bc988..9f653c0 100644 (file)
@@ -1129,8 +1129,8 @@ class quiz_question_bank_view extends question_bank_view {
         return new moodle_url('/mod/quiz/edit.php', $params);
     }
 
-    public function display($tabname, $page, $perpage, $sortorder,
-            $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext) {
+    public function display($tabname, $page, $perpage, $cat,
+            $recurse, $showhidden, $showquestiontext) {
         global $OUTPUT;
         if ($this->process_actions_needing_ui()) {
             return;
@@ -1150,7 +1150,7 @@ class quiz_question_bank_view extends question_bank_view {
         // continues with list of questions
         $this->display_question_list($this->contexts->having_one_edit_tab_cap($tabname),
                 $this->baseurl, $cat, $this->cm, $recurse, $page,
-                $perpage, $showhidden, $sortorder, $sortorderdecoded, $showquestiontext,
+                $perpage, $showhidden, $showquestiontext,
                 $this->contexts->having_cap('moodle/question:add'));
 
         $this->display_options($recurse, $showhidden, $showquestiontext);
index cde3701..a81aa5e 100644 (file)
@@ -588,6 +588,7 @@ function quiz_upgrade_grades() {
  */
 function quiz_grade_item_update($quiz, $grades = null) {
     global $CFG, $OUTPUT;
+    require_once($CFG->dirroot . '/mod/quiz/locallib.php');
     require_once($CFG->libdir.'/gradelib.php');
 
     if (array_key_exists('cmidnumber', $quiz)) { // may not be always present
index 3aa9022..1c030f4 100644 (file)
@@ -98,12 +98,18 @@ class quiz_statistics_response_analyser {
 
     /**
      * @return bool whether this analysis has a response class more than one
-     *      different acutal response.
+     *      different acutal response, or if the actual response is different from
+     *      the model response.
      */
     public function has_actual_responses() {
         foreach ($this->responseclasses as $subpartid => $partclasses) {
-            foreach ($partclasses as $responseclassid => $notused) {
-                if (count($this->responses[$subpartid][$responseclassid]) > 1) {
+            foreach ($partclasses as $responseclassid => $modelresponse) {
+                $numresponses = count($this->responses[$subpartid][$responseclassid]);
+                if ($numresponses > 1) {
+                    return true;
+                }
+                $actualresponse = key($this->responses[$subpartid][$responseclassid]);
+                if ($numresponses == 1 && $actualresponse != $modelresponse->responseclass) {
                     return true;
                 }
             }
index 9d3c4ab..cd36bd9 100644 (file)
@@ -173,7 +173,7 @@ if ($attempt->timefinish) {
     );
     $summarydata['timetaken'] = array(
         'title'   => get_string('timetaken', 'quiz'),
-        'content' => format_time($timetaken),
+        'content' => $timetaken,
     );
 }
 
index 8bee8d8..1d9dd02 100644 (file)
@@ -17,7 +17,7 @@
 function scorm_get_resources($blocks) {
     $resources = array();
     foreach ($blocks as $block) {
-        if ($block['name'] == 'RESOURCES') {
+        if ($block['name'] == 'RESOURCES' && isset($block['children'])) {
             foreach ($block['children'] as $resource) {
                 if ($resource['name'] == 'RESOURCE') {
                     $resources[addslashes_js($resource['attrs']['IDENTIFIER'])] = $resource['attrs'];
@@ -29,12 +29,14 @@ function scorm_get_resources($blocks) {
 }
 
 function scorm_get_manifest($blocks, $scoes) {
+    global $OUTPUT;
     static $parents = array();
     static $resources;
 
     static $manifest;
     static $organization;
 
+    $manifestresourcesnotfound = array();
     if (count($blocks) > 0) {
         foreach ($blocks as $block) {
             switch ($block['name']) {
@@ -130,11 +132,16 @@ function scorm_get_manifest($blocks, $scoes) {
                         if (isset($resources[$idref]['XML:BASE'])) {
                             $base = $resources[$idref]['XML:BASE'];
                         }
-                        $scoes->elements[$manifest][$organization][$identifier]->launch = $base.$resources[$idref]['HREF'];
-                        if (empty($resources[$idref]['ADLCP:SCORMTYPE'])) {
-                            $resources[$idref]['ADLCP:SCORMTYPE'] = 'asset';
+                        if (!isset($resources[$idref])) {
+                            $manifestresourcesnotfound[] = $idref;
+                            $scoes->elements[$manifest][$organization][$identifier]->launch = '';
+                        } else {
+                            $scoes->elements[$manifest][$organization][$identifier]->launch = $base.$resources[$idref]['HREF'];
+                            if (empty($resources[$idref]['ADLCP:SCORMTYPE'])) {
+                                $resources[$idref]['ADLCP:SCORMTYPE'] = 'asset';
+                            }
+                            $scoes->elements[$manifest][$organization][$identifier]->scormtype = $resources[$idref]['ADLCP:SCORMTYPE'];
                         }
-                        $scoes->elements[$manifest][$organization][$identifier]->scormtype = $resources[$idref]['ADLCP:SCORMTYPE'];
                     }
 
                     $parent = new stdClass();
@@ -473,6 +480,13 @@ function scorm_get_manifest($blocks, $scoes) {
             }
         }
     }
+    if (!empty($manifestresourcesnotfound)) {
+        //throw warning to user to let them know manifest contains references to resources that don't appear to exist.
+        if (!defined('DEBUGGING_PRINTED')) { //prevent redirect and display warning
+            define('DEBUGGING_PRINTED', 1);
+        }
+        echo $OUTPUT->notification(get_string('invalidmanifestresource', 'scorm').' '. implode(', ',$manifestresourcesnotfound));
+    }
     return $scoes;
 }
 
index c5ce688..b7a307c 100644 (file)
@@ -546,7 +546,7 @@ function xmldb_scorm_upgrade($oldversion) {
         unset_config('updatetime', 'scorm');
         upgrade_mod_savepoint(true, 2011021402, 'scorm');
     }
-    
+
     if ($oldversion < 2011073100) {
         // change field type of objectiveid
         $table = new xmldb_table('scorm_seq_objective');
index 2fcb0ec..b972a29 100644 (file)
@@ -146,6 +146,7 @@ $string['incomplete'] = 'Incomplete';
 $string['info'] = 'Info';
 $string['interactions'] = 'Interactions';
 $string['invalidactivity'] = 'Scorm activity is incorrect';
+$string['invalidmanifestresource'] = 'WARNING: The following resources were referenced in your manifest but couldn\'t be found:';
 $string['last'] = 'Last accessed on';
 $string['lastaccess'] = 'Last access';
 $string['lastattempt'] = 'Last attempt';
diff --git a/mod/scorm/rd.js b/mod/scorm/rd.js
deleted file mode 100644 (file)
index f24a121..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-<!--
-function attach_resize_event() {
-    YAHOO.util.Event.addListener(window, 'resize', scorm_resize);
-}
-
-var prev='';
-var next='';
-function scorm_set_prev(url) {
-    prev = url;
-}
-function scorm_set_next(url) {
-    next = url;
-}
-
-function scorm_get_element_style(obj, prop, cssProp) {
-    var ret = '';
-
-    if (obj.currentStyle) {
-        ret = obj.currentStyle[prop];
-    } else if (document.defaultView && document.defaultView.getComputedStyle) {
-        var compStyle = document.defaultView.getComputedStyle(obj, null);
-        ret = compStyle.getPropertyValue(cssProp);
-    }
-
-    if (ret == 'auto') ret = '0';
-    return ret;
-}
-
-function scorm_resize () {
-    var cwidth = scormplayerdata.cwidth;
-    var cheight = scormplayerdata.cheight;
-    var winwidth = 0, winheight = 0;
-    if( typeof( window.innerWidth ) == 'number' ) {
-        //Non-IE
-        winwidth = window.innerWidth;
-        winheight = window.innerHeight;
-    } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
-        //IE 6+ in 'standards compliant mode'
-        winwidth = document.documentElement.clientWidth;
-        winheight = document.documentElement.clientHeight;
-    } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
-        //IE 4 compatible
-        winwidth = document.body.clientWidth;
-        winheight = document.body.clientHeight;
-    }
-
-    var header = document.getElementById('header');
-    var content = document.getElementById('region-content');
-    var headerheight = 0;
-    if (content) {
-        headerheight = content.offsetTop;
-    }
-
-    var footer = document.getElementById('footer');
-    var imsnavbar = document.getElementById('tochead');
-    var scormtop = document.getElementById('scormtop');
-    var footerheight = 0;
-    var imsnavheight = 0;
-    var scormtopheight = 0;
-    if (footer) {
-        footerheight = footer.offsetHeight + parseInt(scorm_get_element_style(footer, 'marginTop', 'margin-top')) + parseInt(scorm_get_element_style(footer, 'marginBottom', 'margin-bottom'));
-    }
-    if (imsnavbar) {
-        imsnavheight = imsnavbar.offsetHeight;
-    }
-    if (scormtop) {
-        scormtopheight = scormtop.offsetHeight;
-    }
-
-    var topmargin = parseInt(scorm_get_element_style(document.getElementsByTagName('body')[0], 'marginTop', 'margin-top'));
-    var bottommargin = parseInt(scorm_get_element_style(document.getElementsByTagName('body')[0], 'marginBottom', 'margin-bottom'));
-
-    var totalheight = headerheight +
-                        footerheight +
-                        scormtopheight+
-                        topmargin +
-                        bottommargin+10; // +10 to save a minor vertical scroll always present!
-
-    var totalheighttoc = totalheight+imsnavheight;
-    // override total height with configured height if it is defined
-    if (cheight > 0) {
-      winheight = cheight;
-    }
-    var finalheighttoc = winheight - totalheighttoc;
-    if (finalheighttoc <= 0) {
-        finalheighttoc = winheight;
-    }
-    var finalheight = winheight - totalheight;
-    if (finalheight <= 0) {
-        finalheight = winheight;
-    }
-    var toctree = document.getElementById('toctree');
-    if (toctree != null){
-        var toctreeHeight = toctree.offsetHeight;
-        document.getElementById('toctree').style.height = finalheighttoc + 'px';
-        var scoframe2 = document.getElementById('scoframe1');
-        document.getElementById('scormobject').style.height = finalheight + 'px';
-    }else{
-        document.getElementById('scormobject').style.height = finalheight + 'px';
-        document.getElementById('scormpage').style.height = finalheight + 'px';
-    }
-
-    // resize the content container too to move the footer below the SCORM content
-    var contenti3 = document.getElementById('content-i3');
-    if (contenti3) {
-        contenti3.style.height = (winheight - totalheight + 30) + 'px';
-    } else {
-       document.getElementById('region-main-box').style.height = (finalheight + 30) + 'px';
-    }
-     // resize the content container too to move the footer below the SCORM content
-    var contenti3 = document.getElementById('content-i3');
-    if (contenti3) {
-        contenti3.style.height = (finalheight + 30) + 'px';
-    } else {
-        document.getElementById('region-main-box').style.height = (finalheight + 30) + 'px';
-    }
-}
--->
index 78b34de..35756cc 100644 (file)
@@ -182,24 +182,33 @@ if (trim($workshop->instructreviewers)) {
     print_collapsible_region_end();
 }
 
+// extend the current assessment record with user details
+$assessment = $workshop->get_assessment_by_id($assessment->id);
+
 if ($isreviewer) {
-    echo $output->heading(get_string('assessmentbyyourself', 'workshop'), 2);
-} elseif (has_capability('mod/workshop:viewreviewernames', $workshop->context)) {
-    $assessment = $workshop->get_assessment_by_id($assessment->id); // extend the current record with user details
-    $reviewer   = new stdclass();
-    $reviewer->firstname = $assessment->reviewerfirstname;
-    $reviewer->lastname = $assessment->reviewerlastname;
-    echo $output->heading(get_string('assessmentbyknown', 'workshop', fullname($reviewer)), 2);
-} else {
-    echo $output->heading(get_string('assessmentbyunknown', 'workshop'), 2);
-}
+    $options    = array(
+        'showreviewer'  => true,
+        'showauthor'    => has_capability('mod/workshop:viewauthornames', $workshop->context),
+        'showform'      => $assessmenteditable or !is_null($assessment->grade),
+        'showweight'    => true,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mform, $options);
+    $assessment->title = get_string('assessmentbyyourself', 'workshop');
+    echo $output->render($assessment);
 
-if ($mform) {
-    $mform->display();
 } else {
-    echo $output->heading(get_string('notassessed', 'workshop'));
+    $options    = array(
+        'showreviewer'  => has_capability('mod/workshop:viewreviewernames', $workshop->context),
+        'showauthor'    => has_capability('mod/workshop:viewauthornames', $workshop->context),
+        'showform'      => $assessmenteditable or !is_null($assessment->grade),
+        'showweight'    => true,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mform, $options);
+    echo $output->render($assessment);
 }
-if ($canoverridegrades) {
+
+if (!$assessmenteditable and $canoverridegrades) {
     $feedbackform->display();
 }
+
 echo $output->footer();
index aee2e41..a487dff 100644 (file)
@@ -56,7 +56,7 @@ if ($isreviewer or $canmanage) {
 }
 
 // only the reviewer is allowed to modify the assessment
-if ($canmanage or ($isreviewer and $workshop->assessing_examples_allowed())) {
+if (($canmanage and $assessment->weight == 1) or ($isreviewer and $workshop->assessing_examples_allowed())) {
     $assessmenteditable = true;
 } else {
     $assessmenteditable = false;
@@ -119,17 +119,40 @@ if (trim($workshop->instructreviewers)) {
     print_collapsible_region_end();
 }
 
-if ($canmanage) {
-    echo $output->heading(get_string('assessmentreference', 'workshop'), 2);
-} elseif ($isreviewer) {
-    echo $output->heading(get_string('assessmentbyyourself', 'workshop'), 2);
-} else {
-    $assessment = $workshop->get_assessment_by_id($assessment->id); // extend the current record with user details
-    $reviewer   = new stdclass();
-    $reviewer->firstname = $assessment->reviewerfirstname;
-    $reviewer->lastname = $assessment->reviewerlastname;
-    echo $output->heading(get_string('assessmentbyknown', 'workshop', fullname($reviewer)), 2);
+// extend the current assessment record with user details
+$assessment = $workshop->get_assessment_by_id($assessment->id);
+
+if ($canmanage and $assessment->weight == 1) {
+    $options = array(
+        'showreviewer'  => false,
+        'showauthor'    => false,
+        'showform'      => true,
+        'showweight'    => false,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mform, $options);
+    $assessment->title = get_string('assessmentreference', 'workshop');
+    echo $output->render($assessment);
+
+} else if ($isreviewer) {
+    $options = array(
+        'showreviewer'  => true,
+        'showauthor'    => false,
+        'showform'      => true,
+        'showweight'    => false,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mform, $options);
+    $assessment->title = get_string('assessmentbyyourself', 'workshop');
+    echo $output->render($assessment);
+
+} else if ($canmanage) {
+    $options = array(
+        'showreviewer'  => true,
+        'showauthor'    => false,
+        'showform'      => true,
+        'showweight'    => false,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mform, $options);
+    echo $output->render($assessment);
 }
 
-$mform->display();
 echo $output->footer();
index 5662860..126fc04 100644 (file)
@@ -78,33 +78,45 @@ echo $output->heading(get_string('assessedexample', 'workshop'), 2);
 
 echo $output->render($workshop->prepare_example_submission($example));
 
+// if the reference assessment is available, display it
 if (!empty($mformreference)) {
-    echo $output->heading(get_string('assessmentreference', 'workshop'), 2);
-    $a = new stdclass();
-    $a->received = $workshop->real_grade($reference->grade);
-    $a->max = $workshop->real_grade(100);
-    echo $output->heading(get_string('gradeinfo', 'workshop' , $a), 3);
-    $mformreference->display();
+    $options = array(
+        'showreviewer'  => false,
+        'showauthor'    => false,
+        'showform'      => true,
+        'showweight'    => false,
+    );
+    $reference = $workshop->prepare_assessment($reference, $mformreference, $options);
+    $reference->title = get_string('assessmentreference', 'workshop');
+    echo $output->render($reference);
 }
 
 if ($isreviewer) {
-    echo $output->heading(get_string('assessmentbyyourself', 'workshop'), 2);
+    $options = array(
+        'showreviewer'  => true,
+        'showauthor'    => false,
+        'showform'      => true,
+        'showweight'    => false,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mformassessment, $options);
+    $assessment->title = get_string('assessmentbyyourself', 'workshop');
+    if ($workshop->assessing_examples_allowed()) {
+        $assessment->add_action(
+            new moodle_url($workshop->exsubmission_url($example->id), array('assess' => 'on', 'sesskey' => sesskey())),
+            get_string('reassess', 'workshop')
+        );
+    }
+    echo $output->render($assessment);
+
 } elseif ($canmanage) {
-    $reviewer   = new stdclass();
-    $reviewer->firstname = $assessment->reviewerfirstname;
-    $reviewer->lastname = $assessment->reviewerlastname;
-    echo $output->heading(get_string('assessmentbyknown', 'workshop', fullname($reviewer)), 2);
-}
-$a = new stdclass();
-$a->received = $workshop->real_grade($assessment->grade);
-$a->max = $workshop->real_grade(100);
-echo $output->heading(get_string('gradeinfo', 'workshop' , $a), 3);
-$mformassessment->display();
-echo $output->container_start('buttonsbar');
-if ($isreviewer and $workshop->assessing_examples_allowed()) {
-    $aurl = new moodle_url($workshop->exsubmission_url($example->id), array('assess' => 'on', 'sesskey' => sesskey()));
-    echo $output->single_button($aurl, get_string('reassess', 'workshop'), 'get');
+    $options = array(
+        'showreviewer'  => true,
+        'showauthor'    => false,
+        'showform'      => true,
+        'showweight'    => false,
+    );
+    $assessment = $workshop->prepare_assessment($assessment, $mformassessment, $options);
+    echo $output->render($assessment);
 }
-echo $output->container_end(); // buttonsbar
 
 echo $output->footer();
index 9031486..2302ebb 100644 (file)
@@ -43,9 +43,10 @@ $string['assessedexample'] = 'Assessed example submission';
 $string['assessedsubmission'] = 'Assessed submission';
 $string['assessingexample'] = 'Assessing example submission';
 $string['assessingsubmission'] = 'Assessing submission';
-$string['assessmentbyknown'] = 'Assessment by {$a}';
-$string['assessmentbyunknown'] = 'Assessment';
-$string['assessmentbyyourself'] = 'Assessment by yourself';
+$string['assessment'] = 'Assessment';
+$string['assessmentby'] = 'by <a href="{$a->url}">{$a->name}</a>';
+$string['assessmentbyfullname'] = 'Assessment by {$a}';
+$string['assessmentbyyourself'] = 'Your assessment';
 $string['assessmentdeleted'] = 'Assessment deallocated';
 $string['assessmentend'] = 'Deadline for assessment';
 $string['assessmentenddatetime'] = 'Assessment deadline: {$a->daydatetime} ({$a->distanceday})';
@@ -114,6 +115,7 @@ $string['examplesmode'] = 'Mode of examples assessment';
 $string['examplesubmissions'] = 'Example submissions';
 $string['examplesvoluntary'] = 'Assessment of example submission is voluntary';
 $string['feedbackauthor'] = 'Feedback for the author';
+$string['feedbackby'] = 'Feedback by {$a}';
 $string['feedbackreviewer'] = 'Feedback for the reviewer';
 $string['formataggregatedgrade'] = '{$a->grade}';
 $string['formataggregatedgradeover'] = '<del>{$a->grade}</del><br /><ins>{$a->over}</ins>';
index 26880ed..051da2f 100644 (file)
@@ -864,12 +864,12 @@ function workshop_print_recent_mod_activity($activity, $courseid, $detail, $modn
             $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
             $name = fullname($activity->user);
             $link = html_writer::link($url, $name);
-            echo get_string('assessmentbyknown', 'workshop', $link);
+            echo get_string('assessmentbyfullname', 'workshop', $link);
             echo ' - '.userdate($activity->timestamp);
             echo html_writer::end_tag('div');
         } else {
             echo html_writer::start_tag('div', array('class'=>'anonymous'));
-            echo get_string('assessmentbyunknown', 'workshop');
+            echo get_string('assessment', 'workshop');
             echo ' - '.userdate($activity->timestamp);
             echo html_writer::end_tag('div');
         }
index 9aeaa83..f0c0c36 100644 (file)
@@ -489,16 +489,15 @@ class workshop {
     public function get_submissions($authorid='all') {
         global $DB;
 
-        $sql = 'SELECT s.id, s.workshopid, s.example, s.authorid, s.timecreated, s.timemodified,
+        $authorfields      = user_picture::fields('u', null, 'authoridx', 'author');
+        $gradeoverbyfields = user_picture::fields('t', null, 'gradeoverbyx', 'over');
+        $sql = "SELECT s.id, s.workshopid, s.example, s.authorid, s.timecreated, s.timemodified,
                        s.title, s.grade, s.gradeover, s.gradeoverby, s.published,
-                       u.lastname AS authorlastname, u.firstname AS authorfirstname,
-                       u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail,
-                       t.lastname AS overlastname, t.firstname AS overfirstname,
-                       t.picture AS overpicture, t.imagealt AS overimagealt, t.email AS overemail
+                       $authorfields, $gradeoverbyfields
                   FROM {workshop_submissions} s
             INNER JOIN {user} u ON (s.authorid = u.id)
              LEFT JOIN {user} t ON (s.gradeoverby = t.id)
-                 WHERE s.example = 0 AND s.workshopid = :workshopid';
+                 WHERE s.example = 0 AND s.workshopid = :workshopid";
         $params = array('workshopid' => $this->id);
 
         if ('all' === $authorid) {
@@ -511,7 +510,7 @@ class workshop {
             // $authorid is empty
             return array();
         }
-        $sql .= ' ORDER BY u.lastname, u.firstname';
+        $sql .= " ORDER BY u.lastname, u.firstname";
 
         return $DB->get_records_sql($sql, $params);
     }
@@ -527,12 +526,13 @@ class workshop {
 
         // we intentionally check the workshopid here, too, so the workshop can't touch submissions
         // from other instances
-        $sql = 'SELECT s.*,
-                       u.lastname AS authorlastname, u.firstname AS authorfirstname, u.id AS authorid,
-                       u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail
+        $authorfields      = user_picture::fields('u', null, 'authoridx', 'author');
+        $gradeoverbyfields = user_picture::fields('g', null, 'gradeoverbyx', 'gradeoverby');
+        $sql = "SELECT s.*, $authorfields, $gradeoverbyfields
                   FROM {workshop_submissions} s
             INNER JOIN {user} u ON (s.authorid = u.id)
-                 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.id = :id';
+             LEFT JOIN {user} g ON (s.gradeoverby = g.id)
+                 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.id = :id";
         $params = array('workshopid' => $this->id, 'id' => $id);
         return $DB->get_record_sql($sql, $params, MUST_EXIST);
     }
@@ -549,12 +549,13 @@ class workshop {
         if (empty($authorid)) {
             return false;
         }
-        $sql = 'SELECT s.*,
-                       u.lastname AS authorlastname, u.firstname AS authorfirstname, u.id AS authorid,
-                       u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail
+        $authorfields      = user_picture::fields('u', null, 'authoridx', 'author');
+        $gradeoverbyfields = user_picture::fields('g', null, 'gradeoverbyx', 'gradeoverby');
+        $sql = "SELECT s.*, $authorfields, $gradeoverbyfields
                   FROM {workshop_submissions} s
             INNER JOIN {user} u ON (s.authorid = u.id)
-                 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.authorid = :authorid';
+             LEFT JOIN {user} g ON (s.gradeoverby = g.id)
+                 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.authorid = :authorid";
         $params = array('workshopid' => $this->id, 'authorid' => $authorid);
         return $DB->get_record_sql($sql, $params);
     }
@@ -567,10 +568,10 @@ class workshop {
     public function get_published_submissions($orderby='finalgrade DESC') {
         global $DB;
 
+        $authorfields = user_picture::fields('u', null, 'authoridx', 'author');
         $sql = "SELECT s.id, s.authorid, s.timecreated, s.timemodified,
                        s.title, s.grade, s.gradeover, COALESCE(s.gradeover,s.grade) AS finalgrade,
-                       u.lastname AS authorlastname, u.firstname AS authorfirstname, u.id AS authorid,
-                       u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail
+                       $authorfields
                   FROM {workshop_submissions} s
             INNER JOIN {user} u ON (s.authorid = u.id)
                  WHERE s.example = 0 AND s.workshopid = :workshopid AND s.published = 1
@@ -705,6 +706,44 @@ class workshop {
         return $summary;
     }
 
+    /**
+     * Prepares renderable assessment component
+     *
+     * The $options array supports the following keys:
+     * showauthor - should the author user info be available for the renderer
+     * showreviewer - should the reviewer user info be available for the renderer
+     * showform - show the assessment form if it is available
+     *
+     * @param stdClass $record as returned by eg {@link self::get_assessment_by_id()}
+     * @param workshop_assessment_form|null $form as returned by {@link workshop_strategy::get_assessment_form()}
+     * @param array $options
+     * @return workshop_assessment
+     */
+    public function prepare_assessment(stdClass $record, $form, array $options = array()) {
+
+        $assessment             = new workshop_assessment($record, $options);
+        $assessment->url        = $this->assess_url($record->id);
+        $assessment->maxgrade   = $this->real_grade(100);
+
+        if (!empty($options['showform']) and !($form instanceof workshop_assessment_form)) {
+            debugging('Not a valid instance of workshop_assessment_form supplied', DEBUG_DEVELOPER);
+        }
+
+        if (!empty($options['showform']) and ($form instanceof workshop_assessment_form)) {
+            $assessment->form = $form;
+        }
+
+        if (empty($options['showweight'])) {
+            $assessment->weight = null;
+        }
+
+        if (!is_null($record->grade)) {
+            $assessment->realgrade = $this->real_grade($record->grade);
+        }
+
+        return $assessment;
+    }
+
     /**
      * Removes the submission and all relevant data
      *
@@ -730,17 +769,20 @@ class workshop {
     public function get_all_assessments() {
         global $DB;
 
-        $sql = 'SELECT a.id, a.submissionid, a.reviewerid, a.timecreated, a.timemodified,
+        $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
+        $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
+        $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
+        $sql = "SELECT a.id, a.submissionid, a.reviewerid, a.timecreated, a.timemodified,
                        a.grade, a.gradinggrade, a.gradinggradeover, a.gradinggradeoverby,
-                       reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
-                       s.title,
-                       author.id AS authorid, author.firstname AS authorfirstname,author.lastname AS authorlastname
+                       $reviewerfields, $authorfields, $overbyfields,
+                       s.title
                   FROM {workshop_assessments} a
             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
             INNER JOIN {user} author ON (s.authorid = author.id)
+             LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
                  WHERE s.workshopid = :workshopid AND s.example = 0
-              ORDER BY reviewer.lastname, reviewer.firstname';
+              ORDER BY reviewer.lastname, reviewer.firstname";
         $params = array('workshopid' => $this->id);
 
         return $DB->get_records_sql($sql, $params);
@@ -755,15 +797,16 @@ class workshop {
     public function get_assessment_by_id($id) {
         global $DB;
 
-        $sql = 'SELECT a.*,
-                       reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
-                       s.title,
-                       author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname
+        $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
+        $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
+        $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
+        $sql = "SELECT a.*, s.title, $reviewerfields, $authorfields, $overbyfields
                   FROM {workshop_assessments} a
             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
             INNER JOIN {user} author ON (s.authorid = author.id)
-                 WHERE a.id = :id AND s.workshopid = :workshopid';
+             LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
+                 WHERE a.id = :id AND s.workshopid = :workshopid";
         $params = array('id' => $id, 'workshopid' => $this->id);
 
         return $DB->get_record_sql($sql, $params, MUST_EXIST);
@@ -779,15 +822,16 @@ class workshop {
     public function get_assessment_of_submission_by_user($submissionid, $reviewerid) {
         global $DB;
 
-        $sql = 'SELECT a.*,
-                       reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
-                       s.title,
-                       author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname
+        $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
+        $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
+        $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
+        $sql = "SELECT a.*, s.title, $reviewerfields, $authorfields, $overbyfields
                   FROM {workshop_assessments} a
             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
             INNER JOIN {user} author ON (s.authorid = author.id)
-                 WHERE s.id = :sid AND reviewer.id = :rid AND s.workshopid = :workshopid';
+             LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
+                 WHERE s.id = :sid AND reviewer.id = :rid AND s.workshopid = :workshopid";
         $params = array('sid' => $submissionid, 'rid' => $reviewerid, 'workshopid' => $this->id);
 
         return $DB->get_record_sql($sql, $params, IGNORE_MISSING);
@@ -802,12 +846,15 @@ class workshop {
     public function get_assessments_of_submission($submissionid) {
         global $DB;
 
-        $sql = 'SELECT a.*,
-                       reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname AS reviewerlastname
+        $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
+        $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
+        $sql = "SELECT a.*, s.title, $reviewerfields, $overbyfields
                   FROM {workshop_assessments} a
             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
-                 WHERE s.example = 0 AND s.id = :submissionid AND s.workshopid = :workshopid';
+             LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
+                 WHERE s.example = 0 AND s.id = :submissionid AND s.workshopid = :workshopid
+              ORDER BY reviewer.lastname, reviewer.firstname, reviewer.id";
         $params = array('submissionid' => $submissionid, 'workshopid' => $this->id);
 
         return $DB->get_records_sql($sql, $params);
@@ -822,17 +869,18 @@ class workshop {
     public function get_assessments_by_reviewer($reviewerid) {
         global $DB;
 
-        $sql = 'SELECT a.*,
-                       reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname AS reviewerlastname,
+        $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
+        $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
+        $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
+        $sql = "SELECT a.*, $reviewerfields, $authorfields, $overbyfields,
                        s.id AS submissionid, s.title AS submissiontitle, s.timecreated AS submissioncreated,
-                       s.timemodified AS submissionmodified,
-                       author.id AS authorid, author.firstname AS authorfirstname,author.lastname AS authorlastname,
-                       author.picture AS authorpicture, author.imagealt AS authorimagealt, author.email AS authoremail
+                       s.timemodified AS submissionmodified
                   FROM {workshop_assessments} a
             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
             INNER JOIN {user} author ON (s.authorid = author.id)
-                 WHERE s.example = 0 AND reviewer.id = :reviewerid AND s.workshopid = :workshopid';
+             LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
+                 WHERE s.example = 0 AND reviewer.id = :reviewerid AND s.workshopid = :workshopid";
         $params = array('reviewerid' => $reviewerid, 'workshopid' => $this->id);
 
         return $DB->get_records_sql($sql, $params);
@@ -2619,6 +2667,124 @@ class workshop_example_submission extends workshop_example_submission_summary im
     protected $fields = array('id', 'title', 'content', 'contentformat', 'contenttrust', 'attachment');
 }
 
+
+/**
+ * Common base class for assessments rendering
+ *
+ * Subclasses of this class convert raw assessment record from
+ * workshop_assessments table (as returned by {@see workshop::get_assessment_by_id()}
+ * for example) into renderable objects.
+ */
+abstract class workshop_assessment_base {
+
+    /** @var string the optional title of the assessment */
+    public $title = '';
+
+    /** @var workshop_assessment_form $form as returned by {@link workshop_strategy::get_assessment_form()} */
+    public $form;
+
+    /** @var moodle_url */
+    public $url;
+
+    /** @var float|null the real received grade */
+    public $realgrade = null;
+
+    /** @var float the real maximum grade */
+    public $maxgrade;
+
+    /** @var stdClass|null reviewer user info */
+    public $reviewer = null;
+
+    /** @var stdClass|null assessed submission's author user info */
+    public $author = null;
+
+    /** @var array of actions */
+    public $actions = array();
+
+    /* @var array of columns that are assigned as properties */
+    protected $fields = array();
+
+    /**
+     * Copies the properties of the given database record into properties of $this instance
+     *
+     * The $options keys are: showreviewer, showauthor
+     * @param stdClass $assessment full record
+     * @param array $options additional properties
+     */
+    public function __construct(stdClass $record, array $options = array()) {
+
+        foreach ($this->fields as $field) {
+            if (!property_exists($record, $field)) {
+                throw new coding_exception('Assessment record must provide public property ' . $field);
+            }
+            if (!property_exists($this, $field)) {
+                throw new coding_exception('Renderable component must accept public property ' . $field);
+            }
+            $this->{$field} = $record->{$field};
+        }
+
+        if (!empty($options['showreviewer'])) {
+            $this->reviewer = user_picture::unalias($record, null, 'revieweridx', 'reviewer');
+        }
+
+        if (!empty($options['showauthor'])) {
+            $this->author = user_picture::unalias($record, null, 'authorid', 'author');
+        }
+    }
+
+    /**
+     * Adds a new action
+     *
+     * @param moodle_url $url action URL
+     * @param string $label action label
+     * @param string $method get|post
+     */
+    public function add_action(moodle_url $url, $label, $method = 'get') {
+
+        $action = new stdClass();
+        $action->url = $url;
+        $action->label = $label;
+        $action->method = $method;
+
+        $this->actions[] = $action;
+    }
+}
+
+
+/**
+ * Represents a rendarable full assessment
+ */
+class workshop_assessment extends workshop_assessment_base implements renderable {
+
+    /** @var int */
+    public $id;
+
+    /** @var int */
+    public $submissionid;
+
+    /** @var int */
+    public $weight;
+
+    /** @var int */
+    public $timecreated;
+
+    /** @var int */
+    public $timemodified;
+
+    /** @var float */
+    public $grade;
+
+    /** @var float */
+    public $gradinggrade;
+
+    /** @var float */
+    public $gradinggradeover;
+
+    /** @var array */
+    protected $fields = array('id', 'submissionid', 'weight', 'timecreated',
+        'timemodified', 'grade', 'gradinggrade', 'gradinggradeover');
+}
+
 /**
  * Renderable message to be displayed to the user
  *
@@ -2813,3 +2979,93 @@ class workshop_grading_report implements renderable {
         return $this->options;
     }
 }
+
+
+/**
+ * Base class for renderable feedback for author and feedback for reviewer
+ */
+abstract class workshop_feedback {
+
+    /** @var stdClass the user info */
+    protected $provider = null;
+
+    /** @var string the feedback text */
+    protected $content = null;
+
+    /** @var int format of the feedback text */
+    protected $format = null;
+
+    /**
+     * @return stdClass the user info
+     */
+    public function get_provider() {
+
+        if (is_null($this->provider)) {
+            throw new coding_exception('Feedback provider not set');
+        }
+
+        return $this->provider;
+    }
+
+    /**
+     * @return string the feedback text
+     */
+    public function get_content() {
+
+        if (is_null($this->content)) {
+            throw new coding_exception('Feedback content not set');
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * @return int format of the feedback text
+     */
+    public function get_format() {
+
+        if (is_null($this->format)) {
+            throw new coding_exception('Feedback text format not set');
+        }
+
+        return $this->format;
+    }
+}
+
+
+/**
+ * Renderable feedback for the author of submission
+ */
+class workshop_feedback_author extends workshop_feedback implements renderable {
+
+    /**
+     * Extracts feedback from the given submission record
+     *
+     * @param stdClass $submission record as returned by {@see self::get_submission_by_id()}
+     */
+    public function __construct(stdClass $submission) {
+
+        $this->provider = user_picture::unalias($submission, null, 'gradeoverbyx', 'gradeoverby');
+        $this->content  = $submission->feedbackauthor;
+        $this->format   = $submission->feedbackauthorformat;
+    }
+}
+
+
+/**
+ * Renderable feedback for the reviewer
+ */
+class workshop_feedback_reviewer extends workshop_feedback implements renderable {
+
+    /**
+     * Extracts feedback from the given assessment record
+     *
+     * @param stdClass $assessment record as returned by eg {@see self::get_assessment_by_id()}
+     */
+    public function __construct(stdClass $assessment) {
+
+        $this->provider = user_picture::unalias($assessment, null, 'gradinggradeoverbyx', 'overby');
+        $this->content  = $assessment->feedbackreviewer;
+        $this->format   = $assessment->feedbackreviewerformat;
+    }
+}
index 1b1333e..955a171 100644 (file)
@@ -91,7 +91,14 @@ class mod_workshop_renderer extends plugin_renderer_base {
         }
         $o .= $this->output->container_start($classes);
         $o .= $this->output->container_start('header');
-        $o .= $this->output->heading(format_string($submission->title), 3, 'title');
+
+        $title = format_string($submission->title);
+
+        if ($this->page->url != $submission->url) {
+            $title = html_writer::link($submission->url, $title);
+        }
+
+        $o .= $this->output->heading($title, 3, 'title');
 
         if (!$anonymous) {
             $author             = new stdclass();
@@ -490,6 +497,137 @@ class mod_workshop_renderer extends plugin_renderer_base {
         return html_writer::table($table);
     }
 
+    /**
+     * Renders the feedback for the author of the submission
+     *
+     * @param workshop_feedback_author $feedback
+     * @return string HTML
+     */
+    protected function render_workshop_feedback_author(workshop_feedback_author $feedback) {
+        return $this->helper_render_feedback($feedback);
+    }
+
+    /**
+     * Renders the feedback for the reviewer of the submission
+     *
+     * @param workshop_feedback_reviewer $feedback
+     * @return string HTML
+     */
+    protected function render_workshop_feedback_reviewer(workshop_feedback_reviewer $feedback) {
+        return $this->helper_render_feedback($feedback);
+    }
+
+    /**
+     * Helper method to rendering feedback
+     *
+     * @param workshop_feedback_author|workshop_feedback_reviewer $feedback
+     * @return string HTML
+     */
+    private function helper_render_feedback($feedback) {
+
+        $o  = '';    // output HTML code
+        $o .= $this->output->container_start('feedback feedbackforauthor');
+        $o .= $this->output->container_start('header');
+        $o .= $this->output->heading(get_string('feedbackby', 'workshop', s(fullname($feedback->get_provider()))), 3, 'title');
+
+        $userpic = $this->output->user_picture($feedback->get_provider(), array('courseid' => $this->page->course->id, 'size' => 32));
+        $o .= $this->output->container($userpic, 'picture');
+        $o .= $this->output->container_end(); // end of header
+
+        $content = format_text($feedback->get_content(), $feedback->get_format(), array('overflowdiv' => true));
+        $o .= $this->output->container($content, 'content');
+
+        $o .= $this->output->container_end();
+
+        return $o;
+    }
+
+    /**
+     * Renders the full assessment
+     *
+     * @param workshop_assessment $assessment
+     * @return string HTML
+     */
+    protected function render_workshop_assessment(workshop_assessment $assessment) {
+
+        $o = ''; // output HTML code
+        $anonymous = is_null($assessment->reviewer);
+        $classes = 'assessment-full';
+        if ($anonymous) {
+            $classes .= ' anonymous';
+        }
+
+        $o .= $this->output->container_start($classes);
+        $o .= $this->output->container_start('header');
+
+        if (!empty($assessment->title)) {
+            $title = s($assessment->title);
+        } else {
+            $title = get_string('assessment', 'workshop');
+        }
+        if ($this->page->url != $assessment->url) {
+            $o .= $this->output->container(html_writer::link($assessment->url, $title), 'title');
+        } else {
+            $o .= $this->output->container($title, 'title');
+        }
+
+        if (!$anonymous) {
+            $reviewer   = $assessment->reviewer;
+            $userpic    = $this->output->user_picture($reviewer, array('courseid' => $this->page->course->id, 'size' => 32));
+
+            $userurl    = new moodle_url('/user/view.php',
+                                       array('id' => $reviewer->id, 'course' => $this->page->course->id));
+            $a          = new stdClass();
+            $a->name    = fullname($reviewer);
+            $a->url     = $userurl->out();
+            $byfullname = get_string('assessmentby', 'workshop', $a);
+            $oo         = $this->output->container($userpic, 'picture');
+            $oo        .= $this->output->container($byfullname, 'fullname');
+
+            $o .= $this->output->container($oo, 'reviewer');
+        }
+
+        if (is_null($assessment->realgrade)) {
+            $o .= $this->output->container(
+                get_string('notassessed', 'workshop'),
+                'grade nograde'
+            );
+        } else {
+            $a              = new stdClass();
+            $a->max         = $assessment->maxgrade;
+            $a->received    = $assessment->realgrade;
+            $o .= $this->output->container(
+                get_string('gradeinfo', 'workshop', $a),
+                'grade'
+            );
+
+            if (!is_null($assessment->weight) and $assessment->weight != 1) {
+                $o .= $this->output->container(
+                    get_string('weightinfo', 'workshop', $assessment->weight),
+                    'weight'
+                );
+            }
+        }
+
+        $o .= $this->output->container_start('actions');
+        foreach ($assessment->actions as $action) {
+            $o .= $this->output->single_button($action->url, $action->label, $action->method);
+        }
+        $o .= $this->output->container_end(); // actions
+
+        $o .= $this->output->container_end(); // header
+
+        if (!is_null($assessment->form)) {
+            $o .= print_collapsible_region_start('assessment-form-wrapper', uniqid('workshop-assessment'),
+                    get_string('assessmentform', 'workshop'), '', false, true);
+            $o .= $this->output->container(self::moodleform($assessment->form), 'assessment-form');
+            $o .= print_collapsible_region_end(true);
+        }
+
+        $o .= $this->output->container_end(); // main wrapper
+
+        return $o;
+    }
 
     ////////////////////////////////////////////////////////////////////////////
     // Internal rendering helper methods
@@ -726,6 +864,22 @@ class mod_workshop_renderer extends plugin_renderer_base {
     // Static helpers
     ////////////////////////////////////////////////////////////////////////////
 
+    /**
+     * Helper method dealing with the fact we can not just fetch the output of moodleforms
+     *
+     * @param moodleform $mform
+     * @return string HTML
+     */
+    protected static function moodleform(moodleform $mform) {
+
+        ob_start();
+        $mform->display();
+        $o = ob_get_contents();
+        ob_end_clean();
+
+        return $o;
+    }
+
     /**
      * Helper function returning the n-th item of the array
      *
index 22c4851..aea0cf8 100644 (file)
@@ -45,7 +45,8 @@
 
 .path-mod-workshop .submission-summary.anonymous .title,
 .path-mod-workshop .submission-summary.anonymous .author,
-.path-mod-workshop .submission-summary.anonymous .userdate {
+.path-mod-workshop .submission-summary.anonymous .userdate,
+.path-mod-workshop .submission-summary.anonymous .grade-status {
     margin: 0px 0px 0px 5px;
 }
 
 }
 
 /**
- * Assessment
+ * Assessment - full display
  */
-.path-mod-workshop .assessment-summary.graded {
+.path-mod-workshop .assessment-full {
+    border: 1px solid #ddd;
+    margin: 0px auto 1em auto;
+}
+
+.path-mod-workshop .assessment-full .header {
+    position: relative;
+    background-color: #ddd;
+    padding: 3px;
+    min-height: 35px;
+}
+
+.path-mod-workshop .assessment-full .header .title {
+    font-weight: bold;
+}
+
+.path-mod-workshop .assessment-full .header .title,
+.path-mod-workshop .assessment-full .header .reviewer,
+.path-mod-workshop .assessment-full .header .grade,
+.path-mod-workshop .assessment-full .header .weight {
+    margin: 0px 0px 0px 40px;
+}
+
+.path-mod-workshop .assessment-full.anonymous .header .title,
+.path-mod-workshop .assessment-full.anonymous .header .reviewer,
+.path-mod-workshop .assessment-full.anonymous .header .grade,
+.path-mod-workshop .assessment-full.anonymous .header .weight {
+    margin: 0px 0px 0px 5px;
+}
+
+.path-mod-workshop .assessment-full .header .reviewer .picture {
+    position: absolute;
+    top: 3px;
+    left: 3px;
+}
+
+.path-mod-workshop .assessment-full .header .actions {
+    position: absolute;
+    top: 5px;
+    right: 5px;
+    text-align: right;
+}
+
+.path-mod-workshop .assessment-full .header .actions .singlebutton,
+.path-mod-workshop .assessment-full .header .actions .singlebutton form,
+.path-mod-workshop .assessment-full .header .actions .singlebutton form div {
+    display: inline;
+}
+
+.path-mod-workshop .assessment-full .assessment-form-wrapper {
+    margin-top: 0.5em;
+    padding: 0px 1em;
+}
+
+.path-mod-workshop .assessment-summary.graded .singlebutton input[type="submit"],
+.path-mod-workshop .example-summary.graded .singlebutton input[type="submit"] {
     background-color: #e7f1c3;
 }
 
-.path-mod-workshop .example-summary.notgraded {
+.path-mod-workshop .assessment-summary.notgraded .singlebutton input[type="submit"],
+.path-mod-workshop .example-summary.notgraded .singlebutton input[type="submit"] {
     background-color: #ffd3d9;
 }
 
+/**
+ * Assessment form
+ */
 .path-mod-workshop .assessmentform .description {
     margin: 0px 1em;
 }
     vertical-align: top;
 }
 
+/**
+ * Feedback
+ */
+.path-mod-workshop .feedback {
+    border: 1px solid #ddd;
+    margin: 0px auto 1em auto;
+    width: 80%;
+}
+
+.path-mod-workshop .feedback .header {
+    position: relative;
+    background-color: #ddd;
+    padding: 3px;
+    min-height: 35px;
+}
+
+.path-mod-workshop .feedback .header .title {
+    margin: 0px 0px 0px 40px;
+}
+
+.path-mod-workshop .feedback .header .picture {
+    position: absolute;
+    top: 3px;
+    left: 3px;
+}
+
+.path-mod-workshop .feedback .content {
+    padding: 5px 10px;
+}
+
 /**
  * Misc
  */
index 2932c90..daf76d1 100644 (file)
@@ -43,7 +43,11 @@ if (isguestuser()) {
 $workshop = $DB->get_record('workshop', array('id' => $cm->instance), '*', MUST_EXIST);
 $workshop = new workshop($workshop, $cm, $course);
 
-$PAGE->set_url($workshop->submission_url(), array('cmid' => $cmid, 'id' => $id, 'edit' => $edit));
+$PAGE->set_url($workshop->submission_url(), array('cmid' => $cmid, 'id' => $id));
+
+if ($edit) {
+    $PAGE->url->param('edit', $edit);
+}
 
 if ($id) { // submission is specified
     $submission = $workshop->get_submission_by_id($id);
@@ -272,69 +276,79 @@ if ($submission->id and !$edit and !$isreviewer and $canallocate and $workshop->
     echo $output->single_button($url, get_string('assess', 'workshop'), 'post');
 }
 
+if (($workshop->phase == workshop::PHASE_CLOSED) and ($ownsubmission or $canviewall)) {
+    if (!empty($submission->gradeoverby) and strlen(trim($submission->feedbackauthor)) > 0) {
+        echo $output->render(new workshop_feedback_author($submission));
+    }
+}
+
 // and possibly display the submission's review(s)
 
 if ($isreviewer) {
-    $strategy = $workshop->grading_strategy_instance();
-    $mform = $strategy->get_assessment_form($PAGE->url, 'assessment', $userassessment, false);
-    echo $output->heading(get_string('assessmentbyyourself', 'workshop'), 2);
-    // reviewers can always see the grades they gave even they are not available yet
-    if (is_null($userassessment->grade)) {
-        echo $output->heading(get_string('notassessed', 'workshop'), 3);
-        if ($workshop->assessing_allowed($USER->id)) {
-            echo $output->container($output->single_button($workshop->assess_url($userassessment->id), get_string('assess', 'workshop'), 'get'),
-                    array('class' => 'buttonsbar'));
-        }
-    } else {
-        $a = new stdclass();
-        $a->max = $workshop->real_grade(100);
-        $a->received = $workshop->real_grade($userassessment->grade);
-        echo $output->heading(get_string('gradeinfo', 'workshop', $a), 3);
-        if ($userassessment->weight != 1) {
-            echo $output->heading(get_string('weightinfo', 'workshop', $userassessment->weight), 3);
+    // user's own assessment
+    $strategy   = $workshop->grading_strategy_instance();
+    $mform      = $strategy->get_assessment_form($PAGE->url, 'assessment', $userassessment, false);
+    $options    = array(
+        'showreviewer'  => true,
+        'showauthor'    => $showauthor,
+        'showform'      => !is_null($userassessment->grade),
+        'showweight'    => true,
+    );
+    $assessment = $workshop->prepare_assessment($userassessment, $mform, $options);
+    $assessment->title = get_string('assessmentbyyourself', 'workshop');
+
+    if ($workshop->assessing_allowed($USER->id)) {
+        if (is_null($userassessment->grade)) {
+            $assessment->add_action($workshop->assess_url($assessment->id), get_string('assess', 'workshop'));
+        } else {
+            $assessment->add_action($workshop->assess_url($assessment->id), get_string('reassess', 'workshop'));
         }
-        if ($workshop->assessing_allowed($USER->id)) {
-            echo $output->container($output->single_button($workshop->assess_url($userassessment->id), get_string('reassess', 'workshop'), 'get'),
-                    array('class' => 'buttonsbar'));
+    }
+    if ($canoverride) {
+        $assessment->add_action($workshop->assess_url($assessment->id), get_string('assessmentsettings', 'workshop'));
+    }
+
+    echo $output->render($assessment);
+
+    if ($workshop->phase == workshop::PHASE_CLOSED) {
+        if (strlen(trim($userassessment->feedbackreviewer)) > 0) {
+            echo $output->render(new workshop_feedback_reviewer($userassessment));
         }
-        $mform->display();
     }
 }
 
 if (has_capability('mod/workshop:viewallassessments', $workshop->context) or ($ownsubmission and $workshop->assessments_available())) {
-    $strategy = $workshop->grading_strategy_instance();
-    $assessments = $workshop->get_assessments_of_submission($submission->id);
-    $canviewreviewernames = has_capability('mod/workshop:viewreviewernames', $workshop->context);
+    // other assessments
+    $strategy       = $workshop->grading_strategy_instance();
+    $assessments    = $workshop->get_assessments_of_submission($submission->id);
+    $showreviewer   = has_capability('mod/workshop:viewreviewernames', $workshop->context);
     foreach ($assessments as $assessment) {
         if ($assessment->reviewerid == $USER->id) {
             // own assessment has been displayed already
             continue;
         }
-        if (is_null($assessment->grade)) {
-            // not graded assessment are not displayed
+        if (is_null($assessment->grade) and !has_capability('mod/workshop:viewallassessments', $workshop->context)) {
+            // students do not see peer-assessment that are not graded yet
             continue;
         }
-        if ($canviewreviewernames) {
-            $reviewer = new stdclass();
-            $reviewer->firstname = $assessment->reviewerfirstname;
-            $reviewer->lastname = $assessment->reviewerlastname;
-            echo $output->heading(get_string('assessmentbyknown', 'workshop', fullname($reviewer)), 2);
-        } else {
-            echo $output->heading(get_string('assessmentbyunknown', 'workshop'), 2);
-        }
-        $a = new stdclass();
-        $a->max = $workshop->real_grade(100);
-        $a->received = $workshop->real_grade($assessment->grade);
-        echo $output->heading(get_string('gradeinfo', 'workshop', $a), 3);
-        if ($assessment->weight != 1) {
-            echo $output->heading(get_string('weightinfo', 'workshop', $assessment->weight), 3);
+        $mform      = $strategy->get_assessment_form($PAGE->url, 'assessment', $assessment, false);
+        $options    = array(
+            'showreviewer'  => $showreviewer,
+            'showauthor'    => $showauthor,
+            'showform'      => !is_null($assessment->grade),
+            'showweight'    => true,
+        );
+        $displayassessment = $workshop->prepare_assessment($assessment, $mform, $options);
+        if ($canoverride) {
+            $displayassessment->add_action($workshop->assess_url($assessment->id), get_string('assessmentsettings', 'workshop'));
         }
-        if (has_capability('mod/workshop:overridegrades', $workshop->context)) {
-            echo $output->container($output->single_button($workshop->assess_url($assessment->id), get_string('assessmentsettings', 'workshop'), 'get'),
-                    array('class' => 'buttonsbar'));
+        echo $output->render($displayassessment);
+
+        if ($workshop->phase == workshop::PHASE_CLOSED and has_capability('mod/workshop:viewallassessments', $workshop->context)) {
+            if (strlen(trim($assessment->feedbackreviewer)) > 0) {
+                echo $output->render(new workshop_feedback_reviewer($assessment));
+            }
         }
-        $mform = $strategy->get_assessment_form($PAGE->url, 'assessment', $assessment, false);
-        $mform->display();
     }
 }
 
index b733ce9..999894d 100644 (file)
@@ -493,6 +493,11 @@ case workshop::PHASE_CLOSED:
             echo $output->container(get_string('noyoursubmission', 'workshop'));
         }
         echo $output->box_end();
+
+        if (!empty($submission->gradeoverby) and strlen(trim($submission->feedbackauthor)) > 0) {
+            echo $output->render(new workshop_feedback_author($submission));
+        }
+
         print_collapsible_region_end();
     }
     if (has_capability('mod/workshop:viewpublishedsubmissions', $workshop->context)) {
@@ -535,6 +540,10 @@ case workshop::PHASE_CLOSED:
             echo $output->box_start('generalbox assessment-summary' . $class);
             echo $output->render($workshop->prepare_submission_summary($submission, $shownames));
             echo $output->box_end();
+
+            if (strlen(trim($assessment->feedbackreviewer)) > 0) {
+                echo $output->render(new workshop_feedback_reviewer($assessment));
+            }
         }
         print_collapsible_region_end();
     }
index f3099de..20600d1 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 require_once("$CFG->libdir/simpletest/testportfoliolib.php");
 require_once("$CFG->dirroot/portfolio/boxnet/lib.php");
-require_once("$CFG->dirroot/$CFG->admin/generator.php");
 
 /*
  * TODO: The portfolio unit tests were obselete and did not work.
index c52b4a4..5359fe7 100644 (file)
@@ -65,9 +65,9 @@ $PAGE->set_heading($COURSE->fullname);
 echo $OUTPUT->header();
 
 echo '<div class="questionbankwindow boxwidthwide boxaligncenter">';
-$questionbank->display('questions', $pagevars['qpage'],
-        $pagevars['qperpage'], $pagevars['qsortorder'], $pagevars['qsortorderdecoded'],
-        $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['showquestiontext']);
+$questionbank->display('questions', $pagevars['qpage'], $pagevars['qperpage'],
+        $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'],
+        $pagevars['showquestiontext']);
 echo "</div>\n";
 
 echo $OUTPUT->footer();
index 89bba58..52faf97 100644 (file)
@@ -155,7 +155,6 @@ abstract class question_bank_column_base {
 
     /**
      * Output the column header cell.
-     * @param int $currentsort 0 for none. 1 for normal sort, -1 for reverse sort.
      */
     public function display_header() {
         echo '<th class="header ' . $this->get_classes() . '" scope="col">';
@@ -1168,8 +1167,8 @@ class question_bank_view {
      * category      Chooses the category
      * displayoptions Sets display options
      */
-    public function display($tabname, $page, $perpage, $sortorder,
-            $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext){
+    public function display($tabname, $page, $perpage, $cat,
+            $recurse, $showhidden, $showquestiontext) {
         global $PAGE, $OUTPUT;
 
         if ($this->process_actions_needing_ui()) {
@@ -1191,8 +1190,9 @@ class question_bank_view {
         $this->print_category_info($category);
 
         // continues with list of questions
-        $this->display_question_list($this->contexts->having_one_edit_tab_cap($tabname), $this->baseurl, $cat, $this->cm,
-                $recurse, $page, $perpage, $showhidden, $sortorder, $sortorderdecoded, $showquestiontext,
+        $this->display_question_list($this->contexts->having_one_edit_tab_cap($tabname),
+                $this->baseurl, $cat, $this->cm,
+                $recurse, $page, $perpage, $showhidden, $showquestiontext,
                 $this->contexts->having_cap('moodle/question:add'));
     }
 
@@ -1297,7 +1297,6 @@ class question_bank_view {
     */
     protected function display_question_list($contexts, $pageurl, $categoryandcontext,
             $cm = null, $recurse=1, $page=0, $perpage=100, $showhidden=false,
-            $sortorder='typename', $sortorderdecoded='qtype, name ASC',
             $showquestiontext = false, $addcontexts = array()) {
         global $CFG, $DB, $OUTPUT;
 
@@ -1639,17 +1638,12 @@ function question_edit_setup($edittab, $baseurl, $requirecmid = false, $requirec
         $pagevars['qperpage'] = DEFAULT_QUESTIONS_PER_PAGE;
     }
 
-    $sortoptions = array('alpha' => 'name, qtype ASC',
-                          'typealpha' => 'qtype, name ASC',
-                          'age' => 'id ASC');
-
-    if ($sortorder = optional_param('qsortorder', '', PARAM_ALPHA)) {
-        $pagevars['qsortorderdecoded'] = $sortoptions[$sortorder];
-        $pagevars['qsortorder'] = $sortorder;
-        $thispageurl->param('qsortorder', $sortorder);
-    } else {
-        $pagevars['qsortorderdecoded'] = $sortoptions['typealpha'];
-        $pagevars['qsortorder'] = 'typealpha';
+    for ($i = 1; $i <= question_bank_view::MAX_SORTS; $i++) {
+        $param = 'qbs' . $i;
+        if (!$sort = optional_param($param, '', PARAM_ALPHAEXT)) {
+            break;
+        }
+        $thispageurl->param($param, $sort);
     }
 
     $defaultcategory = question_make_default_categories($contexts->all());
index b08e7fb..757099b 100644 (file)
@@ -355,7 +355,7 @@ abstract class question_edit_form extends moodleform {
         $mform = $this->_form;
 
         $repeated = array();
-        $repeated[] = $mform->createElement('header', 'answerhdr', get_string('hintn', 'question'));
+        $repeated[] = $mform->createElement('header', 'hinthdr', get_string('hintn', 'question'));
         $repeated[] = $mform->createElement('editor', 'hint', get_string('hinttext', 'question'),
                 array('rows' => 5), $this->editoroptions);
         $repeatedoptions['hint']['type'] = PARAM_RAW;
index 8828030..36f08b5 100644 (file)
@@ -206,6 +206,8 @@ class qtype_match extends question_type {
             $fs->move_area_files_to_new_context($oldcontextid,
                     $newcontextid, 'qtype_match', 'subquestion', $subquestionid);
         }
+
+        $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid);
     }
 
     protected function delete_files($questionid, $contextid) {
@@ -220,11 +222,6 @@ class qtype_match extends question_type {
             $fs->delete_area_files($contextid, 'qtype_match', 'subquestion', $subquestionid);
         }
 
-        $fs->delete_area_files($contextid, 'qtype_multichoice',
-                'correctfeedback', $questionid);
-        $fs->delete_area_files($contextid, 'qtype_multichoice',
-                'partiallycorrectfeedback', $questionid);
-        $fs->delete_area_files($contextid, 'qtype_multichoice',
-                'incorrectfeedback', $questionid);
+        $this->delete_files_in_combined_feedback($questionid, $contextid);
     }
 }
index b4d20db..70724e0 100644 (file)
@@ -69,17 +69,4 @@ class backup_qtype_multichoice_plugin extends backup_qtype_plugin {
 
         return $plugin;
     }
-
-    /**
-     * Returns one array with filearea => mappingname elements for the qtype
-     *
-     * Used by {@link get_components_and_fileareas} to know about all the qtype
-     * files to be processed both in backup and restore.
-     */
-    public static function get_qtype_fileareas() {
-        return array(
-            'correctfeedback' => 'question_created',
-            'partiallycorrectfeedback' => 'question_created',
-            'incorrectfeedback' => 'question_created');
-    }
 }
index f7b46ad..240bc4c 100644 (file)
@@ -231,30 +231,14 @@ class qtype_multichoice extends question_type {
     }
 
     public function move_files($questionid, $oldcontextid, $newcontextid) {
-        $fs = get_file_storage();
-
         parent::move_files($questionid, $oldcontextid, $newcontextid);
         $this->move_files_in_answers($questionid, $oldcontextid, $newcontextid, true);
-
-        $fs->move_area_files_to_new_context($oldcontextid,
-                $newcontextid, 'qtype_multichoice', 'correctfeedback', $questionid);
-        $fs->move_area_files_to_new_context($oldcontextid,
-                $newcontextid, 'qtype_multichoice', 'partiallycorrectfeedback', $questionid);
-        $fs->move_area_files_to_new_context($oldcontextid,
-                $newcontextid, 'qtype_multichoice', 'incorrectfeedback', $questionid);
+        $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid);
     }
 
     protected function delete_files($questionid, $contextid) {
-        $fs = get_file_storage();
-
         parent::delete_files($questionid, $contextid);
         $this->delete_files_in_answers($questionid, $contextid, true);
-
-        $fs->delete_area_files($contextid,
-                'qtype_multichoice', 'correctfeedback', $questionid);
-        $fs->delete_area_files($contextid,
-                'qtype_multichoice', 'partiallycorrectfeedback', $questionid);
-        $fs->delete_area_files($contextid,
-                'qtype_multichoice', 'incorrectfeedback', $questionid);
+        $this->delete_files_in_combined_feedback($questionid, $contextid);
     }
 }
index f45bf40..edd9266 100644 (file)
@@ -454,7 +454,7 @@ class question_classified_response {
     }
 
     public static function no_response() {
-        return new question_classified_response(null, null, null);
+        return new question_classified_response(null, get_string('noresponse', 'question'), null);
     }
 }
 
index 7576669..d127c6e 100644 (file)
@@ -1107,6 +1107,28 @@ class question_type {
         }
     }
 
+    /**
+     * Move all the files belonging to this question's answers when the question
+     * is moved from one context to another.
+     * @param int $questionid the question being moved.
+     * @param int $oldcontextid the context it is moving from.
+     * @param int $newcontextid the context it is moving to.
+     * @param bool $answerstoo whether there is an 'answer' question area,
+     *      as well as an 'answerfeedback' one. Default false.
+     */
+    protected function move_files_in_combined_feedback($questionid, $oldcontextid,
+            $newcontextid) {
+        global $DB;
+        $fs = get_file_storage();
+
+        $fs->move_area_files_to_new_context($oldcontextid,
+                $newcontextid, 'question', 'correctfeedback', $questionid);
+        $fs->move_area_files_to_new_context($oldcontextid,
+                $newcontextid, 'question', 'partiallycorrectfeedback', $questionid);
+        $fs->move_area_files_to_new_context($oldcontextid,
+                $newcontextid, 'question', 'incorrectfeedback', $questionid);
+    }
+
     /**
      * Delete all the files belonging to this question.
      * @param int $questionid the question being deleted.
@@ -1139,6 +1161,25 @@ class question_type {
         }
     }
 
+    /**
+     * Delete all the files belonging to this question's answers.
+     * @param int $questionid the question being deleted.
+     * @param int $contextid the context the question is in.
+     * @param bool $answerstoo whether there is an 'answer' question area,
+     *      as well as an 'answerfeedback' one. Default false.
+     */
+    protected function delete_files_in_combined_feedback($questionid, $contextid) {
+        global $DB;
+        $fs = get_file_storage();
+
+        $fs->delete_area_files($contextid,
+                'question', 'correctfeedback', $questionid);
+        $fs->delete_area_files($contextid,
+                'question', 'partiallycorrectfeedback', $questionid);
+        $fs->delete_area_files($contextid,
+                'question', 'incorrectfeedback', $questionid);
+    }
+
     public function import_file($context, $component, $filearea, $itemid, $file) {
         $fs = get_file_storage();
         $record = new stdClass();
index 30ed13d..b458b4e 100644 (file)
@@ -79,6 +79,24 @@ h1.headerheading {margin:14px 11px 8px 11px;float:left;font-size:200%;}
 #page-enrol-instances .generalbox {border:none;}
 #page-enrol-instances .select.menujump {margin-left:0.5em;}
 
+/* environmenttable */
+.environmenttable .error {
+    background-color: red;
+}
+
+.environmenttable .warn {
+    background-color: yellow;
+}
+
+.environmenttable .ok {
+    background-color: lime;
+}
+
+/* adminsettings */
+#adminsettings .form-overridden {
+    background-color: yellow;
+}
+
 /* tables */
 .editcourse th,
 .editcourse td,
index 464638a..85c63b0 100644 (file)
@@ -161,23 +161,27 @@ function combo_send_uncached($content, $mimetype) {
     die;
 }
 
-function combo_not_found() {
+function combo_not_found($message = '') {
     header('HTTP/1.0 404 not found');
-    die('Combo resource not found, sorry.');
+    if ($message) {
+        echo $message;
+    } else {
+        echo 'Combo resource not found, sorry.';
+    }
+    die;
 }
 
 function combo_params() {
-    if (!empty($_SERVER['REQUEST_URI'])) {
-        $parts = explode('?', $_SERVER['REQUEST_URI']);
-        if (count($parts) != 2) {
-            return '';
-        }
+    // note: buggy or misconfigured IIS does return the query string in REQUEST_URL
+    if (isset($_SERVER['REQUEST_URI']) and strpos($_SERVER['REQUEST_URI'], '?') !== false) {
+        $parts = explode('?', $_SERVER['REQUEST_URI'], 2);
         return $parts[1];
 
-    } else if (!empty($_SERVER['QUERY_STRING'])) {
+    } else if (isset($_SERVER['QUERY_STRING'])) {
         return $_SERVER['QUERY_STRING'];
 
     } else {
-        return '';
+        // unsupported server, sorry!
+        combo_not_found('Unsupported server - query string can not be determined, try disabling YUI combo loading in admin settings.');
     }
 }