diff --git a/grade/grading/form/lib.php b/grade/grading/form/lib.php index 01de4a8f4bf..e6f2272a0a2 100644 --- a/grade/grading/form/lib.php +++ b/grade/grading/form/lib.php @@ -289,6 +289,18 @@ abstract class gradingform_controller { return true; } + /** + * Returns the renderer for the current plugin + * + * @param string $subtype optional subtype + * @param string $target one of rendering target constants + * @return renderer_base + */ + public function get_renderer($subtype = null, $target = null) { + global $PAGE; + return $PAGE->get_renderer('gradingform_'. $this->get_method_name(), $subtype, $target); + } + //////////////////////////////////////////////////////////////////////////// diff --git a/grade/grading/form/rubric/js/rubric.js b/grade/grading/form/rubric/js/rubric.js index c154d2563da..8c9f232cf98 100644 --- a/grade/grading/form/rubric/js/rubric.js +++ b/grade/grading/form/rubric/js/rubric.js @@ -6,6 +6,11 @@ M.gradingform_rubric = {}; M.gradingform_rubric.init = function(Y, options) { Y.on('click', M.gradingform_rubric.levelclick, '#rubric-'+options.name+' .level', null, Y, options.name); Y.all('#rubric-'+options.name+' .radio').setStyle('display', 'none') + Y.all('#rubric-'+options.name+' .level').each(function (node) { + if (node.one('input[type=radio][checked]')) { + node.addClass('checked'); + } + }); }; M.gradingform_rubric.levelclick = function(e, Y, name) { diff --git a/grade/grading/form/rubric/js/rubriceditor.js b/grade/grading/form/rubric/js/rubriceditor.js index 4ef9b15d44f..107348c9b86 100644 --- a/grade/grading/form/rubric/js/rubriceditor.js +++ b/grade/grading/form/rubric/js/rubriceditor.js @@ -8,13 +8,82 @@ M.gradingform_rubriceditor.init = function(Y, options) { 'criterion' : options.criteriontemplate, 'level' : options.leveltemplate } - M.gradingform_rubriceditor.addhandlers(Y, options.name); + M.gradingform_rubriceditor.disablealleditors(null, Y, options.name) + M.gradingform_rubriceditor.addhandlers(Y, options.name) }; // Adds handlers for clicking submit button. This function must be called each time JS adds new elements to html M.gradingform_rubriceditor.addhandlers = function(Y, name) { if (M.gradingform_rubriceditor.eventhandler) M.gradingform_rubriceditor.eventhandler.detach() - M.gradingform_rubriceditor.eventhandler = Y.on('click', M.gradingform_rubriceditor.buttonclick, '#rubriceditor-'+name+' input[type=submit]', null, Y, name); + M.gradingform_rubriceditor.eventhandler = Y.on('click', M.gradingform_rubriceditor.clickanywhere, 'body', null, Y, name); + M.gradingform_rubriceditor.eventhandler = Y.on('click', M.gradingform_rubriceditor.buttonclick, '#rubric-'+name+' input[type=submit]', null, Y, name); +} + +M.gradingform_rubriceditor.disablealleditors = function(e, Y, name) { + Y.all('#rubric-'+name+' .level').each( function(node) {M.gradingform_rubriceditor.editmode(node, false)} ); + Y.all('#rubric-'+name+' .description').each( function(node) {M.gradingform_rubriceditor.editmode(node, false)} ); +} + +M.gradingform_rubriceditor.clickanywhere = function(e, Y, name) { + var el = e.target + // if clicked on button - disablecurrenteditor, continue + if (el.get('tagName') == 'INPUT' && el.get('type') == 'submit') { + M.gradingform_rubriceditor.disablealleditors(null, Y, name) + return + } + // else if clicked on level and this level is not enabled - enable it + // or if clicked on description and this description is not enabled - enable it + while (el && !(el.hasClass('level') || el.hasClass('description'))) el = el.get('parentNode') + if (el) { + if (el.one('textarea').getStyle('display') == 'none') { + M.gradingform_rubriceditor.disablealleditors(null, Y, name) + M.gradingform_rubriceditor.editmode(el, true) + } + return + } + // else disablecurrenteditor + M.gradingform_rubriceditor.disablealleditors(null, Y, name) +} + +M.gradingform_rubriceditor.editmode = function(el, editmode) { + var ta = el.one('textarea') + if (!ta.get('parentNode').one('.plainvalue')) { + ta.get('parentNode').append('
') + } + var tb = el.one('input[type=text]') + if (tb && !tb.get('parentNode').one('.plainvalue')) { + tb.get('parentNode').append('
') + } + if (!editmode) { + var value = ta.get('value') + if (value.length) ta.get('parentNode').one('.plainvalue').removeClass('empty') + else { + value = (el.hasClass('level')) ? M.str.gradingform_rubric.levelempty : M.str.gradingform_rubric.criterionempty + ta.get('parentNode').one('.plainvalue').addClass('empty') + } + ta.get('parentNode').one('.plainvalue').set('innerHTML', value) + ta.get('parentNode').one('.plainvalue').setStyle('display', 'block') + ta.setStyle('display', 'none') + if (tb) { + tb.get('parentNode').one('.plainvalue').set('innerHTML', tb.get('value')) + tb.get('parentNode').one('.plainvalue').setStyle('display', 'inline-block') + tb.setStyle('display', 'none') + } + } else { + if (tb) { + tb.get('parentNode').one('.plainvalue').setStyle('display', 'none') + tb.setStyle('display', 'inline-block') + } + var width = ta.get('parentNode').getComputedStyle('width') // TODO min width + var height = ta.get('parentNode').getComputedStyle('height') // TODO min height + if (el.hasClass('level')) { + height = el.getComputedStyle('height') - el.one('.score').getComputedStyle('height') + } else if (el.hasClass('description')) { + height = el.get('parentNode').getComputedStyle('height') + } + ta.get('parentNode').one('.plainvalue').setStyle('display', 'none') + ta.setStyle('display', 'block').setStyle('width', width).setStyle('height', height) + } } // handler for clicking on submit buttons within rubriceditor element. Adds/deletes/rearranges criteria and/or levels on client side @@ -25,9 +94,9 @@ M.gradingform_rubriceditor.buttonclick = function(e, Y, name, confirmed) { if (chunks[0] != name) return; var elements_str if (chunks.length>3 || action == 'addlevel') { - elements_str = '#rubriceditor-'+name+' #'+name+'-'+chunks[1]+'-levels .level' + elements_str = '#rubric-'+name+' #'+name+'-'+chunks[1]+'-levels .level' } else { - elements_str = '#rubriceditor-'+name+' .criterion' + elements_str = '#rubric-'+name+' .criterion' } // prepare the id of the next inserted level or criterion var newid = 1 @@ -48,7 +117,7 @@ M.gradingform_rubriceditor.buttonclick = function(e, Y, name, confirmed) { replace(/\{CRITERION-id\}/g, 'NEWID'+newid).replace(/\{.+?\}/g, '') Y.one('#'+name+'-criteria').append(newcriterion) M.gradingform_rubriceditor.addhandlers(Y, name); - } else if (chunks.length == 3 && action == 'addlevel') { + } else if (chunks.length == 4 && action == 'addlevel') { // ADD NEW LEVEL var newlevel = M.gradingform_rubriceditor.templates[name]['level']. replace(/\{CRITERION-id\}/g, chunks[1]).replace(/\{LEVEL-id\}/g, 'NEWID'+newid).replace(/\{.+?\}/g, '') diff --git a/grade/grading/form/rubric/lang/en/gradingform_rubric.php b/grade/grading/form/rubric/lang/en/gradingform_rubric.php index 3c4ee9ef1ad..a634486d330 100644 --- a/grade/grading/form/rubric/lang/en/gradingform_rubric.php +++ b/grade/grading/form/rubric/lang/en/gradingform_rubric.php @@ -33,9 +33,12 @@ $string['description'] = 'Description'; $string['name'] = 'Name'; $string['addcriterion'] = 'Add criterion'; -$string['criterionmoveup'] = 'Up'; -$string['criteriondelete'] = 'Delete'; -$string['criterionmovedown'] = 'Down'; +$string['criterionmoveup'] = 'Move up'; +$string['criteriondelete'] = 'Delete criterion'; +$string['criterionmovedown'] = 'Move down'; $string['criterionaddlevel'] = 'Add level'; $string['scorepostfix'] = ' pts'; -$string['leveldelete'] = 'Del'; +$string['leveldelete'] = 'Delete level'; + +$string['criterionempty'] = 'Click to edit criterion'; +$string['levelempty'] = 'Click to edit level'; diff --git a/grade/grading/form/rubric/lib.php b/grade/grading/form/rubric/lib.php index 74c56a79fbf..0573eac2048 100644 --- a/grade/grading/form/rubric/lib.php +++ b/grade/grading/form/rubric/lib.php @@ -32,6 +32,13 @@ require_once($CFG->dirroot.'/grade/grading/form/lib.php'); * This controller encapsulates the rubric grading logic */ class gradingform_rubric_controller extends gradingform_controller { + // Modes of displaying the rubric (used in gradingform_rubric_renderer) + const DISPLAY_EDIT_FULL = 1; // For editing (moderator or teacher creates a rubric) + const DISPLAY_EDIT_FROZEN = 2; // Preview the rubric design with hidden fields + const DISPLAY_PREVIEW = 3; // Preview the rubric design + const DISPLAY_EVAL = 4; // For evaluation, enabled (teacher grades a student) + const DISPLAY_EVAL_FROZEN = 5; // For evaluation, with hidden fields + const DISPLAY_REVIEW = 6; // Dispaly filled rubric (i.e. students see their grades) /** * Extends the module settings navigation with the rubric grading settings @@ -306,6 +313,32 @@ class gradingform_rubric_controller extends gradingform_controller { * Returns html for form element */ public function to_html($gradingformelement) { + global $PAGE, $USER; + if (!$gradingformelement->_flagFrozen) { + $module = array('name'=>'gradingform_rubric', 'fullpath'=>'/grade/grading/form/rubric/js/rubric.js'); + $PAGE->requires->js_init_call('M.gradingform_rubric.init', array(array('name' => $gradingformelement->getName(), 'criteriontemplate' =>'', 'leveltemplate' => '')), true, $module); + $mode = self::DISPLAY_EVAL; + } else { + if ($this->_persistantFreeze) { + $mode = gradingform_rubric_controller::DISPLAY_EVAL_FROZEN; + } else { + $mode = gradingform_rubric_controller::DISPLAY_REVIEW; + } + } + $criteria = $this->definition->rubric_criteria; + $submissionid = $gradingformelement->get_grading_attribute('submissionid'); + $raterid = $USER->id; // TODO - this is very strange! + $value = $gradingformelement->getValue(); + if ($value === null) { + $value = $this->get_grading($raterid, $submissionid); // TODO maybe implement in form->set_data() ? + } + return $this->get_renderer()->display_rubric($criteria, $mode, $gradingformelement->getName(), $value); + } + + /** + * Returns html for form element + */ + public function to_html_old($gradingformelement) { global $PAGE, $USER; //TODO move to renderer diff --git a/grade/grading/form/rubric/renderer.php b/grade/grading/form/rubric/renderer.php index 039823df1e4..349eb3c85b1 100644 --- a/grade/grading/form/rubric/renderer.php +++ b/grade/grading/form/rubric/renderer.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); /** * Grading method plugin renderer */ -class gradingform_rubric_renderer extends gradingform_renderer { +class gradingform_rubric_renderer { /** * Renders grading widget @@ -43,4 +43,188 @@ class gradingform_rubric_renderer extends gradingform_renderer { return $this->output->container($button.$span, 'gradingform_rubric-widget-wrapper', 1); } + + /** + * + * @param int $mode @see gradingform_rubric_controller + * @return string + */ + public function criterion_template($mode, $elementname = '{NAME}', $criterion = null, $levels_str = '{LEVELS}') { + // TODO description 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 { + foreach (array('sortorder', 'description', 'class') as $key) { + // set missing array elements to empty strings to avoid warnings + if (!array_key_exists($key, $criterion)) { + $criterion[$key] = ''; + } + } + } + $criterion_template = html_writer::start_tag('div', array('class' => 'clearfix criterion'. $criterion['class'], 'id' => '{NAME}-{CRITERION-id}')); + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) { + $criterion_template .= html_writer::start_tag('div', array('class' => 'controls')); + foreach (array('moveup', 'delete', 'movedown') as $key) { + $value = get_string('criterion'.$key, 'gradingform_rubric'); + $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[{CRITERION-id}]['.$key.']', + 'id' => '{NAME}-{CRITERION-id}-'.$key, 'value' => $value, 'title' => $value)); + $criterion_template .= html_writer::tag('div', $button, array('class' => $key)); + } + $criterion_template .= html_writer::end_tag('div'); // .controls + $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder'])); + $description = html_writer::tag('textarea', htmlspecialchars($criterion['description']), array('name' => '{NAME}[{CRITERION-id}][description]', 'cols' => '10', 'rows' => '5')); + } else { + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) { + $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder'])); + $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][description]', 'value' => $criterion['description'])); + } + $description = $criterion['description']; + } + $criterion_template .= html_writer::tag('div', $description, array('class' => 'description', 'id' => '{NAME}-{CRITERION-id}-description')); + $criterion_template .= html_writer::tag('div', $levels_str, array('class' => 'clearfix levels', 'id' => '{NAME}-{CRITERION-id}-levels')); + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) { + $value = get_string('criterionaddlevel', 'gradingform_rubric'); + $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[{CRITERION-id}][levels][addlevel]', + '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')); + } + $criterion_template .= html_writer::end_tag('div'); // .criterion + + $criterion_template = str_replace('{NAME}', $elementname, $criterion_template); + $criterion_template = str_replace('{CRITERION-id}', $criterion['id'], $criterion_template); + return $criterion_template; + } + + 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)) { + $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) { + // set missing array elements to empty strings to avoid warnings + if (!array_key_exists($key, $level)) { + $level[$key] = ''; + } + } + } + + // Template for one level within one criterion + $level_template = html_writer::start_tag('div', array('id' => '{NAME}-{CRITERION-id}-levels-{LEVEL-id}', 'class' => 'clearfix level'. $level['class'])); + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) { + $definition = html_writer::tag('textarea', htmlspecialchars($level['definition']), array('name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][definition]', 'cols' => '10', 'rows' => '4')); + $score = html_writer::empty_tag('input', array('type' => 'text', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][score]', 'size' => '4', 'value' => $level['score'])); + } else { + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) { + $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][definition]', 'value' => $level['definition'])); + $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][score]', 'value' => $level['score'])); + } + $definition = $level['definition']; + $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']) + + ($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'])); + } + $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')); + $level_template .= html_writer::tag('div', $score. get_string('scorepostfix', 'gradingform_rubric'), array('class' => 'score')); + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) { + $value = get_string('leveldelete', 'gradingform_rubric'); + $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][delete]', 'id' => '{NAME}-{CRITERION-id}-levels-{LEVEL-id}-delete', 'value' => $value, 'title' => $value)); + $level_template .= html_writer::tag('div', $button, array('class' => 'delete')); + } + $level_template .= html_writer::end_tag('div'); // .level + + $level_template = str_replace('{NAME}', $elementname, $level_template); + $level_template = str_replace('{CRITERION-id}', $criterionid, $level_template); + $level_template = str_replace('{LEVEL-id}', $level['id'], $level_template); + return $level_template; + } + + protected function rubric_template($mode, $elementname = '{NAME}', $criteria_str = '{CRITERIA}') { + $classsuffix = ''; // CSS suffix for class of the main div. Depends on the mode + switch ($mode) { + case gradingform_rubric_controller::DISPLAY_EDIT_FULL: + $classsuffix = ' editor editable'; break; + case gradingform_rubric_controller::DISPLAY_EDIT_FROZEN: + $classsuffix = ' editor frozen'; break; + case gradingform_rubric_controller::DISPLAY_PREVIEW: + $classsuffix = ' editor preview'; break; + case gradingform_rubric_controller::DISPLAY_EVAL: + $classsuffix = ' evaluate editable'; break; + case gradingform_rubric_controller::DISPLAY_EVAL_FROZEN: + $classsuffix = ' evaluate frozen'; break; + case gradingform_rubric_controller::DISPLAY_REVIEW: + $classsuffix = ' review'; break; + } + + $rubric_template = html_writer::start_tag('div', array('id' => 'rubric-{NAME}', 'class' => 'clearfix form_rubric'.$classsuffix)); + $rubric_template .= html_writer::tag('div', $criteria_str, array('class' => 'criteria', 'id' => '{NAME}-criteria')); + if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) { + $value = get_string('addcriterion', 'gradingform_rubric'); + $input = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[addcriterion]', 'id' => '{NAME}-addcriterion', 'value' => $value, 'title' => $value)); + $rubric_template .= html_writer::tag('div', $input, array('class' => 'addcriterion')); + } + $rubric_template .= html_writer::end_tag('div'); + + return str_replace('{NAME}', $elementname, $rubric_template); + } + + /** + * Returns html code for displaying the rubric in the specified mode + * + * @param array $criteria + * @param int $mode + * @param string $elementname + * @param array $values + * @return string + */ + public function display_rubric($criteria, $mode, $elementname = null, $values = null) { + $criteria_str = ''; + $cnt = 0; + foreach ($criteria as $id => $criterion) { + $criterion['class'] = $this->get_css_class_suffix($cnt++, sizeof($criteria) -1); + $levels_str = ''; + $levelcnt = 0; + 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))); + 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); + } + return $this->rubric_template($mode, $elementname, $criteria_str); + } + + /** + * Help function to return CSS class names for element (first/last/even/odd) + * + * @param $cnt + * @param $maxcnt + * @return string + */ + private function get_css_class_suffix($cnt, $maxcnt) { + $class = ''; + if ($cnt == 0) { + $class .= ' first'; + } + if ($cnt == $maxcnt) { + $class .= ' last'; + } + if ($cnt%2) { + $class .= ' odd'; + } else { + $class .= ' even'; + } + return $class; + } } diff --git a/grade/grading/form/rubric/rubriceditor.php b/grade/grading/form/rubric/rubriceditor.php index 75a8db93f0f..1b403660976 100644 --- a/grade/grading/form/rubric/rubriceditor.php +++ b/grade/grading/form/rubric/rubriceditor.php @@ -49,116 +49,28 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input { function toHtml() { global $PAGE; $html = $this->_getTabs(); - - // Template for the whole rubric editor - $classsuffix = $this->_flagFrozen ? 'frozen' : 'editable'; - $rubric_template = html_writer::start_tag('div', array('id' => 'rubriceditor-{NAME}', 'class' => 'clearfix form_rubric editor '.$classsuffix)); - $rubric_template .= html_writer::tag('div', '{CRITERIA}', array('class' => 'criteria', 'id' => '{NAME}-criteria')); - if (!$this->_flagFrozen) { - $rubric_template .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[addcriterion]', 'id' => '{NAME}-addcriterion', 'value' => get_string('addcriterion', 'gradingform_rubric'))); - } - $rubric_template .= html_writer::end_tag('div'); - - // Template for one criterion - $criterion_template = html_writer::start_tag('div', array('class' => 'clearfix criterion{CRITERION-class}', 'id' => '{NAME}-{CRITERION-id}')); - if (!$this->_flagFrozen) { - $criterion_template .= html_writer::start_tag('div', array('class' => 'controls')); - foreach (array('moveup', 'delete', 'movedown') as $key) { - $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[{CRITERION-id}]['.$key.']', - 'id' => '{NAME}-{CRITERION-id}-'.$key, 'value' => get_string('criterion'.$key, 'gradingform_rubric'))); - $criterion_template .= html_writer::tag('div', $button, array('class' => $key)); - } - $criterion_template .= html_writer::end_tag('div'); // .controls - $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][sortorder]', 'value' => '{CRITERION-sortorder}')); - $description = html_writer::tag('textarea', '{CRITERION-description}', array('name' => '{NAME}[{CRITERION-id}][description]', 'cols' => '10', 'rows' => '5')); - } else { - if ($this->_persistantFreeze) { - $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][sortorder]', 'value' => '{CRITERION-sortorder}')); - $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][description]', 'value' => '{CRITERION-description}')); - } - $description = '{CRITERION-description}'; - } - $criterion_template .= html_writer::tag('div', $description, array('class' => 'description', 'id' => '{NAME}-{CRITERION-id}-description')); - $criterion_template .= html_writer::tag('div', '{LEVELS}', array('class' => 'levels', 'id' => '{NAME}-{CRITERION-id}-levels')); - if (!$this->_flagFrozen) { - $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[{CRITERION-id}][levels][addlevel]', - 'id' => '{NAME}-{CRITERION-id}-addlevel', 'value' => get_string('criterionaddlevel', 'gradingform_rubric'))); //TODO '{NAME}-{CRITERION-id}-levels-addlevel - $criterion_template .= html_writer::tag('div', $button, array('class' => 'addlevel')); - } - $criterion_template .= html_writer::end_tag('div'); // .criterion - - // Template for one level within one criterion - $level_template = html_writer::start_tag('div', array('id' => '{NAME}-{CRITERION-id}-levels-{LEVEL-id}', 'class' => 'level{LEVEL-class}')); - if (!$this->_flagFrozen) { - $definition = html_writer::tag('textarea', '{LEVEL-definition}', array('name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][definition]', 'cols' => '10', 'rows' => '4')); - $score = html_writer::empty_tag('input', array('type' => 'text', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][score]', 'size' => '4', 'value' => '{LEVEL-score}')); - } else { - if ($this->_persistantFreeze) { - $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][definition]', 'value' => '{LEVEL-definition}')); - $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][score]', 'value' => '{LEVEL-score}')); - } - $definition = '{LEVEL-definition}'; - $score = '{LEVEL-score}'; - } - $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')); - $level_template .= html_writer::tag('div', $score. get_string('scorepostfix', 'gradingform_rubric'), array('class' => 'score')); - if (!$this->_flagFrozen) { - $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[{CRITERION-id}][levels][{LEVEL-id}][delete]', 'id' => '{NAME}-{CRITERION-id}-levels-{LEVEL-id}-delete', 'value' => get_string('leveldelete', 'gradingform_rubric'))); - $level_template .= html_writer::tag('div', $button, array('class' => 'delete')); - } - $level_template .= html_writer::end_tag('div'); // .level - - $criterion_template = str_replace('{NAME}', $this->getName(), $criterion_template); - $level_template = str_replace('{NAME}', $this->getName(), $level_template); - $rubric_template = str_replace('{NAME}', $this->getName(), $rubric_template); - + $renderer = $PAGE->get_renderer('gradingform_rubric'); if (!$this->_flagFrozen) { + $mode = gradingform_rubric_controller::DISPLAY_EDIT_FULL; $module = array('name'=>'gradingform_rubriceditor', 'fullpath'=>'/grade/grading/form/rubric/js/rubriceditor.js', - 'strings' => array(array('confirmdeletecriterion', 'gradingform_rubric'), array('confirmdeletelevel', 'gradingform_rubric'))); - $PAGE->requires->js_init_call('M.gradingform_rubriceditor.init', array(array('name' => $this->getName(), 'criteriontemplate' => $criterion_template, 'leveltemplate' => $level_template)), true, $module); - } - $rubric_html = $rubric_template; - $criteria = $this->prepare_non_js_data(); - $cnt = 0; - foreach ($criteria as $id => $criterion) { - $criterion_html = $criterion_template; - $levelcnt = 0; - foreach ($criterion['levels'] as $levelid => $level) { - $cell_html = $level_template; - $cell_html = str_replace('{LEVEL-id}', $levelid, $cell_html); - $cell_html = str_replace('{LEVEL-definition}', htmlspecialchars($level['definition']), $cell_html); - $cell_html = str_replace('{LEVEL-score}', htmlspecialchars($level['score']), $cell_html); - $cell_html = str_replace('{LEVEL-class}', $this->get_css_class_suffix($levelcnt++, sizeof($criterion['levels']) -1), $cell_html); - $criterion_html = str_replace('{LEVELS}', $cell_html.'{LEVELS}', $criterion_html); - } - $criterion_html = str_replace('{LEVELS}', '', $criterion_html); - $criterion_html = str_replace('{CRITERION-id}', $id, $criterion_html); - $criterion_html = str_replace('{CRITERION-description}', htmlspecialchars($criterion['description']), $criterion_html); - $criterion_html = str_replace('{CRITERION-sortorder}', htmlspecialchars($criterion['sortorder']), $criterion_html); - $criterion_html = str_replace('{CRITERION-class}', $this->get_css_class_suffix($cnt++, sizeof($criteria) -1), $criterion_html); - $rubric_html = str_replace('{CRITERIA}', $criterion_html.'{CRITERIA}', $rubric_html); - } - $rubric_html = str_replace('{CRITERIA}', '', $rubric_html); - $html .= $rubric_html; - - return $html; - } - - function get_css_class_suffix($cnt, $maxcnt) { - $class = ''; - if ($cnt == 0) { - $class .= ' first'; - } - if ($cnt == $maxcnt) { - $class .= ' last'; - } - if ($cnt%2) { - $class .= ' odd'; + 'strings' => array(array('confirmdeletecriterion', 'gradingform_rubric'), array('confirmdeletelevel', 'gradingform_rubric'), + array('criterionempty', 'gradingform_rubric'), array('levelempty', 'gradingform_rubric') + )); + $PAGE->requires->js_init_call('M.gradingform_rubriceditor.init', array( + array('name' => $this->getName(), + 'criteriontemplate' => $renderer->criterion_template($mode, $this->getName()), + 'leveltemplate' => $renderer->level_template($mode, $this->getName()) + )), + true, $module); } else { - $class .= ' even'; + if ($this->_persistantFreeze) { + $mode = gradingform_rubric_controller::DISPLAY_EDIT_FROZEN; + } else { + $mode = gradingform_rubric_controller::DISPLAY_PREVIEW; + } } - return $class; + $html .= $renderer->display_rubric($this->prepare_non_js_data(), $mode, $this->getName()); + return $html; } function prepare_non_js_data() { diff --git a/grade/grading/form/rubric/styles.css b/grade/grading/form/rubric/styles.css index d0822ad01e8..eed125d56bd 100644 --- a/grade/grading/form/rubric/styles.css +++ b/grade/grading/form/rubric/styles.css @@ -9,28 +9,30 @@ .movedown [input type=submit] .description - .levels - .level[.first][.last].odd/even - .definition - [textarea] - .score - span - [input type=text] - .delete - [input type=submit] - .addlevel - [input type=submit] + .levels + .level[.first][.last].odd/even + .definition + [textarea] + .score + span + [input type=text] + .delete + [input type=submit] + .addlevel + [input type=submit] .addcriterion [input type=submit] .form_rubric.evaluate .criterion[.first][.last].odd/even .description - .levels - .level[.first][.last].odd/even - .definition - .score - span + .levels + .level[.first][.last].odd/even + .radio + input + .definition + .score + span */ @@ -39,23 +41,30 @@ .form_rubric .criterion .description, .form_rubric .criterion .levels, .form_rubric.editor .criterion .addlevel, -.form_rubric .criterion .levels .level {display: inline-block; vertical-align: top;} +.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 .levels .level {padding:3px} +.form_rubric .criterion .levels .level {padding:3px;} -.form_rubric .criterion {border:1px solid #DDD;} +/* Those divs should extend vertically and fill 100% of parent element height */ +.form_rubric .criterion .levels .level, +.form_rubric .criterion .description, +.form_rubric .criterion .levels {padding-bottom: 32767px;margin-bottom: -32767px;} + +.form_rubric .criterion {border:1px solid #DDD;overflow: hidden;} .form_rubric .criterion.even {background:#F0F0F0;} -.form_rubric .criterion .description {min-width:100px;font-weight:bold;} +.form_rubric .criterion .description {width:150px;font-weight:bold;} -.form_rubric .criterion .levels .level {border-left:1px solid #DDD;min-width:80px;} +.form_rubric .criterion .levels .level {border-left:1px solid #DDD;min-width:80px;max-width:150px;position:relative;} .form_rubric .criterion .levels .level.last {border-right:1px solid #DDD;} -.form_rubric .criterion .levels .level .score {display:inline-block;} -.form_rubric.editor .criterion .levels .level .delete {display:inline-block; padding-left: 5px;} +.form_rubric .plainvalue.empty {font-style: italic; color: #AAA;} + +.form_rubric.editor .criterion .levels .level .delete {position:absolute;right:0px;bottom:32767px;} +.form_rubric .criterion .levels .level .score {display:block;} /* Make invisible the buttons 'Move up' for the first criterion and 'Move down' for the last, because those buttons will make no change */ .form_rubric.editor .criterion.first .controls .moveup input, @@ -64,3 +73,17 @@ /* evaluation */ .form_rubric.evaluate .criterion .levels .level.checked {background:#d0ffd0;} .form_rubric.evaluate .criterion .levels .level:hover {background:#30ff30;} + +/* replace buttons with images */ +.form_rubric.editor .delete input, +.form_rubric.editor .moveup input, +.form_rubric.editor .movedown input{text-indent: -1000em;cursor:pointer;border:none;} +.form_rubric.editor .criterion .controls .delete input {width:20px;height:16px;background: transparent url([[pix:i/cross_red_big]]) no-repeat center top;} +.form_rubric.editor .levels .level .delete input {width:20px;height:16px;background: transparent url([[pix:i/cross_red_small]]) no-repeat center top;} +.form_rubric.editor .moveup input {width:20px;height:15px;background: transparent url([[pix:t/up]]) no-repeat center top;margin-top:4px;} +.form_rubric.editor .movedown input {width:20px;height:15px;background: transparent url([[pix:t/down]]) no-repeat center top;margin-top:4px;} + +.form_rubric.editor .addcriterion input, +.form_rubric.editor .addlevel input {background: transparent url([[pix:t/addgreen]]) no-repeat;display:block;color:#555555;font-weight:bold;text-decoration:none;} +.form_rubric.editor .addcriterion input {background-position: 5px 8px;height:30px;line-height:29px;margin-bottom:14px;padding-left:20px;padding-right:10px;} +.form_rubric.editor .addlevel input {background-position: 5px 6px;height:25px;line-height:24px;margin-bottom:10px;padding-left:18px;padding-right:8px;}