diff --git a/grade/grading/form/lib.php b/grade/grading/form/lib.php index bad66c8d5f5..8e1ebe904cb 100644 --- a/grade/grading/form/lib.php +++ b/grade/grading/form/lib.php @@ -48,7 +48,7 @@ abstract class gradingform_controller { protected $areaid; /** @var stdClass|false the definition structure */ - protected $definition; + protected $definition = false; /** @var array graderange array of valid grades for this area. Use set_grade_range and get_grade_range to access this */ private $graderange = null; @@ -110,7 +110,7 @@ abstract class gradingform_controller { * @return boolean */ public function is_form_defined() { - return !empty($this->definition); + return ($this->definition !== false); } /** @@ -181,7 +181,7 @@ abstract class gradingform_controller { * @return stdClass|false definition data or false if the form is not defined yet */ public function get_definition($force = false) { - if (is_null($this->definition)) { + if ($this->definition === false || $force) { $this->load_definition(); } return $this->definition; diff --git a/grade/grading/form/rubric/lib.php b/grade/grading/form/rubric/lib.php index bdcefc41059..f788b8bfdac 100644 --- a/grade/grading/form/rubric/lib.php +++ b/grade/grading/form/rubric/lib.php @@ -190,7 +190,7 @@ class gradingform_rubric_controller extends gradingform_controller { $this->definition = false; foreach ($rs as $record) { // pick the common definition data - if (empty($this->definition)) { + if ($this->definition === false) { $this->definition = new stdClass(); foreach (array('id', 'name', 'description', 'descriptionformat', 'status', 'copiedfromid', 'timecreated', 'usercreated', 'timemodified', 'usermodified', 'options') as $fieldname) { @@ -290,7 +290,7 @@ class gradingform_rubric_controller extends gradingform_controller { } public function get_formatted_description() { - if (!$this->definition) { + if ($this->definition === false) { return null; } $context = $this->get_context(); @@ -385,6 +385,8 @@ class gradingform_rubric_controller extends gradingform_controller { */ class gradingform_rubric_instance extends gradingform_instance { + protected $rubric; + /** * Deletes this (INCOMPLETE) instance from database. */ @@ -406,11 +408,11 @@ class gradingform_rubric_instance extends gradingform_instance { global $DB; $instanceid = parent::copy($raterid, $itemid); $currentgrade = $this->get_rubric_filling(); - foreach ($currentgrade as $criterionid => $levelid) { - $params = array('forminstanceid' => $instanceid, 'criterionid' => $criterionid, 'levelid' => $levelid); + foreach ($currentgrade['criteria'] as $criterionid => $record) { + $params = array('forminstanceid' => $instanceid, 'criterionid' => $criterionid, + 'levelid' => $record['levelid'], 'remark' => $record['remark'], 'remarkformat' => $record['remarkformat']); $DB->insert_record('gradingform_rubric_fillings', $params); } - // TODO remarks return $instanceid; } @@ -421,11 +423,12 @@ class gradingform_rubric_instance extends gradingform_instance { public function validate_grading_element($elementvalue) { // TODO: if there is nothing selected in rubric, we don't enter this function at all :( $criteria = $this->get_controller()->get_definition()->rubric_criteria; - if (!is_array($elementvalue) || sizeof($elementvalue) < sizeof($criteria)) { + if (!isset($elementvalue['criteria']) || !is_array($elementvalue['criteria']) || sizeof($elementvalue['criteria']) < sizeof($criteria)) { return false; } foreach ($criteria as $id => $criterion) { - if (!array_key_exists($id, $elementvalue) || !array_key_exists($elementvalue[$id], $criterion['levels'])) { + if (!isset($elementvalue['criteria'][$id]['levelid']) + || !array_key_exists($elementvalue['criteria'][$id]['levelid'], $criterion['levels'])) { return false; } } @@ -435,20 +438,19 @@ class gradingform_rubric_instance extends gradingform_instance { /** * Retrieves from DB and returns the data how this rubric was filled * + * @param boolean $force whether to force DB query even if the data is cached * @return array */ - public function get_rubric_filling() { - // TODO cache + public function get_rubric_filling($force = false) { global $DB; - $rs = $DB->get_records('gradingform_rubric_fillings', array('forminstanceid' => $this->get_id())); - $grading = array(); - foreach ($rs as $record) { - if ($record->levelid) { - $grading[$record->criterionid] = $record->levelid; + if ($this->rubric === null || $force) { + $records = $DB->get_records('gradingform_rubric_fillings', array('forminstanceid' => $this->get_id())); + $this->rubric = array('criteria' => array()); + foreach ($records as $record) { + $this->rubric['criteria'][$record->criterionid] = (array)$record; } - // TODO: remarks } - return $grading; + return $this->rubric; } /** @@ -460,21 +462,29 @@ class gradingform_rubric_instance extends gradingform_instance { global $DB; $currentgrade = $this->get_rubric_filling(); parent::update($data); // TODO ? +timemodified - foreach ($data as $criterionid => $levelid) { - $params = array('forminstanceid' => $this->get_id(), 'criterionid' => $criterionid); - if (!array_key_exists($criterionid, $currentgrade)) { - $DB->insert_record('gradingform_rubric_fillings', $params + array('levelid' => $levelid)); - } else if ($currentgrade[$criterionid] != $levelid) { - $DB->set_field('gradingform_rubric_fillings', 'levelid', $levelid, $params); + foreach ($data['criteria'] as $criterionid => $record) { + if (!array_key_exists($criterionid, $currentgrade['criteria'])) { + $newrecord = array('forminstanceid' => $this->get_id(), 'criterionid' => $criterionid, + 'levelid' => $record['levelid'], 'remark' => $record['remark'], 'remarkformat' => FORMAT_MOODLE); + $DB->insert_record('gradingform_rubric_fillings', $newrecord); + } else { + $newrecord = array('id' => $currentgrade['criteria'][$criterionid]['id']); + foreach (array('levelid', 'remark'/*, 'remarkformat' TODO */) as $key) { + if ($currentgrade['criteria'][$criterionid][$key] != $record[$key]) { + $newrecord[$key] = $record[$key]; + } + } + if (count($newrecord) > 1) { + $DB->update_record('gradingform_rubric_fillings', $newrecord); + } } } - foreach ($currentgrade as $criterionid => $levelid) { - if (!array_key_exists($criterionid, $data)) { - $params = array('forminstanceid' => $this->get_id(), 'criterionid' => $criterionid); - $DB->delete_records('gradingform_rubric_fillings', $params); + foreach ($currentgrade['criteria'] as $criterionid => $record) { + if (!array_key_exists($criterionid, $data['criteria'])) { + $DB->delete_records('gradingform_rubric_fillings', array('id' => $record['id'])); } } - // TODO: remarks + $this->get_rubric_filling(true); } /** @@ -508,8 +518,8 @@ class gradingform_rubric_instance extends gradingform_instance { $maxgrade = $graderange[sizeof($graderange) - 1]; $curscore = 0; - foreach ($grade as $id => $levelid) { - $curscore += $this->get_controller()->get_definition()->rubric_criteria[$id]['levels'][$levelid]['score']; + foreach ($grade['criteria'] as $id => $record) { + $curscore += $this->get_controller()->get_definition()->rubric_criteria[$id]['levels'][$record['levelid']]['score']; } return round(($curscore-$minscore)/($maxscore-$minscore)*($maxgrade-$mingrade), 0) + $mingrade; // TODO mapping } diff --git a/grade/grading/form/rubric/renderer.php b/grade/grading/form/rubric/renderer.php index 547e8091cd4..6b326ef1646 100644 --- a/grade/grading/form/rubric/renderer.php +++ b/grade/grading/form/rubric/renderer.php @@ -35,8 +35,8 @@ class gradingform_rubric_renderer { * @param int $mode @see gradingform_rubric_controller * @return string */ - public function criterion_template($mode, $elementname = '{NAME}', $criterion = null, $levels_str = '{LEVELS}') { - // TODO description format + public function criterion_template($mode, $elementname = '{NAME}', $criterion = null, $levels_str = '{LEVELS}', $value = null) { + // TODO description format, remark format if ($criterion === null || !is_array($criterion) || !array_key_exists('id', $criterion)) { $criterion = array('id' => '{CRITERION-id}', 'description' => '{CRITERION-description}', 'sortorder' => '{CRITERION-sortorder}', 'class' => '{CRITERION-class}'); } else { @@ -74,6 +74,21 @@ class gradingform_rubric_renderer { 'id' => '{NAME}-{CRITERION-id}-levels-addlevel', 'value' => $value, 'title' => $value)); //TODO '{NAME}-{CRITERION-id}-levels-addlevel $criterion_template .= html_writer::tag('div', $button, array('class' => 'addlevel')); } + if (isset($value['remark'])) { + $currentremark = $value['remark']; + } else { + $currentremark = ''; + } + if ($mode == gradingform_rubric_controller::DISPLAY_EVAL) { + $input = html_writer::tag('textarea', htmlspecialchars($currentremark), array('name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'cols' => '10', 'rows' => '5')); + $criterion_template .= html_writer::tag('div', $input, array('class' => 'remark')); + } + if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN) { + $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'value' => $currentremark)); + } + if ($mode == gradingform_rubric_controller::DISPLAY_REVIEW) { + $criterion_template .= html_writer::tag('div', $currentremark, array('class' => 'remark')); // TODO maybe some prefix here like 'Teacher remark:' + } $criterion_template .= html_writer::end_tag('div'); // .criterion $criterion_template = str_replace('{NAME}', $elementname, $criterion_template); @@ -83,7 +98,7 @@ class gradingform_rubric_renderer { public function level_template($mode, $elementname = '{NAME}', $criterionid = '{CRITERION-id}', $level = null) { // TODO definition format - if ($level === null || !is_array($level) || !array_key_exists('id', $level)) { + if (!isset($level['id'])) { $level = array('id' => '{LEVEL-id}', 'definition' => '{LEVEL-definition}', 'score' => '{LEVEL-score}', 'class' => '{LEVEL-class}', 'checked' => false); } else { foreach (array('score', 'definition', 'class', 'checked') as $key) { @@ -108,12 +123,12 @@ class gradingform_rubric_renderer { $score = $level['score']; } if ($mode == gradingform_rubric_controller::DISPLAY_EVAL) { - $input = html_writer::empty_tag('input', array('type' => 'radio', 'name' => '{NAME}[{CRITERION-id}]', 'value' => $level['id']) + + $input = html_writer::empty_tag('input', array('type' => 'radio', 'name' => '{NAME}[criteria][{CRITERION-id}][levelid]', 'value' => $level['id']) + ($level['checked'] ? array('checked' => 'checked') : array())); $level_template .= html_writer::tag('div', $input, array('class' => 'radio')); } if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN && $level['checked']) { - $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}]', 'value' => $level['id'])); + $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levelid]', 'value' => $level['id'])); } $score = html_writer::tag('span', $score, array('id' => '{NAME}-{CRITERION-id}-levels-{LEVEL-id}-score')); $level_template .= html_writer::tag('div', $definition, array('class' => 'definition', 'id' => '{NAME}-{CRITERION-id}-levels-{LEVEL-id}-definition')); @@ -176,17 +191,22 @@ class gradingform_rubric_renderer { $criterion['class'] = $this->get_css_class_suffix($cnt++, sizeof($criteria) -1); $levels_str = ''; $levelcnt = 0; + if (isset($values['criteria'][$id])) { + $criterionvalue = $values['criteria'][$id]; + } else { + $criterionvalue = null; + } foreach ($criterion['levels'] as $levelid => $level) { $level['score'] = (float)$level['score']; // otherwise the display will look like 1.00000 $level['class'] = $this->get_css_class_suffix($levelcnt++, sizeof($criterion['levels']) -1); - $level['checked'] = (is_array($values) && (array_key_exists($id, $values) && ((int)$values[$id] === $levelid))); + $level['checked'] = (isset($criterionvalue['levelid']) && ((int)$criterionvalue['levelid'] === $levelid)); if ($level['checked'] && ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_REVIEW)) { $level['class'] .= ' checked'; //in mode DISPLAY_EVAL the class 'checked' will be added by JS if it is enabled. If it is not enabled, the 'checked' class will only confuse } $levels_str .= $this->level_template($mode, $elementname, $id, $level); } - $criteria_str .= $this->criterion_template($mode, $elementname, $criterion, $levels_str); + $criteria_str .= $this->criterion_template($mode, $elementname, $criterion, $levels_str, $criterionvalue); } return $this->rubric_template($mode, $elementname, $criteria_str); } diff --git a/grade/grading/form/rubric/styles.css b/grade/grading/form/rubric/styles.css index 0168a207062..0802227a57f 100644 --- a/grade/grading/form/rubric/styles.css +++ b/grade/grading/form/rubric/styles.css @@ -20,6 +20,8 @@ [input type=submit] .addlevel [input type=submit] + .remark + textarea .addcriterion [input type=submit] @@ -41,11 +43,13 @@ .form_rubric .criterion .description, .form_rubric .criterion .levels, .form_rubric.editor .criterion .addlevel, +.form_rubric .criterion .remark, .form_rubric .criterion .levels .level {display: inline-block; vertical-align: top;overflow: hidden;} .form_rubric.editor .criterion .controls, .form_rubric .criterion .description, .form_rubric.editor .criterion .addlevel, +.form_rubric .criterion .remark, .form_rubric .criterion .levels .level {padding:3px;} /* Those divs should extend vertically and fill 100% of parent element height */