MDL-31731 Grading form Marking Guide - fix for test 6.2, checkboxes not saving state...
[moodle.git] / grade / grading / form / guide / renderer.php
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/>.
17 /**
18  * Contains the Guide grading form renderer in all of its glory
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  */
25 defined('MOODLE_INTERNAL') || die();
27 /**
28  * Grading method plugin renderer
29  *
30  * @package    gradingform_guide
31  * @copyright  2012 Dan Marsden <dan@danmarsden.com>
32  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33  */
34 class gradingform_guide_renderer extends plugin_renderer_base {
36     /**
37      * This function returns html code for displaying criterion. Depending on $mode it may be the
38      * code to edit guide, to preview the guide, to evaluate somebody or to review the evaluation.
39      *
40      * This function may be called from display_guide() to display the whole guide, or it can be
41      * called by itself to return a template used by JavaScript to add new empty criteria to the
42      * guide being designed.
43      * In this case it will use macros like {NAME}, {LEVELS}, {CRITERION-id}, etc.
44      *
45      * When overriding this function it is very important to remember that all elements of html
46      * form (in edit or evaluate mode) must have the name $elementname.
47      *
48      * Also JavaScript relies on the class names of elements and when developer changes them
49      * script might stop working.
50      *
51      * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller()}
52      * @param array $options An array of options.
53      *      showmarkspercriterionstudents (bool) If true adds the current score to the display
54      * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
55      * @param array $criterion criterion data
56      * @param array $value (only in view mode) teacher's feedback on this criterion
57      * @param array $validationerrors An array containing validation errors to be shown
58      * @return string
59      */
60     public function criterion_template($mode, $options, $elementname = '{NAME}', $criterion = null, $value = null,
61                                        $validationerrors = null) {
62         if ($criterion === null || !is_array($criterion) || !array_key_exists('id', $criterion)) {
63             $criterion = array('id' => '{CRITERION-id}',
64                                'description' => '{CRITERION-description}',
65                                'sortorder' => '{CRITERION-sortorder}',
66                                'class' => '{CRITERION-class}',
67                                'descriptionmarkers' => '{CRITERION-descriptionmarkers}',
68                                'shortname' => '{CRITERION-shortname}',
69                                'maxscore' => '{CRITERION-maxscore}');
70         } else {
71             foreach (array('sortorder', 'description', 'class', 'shortname', 'descriptionmarkers', 'maxscore') as $key) {
72                 // Set missing array elements to empty strings to avoid warnings.
73                 if (!array_key_exists($key, $criterion)) {
74                     $criterion[$key] = '';
75                 }
76             }
77         }
79         $criteriontemplate = html_writer::start_tag('tr', array('class' => 'criterion'. $criterion['class'],
80             'id' => '{NAME}-criteria-{CRITERION-id}'));
81         $descriptionclass = 'description';
82         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
83             $criteriontemplate .= html_writer::start_tag('td', array('class' => 'controls'));
84             foreach (array('moveup', 'delete', 'movedown') as $key) {
85                 $value = get_string('criterion'.$key, 'gradingform_guide');
86                 $button = html_writer::empty_tag('input', array('type' => 'submit',
87                     'name' => '{NAME}[criteria][{CRITERION-id}]['.$key.']',
88                     'id' => '{NAME}-criteria-{CRITERION-id}-'.$key, 'value' => $value, 'title' => $value, 'tabindex' => -1));
89                 $criteriontemplate .= html_writer::tag('div', $button, array('class' => $key));
90             }
91             $criteriontemplate .= html_writer::end_tag('td'); // Controls.
92             $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
93                 'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
95             $shortname = html_writer::empty_tag('input', array('type'=> 'text',
96                 'name' => '{NAME}[criteria][{CRITERION-id}][shortname]',  'value' => htmlspecialchars($criterion['shortname'])));
97             $shortname = html_writer::tag('div', $shortname, array('class'=>'criterionname'));
98             $description = html_writer::tag('textarea', htmlspecialchars($criterion['description']),
99                 array('name' => '{NAME}[criteria][{CRITERION-id}][description]', 'cols' => '65', 'rows' => '5'));
100             $description = html_writer::tag('div', $description, array('class'=>'criteriondesc'));
102             $descriptionmarkers = html_writer::tag('textarea', htmlspecialchars($criterion['descriptionmarkers']),
103                 array('name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]', 'cols' => '65', 'rows' => '5'));
104             $descriptionmarkers = html_writer::tag('div', $descriptionmarkers, array('class'=>'criteriondescmarkers'));
106             $maxscore = html_writer::empty_tag('input', array('type'=> 'text',
107                 'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]', 'size' => '3',
108                 'value' => htmlspecialchars($criterion['maxscore'])));
109             $maxscore = html_writer::tag('div', $maxscore, array('class'=>'criterionmaxscore'));
110         } else {
111             if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN) {
112                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
113                     'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
114                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
115                     'name' => '{NAME}[criteria][{CRITERION-id}][shortname]', 'value' => $criterion['shortname']));
116                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
117                     'name' => '{NAME}[criteria][{CRITERION-id}][description]', 'value' => $criterion['description']));
118                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
119                     'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]', 'value' => $criterion['descriptionmarkers']));
120                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
121                     'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]', 'value' => $criterion['maxscore']));
122             } else if ($mode == gradingform_guide_controller::DISPLAY_EVAL ||
123                        $mode == gradingform_guide_controller::DISPLAY_VIEW) {
124                 $descriptionclass = 'descriptionreadonly';
125             }
126             $shortname   = html_writer::tag('div', $criterion['shortname'],
127                 array('class'=>'criterionshortname', 'name' => '{NAME}[criteria][{CRITERION-id}][shortname]'));
128             $descmarkerclass = '';
129             $descstudentclass = '';
130             if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
131                 if (!get_user_preferences('gradingform_guide-showmarkerdesc', true)) {
132                     $descmarkerclass = ' hide';
133                 }
134                 if (!get_user_preferences('gradingform_guide-showstudentdesc', true)) {
135                     $descstudentclass = ' hide';
136                 }
137             }
138             $description = html_writer::tag('div', $criterion['description'],
139                 array('class'=>'criteriondescription'.$descstudentclass,
140                       'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
141             $descriptionmarkers   = html_writer::tag('div', $criterion['descriptionmarkers'],
142                 array('class'=>'criteriondescriptionmarkers'.$descmarkerclass,
143                       'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
144             $maxscore   = html_writer::tag('div', $criterion['maxscore'],
145                 array('class'=>'criteriondescriptionscore', 'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]'));
146         }
148         if (isset($criterion['error_description'])) {
149             $descriptionclass .= ' error';
150         }
152         $title = html_writer::tag('label', get_string('criterion', 'gradingform_guide'),
153             array('for'=>'{NAME}[criteria][{CRITERION-id}][shortname]', 'class' => 'criterionnamelabel'));
154         $title .= $shortname;
155         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL ||
156             $mode == gradingform_guide_controller::DISPLAY_PREVIEW) {
157             $title .= html_writer::tag('label', get_string('descriptionstudents', 'gradingform_guide'),
158                 array('for'=>'{NAME}[criteria][{CRITERION-id}][description]'));
159             $title .= $description;
160             $title .= html_writer::tag('label', get_string('descriptionmarkers', 'gradingform_guide'),
161                 array('for'=>'{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
162             $title .= $descriptionmarkers;
163             $title .=  html_writer::tag('label', get_string('maxscore', 'gradingform_guide'),
164                 array('for'=>'{NAME}[criteria][{CRITERION-id}][maxscore]'));
165             $title .= $maxscore;
166         } else if ($mode == gradingform_guide_controller::DISPLAY_PREVIEW_GRADED ||
167                    $mode == gradingform_guide_controller::DISPLAY_VIEW) {
168             $title .= $description;
169         } else {
170             $title .= $description . $descriptionmarkers;
171         }
172         $criteriontemplate .= html_writer::tag('td', $title, array('class' => $descriptionclass,
173             'id' => '{NAME}-criteria-{CRITERION-id}-shortname'));
175         $currentremark = '';
176         $currentscore = '';
177         if (isset($value['remark'])) {
178             $currentremark = $value['remark'];
179         }
180         if (isset($value['score'])) {
181             $currentscore = $value['score'];
182         }
183         if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
184             $scoreclass = '';
185             if (!empty($validationerrors[$criterion['id']]['score'])) {
186                 $scoreclass = 'error';
187                 $currentscore = $validationerrors[$criterion['id']]['score']; // Show invalid score in form.
188             }
189             $input = html_writer::tag('textarea', htmlspecialchars($currentremark),
190                 array('name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'cols' => '65', 'rows' => '5',
191                       'class' => 'markingguideremark'));
192             $criteriontemplate .= html_writer::tag('td', $input, array('class' => 'remark'));
193             $score = html_writer::tag('label', get_string('score', 'gradingform_guide'),
194                 array('for'=>'{NAME}[criteria][{CRITERION-id}][score]', 'class' => $scoreclass));
195             $score .= html_writer::empty_tag('input', array('type'=> 'text',
196                 'name' => '{NAME}[criteria][{CRITERION-id}][score]', 'class' => $scoreclass,
197                 'size' => '3', 'value' => htmlspecialchars($currentscore)));
198             $score .= '/'.$maxscore;
200             $criteriontemplate .= html_writer::tag('td', $score, array('class' => 'score'));
201         } else if ($mode == gradingform_guide_controller::DISPLAY_EVAL_FROZEN) {
202             $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
203                 'name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'value' => $currentremark));
204         } else if ($mode == gradingform_guide_controller::DISPLAY_REVIEW ||
205             $mode == gradingform_guide_controller::DISPLAY_VIEW) {
206             $criteriontemplate .= html_writer::tag('td', $currentremark, array('class' => 'remark'));
207             if (!empty($options['showmarkspercriterionstudents'])) {
208                 $criteriontemplate .= html_writer::tag('td', htmlspecialchars($currentscore). ' / '.$maxscore,
209                     array('class' => 'score'));
210             }
211         }
212         $criteriontemplate .= html_writer::end_tag('tr'); // Criterion.
214         $criteriontemplate = str_replace('{NAME}', $elementname, $criteriontemplate);
215         $criteriontemplate = str_replace('{CRITERION-id}', $criterion['id'], $criteriontemplate);
216         return $criteriontemplate;
217     }
219     /**
220      * This function returns html code for displaying criterion. Depending on $mode it may be the
221      * code to edit guide, to preview the guide, to evaluate somebody or to review the evaluation.
222      *
223      * This function may be called from display_guide() to display the whole guide, or it can be
224      * called by itself to return a template used by JavaScript to add new empty criteria to the
225      * guide being designed.
226      * In this case it will use macros like {NAME}, {LEVELS}, {CRITERION-id}, etc.
227      *
228      * When overriding this function it is very important to remember that all elements of html
229      * form (in edit or evaluate mode) must have the name $elementname.
230      *
231      * Also JavaScript relies on the class names of elements and when developer changes them
232      * script might stop working.
233      *
234      * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
235      * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
236      * @param array $comment
237      * @return string
238      */
239     public function comment_template($mode, $elementname = '{NAME}', $comment = null) {
240         if ($comment === null || !is_array($comment) || !array_key_exists('id', $comment)) {
241             $comment = array('id' => '{COMMENT-id}',
242                 'description' => '{COMMENT-description}',
243                 'sortorder' => '{COMMENT-sortorder}',
244                 'class' => '{COMMENT-class}');
245         } else {
246             foreach (array('sortorder', 'description', 'class') as $key) {
247                 // Set missing array elements to empty strings to avoid warnings.
248                 if (!array_key_exists($key, $comment)) {
249                     $criterion[$key] = '';
250                 }
251             }
252         }
253         $criteriontemplate = html_writer::start_tag('tr', array('class' => 'criterion'. $comment['class'],
254             'id' => '{NAME}-comments-{COMMENT-id}'));
255         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
256             $criteriontemplate .= html_writer::start_tag('td', array('class' => 'controls'));
257             foreach (array('moveup', 'delete', 'movedown') as $key) {
258                 $value = get_string('comments'.$key, 'gradingform_guide');
259                 $button = html_writer::empty_tag('input', array('type' => 'submit',
260                     'name' => '{NAME}[comments][{COMMENT-id}]['.$key.']', 'id' => '{NAME}-comments-{COMMENT-id}-'.$key,
261                     'value' => $value, 'title' => $value, 'tabindex' => -1));
262                 $criteriontemplate .= html_writer::tag('div', $button, array('class' => $key));
263             }
264             $criteriontemplate .= html_writer::end_tag('td'); // Controls.
265             $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
266                 'name' => '{NAME}[comments][{COMMENT-id}][sortorder]', 'value' => $comment['sortorder']));
267             $description = html_writer::tag('textarea', htmlspecialchars($comment['description']),
268                 array('name' => '{NAME}[comments][{COMMENT-id}][description]', 'cols' => '65', 'rows' => '5'));
269             $description = html_writer::tag('div', $description, array('class'=>'criteriondesc'));
270         } else {
271             if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN) {
272                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
273                     'name' => '{NAME}[comments][{COMMENT-id}][sortorder]', 'value' => $comment['sortorder']));
274                 $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
275                     'name' => '{NAME}[comments][{COMMENT-id}][description]', 'value' => $comment['description']));
276             }
277             if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
278                 $description = html_writer::tag('span', htmlspecialchars($comment['description']),
279                     array('name' => '{NAME}[comments][{COMMENT-id}][description]',
280                           'title' => get_string('clicktocopy', 'gradingform_guide'),
281                           'id' => '{NAME}[comments][{COMMENT-id}]', 'class'=>'markingguidecomment'));
282             } else {
283                 $description = $comment['description'];
284             }
285         }
286         $descriptionclass = 'description';
287         if (isset($comment['error_description'])) {
288             $descriptionclass .= ' error';
289         }
290         $criteriontemplate .= html_writer::tag('td', $description, array('class' => $descriptionclass,
291             'id' => '{NAME}-comments-{COMMENT-id}-description'));
292         $criteriontemplate .= html_writer::end_tag('tr'); // Criterion.
294         $criteriontemplate = str_replace('{NAME}', $elementname, $criteriontemplate);
295         $criteriontemplate = str_replace('{COMMENT-id}', $comment['id'], $criteriontemplate);
296         return $criteriontemplate;
297     }
298     /**
299      * This function returns html code for displaying guide template (content before and after
300      * criteria list). Depending on $mode it may be the code to edit guide, to preview the guide,
301      * to evaluate somebody or to review the evaluation.
302      *
303      * This function is called from display_guide() to display the whole guide.
304      *
305      * When overriding this function it is very important to remember that all elements of html
306      * form (in edit or evaluate mode) must have the name $elementname.
307      *
308      * Also JavaScript relies on the class names of elements and when developer changes them
309      * script might stop working.
310      *
311      * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
312      * @param array $options An array of options provided to {@link gradingform_guide_renderer::guide_edit_options()}
313      * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
314      * @param string $criteriastr evaluated templates for this guide's criteria
315      * @param string $commentstr
316      * @return string
317      */
318     protected function guide_template($mode, $options, $elementname, $criteriastr, $commentstr) {
319         $classsuffix = ''; // CSS suffix for class of the main div. Depends on the mode.
320         switch ($mode) {
321             case gradingform_guide_controller::DISPLAY_EDIT_FULL:
322                 $classsuffix = ' editor editable';
323                 break;
324             case gradingform_guide_controller::DISPLAY_EDIT_FROZEN:
325                 $classsuffix = ' editor frozen';
326                 break;
327             case gradingform_guide_controller::DISPLAY_PREVIEW:
328             case gradingform_guide_controller::DISPLAY_PREVIEW_GRADED:
329                 $classsuffix = ' editor preview';
330                 break;
331             case gradingform_guide_controller::DISPLAY_EVAL:
332                 $classsuffix = ' evaluate editable';
333                 break;
334             case gradingform_guide_controller::DISPLAY_EVAL_FROZEN:
335                 $classsuffix = ' evaluate frozen';
336                 break;
337             case gradingform_guide_controller::DISPLAY_REVIEW:
338                 $classsuffix = ' review';
339                 break;
340             case gradingform_guide_controller::DISPLAY_VIEW:
341                 $classsuffix = ' view';
342                 break;
343         }
345         $guidetemplate = html_writer::start_tag('div', array('id' => 'guide-{NAME}',
346             'class' => 'clearfix gradingform_guide'.$classsuffix));
347         $guidetemplate .= html_writer::tag('table', $criteriastr, array('class' => 'criteria', 'id' => '{NAME}-criteria'));
348         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
349             $value = get_string('addcriterion', 'gradingform_guide');
350             $input = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][addcriterion]',
351                 'id' => '{NAME}-criteria-addcriterion', 'value' => $value, 'title' => $value));
352             $guidetemplate .= html_writer::tag('div', $input, array('class' => 'addcriterion'));
353         }
355         if (!empty($commentstr)) {
356             $guidetemplate .= html_writer::tag('label', get_string('comments', 'gradingform_guide'),
357                 array('for' => '{NAME}-comments', 'class' => 'commentheader'));
358             $guidetemplate .= html_writer::tag('table', $commentstr, array('class' => 'comments', 'id' => '{NAME}-comments'));
359         }
360         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
361             $value = get_string('addcomment', 'gradingform_guide');
362             $input = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[comments][addcomment]',
363                 'id' => '{NAME}-comments-addcomment', 'value' => $value, 'title' => $value));
364             $guidetemplate .= html_writer::tag('div', $input, array('class' => 'addcomment'));
365         }
367         $guidetemplate .= $this->guide_edit_options($mode, $options);
368         $guidetemplate .= html_writer::end_tag('div');
370         return str_replace('{NAME}', $elementname, $guidetemplate);
371     }
373     /**
374      * Generates html template to view/edit the guide options. Expression {NAME} is used in
375      * template for the form element name
376      *
377      * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
378      * @param array $options
379      * @return string
380      */
381     protected function guide_edit_options($mode, $options) {
382         if ($mode != gradingform_guide_controller::DISPLAY_EDIT_FULL
383             && $mode != gradingform_guide_controller::DISPLAY_EDIT_FROZEN
384             && $mode != gradingform_guide_controller::DISPLAY_PREVIEW) {
385             // Options are displayed only for people who can manage.
386             return;
387         }
388         $html = html_writer::start_tag('div', array('class' => 'options'));
389         $html .= html_writer::tag('div', get_string('guideoptions', 'gradingform_guide'), array('class' => 'optionsheading'));
390         $attrs = array('type' => 'hidden', 'name' => '{NAME}[options][optionsset]', 'value' => 1);
391         $html .= html_writer::empty_tag('input', $attrs);
392         foreach ($options as $option => $value) {
393             $html .= html_writer::start_tag('div', array('class' => 'option '.$option));
394             $attrs = array('name' => '{NAME}[options]['.$option.']', 'id' => '{NAME}-options-'.$option);
395             switch ($option) {
396                 case 'sortlevelsasc':
397                     // Display option as dropdown.
398                     $html .= html_writer::tag('span', get_string($option, 'gradingform_guide'), array('class' => 'label'));
399                     $value = (int)(!!$value); // Make sure $value is either 0 or 1.
400                     if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
401                         $selectoptions = array(0 => get_string($option.'0', 'gradingform_guide'),
402                             1 => get_string($option.'1', 'gradingform_guide'));
403                         $valuestr = html_writer::select($selectoptions, $attrs['name'], $value, false, array('id' => $attrs['id']));
404                         $html .= html_writer::tag('span', $valuestr, array('class' => 'value'));
405                         // TODO add here button 'Sort levels'.
406                     } else {
407                         $html .= html_writer::tag('span', get_string($option.$value, 'gradingform_guide'),
408                             array('class' => 'value'));
409                         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN) {
410                             $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
411                         }
412                     }
413                     break;
414                 default:
415                     if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN && $value) {
416                         $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
417                     }
418                     // Display option as checkbox.
419                     $attrs['type'] = 'checkbox';
420                     $attrs['value'] = 1;
421                     if ($value) {
422                         $attrs['checked'] = 'checked';
423                     }
424                     if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN ||
425                         $mode == gradingform_guide_controller::DISPLAY_PREVIEW) {
426                         $attrs['disabled'] = 'disabled';
427                         unset($attrs['name']);
428                     }
429                     $html .= html_writer::empty_tag('input', $attrs);
430                     $html .= html_writer::tag('label', get_string($option, 'gradingform_guide'), array('for' => $attrs['id']));
431                     break;
432             }
433             $html .= html_writer::end_tag('div'); // Option.
434         }
435         $html .= html_writer::end_tag('div'); // Options.
436         return $html;
437     }
439     /**
440      * This function returns html code for displaying guide. Depending on $mode it may be the code
441      * to edit guide, to preview the guide, to evaluate somebody or to review the evaluation.
442      *
443      * It is very unlikely that this function needs to be overriden by theme. It does not produce
444      * any html code, it just prepares data about guide design and evaluation, adds the CSS
445      * class to elements and calls the functions level_template, criterion_template and
446      * guide_template
447      *
448      * @param array $criteria data about the guide design
449      * @param array $comments
450      * @param array $options
451      * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
452      * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
453      * @param array $values evaluation result
454      * @param array $validationerrors
455      * @return string
456      */
457     public function display_guide($criteria, $comments, $options, $mode, $elementname = null, $values = null,
458                                   $validationerrors = null) {
459         $criteriastr = '';
460         $cnt = 0;
461         foreach ($criteria as $id => $criterion) {
462             $criterion['class'] = $this->get_css_class_suffix($cnt++, count($criteria) -1);
463             $criterion['id'] = $id;
464             if (isset($values['criteria'][$id])) {
465                 $criterionvalue = $values['criteria'][$id];
466             } else {
467                 $criterionvalue = null;
468             }
469             $criteriastr .= $this->criterion_template($mode, $options, $elementname, $criterion, $criterionvalue,
470                                                       $validationerrors);
471         }
472         $cnt = 0;
473         $commentstr = '';
474         // Check if comments should be displayed.
475         if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL ||
476             $mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN ||
477             $mode == gradingform_guide_controller::DISPLAY_PREVIEW ||
478             $mode == gradingform_guide_controller::DISPLAY_EVAL ||
479             $mode == gradingform_guide_controller::DISPLAY_EVAL_FROZEN) {
481             foreach ($comments as $id => $comment) {
482                 $comment['id'] = $id;
483                 $comment['class'] = $this->get_css_class_suffix($cnt++, count($comments) -1);
484                 $commentstr  .= $this->comment_template($mode, $elementname, $comment);
485             }
486         }
487         $output = $this->guide_template($mode, $options, $elementname, $criteriastr, $commentstr);
488         if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
489             $showdesc = get_user_preferences('gradingform_guide-showmarkerdesc', true);
490             $showdescstud = get_user_preferences('gradingform_guide-showstudentdesc', true);
491             $checked1 = array();
492             $checked2 = array();
493             $checked_s1 = array();
494             $checked_s2 = array();
495             $checked = array('checked' => 'checked');
496             if ($showdesc) {
497                 $checked1 = $checked;
498             } else {
499                 $checked2 = $checked;
500             }
501             if ($showdescstud) {
502                 $checked_s1 = $checked;
503             } else {
504                 $checked_s2 = $checked;
505             }
507             $radio = html_writer::tag('input', get_string('showmarkerdesc', 'gradingform_guide'), array('type' => 'radio',
508                 'name' => 'showmarkerdesc',
509                 'value' => "true")+$checked1);
510             $radio .= html_writer::tag('input', get_string('hidemarkerdesc', 'gradingform_guide'), array('type' => 'radio',
511                 'name' => 'showmarkerdesc',
512                 'value' => "false")+$checked2);
513             $output .= html_writer::tag('div', $radio, array('class' => 'showmarkerdesc'));
515             $radio = html_writer::tag('input', get_string('showstudentdesc', 'gradingform_guide'), array('type' => 'radio',
516                 'name' => 'showstudentdesc',
517                 'value' => "true")+$checked_s1);
518             $radio .= html_writer::tag('input', get_string('hidestudentdesc', 'gradingform_guide'), array('type' => 'radio',
519                 'name' => 'showstudentdesc',
520                 'value' => "false")+$checked_s2);
521             $output .= html_writer::tag('div', $radio, array('class' => 'showstudentdesc'));
522         }
523         return $output;
524     }
526     /**
527      * Help function to return CSS class names for element (first/last/even/odd) with leading space
528      *
529      * @param int $idx index of this element in the row/column
530      * @param int $maxidx maximum index of the element in the row/column
531      * @return string
532      */
533     protected function get_css_class_suffix($idx, $maxidx) {
534         $class = '';
535         if ($idx == 0) {
536             $class .= ' first';
537         }
538         if ($idx == $maxidx) {
539             $class .= ' last';
540         }
541         if ($idx % 2) {
542             $class .= ' odd';
543         } else {
544             $class .= ' even';
545         }
546         return $class;
547     }
549     /**
550      * Displays for the student the list of instances or default content if no instances found
551      *
552      * @param array $instances array of objects of type gradingform_guide_instance
553      * @param string $defaultcontent default string that would be displayed without advanced grading
554      * @param bool $cangrade whether current user has capability to grade in this context
555      * @return string
556      */
557     public function display_instances($instances, $defaultcontent, $cangrade) {
558         $return = '';
559         if (count($instances)) {
560             $return .= html_writer::start_tag('div', array('class' => 'advancedgrade'));
561             $idx = 0;
562             foreach ($instances as $instance) {
563                 $return .= $this->display_instance($instance, $idx++, $cangrade);
564             }
565             $return .= html_writer::end_tag('div');
566         }
567         return $return. $defaultcontent;
568     }
570     /**
571      * Displays one grading instance
572      *
573      * @param gradingform_guide_instance $instance
574      * @param int $idx unique number of instance on page
575      * @param bool $cangrade whether current user has capability to grade in this context
576      */
577     public function display_instance(gradingform_guide_instance $instance, $idx, $cangrade) {
578         $criteria = $instance->get_controller()->get_definition()->guide_criteria;
579         $options = $instance->get_controller()->get_options();
580         $values = $instance->get_guide_filling();
581         if ($cangrade) {
582             $mode = gradingform_guide_controller::DISPLAY_REVIEW;
583         } else {
584             $mode = gradingform_guide_controller::DISPLAY_VIEW;
585         }
587         $output = $this->box($instance->get_controller()->get_formatted_description(), 'gradingform_guide-description').
588                   $this->display_guide($criteria, array(), $options, $mode, 'guide'.$idx, $values);
589         return $output;
590     }
593     /**
594      * Displays a confirmation message after a regrade has occured
595      *
596      * @param string $elementname
597      * @param int $changelevel
598      * @param int $value The regrade option that was used
599      * @return string
600      */
601     public function display_regrade_confirmation($elementname, $changelevel, $value) {
602         $html = html_writer::start_tag('div', array('class' => 'gradingform_guide-regrade'));
603         if ($changelevel<=2) {
604             $html .= get_string('regrademessage1', 'gradingform_guide');
605             $selectoptions = array(
606                 0 => get_string('regradeoption0', 'gradingform_guide'),
607                 1 => get_string('regradeoption1', 'gradingform_guide')
608             );
609             $html .= html_writer::select($selectoptions, $elementname.'[regrade]', $value, false);
610         } else {
611             $html .= get_string('regrademessage5', 'gradingform_guide');
612             $html .= html_writer::empty_tag('input', array('name' => $elementname.'[regrade]', 'value' => 1, 'type' => 'hidden'));
613         }
614         $html .= html_writer::end_tag('div');
615         return $html;
616     }
617     /**
618      * Generates and returns HTML code to display information box about how guide score is converted to the grade
619      *
620      * @param array $scores
621      * @return string
622      */
623     public function display_guide_mapping_explained($scores) {
624         $html = '';
625         if (!$scores) {
626             return $html;
627         }
628         if (isset($scores['modulegrade']) && $scores['maxscore'] < $scores['modulegrade']) {
629             $html .= $this->box(html_writer::tag('div', get_string('guidemappingexplained', 'gradingform_guide', (object)$scores))
630                 , 'generalbox gradingform_guide-error');
631         }
633         return $html;
634     }