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
namespace core_grades\form;
18
 
19
defined('MOODLE_INTERNAL') || die;
20
 
21
use context;
22
use context_course;
23
use core_form\dynamic_form;
24
use grade_category;
25
use grade_item;
26
use grade_outcome;
27
use grade_plugin_return;
28
use moodle_url;
29
 
30
require_once($CFG->dirroot.'/grade/lib.php');
31
 
32
/**
33
 * Prints the add outcome gradebook form.
34
 *
35
 * @copyright 2023 Mathew May <mathew.solutions>
36
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
37
 * @package core_grades
38
 */
39
class add_outcome extends dynamic_form {
40
 
41
    /** Grade plugin return tracking object.
42
     * @var object $gpr
43
     */
44
    public $gpr;
45
 
46
    /**
47
     * Helper function to grab the current grade outcome item based on information within the form.
48
     *
49
     * @return array
50
     * @throws \moodle_exception
51
     */
52
    private function get_gradeitem(): array {
53
        $courseid = $this->optional_param('courseid', null, PARAM_INT);
54
        $id = $this->optional_param('itemid', null, PARAM_INT);
55
 
56
        if ($gradeitem = grade_item::fetch(['id' => $id, 'courseid' => $courseid])) {
57
            // Redirect if outcomeid not present.
58
            if (empty($gradeitem->outcomeid)) {
59
                $url = new moodle_url('/grade/edit/tree/item.php', ['id' => $id, 'courseid' => $courseid]);
60
                redirect($this->gpr->add_url_params($url));
61
            }
62
            $item = $gradeitem->get_record_data();
63
            $parentcategory = $gradeitem->get_parent_category();
64
            if ($item->itemtype == 'mod') {
65
                $cm = get_coursemodule_from_instance($item->itemmodule, $item->iteminstance, $item->courseid);
66
                $item->cmid = $cm->id;
67
            } else {
68
                $item->cmid = 0;
69
            }
70
        } else {
71
            $gradeitem = new grade_item(['courseid' => $courseid, 'itemtype' => 'manual'], false);
72
            $item = $gradeitem->get_record_data();
73
            $parentcategory = grade_category::fetch_course_category($courseid);
74
        }
75
        $item->parentcategory = $parentcategory->id;
76
 
77
        if ($item->hidden > 1) {
78
            $item->hiddenuntil = $item->hidden;
79
            $item->hidden = 0;
80
        } else {
81
            $item->hiddenuntil = 0;
82
        }
83
 
84
        $item->locked = !empty($item->locked);
85
 
86
        if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM || $parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
87
            $item->aggregationcoef = $item->aggregationcoef == 0 ? 0 : 1;
88
        } else {
89
            $item->aggregationcoef = format_float($item->aggregationcoef, 4);
90
        }
91
        if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
92
            $item->aggregationcoef2 = format_float($item->aggregationcoef2 * 100.0);
93
        }
94
        $item->cancontrolvisibility = $gradeitem->can_control_visibility();
95
        return [
96
            'gradeitem' => $gradeitem,
97
            'item' => $item
98
        ];
99
    }
100
 
101
    /**
102
     * Form definition
103
     *
104
     * @return void
105
     * @throws \coding_exception
106
     * @throws \dml_exception
107
     * @throws \moodle_exception
108
     */
