Merge pull request #17 from marinaglancy/wip-rubricrenderer-rubric
authorDavid Mudrak <david.mudrak@gmail.com>
Thu, 27 Oct 2011 06:47:29 +0000 (23:47 -0700)
committerDavid Mudrak <david.mudrak@gmail.com>
Thu, 27 Oct 2011 06:47:29 +0000 (23:47 -0700)
Better rendering of the rubrics, rubric display options

grade/grading/form/rubric/edit_form.php
grade/grading/form/rubric/js/rubriceditor.js
grade/grading/form/rubric/lang/en/gradingform_rubric.php
grade/grading/form/rubric/lib.php
grade/grading/form/rubric/renderer.php
grade/grading/form/rubric/rubriceditor.php
grade/grading/form/rubric/styles.css

index ba234a1..1341dc4 100644 (file)
@@ -57,9 +57,9 @@ class gradingform_rubric_editrubric extends moodleform {
         $form->setType('description_editor', PARAM_RAW);
 
         // rubric editor
-        $element = $form->addElement('rubriceditor', 'rubric_criteria', 'Rubric 1'); // todo label
-        $form->setType('rubric_criteria', PARAM_RAW);
-        $form->addRule('rubric_criteria', '', 'rubriceditorcompleted'); //TODO how to add this rule automatically?????
+        $element = $form->addElement('rubriceditor', 'rubric', get_string('rubric', 'gradingform_rubric'));
+        $form->setType('rubric', PARAM_RAW);
+        $form->addRule('rubric', '', 'rubriceditorcompleted'); //TODO how to add this rule automatically?????
         if (array_key_exists('freezerubric', $this->_customdata) && $this->_customdata['freezerubric']) {
             $element->freeze();
         }
index 64da044..4d1fc76 100644 (file)
@@ -92,10 +92,10 @@ M.gradingform_rubriceditor.buttonclick = function(e, Y, name, confirmed) {
     if (e.target.get('type') != 'submit') return;
     var chunks = e.target.get('id').split('-'),
         action = chunks[chunks.length-1]
-    if (chunks[0] != name) return;
+    if (chunks[0] != name || chunks[1] != 'criteria') return;
     var elements_str
-    if (chunks.length>3 || action == 'addlevel') {
-        elements_str = '#rubric-'+name+' #'+name+'-'+chunks[1]+'-levels .level'
+    if (chunks.length>4 || action == 'addlevel') {
+        elements_str = '#rubric-'+name+' #'+name+'-criteria-'+chunks[2]+'-levels .level'
     } else {
         elements_str = '#rubric-'+name+' .criterion'
     }
@@ -112,38 +112,44 @@ M.gradingform_rubriceditor.buttonclick = function(e, Y, name, confirmed) {
         'callbackargs' : [e, Y, name, true],
         'callback' : M.gradingform_rubriceditor.buttonclick
     };
-    if (chunks.length == 2 && action == 'addcriterion') {
+    if (chunks.length == 3 && action == 'addcriterion') {
         // ADD NEW CRITERION
         var newcriterion = M.gradingform_rubriceditor.templates[name]['criterion'].
             replace(/\{CRITERION-id\}/g, 'NEWID'+newid).replace(/\{.+?\}/g, '')
-        Y.one('#'+name+'-criteria').append(newcriterion)
+        var parentel = Y.one('#'+name+'-criteria')
+        if (parentel.one('>tbody')) parentel = parentel.one('>tbody')
+        parentel.append(newcriterion)
         M.gradingform_rubriceditor.addhandlers(Y, name);
-    } else if (chunks.length == 4 && action == 'addlevel') {
+    } else if (chunks.length == 5 && 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, '')
-        Y.one('#'+name+'-'+chunks[1]+'-levels').append(newlevel)
+            replace(/\{CRITERION-id\}/g, chunks[2]).replace(/\{LEVEL-id\}/g, 'NEWID'+newid).replace(/\{.+?\}/g, '')
+        Y.one('#'+name+'-criteria-'+chunks[2]+'-levels').append(newlevel)
+        var levels = Y.all('#'+name+'-criteria-'+chunks[2]+'-levels .level')
+        if (levels.size()) levels.set('width', Math.round(100/levels.size())+'%')
         M.gradingform_rubriceditor.addhandlers(Y, name);
-    } else if (chunks.length == 3 && action == 'moveup') {
+    } else if (chunks.length == 4 && action == 'moveup') {
         // MOVE CRITERION UP
-        el = Y.one('#'+name+'-'+chunks[1])
+        el = Y.one('#'+name+'-criteria-'+chunks[2])
         if (el.previous()) el.get('parentNode').insertBefore(el, el.previous())
-    } else if (chunks.length == 3 && action == 'movedown') {
+    } else if (chunks.length == 4 && action == 'movedown') {
         // MOVE CRITERION DOWN
-        el = Y.one('#'+name+'-'+chunks[1])
+        el = Y.one('#'+name+'-criteria-'+chunks[2])
         if (el.next()) el.get('parentNode').insertBefore(el.next(), el)
-    } else if (chunks.length == 3 && action == 'delete') {
+    } else if (chunks.length == 4 && action == 'delete') {
         // DELETE CRITERION
         if (confirmed) {
-            Y.one('#'+name+'-'+chunks[1]).remove()
+            Y.one('#'+name+'-criteria-'+chunks[2]).remove()
         } else {
             dialog_options['message'] = M.str.gradingform_rubric.confirmdeletecriterion
             M.util.show_confirm_dialog(e, dialog_options);
         }
-    } else if (chunks.length == 5 && action == 'delete') {
+    } else if (chunks.length == 6 && action == 'delete') {
         // DELETE LEVEL
         if (confirmed) {
-            Y.one('#'+name+'-'+chunks[1]+'-'+chunks[2]+'-'+chunks[3]).remove()
+            Y.one('#'+name+'-criteria-'+chunks[2]+'-'+chunks[3]+'-'+chunks[4]).remove()
+            levels = Y.all('#'+name+'-criteria-'+chunks[2]+'-levels .level')
+            if (levels.size()) levels.set('width', Math.round(100/levels.size())+'%')
         } else {
             dialog_options['message'] = M.str.gradingform_rubric.confirmdeletelevel
             M.util.show_confirm_dialog(e, dialog_options);
index a634486..21b2bbb 100644 (file)
@@ -37,8 +37,21 @@ $string['criterionmoveup'] = 'Move up';
 $string['criteriondelete'] = 'Delete criterion';
 $string['criterionmovedown'] = 'Move down';
 $string['criterionaddlevel'] = 'Add level';
-$string['scorepostfix'] = ' pts';
+$string['scorepostfix'] = ' points';
 $string['leveldelete'] = 'Delete level';
 
 $string['criterionempty'] = 'Click to edit criterion';
 $string['levelempty'] = 'Click to edit level';
+
+$string['rubric'] = 'Rubric';
+$string['rubricoptions'] = 'Rubric options';
+
+$string['sortlevelsasc'] = 'Sort order for levels:';
+$string['sortlevelsasc1'] = 'Ascending by number of points';
+$string['sortlevelsasc0'] = 'Descending by number of points';
+$string['showdescriptionteacher'] = 'Display rubric description during evaluation';
+$string['showdescriptionstudent'] = 'Display rubric description to those being graded';
+$string['showscoreteacher'] = 'Display points for each level during evaluation';
+$string['showscorestudent'] = 'Display points for each level to those being graded';
+$string['enableremarks'] = 'Allow grader to add text remarks for each criteria';
+$string['showremarksstudent'] = 'Show remarks to those being graded';
index 0be5fa6..63e08ac 100644 (file)
@@ -38,7 +38,8 @@ class gradingform_rubric_controller extends gradingform_controller {
     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)
+    const DISPLAY_REVIEW        = 6; // Teacher reviews filled rubric
+    const DISPLAY_VIEW          = 7; // Dispaly filled rubric (i.e. students see their grades)
 
     /**
      * Extends the module settings navigation with the rubric grading settings
@@ -73,8 +74,12 @@ class gradingform_rubric_controller extends gradingform_controller {
             parent::update_definition((object)array('descriptionformat' => FORMAT_MOODLE), $usermodified);
             parent::load_definition();
         }
-        $options = self::description_form_field_options($this->get_context());
-        $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $options, $this->get_context(),
+        if (!isset($newdefinition->rubric['options'])) {
+            $newdefinition->rubric['options'] = self::get_default_options();
+        }
+        $newdefinition->options = json_encode($newdefinition->rubric['options']);
+        $editoroptions = self::description_form_field_options($this->get_context());
+        $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(),
             'gradingform_rubric', 'definition_description', $this->definition->id);
         parent::update_definition($newdefinition, $usermodified);
 
@@ -83,10 +88,10 @@ class gradingform_rubric_controller extends gradingform_controller {
 
         // update rubric data
         $haschanges = false;
-        if (empty($newdefinition->rubric_criteria)) {
+        if (empty($newdefinition->rubric['criteria'])) {
             $newcriteria = array();
         } else {
-            $newcriteria = $newdefinition->rubric_criteria; // new ones to be saved
+            $newcriteria = $newdefinition->rubric['criteria']; // new ones to be saved
         }
         $currentcriteria = $currentdefinition->rubric_criteria;
         $criteriafields = array('sortorder', 'description', 'descriptionformat');
@@ -175,7 +180,6 @@ class gradingform_rubric_controller extends gradingform_controller {
      */
     protected function load_definition() {
         global $DB;
-
         $sql = "SELECT gd.*,
                        rc.id AS rcid, rc.sortorder AS rcsortorder, rc.description AS rcdescription, rc.descriptionformat AS rcdescriptionformat,
                        rl.id AS rlid, rl.score AS rlscore, rl.definition AS rldefinition, rl.definitionformat AS rldefinitionformat
@@ -213,6 +217,37 @@ class gradingform_rubric_controller extends gradingform_controller {
             }
         }
         $rs->close();
+        $options = $this->get_options();
+        if (!$options['sortlevelsasc']) {
+            foreach (array_keys($this->definition->rubric_criteria) as $rcid) {
+                $this->definition->rubric_criteria[$rcid]['levels'] = array_reverse($this->definition->rubric_criteria[$rcid]['levels'], true);
+            }
+        }
+    }
+
+    public static function get_default_options() {
+        $options = array(
+            'sortlevelsasc' => 1,
+            //'showdescriptionteacher' => 1,
+            //'showdescriptionstudent' => 1,
+            'showscoreteacher' => 1,
+            'showscorestudent' => 1,
+            'enableremarks' => 1,
+            'showremarksstudent' => 1
+        );
+        // TODO description options
+        return $options;
+    }
+
+    public function get_options() {
+        $options = self::get_default_options();
+        if (!empty($this->definition->options)) {
+            $thisoptions = json_decode($this->definition->options);
+            foreach ($thisoptions as $option => $value) {
+                $options[$option] = $value;
+            }
+        }
+        return $options;
     }
 
     /**
@@ -226,17 +261,16 @@ class gradingform_rubric_controller extends gradingform_controller {
         $properties = new stdClass();
         $properties->areaid = $this->areaid;
         if ($definition) {
-            foreach (array('id', 'name', 'description', 'descriptionformat', 'options', 'status') as $key) {
+            foreach (array('id', 'name', 'description', 'descriptionformat', 'status') as $key) {
                 $properties->$key = $definition->$key;
             }
             $options = self::description_form_field_options($this->get_context());
             $properties = file_prepare_standard_editor($properties, 'description', $options, $this->get_context(),
                 'gradingform_rubric', 'definition_description', $definition->id);
         }
+        $properties->rubric = array('criteria' => array(), 'options' => $this->get_options());
         if (!empty($definition->rubric_criteria)) {
-            $properties->rubric_criteria = $definition->rubric_criteria;
-        } else {
-            $properties->rubric_criteria = array();
+            $properties->rubric['criteria'] = $definition->rubric_criteria;
         }
 
         return $properties;
@@ -252,11 +286,12 @@ class gradingform_rubric_controller extends gradingform_controller {
     public function get_definition_copy(gradingform_controller $target) {
 
         $new = parent::get_definition_copy($target);
-        $old = $this->get_definition();
-        $new->rubric_criteria = array();
+        $old = $this->get_definition_for_editing();
+        $new->description_editor = $old->description_editor;
+        $new->rubric = array('criteria' => array(), 'options' => $old->rubric['options']);
         $newcritid = 1;
         $newlevid = 1;
-        foreach ($old->rubric_criteria as $oldcritid => $oldcrit) {
+        foreach ($old->rubric['criteria'] as $oldcritid => $oldcrit) {
             unset($oldcrit['id']);
             if (isset($oldcrit['levels'])) {
                 foreach ($oldcrit['levels'] as $oldlevid => $oldlev) {
@@ -268,7 +303,7 @@ class gradingform_rubric_controller extends gradingform_controller {
             } else {
                 $oldcrit['levels'] = array();
             }
-            $new->rubric_criteria['NEWID'.$newcritid] = $oldcrit;
+            $new->rubric['criteria']['NEWID'.$newcritid] = $oldcrit;
             $newcritid++;
         }
 
@@ -337,7 +372,8 @@ class gradingform_rubric_controller extends gradingform_controller {
         // append the rubric itself, using own renderer
         $output = $this->get_renderer($page);
         $criteria = $this->definition->rubric_criteria;
-        $rubric = $output->display_rubric($criteria, self::DISPLAY_PREVIEW, 'rubric');
+        $options = $this->get_options();
+        $rubric = $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
 
         return $header . $rubric;
     }
@@ -556,10 +592,11 @@ class gradingform_rubric_instance extends gradingform_instance {
             }
         }
         $criteria = $this->get_controller()->get_definition()->rubric_criteria;
+        $options = $this->get_controller()->get_options();
         $value = $gradingformelement->getValue();
         if ($value === null) {
             $value = $this->get_rubric_filling();
         }
-        return $this->get_controller()->get_renderer($page)->display_rubric($criteria, $mode, $gradingformelement->getName(), $value);
+        return $this->get_controller()->get_renderer($page)->display_rubric($criteria, $options, $mode, $gradingformelement->getName(), $value);
     }
 }
\ No newline at end of file
index 6b326ef..4899778 100644 (file)
@@ -22,7 +22,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-
 defined('MOODLE_INTERNAL') || die();
 
 /**
@@ -31,11 +30,28 @@ defined('MOODLE_INTERNAL') || die();
 class gradingform_rubric_renderer {
 
     /**
+     * This function returns html code for displaying criterion. Depending on $mode it may be the
+     * code to edit rubric, to preview the rubric, to evaluate somebody or to review the evaluation.
+     *
+     * This function may be called from display_rubric() to display the whole rubric, or it can be
+     * called by itself to return a template used by JavaScript to add new empty criteria to the
+     * rubric being designed.
+     * In this case it will use macros like {NAME}, {LEVELS}, {CRITERION-id}, etc.
+     *
+     * When overriding this function it is very important to remember that all elements of html
+     * form (in edit or evaluate mode) must have the name $elementname.
      *
-     * @param int $mode @see gradingform_rubric_controller
+     * Also JavaScript relies on the class names of elements and when developer changes them
+     * script might stop working.
+     *
+     * @param int $mode rubric display mode @see gradingform_rubric_controller
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param array|null $criterion criterion data
+     * @param string $levels_str evaluated templates for this criterion levels
+     * @param array|null $value (only in view mode) teacher's feedback on this criterion
      * @return string
      */
-    public function criterion_template($mode, $elementname = '{NAME}', $criterion = null, $levels_str = '{LEVELS}', $value = null) {
+    public function criterion_template($mode, $options, $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}');
@@ -47,56 +63,78 @@ class gradingform_rubric_renderer {
                 }
             }
         }
-        $criterion_template = html_writer::start_tag('div', array('class' => 'clearfix criterion'. $criterion['class'], 'id' => '{NAME}-{CRITERION-id}'));
+        $criterion_template = html_writer::start_tag('tr', array('class' => 'criterion'. $criterion['class'], 'id' => '{NAME}-criteria-{CRITERION-id}'));
         if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
-            $criterion_template .= html_writer::start_tag('div', array('class' => 'controls'));
+            $criterion_template .= html_writer::start_tag('td', 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));
+                $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][{CRITERION-id}]['.$key.']',
+                    'id' => '{NAME}-criteria-{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'));
+            $criterion_template .= html_writer::end_tag('td'); // .controls
+            $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
+            $description = html_writer::tag('textarea', htmlspecialchars($criterion['description']), array('name' => '{NAME}[criteria][{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']));
+                $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
+                $criterion_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{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'));
+        $criterion_template .= html_writer::tag('td', $description, array('class' => 'description', 'id' => '{NAME}-criteria-{CRITERION-id}-description'));
+        $levels_str_table = html_writer::tag('table', html_writer::tag('tr', $levels_str, array('id' => '{NAME}-criteria-{CRITERION-id}-levels')));
+        $criterion_template .= html_writer::tag('td', $levels_str_table, array('class' => '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'));
+            $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][addlevel]',
+                'id' => '{NAME}-criteria-{CRITERION-id}-levels-addlevel', 'value' => $value, 'title' => $value));
+            $criterion_template .= html_writer::tag('td', $button, array('class' => 'addlevel'));
         }
