From 21d37aa6230dd4a79d40480762a2266d1d0aed25 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Sat, 8 Oct 2011 00:59:01 +0200 Subject: [PATCH] MDL-29479 The draft of embedding the UI widgets into the page The patch makes more sense when reviewed together with the parallel work on gradingform_random (see MDL-29631) where the developed API is actually tested. --- grade/grading/form/lib.php | 187 +++++++++++++++++++++++++++--- grade/grading/form/rubric/lib.php | 18 ++- grade/grading/lib.php | 21 ++++ lang/en/grading.php | 2 + 4 files changed, 214 insertions(+), 14 deletions(-) diff --git a/grade/grading/form/lib.php b/grade/grading/form/lib.php index f1d0bfe2cb6..32b5d26a341 100644 --- a/grade/grading/form/lib.php +++ b/grade/grading/form/lib.php @@ -16,6 +16,8 @@ // along with Moodle. If not, see . /** + * Common classes used by gradingform plugintypes are defined here + * * @package core * @subpackage grading * @copyright 2011 David Mudrak @@ -25,13 +27,14 @@ defined('MOODLE_INTERNAL') || die(); /** - * Grading method controller encapsulates the logic of the plugin - * - * @copyright 2011 David Mudrak - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * Grading method controller represents a plugin used in a particular area */ abstract class gradingform_controller { + const DEFINITION_STATUS_WORKINPROGRESS = 0; + const DEFINITION_STATUS_PRIVATE = 1; + const DEFINITION_STATUS_PUBLIC = 2; + /** @var stdClass the context */ protected $context; @@ -53,6 +56,9 @@ abstract class gradingform_controller { /** @var bool is the target grading page finalized for sending output to the browser */ protected $pagefinalized = false; + /** @var array list of widgets made by this controller for $this->page */ + protected $widgets = array(); + /** * Do not instantinate this directly, use {@link grading_manager::get_controller()} * @@ -70,26 +76,52 @@ abstract class gradingform_controller { } /** - * Is the grading form defined and released for usage? + * Is the grading form defined and released for usage by the given user? * + * @param int $foruserid the id of the user who attempts to work with the form * @return boolean */ - public function is_form_available() { - return true; // todo make this dependent on grading_definitions existence and its status + public function is_form_available($foruserid = null) { + global $USER; + + if (is_null($foruserid)) { + $foruserid = $USER->id; + } + + if (empty($this->definition)) { + return false; + } + + if ($this->definition->status == self::DEFINITION_STATUS_PUBLIC) { + return true; + } + + if ($this->definition->status == self::DEFINITION_STATUS_PRIVATE) { + if ($this->definition->usercreated == $foruserid) { + return true; + } + } + + return false; } /** * Prepare a grading widget for the given rater and item * - * If you make multiple widgets, pass bulk = true. Note that then it is - * the caller's responsibility to call {@link finalize_page()} method explicitly. + * The options array MUST contain (string)displayas set to either 'scale' or 'grade'. + * If scale is used, the (int)scaleid must be provided. If grade is used, (int)maxgrade + * must be provided and (int)decimals can be provided (defaults to 0). + * The options array CAN contain (bool)bulk to signalize whether there are more widgets to be + * made by this controller instance or whether this is the last one. + * If you make multiple widgets, pass bulk option se to true. Note that then it is + * the caller's responsibility to call {@link finalize_page()} method explicitly then. * * @param int $raterid the user who will use the widget for grading * @param int $itemid the graded item - * @param bool $bulk are more widgets to be made by this instance or is this the last one? + * @param array $options the widget options * @return gradingform_widget renderable widget to insert into the page */ - abstract public function make_grading_widget($raterid, $itemid, $bulk = false); + abstract public function make_grading_widget($raterid, $itemid, array $options); /** * Does everything needed before the page is sent to the browser @@ -132,9 +164,95 @@ abstract class gradingform_controller { } /** - * Loads the form definition is it exists + * Saves the defintion data into database * - * The default implementation tries to load just the record ftom the {grading_definitions} + * The default implementation stored the record into the {grading_definition} table. The + * plugins are likely to extend this and save their data into own tables, too. + * + * @param stdClass $definition + */ + public function update_definition(stdClass $definition, $usermodified = null) { + global $DB, $USER; + + if (is_null($usermodified)) { + $usermodified = $USER->id; + } + + if ($this->definition) { + // the following fields can't be altered by the caller + $definition->id = $this->definition->id; + $definition->timemodified = time(); + $definition->usermodified = $usermodified; + unset($definition->areaid); + unset($definition->method); + unset($definition->timecreated); + + $DB->update_record('grading_definitions', $definition); + + } else if ($this->definition === false) { + // the record did not existed when the controller was instantinated + // let us assume it still does not exist (this may throw exception + // in case of two users working on the same form definition at the same time) + unset($definition->id); + $definition->areaid = $this->areaid; + $definition->method = $this->get_method_name(); + $definition->timecreated = time(); + $definition->usercreated = $usermodified; + $definition->timemodified = $definition->timecreated; + $definition->usermodified = $definition->usercreated; + $definition->status = self::DEFINITION_STATUS_WORKINPROGRESS; + + $DB->insert_record('grading_definitions', $definition); + + } else { + // this should not happen - the record cache status is unknown, let us + // reload it and start again + $this->load_definition(); + $this->update_definition($definition, $usermodified); + } + } + + /** + * Makes sure there is a form instance for the given rater grading the given item + * + * Plugins will probably override/extend this and load additional data of how their + * forms are filled in one complex query. + * + * @todo this might actually become abstract method + * @param int $raterid + * @param int $itemid + * @return stdClass newly created or existing record from {grading_instances} + */ + public function prepare_instance($raterid, $itemid) { + global $DB; + + if (empty($this->definition)) { + throw new coding_exception('Attempting to prepare an instance of non-existing grading form'); + } + + $current = $DB->get_record('grading_instances', array( + 'formid' => $this->definition->id, + 'raterid' => $raterid, + 'itemid' => $itemid), '*', IGNORE_MISSING); + + if (empty($current)) { + $instance = new stdClass(); + $instance->formid = $this->definition->id; + $instance->raterid = $raterid; + $instance->itemid = $itemid; + $instance->timemodified = time(); + $instance->id = $DB->insert_record('grading_instances', $instance); + return $instance; + + } else { + return $current; + } + } + + /** + * Loads the form definition if it exists + * + * The default implementation just tries to load 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. */ @@ -159,4 +277,47 @@ abstract class gradingform_renderer extends plugin_renderer_base { * Base class for all gradingform renderable widgets */ abstract class gradingform_widget implements renderable { + + /** @var string unique identifier that can be used as the element id during the rendering, for example */ + public $id; + + /** @var gradingform_controller instance of the controller that created this widget */ + public $controller; + + /** @var array the widget options */ + public $options; + + /** @var stdClass the current instance data */ + public $instance; + + /** + * @param gradingform_controller the method controller that created this widget + */ + public function __construct(gradingform_controller $controller, array $options, stdClass $instance) { + $this->id = uniqid(get_class($this)); + $this->controller = $controller; + $this->options = $options; + $this->instance = $instance; + } +} + + +/** + * Base class for all gradingform renderable grading widgets + * + * Grading widget is the UI element that raters use when they interact with + * the grading form. + */ +abstract class gradingform_grading_widget extends gradingform_widget { +} + + +/** + * Base class for all gradingform renderable editor widgets + * + * Plugins that implement editor UI via its own renderable widgets should + * probably extend this. Editor widget is the UI element that course designers + * use when they design (define) the grading form. + */ +abstract class gradingform_editor_widget implements renderable { } diff --git a/grade/grading/form/rubric/lib.php b/grade/grading/form/rubric/lib.php index c501919947b..ed30d1d73b6 100644 --- a/grade/grading/form/rubric/lib.php +++ b/grade/grading/form/rubric/lib.php @@ -31,7 +31,16 @@ 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 { + + + /** + * @see parent::make_grading_widget() + * @return gradingform_rubric_grading_widget + */ + public function make_grading_widget($raterid, $itemid, array $options) { + // todo + } /** * Extends the module settings navigation with the rubric grading settings @@ -48,4 +57,11 @@ class rubric_grading_controller extends grading_controller { new moodle_url('/grade/grading/form/rubric/edit.php', array('area' => $this->areaid)), settings_navigation::TYPE_CUSTOM, null, null, new pix_icon('icon', '', 'gradingform_rubric')); } + + /** + * @see parent::get_method_name() + */ + protected function get_method_name() { + return 'rubric'; + } } diff --git a/grade/grading/lib.php b/grade/grading/lib.php index 3c3446c65ef..7a354d5b8e3 100644 --- a/grade/grading/lib.php +++ b/grade/grading/lib.php @@ -91,6 +91,13 @@ class grading_manager { /** @var stdClass|false|null the raw record from {grading_areas}, false if does not exist, null if invalidated cache */ private $areacache = null; + /** + * @return stdClass grading manager context + */ + public function get_context() { + return $this->context; + } + /** * Sets the context the manager operates on * @@ -101,6 +108,13 @@ class grading_manager { $this->context = $context; } + /** + * @return string grading manager component + */ + public function get_component() { + return $this->component; + } + /** * Sets the component the manager operates on * @@ -112,6 +126,13 @@ class grading_manager { $this->component = $type.'_'.$name; } + /** + * @return string grading manager area name + */ + public function get_area() { + return $this->area; + } + /** * Sets the area the manager operates on * diff --git a/lang/en/grading.php b/lang/en/grading.php index 6c78cad3631..27ac999af00 100644 --- a/lang/en/grading.php +++ b/lang/en/grading.php @@ -26,7 +26,9 @@ defined('MOODLE_INTERNAL') || die(); +$string['formnotavailable'] = 'Advanced grading method was selected to use but the grading form is not available yet. You may need to define it first via a link in the Settings block.'; $string['gradinginarea'] = 'Grading ({$a})'; $string['gradingmethod'] = 'Grading method'; $string['gradingmethods'] = 'Grading methods'; $string['gradingmethodnone'] = 'Simple direct grading'; +$string['noitemid'] = 'Grading not possible. The graded item does not exist.';