- In rubric editor the line 'Current rubric status' is hidden if there is no status yet
- If present the style of the status is the same as on manage.php page
- For newly created rubric 'Add criterion' button is pre-pressed automatically
- Changed JavaScript to work for Mac browsers default settings and for IPad
- MDL-30269: added explanation message about score to grade mapping
- fixed bug with non-javascript 'Add criterion' behaviour
$PAGE->set_title(get_string('definerubric', 'gradingform_rubric'));
$PAGE->set_heading(get_string('definerubric', 'gradingform_rubric'));
-$mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'context' => $context, 'allowdraft' => !$controller->has_active_instances()));
-$data = $controller->get_definition_for_editing();
+$mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'context' => $context, 'allowdraft' => !$controller->has_active_instances()), 'post', '', array('class' => 'gradingform_rubric_editform'));
+$data = $controller->get_definition_for_editing(true);
$returnurl = optional_param('returnurl', $manager->get_management_url(), PARAM_LOCALURL);
$data->returnurl = $returnurl;
$mform->set_data($data);
// rubric completion status
$choices = array();
- $choices[gradingform_controller::DEFINITION_STATUS_DRAFT] = get_string('statusdraft', 'grading');
- $choices[gradingform_controller::DEFINITION_STATUS_READY] = get_string('statusready', 'grading');
+ $choices[gradingform_controller::DEFINITION_STATUS_DRAFT] = html_writer::tag('span', get_string('statusdraft', 'core_grading'), array('class' => 'status draft'));
+ $choices[gradingform_controller::DEFINITION_STATUS_READY] = html_writer::tag('span', get_string('statusready', 'core_grading'), array('class' => 'status ready'));
$form->addElement('select', 'status', get_string('rubricstatus', 'gradingform_rubric'), $choices)->freeze();
// rubric editor
$form->closeHeaderBefore('buttonar');
}
+ /**
+ * Setup the form depending on current values. This method is called after definition(),
+ * data submission and set_data().
+ * All form setup that is dependent on form values should go in here.
+ *
+ * We remove the element status if there is no current status (i.e. rubric is only being created)
+ * so the users do not get confused
+ */
+ public function definition_after_data() {
+ $form = $this->_form;
+ $el = $form->getElement('status');
+ if (!$el->getValue()) {
+ $form->removeElement('status');
+ } else {
+ $vals = array_values($el->getValue());
+ if ($vals[0] == gradingform_controller::DEFINITION_STATUS_READY) {
+ $this->findButton('saverubric')->setValue(get_string('save', 'gradingform_rubric'));
+ }
+ }
+ }
+
/**
* Form vlidation.
* If there are errors return array of errors ("fieldname"=>"error message"),
}
M.gradingform_rubriceditor.disablealleditors()
Y.on('click', M.gradingform_rubriceditor.clickanywhere, 'body', null)
+ YUI().use('event-touch', function (Y) {
+ Y.one('body').on('touchstart', M.gradingform_rubriceditor.clickanywhere);
+ Y.one('body').on('touchend', M.gradingform_rubriceditor.clickanywhere);
+ })
M.gradingform_rubriceditor.addhandlers()
};
// it switches this element to edit mode. If rubric button is clicked it does nothing so the 'buttonclick'
// function is invoked
M.gradingform_rubriceditor.clickanywhere = function(e) {
+ if (e.type == 'touchstart') return
var el = e.target
// if clicked on button - disablecurrenteditor, continue
if (el.get('tagName') == 'INPUT' && el.get('type') == 'submit') {
el = el.get('parentNode')
}
if (el) {
- if (el.one('textarea').getStyle('display') == 'none') {
+ if (el.one('textarea').hasClass('hiddenelement')) {
M.gradingform_rubriceditor.disablealleditors()
M.gradingform_rubriceditor.editmode(el, true, focustb)
}
// switch the criterion description or level to edit mode or switch back
M.gradingform_rubriceditor.editmode = function(el, editmode, focustb) {
var ta = el.one('textarea')
- if (!editmode && ta.getStyle('display') == 'none') return;
- if (editmode && ta.getStyle('display') == 'block') return;
- var pseudotablink = '<a href="#" class="pseudotablink"> </a>',
+ if (!editmode && ta.hasClass('hiddenelement')) return;
+ if (editmode && !ta.hasClass('hiddenelement')) return;
+ var pseudotablink = '<input type="text" size="1" class="pseudotablink"/>',
taplain = ta.get('parentNode').one('.plainvalue'),
tbplain = null,
- tb = el.one('input[type=text]')
+ tb = el.one('.score input[type=text]')
// add 'plainvalue' next to textarea for description/definition and next to input text field for score (if applicable)
if (!taplain) {
- ta.get('parentNode').append('<div class="plainvalue"><span class="textvalue"> </span>'+pseudotablink+'</div>')
+ ta.get('parentNode').append('<div class="plainvalue">'+pseudotablink+'<span class="textvalue"> </span></div>')
taplain = ta.get('parentNode').one('.plainvalue')
taplain.one('.pseudotablink').on('focus', M.gradingform_rubriceditor.clickanywhere)
if (tb) {
- tb.get('parentNode').append('<div class="plainvalue"><span class="textvalue"> </span>'+pseudotablink+'</div>')
+ tb.get('parentNode').append('<span class="plainvalue">'+pseudotablink+'<span class="textvalue"> </span></span>')
tbplain = tb.get('parentNode').one('.plainvalue')
tbplain.one('.pseudotablink').on('focus', M.gradingform_rubriceditor.clickanywhere)
}
}
taplain.one('.textvalue').set('innerHTML', value)
if (tb) tbplain.one('.textvalue').set('innerHTML', tb.get('value'))
+ // hide/display textarea, textbox and plaintexts
+ taplain.removeClass('hiddenelement')
+ ta.addClass('hiddenelement')
+ if (tb) {
+ tbplain.removeClass('hiddenelement')
+ tb.addClass('hiddenelement')
+ }
} else {
// if we need to show the input fields, set the width/height for textarea so it fills the cell
- var width = parseFloat(ta.get('parentNode').getComputedStyle('width')),
- height
- if (el.hasClass('level')) height = parseFloat(el.getComputedStyle('height')) - parseFloat(el.one('.score').getComputedStyle('height'))
- else height = parseFloat(ta.get('parentNode').getComputedStyle('height'))
- ta.setStyle('width', Math.max(width,50)+'px').setStyle('height', Math.max(height,20)+'px')
- }
- // hide/display textarea, textbox and plaintexts
- taplain.setStyle('display', editmode ? 'none' : 'block')
- ta.setStyle('display', editmode ? 'block' : 'none')
- if (tb) {
- tbplain.setStyle('display', editmode ? 'none' : 'inline-block')
- tb.setStyle('display', editmode ? 'inline-block' : 'none')
+ try {
+ var width = parseFloat(ta.get('parentNode').getComputedStyle('width')),
+ height
+ if (el.hasClass('level')) height = parseFloat(el.getComputedStyle('height')) - parseFloat(el.one('.score').getComputedStyle('height'))
+ else height = parseFloat(ta.get('parentNode').getComputedStyle('height'))
+ ta.setStyle('width', Math.max(width,50)+'px')
+ ta.setStyle('height', Math.max(height,20)+'px')
+ }
+ catch (err) {
+ // this browser do not support 'computedStyle', leave the default size of the textbox
+ }
+ // hide/display textarea, textbox and plaintexts
+ taplain.addClass('hiddenelement')
+ ta.removeClass('hiddenelement')
+ if (tb) {
+ tbplain.addClass('hiddenelement')
+ tb.removeClass('hiddenelement')
+ }
}
// focus the proper input field in edit mode
if (editmode) { if (tb && focustb) tb.focus(); else ta.focus() }
$string['regradeoption1'] = 'Mark for regrade';
$string['restoredfromdraft'] = 'NOTE: The last attempt to grade this person was not saved properly so draft grades have been restored. If you want to cancel these changes use the \'Cancel\' button below.';
$string['rubric'] = 'Rubric';
+$string['rubricmapping'] = 'Score to grade mapping rules';
+$string['rubricmappingexplained'] = 'The minimum possible score for this rubric is <b>{$a->minscore} points</b> and it will be converted to the minimum grade available in this module (which is zero unless the scale is used).
+ The maximum score <b>{$a->maxscore} points</b> will be converted to the maximum grade.<br />
+ Intermediate scores will be converted respectively and rounded to the nearest available grade.<br />
+ If a scale is used instead of a grade, the score will be converted to the scale elements as if they were consecutive integers.';
$string['rubricnotcompleted'] = 'Please choose something for each criterion';
$string['rubricoptions'] = 'Rubric options';
$string['rubricstatus'] = 'Current rubric status';
+$string['save'] = 'Save';
$string['saverubric'] = 'Save rubric and make it ready';
$string['saverubricdraft'] = 'Save as draft';
-$string['scorepostfix'] = '{$a} points';
+$string['scorepostfix'] = '{$a}points';
$string['showdescriptionstudent'] = 'Display rubric description to those being graded';
$string['showdescriptionteacher'] = 'Display rubric description during evaluation';
$string['showremarksstudent'] = 'Show remarks to those being graded';
$string['showscoreteacher'] = 'Display points for each level during evaluation';
$string['sortlevelsasc'] = 'Sort order for levels:';
$string['sortlevelsasc0'] = 'Descending by number of points';
-$string['sortlevelsasc1'] = 'Ascending by number of points';
-$string['statusdraft'] = 'Draft';
-$string['statusready'] = 'Ready';
\ No newline at end of file
+$string['sortlevelsasc1'] = 'Ascending by number of points';
\ No newline at end of file
/**
* Converts the current definition into an object suitable for the editor form's set_data()
*
+ * @param boolean $addemptycriterion whether to add an empty criterion if the rubric is completely empty (just being created)
* @return stdClass
*/
- public function get_definition_for_editing() {
+ public function get_definition_for_editing($addemptycriterion = false) {
$definition = $this->get_definition();
$properties = new stdClass();
$properties->rubric = array('criteria' => array(), 'options' => $this->get_options());
if (!empty($definition->rubric_criteria)) {
$properties->rubric['criteria'] = $definition->rubric_criteria;
+ } else if (!$definition && $addemptycriterion) {
+ $properties->rubric['criteria'] = array('addcriterion' => 1);
}
return $properties;
$output = $this->get_renderer($page);
$criteria = $this->definition->rubric_criteria;
$options = $this->get_options();
- $rubric = $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
+ $rubric = $output->display_rubric_mapping_explained($this->get_min_max_score());
+ $rubric .= $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
return $rubric;
}
return array($subsql, $params);
}
+
+ /**
+ * Calculates and returns the possible minimum and maximum score (in points) for this rubric
+ *
+ * @return array
+ */
+ public function get_min_max_score() {
+ if (!$this->is_form_available()) {
+ return null;
+ }
+ $returnvalue = array('minscore' => 0, 'maxscore' => 0);
+ foreach ($this->get_definition()->rubric_criteria as $id => $criterion) {
+ $scores = array();
+ foreach ($criterion['levels'] as $level) {
+ $scores[] = $level['score'];
+ }
+ sort($scores);
+ $returnvalue['minscore'] += $scores[0];
+ $returnvalue['maxscore'] += $scores[sizeof($scores)-1];
+ }
+ return $returnvalue;
+ }
}
/**
global $DB, $USER;
$grade = $this->get_rubric_filling();
- $minscore = 0;
- $maxscore = 0;
- foreach ($this->get_controller()->get_definition()->rubric_criteria as $id => $criterion) {
- $scores = array();
- foreach ($criterion['levels'] as $level) {
- $scores[] = $level['score'];
- }
- sort($scores);
- $minscore += $scores[0];
- $maxscore += $scores[sizeof($scores)-1];
- }
-
- if ($maxscore <= $minscore) {
+ if (!($scores = $this->get_controller()->get_min_max_score()) || $scores['maxscore'] <= $scores['minscore']) {
return -1;
}
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
+ return round(($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*($maxgrade-$mingrade), 0) + $mingrade;
}
/**
$leveltemplate .= 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}[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']));
+ $score = html_writer::empty_tag('input', array('type' => 'text', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][score]', 'size' => '3', 'value' => $level['score']));
} else {
if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN) {
$leveltemplate .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => '{NAME}[criteria][{CRITERION-id}][levels][{LEVEL-id}][definition]', 'value' => $level['definition']));
if ($mode == gradingform_rubric_controller::DISPLAY_EVAL_FROZEN && $level['checked']) {
$leveltemplate .= 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}-criteria-{CRITERION-id}-levels-{LEVEL-id}-score'));
+ $score = html_writer::tag('span', $score, array('id' => '{NAME}-criteria-{CRITERION-id}-levels-{LEVEL-id}-score', 'class' => 'scorevalue'));
$definitionclass = 'definition';
if (isset($level['error_definition'])) {
$definitionclass .= ' error';
$html .= html_writer::end_tag('div');
return $html;
}
+
+ /**
+ * Generates and returns HTML code to display information box about how rubric score is converted to the grade
+ *
+ * @param array $scores
+ * @return string
+ */
+ public function display_rubric_mapping_explained($scores) {
+ $html = '';
+ if (!$scores) {
+ return $html;
+ }
+ $html .= $this->box(
+ html_writer::tag('h4', get_string('rubricmapping', 'gradingform_rubric')).
+ html_writer::tag('div', get_string('rubricmappingexplained', 'gradingform_rubric', (object)$scores))
+ , 'generalbox rubricmappingexplained');
+ return $html;
+ }
}
// when adding new criterion copy the number of levels and their scores from the last criterion
if (!empty($value['criteria'][$lastid]['levels'])) {
foreach ($value['criteria'][$lastid]['levels'] as $lastlevel) {
- $criterion['levels']['NEWID'+($i++)]['score'] = $lastlevel['score'];
+ $criterion['levels']['NEWID'.($i++)]['score'] = $lastlevel['score'];
}
} else {
- $criterion['levels']['NEWID'+($i++)]['score'] = 0;
+ $criterion['levels']['NEWID'.($i++)]['score'] = 0;
}
// add more levels so there are at least 3 in the new criterion. Increment by 1 the score for each next one
- for ($i; $i<3; $i++) {
- $criterion['levels']['NEWID'+$i]['score'] = $criterion['levels']['NEWID'+($i-1)]['score'] + 1;
+ for ($i=$i; $i<3; $i++) {
+ $criterion['levels']['NEWID'.$i]['score'] = $criterion['levels']['NEWID'.($i-1)]['score'] + 1;
}
// set other necessary fields (definition) for the levels in the new criterion
foreach (array_keys($criterion['levels']) as $i) {
*/
+.gradingform_rubric_editform .status {font-weight:normal;text-transform:uppercase;font-size:60%;padding:0.25em;border:1px solid #EEE;-moz-border-radius:5px;}
+.gradingform_rubric_editform .status.ready {background-color:#e7f1c3;border-color:#AAEEAA;}
+.gradingform_rubric_editform .status.draft {background-color:#f3f2aa;border-color:#EEEE22;}
+
.gradingform_rubric.editor .criterion .controls,
.gradingform_rubric .criterion .description,
.gradingform_rubric .criterion .levels,
.gradingform_rubric .plainvalue.empty {font-style: italic; color: #AAA;}
.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;}
+.gradingform_rubric .criterion .levels .level .score {font-style:italic;color:#575;font-weight: bold;margin-top:5px;nowrap:nowrap;}
+.gradingform_rubric .criterion .levels .level .score .scorevalue {padding-right:5px;}
/* Make invisible the buttons 'Move up' for the first criterion and 'Move down' for the last, because those buttons will make no change */
.gradingform_rubric.editor .criterion.first .controls .moveup input,
.gradingform_rubric .criterion .levels .level .definition.error,
.gradingform_rubric .criterion .levels .level .score.error {background:#FFDDDD;}
+/* special classes for elements created by rubriceditor.js */
+.gradingform_rubric.editor .hiddenelement {display:none;}
+.gradingform_rubric.editor .pseudotablink {background-color:transparent;border:0px solid;height:1px;width:1px;color:transparent;padding:0;margin:0;position:relative;float:right;}
+
/**
*
*/