-        if (isset($value['remark'])) {
-            $currentremark = $value['remark'];
-        } else {
+        $displayremark = ($options['enableremarks'] && ($mode != gradingform_rubric_controller::DISPLAY_VIEW || $options['showremarksstudent']));
+        if ($displayremark) {
             $currentremark = '';
+            if (isset($value['remark'])) {
+                $currentremark = $value['remark'];
+            }
+            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('td', $input, array('class' => 'remark'));
+            } else 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));
+            }else if ($mode == gradingform_rubric_controller::DISPLAY_REVIEW || $mode == gradingform_rubric_controller::DISPLAY_VIEW) {
+                $criterion_template .= html_writer::tag('td', $currentremark, array('class' => 'remark')); // TODO maybe some prefix here like 'Teacher remark:'
+            }
         }
-        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 .= html_writer::end_tag('tr'); // .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) {
+    /**
+     * This function returns html code for displaying one level of one criterion. Depending on $mode
+     * it may be the code to edit rubric, to preview the rubric, to evaluate somebody or to review the evaluation.
+     *
+     * This function may be called from display_rubric() to display the whole rubric, or it can be
+     * called by itself to return a template used by JavaScript to add new empty level to the
+     * criterion during the design of rubric.
+     * In this case it will use macros like {NAME}, {CRITERION-id}, {LEVEL-id}, etc.
+     *
+     * When overriding this function it is very important to remember that all elements of html
+     * form (in edit or evaluate mode) must have the name $elementname.
+     *
+     * Also JavaScript relies on the class names of elements and when developer changes them
+     * script might stop working.
+     *
+     * @param int $mode rubric display mode @see gradingform_rubric_controller
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param string|int $criterionid either id of the nesting criterion or a macro for template
+     * @param array|null $level level data, also in view mode it might also have property $level['checked'] whether this level is checked
+     * @return string
+     */
+    public function level_template($mode, $options, $elementname = '{NAME}', $criterionid = '{CRITERION-id}', $level = null) {
         // TODO definition format
         if (!isset($level['id'])) {
             $level = array('id' => '{LEVEL-id}', 'definition' => '{LEVEL-definition}', 'score' => '{LEVEL-score}', 'class' => '{LEVEL-class}', 'checked' => false);
@@ -110,14 +148,19 @@ class gradingform_rubric_renderer {
         }
 
         // 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']));
+        $tdattributes = array('id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}', 'class' => 'level'. $level['class']);
+        if (isset($level['tdwidth'])) {
+            $tdattributes['width'] = round($level['tdwidth']).'%';
+        }
+        $level_template = html_writer::start_tag('td', $tdattributes);
+        $level_template .= html_writer::start_tag('div', array('class' => 'level-wrapper'));
         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']));
+            $definition = html_writer::tag('textarea', htmlspecialchars($level['definition']), array('name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]', 'cols' => '10', 'rows' => '4'));
+            $score = html_writer::empty_tag('input', array('type' => 'text', 'name' => '{NAME}[criteria][{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']));
+                $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]', 'value' => $level['definition']));
+                $level_template .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]', 'value' => $level['score']));
             }
             $definition = $level['definition'];
             $score = $level['score'];
