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
defined('MOODLE_INTERNAL') || die;
18
 
19
require_once($CFG->libdir.'/formslib.php');
20
require_once($CFG->dirroot.'/course/modlib.php');
21
 
22
/**
23
 * Base form for changing completion rules. Used in bulk editing activity completion and editing default activity completion
24
 *
25
 * @package     core_completion
26
 * @copyright   2017 Marina Glancy
27
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28
 */
29
abstract class core_completion_edit_base_form extends moodleform {
30
 
31
    use \core_completion\form\form_trait;
32
 
33
    /** @var moodleform_mod Do not use directly, call $this->get_module_form() */
34
    protected $_moduleform = null;
35
    /** @var bool */
36
    protected $hascustomrules = false;
37
    /** @var stdClass */
38
    protected $course;
39
 
40
    /**
41
     * Returns list of types of selected module types
42
     *
43
     * @return array modname=>modfullname
44
     */
45
    abstract protected function get_module_names();
46
 
47
    /**
48
     * Get the module name. If the form have more than one modules, it will return the first one.
49
     *
50
     * @return string|null The module name or null if there is no modules associated to this form.
51
     */
52
    protected function get_module_name(): ?string {
53
        $modnames = $this->get_module_names();
54
        if (empty($modnames)) {
55
            return null;
56
        }
57
 
58
        $modnamekeys = array_keys($modnames);
59
        return reset($modnamekeys);
60
    }
61
 
62
    /**
63
     * Returns true if all selected modules support tracking view.
64
     *
65
     * @return bool
66
     */
67
    protected function support_views() {
68
        foreach ($this->get_module_names() as $modname => $modfullname) {
69
            if (!plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
70
                return false;
71
            }
72
        }
73
        return true;
74
    }
75
 
76
    /**
77
     * Returns true if all selected modules support grading.
78
     *
79
     * @return bool
80
     */
81
    protected function support_grades() {
82
        foreach ($this->get_module_names() as $modname => $modfullname) {
83
            if (!plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
84
                return false;
85
            }
86
        }
87
        return true;
88
    }
89
 
90
    /**
91
     * Returns an instance of component-specific module form for the first selected module
92
     *
93
     * @return moodleform_mod|null
94
     */
95
    abstract protected function get_module_form();
96
 
97
    /**
98
     * If all selected modules are of the same module type, adds custom completion rules from this module type
99
     *
100
     * @return array
101
     */
102
    protected function add_custom_completion(string $function): array {
103
        $modnames = array_keys($this->get_module_names());
104
 
105
        if (count($modnames) != 1 || !plugin_supports('mod', $modnames[0], FEATURE_COMPLETION_HAS_RULES, false)) {
106
            return [false, []];
107
        }
108
 
109
        $component = "mod_{$modnames[0]}";
110
        $itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component);
111
        $hascustomrules = count($itemnames) > 1;
112
        $customcompletionelements = [];
113
 
114
        try {
115
            // Add completion rules from the module form to this form.
116
            $moduleform = $this->get_module_form();
117
            // If the module doesn't return a form for any reason, we don't continue checking.
118
            if (!$moduleform) {
119
                return [false, []];
120
            }
121
            $moduleform->_form = $this->_form;
122
            if ($customcompletionelements = $moduleform->{$function}()) {
123
                $hascustomrules = true;
124
                foreach ($customcompletionelements as $customcompletionelement) {
125
                    // Instead of checking for the suffix at the end of the element name, we need to check for its presence
126
                    // because some modules, like SCORM, are adding things at the end.
127
                    if (!str_contains($customcompletionelement, $this->get_suffix())) {
128
                        debugging(
129
                            'Custom completion rule '  . $customcompletionelement . ' of module ' . $modnames[0] .
130
                            ' has wrong suffix and has been removed from the form. This has to be fixed by the developer',
131
                            DEBUG_DEVELOPER
132
                        );
133
                        if ($moduleform->_form->elementExists($customcompletionelement)) {
134
                            $moduleform->_form->removeElement($customcompletionelement);
135
                        }
136
                    }
137
                }
138
            }
139
            return [$hascustomrules, $customcompletionelements];
140
        } catch (Exception $e) {
141
            debugging('Could not add custom completion rule of module ' . $modnames[0] .
142
                ' to this form, this has to be fixed by the developer', DEBUG_DEVELOPER);
143
            return [false, $customcompletionelements];
144
        }
145
    }
146
 
147
    /**
148
     * If all selected modules are of the same module type, adds custom completion rules from this module type
149
     *
150
     * @return array
151
     */
152
    protected function add_completion_rules() {
153
        list($hascustomrules, $customcompletionelements) = $this->add_custom_completion('add_completion_rules');
154
        if (!$this->hascustomrules && $hascustomrules) {
155
            $this->hascustomrules = true;
156
        }
157
 
158
        $component = "mod_{$this->get_module_name()}";
159
        $itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component);
160
        if (count($itemnames) > 1) {
161
            $customcompletionelements[] = 'completiongradeitemnumber';
162
        }
163
 
