MDL-29481: start implementing rubric editor
This commit is contained in:
@@ -134,7 +134,7 @@ abstract class gradingform_controller {
|
||||
/**
|
||||
* Loads the form definition is it exists
|
||||
*
|
||||
* The default implementation tries to load just the record ftom the {grading_definitions}
|
||||
* The default implementation tries to load just the record from the {grading_definitions}
|
||||
* table. The plugins are likely to override this with a more complex query that loads
|
||||
* all required data at once.
|
||||
*/
|
||||
@@ -145,6 +145,56 @@ abstract class gradingform_controller {
|
||||
'areaid' => $this->areaid,
|
||||
'method' => $this->get_method_name()), '*', IGNORE_MISSING);
|
||||
}
|
||||
|
||||
public function get_context() {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the entry in table {grading_definitions} (or creates a new one if not present)
|
||||
*
|
||||
* The plugins are likely to override (extend) this with a more complex function that updates
|
||||
* all changed data.
|
||||
*
|
||||
* @param $properties array or object of changed fields
|
||||
* @param boolean $force specifies whether usermodified/timemodified should be explicitly updated even if $properties is empty
|
||||
* @return boolean whether changes are made to DB
|
||||
*/
|
||||
public function update($properties, $force = false) {
|
||||
global $DB, $USER;
|
||||
if (!empty($properties)) {
|
||||
$properties = (array)$properties;
|
||||
} else {
|
||||
$properties = array();
|
||||
}
|
||||
|
||||
$data = array();
|
||||
foreach (array('name', 'description', 'descriptionformat', 'options', 'status') as $key) {
|
||||
if (array_key_exists($key, $properties) && (!$this->definition || $this->definition->$key != $properties[$key])) {
|
||||
$data[$key] = $properties[$key];
|
||||
}
|
||||
}
|
||||
if (!$this->definition || !empty($data) || $force) {
|
||||
$data['timemodified'] = time();
|
||||
$data['usermodified'] = $USER->id;
|
||||
if (!$this->definition) {
|
||||
// Insert entry in DB
|
||||
$data['areaid'] = $this->areaid;
|
||||
$data['method'] = $this->get_method_name();
|
||||
if (!array_key_exists('descriptionformat', $data)) {
|
||||
$data['descriptionformat'] = FORMAT_MOODLE; //TODO ?
|
||||
}
|
||||
$id = $DB->insert_record('grading_definitions', $data);
|
||||
} else {
|
||||
// Update entry in DB
|
||||
$data['id'] = $this->definition->id;
|
||||
$DB->update_record('grading_definitions', $data);
|
||||
}
|
||||
$this->load_definition();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -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) }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,3 +26,16 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$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';
|
||||
|
||||
@@ -31,7 +31,8 @@ require_once($CFG->dirroot.'/grade/grading/form/lib.php'); // parent class
|
||||
/**
|
||||
* This controller encapsulates the rubric grading logic
|
||||
*/
|
||||
class rubric_grading_controller extends grading_controller {
|
||||
class gradingform_rubric_controller extends gradingform_controller {
|
||||
protected $_rubric;
|
||||
|
||||
/**
|
||||
* Extends the module settings navigation with the rubric grading settings
|
||||
@@ -44,8 +45,226 @@ class rubric_grading_controller extends grading_controller {
|
||||
* @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'));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;}
|
||||
@@ -101,6 +101,17 @@ class grading_manager {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current context
|
||||
*/
|
||||
public function get_context() {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
public function get_cm() {
|
||||
return get_coursemodule_from_id(null, $this->context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component the manager operates on
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user