@@ -130,15 +173,25 @@ class gradingform_rubric_renderer {
         if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN && $level['checked']) {
             $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'));
-        $level_template .= html_writer::tag('div', $score. get_string('scorepostfix', 'gradingform_rubric'), array('class' => 'score'));
+        $score = html_writer::tag('span', $score, array('id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-score'));
+        $level_template .= html_writer::tag('div', $definition, array('class' => 'definition', 'id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-definition'));
+        $displayscore = true;
+        if (!$options['showscoreteacher'] && in_array($mode, array(gradingform_rubric_controller::DISPLAY_EVAL, gradingform_rubric_controller::DISPLAY_EVAL_FROZEN, gradingform_rubric_controller::DISPLAY_REVIEW))) {
+            $displayscore = false;
+        }
+        if (!$options['showscorestudent'] && $mode == gradingform_rubric_controller::DISPLAY_VIEW) {
+            $displayscore = false;
+        }
+        if ($displayscore) {
+            $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));
+            $button = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][delete]', 'id' => '{NAME}-criteria-{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 .= html_writer::end_tag('div'); // .level-wrapper
+        $level_template .= html_writer::end_tag('td'); // .level
 
         $level_template = str_replace('{NAME}', $elementname, $level_template);
         $level_template = str_replace('{CRITERION-id}', $criterionid, $level_template);
@@ -146,7 +199,25 @@ class gradingform_rubric_renderer {
         return $level_template;
     }
 
-    protected function rubric_template($mode, $elementname = '{NAME}', $criteria_str = '{CRITERIA}') {
+    /**
+     * This function returns html code for displaying rubric template (content before and after
+     * criteria list). Depending on $mode it may be the code to edit rubric, to preview the rubric,
+     * to evaluate somebody or to review the evaluation.
+     *
+     * This function is called from display_rubric() to display the whole rubric.
+     *
+     * When overriding this function it is very important to remember that all elements of html
+     * form (in edit or evaluate mode) must have the name $elementname.
+     *
+     * Also JavaScript relies on the class names of elements and when developer changes them
+     * script might stop working.
+     *
+     * @param int $mode rubric display mode @see gradingform_rubric_controller
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param string $criteria_str evaluated templates for this rubric's criteria
+     * @return string
+     */
+    protected function rubric_template($mode, $options, $elementname, $criteria_str) {
         $classsuffix = ''; // CSS suffix for class of the main div. Depends on the mode
         switch ($mode) {
             case gradingform_rubric_controller::DISPLAY_EDIT_FULL:
@@ -161,34 +232,98 @@ class gradingform_rubric_renderer {
                 $classsuffix = ' evaluate frozen';  break;
             case gradingform_rubric_controller::DISPLAY_REVIEW:
                 $classsuffix = ' review';  break;
+            case gradingform_rubric_controller::DISPLAY_VIEW:
+                $classsuffix = ' view';  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'));
+        $rubric_template = html_writer::start_tag('div', array('id' => 'rubric-{NAME}', 'class' => 'clearfix gradingform_rubric'.$classsuffix));
+        $rubric_template .= html_writer::tag('table', $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));
+            $input = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][addcriterion]', 'id' => '{NAME}-criteria-addcriterion', 'value' => $value, 'title' => $value));
             $rubric_template .= html_writer::tag('div', $input, array('class' => 'addcriterion'));
         }
+        $rubric_template .= $this->rubric_edit_options($mode, $options);
         $rubric_template .= html_writer::end_tag('div');
 
         return str_replace('{NAME}', $elementname, $rubric_template);
     }
 
+    protected function rubric_edit_options($mode, $options) {
+        if ($mode != gradingform_rubric_controller::DISPLAY_EDIT_FULL
+                && $mode != gradingform_rubric_controller::DISPLAY_EDIT_FROZEN
+                && $mode != gradingform_rubric_controller::DISPLAY_PREVIEW) {
+            // Options are displayed only in edit mode
+            return;
+        }
+        $html = html_writer::start_tag('div', array('class' => 'options'));
+        $html .= html_writer::tag('div', get_string('rubricoptions', 'gradingform_rubric'), array('class' => 'optionsheading'));
+        $attrs = array('type' => 'hidden', 'name' => '{NAME}[options][optionsset]', 'value' => 1);
+        foreach ($options as $option => $value) {
+            $html .= html_writer::start_tag('div', array('class' => 'option '.$option));
+            $attrs = array('name' => '{NAME}[options]['.$option.']', 'id' => '{NAME}-options-'.$option);
+            switch ($option) {
+                case 'sortlevelsasc':
+                    // Display option as dropdown
+                    $html .= html_writer::tag('span', get_string($option, 'gradingform_rubric'), array('class' => 'label'));
+                    $value = (int)(!!$value); // make sure $value is either 0 or 1
+                    if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FULL) {
+                        $selectoptions = array(0 => get_string($option.'0', 'gradingform_rubric'), 1 => get_string($option.'1', 'gradingform_rubric'));
+                        $value_str = html_writer::select($selectoptions, $attrs['name'], $value, false, array('id' => $attrs['id']));
+                        $html .= html_writer::tag('span', $value_str, array('class' => 'value'));
+                        // TODO add here button 'Sort levels'
+                    } else {
+                        $html .= html_writer::tag('span', get_string($option.$value, 'gradingform_rubric'), array('class' => 'value'));
+                        if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
+                            $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
+                        }
+                    }
+                    break;
+                default:
+                    // Display option as checkbox
+                    $attrs['type'] = 'checkbox';
+                    $attrs['value'] = 1;
+                    if ($value) {
+                        $attrs['checked'] = 'checked';
+                    }
+                    if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
+                        $attrs['disabled'] = 'disabled';
+                    }
+                    if ($mode == gradingform_rubric_controller::DISPLAY_PREVIEW) {
+                        $attrs['disabled'] = 'disabled';
+                        unset($attrs['name']);
+                    }
+                    $html .= html_writer::empty_tag('input', $attrs);
+                    $html .= html_writer::tag('label', get_string($option, 'gradingform_rubric'), array('for' => $attrs['id']));
+                    break;
+            }
+            $html .= html_writer::end_tag('div'); // .option
+        }
+        $html .= html_writer::end_tag('div'); // .options
+        return $html;
+    }
+
     /**
-     * Returns html code for displaying the rubric in the specified mode
+     * This function returns html code for displaying rubric. Depending on $mode it may be the code
+     * to edit rubric, to preview the rubric, to evaluate somebody or to review the evaluation.
+     *
+     * It is very unlikely that this function needs to be overriden by theme. It does not produce
+     * any html code, it just prepares data about rubric design and evaluation, adds the CSS
+     * class to elements and calls the functions level_template, criterion_template and
+     * rubric_template
      *
-     * @param array $criteria
-     * @param int $mode
-     * @param string $elementname
-     * @param array $values
+     * @param array $criteria data about the rubric design
+     * @param int $mode rubric display mode @see gradingform_rubric_controller
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param array $values evaluation result
      * @return string
      */
