Differences Between: [Versions 400 and 402] [Versions 400 and 403]
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 public function restore_instance_data_from_backup(\restore_task $task, array $data): void { 308 309 $editablefields = $this->get_editable_fields($data['newquestion']); 310 $records = api::get_instance_fields_data($editablefields, $data['newquestion']); 311 $target = $task->get_target(); 312 $override = ($target != \backup::TARGET_CURRENT_ADDING && $target != \backup::TARGET_EXISTING_ADDING); 313 314 foreach ($records as $d) { 315 $field = $d->get_field(); 316 if ($field->get('shortname') === $data['shortname'] && $field->get('type') === $data['type']) { 317 if (!$d->get('id') || $override) { 318 $d->set($d->datafield(), $data['value']); 319 $d->set('value', $data['value']); 320 $d->set('valueformat', $data['valueformat']); 321 $d->set('contextid', $data['fieldcontextid']); 322 $d->save(); 323 } 324 return; 325 } 326 } 327 } 328 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body