Initial support for the grading management screen
authorDavid Mudrak <david@moodle.com>
Mon, 17 Oct 2011 23:05:07 +0000 (01:05 +0200)
committerDavid Mudrak <david@moodle.com>
Mon, 17 Oct 2011 23:05:07 +0000 (01:05 +0200)
The user is sent to the new grade/grading/manage.php screen when an
advanced grading method is set in the activity mod form or via the
settings block.

The screen is supposed to (1) let the user change the currently active
grading method, (2) open the editor for the selected method, (3) create
new form from a template (4) display a preview of the form.

course/moodleform_mod.php
grade/grading/form/lib.php
grade/grading/form/rubric/lib.php
grade/grading/form/rubric/renderer.php
grade/grading/lib.php
grade/grading/manage.php [new file with mode: 0644]
grade/grading/renderer.php [new file with mode: 0644]
lang/en/grading.php
theme/standard/style/grade.css

index 5e6ff41..008e755 100644 (file)
@@ -666,6 +666,7 @@ abstract class moodleform_mod extends moodleform {
                     $areaname = key($this->current->_advancedgradingdata['areas']);
                     $mform->addElement('select', 'advancedgradingmethod_'.$areaname,
                         get_string('gradingmethod', 'core_grading'), $this->current->_advancedgradingdata['methods']);
+                    $mform->addHelpButton('advancedgradingmethod_'.$areaname, 'gradingmethod', 'core_grading');
 
                 } else {
                     // the module defines multiple gradable areas, display a selector
index e6f2272..d6c4e59 100644 (file)
@@ -98,6 +98,18 @@ abstract class gradingform_controller {
         return $this->areaid;
     }
 
+    /**
+     * Is the form definition record available?
+     *
+     * Note that this actually checks whether the process of defining the form ever started
+     * and not whether the form definition should be considered as final.
+     *
+     * @return boolean
+     */
+    public function is_form_defined() {
+        return !empty($this->definition);
+    }
+
     /**
      * Is the grading form defined and released for usage by the given user?
      *
@@ -111,7 +123,7 @@ abstract class gradingform_controller {
             $foruserid = $USER->id;
         }
 
-        if (empty($this->definition)) {
+        if (!$this->is_form_defined()) {
             return false;
         }
 
@@ -128,6 +140,23 @@ abstract class gradingform_controller {
         return false;
     }
 
+    /**
+     * Returns URL of a page where the grading form can be defined and edited.
+     *
+     * @param moodle_url $returnurl optional URL of a page where the user should be sent once they are finished with editing
+     * @return moodle_url
+     */
+    public function get_editor_url(moodle_url $returnurl = null) {
+
+        $params = array('areaid' => $this->areaid);
+
+        if (!is_null($returnurl)) {
+            $params['returnurl'] = $returnurl->out(false);
+        }
+
+        return new moodle_url('/grade/grading/form/'.$this->get_method_name().'/edit.php', $params);
+    }
+
     /**
      * Extends the module settings navigation
      *
@@ -290,15 +319,23 @@ abstract class gradingform_controller {
     }
 
     /**
-     * Returns the renderer for the current plugin
+     * Returns the HTML code displaying the preview of the grading form
      *
-     * @param string $subtype optional subtype
-     * @param string $target one of rendering target constants
-     * @return renderer_base
+     * Plugins are supposed to override/extend this. Ideally they should delegate
+     * the task to their own renderer.
+     *
+     * @param moodle_page $page the target page
+     * @return string
      */
-    public function get_renderer($subtype = null, $target = null) {
-        global $PAGE;
-        return $PAGE->get_renderer('gradingform_'. $this->get_method_name(), $subtype, $target);
+    public function render_preview(moodle_page $page) {
+
+        if (!$this->is_form_defined()) {
+            throw new coding_exception('It is the caller\'s responsibility to make sure that the form is actually defined');
+        }
+
+        $output = $page->get_renderer('core_grading');
+
+        return $output->preview_definition_header($this);
     }
 
     ////////////////////////////////////////////////////////////////////////////
index 0573eac..0a293c6 100644 (file)
@@ -52,7 +52,7 @@ class gradingform_rubric_controller extends gradingform_controller {
      */
     public function extend_settings_navigation(settings_navigation $settingsnav, navigation_node $node=null) {
         $node->add(get_string('definerubric', 'gradingform_rubric'),
-            new moodle_url('/grade/grading/form/rubric/edit.php', array('areaid' => $this->areaid)), settings_navigation::TYPE_CUSTOM,
+            $this->get_editor_url(), settings_navigation::TYPE_CUSTOM,
             null, null, new pix_icon('icon', '', 'gradingform_rubric'));
     }
 
@@ -332,7 +332,7 @@ class gradingform_rubric_controller extends gradingform_controller {
         if ($value === null) {
             $value = $this->get_grading($raterid, $submissionid); // TODO maybe implement in form->set_data() ?
         }
-        return $this->get_renderer()->display_rubric($criteria, $mode, $gradingformelement->getName(), $value);
+        return $this->get_renderer($PAGE)->display_rubric($criteria, $mode, $gradingformelement->getName(), $value);
     }
 
     /**
@@ -486,4 +486,33 @@ class gradingform_rubric_controller extends gradingform_controller {
         }
         return true;
     }
+
+    /**
+     * Returns the rubric plugin renderer
+     *
+     * @param moodle_page $page the target page
+     * @return renderer_base
+     */
+    public function get_renderer(moodle_page $page) {
+        return $page->get_renderer('gradingform_'. $this->get_method_name());
+    }
+
+    /**
+     * Returns the HTML code displaying the preview of the grading form
+     *
+     * @param moodle_page $page the target page
+     * @return string
+     */
+    public function render_preview(moodle_page $page) {
+
+        // use the parent's method to render the common information about the form
+        $header = parent::render_preview($page);
+
+        // append the rubric itself, using own renderer
+        $output = $this->get_renderer($page);
+        // todo something like $rubric = $output->render_preview($this);
+        $rubric = '[[TODO RUBRIC PREVIEW]]';
+
+        return $header . $rubric;
+    }
 }
index 349eb3c..71b7e22 100644 (file)
@@ -30,20 +30,6 @@ defined('MOODLE_INTERNAL') || die();
  */
 class gradingform_rubric_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);
-    }
-
     /**
      *
      * @param int $mode @see gradingform_rubric_controller
index 97734f0..ce96d87 100644 (file)
@@ -143,6 +143,43 @@ class grading_manager {
         $this->area = $area;
     }
 
+    /**
+     * Returns a text describing the context and the component
+     *
+     * At the moment this works for gradable areas in course modules. In the future, this
+     * method should be improved so it works for other contexts (blocks, gradebook items etc)
+     * or subplugins.
+     *
+     * @return string
+     */
+    public function get_component_title() {
+
+        $this->ensure_isset(array('context', 'component'));
+        list($context, $course, $cm) = get_context_info_array($this->get_context()->id);
+
+        if (!empty($cm->name)) {
+            $title = $cm->name;
+        } else {
+            debugging('Gradable areas are currently supported at the course module level only', DEBUG_DEVELOPER);
+            $title = $this->get_component();
+        }
+
+        return $title;
+    }
+
+    /**
+     * Returns the localized title of the currently set area
+     *
+     * @return string
+     */
+    public function get_area_title() {
+
+        $this->ensure_isset(array('context', 'component', 'area'));
+        $areas = $this->get_available_areas();
+
+        return $areas[$this->get_area()];
+    }
+
     /**
      * Loads the gradable area info from the database
      *
@@ -302,7 +339,6 @@ class grading_manager {
      * @param navigation_node $modulenode {@link navigation_node}
      */
     public function extend_settings_navigation(settings_navigation $settingsnav, navigation_node $modulenode=null) {
-        global $PAGE, $CFG;
 
         $this->ensure_isset(array('context', 'component'));
 
@@ -313,14 +349,8 @@ class grading_manager {
             return;
         }
 
-        if ($PAGE->url->compare(new moodle_url('/grade/grading/management.php'), URL_MATCH_BASE)) {
-            // we are already at the management page, do not produce link to ourselves
-            // (because of the returnurl)
-            $managementurl = null;
-        } else {
-            $managementurl = $this->get_management_url($PAGE->url);
-        }
-        $managementnode = $modulenode->add(get_string('gradingmanagement', 'core_grading'), $managementurl, settings_navigation::TYPE_CUSTOM);
+        $managementnode = $modulenode->add(get_string('gradingmanagement', 'core_grading'),
+            $this->get_management_url(), settings_navigation::TYPE_CUSTOM);
 
         foreach ($areas as $areaname => $areatitle) {
             $this->set_area($areaname);
@@ -333,7 +363,7 @@ class grading_manager {
 
             if (count($areas) > 1) {
                 // if the module supports multiple gradable areas, make a node for each of them
-                $node = $managementnode->add(get_string('gradinginarea', 'core_grading', $areatitle), null, settings_navigation::NODETYPE_BRANCH);
+                $node = $managementnode->add($areatitle, null, settings_navigation::NODETYPE_BRANCH);
             } else {
                 // otherwise put the items directly into the module's node
                 $node = $managementnode;
@@ -409,9 +439,10 @@ class grading_manager {
      * Returns the URL of the grading area management page
      *
      * @param moodle_url $returnurl optional URL of the page where the user should be sent back to
+     * @param string $area optional area name for multi-area components
      * @return moodle_url
      */
-    public function get_management_url(moodle_url $returnurl = null) {
+    public function get_management_url(moodle_url $returnurl = null, $area = null) {
 
         $this->ensure_isset(array('context', 'component'));
 
@@ -419,13 +450,16 @@ class grading_manager {
             $params = array('areaid' => $this->areacache->id);
         } else {
             $params = array('contextid' => $this->context->id, 'component' => $this->component);
+            if (!is_null($area)) {
+                $params['area'] = $area;
+            }
         }
 
         if (!is_null($returnurl)) {
             $params['returnurl'] = $returnurl->out(false);
         }
 
-        return new moodle_url('/grade/grading/management.php', $params);
+        return new moodle_url('/grade/grading/manage.php', $params);
     }
 
     ////////////////////////////////////////////////////////////////////////////
diff --git a/grade/grading/manage.php b/grade/grading/manage.php
new file mode 100644 (file)
index 0000000..96cbfe7
--- /dev/null
@@ -0,0 +1,130 @@
+<?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    core
+ * @subpackage grading
+ * @copyright  2011 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(dirname(__FILE__))).'/config.php');
+require_once($CFG->dirroot.'/grade/grading/lib.php');
+
+$areaid         = optional_param('areaid', null, PARAM_INT);
+$contextid      = optional_param('contextid', null, PARAM_INT);
+$component      = optional_param('component', null, PARAM_COMPONENT);
+$area           = optional_param('area', null, PARAM_AREA);
+$returnurl      = optional_param('returnurl', null, PARAM_LOCALURL);
+$activemethod   = optional_param('activemethod', null, PARAM_PLUGIN);
+
+if (!is_null($areaid)) {
+    // get manager by id
+    $manager = get_grading_manager($areaid);
+} else {
+    // get manager by context and component
+    if (is_null($contextid) or is_null($component)) {
+        throw new coding_exception('The caller script must identify the gradable area.');
+    }
+    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    $manager = get_grading_manager($context, $component, $area);
+}
+
+list($context, $course, $cm) = get_context_info_array($manager->get_context()->id);
+
+if (is_null($returnurl)) {
+    $returnurl = new moodle_url('/course/view.php', array('id' => $course->id));
+} else {
+    $returnurl = new moodle_url($returnurl);
+}
+
+require_login($course, true, $cm);
+require_capability('moodle/grade:managegradingforms', $context);
+
+$PAGE->set_url($manager->get_management_url($returnurl, $area));
+navigation_node::override_active_url($manager->get_management_url());
+$PAGE->set_title(get_string('gradingmanagement', 'core_grading'));
+$PAGE->set_heading(get_string('gradingmanagement', 'core_grading'));
+$output = $PAGE->get_renderer('core_grading');
+
+if (is_null($manager->get_area())) {
+    $areas = $manager->get_available_areas();
+
+    if (empty($areas)) {
+        throw new moodle_exception('no_gradable_area_available', 'core_grading');
+    }
+
+    if (count($areas) == 1) {
+        // if there is just one area available, set it automatically
+        $area = reset(array_keys($areas));
+        $manager->set_area($area);
+
+    } else {
+        // display area selector (we will make this page nicer once we have some real multi-area component)
+        echo $output->header();
+        echo $output->heading($manager->get_component_title());
+        echo $output->single_select($PAGE->url, 'area', $areas);
+        echo $output->footer();
+        die();
+    }
+}
+
+// process the eventual change of the active grading method
+if (!empty($activemethod)) {
+    require_sesskey();
+    if ($activemethod == 'none') {
+        // here we expect that noone would actually want to call their plugin as 'none'
+        $activemethod = null;
+    }
+    $manager->set_active_method($activemethod);
+    redirect($PAGE->url);
+}
+
+echo $output->header();
+echo $output->heading(get_string('gradingmanagementtitle', 'core_grading', array(
+    'component' => $manager->get_component_title(), 'area' => $manager->get_area_title())));
+
+// display the active grading method information and selector
+echo $output->management_method_selector($manager, $PAGE->url);
+
+// get the currently active method's controller
+$method = $manager->get_active_method();
+if (!empty($method)) {
+    $controller = $manager->get_controller($method);
+    // display relevant actions
+    echo $output->container_start('actions');
+    if ($controller->is_form_defined()) {
+        echo $output->management_action_icon($controller->get_editor_url($returnurl),
+            get_string('manageactionedit', 'core_grading'), 'b/document-properties');
+        echo $output->management_action_icon($PAGE->url,
+            get_string('manageactiondelete', 'core_grading'), 'b/edit-delete');
+    } else {
+        echo $output->management_action_icon($controller->get_editor_url($returnurl),
+            get_string('manageactionnew', 'core_grading'), 'b/document-new');
+        echo $output->management_action_icon($PAGE->url,
+            get_string('manageactionclone', 'core_grading'), 'b/edit-copy');
+    }
+    echo $output->container_end();
+
+    // display the grading form preview
+    if ($controller->is_form_defined()) {
+        echo $output->box($controller->render_preview($PAGE), 'preview');
+    }
+}
+
+
+echo $output->footer();
diff --git a/grade/grading/renderer.php b/grade/grading/renderer.php
new file mode 100644 (file)
index 0000000..fa4ba46
--- /dev/null
@@ -0,0 +1,81 @@
+<?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/>.
+
+/**
+ * Renderer for core_grading subsystem
+ *
+ * @package    core
+ * @subpackage grading
+ * @copyright  2011 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Standard HTML output renderer for core_grading subsystem
+ */
+class core_grading_renderer extends plugin_renderer_base {
+
+    /**
+     * Renders the active method selector at the grading method management screen
+     *
+     * @param grading_manager $gradingman
+     * @param moodle_url $targeturl
+     * @return string
+     */
+    public function management_method_selector(grading_manager $manager, moodle_url $targeturl) {
+
+        $method = $manager->get_active_method();
+        $methods = $manager->get_available_methods(false);
+        $methods['none'] = get_string('gradingmethodnone', 'core_grading');
+        $selector = new single_select(new moodle_url($targeturl, array('sesskey' => sesskey())),
+            'activemethod', $methods, empty($method) ? 'none' : $method, null, 'activemethodselector');
+        $selector->set_label(get_string('changeactivemethod', 'core_grading'));
+        $selector->set_help_icon('gradingmethod', 'core_grading');
+
+        return $this->output->render($selector);
+    }
+
+    /**
+     * Renders an action icon at the gradng method management screen
+     *
+     * @param moodle_url $url action URL
+     * @param string $text action text
+     * @param string $icon the name of the icon to use
+     * @return string
+     */
+    public function management_action_icon(moodle_url $url, $text, $icon) {
+
+        $img = html_writer::empty_tag('img', array('src' => $this->output->pix_url($icon), 'class' => 'action-icon'));
+        $txt = html_writer::tag('div', $text, array('class' => 'action-text'));
+        return html_writer::link($url, $img . $txt, array('class' => 'action'));
+    }
+
+    /**
+     * Renders the common information about the form definition
+     *
+     * @param gradingform_controller $controller
+     * @return string
+     */
+    public function preview_definition_header(gradingform_controller $controller) {
+
+        $definition = $controller->get_definition();
+        // todo make this nicer, append the information about the time created/modified etc
+        return $this->output->heading(format_text($definition->name));
+    }
+}
index 0b8909d..57c2446 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['activemethodinfo'] = '\'{$a->method}\' is selected as the active grading method for the \'{$a->area}\' area';
+$string['activemethodinfonone'] = 'There is no advanced grading method selected for the \'{$a->area}\' area. Simple direct grading will be used.';
+$string['changeactivemethod'] = 'Change active grading method to';
 $string['exc_gradingformelement'] = 'Unable to instantiate grading form element';
 $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['gradingmanagement'] = 'Advanced grading';
+$string['gradingmanagementtitle'] = 'Advanced grading: {$a->component} ({$a->area})';
 $string['gradingmethod'] = 'Grading method';
+$string['gradingmethod_help'] = 'Choose the advanced grading method that should be used for calculating grades in the given context.
+
+To disable advance grading and switch back to the default grading mechanism, choose \'Simple direct grading\'.';
 $string['gradingmethods'] = 'Grading methods';
 $string['gradingmethodnone'] = 'Simple direct grading';
+$string['manageactionclone'] = 'Create new grading form from template';
+$string['manageactiondelete'] = 'Remove the currently defined form';
+$string['manageactionedit'] = 'Edit the current form definition';
+$string['manageactionnew'] = 'Define new grading form from scratch';
 $string['noitemid'] = 'Grading not possible. The graded item does not exist.';
index 5b901c0..a8a47e2 100644 (file)
@@ -33,4 +33,12 @@ td.grade div.overridden {background-color: #DDDDDD;}
 .gradetreebox tr .cell.level2 {background-color: #D0DBD3; width: 10px;}
 .gradetreebox tr .cell.level3 {background-color: #D0F3D6; width: 10px;}
 .gradetreebox tr .cell.level4 {background-color: #F0F0AA; width: 10px;}
-.gradetreebox tr .cell.level5 {background-color: #EBDEF6; width: 10px;}
\ No newline at end of file
+.gradetreebox tr .cell.level5 {background-color: #EBDEF6; width: 10px;}
+
+/** Advanced grading **/
+#page-grade-grading-manage #activemethodselector {text-align:center;margin-bottom:1em;}
+#page-grade-grading-manage #activemethodselector select {margin:0px 1em;}
+#page-grade-grading-manage .actions {text-align:center;}
+#page-grade-grading-manage .action {display:inline-block;width: 150px;background-color:#EEE;border:2px solid #CCC;
+    margin:0.5em;padding:0.5em;text-align:center;-moz-border-radius:5px}
+#page-grade-grading-manage .action:hover {text-decoration:none;background-color:#F6F6F6;
\ No newline at end of file