Merged Marina's recent work on rubric plugin
[moodle.git] / grade / grading / form / rubric / lib.php
CommitLineData
9b8550f8
DM
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Grading method controller for the Rubric plugin
20 *
21 * @package gradingform
22 * @subpackage rubric
23 * @copyright 2011 David Mudrak <david@moodle.com>
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27defined('MOODLE_INTERNAL') || die();
28
29require_once($CFG->dirroot.'/grade/grading/form/lib.php'); // parent class
30
31/**
32 * This controller encapsulates the rubric grading logic
33 */
21d37aa6 34class gradingform_rubric_controller extends gradingform_controller {
c586d2bf 35 protected $_rubric;
9b8550f8
DM
36
37 /**
38 * Extends the module settings navigation with the rubric grading settings
39 *
40 * This function is called when the context for the page is an activity module with the
41 * FEATURE_ADVANCED_GRADING, the user has the permission moodle/grade:managegradingforms
42 * and there is an area with the active grading method set to 'rubric'.
43 *
44 * @param settings_navigation $settingsnav {@link settings_navigation}
45 * @param navigation_node $node {@link navigation_node}
46 */
47 public function extend_settings_navigation(settings_navigation $settingsnav, navigation_node $node=null) {
c586d2bf 48 // TODO check permission to edit rubric
9b8550f8 49 $node->add(get_string('definerubric', 'gradingform_rubric'),
c586d2bf 50 new moodle_url('/grade/grading/form/rubric/edit.php', array('areaid' => $this->areaid)), settings_navigation::TYPE_CUSTOM,
9b8550f8
DM
51 null, null, new pix_icon('icon', '', 'gradingform_rubric'));
52 }
21d37aa6 53
c586d2bf
MG
54 public function make_grading_widget($raterid, $itemid, $bulk = false) {
55 return new gradingform_rubric_widget();
56 }
57
21d37aa6
DM
58 protected function get_method_name() {
59 return 'rubric';
60 }
c586d2bf
MG
61
62 /**
63 * Updates DB with the changes
64 *
65 * @param $properties array or object of elements ($form->get_data())
66 * @param boolean $force specifies whether usermodified/timemodified should be explicitly updated even if $properties is empty. Usually false for this class
67 */
68 public function update($properties, $force = false) {
69 global $DB;
70 $parentmodified = parent::update($properties, $force);
71
72 if (!empty($properties)) {
73 $properties = (array)$properties;
74 } else {
75 $properties = array();
76 }
77 $haschanges = false;
78 if (array_key_exists('rubric', $properties)) {
79 $rubricdata = $properties['rubric'];
80 $rubric = $this->get_rubric();
81 $criteriafields = array('sortorder', 'description', 'descriptionformat');
82 $levelfields = array('score', 'definition', 'definitionformat');
83 foreach ($rubricdata as $id => $criterion) {
84 // get list of submitted levels
85 $levelsdata = array();
86 if (array_key_exists('levels', $criterion)) {
87 $levelsdata = $criterion['levels'];
88 }
89 if (preg_match('/^NEWID\d+$/', $id)) {
90 // insert criterion into DB
91 $data = array('formid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE); // TODO format is not supported yet
92 foreach ($criteriafields as $key) {
93 if (array_key_exists($key, $criterion)) {
94 $data[$key] = $criterion[$key];
95 }
96 }
97 $id = $DB->insert_record('gradingform_rubric_criteria', $data);
98 $haschanges = true;
99 } else {
100 // update criterion in DB
101 $data = array();
102 foreach ($criteriafields as $key) {
103 if (array_key_exists($key, $criterion) && $criterion[$key] != $rubric[$id][$key]) {
104 $data[$key] = $criterion[$key];
105 }
106 }
107 if (!empty($data)) {
108 // update only if something is changed
109 $data['id'] = $id;
110 $DB->update_record('gradingform_rubric_criteria', $data);
111 $haschanges = true;
112 }
113 // remove deleted levels from DB
114 foreach (array_keys($rubric[$id]['levels']) as $levelid) {
115 if (!array_key_exists($levelid, $levelsdata)) {
116 $DB->delete_records('gradingform_rubric_levels', array('id' => $levelid));
117 $haschanges = true;
118 }
119 }
120 }
121 foreach ($levelsdata as $levelid => $level) {
122 if (preg_match('/^NEWID\d+$/', $levelid)) {
123 // insert level into DB
124 $data = array('criterionid' => $id, 'definitionformat' => FORMAT_MOODLE); // TODO format is not supported yet
125 foreach ($levelfields as $key) {
126 if (array_key_exists($key, $level)) {
127 $data[$key] = $level[$key];
128 }
129 }
130 $levelid = $DB->insert_record('gradingform_rubric_levels', $data);
131 $haschanges = true;
132 } else {
133 // update level in DB
134 $data = array();
135 foreach ($levelfields as $key) {
136 if (array_key_exists($key, $level) && $level[$key] != $rubric[$id]['levels'][$levelid][$key]) {
137 $data[$key] = $level[$key];
138 }
139 }
140 if (!empty($data)) {
141 // update only if something is changed
142 $data['id'] = $levelid;
143 $DB->update_record('gradingform_rubric_levels', $data);
144 $haschanges = true;
145 }
146 }
147 }
148 }
149 // remove deleted criteria from DB
150 foreach (array_keys($rubric) as $id) {
151 if (!array_key_exists($id, $rubricdata)) {
152 $DB->delete_records('gradingform_rubric_criteria', array('id' => $id));
153 $DB->delete_records('gradingform_rubric_levels', array('criterionid' => $id));
154 $haschanges = true;
155 }
156 }
157 $this->get_rubric($haschanges);
158 }
159
160 // update time modified if was not updated before
161 if (!$parentmodified && ($force || $haschanges)) {
162 parent::update($properties, true);
163 }
164 }
165
166 /**
167 * Returns the criteria/levels information stored in DB for current rubric
168 *
169 * @param boolean $force if true retrieves from DB even if cached result exists
170 * @return array
171 */
172 public function get_rubric($force = false) {
173 global $DB;
174 if ($this->_rubric === null || $force) {
175 if (!$this->definition) {
176 return array(); // definition does not exist yet
177 }
178 $this->_rubric = array();
179 $records = $DB->get_records('gradingform_rubric_criteria', array('formid' => $this->definition->id), 'sortorder, id');
180 foreach ($records as $criterion) {
181 $this->_rubric[$criterion->id] = array(
182 'description' => $criterion->description,
183 'descriptionformat' => $criterion->descriptionformat,
184 'sortorder' => $criterion->sortorder,
185 'levels' => array(),
186 );
187 $levels = $DB->get_records('gradingform_rubric_levels', array('criterionid' => $criterion->id), 'score, id'); // TODO sort order may be DESC!
188 foreach ($levels as $level) {
189 $this->_rubric[$criterion->id]['levels'][$level->id] = array(
190 'definition' => $level->definition,
191 'definitionformat' => $level->definitionformat,
192 'score' => (float)$level->score,
193 );
194 }
195 }
196 }
197 // TODO descriptionformat and definitionformat are not used at the moment
198 return $this->_rubric;
199 }
200
201 /**
202 * returns an object for edit-rubric-form
203 *
204 * @return object
205 */
206 public function get_data_for_edit() {
207 //TODO the following code may be moved to parent
208 $properties = new stdClass();
209 if ($this->definition) {
210 foreach (array('id', 'name', 'description', 'descriptionformat', 'options', 'status') as $key) {
211 $properties->$key = $this->definition->$key;
212 }
213 }
214 $properties->areaid = $this->areaid;
215 if ($this->definition) {
216 $options = self::description_form_field_options($this->get_context());
217 $properties = file_prepare_standard_editor($properties, 'description', $options, $this->get_context(), 'gradingform_rubric' /* TODO or gradingform? */, 'definition_description', $this->definition->id);
218 }
219 //TODO end // $properties = parent::get_data_for_edit();
220
221 $properties->rubric = $this->get_rubric();
222 return $properties;
223 }
224
225 // TODO the following functions may be moved to parent:
226
227 public static function description_form_field_options($context) {
228 global $CFG;
229 return array(
230 'maxfiles' => -1,
231 'maxbytes' => get_max_upload_file_size($CFG->maxbytes),
232 'context' => $context,
233 );
234 }
235
236 public function get_formatted_description() {
237 if (!$this->definition) {
238 return null;
239 }
240 $context = $this->get_context();
241
242 $options = self::description_form_field_options($this->get_context());
243 $description = file_rewrite_pluginfile_urls($this->definition->description, 'pluginfile.php', $context->id, 'gradingform_rubric' /* TODO or gradingform? */, 'definition_description', $this->definition->id, $options);
244
245 $formatoptions = array(
246 'noclean' => false,
247 'trusted' => false,
248 'filter' => true,
249 'context' => $context
250 );
251 return format_text($description, $this->definition->descriptionformat, $formatoptions);
252 }
253
254 public function postupdate_data($data) {
255 if (!$this->definition) {
256 return $data;
257 }
258 $options = self::description_form_field_options($this->get_context());
259 $data = file_postupdate_standard_editor($data, 'description', $options, $this->get_context(), 'gradingform_rubric' /* TODO or gradingform? */, 'definition_description', $this->definition->id);
260 // TODO change filearea for embedded files in grading_definition.description
261 return $data;
262 }
9b8550f8 263}
c586d2bf
MG
264
265class gradingform_rubric_widget extends gradingform_widget {
266
267 /** @var string unique identifier */
268 public $id;
269
9b8550f8 270}