164
        return $customcompletionelements;
165
    }
166
 
167
    /**
168
     * Checks if at least one of the custom completion rules is enabled
169
     *
170
     * @param array $data Input data (not yet validated)
171
     * @return bool True if one or more rules is enabled, false if none are;
172
     *   default returns false
173
     */
174
    protected function completion_rule_enabled($data) {
175
        if ($this->hascustomrules) {
176
            return $this->get_module_form()->completion_rule_enabled($data);
177
        }
178
        return false;
179
    }
180
 
181
    /**
182
     * If all selected modules are of the same module type, adds custom completion rules from this module type
183
     *
184
     * @return array
185
     */
186
    public function add_completiongrade_rules(): array {
187
        list($hascustomrules, $customcompletionelements) = $this->add_custom_completion('add_completiongrade_rules');
188
        if (!$this->hascustomrules && $hascustomrules) {
189
            $this->hascustomrules = true;
190
        }
191
 
192
        return $customcompletionelements;
193
    }
194
 
195
    /**
196
     * Returns list of modules that have automatic completion rules that are not shown on this form
197
     * (because they are not present in at least one other selected module).
198
     *
199
     * @return array
200
     */
201
    protected function get_modules_with_hidden_rules() {
202
        $modnames = $this->get_module_names();
203
        if (count($modnames) <= 1) {
204
            // No rules definitions conflicts if there is only one module type.
205
            return [];
206
        }
207
 
208
        $conflicts = [];
209
 
210
        if (!$this->support_views()) {
211
            // If we don't display views rule but at least one module supports it - we have conflicts.
212
            foreach ($modnames as $modname => $modfullname) {
213
                if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
214
                    $conflicts[$modname] = $modfullname;
215
                }
216
            }
217
        }
218
 
219
        if (!$this->support_grades()) {
220
            // If we don't display grade rule but at least one module supports it - we have conflicts.
221
            foreach ($modnames as $modname => $modfullname) {
222
                if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
223
                    $conflicts[$modname] = $modfullname;
224
                }
225
            }
226
        }
227
 
228
        foreach ($modnames as $modname => $modfullname) {
229
            // We do not display any custom completion rules, find modules that define them and add to conflicts list.
230
            if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_HAS_RULES, false)) {
231
                $conflicts[$modname] = $modfullname;
232
            }
233
        }
234
 
235
        return $conflicts;
236
    }
237
 
238
    /**
239
     * Form definition
240
     */
241
    public function definition() {
242
        $mform = $this->_form;
243
 
244
        // Course id.
245
        $mform->addElement('hidden', 'id', $this->course->id);
246
        $mform->setType('id', PARAM_INT);
247
 
248
        // Add the completion elements to the form.
249
        $this->add_completion_elements(
250
            $this->get_module_name(),
251
            $this->support_views(),
252
            $this->support_grades(),
253
            false,
254
            $this->course->id
255
        );
256
 
257
        if ($conflicts = $this->get_modules_with_hidden_rules()) {
258
            $mform->addElement('static', 'qwerty', '', get_string('hiddenrules', 'completion', join(', ', $conflicts)));
259
        }
260
 
261
        // Whether to show the cancel button or not in the form.
262
        $displaycancel = $this->_customdata['displaycancel'] ?? true;
263
        $this->add_action_buttons($displaycancel);
264
    }
265
 
266
    /**
267
     * Return the course module of the form, if any.
268
     *
269
     * @return cm_info|null
270
     */
271
    protected function get_cm(): ?cm_info {
272
        return null;
273
    }
274
 
275
    /**
276
     * Each module which defines definition_after_data() must call this method.
277
     */
278
    public function definition_after_data() {
279
        $this->definition_after_data_completion($this->get_cm());
280
    }
281
 
282
    /**
283
     * Form validation
284
     *
285
     * @param array $data array of ("fieldname"=>value) of submitted data
286
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
287
     * @return array of "element_name"=>"error_description" if there are errors,
288
     *         or an empty array if everything is OK (true allowed for backwards compatibility too).
289
     */
290
    public function validation($data, $files) {
291
        $errors = parent::validation($data, $files);
292
 
293
        // Completion: Check completion fields don't have errors.
294
        $errors = array_merge($errors, $this->validate_completion($data));
295
 
296
        return $errors;
297
    }
298
 
299
    /**
300
     * Returns if this form has custom completion rules. This is only possible if all selected modules have the same
301
     * module type and this module type supports custom completion rules
302
     *
303
     * @return bool
304
     */
305
    public function has_custom_completion_rules() {
306
        return $this->hascustomrules;
307
    }
308
 
309
    /**
310
     * Return submitted data if properly submitted or returns NULL if validation fails or
311
     * if there is no submitted data.
312
     *
313
     * @return object submitted data; NULL if not valid or not submitted or cancelled
314
     */
315
    public function get_data() {
316
        $data = parent::get_data();
317
        if ($data && $this->hascustomrules) {
318
            $this->get_module_form()->data_postprocessing($data);
319
        }
320
        return $data;
321
    }
322
}