MDL-41648 grade: Remove 'error' parameter in grading notification divs
[moodle.git] / grade / grading / form / guide / guideeditor.php
CommitLineData
77143217
DM
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * This file contains the marking guide editor element
19 *
20 * @package gradingform_guide
21 * @copyright 2012 Dan Marsden <dan@danmarsden.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26
27require_once("HTML/QuickForm/input.php");
28
29/**
30 * The editor for the marking guide advanced grading plugin.
31 *
32 * @package gradingform_guide
33 * @copyright 2012 Dan Marsden <dan@danmarsden.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
36class moodlequickform_guideeditor extends HTML_QuickForm_input {
37 /** @var string help message */
38 public $_helpbutton = '';
39 /** @var null|false|string stores the result of the last validation: null - undefined, false - no errors,
40 * string - error(s) text */
41 protected $validationerrors = null;
42 /** @var bool if element has already been validated **/
43 protected $wasvalidated = false;
44 /** @var null|bool If non-submit (JS) button was pressed: null - unknown, true/false - button was/wasn't pressed */
45 protected $nonjsbuttonpressed = false;
46 /** @var string|false Message to display in front of the editor (that there exist grades on this guide being edited) */
47 protected $regradeconfirmation = false;
48
49 /**
50 * Constructor
51 *
52 * @param string $elementname
53 * @param string $elementlabel
54 * @param array $attributes
55 */
1a0df553
MG
56 public function __construct($elementname=null, $elementlabel=null, $attributes=null) {
57 parent::__construct($elementname, $elementlabel, $attributes);
58 }
59
60 /**
61 * Old syntax of class constructor. Deprecated in PHP7.
62 *
63 * @deprecated since Moodle 3.1
64 */
77143217 65 public function moodlequickform_guideeditor($elementname=null, $elementlabel=null, $attributes=null) {
1a0df553
MG
66 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
67 self::__construct($elementname, $elementlabel, $attributes);
77143217
DM
68 }
69
70 /**
71 * get html for help button
72 *
73 * @return string html for help button
74 */
75 public function getHelpButton() {
76 return $this->_helpbutton;
77 }
78
79 /**
80 * The renderer will take care itself about different display in normal and frozen states
81 *
82 * @return string
83 */
84 public function getElementTemplateType() {
85 return 'default';
86 }
87
88 /**
89 * Specifies that confirmation about re-grading needs to be added to this rubric editor.
90 * $changelevel is saved in $this->regradeconfirmation and retrieved in toHtml()
91 *
92 * @see gradingform_rubric_controller::update_or_check_rubric()
93 * @param int $changelevel
94 */
95 public function add_regrade_confirmation($changelevel) {
96 $this->regradeconfirmation = $changelevel;
97 }
98
99 /**
100 * Returns html string to display this element
101 *
102 * @return string
103 */
104 public function toHtml() {
105 global $PAGE;
106 $html = $this->_getTabs();
107 $renderer = $PAGE->get_renderer('gradingform_guide');
108 $data = $this->prepare_data(null, $this->wasvalidated);
109 if (!$this->_flagFrozen) {
110 $mode = gradingform_guide_controller::DISPLAY_EDIT_FULL;
111 $module = array('name'=>'gradingform_guideeditor',
112 'fullpath'=>'/grade/grading/form/guide/js/guideeditor.js',
18619d84 113 'requires' => array('base', 'dom', 'event', 'event-touch', 'escape'),
77143217
DM
114 'strings' => array(
115 array('confirmdeletecriterion', 'gradingform_guide'),
116 array('clicktoedit', 'gradingform_guide'),
117 array('clicktoeditname', 'gradingform_guide')
118 ));
119 $PAGE->requires->js_init_call('M.gradingform_guideeditor.init', array(
120 array('name' => $this->getName(),
121 'criteriontemplate' => $renderer->criterion_template($mode, $data['options'], $this->getName()),
122 'commenttemplate' => $renderer->comment_template($mode, $this->getName())
123 )),
124 true, $module);
125 } else {
126 // Guide is frozen, no javascript needed.
127 if ($this->_persistantFreeze) {
128 $mode = gradingform_guide_controller::DISPLAY_EDIT_FROZEN;
129 } else {
130 $mode = gradingform_guide_controller::DISPLAY_PREVIEW;
131 }
132 }
133 if ($this->regradeconfirmation) {
134 if (!isset($data['regrade'])) {
135 $data['regrade'] = 1;
136 }
137 $html .= $renderer->display_regrade_confirmation($this->getName(), $this->regradeconfirmation, $data['regrade']);
138 }
139 if ($this->validationerrors) {
df8674d2 140 $html .= html_writer::div($renderer->notification($this->validationerrors), '', array('role' => 'alert'));
77143217
DM
141 }
142 $html .= $renderer->display_guide($data['criteria'], $data['comments'], $data['options'], $mode, $this->getName());
143 return $html;
144 }
145 /**
146 * Prepares the data passed in $_POST:
147 * - processes the pressed buttons 'addlevel', 'addcriterion', 'moveup', 'movedown', 'delete' (when JavaScript is disabled)
148 * sets $this->nonjsbuttonpressed to true/false if such button was pressed
149 * - if options not passed (i.e. we create a new guide) fills the options array with the default values
150 * - if options are passed completes the options array with unchecked checkboxes
151 * - if $withvalidation is set, adds 'error_xxx' attributes to elements that contain errors and creates an error string
152 * and stores it in $this->validationerrors
153 *
154 * @param array $value
155 * @param boolean $withvalidation whether to enable data validation
156 * @return array
157 */
158 protected function prepare_data($value = null, $withvalidation = false) {
159 if (null === $value) {
160 $value = $this->getValue();
161 }
162 if ($this->nonjsbuttonpressed === null) {
163 $this->nonjsbuttonpressed = false;
164 }
165
166 $errors = array();
167 $return = array('criteria' => array(), 'options' => gradingform_guide_controller::get_default_options(),
168 'comments' => array());
169 if (!isset($value['criteria'])) {
170 $value['criteria'] = array();
171 $errors['err_nocriteria'] = 1;
172 }
173 // If options are present in $value, replace default values with submitted values.
174 if (!empty($value['options'])) {
175 foreach (array_keys($return['options']) as $option) {
176 // Special treatment for checkboxes.
177 if (!empty($value['options'][$option])) {
178 $return['options'][$option] = $value['options'][$option];
179 } else {
180 $return['options'][$option] = null;
181 }
182
183 }
184 }
185
186 if (is_array($value)) {
187 // For other array keys of $value no special treatmeant neeeded, copy them to return value as is.
188 foreach (array_keys($value) as $key) {
189 if ($key != 'options' && $key != 'criteria' && $key != 'comments') {
190 $return[$key] = $value[$key];
191 }
192 }
193 }
194
195 // Iterate through criteria.
196 $lastaction = null;
197 $lastid = null;
198 foreach ($value['criteria'] as $id => $criterion) {
199 if ($id == 'addcriterion') {
200 $id = $this->get_next_id(array_keys($value['criteria']));
201 $criterion = array('description' => '');
202 $this->nonjsbuttonpressed = true;
203 }
204
205 if ($withvalidation && !array_key_exists('delete', $criterion)) {
206 if (!strlen(trim($criterion['shortname']))) {
207 $errors['err_noshortname'] = 1;
3a1cc947
MH
208 $criterion['error_description'] = true;
209 }
210 if (strlen(trim($criterion['shortname'])) > 255) {
211 $errors['err_shortnametoolong'] = 1;
77143217
DM
212 $criterion['error_description'] = true;
213 }
214 if (!strlen(trim($criterion['maxscore']))) {
215 $errors['err_nomaxscore'] = 1;
216 $criterion['error_description'] = true;
ef8dc857 217 } else if (!is_numeric($criterion['maxscore'])) {
77143217
DM
218 $errors['err_maxscorenotnumeric'] = 1;
219 $criterion['error_description'] = true;
ef8dc857
R
220 } else if ($criterion['maxscore'] < 0) {
221 $errors['err_maxscoreisnegative'] = 1;
222 $criterion['error_description'] = true;
77143217
DM
223 }
224 }
225 if (array_key_exists('moveup', $criterion) || $lastaction == 'movedown') {
226 unset($criterion['moveup']);
227 if ($lastid !== null) {
228 $lastcriterion = $return['criteria'][$lastid];
229 unset($return['criteria'][$lastid]);
230 $return['criteria'][$id] = $criterion;
231 $return['criteria'][$lastid] = $lastcriterion;
232 } else {
233 $return['criteria'][$id] = $criterion;
234 }
235 $lastaction = null;
236 $lastid = $id;
237 $this->nonjsbuttonpressed = true;
238 } else if (array_key_exists('delete', $criterion)) {
239 $this->nonjsbuttonpressed = true;
240 } else {
241 if (array_key_exists('movedown', $criterion)) {
242 unset($criterion['movedown']);
243 $lastaction = 'movedown';
244 $this->nonjsbuttonpressed = true;
245 }
246 $return['criteria'][$id] = $criterion;
247 $lastid = $id;
248 }
249 }
250
251 // Add sort order field to criteria.
252 $csortorder = 1;
253 foreach (array_keys($return['criteria']) as $id) {
254 $return['criteria'][$id]['sortorder'] = $csortorder++;
255 }
256
257 // Iterate through comments.
258 $lastaction = null;
259 $lastid = null;
260 if (!empty($value['comments'])) {
261 foreach ($value['comments'] as $id => $comment) {
262 if ($id == 'addcomment') {
263 $id = $this->get_next_id(array_keys($value['comments']));
264 $comment = array('description' => '');
265 $this->nonjsbuttonpressed = true;
266 }
267
268 if (array_key_exists('moveup', $comment) || $lastaction == 'movedown') {
269 unset($comment['moveup']);
270 if ($lastid !== null) {
271 $lastcomment = $return['comments'][$lastid];
272 unset($return['comments'][$lastid]);
273 $return['comments'][$id] = $comment;
274 $return['comments'][$lastid] = $lastcomment;
275 } else {
276 $return['comments'][$id] = $comment;
277 }
278 $lastaction = null;
279 $lastid = $id;
280 $this->nonjsbuttonpressed = true;
281 } else if (array_key_exists('delete', $comment)) {
282 $this->nonjsbuttonpressed = true;
283 } else {
284 if (array_key_exists('movedown', $comment)) {
285 unset($comment['movedown']);
286 $lastaction = 'movedown';
287 $this->nonjsbuttonpressed = true;
288 }
289 $return['comments'][$id] = $comment;
290 $lastid = $id;
291 }
292 }
293 // Add sort order field to comments.
294 $csortorder = 1;
295 foreach (array_keys($return['comments']) as $id) {
296 $return['comments'][$id]['sortorder'] = $csortorder++;
297 }
298 }
299 // Create validation error string (if needed).
300 if ($withvalidation) {
301 if (count($errors)) {
302 $rv = array();
303 foreach ($errors as $error => $v) {
304 $rv[] = get_string($error, 'gradingform_guide');
305 }
306 $this->validationerrors = join('<br/ >', $rv);
307 } else {
308 $this->validationerrors = false;
309 }
310 $this->wasvalidated = true;
311 }
312 return $return;
313
314 }
315
316 /**
317 * Scans array $ids to find the biggest element ! NEWID*, increments it by 1 and returns
318 *
319 * @param array $ids
320 * @return string
321 */
322 protected function get_next_id($ids) {
323 $maxid = 0;
324 foreach ($ids as $id) {
325 if (preg_match('/^NEWID(\d+)$/', $id, $matches) && ((int)$matches[1]) > $maxid) {
326 $maxid = (int)$matches[1];
327 }
328 }
329 return 'NEWID'.($maxid+1);
330 }
331
332 /**
333 * Checks if a submit button was pressed which is supposed to be processed on client side by JS
334 * but user seem to have disabled JS in the browser.
335 * (buttons 'add criteria', 'add level', 'move up', 'move down', 'add comment')
336 * In this case the form containing this element is prevented from being submitted
337 *
338 * @param array $value
339 * @return boolean true if non-submit button was pressed and not processed by JS
340 */
341 public function non_js_button_pressed($value) {
342 if ($this->nonjsbuttonpressed === null) {
343 $this->prepare_data($value);
344 }
345 return $this->nonjsbuttonpressed;
346 }
347
348 /**
349 * Validates that guide has at least one criterion, filled definitions and all criteria
350 * have filled descriptions
351 *
352 * @param array $value
353 * @return string|false error text or false if no errors found
354 */
355 public function validate($value) {
356 if (!$this->wasvalidated) {
357 $this->prepare_data($value, true);
358 }
359 return $this->validationerrors;
360 }
361
362 /**
363 * Prepares the data for saving
364 * @see prepare_data()
365 *
366 * @param array $submitvalues
367 * @param boolean $assoc
368 * @return array
369 */
370 public function exportValue(&$submitvalues, $assoc = false) {
371 $value = $this->prepare_data($this->_findValue($submitvalues));
372 return $this->_prepareValue($value, $assoc);
373 }
374}