109
    protected function definition() {
110
        $courseid = $this->optional_param('courseid', null, PARAM_INT);
111
        $id = $this->optional_param('itemid', 0, PARAM_INT);
112
        $gprplugin = $this->optional_param('gpr_plugin', '', PARAM_TEXT);
113
 
114
        if ($gprplugin && ($gprplugin !== 'tree')) {
115
            $this->gpr = new grade_plugin_return(['type' => 'report', 'plugin' => $gprplugin, 'courseid' => $courseid]);
116
        } else {
117
            $this->gpr = new grade_plugin_return(['type' => 'edit', 'plugin' => 'tree', 'courseid' => $courseid]);
118
        }
119
 
120
        $mform =& $this->_form;
121
 
122
        $local = $this->get_gradeitem();
123
        $gradeitem = $local['gradeitem'];
124
        $item = $local['item'];
125
 
126
        // Hidden elements.
127
        $mform->addElement('hidden', 'id', 0);
128
        $mform->setType('id', PARAM_INT);
129
        $mform->addElement('hidden', 'courseid', $courseid);
130
        $mform->setType('courseid', PARAM_INT);
131
        $mform->addElement('hidden', 'itemid', $id);
132
        $mform->setType('itemid', PARAM_INT);
133
 
134
        // Allow setting of outcomes on module items too.
135
        $outcomeoptions = [];
136
        if ($outcomes = grade_outcome::fetch_all_available($courseid)) {
137
            foreach ($outcomes as $outcome) {
138
                $outcomeoptions[$outcome->id] = $outcome->get_name();
139
            }
140
        }
141
 
142
        // Visible elements.
143
        $mform->addElement('text', 'itemname', get_string('itemname', 'grades'));
144
        $mform->addRule('itemname', get_string('required'), 'required', null, 'client');
145
        $mform->setType('itemname', PARAM_TEXT);
146
 
147
        $mform->addElement('selectwithlink', 'outcomeid', get_string('outcome', 'grades'), $outcomeoptions);
148
        $mform->addHelpButton('outcomeid', 'outcome', 'grades');
149
        $mform->addRule('outcomeid', get_string('required'), 'required');
150
 
151
        $options = [0 => get_string('none')];
152
        if ($coursemods = get_course_mods($courseid)) {
153
            foreach ($coursemods as $coursemod) {
154
                if ($mod = get_coursemodule_from_id($coursemod->modname, $coursemod->id)) {
155
                    $options[$coursemod->id] = format_string($mod->name);
156
                }
157
            }
158
        }
159
        $mform->addElement('select', 'cmid', get_string('linkedactivity', 'grades'), $options);
160
        $mform->addHelpButton('cmid', 'linkedactivity', 'grades');
161
        $mform->setDefault('cmid', 0);
162
 
163
        // Hiding.
164
        $mform->addElement('checkbox', 'hidden', get_string('hidden', 'grades'));
165
        $mform->addHelpButton('hidden', 'hidden', 'grades');
166
 
167
        // Locking.
168
        $mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades'));
169
        $mform->addHelpButton('locked', 'locked', 'grades');
170
 
171
        // Parent category related settings.
172
        $mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades'));
173
        $mform->addHelpButton('weightoverride', 'weightoverride', 'grades');
174
 
175
        $mform->addElement('text', 'aggregationcoef2', get_string('weight', 'grades'));
176
        $mform->addHelpButton('aggregationcoef2', 'weight', 'grades');
177
        $mform->setType('aggregationcoef2', PARAM_RAW);
178
        $mform->hideIf('aggregationcoef2', 'weightoverride');
179
 
180
        $options = [];
181
        $coefstring = '';
182
        $categories = grade_category::fetch_all(['courseid' => $courseid]);
183
        foreach ($categories as $cat) {
184
            $cat->apply_forced_settings();
185
            $options[$cat->id] = $cat->get_name();
186
            if ($cat->is_aggregationcoef_used()) {
187
                if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
188
                    $coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefweight') ?
189
                        'aggregationcoefweight' : 'aggregationcoef';
190
                } else if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
191
                    $coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefextrasum') ?
192
                        'aggregationcoefextrasum' : 'aggregationcoef';
193
                } else if ($cat->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
194
                    $coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefextraweight') ?
195
                        'aggregationcoefextraweight' : 'aggregationcoef';
196
                } else if ($cat->aggregation == GRADE_AGGREGATE_SUM) {
197
                    $coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefextrasum') ?
198
                        'aggregationcoefextrasum' : 'aggregationcoef';
199
                } else {
200
                    $coefstring = 'aggregationcoef';
201
                }
202
            } else {
203
                $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $cat->id);