-    public function display_rubric($criteria, $mode, $elementname = null, $values = null) {
+    public function display_rubric($criteria, $options, $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);
+            $criterion['id'] = $id;
             $levels_str = '';
             $levelcnt = 0;
             if (isset($values['criteria'][$id])) {
@@ -197,28 +332,30 @@ class gradingform_rubric_renderer {
                 $criterionvalue = null;
             }
             foreach ($criterion['levels'] as $levelid => $level) {
+                $level['id'] = $levelid;
                 $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'] = (isset($criterionvalue['levelid']) && ((int)$criterionvalue['levelid'] === $levelid));
-                if ($level['checked'] && ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_REVIEW)) {
+                if ($level['checked'] && ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_REVIEW || $mode == gradingform_rubric_controller::DISPLAY_VIEW)) {
                     $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
+                    //in mode DISPLAY_EVAL the class 'checked' will be added by JS if it is enabled. If JS is not enabled, the 'checked' class will only confuse
                 }
-                $levels_str .= $this->level_template($mode, $elementname, $id, $level);
+                $level['tdwidth'] = 100/count($criterion['levels']);
+                $levels_str .= $this->level_template($mode, $options, $elementname, $id, $level);
             }
-            $criteria_str .= $this->criterion_template($mode, $elementname, $criterion, $levels_str, $criterionvalue);
+            $criteria_str .= $this->criterion_template($mode, $options, $elementname, $criterion, $levels_str, $criterionvalue);
         }
