Skip to content
Browse files

Merged Marina's recent work on rubric plugin

Conflicts:
	grade/grading/form/lib.php
	grade/grading/form/rubric/lib.php
	grade/grading/lib.php
  • Loading branch information...
2 parents fba356f + c586d2b commit 2824c44aecd82d7d44d98d875c54fe73378e634a @mudrd8mz mudrd8mz committed
View
108 grade/grading/form/rubric/edit.php
@@ -0,0 +1,108 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package gradingform
+ * @subpackage rubric
+ * @copyright 2011 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once("../../../../config.php");
+require_once("../../lib.php");
+require_once($CFG->dirroot.'/lib/formslib.php');
+require_once($CFG->dirroot."/grade/grading/form/rubric/rubriceditor.php");
+MoodleQuickForm::registerElementType('rubriceditor', $CFG->dirroot.'/grade/grading/form/rubric/rubriceditor.php', 'MoodleQuickForm_rubriceditor');
+
+$areaid = required_param('areaid', PARAM_INT); // Area ID
+
+$manager = get_grading_manager($areaid);
+$controller = $manager->get_controller('rubric');
+
+$cm = $manager->get_cm();
+if (! $assignment = $DB->get_record("assignment", array("id"=>$cm->instance))) {
+ print_error('invalidid', 'assignment');
+}
+if (! $course = $DB->get_record("course", array("id"=>$assignment->course))) {
+ print_error('coursemisconf', 'assignment');
+}
+
+require_login($course, false, $cm);
+//require_capability('TODO', $context); // TODO
+
+$url = new moodle_url('/grade/grading/form/rubric/edit.php', array('areaid' => $areaid));
+$PAGE->set_url($url);
+$title = get_string('definerubric', 'gradingform_rubric');
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+
+$PAGE->requires->js('/grade/grading/form/rubric/js/rubriceditor.js');
+
+
+
+class gradingform_rubric_editrubric extends moodleform {
+ function definition() {
+ $form = $this->_form;
+
+ $form->addElement('hidden', 'areaid');
+ $form->setType('areaid', PARAM_INT);
+
+ // name
+ $form->addElement('text', 'name', get_string('name', 'gradingform_rubric'), array('size'=>52));
+ $form->addRule('name', get_string('required'), 'required');
+ $form->setType('name', PARAM_TEXT);
+
+ // description
+ $options = array();//gradingform_definition_base::description_form_field_options($this->_customdata['areaid']);
+ $form->addElement('editor', 'description_editor', get_string('description', 'gradingform_rubric'), null, $options);
+ $form->setType('description_editor', PARAM_RAW);
+
+ // rubric editor
+
+ $element = $form->addElement('rubriceditor', 'rubric', 'Rubric 1');
+ $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();
+ }
+
+ $this->add_action_buttons(true);
+ }
+
+ function set_data($obj) {
+ if ($obj instanceof gradingform_rubric_controller) {
+ $properties = $obj->get_data_for_edit();
+ } else {
+ $properties = $obj;
+ }
+ parent::set_data($properties);
+ }
+}
+
+//TODO freeze rubric editor if needed
+$mform = new gradingform_rubric_editrubric(null, array('areaid' => $areaid, 'freezerubric' => optional_param('freeze', 0, PARAM_INT)));
+$mform->set_data($controller);
+if ($mform->is_submitted() && $mform->is_validated()) {
+ $data = $mform->get_data();
+ $data = $controller->postupdate_data($data);
+ $controller->update($data);
+ redirect($url);
+}
+
+echo $OUTPUT->header();
+$mform->display();
+echo $OUTPUT->footer();
View
95 grade/grading/form/rubric/js/rubriceditor.js
@@ -0,0 +1,95 @@
+M.gradingform_rubriceditor = {'templates' : {}, 'eventhandler' : null};
+
+/**
+ * This function is called for each rubriceditor on page.
+ */
+M.gradingform_rubriceditor.init = function(Y, options) {
+ M.gradingform_rubriceditor.templates[options.name] = {
+ 'criterion' : options.criteriontemplate,
+ 'level' : options.leveltemplate
+ }
+ 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);
+}
+
+// handler for clicking on submit buttons within rubriceditor element. Adds/deletes/rearranges criteria and/or levels on client side
+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;
+ var elements_str
+ if (chunks.length>3 || action == 'addlevel') {
+ elements_str = '#rubriceditor-'+name+' #'+name+'-'+chunks[1]+'-levels .level'
+ } else {
+ elements_str = '#rubriceditor-'+name+' .criterion'
+ }
+ // prepare the id of the next inserted level or criterion
+ var newid = 1
+ if (action == 'addcriterion' || action == 'addlevel') {
+ Y.all(elements_str).each( function(node) {
+ var idchunks = node.get('id').split('-'), id = idchunks.pop();
+ if (id.match(/^NEWID(\d+)$/)) newid = Math.max(newid, parseInt(id.substring(5))+1);
+ } );
+ }
+ var dialog_options = {
+ 'scope' : this,
+ 'callbackargs' : [e, Y, name, true],
+ 'callback' : M.gradingform_rubriceditor.buttonclick
+ };
+ if (chunks.length == 2 && 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)
+ M.gradingform_rubriceditor.addhandlers(Y, name);
+ } else if (chunks.length == 3 && 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)
+ M.gradingform_rubriceditor.addhandlers(Y, name);
+ } else if (chunks.length == 3 && action == 'moveup') {
+ // MOVE CRITERION UP
+ el = Y.one('#'+name+'-'+chunks[1])
+ if (el.previous()) el.get('parentNode').insertBefore(el, el.previous())
+ } else if (chunks.length == 3 && action == 'movedown') {
+ // MOVE CRITERION DOWN
+ el = Y.one('#'+name+'-'+chunks[1])
+ if (el.next()) el.get('parentNode').insertBefore(el.next(), el)
+ } else if (chunks.length == 3 && action == 'delete') {
+ // DELETE CRITERION
+ if (confirmed) {
+ Y.one('#'+name+'-'+chunks[1]).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') {
+ // DELETE LEVEL
+ if (confirmed) {
+ Y.one('#'+name+'-'+chunks[1]+'-'+chunks[2]+'-'+chunks[3]).remove()
+ } else {
+ dialog_options['message'] = M.str.gradingform_rubric.confirmdeletelevel
+ M.util.show_confirm_dialog(e, dialog_options);
+ }
+ } else {
+ // unknown action
+ return;
+ }
+ e.preventDefault();
+ // properly set classes and sortorder
+ var elements = Y.all(elements_str)
+ for (var i=0;i<elements.size();i++) {
+ elements.item(i).removeClass('first').removeClass('last').removeClass('even').removeClass('odd').
+ addClass(((i%2)?'odd':'even') + ((i==0)?' first':'') + ((i==elements.size()-1)?' last':''))
+ elements.item(i).all('input[type=hidden]').each(
+ function(node) { if (node.get('name').match(/sortorder/)) node.set('value', i) }
+ );
+ }
+}
View
13 grade/grading/form/rubric/lang/en/gradingform_rubric.php
@@ -26,3 +26,16 @@
$string['definerubric'] = 'Define rubric';
$string['pluginname'] = 'Rubric';
+
+$string['confirmdeletecriterion'] = 'Are you sure you want to delete this criterion?';
+$string['confirmdeletelevel'] = 'Are you sure you want to delete this level?';
+$string['description'] = 'Description';
+$string['name'] = 'Name';
+
+$string['addcriterion'] = 'Add criterion';
+$string['criterionmoveup'] = 'Up';
+$string['criteriondelete'] = 'Delete';
+$string['criterionmovedown'] = 'Down';
+$string['criterionaddlevel'] = 'Add level';
+$string['scorepostfix'] = ' pts';
+$string['leveldelete'] = 'Del';
View
229 grade/grading/form/rubric/lib.php
@@ -32,15 +32,7 @@
* This controller encapsulates the rubric grading logic
*/
class gradingform_rubric_controller extends gradingform_controller {
-
-
- /**
- * @see parent::make_grading_widget()
- * @return gradingform_rubric_grading_widget
- */
- public function make_grading_widget($raterid, $itemid, array $options) {
- // todo
- }
+ protected $_rubric;
/**
* Extends the module settings navigation with the rubric grading settings
@@ -53,15 +45,226 @@ public function make_grading_widget($raterid, $itemid, array $options) {
* @param navigation_node $node {@link navigation_node}
*/
public function extend_settings_navigation(settings_navigation $settingsnav, navigation_node $node=null) {
+ // TODO check permission to edit rubric
$node->add(get_string('definerubric', 'gradingform_rubric'),
- new moodle_url('/grade/grading/form/rubric/edit.php', array('area' => $this->areaid)), settings_navigation::TYPE_CUSTOM,
+ new moodle_url('/grade/grading/form/rubric/edit.php', array('areaid' => $this->areaid)), settings_navigation::TYPE_CUSTOM,
null, null, new pix_icon('icon', '', 'gradingform_rubric'));
}
- /**
- * @see parent::get_method_name()
- */
+ public function make_grading_widget($raterid, $itemid, $bulk = false) {
+ return new gradingform_rubric_widget();
+ }
+
protected function get_method_name() {
return 'rubric';
}
+
+ /**
+ * Updates DB with the changes
+ *
+ * @param $properties array or object of elements ($form->get_data())
+ * @param boolean $force specifies whether usermodified/timemodified should be explicitly updated even if $properties is empty. Usually false for this class
+ */
+ public function update($properties, $force = false) {
+ global $DB;
+ $parentmodified = parent::update($properties, $force);
+
+ if (!empty($properties)) {
+ $properties = (array)$properties;
+ } else {
+ $properties = array();
+ }
+ $haschanges = false;
+ if (array_key_exists('rubric', $properties)) {
+ $rubricdata = $properties['rubric'];
+ $rubric = $this->get_rubric();
+ $criteriafields = array('sortorder', 'description', 'descriptionformat');
+ $levelfields = array('score', 'definition', 'definitionformat');
+ foreach ($rubricdata as $id => $criterion) {
+ // get list of submitted levels
+ $levelsdata = array();
+ if (array_key_exists('levels', $criterion)) {
+ $levelsdata = $criterion['levels'];
+ }
+ if (preg_match('/^NEWID\d+$/', $id)) {
+ // insert criterion into DB
+ $data = array('formid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); // TODO format is not supported yet
+ foreach ($criteriafields as $key) {
+ if (array_key_exists($key, $criterion)) {
+ $data[$key] = $criterion[$key];
+ }
+ }
+ $id = $DB->insert_record('gradingform_rubric_criteria', $data);
+ $haschanges = true;
+ } else {
+ // update criterion in DB
+ $data = array();
+ foreach ($criteriafields as $key) {
+ if (array_key_exists($key, $criterion) && $criterion[$key] != $rubric[$id][$key]) {
+ $data[$key] = $criterion[$key];
+ }
+ }
+ if (!empty($data)) {
+ // update only if something is changed
+ $data['id'] = $id;
+ $DB->update_record('gradingform_rubric_criteria', $data);
+ $haschanges = true;
+ }
+ // remove deleted levels from DB
+ foreach (array_keys($rubric[$id]['levels']) as $levelid) {
+ if (!array_key_exists($levelid, $levelsdata)) {
+ $DB->delete_records('gradingform_rubric_levels', array('id' => $levelid));
+ $haschanges = true;
+ }
+ }
+ }
+ foreach ($levelsdata as $levelid => $level) {
+ if (preg_match('/^NEWID\d+$/', $levelid)) {
+ // insert level into DB
+ $data = array('criterionid' => $id, 'definitionformat' => FORMAT_MOODLE); // TODO format is not supported yet
+ foreach ($levelfields as $key) {
+ if (array_key_exists($key, $level)) {
+ $data[$key] = $level[$key];
+ }
+ }
+ $levelid = $DB->insert_record('gradingform_rubric_levels', $data);
+ $haschanges = true;
+ } else {
+ // update level in DB
+ $data = array();
+ foreach ($levelfields as $key) {
+ if (array_key_exists($key, $level) && $level[$key] != $rubric[$id]['levels'][$levelid][$key]) {
+ $data[$key] = $level[$key];
+ }
+ }
+ if (!empty($data)) {
+ // update only if something is changed
+ $data['id'] = $levelid;
+ $DB->update_record('gradingform_rubric_levels', $data);
+ $haschanges = true;
+ }
+ }
+ }
+ }
+ // remove deleted criteria from DB
+ foreach (array_keys($rubric) as $id) {
+ if (!array_key_exists($id, $rubricdata)) {
+ $DB->delete_records('gradingform_rubric_criteria', array('id' => $id));
+ $DB->delete_records('gradingform_rubric_levels', array('criterionid' => $id));
+ $haschanges = true;
+ }
+ }
+ $this->get_rubric($haschanges);
+ }
+
+ // update time modified if was not updated before
+ if (!$parentmodified && ($force || $haschanges)) {
+ parent::update($properties, true);
+ }
+ }
+
+ /**
+ * Returns the criteria/levels information stored in DB for current rubric
+ *
+ * @param boolean $force if true retrieves from DB even if cached result exists
+ * @return array
+ */
+ public function get_rubric($force = false) {
+ global $DB;
+ if ($this->_rubric === null || $force) {
+ if (!$this->definition) {
+ return array(); // definition does not exist yet
+ }
+ $this->_rubric = array();
+ $records = $DB->get_records('gradingform_rubric_criteria', array('formid' => $this->definition->id), 'sortorder, id');
+ foreach ($records as $criterion) {
+ $this->_rubric[$criterion->id] = array(
+ 'description' => $criterion->description,
+ 'descriptionformat' => $criterion->descriptionformat,
+ 'sortorder' => $criterion->sortorder,
+ 'levels' => array(),
+ );
+ $levels = $DB->get_records('gradingform_rubric_levels', array('criterionid' => $criterion->id), 'score, id'); // TODO sort order may be DESC!
+ foreach ($levels as $level) {
+ $this->_rubric[$criterion->id]['levels'][$level->id] = array(
+ 'definition' => $level->definition,
+ 'definitionformat' => $level->definitionformat,
+ 'score' => (float)$level->score,
+ );
+ }
+ }
+ }
+ // TODO descriptionformat and definitionformat are not used at the moment
+ return $this->_rubric;
+ }
+
+ /**
+ * returns an object for edit-rubric-form
+ *
+ * @return object
+ */
+ public function get_data_for_edit() {
+ //TODO the following code may be moved to parent
+ $properties = new stdClass();
+ if ($this->definition) {
+ foreach (array('id', 'name', 'description', 'descriptionformat', 'options', 'status') as $key) {
+ $properties->$key = $this->definition->$key;
+ }
+ }
+ $properties->areaid = $this->areaid;
+ if ($this->definition) {
+ $options = self::description_form_field_options($this->get_context());
+ $properties = file_prepare_standard_editor($properties, 'description', $options, $this->get_context(), 'gradingform_rubric' /* TODO or gradingform? */, 'definition_description', $this->definition->id);
+ }
+ //TODO end // $properties = parent::get_data_for_edit();
+
+ $properties->rubric = $this->get_rubric();
+ return $properties;
+ }
+
+ // TODO the following functions may be moved to parent:
+
+ public static function description_form_field_options($context) {
+ global $CFG;
+ return array(
+ 'maxfiles' => -1,
+ 'maxbytes' => get_max_upload_file_size($CFG->maxbytes),
+ 'context' => $context,
+ );
+ }
+
+ public function get_formatted_description() {
+ if (!$this->definition) {
+ return null;
+ }
+ $context = $this->get_context();
+
+ $options = self::description_form_field_options($this->get_context());
+ $description = file_rewrite_pluginfile_urls($this->definition->description, 'pluginfile.php', $context->id, 'gradingform_rubric' /* TODO or gradingform? */, 'definition_description', $this->definition->id, $options);
+
+ $formatoptions = array(
+ 'noclean' => false,
+ 'trusted' => false,
+ 'filter' => true,
+ 'context' => $context
+ );
+ return format_text($description, $this->definition->descriptionformat, $formatoptions);
+ }
+
+ public function postupdate_data($data) {
+ if (!$this->definition) {
+ return $data;
+ }
+ $options = self::description_form_field_options($this->get_context());
+ $data = file_postupdate_standard_editor($data, 'description', $options, $this->get_context(), 'gradingform_rubric' /* TODO or gradingform? */, 'definition_description', $this->definition->id);
+ // TODO change filearea for embedded files in grading_definition.description
+ return $data;
+ }
+}
+
+class gradingform_rubric_widget extends gradingform_widget {
+
+ /** @var string unique identifier */
+ public $id;
+
}
View
46 grade/grading/form/rubric/renderer.php
@@ -0,0 +1,46 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package gradingform
+ * @subpackage rubric
+ * @copyright 2011 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Grading method plugin renderer
+ */
+class gradingform_rubric_renderer extends gradingform_renderer {
+
+ /**
+ * Renders grading widget
+ *
+ * @param gradingform_random_widget $widget
+ * @return string HTML
+ */
+ protected function render_gradingform_rubric_widget(gradingform_rubric_widget $widget) {
+
+ $button = html_writer::tag('button', 'Loading ...', array('type' => 'button', 'value' => 'Go'));
+ $span = html_writer::tag('span', '');
+
+ return $this->output->container($button.$span, 'gradingform_rubric-widget-wrapper', 1);
+ }
+}
View
268 grade/grading/form/rubric/rubriceditor.php
@@ -0,0 +1,268 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package gradingform
+ * @subpackage rubric
+ * @copyright 2011 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("HTML/QuickForm/input.php");
+
+// register file-related rules
+if (class_exists('HTML_QuickForm')) {
+ HTML_QuickForm::registerRule('rubriceditorcompleted', 'callback', '_ruleIsCompleted', 'MoodleQuickForm_rubriceditor');
+}
+
+class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
+ public $_helpbutton = '';
+
+ function MoodleQuickForm_rubriceditor($elementName=null, $elementLabel=null, $attributes=null) {
+ parent::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
+ }
+
+ function getHelpButton() {
+ return $this->_helpbutton;
+ }
+
+ function getElementTemplateType() {
+ return 'default';
+ }
+
+ 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);
+
+ if (!$this->_flagFrozen) {
+ $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';
+ } else {
+ $class .= ' even';
+ }
+ return $class;
+ }
+
+ function prepare_non_js_data() {
+ $return = array();
+ $criteria = $this->getValue();
+ if (empty($criteria)) {
+ $criteria = array();
+ }
+ $lastaction = null;
+ $lastid = null;
+ foreach ($criteria as $id => $criterion) {
+ if ($id == 'addcriterion') {
+ $id = $this->get_next_id(array_keys($criteria));
+ $criterion = array('description' => '');
+ }
+ $levels = array();
+ if (array_key_exists('levels', $criterion)) {
+ foreach ($criterion['levels'] as $levelid => $level) {
+ if ($levelid == 'addlevel') {
+ $levelid = $this->get_next_id(array_keys($criterion['levels']));
+ $level = array(
+ 'definition' => '',
+ 'score' => 0,
+ );
+ }
+ if (!array_key_exists('delete', $level)) {
+ $levels[$levelid] = $level;
+ }
+ }
+ }
+ $criterion['levels'] = $levels;
+ 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;
+ } else {
+ $return[$id] = $criterion;
+ }
+ $lastaction = null;
+ $lastid = $id;
+ } else if (array_key_exists('delete', $criterion)) {
+ } else {
+ if (array_key_exists('movedown', $criterion)) {
+ unset($criterion['movedown']);
+ $lastaction = 'movedown';
+ }
+ $return[$id] = $criterion;
+ $lastid = $id;
+ }
+ }
+ $csortorder = 1;
+ foreach (array_keys($return) as $id) {
+ $return[$id]['sortorder'] = $csortorder++;
+ }
+ return $return;
+ }
+
+ function get_next_id($ids) {
+ $maxid = 0;
+ foreach ($ids as $id) {
+ if (preg_match('/^NEWID(\d+)$/', $id, $matches) && ((int)$matches[1]) > $maxid) {
+ $maxid = (int)$matches[1];
+ }
+ }
+ return 'NEWID'.($maxid+1);
+ }
+
+ function _ruleIsCompleted($elementValue) {
+ //echo "_ruleIsCompleted";
+ if (is_array($elementValue)) {
+ foreach ($elementValue as $criterionid => $criterion) {
+ if ($criterionid == 'addcriterion') {
+ return false;
+ }
+ if (array_key_exists('moveup', $criterion) || array_key_exists('movedown', $criterion) || array_key_exists('delete', $criterion)) {
+ return false;
+ }
+ if (array_key_exists('levels', $criterion) && is_array($criterion['levels'])) {
+ foreach ($criterion['levels'] as $levelid => $level) {
+ if ($levelid == 'addlevel') {
+ return false;
+ }
+ if (array_key_exists('delete', $level)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ //TODO check everything is filled
+ //echo "<pre>";print_r($elementValue);echo "</pre>";
+ return true;
+ }
+
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ $name = $this->getName();
+ if ($name && $caller->elementExists($name)) {
+ $caller->addRule($name, '', 'rubriceditorcompleted');
+ }
+ return parent::onQuickFormEvent($event, $arg, $caller);
+ }
+
+}
View
66 grade/grading/form/rubric/styles.css
@@ -0,0 +1,66 @@
+/*
+.form_rubric.editor[.frozen|.editable]
+ .criterion[.first][.last].odd/even
+ .controls
+ .moveup
+ [input type=submit]
+ .delete
+ [input type=submit]
+ .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]
+ .addcriterion
+ [input type=submit]
+
+.form_rubric.evaluate
+ .criterion[.first][.last].odd/even
+ .description
+ .levels
+ .level[.first][.last].odd/even
+ .definition
+ .score
+ span
+
+*/
+
+
+.form_rubric.editor .criterion .controls,
+.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.editor .criterion .controls,
+.form_rubric .criterion .description,
+.form_rubric.editor .criterion .addlevel,
+.form_rubric .criterion .levels .level {padding:3px}
+
+.form_rubric .criterion {border:1px solid #DDD;}
+.form_rubric .criterion.even {background:#F0F0F0;}
+
+.form_rubric .criterion .description {min-width:100px;font-weight:bold;}
+
+.form_rubric .criterion .levels .level {border-left:1px solid #DDD;min-width:80px;}
+.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;}
+
+/* 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;}
+
+/* evaluation */
+.form_rubric.evaluate .criterion .levels .level.checked {background:#d0ffd0;}
+.form_rubric.evaluate .criterion .levels .level:hover {background:#30ff30;}

0 comments on commit 2824c44

Please sign in to comment.
Something went wrong with that request. Please try again.