204
            }
205
        }
206
 
207
        if (count($categories) > 1) {
208
            $mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options);
209
            $mform->disabledIf('parentcategory', 'cmid', 'noteq', 0);
210
        }
211
 
212
        if ($coefstring !== '') {
213
            if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') {
214
                $coefstring = 'aggregationcoefextrasum';
215
                $mform->addElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
216
            } else {
217
                $mform->addElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
218
            }
219
            $mform->addHelpButton('aggregationcoef', $coefstring, 'grades');
220
        }
221
 
222
        // Remove the aggregation coef element if not needed.
223
        if ($gradeitem->is_course_item()) {
224
            if ($mform->elementExists('parentcategory')) {
225
                $mform->removeElement('parentcategory');
226
            }
227
            if ($mform->elementExists('aggregationcoef')) {
228
                $mform->removeElement('aggregationcoef');
229
            }
230
 
231
        } else {
232
            // If we wanted to change parent of existing item - we would have to verify there are no circular references in parents.
233
            if ($id > -1 && $mform->elementExists('parentcategory')) {
234
                $mform->hardFreeze('parentcategory');
235
            }
236
 
237
            $parentcategory = $gradeitem->get_parent_category();
238
            if (!$parentcategory) {
239
                // If we do not have an id, we are creating a new grade item.
240
 
241
                // Assign the course category to this grade item.
242
                $parentcategory = grade_category::fetch_course_category($courseid);
243
                $gradeitem->parent_category = $parentcategory;
244
            }
245
 
246
            $parentcategory->apply_forced_settings();
247
 
248
            if (!$parentcategory->is_aggregationcoef_used() || !$parentcategory->aggregateoutcomes) {
249
                if ($mform->elementExists('aggregationcoef')) {
250
                    $mform->removeElement('aggregationcoef');
251
                }
252
            } else {
253
                // Fix label if needed.
254
                $agg_el =& $mform->getElement('aggregationcoef');
255
                $aggcoef = '';
256
                if ($parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
257
                    $aggcoef = 'aggregationcoefweight';
258
 
259
                } else if ($parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
260
                    $aggcoef = 'aggregationcoefextrasum';
261
 
262
                } else if ($parentcategory->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
263
                    $aggcoef = 'aggregationcoefextraweight';
264
 
265
                } else if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
266
                    $aggcoef = 'aggregationcoefextrasum';
267
                }
268
 
269
                if ($aggcoef !== '') {
270
                    $agg_el->setLabel(get_string($aggcoef, 'grades'));
271
                    $mform->addHelpButton('aggregationcoef', $aggcoef, 'grades');
272
                }
273
            }
274
 
275
            // Remove the natural weighting fields for other aggregations,
276
            // or when the category does not aggregate outcomes.
277
            if ($parentcategory->aggregation != GRADE_AGGREGATE_SUM ||
278
                !$parentcategory->aggregateoutcomes) {
279
                if ($mform->elementExists('weightoverride')) {
280
                    $mform->removeElement('weightoverride');
281
                }
282
                if ($mform->elementExists('aggregationcoef2')) {
283
                    $mform->removeElement('aggregationcoef2');
284
                }
285
            }
286
        }
287
 
288
        $url = new moodle_url('/grade/edit/tree/outcomeitem.php', ['id' => $id, 'courseid' => $courseid]);
289
        $url = $this->gpr->add_url_params($url);
290
        $url = '<a class="showadvancedform" href="' . $url . '">' . get_string('showmore', 'form') .'</a>';
291
        $mform->addElement('static', 'advancedform', $url);
292
 
293
        // Add return tracking info.
294
        $this->gpr->add_mform_elements($mform);
295
 
296
        $this->set_data($item);
297
    }
298
 
299
    /**
300
     * Return form context
301
     *
302
     * @return context
303
     */
