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 qbank_customfields\customfield;
18
 
19
use core_customfield\api;
20
use core_customfield\field_controller;
21
use core_customfield\output\field_data;
22
 
23
/**
24
 * Question handler for custom fields.
25
 *
26
 * @package     qbank_customfields
27
 * @copyright   2021 Catalyst IT Australia Pty Ltd
28
 * @author      Matt Porritt <mattp@catalyst-au.net>
29
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
 */
31
class question_handler extends \core_customfield\handler {
32
 
33
    /**
34
     * @var question_handler
35
     */
36
    static protected $singleton;
37
 
38
    /**
39
     * @var \context
40
     */
41
    protected $parentcontext;
42
 
43
    /** @var int Field is displayed in the question display and question preview, visible to everybody */
44
    const VISIBLETOALL = 2;
45
    /** @var int Field is displayed in the question display and question preview but only for "teachers" */
46
    const VISIBLETOTEACHERS = 1;
47
    /** @var int Field is not displayed in the question display and question preview */
48
    const NOTVISIBLE = 0;
49
 
50
    /**
51
     * Creates the custom field handler and returns a singleton.
52
     * Itemid is always zero as the custom fields are the same
53
     * for every question across the system.
54
     *
55
     * @param int $itemid Always zero.
56
     * @return \qbank_customfields\customfield\question_handler
57
     */
58
    public static function create(int $itemid = 0): \core_customfield\handler {
59
        if (static::$singleton === null) {
60
            self::$singleton = new static(0);
61
        }
62
        return self::$singleton;
63
    }
64
 
65
    /**
66
     * Run reset code after unit tests to reset the singleton usage.
67
     */
68
    public static function reset_caches(): void {
69
        if (!PHPUNIT_TEST) {
70
            throw new \coding_exception('This feature is only intended for use in unit tests');
71
        }
72
 
73
        static::$singleton = null;
74
    }
75
 
76
    /**
77
     * The current user can configure custom fields on this component.
78
     *
79
     * @return bool true if the current can configure custom fields, false otherwise
80
     */
81
    public function can_configure(): bool {
82
        return has_capability('qbank/customfields:configurecustomfields', $this->get_configuration_context());
83
    }
84
 
85
    /**
86
     * The current user can edit custom fields for the given question.
87
     *
88
     * @param field_controller $field
89
     * @param int $instanceid id of the question to test edit permission
90
     * @return bool true if the current can edit custom fields, false otherwise
91
     */
92
    public function can_edit(field_controller $field, int $instanceid = 0): bool {
93
        if ($instanceid) {
94
            $context = $this->get_instance_context($instanceid);
95
        } else {
96
            $context = $this->get_parent_context();
97
        }
98
 
99
        return (!$field->get_configdata_property('locked') ||
100
                has_capability('qbank/customfields:changelockedcustomfields', $context));
101
    }
102
 
103
    /**
104
     * The current user can view custom fields for the given question.
105
     *
106
     * @param field_controller $field
107
     * @param int $instanceid id of the question to test edit permission
108
     * @return bool true if the current can edit custom fields, false otherwise
109
     */
110
    public function can_view(field_controller $field, int $instanceid): bool {
111
        $visibility = $field->get_configdata_property('visibility');
112
        if ($visibility == self::NOTVISIBLE) {
113
            return false;
114
        } else if ($visibility == self::VISIBLETOTEACHERS) {
115
            return has_capability('qbank/customfields:viewhiddencustomfields', $this->get_instance_context($instanceid));
116
        } else {
117
            return true;
118
        }
119
    }
120
 
121
    /**
122
     * Determine if the current user can view custom field in their given context.
123
     * This determines if the user can see the field at all not just the field for
124
     * a particular instance.
125
     * Used primarily in showing or not the field in the question bank table.
126
     *
127
     * @param field_controller $field The field trying to be viewed.
128
     * @param \context $context The context the field is being displayed in.
129
     * @return bool true if the current can edit custom fields, false otherwise.
130
     */
131
    public function can_view_type(field_controller $field, \context $context): bool {
132
        $visibility = $field->get_configdata_property('visibility');
133
        if ($visibility == self::NOTVISIBLE) {
134
            return false;
135
        } else if ($visibility == self::VISIBLETOTEACHERS) {
136
            return has_capability('qbank/customfields:viewhiddencustomfields', $context);
137
        } else {
138
            return true;
139
        }
140
    }
141
 
142
    /**
143
     * Sets parent context for the question.
144
     *
145
     * This may be needed when question is being created, there is no question context but we need to check capabilities
146
     *
147
     * @param \context $context
148
     */
149
    public function set_parent_context(\context $context): void {
150
        $this->parentcontext = $context;
151
    }
152
 
153
    /**
154
     * Returns the parent context for the question.
155
     *
156
     * @return \context
157
     */
158
    protected function get_parent_context(): \context {
159
        if ($this->parentcontext) {
160
            return $this->parentcontext;
161
        } else {
162
            return \context_system::instance();
163
        }
164
    }
165
 
166
    /**
167
     * Context that should be used for new categories created by this handler.
168
     *
169
     * @return \context the context for configuration
170
     */
171
    public function get_configuration_context(): \context {
172
        return \context_system::instance();
173
    }
174
 
175
    /**
176
     * URL for configuration page for the fields for the question custom fields.
177
     *
178
     * @return \moodle_url The URL to configure custom fields for this component
179
     */
180
    public function get_configuration_url(): \moodle_url {
181
        return new \moodle_url('/question/customfield.php');
182
    }
183
 
184
    /**
185
     * Returns the context for the data associated with the given instanceid.
186
     *
187
     * @param int $instanceid id of the record to get the context for
188
     * @return \context the context for the given record
189
     * @throws \coding_exception
190
     */
191
    public function get_instance_context(int $instanceid = 0): \context {
192
        if ($instanceid > 0) {
193
            $questiondata = \question_bank::load_question_data($instanceid);
194
            $contextid = $questiondata->contextid;
195
            $context = \context::instance_by_id($contextid);
196
            return $context;
197
        } else {
198
            throw new \coding_exception('Instance id must be provided.');
199
        }
200
    }
201
 
202
    /**
203
     * Given a field and instance id get all the filed data.
204
     *
205
     * @param field_controller $field The field to get the data for.
206
     * @param int $instanceid The instance id to get the data for.
207
     * @return \core_customfield\data_controller The fetched data.
208
     */
209
    public function get_field_data(\core_customfield\field_controller $field, int $instanceid): \core_customfield\data_controller {
210
        $fields = [$field->get('id') => $field];
211
        $fieldsdata = api::get_instance_fields_data($fields, $instanceid);
212
        return $fieldsdata[$field->get('id')];
213
    }
214
 
215
    /**
216
     * For a given instance id (question id) get the categories and the
217
     * fields with any data. Return an array of categories containing an
218
     * array of field names and values that is ready to be passed to a renderer.
219
     *
220
     * @param int $instanceid The instance id to get the data for.
221
     * @return array $cfdata The fetched data
222
     */
223
    public function get_categories_fields_data(int $instanceid): array {
224
        // Prepare custom fields data.
225
        $instancedata = $this->get_instance_data($instanceid);
226
 
227
        $cfdata = [];
228
 
229
        foreach ($instancedata as $instance) {
230
            $field = $instance->get_field();
231
 
232
            if ($this->can_view($field, $instanceid)) {
233
                $category = $instance->get_field()->get_category()->get('name');
234
                $fieldname = $field->get_formatted_name();
235
                $fieldvalue = $this->get_field_data($field, $instanceid)->export_value();
236
                $cfdata[$category][] = ['name' => $fieldname,  'value' => $fieldvalue];
237
            }
238
 
239
        }
240
 
241
        return $cfdata;
242
    }
243
 
244
    /**
245
     * Get the custom data for the given field
246
     * and render HTML ready for display in question table.
247
     *
248
     * @param object $fielddata The field data used for display.
249
     * @return string The HTML to display in the table column.
250
     */
251
    public function display_custom_field_table(object $fielddata): string {
252
        global $PAGE;
253
 
254
        $output = $PAGE->get_renderer('qbank_customfields');
255
        $outputdata = new field_data($fielddata);
256
 
257
        return $output->render_for_table($outputdata);
258
    }
259
 
260
    /**
261
     * Render the custom field category and filed data as HTML ready for display.
262
     *
263
     * @param array $catfielddata Array of categories and field names and values.
264
     * @return string The HTML to display.
265
     */
266
    public function display_custom_categories_fields(array $catfielddata): string {
267
        global $PAGE;
268
        $output = $PAGE->get_renderer('qbank_customfields');
269
 
270
        return $output->render_for_preview($catfielddata);
271
    }
272
 
273
    /**
274
     * Add custom controls to the field configuration form that will be saved.
275
     *
276
     * @param \MoodleQuickForm $mform The form to add the custom fields to.
277
     */
278
    public function config_form_definition(\MoodleQuickForm $mform): void {
279
        $mform->addElement('header', 'question_handler_header',
280
                get_string('customfieldsettings', 'qbank_customfields'));
281
        $mform->setExpanded('question_handler_header', true);
282
 
283
        // If field is locked.
284
        $mform->addElement('selectyesno', 'configdata[locked]',
285
                get_string('customfield_islocked', 'qbank_customfields'));
286
        $mform->addHelpButton('configdata[locked]', 'customfield_islocked', 'qbank_customfields');
287
 
288
        // Field data visibility.
289
        $visibilityoptions = [
290
                self::VISIBLETOALL => get_string('customfield_visibletoall', 'qbank_customfields'),
291
                self::VISIBLETOTEACHERS => get_string('customfield_visibletoteachers', 'qbank_customfields'),
292
                self::NOTVISIBLE => get_string('customfield_notvisible', 'qbank_customfields')
293
        ];
294
        $mform->addElement('select', 'configdata[visibility]',
295
                get_string('customfield_visibility', 'qbank_customfields'),
296
                $visibilityoptions);
297
        $mform->addHelpButton(
298
                'configdata[visibility]', 'customfield_visibility', 'qbank_customfields');
299
    }
300
 
301
    /**
302
     * Creates or updates the question custom field data when restoring from a backup.
303
     *
304
     * @param \restore_task $task
305
     * @param array $data
306
     *
307
     * @return int|void Conditionally returns the ID of the created or updated record.
308
     */
309
    public function restore_instance_data_from_backup(\restore_task $task, array $data) {
310
 
311
        $editablefields = $this->get_editable_fields($data['newquestion']);
312
        $records = api::get_instance_fields_data($editablefields, $data['newquestion']);
313
        $target = $task->get_target();
314
        $override = ($target != \backup::TARGET_CURRENT_ADDING && $target != \backup::TARGET_EXISTING_ADDING);
315
 
316
        foreach ($records as $d) {
317
            $field = $d->get_field();
318
            if ($field->get('shortname') === $data['shortname'] && $field->get('type') === $data['type']) {
319
                if (!$d->get('id') || $override) {
320
                    $d->set($d->datafield(), $data['value']);
321
                    $d->set('value', $data['value']);
322
                    $d->set('valueformat', $data['valueformat']);
323
                    $d->set('valuetrust', !empty($data['valuetrust']));
324
                    $d->set('contextid', $data['fieldcontextid']);
325
                    $d->save();
326
                }
327
                return $d->get('id');
328
            }
329
        }
330
    }
331
}