Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
require_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
 */
36
class 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
     */
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
     */
65
    public function moodlequickform_guideeditor($elementname=null, $elementlabel=null, $attributes=null) {
66
        debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
67
        self::__construct($elementname, $elementlabel, $attributes);
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',
113
                'requires' => array('base', 'dom', 'event', 'event-touch', 'escape'),
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) {
140
            $html .= html_writer::div($renderer->notification($this->validationerrors));
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;
208
                    $criterion['error_description'] = true;
209
                }
210
                if (strlen(trim($criterion['shortname'])) > 255) {
211
                    $errors['err_shortnametoolong'] = 1;
212
                    $criterion['error_description'] = true;
213
                }
214
                if (!strlen(trim($criterion['maxscore']))) {
215
                    $errors['err_nomaxscore'] = 1;
216
                    $criterion['error_description'] = true;
217
                } else if (!is_numeric($criterion['maxscore'])) {
218
                    $errors['err_maxscorenotnumeric'] = 1;
219
                    $criterion['error_description'] = true;
220
                } else if ($criterion['maxscore'] < 0) {
221
                    $errors['err_maxscoreisnegative'] = 1;
222
                    $criterion['error_description'] = true;
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
}