304
    protected function get_context_for_dynamic_submission(): context {
305
        $courseid = $this->optional_param('courseid', null, PARAM_INT);
306
        return context_course::instance($courseid);
307
    }
308
 
309
    /**
310
     * Check if current user has access to this form, otherwise throw exception
311
     *
312
     * @return void
313
     * @throws \required_capability_exception
314
     */
315
    protected function check_access_for_dynamic_submission(): void {
316
        $courseid = $this->optional_param('courseid', null, PARAM_INT);
317
        require_capability('moodle/grade:manage', context_course::instance($courseid));
318
    }
319
 
320
    /**
321
     * Load in existing data as form defaults
322
     *
323
     * @return void
324
     */
325
    public function set_data_for_dynamic_submission(): void {
326
        $this->set_data((object)[
327
            'courseid' => $this->optional_param('courseid', null, PARAM_INT),
328
            'itemid' => $this->optional_param('itemid', null, PARAM_INT)
329
        ]);
330
    }
331
 
332
    /**
333
     * Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
334
     *
335
     * @return moodle_url
336
     * @throws \moodle_exception
337
     */
338
    protected function get_page_url_for_dynamic_submission(): moodle_url {
339
        $params = [
340
            'id' => $this->optional_param('courseid', null, PARAM_INT),
341
            'itemid' => $this->optional_param('itemid', null, PARAM_INT),
342
        ];
343
        return new moodle_url('/grade/edit/tree/index.php', $params);
344
    }
345
 
346
    /**
347
     * Process the form submission, used if form was submitted via AJAX
348
     *
349
     * @return array
350
     * @throws \moodle_exception
351
     */
