Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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 /** 18 * This file contains the profile_define_base class. 19 * 20 * @package core_user 21 * @copyright 2007 onwards Shane Elliot {@link http://pukunui.com} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 /** 26 * Class profile_define_base 27 * 28 * @copyright 2007 onwards Shane Elliot {@link http://pukunui.com} 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class profile_define_base { 32 33 /** 34 * Prints out the form snippet for creating or editing a profile field 35 * @param moodleform $form instance of the moodleform class 36 */ 37 public function define_form(&$form) { 38 $form->addElement('header', '_commonsettings', get_string('profilecommonsettings', 'admin')); 39 $this->define_form_common($form); 40 41 $form->addElement('header', '_specificsettings', get_string('profilespecificsettings', 'admin')); 42 $this->define_form_specific($form); 43 } 44 45 /** 46 * Prints out the form snippet for the part of creating or editing a profile field common to all data types. 47 * 48 * @param moodleform $form instance of the moodleform class 49 */ 50 public function define_form_common(&$form) { 51 52 $strrequired = get_string('required'); 53 54 // Accepted values for 'shortname' would follow [a-zA-Z0-9_] pattern, 55 // but we are accepting any PARAM_TEXT value here, 56 // and checking [a-zA-Z0-9_] pattern in define_validate_common() function to throw an error when needed. 57 $form->addElement('text', 'shortname', get_string('profileshortname', 'admin'), 'maxlength="100" size="25"'); 58 $form->addRule('shortname', $strrequired, 'required', null, 'client'); 59 $form->setType('shortname', PARAM_TEXT); 60 61 $form->addElement('text', 'name', get_string('profilename', 'admin'), 'size="50"'); 62 $form->addRule('name', $strrequired, 'required', null, 'client'); 63 $form->setType('name', PARAM_TEXT); 64 65 $form->addElement('editor', 'description', get_string('profiledescription', 'admin'), null, null); 66 67 $form->addElement('selectyesno', 'required', get_string('profilerequired', 'admin')); 68 69 $form->addElement('selectyesno', 'locked', get_string('profilelocked', 'admin')); 70 71 $form->addElement('selectyesno', 'forceunique', get_string('profileforceunique', 'admin')); 72 73 $form->addElement('selectyesno', 'signup', get_string('profilesignup', 'admin')); 74 75 $choices = array(); 76 $choices[PROFILE_VISIBLE_NONE] = get_string('profilevisiblenone', 'admin'); 77 $choices[PROFILE_VISIBLE_PRIVATE] = get_string('profilevisibleprivate', 'admin'); 78 $choices[PROFILE_VISIBLE_ALL] = get_string('profilevisibleall', 'admin'); 79 $form->addElement('select', 'visible', get_string('profilevisible', 'admin'), $choices); 80 $form->addHelpButton('visible', 'profilevisible', 'admin'); 81 $form->setDefault('visible', PROFILE_VISIBLE_ALL); 82 83 $choices = profile_list_categories(); 84 $form->addElement('select', 'categoryid', get_string('profilecategory', 'admin'), $choices); 85 } 86 87 /** 88 * Prints out the form snippet for the part of creating or editing a profile field specific to the current data type. 89 * @param moodleform $form instance of the moodleform class 90 */ 91 public function define_form_specific($form) { 92 // Do nothing - overwrite if necessary. 93 } 94 95 /** 96 * Validate the data from the add/edit profile field form. 97 * 98 * Generally this method should not be overwritten by child classes. 99 * 100 * @param stdClass|array $data from the add/edit profile field form 101 * @param array $files 102 * @return array associative array of error messages 103 */ 104 public function define_validate($data, $files) { 105 106 $data = (object)$data; 107 $err = array(); 108 109 $err += $this->define_validate_common($data, $files); 110 $err += $this->define_validate_specific($data, $files); 111 112 return $err; 113 } 114 115 /** 116 * Validate the data from the add/edit profile field form that is common to all data types. 117 * 118 * Generally this method should not be overwritten by child classes. 119 * 120 * @param stdClass|array $data from the add/edit profile field form 121 * @param array $files 122 * @return array associative array of error messages 123 */ 124 public function define_validate_common($data, $files) { 125 global $DB; 126 127 $err = array(); 128 129 // Check the shortname was not truncated by cleaning. 130 if (empty($data->shortname)) { 131 $err['shortname'] = get_string('required'); 132 133 } else { 134 // Check allowed pattern (numbers, letters and underscore). 135 if (!preg_match('/^[a-zA-Z0-9_]+$/', $data->shortname)) { 136 $err['shortname'] = get_string('profileshortnameinvalid', 'admin'); 137 } else { 138 // Fetch field-record from DB. 139 $field = $DB->get_record('user_info_field', array('shortname' => $data->shortname)); 140 // Check the shortname is unique. 141 if ($field and $field->id <> $data->id) { 142 $err['shortname'] = get_string('profileshortnamenotunique', 'admin'); 143 } 144 // NOTE: since 2.0 the shortname may collide with existing fields in $USER because we load these fields into 145 // $USER->profile array instead. 146 } 147 } 148 149 // No further checks necessary as the form class will take care of it. 150 return $err; 151 } 152 153 /** 154 * Validate the data from the add/edit profile field form 155 * that is specific to the current data type 156 * @param array $data 157 * @param array $files 158 * @return array associative array of error messages 159 */ 160 public function define_validate_specific($data, $files) { 161 // Do nothing - overwrite if necessary. 162 return array(); 163 } 164 165 /** 166 * Alter form based on submitted or existing data 167 * @param moodleform $mform 168 */ 169 public function define_after_data(&$mform) { 170 // Do nothing - overwrite if necessary. 171 } 172 173 /** 174 * Add a new profile field or save changes to current field 175 * @param array|stdClass $data from the add/edit profile field form 176 */ 177 public function define_save($data) { 178 global $DB; 179 180 $data = $this->define_save_preprocess($data); // Hook for child classes. 181 182 $old = false; 183 if (!empty($data->id)) { 184 $old = $DB->get_record('user_info_field', array('id' => (int)$data->id)); 185 } 186 187 // Check to see if the category has changed. 188 if (!$old or $old->categoryid != $data->categoryid) { 189 $data->sortorder = $DB->count_records('user_info_field', array('categoryid' => $data->categoryid)) + 1; 190 } 191 192 if (empty($data->id)) { 193 unset($data->id); 194 $data->id = $DB->insert_record('user_info_field', $data); 195 } else { 196 $DB->update_record('user_info_field', $data); 197 } 198 199 $field = $DB->get_record('user_info_field', array('id' => $data->id)); 200 if ($old) { 201 \core\event\user_info_field_updated::create_from_field($field)->trigger(); 202 } else { 203 \core\event\user_info_field_created::create_from_field($field)->trigger(); 204 } 205 } 206 207 /** 208 * Preprocess data from the add/edit profile field form before it is saved. 209 * 210 * This method is a hook for the child classes to overwrite. 211 * 212 * @param array|stdClass $data from the add/edit profile field form 213 * @return array|stdClass processed data object 214 */ 215 public function define_save_preprocess($data) { 216 // Do nothing - overwrite if necessary. 217 return $data; 218 } 219 220 /** 221 * Provides a method by which we can allow the default data in profile_define_* to use an editor 222 * 223 * This should return an array of editor names (which will need to be formatted/cleaned) 224 * 225 * @return array 226 */ 227 public function define_editors() { 228 return array(); 229 } 230 } 231 232 233 234 /** 235 * Reorder the profile fields within a given category starting at the field at the given startorder. 236 */ 237 function profile_reorder_fields() { 238 global $DB; 239 240 if ($categories = $DB->get_records('user_info_category')) { 241 foreach ($categories as $category) { 242 $i = 1; 243 if ($fields = $DB->get_records('user_info_field', array('categoryid' => $category->id), 'sortorder ASC')) { 244 foreach ($fields as $field) { 245 $f = new stdClass(); 246 $f->id = $field->id; 247 $f->sortorder = $i++; 248 $DB->update_record('user_info_field', $f); 249 } 250 } 251 } 252 } 253 } 254 255 /** 256 * Reorder the profile categoriess starting at the category at the given startorder. 257 */ 258 function profile_reorder_categories() { 259 global $DB; 260 261 $i = 1; 262 if ($categories = $DB->get_records('user_info_category', null, 'sortorder ASC')) { 263 foreach ($categories as $cat) { 264 $c = new stdClass(); 265 $c->id = $cat->id; 266 $c->sortorder = $i++; 267 $DB->update_record('user_info_category', $c); 268 } 269 } 270 } 271 272 /** 273 * Delete a profile category 274 * @param int $id of the category to be deleted 275 * @return bool success of operation 276 */ 277 function profile_delete_category($id) { 278 global $DB; 279 280 // Retrieve the category. 281 if (!$category = $DB->get_record('user_info_category', array('id' => $id))) { 282 print_error('invalidcategoryid'); 283 } 284 285 if (!$categories = $DB->get_records('user_info_category', null, 'sortorder ASC')) { 286 print_error('nocate', 'debug'); 287 } 288 289 unset($categories[$category->id]); 290 291 if (!count($categories)) { 292 return false; // We can not delete the last category. 293 } 294 295 // Does the category contain any fields. 296 if ($DB->count_records('user_info_field', array('categoryid' => $category->id))) { 297 if (array_key_exists($category->sortorder - 1, $categories)) { 298 $newcategory = $categories[$category->sortorder - 1]; 299 } else if (array_key_exists($category->sortorder + 1, $categories)) { 300 $newcategory = $categories[$category->sortorder + 1]; 301 } else { 302 $newcategory = reset($categories); // Get first category if sortorder broken. 303 } 304 305 $sortorder = $DB->count_records('user_info_field', array('categoryid' => $newcategory->id)) + 1; 306 307 if ($fields = $DB->get_records('user_info_field', array('categoryid' => $category->id), 'sortorder ASC')) { 308 foreach ($fields as $field) { 309 $f = new stdClass(); 310 $f->id = $field->id; 311 $f->sortorder = $sortorder++; 312 $f->categoryid = $newcategory->id; 313 if ($DB->update_record('user_info_field', $f)) { 314 $field->sortorder = $f->sortorder; 315 $field->categoryid = $f->categoryid; 316 \core\event\user_info_field_updated::create_from_field($field)->trigger(); 317 } 318 } 319 } 320 } 321 322 // Finally we get to delete the category. 323 $DB->delete_records('user_info_category', array('id' => $category->id)); 324 profile_reorder_categories(); 325 326 \core\event\user_info_category_deleted::create_from_category($category)->trigger(); 327 328 return true; 329 } 330 331 /** 332 * Deletes a profile field. 333 * @param int $id 334 */ 335 function profile_delete_field($id) { 336 global $DB; 337 338 // Remove any user data associated with this field. 339 if (!$DB->delete_records('user_info_data', array('fieldid' => $id))) { 340 print_error('cannotdeletecustomfield'); 341 } 342 343 // Note: Any availability conditions that depend on this field will remain, 344 // but show the field as missing until manually corrected to something else. 345 346 // Need to rebuild course cache to update the info. 347 rebuild_course_cache(0, true); 348 349 // Prior to the delete, pull the record for the event. 350 $field = $DB->get_record('user_info_field', array('id' => $id)); 351 352 // Try to remove the record from the database. 353 $DB->delete_records('user_info_field', array('id' => $id)); 354 355 \core\event\user_info_field_deleted::create_from_field($field)->trigger(); 356 357 // Reorder the remaining fields in the same category. 358 profile_reorder_fields(); 359 } 360 361 /** 362 * Change the sort order of a field 363 * 364 * @param int $id of the field 365 * @param string $move direction of move 366 * @return bool success of operation 367 */ 368 function profile_move_field($id, $move) { 369 global $DB; 370 371 // Get the field object. 372 if (!$field = $DB->get_record('user_info_field', array('id' => $id))) { 373 return false; 374 } 375 // Count the number of fields in this category. 376 $fieldcount = $DB->count_records('user_info_field', array('categoryid' => $field->categoryid)); 377 378 // Calculate the new sortorder. 379 if ( ($move == 'up') and ($field->sortorder > 1)) { 380 $neworder = $field->sortorder - 1; 381 } else if (($move == 'down') and ($field->sortorder < $fieldcount)) { 382 $neworder = $field->sortorder + 1; 383 } else { 384 return false; 385 } 386 387 // Retrieve the field object that is currently residing in the new position. 388 $params = array('categoryid' => $field->categoryid, 'sortorder' => $neworder); 389 if ($swapfield = $DB->get_record('user_info_field', $params)) { 390 391 // Swap the sortorders. 392 $swapfield->sortorder = $field->sortorder; 393 $field->sortorder = $neworder; 394 395 // Update the field records. 396 $DB->update_record('user_info_field', $field); 397 $DB->update_record('user_info_field', $swapfield); 398 399 \core\event\user_info_field_updated::create_from_field($field)->trigger(); 400 \core\event\user_info_field_updated::create_from_field($swapfield)->trigger(); 401 } 402 403 profile_reorder_fields(); 404 return true; 405 } 406 407 /** 408 * Change the sort order of a category. 409 * 410 * @param int $id of the category 411 * @param string $move direction of move 412 * @return bool success of operation 413 */ 414 function profile_move_category($id, $move) { 415 global $DB; 416 // Get the category object. 417 if (!($category = $DB->get_record('user_info_category', array('id' => $id)))) { 418 return false; 419 } 420 421 // Count the number of categories. 422 $categorycount = $DB->count_records('user_info_category'); 423 424 // Calculate the new sortorder. 425 if (($move == 'up') and ($category->sortorder > 1)) { 426 $neworder = $category->sortorder - 1; 427 } else if (($move == 'down') and ($category->sortorder < $categorycount)) { 428 $neworder = $category->sortorder + 1; 429 } else { 430 return false; 431 } 432 433 // Retrieve the category object that is currently residing in the new position. 434 if ($swapcategory = $DB->get_record('user_info_category', array('sortorder' => $neworder))) { 435 436 // Swap the sortorders. 437 $swapcategory->sortorder = $category->sortorder; 438 $category->sortorder = $neworder; 439 440 // Update the category records. 441 $DB->update_record('user_info_category', $category); 442 $DB->update_record('user_info_category', $swapcategory); 443 444 \core\event\user_info_category_updated::create_from_category($category)->trigger(); 445 \core\event\user_info_category_updated::create_from_category($swapcategory)->trigger(); 446 447 return true; 448 } 449 450 return false; 451 } 452 453 /** 454 * Retrieve a list of all the available data types 455 * @return array a list of the datatypes suitable to use in a select statement 456 */ 457 function profile_list_datatypes() { 458 $datatypes = array(); 459 460 $plugins = core_component::get_plugin_list('profilefield'); 461 foreach ($plugins as $type => $unused) { 462 $datatypes[$type] = get_string('pluginname', 'profilefield_'.$type); 463 } 464 asort($datatypes); 465 466 return $datatypes; 467 } 468 469 /** 470 * Retrieve a list of categories and ids suitable for use in a form 471 * @return array 472 */ 473 function profile_list_categories() { 474 global $DB; 475 $categories = $DB->get_records_menu('user_info_category', null, 'sortorder ASC', 'id, name'); 476 return array_map('format_string', $categories); 477 } 478 479 480 /** 481 * Edit a category 482 * 483 * @param int $id 484 * @param string $redirect 485 */ 486 function profile_edit_category($id, $redirect) { 487 global $DB, $OUTPUT, $CFG; 488 489 require_once($CFG->dirroot.'/user/profile/index_category_form.php'); 490 $categoryform = new category_form(); 491 492 if ($category = $DB->get_record('user_info_category', array('id' => $id))) { 493 $categoryform->set_data($category); 494 } 495 496 if ($categoryform->is_cancelled()) { 497 redirect($redirect); 498 } else { 499 if ($data = $categoryform->get_data()) { 500 if (empty($data->id)) { 501 unset($data->id); 502 $data->sortorder = $DB->count_records('user_info_category') + 1; 503 $data->id = $DB->insert_record('user_info_category', $data, true); 504 505 $createdcategory = $DB->get_record('user_info_category', array('id' => $data->id)); 506 \core\event\user_info_category_created::create_from_category($createdcategory)->trigger(); 507 } else { 508 $DB->update_record('user_info_category', $data); 509 510 $updatedcateogry = $DB->get_record('user_info_category', array('id' => $data->id)); 511 \core\event\user_info_category_updated::create_from_category($updatedcateogry)->trigger(); 512 } 513 profile_reorder_categories(); 514 redirect($redirect); 515 516 } 517 518 if (empty($id)) { 519 $strheading = get_string('profilecreatenewcategory', 'admin'); 520 } else { 521 $strheading = get_string('profileeditcategory', 'admin', format_string($category->name)); 522 } 523 524 // Print the page. 525 echo $OUTPUT->header(); 526 echo $OUTPUT->heading($strheading); 527 $categoryform->display(); 528 echo $OUTPUT->footer(); 529 die; 530 } 531 532 } 533 534 /** 535 * Edit a profile field. 536 * 537 * @param int $id 538 * @param string $datatype 539 * @param string $redirect 540 */ 541 function profile_edit_field($id, $datatype, $redirect) { 542 global $CFG, $DB, $OUTPUT, $PAGE; 543 544 if (!$field = $DB->get_record('user_info_field', array('id' => $id))) { 545 $field = new stdClass(); 546 $field->datatype = $datatype; 547 $field->description = ''; 548 $field->descriptionformat = FORMAT_HTML; 549 $field->defaultdata = ''; 550 $field->defaultdataformat = FORMAT_HTML; 551 } 552 553 // Clean and prepare description for the editor. 554 $field->description = clean_text($field->description, $field->descriptionformat); 555 $field->description = array('text' => $field->description, 'format' => $field->descriptionformat, 'itemid' => 0); 556 557 require_once($CFG->dirroot.'/user/profile/index_field_form.php'); 558 $fieldform = new field_form(null, $field->datatype); 559 560 // Convert the data format for. 561 if (is_array($fieldform->editors())) { 562 foreach ($fieldform->editors() as $editor) { 563 if (isset($field->$editor)) { 564 $field->$editor = clean_text($field->$editor, $field->{$editor.'format'}); 565 $field->$editor = array('text' => $field->$editor, 'format' => $field->{$editor.'format'}, 'itemid' => 0); 566 } 567 } 568 } 569 570 $fieldform->set_data($field); 571 572 if ($fieldform->is_cancelled()) { 573 redirect($redirect); 574 575 } else { 576 if ($data = $fieldform->get_data()) { 577 require_once($CFG->dirroot.'/user/profile/field/'.$datatype.'/define.class.php'); 578 $newfield = 'profile_define_'.$datatype; 579 $formfield = new $newfield(); 580 581 // Collect the description and format back into the proper data structure from the editor. 582 // Note: This field will ALWAYS be an editor. 583 $data->descriptionformat = $data->description['format']; 584 $data->description = $data->description['text']; 585 586 // Check whether the default data is an editor, this is (currently) only the textarea field type. 587 if (is_array($data->defaultdata) && array_key_exists('text', $data->defaultdata)) { 588 // Collect the default data and format back into the proper data structure from the editor. 589 $data->defaultdataformat = $data->defaultdata['format']; 590 $data->defaultdata = $data->defaultdata['text']; 591 } 592 593 // Convert the data format for. 594 if (is_array($fieldform->editors())) { 595 foreach ($fieldform->editors() as $editor) { 596 if (isset($field->$editor)) { 597 $field->{$editor.'format'} = $field->{$editor}['format']; 598 $field->$editor = $field->{$editor}['text']; 599 } 600 } 601 } 602 603 $formfield->define_save($data); 604 profile_reorder_fields(); 605 profile_reorder_categories(); 606 redirect($redirect); 607 } 608 609 $datatypes = profile_list_datatypes(); 610 611 if (empty($id)) { 612 $strheading = get_string('profilecreatenewfield', 'admin', $datatypes[$datatype]); 613 } else { 614 $strheading = get_string('profileeditfield', 'admin', format_string($field->name)); 615 } 616 617 // Print the page. 618 $PAGE->navbar->add($strheading); 619 echo $OUTPUT->header(); 620 echo $OUTPUT->heading($strheading); 621 $fieldform->display(); 622 echo $OUTPUT->footer(); 623 die; 624 } 625 } 626 627
title
Description
Body
title
Description
Body
title
Description
Body
title
Body