-        return $this->rubric_template($mode, $elementname, $criteria_str);
+        return $this->rubric_template($mode, $options, $elementname, $criteria_str);
     }
 
     /**
-     * Help function to return CSS class names for element (first/last/even/odd)
+     * Help function to return CSS class names for element (first/last/even/odd) with leading space
      *
-     * @param <type> $cnt
-     * @param <type> $maxcnt
+     * @param int $cnt
+     * @param int $maxcnt
      * @return string
      */
-    private function get_css_class_suffix($cnt, $maxcnt) {
+    protected function get_css_class_suffix($cnt, $maxcnt) {
         $class = '';
         if ($cnt == 0) {
             $class .= ' first';
@@ -261,7 +398,9 @@ class gradingform_rubric_renderer {
      */
     public function display_instance(gradingform_rubric_instance $instance, $idx) {
         $criteria = $instance->get_controller()->get_definition()->rubric_criteria;
+        $options = $instance->get_controller()->get_options();
         $values = $instance->get_rubric_filling();
-        return $this->display_rubric($criteria, gradingform_rubric_controller::DISPLAY_REVIEW, 'rubric'.$idx, $values);
+        // TODO mode should be DISPLAY_REVIEW if this user is a teacher
+        return $this->display_rubric($criteria, $options, gradingform_rubric_controller::DISPLAY_VIEW, 'rubric'.$idx, $values);
     }
 }
index 1b40366..5146149 100644 (file)
@@ -50,6 +50,7 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
         global $PAGE;
         $html = $this->_getTabs();
         $renderer = $PAGE->get_renderer('gradingform_rubric');
+        $data = $this->prepare_non_js_data();
         if (!$this->_flagFrozen) {
             $mode = gradingform_rubric_controller::DISPLAY_EDIT_FULL;
             $module = array('name'=>'gradingform_rubriceditor', 'fullpath'=>'/grade/grading/form/rubric/js/rubriceditor.js',
@@ -58,32 +59,54 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
                     ));
             $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())
+                    'criteriontemplate' => $renderer->criterion_template($mode, $data['options'], $this->getName()),
+                    'leveltemplate' => $renderer->level_template($mode, $data['options'], $this->getName())
                    )),
                 true, $module);
         } else {
+            // Rubric is frozen, no javascript needed
             if ($this->_persistantFreeze) {
                 $mode = gradingform_rubric_controller::DISPLAY_EDIT_FROZEN;
             } else {
                 $mode = gradingform_rubric_controller::DISPLAY_PREVIEW;
             }
         }