352
    public function process_dynamic_submission() {
353
        global $DB;
354
        $data = $this->get_data();
355
 
356
        $url = $this->gpr->get_return_url('index.php?id=' . $data->courseid);
357
        $local = $this->get_gradeitem();
358
        $gradeitem = $local['gradeitem'];
359
        $item = $local['item'];
360
        $parentcategory = grade_category::fetch_course_category($data->courseid);
361
 
362
        // Form submission handling.
363
        // If unset, give the aggregation values a default based on parent aggregation method.
364
        $defaults = grade_category::get_default_aggregation_coefficient_values($parentcategory->aggregation);
365
        if (!isset($data->aggregationcoef) || $data->aggregationcoef == '') {
366
            $data->aggregationcoef = $defaults['aggregationcoef'];
367
        }
368
        if (!isset($data->weightoverride)) {
369
            $data->weightoverride = $defaults['weightoverride'];
370
        }
371
 
372
        if (property_exists($data, 'calculation')) {
373
            $data->calculation = grade_item::normalize_formula($data->calculation, $data->courseid);
374
        }
375
 
376
        $hide = empty($data->hiddenuntil) ? 0 : $data->hiddenuntil;
377
        if (!$hide) {
378
            $hide = empty($data->hidden) ? 0 : $data->hidden;
379
        }
380
 
381
        $locked   = empty($data->locked) ? 0 : $data->locked;
382
        $locktime = empty($data->locktime) ? 0 : $data->locktime;
383
 
384
        $convert = ['gradepass', 'aggregationcoef', 'aggregationcoef2'];
385
        foreach ($convert as $param) {
386
            if (property_exists($data, $param)) {
387
                $data->$param = unformat_float($data->$param);
388
            }
389
        }
390
        if (isset($data->aggregationcoef2) && $parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
391
            $data->aggregationcoef2 = $data->aggregationcoef2 / 100.0;
392
        } else {
393
            $data->aggregationcoef2 = $defaults['aggregationcoef2'];
394
        }
395
 
396
        grade_item::set_properties($gradeitem, $data);
397
 
398
        // Link this outcome item to the user specified linked activity.
399
        if (empty($data->cmid) || $data->cmid == 0) {
400
            // Manual item.
401
            $gradeitem->itemtype     = 'manual';
402
            $gradeitem->itemmodule   = null;
403
            $gradeitem->iteminstance = null;
404
            $gradeitem->itemnumber   = 0;
405
 
406
        } else {
407
            $params = [$data->cmid];
408
            $module = $DB->get_record_sql("SELECT cm.*, m.name as modname
409
                                    FROM {modules} m, {course_modules} cm
410
                                   WHERE cm.id = ? AND cm.module = m.id ", $params);
411
            $gradeitem->itemtype     = 'mod';
412
            $gradeitem->itemmodule   = $module->modname;
413
            $gradeitem->iteminstance = $module->instance;
414
 
415
            if ($items = grade_item::fetch_all(['itemtype' => 'mod', 'itemmodule' => $gradeitem->itemmodule,
416
                'iteminstance' => $gradeitem->iteminstance, 'courseid' => $data->courseid])) {
417
                if (!empty($gradeitem->id) && in_array($gradeitem, $items)) {
418
                    // No change needed.
419
                } else {
420
                    $max = 999;
421
                    foreach ($items as $item) {
422
                        if (empty($item->outcomeid)) {
423
                            continue;
424
                        }
425
                        if ($item->itemnumber > $max) {
426
                            $max = $item->itemnumber;
427
                        }
428
                    }
429
                    $gradeitem->itemnumber = $max + 1;
430
                }
431
            } else {
432
                $gradeitem->itemnumber = 1000;
433
            }
434
        }
435
 
436
        // Fix scale used.
437
        $outcome = grade_outcome::fetch(['id' => $data->outcomeid]);
438
        $gradeitem->gradetype = GRADE_TYPE_SCALE;
439
        $gradeitem->scaleid = $outcome->scaleid; // TODO: we might recalculate existing outcome grades when changing scale.
440
 
441
        if (empty($gradeitem->id)) {
442
            $gradeitem->insert();
443
            // Move next to activity if adding linked outcome.
444
            if ($gradeitem->itemtype == 'mod') {
445
                if ($linkeditem = grade_item::fetch(['itemtype' => 'mod', 'itemmodule' => $gradeitem->itemmodule,
446
                    'iteminstance' => $gradeitem->iteminstance, 'itemnumber' => 0, 'courseid' => $data->courseid])) {
447
                    $gradeitem->set_parent($linkeditem->categoryid);
448
                    $gradeitem->move_after_sortorder($linkeditem->sortorder);
449
                }
450
            } else {
451
                // Set parent if needed.
452
                if (isset($data->parentcategory)) {
453
                    $gradeitem->set_parent($data->parentcategory, false);
454
                }
455
            }
456
 
457
        } else {
458
            $gradeitem->update();
459
        }
460
 
461
        if ($item->cancontrolvisibility) {
462
            // Update hiding flag.
463
            $gradeitem->set_hidden($hide, true);
464
        }
465
 
466
        $gradeitem->set_locktime($locktime); // Locktime first - it might be removed when unlocking.
467
        $gradeitem->set_locked($locked, false, true);
468
        return [
469
            'result' => true,
470
            'url' => $url,
471
            'errors' => [],
472
        ];
473
    }
474
 
475
    /**
476
     * Form validation.
477
     *
478
     * @param array $data array of ("fieldname"=>value) of submitted data
479
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
480
     * @return array of "element_name"=>"error_description" if there are errors,
481
     *         or an empty array if everything is OK (true allowed for backwards compatibility too).
482
     */
483
    public function validation($data, $files): array {
484
        $errors = [];
485
        $local = $this->get_gradeitem();
486
        $gradeitem = $local['gradeitem'];
487
        $item = $local['item'];
488
 
489
        if (!grade_verify_idnumber($gradeitem->id, $item->courseid, $gradeitem)) {
490
            $errors['idnumber'] = get_string('idnumbertaken');
491
        }
492
        return $errors;
493
    }
494
}