-        $html .= $renderer->display_rubric($this->prepare_non_js_data(), $mode, $this->getName());
+        $html .= $renderer->display_rubric($data['criteria'], $data['options'], $mode, $this->getName());
         return $html;
     }
 
-    function prepare_non_js_data() {
-        $return = array();
-        $criteria = $this->getValue();
-        if (empty($criteria)) {
-            $criteria = array();
+    /**
+     * Prepares the data passed in $_POST:
+     * - processes the pressed buttons 'addlevel', 'addcriterion', 'moveup', 'movedown', 'delete' (when JavaScript is disabled)
+     * - if options not passed (i.e. we create a new rubric) fills the options array with the default values
+     * - if options are passed completes the options array with unchecked checkboxes
+     *
+     * @param array $value
+     * @return array
+     */
+    function prepare_non_js_data($value = null) {
+        if (null === $value) {
+            $value = $this->getValue();
+        }
+        $return = array('criteria' => array(), 'options' => gradingform_rubric_controller::get_default_options());
+        if (!isset($value['criteria'])) {
+            $value['criteria'] = array();
+        }
+        if (!empty($value['options'])) {
+            foreach (array_keys($return['options']) as $option) {
+                // special treatment for checkboxes
+                if (!empty($value['options'][$option])) {
+                    $return['options'][$option] = $value['options'][$option];
+                } else {
+                    $return['options'][$option] = null;
+                }
+            }
         }
         $lastaction = null;
         $lastid = null;
-        foreach ($criteria as $id => $criterion) {
+        foreach ($value['criteria'] as $id => $criterion) {
             if ($id == 'addcriterion') {
-                $id = $this->get_next_id(array_keys($criteria));
+                $id = $this->get_next_id(array_keys($value['criteria']));
                 $criterion = array('description' => '');
             }
             $levels = array();
@@ -105,12 +128,12 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
             if (array_key_exists('moveup', $criterion) || $lastaction == 'movedown') {
                 unset($criterion['moveup']);
                 if ($lastid !== null) {
-                    $lastcriterion = $return[$lastid];
-                    unset($return[$lastid]);
-                    $return[$id] = $criterion;
-                    $return[$lastid] = $lastcriterion;
+                    $lastcriterion = $return['criteria'][$lastid];
+                    unset($return['criteria'][$lastid]);
+                    $return['criteria'][$id] = $criterion;
+                    $return['criteria'][$lastid] = $lastcriterion;
                 } else {
-                    $return[$id] = $criterion;
+                    $return['criteria'][$id] = $criterion;
                 }
                 $lastaction = null;
                 $lastid = $id;
@@ -120,13 +143,13 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
                     unset($criterion['movedown']);
                     $lastaction = 'movedown';
                 }
-                $return[$id] = $criterion;
+                $return['criteria'][$id] = $criterion;
                 $lastid = $id;
             }
         }
         $csortorder = 1;
-        foreach (array_keys($return) as $id) {
-            $return[$id]['sortorder'] = $csortorder++;
+        foreach (array_keys($return['criteria']) as $id) {
+            $return['criteria'][$id]['sortorder'] = $csortorder++;
         }
         return $return;
     }
@@ -143,8 +166,8 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
 
     function _ruleIsCompleted($elementValue) {
         //echo "_ruleIsCompleted";
-        if (is_array($elementValue)) {
-            foreach ($elementValue as $criterionid => $criterion) {
+        if (isset($elementValue['criteria'])) {
+            foreach ($elementValue['criteria'] as $criterionid => $criterion) {
                 if ($criterionid == 'addcriterion') {
                     return false;
                 }
@@ -177,4 +200,16 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
         return parent::onQuickFormEvent($event, $arg, $caller);
     }
 
+    /**
+     * Prepares the data for saving
+     * @see prepare_non_js_data
+     *
+     * @param array $submitValues
+     * @param boolean $assoc
+     * @return array
+     */
+    function exportValue(&$submitValues, $assoc = false) {
+        $value =  $this->prepare_non_js_data($this->_findValue($submitValues));
+        return $this->_prepareValue($value, $assoc);
+    }
 }
\ No newline at end of file
index 0802227..66dc3c0 100644 (file)
@@ -1,6 +1,7 @@
 /*
-.form_rubric.editor[.frozen|.editable]
-    .criterion[.first][.last].odd/even
+.gradingform_rubric.editor[.frozen|.editable]
+  .criteria
+    .criterion[.first][.last][.odd|.even]
         .controls
             .moveup
                 [input type=submit]
                 [input type=submit]
         .description
         .levels
-            .level[.first][.last].odd/even
-                .definition
-                    [textarea]
-                .score
-                    span
-                        [input type=text]
-                .delete
-                    [input type=submit]
+            td.level[.first][.last][.odd|.even]
+                div.level-wrapper
+                    .definition
+                        [textarea]
+                    .score
+                        span
+                            [input type=text]
+                    .delete
+                        [input type=submit]
         .addlevel
             [input type=submit]
         .remark
             textarea
     .addcriterion
         [input type=submit]
+  .options
+    .optionsheading
+    .option.OPTIONNAME
 
-.form_rubric.evaluate
-    .criterion[.first][.last].odd/even
+.gradingform_rubric[.review][.evaluate[.editable|.frozen]]
+  .criteria
+    .criterion[.first][.last][.odd|.even]
         .description
         .levels
-            .level[.first][.last].odd/even
-                .radio
-                    input
-                .definition
-                .score
-                    span
+            td.level[.first][.last][.odd|.even]
+                div.level-wrapper
+                    div.radio
+                        input
+                    .definition
+                    .score
+                        span
 
 */
 
+.gradingform_rubric.editor .criterion .controls,
+.gradingform_rubric .criterion .description,
+.gradingform_rubric .criterion .levels,
+.gradingform_rubric.editor .criterion .addlevel,
+.gradingform_rubric .criterion .remark,
+.gradingform_rubric .criterion .levels .level {vertical-align: top;}
 
-.form_rubric.editor .criterion .controls,
-.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;}
+.gradingform_rubric.editor .criterion .controls,
+.gradingform_rubric .criterion .description,
+.gradingform_rubric.editor .criterion .addlevel,
+.gradingform_rubric .criterion .remark,
+.gradingform_rubric .criterion .levels .level {padding:3px;}
 
-.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;}
+.gradingform_rubric .criteria {height:100%;}
+.gradingform_rubric .criterion {border:1px solid #DDD;overflow: hidden;}
+.gradingform_rubric .criterion.even {background:#F0F0F0;}
 
-/* 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;}
+.gradingform_rubric .criterion .description {width:150px;font-weight:bold;}
 
-.form_rubric .criterion {border:1px solid #DDD;overflow: hidden;}
-.form_rubric .criterion.even {background:#F0F0F0;}
+.gradingform_rubric .criterion .levels table {width:100%;height:100%;}
+.gradingform_rubric .criterion .levels,
+.gradingform_rubric .criterion .levels table,
+.gradingform_rubric .criterion .levels table tbody {padding:0;margin:0;}
+.gradingform_rubric .criterion .levels .level {border-left:1px solid #DDD;max-width:150px;}
+.gradingform_rubric .criterion .levels .level .level-wrapper {position:relative;}
+.gradingform_rubric .criterion .levels .level.last {border-right:1px solid #DDD;}
 
-.form_rubric .criterion .description {width:150px;font-weight:bold;}
+.gradingform_rubric .plainvalue.empty {font-style: italic; color: #AAA;}
 
-.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 .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;}
+.gradingform_rubric.editor .criterion .levels .level .delete {position:absolute;right:0;bottom:0;}
+.gradingform_rubric .criterion .levels .level .score {font-style:italic;color:#575;font-weight: bold;margin-top:5px;}
 
 /* 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,
-.form_rubric.editor .criterion.last .controls .movedown input {display:none;}
+.gradingform_rubric.editor .criterion.first .controls .moveup input,
+.gradingform_rubric.editor .criterion.last .controls .movedown input {display:none;}
 
 /* evaluation */
-.form_rubric .criterion .levels .level.checked {background:#d0ffd0;}
-.form_rubric.evaluate .criterion .levels .level:hover {background:#30ff30;}
+.gradingform_rubric .criterion .levels .level.checked {background:#d0ffd0;}
+.gradingform_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;}
+.gradingform_rubric.editor .delete input,
+.gradingform_rubric.editor .moveup input,
+.gradingform_rubric.editor .movedown input{text-indent: -1000em;cursor:pointer;border:none;}
+.gradingform_rubric.editor .criterion .controls .delete input {width:20px;height:16px;background: transparent url([[pix:i/cross_red_big]]) no-repeat center top;}
+.gradingform_rubric.editor .levels .level .delete input {width:20px;height:16px;background: transparent url([[pix:i/cross_red_small]]) no-repeat center top;}
+.gradingform_rubric.editor .moveup input {width:20px;height:15px;background: transparent url([[pix:t/up]]) no-repeat center top;margin-top:4px;}
+.gradingform_rubric.editor .movedown input {width:20px;height:15px;background: transparent url([[pix:t/down]]) no-repeat center top;margin-top:4px;}
+
+.gradingform_rubric.editor .addcriterion input,
+.gradingform_rubric.editor .addlevel input {background: transparent url([[pix:t/addgreen]]) no-repeat;display:block;color:#555555;font-weight:bold;text-decoration:none;}
+.gradingform_rubric.editor .addcriterion input {background-position: 5px 8px;height:30px;line-height:29px;margin-bottom:14px;padding-left:20px;padding-right:10px;}
+.gradingform_rubric.editor .addlevel input {background-position: 5px 6px;height:25px;line-height:24px;margin-bottom:10px;padding-left:18px;padding-right:8px;}
 
-.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;}
+.gradingform_rubric .options .optionsheading {font-weight:bold;font-size:1.1em;padding-bottom:5px;}
+.gradingform_rubric .options .option {padding-bottom:2px;}
+.gradingform_rubric .options .option label {margin-left: 5px;}
+.gradingform_rubric .options .option .value {margin-left: 5px;font-weight:bold;}