Merge branch 'MDL-71051-master-2' of git://github.com/marinaglancy/moodle

This commit is contained in:
Adrian Greeve 2021-04-29 09:27:07 +08:00
commit d4673e3047
43 changed files with 1005 additions and 933 deletions

View File

@ -213,7 +213,8 @@ if ($hassiteconfig
new lang_string('showuseridentity', 'admin'),
new lang_string('showuseridentity_desc', 'admin'), ['email' => 1],
function() {
global $DB;
global $CFG;
require_once($CFG->dirroot.'/user/profile/lib.php');
// Basic fields available in user table.
$fields = [
@ -229,10 +230,10 @@ if ($hassiteconfig
];
// Custom profile fields.
$profilefields = $DB->get_records('user_info_field', ['datatype' => 'text'], 'sortorder ASC');
foreach ($profilefields as $key => $field) {
// Only reasonable-length fields can be used as identity fields.
if ($field->param2 > 255) {
$profilefields = profile_get_custom_fields();
foreach ($profilefields as $field) {
// Only reasonable-length text fields can be used as identity fields.
if ($field->param2 > 255 || $field->datatype != 'text') {
continue;
}
$fields['profile_field_' . $field->shortname] = $field->name . ' *';

View File

@ -60,7 +60,7 @@ class process {
protected $standardfields = [];
/** @var array */
protected $profilefields = [];
/** @var array */
/** @var \profile_field_base[] */
protected $allprofilefields = [];
/** @var string|\uu_progress_tracker|null */
protected $progresstrackerclass = null;
@ -161,12 +161,13 @@ class process {
* Profile fields
*/
protected function find_profile_fields(): void {
global $DB;
$this->allprofilefields = $DB->get_records('user_info_field');
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
$this->allprofilefields = profile_get_user_fields_with_data(0);
$this->profilefields = [];
if ($proffields = $this->allprofilefields) {
foreach ($proffields as $key => $proffield) {
$profilefieldname = 'profile_field_'.$proffield->shortname;
$profilefieldname = 'profile_field_'.$proffield->get_shortname();
$this->profilefields[] = $profilefieldname;
// Re-index $proffields with key as shortname. This will be
// used while checking if profile data is key and needs to be converted (eg. menu profile field).
@ -528,8 +529,7 @@ class process {
}
}
}
$proffields = $this->allprofilefields;
foreach ($this->profilefields as $field) {
foreach ($this->allprofilefields as $field => $profilefield) {
if (isset($user->$field)) {
continue;
}
@ -539,9 +539,6 @@ class process {
// Form contains key and later code expects value.
// Convert key to value for required profile fields.
require_once($CFG->dirroot.'/user/profile/field/'.$proffields[$field]->datatype.'/field.class.php');
$profilefieldclass = 'profile_field_'.$proffields[$field]->datatype;
$profilefield = new $profilefieldclass($proffields[$field]->id);
if (method_exists($profilefield, 'convert_external_data')) {
$user->$field = $profilefield->edit_save_data_preprocess($user->$field, null);
}

View File

@ -415,17 +415,17 @@ function uu_allowed_sysroles_cache() {
* @return stdClass pre-processed custom profile data
*/
function uu_pre_process_custom_profile_data($data) {
global $CFG, $DB;
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
$fields = profile_get_user_fields_with_data(0);
// find custom profile fields and check if data needs to converted.
foreach ($data as $key => $value) {
if (preg_match('/^profile_field_/', $key)) {
$shortname = str_replace('profile_field_', '', $key);
if ($fields = $DB->get_records('user_info_field', array('shortname' => $shortname))) {
foreach ($fields as $field) {
require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
$newfield = 'profile_field_'.$field->datatype;
$formfield = new $newfield($field->id, $data->id);
if (method_exists($formfield, 'convert_external_data')) {
if ($fields) {
foreach ($fields as $formfield) {
if ($formfield->get_shortname() === $shortname && method_exists($formfield, 'convert_external_data')) {
$data->$key = $formfield->convert_external_data($value);
}
}
@ -443,7 +443,9 @@ function uu_pre_process_custom_profile_data($data) {
* @return bool true if no error else false
*/
function uu_check_custom_profile_data(&$data) {
global $CFG, $DB;
global $CFG;
require_once($CFG->dirroot.'/user/profile/lib.php');
$noerror = true;
$testuserid = null;
@ -452,15 +454,13 @@ function uu_check_custom_profile_data(&$data) {
$testuserid = $result[1];
}
}
$profilefields = profile_get_user_fields_with_data(0);
// Find custom profile fields and check if data needs to converted.
foreach ($data as $key => $value) {
if (preg_match('/^profile_field_/', $key)) {
$shortname = str_replace('profile_field_', '', $key);
if ($fields = $DB->get_records('user_info_field', array('shortname' => $shortname))) {
foreach ($fields as $field) {
require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
$newfield = 'profile_field_'.$field->datatype;
$formfield = new $newfield($field->id, 0);
foreach ($profilefields as $formfield) {
if ($formfield->get_shortname() === $shortname) {
if (method_exists($formfield, 'convert_external_data') &&
is_null($formfield->convert_external_data($value))) {
$data['status'][] = get_string('invaliduserfield', 'error', $shortname);

View File

@ -69,7 +69,8 @@ Feature: Upload users
# Create user profile field.
Given I log in as "admin"
And I navigate to "Users > Accounts > User profile fields" in site administration
And I set the field "datatype" to "Text area"
And I click on "Create a new profile field" "link"
And I click on "Text area" "link"
And I set the following fields to these values:
| Short name | superfield |
| Name | Super field |

View File

@ -126,14 +126,13 @@ class tool_uploaduser_cli_testcase extends advanced_testcase {
* User upload with user profile fields
*/
public function test_upload_with_profile_fields() {
global $DB, $CFG;
global $CFG;
$this->resetAfterTest();
set_config('passwordpolicy', 0);
$this->setAdminUser();
$categoryid = $DB->insert_record('user_info_category', ['name' => 'Cat 1', 'sortorder' => 1]);
$this->field1 = $DB->insert_record('user_info_field', [
'shortname' => 'superfield', 'name' => 'Super field', 'categoryid' => $categoryid,
$this->field1 = $this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'superfield', 'name' => 'Super field',
'datatype' => 'text', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 1]);
$filepath = $CFG->dirroot.'/lib/tests/fixtures/upload_users_profile.csv';

View File

@ -50,11 +50,9 @@ if ($dataformat) {
'city' => 'city',
'country' => 'country');
if ($extrafields = $DB->get_records('user_info_field')) {
foreach ($extrafields as $n => $field) {
$fields['profile_field_'.$field->shortname] = 'profile_field_'.$field->shortname;
require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
}
$extrafields = profile_get_user_fields_with_data(0);
foreach ($extrafields as $formfield) {
$fields['profile_field_'.$formfield->get_shortname()] = 'profile_field_'.$formfield->get_shortname();
}
$filename = clean_filename(get_string('users'));
@ -68,11 +66,7 @@ if ($dataformat) {
if (!$user = $DB->get_record('user', array('id' => $userid))) {
return null;
}
foreach ($extrafields as $field) {
$newfield = 'profile_field_'.$field->datatype;
$formfield = new $newfield($field->id, $user->id);
$formfield->edit_load_user_data($user);
}
profile_load_data($user);
$userprofiledata = array();
foreach ($fields as $field => $unused) {
// Custom user profile textarea fields come in an array

View File

@ -139,8 +139,7 @@ class auth_db_testcase extends advanced_testcase {
set_config('field_lock_email', 'unlocked', 'auth_db');
// Create a user profile field and add mapping to it.
$DB->insert_record('user_info_field', ['shortname' => 'pet', 'name' => 'Pet', 'required' => 0,
'visible' => 1, 'locked' => 0, 'categoryid' => 1, 'datatype' => 'text']);
$this->getDataGenerator()->create_custom_profile_field(['shortname' => 'pet', 'name' => 'Pet', 'datatype' => 'text']);
set_config('field_map_profile_field_pet', 'animal', 'auth_db');
set_config('field_updatelocal_profile_field_pet', 'oncreate', 'auth_db');

View File

@ -44,18 +44,17 @@ class auth_email_external_testcase extends externallib_advanced_testcase {
* Set up for every test
*/
public function setUp(): void {
global $CFG, $DB;
global $CFG;
$this->resetAfterTest(true);
$CFG->registerauth = 'email';
$categoryid = $DB->insert_record('user_info_category', array('name' => 'Cat 1', 'sortorder' => 1));
$this->field1 = $DB->insert_record('user_info_field', array(
'shortname' => 'frogname', 'name' => 'Name of frog', 'categoryid' => $categoryid,
'datatype' => 'text', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 1));
$this->field2 = $DB->insert_record('user_info_field', array(
'shortname' => 'sometext', 'name' => 'Some text in textarea', 'categoryid' => $categoryid,
'datatype' => 'textarea', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 2));
$this->field1 = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'frogname', 'name' => 'Name of frog',
'datatype' => 'text', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 1))->id;
$this->field2 = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'sometext', 'name' => 'Some text in textarea',
'datatype' => 'textarea', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 2))->id;
}
public function test_get_signup_settings() {
@ -109,8 +108,8 @@ class auth_email_external_testcase extends externallib_advanced_testcase {
// Create category with MathJax and a new field with MathJax.
$categoryname = 'Cat $$(a+b)=2$$';
$fieldname = 'Some text $$(a+b)=2$$';
$categoryid = $DB->insert_record('user_info_category', array('name' => $categoryname, 'sortorder' => 1));
$field3 = $DB->insert_record('user_info_field', array(
$categoryid = $this->getDataGenerator()->create_custom_profile_field_category(['name' => $categoryname])->id;
$this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'mathjaxname', 'name' => $fieldname, 'categoryid' => $categoryid,
'datatype' => 'textarea', 'signup' => 1, 'visible' => 1, 'required' => 1, 'sortorder' => 2));

View File

@ -65,7 +65,8 @@ Feature: availability_profile
# Add custom field.
Given I log in as "admin"
And I navigate to "Users > Accounts > User profile fields" in site administration
And I set the field "datatype" to "Text input"
And I click on "Create a new profile field" "link"
And I click on "Text input" "link"
And I set the following fields to these values:
| Short name | superfield |
| Name | Super field |

View File

@ -50,14 +50,10 @@ class availability_profile_condition_testcase extends advanced_testcase {
$this->resetAfterTest();
// Add a custom profile field type. The API for doing this is indescribably
// horrid and tightly intertwined with the form UI, so it's best to add
// it directly in database.
$DB->insert_record('user_info_field', array(
'shortname' => 'frogtype', 'name' => 'Type of frog', 'categoryid' => 1,
// Add a custom profile field type.
$this->profilefield = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'frogtype', 'name' => 'Type of frog',
'datatype' => 'text'));
$this->profilefield = $DB->get_record('user_info_field',
array('shortname' => 'frogtype'));
// Clear static cache.
\availability_profile\condition::wipe_static_cache();
@ -333,11 +329,9 @@ class availability_profile_condition_testcase extends advanced_testcase {
$info = new \core_availability\mock_info();
// Add custom textarea type.
$DB->insert_record('user_info_field', array(
'shortname' => 'longtext', 'name' => 'Long text', 'categoryid' => 1,
$customfield = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'longtext', 'name' => 'Long text',
'datatype' => 'textarea'));
$customfield = $DB->get_record('user_info_field',
array('shortname' => 'longtext'));
// The list of fields should include the text field added in setUp(),
// but should not include the textarea field added just now.
@ -465,11 +459,9 @@ class availability_profile_condition_testcase extends advanced_testcase {
condition::wipe_static_cache();
// For testing, make another info field with default value.
$DB->insert_record('user_info_field', array(
'shortname' => 'tonguestyle', 'name' => 'Tongue style', 'categoryid' => 1,
$otherprofilefield = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'tonguestyle', 'name' => 'Tongue style',
'datatype' => 'text', 'defaultdata' => 'Slimy'));
$otherprofilefield = $DB->get_record('user_info_field',
array('shortname' => 'tonguestyle'));
// Make a test course and some users.
$generator = $this->getDataGenerator();

View File

@ -1691,7 +1691,9 @@ class restore_section_structure_step extends restore_structure_step {
* @param stdClass $data Record data
*/
public function process_availability_field($data) {
global $DB;
global $DB, $CFG;
require_once($CFG->dirroot.'/user/profile/lib.php');
$data = (object)$data;
// Mark it is as passed by default
$passed = true;
@ -1704,9 +1706,8 @@ class restore_section_structure_step extends restore_structure_step {
// If one is null but the other isn't something clearly went wrong and we'll skip this condition.
$passed = false;
} else if (!is_null($data->customfield)) {
$params = array('shortname' => $data->customfield, 'datatype' => $data->customfieldtype);
$customfieldid = $DB->get_field('user_info_field', 'id', $params);
$passed = ($customfieldid !== false);
$field = profile_get_custom_field_data_by_shortname($data->customfield);
$passed = $field && $field->datatype == $data->customfieldtype;
}
if ($passed) {
@ -4534,7 +4535,9 @@ class restore_module_structure_step extends restore_structure_step {
* @param stdClass $data Record data
*/
protected function process_availability_field($data) {
global $DB;
global $DB, $CFG;
require_once($CFG->dirroot.'/user/profile/lib.php');
$data = (object)$data;
// Mark it is as passed by default
$passed = true;
@ -4547,9 +4550,8 @@ class restore_module_structure_step extends restore_structure_step {
// If one is null but the other isn't something clearly went wrong and we'll skip this condition.
$passed = false;
} else if (!empty($data->customfield)) {
$params = array('shortname' => $data->customfield, 'datatype' => $data->customfieldtype);
$customfieldid = $DB->get_field('user_info_field', 'id', $params);
$passed = ($customfieldid !== false);
$field = profile_get_custom_field_data_by_shortname($data->customfield);
$passed = $field && $field->datatype == $data->customfieldtype;
}
if ($passed) {

View File

@ -1151,6 +1151,7 @@ abstract class restore_dbops {
public static function create_included_users($basepath, $restoreid, $userid,
\core\progress\base $progress) {
global $CFG, $DB;
require_once($CFG->dirroot.'/user/profile/lib.php');
$progress->start_progress('Creating included users');
$authcache = array(); // Cache to get some bits from authentication plugins
@ -1257,8 +1258,9 @@ abstract class restore_dbops {
$udata = (object)$udata;
// If the profile field has data and the profile shortname-datatype is defined in server
if ($udata->field_data) {
if ($field = $DB->get_record('user_info_field', array('shortname'=>$udata->field_name, 'datatype'=>$udata->field_type))) {
/// Insert the user_custom_profile_field
$field = profile_get_custom_field_data_by_shortname($udata->field_name);
if ($field && $field->datatype === $udata->field_type) {
// Insert the user_custom_profile_field.
$rec = new stdClass();
$rec->userid = $newuserid;
$rec->fieldid = $field->id;

View File

@ -44,7 +44,8 @@ class award_criteria_profile extends award_criteria {
*
*/
public function get_options(&$mform) {
global $DB;
global $CFG, $DB;
require_once($CFG->dirroot . '/user/profile/lib.php');
$none = true;
$existing = array();
@ -54,17 +55,11 @@ class award_criteria_profile extends award_criteria {
$dfields = array('firstname', 'lastname', 'email', 'address', 'phone1', 'phone2',
'department', 'institution', 'description', 'picture', 'city', 'country');
$sql = "SELECT uf.id as fieldid, uf.name as name, ic.id as categoryid, ic.name as categoryname, uf.datatype
FROM {user_info_field} uf
JOIN {user_info_category} ic
ON uf.categoryid = ic.id AND uf.visible <> 0
ORDER BY ic.sortorder ASC, uf.sortorder ASC";
// Get custom fields.
$cfields = $DB->get_records_sql($sql);
$cfids = array_map(function($o) {
return $o->fieldid;
}, $cfields);
$cfields = array_filter(profile_get_custom_fields(), function($field) {
return $field->visible <> 0;
});
$cfids = array_keys($cfields);
if ($this->id !== 0) {
$existing = array_keys($this->params);
@ -98,13 +93,14 @@ class award_criteria_profile extends award_criteria {
foreach ($cfields as $field) {
if (!isset($currentcat) || $currentcat != $field->categoryid) {
$currentcat = $field->categoryid;
$mform->addElement('header', 'category_' . $currentcat, format_string($field->categoryname));
$categoryname = $DB->get_field('user_info_category', 'name', ['id' => $field->categoryid]);
$mform->addElement('header', 'category_' . $currentcat, format_string($categoryname));
}
$checked = false;
if (in_array($field->fieldid, $existing)) {
if (in_array($field->id, $existing)) {
$checked = true;
}
$this->config_options($mform, array('id' => $field->fieldid, 'checked' => $checked, 'name' => $field->name, 'error' => false));
$this->config_options($mform, array('id' => $field->id, 'checked' => $checked, 'name' => $field->name, 'error' => false));
$none = false;
}
}
@ -133,11 +129,16 @@ class award_criteria_profile extends award_criteria {
* @return string
*/
public function get_details($short = '') {
global $DB, $OUTPUT;
global $OUTPUT, $CFG;
require_once($CFG->dirroot.'/user/profile/lib.php');
$output = array();
foreach ($this->params as $p) {
if (is_numeric($p['field'])) {
$str = $DB->get_field('user_info_field', 'name', array('id' => $p['field']));
$fields = profile_get_custom_fields();
// Get formatted field name if such field exists.
$str = isset($fields[$p['field']]->name) ?
format_string($fields[$p['field']]->name) : null;
} else {
$str = \core_user\fields::get_display_name($p['field']);
}

View File

@ -592,9 +592,9 @@ class badgeslib_test extends advanced_testcase {
require_once($CFG->dirroot.'/user/profile/lib.php');
// Add a custom field of textarea type.
$customprofileid = $DB->insert_record('user_info_field', array(
'shortname' => 'newfield', 'name' => 'Description of new field', 'categoryid' => 1,
'datatype' => 'textarea'));
$customprofileid = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'newfield', 'name' => 'Description of new field',
'datatype' => 'textarea'))->id;
$this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->coursebadge);

View File

@ -36,7 +36,6 @@ require_once($CFG->libdir . '/form/datetimeselector.php');
// Used to test the user datetime profile field.
require_once($CFG->dirroot . '/user/profile/lib.php');
require_once($CFG->dirroot . '/user/profile/definelib.php');
require_once($CFG->dirroot . '/user/profile/index_field_form.php');
/**
* Unit tests for the calendar type system.
@ -273,12 +272,13 @@ class core_calendar_type_testcase extends advanced_testcase {
$formdata['name'] = 'Name';
$formdata['param1'] = $date['inputminyear'];
$formdata['param2'] = $date['inputmaxyear'];
$formdata['datatype'] = 'datetime';
// Mock submitting this.
field_form::mock_submit($formdata);
\core_user\form\profile_field_form::mock_submit($formdata);
// Create the user datetime form.
$form = new field_form(null, 'datetime');
$form = new \core_user\form\profile_field_form();
// Get the data from the submission.
$submissiondata = $form->get_data();

View File

@ -3330,14 +3330,10 @@ abstract class grade_helper {
// Sets the list of custom profile fields
$customprofilefields = array_map('trim', explode(',', $CFG->grade_export_customprofilefields));
if ($includecustomfields && !empty($customprofilefields)) {
list($wherefields, $whereparams) = $DB->get_in_or_equal($customprofilefields);
$customfields = $DB->get_records_sql("SELECT f.*
FROM {user_info_field} f
JOIN {user_info_category} c ON f.categoryid=c.id
WHERE f.shortname $wherefields
ORDER BY c.sortorder ASC, f.sortorder ASC", $whereparams);
$customfields = profile_get_user_fields_with_data(0);
foreach ($customfields as $field) {
foreach ($customfields as $fieldobj) {
$field = (object)$fieldobj->get_field_config_for_external();
// Make sure we can display this custom field
if (!in_array($field->shortname, $customprofilefields)) {
continue;

View File

@ -989,7 +989,7 @@ $string['profilecommonsettings'] = 'Common settings';
$string['profileconfirmcategorydeletion'] = 'There is/are {$a} field/s in this category which will be moved into the category above (or below if in the top category).<br />Do you still wish to delete this category?';
$string['profileconfirmfielddeletion'] = 'There is/are {$a} user record/s for this field which will be deleted.<br />Do you still wish to delete this field?';
$string['profilecreatecategory'] = 'Create a new profile category';
$string['profilecreatefield'] = 'Create a new profile field:';
$string['profilecreatefield'] = 'Create a new profile field';
$string['profilecreatenewcategory'] = 'Creating a new category';
$string['profilecreatenewfield'] = 'Creating a new \'{$a}\' profile field';
$string['profiledefaultcategory'] = 'Other fields';

View File

@ -598,14 +598,16 @@ class auth_plugin_base {
* @return array list of custom fields.
*/
public function get_custom_user_profile_fields() {
global $DB;
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
// If already retrieved then return.
if (!is_null($this->customfields)) {
return $this->customfields;
}
$this->customfields = array();
if ($proffields = $DB->get_records('user_info_field')) {
if ($proffields = profile_get_custom_fields()) {
foreach ($proffields as $proffield) {
$this->customfields[] = 'profile_field_'.$proffield->shortname;
}
@ -1159,7 +1161,8 @@ function signup_is_enabled() {
* @since Moodle 3.3
*/
function display_auth_lock_options($settings, $auth, $userfields, $helptext, $mapremotefields, $updateremotefields, $customfields = array()) {
global $DB;
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
// Introductory explanation and help text.
if ($mapremotefields) {
@ -1180,7 +1183,8 @@ function display_auth_lock_options($settings, $auth, $userfields, $helptext, $ma
// Generate the list of profile fields to allow updates / lock.
if (!empty($customfields)) {
$userfields = array_merge($userfields, $customfields);
$customfieldname = $DB->get_records('user_info_field', null, '', 'shortname, name');
$allcustomfields = profile_get_custom_fields();
$customfieldname = array_combine(array_column($allcustomfields, 'shortname'), $allcustomfields);
}
foreach ($userfields as $field) {

View File

@ -1289,7 +1289,7 @@ EOD;
'defaultdata' => 0
]
];
foreach ($typedefaults[$data['datatype']] as $field => $value) {
foreach ($typedefaults[$data['datatype']] ?? [] as $field => $value) {
$defaults[$field] = $value;
}

View File

@ -22,7 +22,7 @@ Feature: Select user identity fields
| user1 | C1 | manager |
| user2 | C1 | manager |
Scenario: The admin settings screen should show text custom fields (and let you choose them)
Scenario: The admin settings screen should show text custom fields of certain length (and let you choose them)
When I log in as "admin"
And I navigate to "Users > Permissions > User policies" in site administration
Then I should see "Speciality" in the "#admin-showuseridentity" "css_element"

View File

@ -50,13 +50,8 @@ class core_event_profile_field_testcase extends advanced_testcase {
* Test that triggering the user_info_category_created event works as expected.
*/
public function test_user_info_category_created_event() {
global $DB;
// Create a new profile category.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
// Trigger the event.
$sink = $this->redirectEvents();
@ -81,15 +76,8 @@ class core_event_profile_field_testcase extends advanced_testcase {
global $DB;
// Create new profile categories.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat2 = new stdClass();
$cat2->name = 'Example category 2';
$cat2->sortorder = $DB->count_records('user_info_category') + 1;
$cat2->id = $DB->insert_record('user_info_category', $cat2);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
$cat2 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category 2']);
// Trigger the events.
$sink = $this->redirectEvents();
@ -116,18 +104,9 @@ class core_event_profile_field_testcase extends advanced_testcase {
* Test that deleting a user info category triggers a delete event.
*/
public function test_user_info_category_deleted_event() {
global $DB;
// Create new profile categories.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat2 = new stdClass();
$cat2->name = 'Example category 2';
$cat2->sortorder = $DB->count_records('user_info_category') + 1;
$cat2->id = $DB->insert_record('user_info_category', $cat2);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
$cat2 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category 2']);
// Trigger the event.
$sink = $this->redirectEvents();
@ -152,10 +131,7 @@ class core_event_profile_field_testcase extends advanced_testcase {
global $DB;
// Create a new profile category.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
// Create a new profile field.
$data = new stdClass();
@ -196,27 +172,17 @@ class core_event_profile_field_testcase extends advanced_testcase {
* Test that updating a user info field triggers an update event.
*/
public function test_user_info_field_updated_event() {
global $DB;
// Create a new profile category.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
// Create a new profile field.
$data = new stdClass();
$data->datatype = 'text';
$data->shortname = 'example';
$data->name = 'Example field';
$data->description = 'Hello this is an example.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $cat1->id;
$data->id = $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => 'text',
'shortname' => 'example',
'name' => 'Example field',
'description' => 'Hello this is an example.',
'categoryid' => $cat1->id,
]);
// Trigger the event.
$sink = $this->redirectEvents();
@ -241,36 +207,25 @@ class core_event_profile_field_testcase extends advanced_testcase {
* Test that moving a field triggers update events.
*/
public function test_user_info_field_updated_event_move_field() {
global $DB;
// Create a new profile category.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
// Create a new profile field.
$field1 = new stdClass();
$field1->datatype = 'text';
$field1->shortname = 'example';
$field1->name = 'Example field';
$field1->description = 'Hello this is an example.';
$field1->required = false;
$field1->locked = false;
$field1->forceunique = false;
$field1->signup = false;
$field1->visible = '0';
$field1->categoryid = $cat1->id;
$field1->sortorder = $DB->count_records('user_info_field') + 1;
$field1->id = $DB->insert_record('user_info_field', $field1);
$field1 = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => 'text',
'shortname' => 'example',
'name' => 'Example field',
'description' => 'Hello this is an example.',
'categoryid' => $cat1->id,
]);
// Create another that we will be moving.
$field2 = clone $field1;
$field2->datatype = 'text';
$field2->shortname = 'example2';
$field2->name = 'Example field 2';
$field2->sortorder = $DB->count_records('user_info_field') + 1;
$field2->id = $DB->insert_record('user_info_field', $field2);
$field2 = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => 'text',
'shortname' => 'example2',
'name' => 'Example field 2',
'categoryid' => $cat1->id,
]);
// Trigger the events.
$sink = $this->redirectEvents();
@ -302,32 +257,18 @@ class core_event_profile_field_testcase extends advanced_testcase {
* another category triggers an update event.
*/
public function test_user_info_field_updated_event_delete_category() {
global $DB;
// Create a new profile category.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat2 = new stdClass();
$cat2->name = 'Example category';
$cat2->sortorder = $DB->count_records('user_info_category') + 1;
$cat2->id = $DB->insert_record('user_info_category', $cat2);
// Create profile categories.
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
$cat2 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
// Create a new profile field.
$field = new stdClass();
$field->datatype = 'text';
$field->shortname = 'example';
$field->name = 'Example field';
$field->description = 'Hello this is an example.';
$field->required = false;
$field->locked = false;
$field->forceunique = false;
$field->signup = false;
$field->visible = '0';
$field->categoryid = $cat1->id;
$field->id = $DB->insert_record('user_info_field', $field);
$field = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => 'text',
'shortname' => 'example',
'name' => 'Example field',
'description' => 'Hello this is an example.',
'categoryid' => $cat1->id,
]);
// Trigger the event.
$sink = $this->redirectEvents();
@ -351,27 +292,17 @@ class core_event_profile_field_testcase extends advanced_testcase {
* Test that deleting a user info field triggers a delete event.
*/
public function test_user_info_field_deleted_event() {
global $DB;
// Create a new profile category.
$cat1 = new stdClass();
$cat1->name = 'Example category';
$cat1->sortorder = $DB->count_records('user_info_category') + 1;
$cat1->id = $DB->insert_record('user_info_category', $cat1);
$cat1 = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Example category']);
// Create a new profile field.
$data = new stdClass();
$data->datatype = 'text';
$data->shortname = 'delete';
$data->name = 'Example field for delete';
$data->description = 'Hello this is an example.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $cat1->id;
$data->id = $DB->insert_record('user_info_field', $data, true);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => 'text',
'shortname' => 'delete',
'name' => 'Example field for delete',
'description' => 'Hello this is an example.',
'categoryid' => $cat1->id,
]);
// Trigger the event.
$sink = $this->redirectEvents();

View File

@ -0,0 +1,2 @@
define ("core_user/edit_profile_fields",["exports","core_form/modalform","core/str"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var d={actions:{editCategory:"[data-action=\"editcategory\"]",editField:"[data-action=\"editfield\"]",createField:"[data-action=\"createfield\"]"}};a.init=function init(){document.addEventListener("click",function(a){var e=a.target.closest(d.actions.editCategory);if(e){a.preventDefault();var f=e.getAttribute("data-id")?(0,c.get_string)("profileeditcategory","admin",e.getAttribute("data-name")):(0,c.get_string)("profilecreatenewcategory","admin"),g=new b.default({formClass:"core_user\\form\\profile_category_form",args:{id:e.getAttribute("data-id")},modalConfig:{title:f},returnFocus:e});g.addEventListener(g.events.FORM_SUBMITTED,function(){return window.location.reload()});g.show()}e=a.target.closest(d.actions.editField);if(e){a.preventDefault();var h=new b.default({formClass:"core_user\\form\\profile_field_form",args:{id:e.getAttribute("data-id")},modalConfig:{title:(0,c.get_string)("profileeditfield","admin",e.getAttribute("data-name"))},returnFocus:e});h.addEventListener(h.events.FORM_SUBMITTED,function(){return window.location.reload()});h.show()}e=a.target.closest(d.actions.createField);if(e){a.preventDefault();var i=new b.default({formClass:"core_user\\form\\profile_field_form",args:{datatype:e.getAttribute("data-datatype"),categoryid:e.getAttribute("data-categoryid")},modalConfig:{title:(0,c.get_string)("profilecreatenewfield","admin",e.getAttribute("data-datatypename"))},returnFocus:e});i.addEventListener(i.events.FORM_SUBMITTED,function(){return window.location.reload()});i.show()}})}});
//# sourceMappingURL=edit_profile_fields.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,80 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import ModalForm from 'core_form/modalform';
import {get_string as getString} from 'core/str';
/**
* User profile fields editor
*
* @module core_user/edit_profile_fields
* @package core_user
* @copyright 2021 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const Selectors = {
actions: {
editCategory: '[data-action="editcategory"]',
editField: '[data-action="editfield"]',
createField: '[data-action="createfield"]',
},
};
export const init = () => {
document.addEventListener('click', function(e) {
let element = e.target.closest(Selectors.actions.editCategory);
if (element) {
e.preventDefault();
const title = element.getAttribute('data-id') ?
getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :
getString('profilecreatenewcategory', 'admin');
const form = new ModalForm({
formClass: 'core_user\\form\\profile_category_form',
args: {id: element.getAttribute('data-id')},
modalConfig: {title},
returnFocus: element,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
}
element = e.target.closest(Selectors.actions.editField);
if (element) {
e.preventDefault();
const form = new ModalForm({
formClass: 'core_user\\form\\profile_field_form',
args: {id: element.getAttribute('data-id')},
modalConfig: {title: getString('profileeditfield', 'admin', element.getAttribute('data-name'))},
returnFocus: element,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
}
element = e.target.closest(Selectors.actions.createField);
if (element) {
e.preventDefault();
const form = new ModalForm({
formClass: 'core_user\\form\\profile_field_form',
args: {datatype: element.getAttribute('data-datatype'), categoryid: element.getAttribute('data-categoryid')},
modalConfig: {title: getString('profilecreatenewfield', 'admin', element.getAttribute('data-datatypename'))},
returnFocus: element,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
}
});
};

View File

@ -371,10 +371,11 @@ class fields {
// or if the user doesn't have access to see them).
foreach ($extra as $key => $field) {
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
$allowed = false;
if ($allowcustom) {
require_once($CFG->dirroot . '/user/profile/lib.php');
$fieldinfo = profile_get_custom_field_data_by_shortname($matches[1]);
switch ($fieldinfo['visible']) {
switch ($fieldinfo->visible ?? -1) {
case PROFILE_VISIBLE_NONE:
case PROFILE_VISIBLE_PRIVATE:
$allowed = !$context || has_capability('moodle/user:viewalldetails', $context);
@ -383,8 +384,6 @@ class fields {
$allowed = true;
break;
}
} else {
$allowed = false;
}
if (!$allowed) {
unset($extra[$key]);
@ -586,7 +585,7 @@ class fields {
require_once($CFG->dirroot . '/user/profile/lib.php');
$fieldinfo = profile_get_custom_field_data_by_shortname($matches[1]);
// Use format_string so it can be translated with multilang filter if necessary.
return format_string($fieldinfo['name']);
return $fieldinfo ? format_string($fieldinfo->name) : $field;
}
// Some fields have language strings which are not the same as field name.

View File

@ -0,0 +1,125 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_user\form;
use context;
use core_form\dynamic_form;
use moodle_url;
/**
* Modal form to edit profile category
*
* @package core_user
* @copyright 2021 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class profile_category_form extends dynamic_form {
/**
* Form definition
*/
protected function definition() {
$mform = $this->_form;
$strrequired = get_string('required');
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'editcategory');
$mform->setType('action', PARAM_ALPHANUMEXT);
$mform->addElement('text', 'name', get_string('profilecategoryname', 'admin'), 'maxlength="255" size="30"');
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', $strrequired, 'required', null, 'client');
}
/**
* Perform some moodle validation.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);
$duplicate = $DB->get_field('user_info_category', 'id', ['name' => $data['name']]);
// Check the name is unique.
if (!empty($data['id'])) { // We are editing an existing record.
$olddata = $DB->get_record('user_info_category', ['id' => $data['id']]);
// Name has changed, new name in use, new name in use by another record.
$dupfound = (($olddata->name !== $data['name']) && $duplicate && ($data['id'] != $duplicate));
} else { // New profile category.
$dupfound = $duplicate;
}
if ($dupfound ) {
$errors['name'] = get_string('profilecategorynamenotunique', 'admin');
}
return $errors;
}
/**
* Returns context where this form is used
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
return \context_system::instance();
}
/**
* Checks if current user has access to this form, otherwise throws exception
*/
protected function check_access_for_dynamic_submission(): void {
require_capability('moodle/site:config', $this->get_context_for_dynamic_submission());
}
/**
* Process the form submission, used if form was submitted via AJAX
*/
public function process_dynamic_submission() {
global $CFG;
require_once($CFG->dirroot.'/user/profile/definelib.php');
profile_save_category($this->get_data());
}
/**
* Load in existing data as form defaults
*/
public function set_data_for_dynamic_submission(): void {
global $DB;
if ($id = $this->optional_param('id', 0, PARAM_INT)) {
$this->set_data($DB->get_record('user_info_category', ['id' => $id], '*', MUST_EXIST));
}
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$id = $this->optional_param('id', 0, PARAM_INT);
return new moodle_url('/user/profile/index.php',
['action' => 'editcategory', 'id' => $id]);
}
}

View File

@ -0,0 +1,183 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_user\form;
use context;
use core_form\dynamic_form;
use moodle_url;
use profile_define_base;
/**
* Class field_form used for profile fields.
*
* @package core_user
* @copyright 2007 onwards Shane Elliot {@link http://pukunui.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class profile_field_form extends dynamic_form {
/** @var profile_define_base $field */
public $field;
/** @var \stdClass */
protected $fieldrecord;
/**
* Define the form
*/
public function definition () {
global $CFG;
require_once($CFG->dirroot.'/user/profile/definelib.php');
$mform = $this->_form;
// Everything else is dependant on the data type.
$datatype = $this->get_field_record()->datatype;
require_once($CFG->dirroot.'/user/profile/field/'.$datatype.'/define.class.php');
$newfield = 'profile_define_'.$datatype;
$this->field = new $newfield();
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'editfield');
$mform->setType('action', PARAM_ALPHANUMEXT);
$mform->addElement('hidden', 'datatype', $datatype);
$mform->setType('datatype', PARAM_ALPHA);
$this->field->define_form($mform);
}
/**
* Alter definition based on existing or submitted data
*/
public function definition_after_data () {
$mform = $this->_form;
$this->field->define_after_data($mform);
}
/**
* Perform some moodle validation.
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
return $this->field->define_validate($data, $files);
}
/**
* Returns the defined editors for the field.
* @return array
*/
public function editors(): array {
$editors = $this->field->define_editors();
return is_array($editors) ? $editors : [];
}
/**
* Returns context where this form is used
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
return \context_system::instance();
}
/**
* Checks if current user has access to this form, otherwise throws exception
*/
protected function check_access_for_dynamic_submission(): void {
require_capability('moodle/site:config', $this->get_context_for_dynamic_submission());
}
/**
* Process the form submission, used if form was submitted via AJAX
*/
public function process_dynamic_submission() {
global $CFG;
require_once($CFG->dirroot.'/user/profile/definelib.php');
profile_save_field($this->get_data(), $this->editors());
}
/**
* Load in existing data as form defaults
*/
public function set_data_for_dynamic_submission(): void {
$field = $this->get_field_record();
// Clean and prepare description for the editor.
$description = clean_text($field->description, $field->descriptionformat);
$field->description = ['text' => $description, 'format' => $field->descriptionformat, 'itemid' => 0];
// Convert the data format for.
if (is_array($this->editors())) {
foreach ($this->editors() as $editor) {
if (isset($field->$editor)) {
$editordesc = clean_text($field->$editor, $field->{$editor.'format'});
$field->$editor = ['text' => $editordesc, 'format' => $field->{$editor.'format'}, 'itemid' => 0];
}
}
}
$this->set_data($field);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$id = $this->optional_param('id', 0, PARAM_INT);
$datatype = $this->optional_param('datatype', 'text', PARAM_PLUGIN);
return new moodle_url('/user/profile/index.php',
['action' => 'editfield', 'id' => $id, 'datatype' => $id ? null : $datatype]);
}
/**
* Record for the field from the database (or generic record for a new field)
*
* @return false|mixed|\stdClass
* @throws \coding_exception
* @throws \dml_exception
*/
public function get_field_record() {
global $DB;
if (!$this->fieldrecord) {
$id = $this->optional_param('id', 0, PARAM_INT);
if (!$id || !($this->fieldrecord = $DB->get_record('user_info_field', ['id' => $id]))) {
$datatype = $this->optional_param('datatype', 'text', PARAM_PLUGIN);
$this->fieldrecord = new \stdClass();
$this->fieldrecord->datatype = $datatype;
$this->fieldrecord->description = '';
$this->fieldrecord->descriptionformat = FORMAT_HTML;
$this->fieldrecord->defaultdata = '';
$this->fieldrecord->defaultdataformat = FORMAT_HTML;
$this->fieldrecord->categoryid = $this->optional_param('categoryid', 0, PARAM_INT);
}
if (!\core_component::get_component_directory('profilefield_'.$this->fieldrecord->datatype)) {
throw new \moodle_exception('fieldnotfound', 'customfield');
}
}
return $this->fieldrecord;
}
}

View File

@ -73,11 +73,12 @@ class user_filter_profilefield extends user_filter_type {
* @return array of profile fields
*/
public function get_profile_fields() {
global $DB;
$order = $DB->sql_order_by_text('name');
if (!$fields = $DB->get_records_menu('user_info_field', null, $order, 'id, name')) {
return null;
}
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
$fieldrecords = profile_get_custom_fields();
$fields = array_combine(array_keys($fieldrecords), array_column($fieldrecords, 'name'));
core_collator::asort($fields);
$res = array(0 => get_string('anyfield', 'filters'));
return $res + $fields;

View File

@ -32,7 +32,7 @@ class profile_define_base {
/**
* Prints out the form snippet for creating or editing a profile field
* @param moodleform $form instance of the moodleform class
* @param MoodleQuickForm $form instance of the moodleform class
*/
public function define_form(&$form) {
$form->addElement('header', '_commonsettings', get_string('profilecommonsettings', 'admin'));
@ -45,7 +45,7 @@ class profile_define_base {
/**
* Prints out the form snippet for the part of creating or editing a profile field common to all data types.
*
* @param moodleform $form instance of the moodleform class
* @param MoodleQuickForm $form instance of the moodleform class
*/
public function define_form_common(&$form) {
@ -88,7 +88,7 @@ class profile_define_base {
/**
* Prints out the form snippet for the part of creating or editing a profile field specific to the current data type.
* @param moodleform $form instance of the moodleform class
* @param MoodleQuickForm $form instance of the moodleform class
*/
public function define_form_specific($form) {
// Do nothing - overwrite if necessary.
@ -138,7 +138,7 @@ class profile_define_base {
$err['shortname'] = get_string('profileshortnameinvalid', 'admin');
} else {
// Fetch field-record from DB.
$field = $DB->get_record('user_info_field', array('shortname' => $data->shortname));
$field = profile_get_custom_field_data_by_shortname($data->shortname);
// Check the shortname is unique.
if ($field and $field->id <> $data->id) {
$err['shortname'] = get_string('profileshortnamenotunique', 'admin');
@ -166,7 +166,7 @@ class profile_define_base {
/**
* Alter form based on submitted or existing data
* @param moodleform $mform
* @param MoodleQuickForm $mform
*/
public function define_after_data(&$mform) {
// Do nothing - overwrite if necessary.
@ -204,6 +204,7 @@ class profile_define_base {
} else {
\core\event\user_info_field_created::create_from_field($field)->trigger();
}
profile_purge_user_fields_cache();
}
/**
@ -251,6 +252,7 @@ function profile_reorder_fields() {
}
}
}
profile_purge_user_fields_cache();
}
}
@ -268,6 +270,7 @@ function profile_reorder_categories() {
$c->sortorder = $i++;
$DB->update_record('user_info_category', $c);
}
profile_purge_user_fields_cache();
}
}
@ -326,6 +329,7 @@ function profile_delete_category($id) {
profile_reorder_categories();
\core\event\user_info_category_deleted::create_from_category($category)->trigger();
profile_purge_user_fields_cache();
return true;
}
@ -355,6 +359,7 @@ function profile_delete_field($id) {
$DB->delete_records('user_info_field', array('id' => $id));
\core\event\user_info_field_deleted::create_from_field($field)->trigger();
profile_purge_user_fields_cache();
// Reorder the remaining fields in the same category.
profile_reorder_fields();
@ -445,6 +450,7 @@ function profile_move_category($id, $move) {
\core\event\user_info_category_updated::create_from_category($category)->trigger();
\core\event\user_info_category_updated::create_from_category($swapcategory)->trigger();
profile_purge_user_fields_cache();
return true;
}
@ -478,18 +484,48 @@ function profile_list_categories() {
return array_map('format_string', $categories);
}
/**
* Create or update a profile category
*
* @param stdClass $data
*/
function profile_save_category(stdClass $data): void {
global $DB;
if (empty($data->id)) {
unset($data->id);
$data->sortorder = $DB->count_records('user_info_category') + 1;
$data->id = $DB->insert_record('user_info_category', $data, true);
$createdcategory = $DB->get_record('user_info_category', array('id' => $data->id));
\core\event\user_info_category_created::create_from_category($createdcategory)->trigger();
} else {
$DB->update_record('user_info_category', $data);
$updatedcateogry = $DB->get_record('user_info_category', array('id' => $data->id));
\core\event\user_info_category_updated::create_from_category($updatedcateogry)->trigger();
}
profile_reorder_categories();
profile_purge_user_fields_cache();
}
/**
* Edit a category
*
* @deprecated since Moodle 3.11 MDL-71051 - please do not use this function any more.
* @todo MDL-71413 This will be deleted in Moodle 4.3.
* @see profile_save_category()
*
* @param int $id
* @param string $redirect
*/
function profile_edit_category($id, $redirect) {
global $DB, $OUTPUT, $CFG;
require_once($CFG->dirroot.'/user/profile/index_category_form.php');
$categoryform = new category_form();
debugging('Function profile_edit_category() is deprecated without replacement, see also profile_save_category()',
DEBUG_DEVELOPER);
$categoryform = new \core_user\form\profile_category_form();
if ($category = $DB->get_record('user_info_category', array('id' => $id))) {
$categoryform->set_data($category);
@ -499,22 +535,8 @@ function profile_edit_category($id, $redirect) {
redirect($redirect);
} else {
if ($data = $categoryform->get_data()) {
if (empty($data->id)) {
unset($data->id);
$data->sortorder = $DB->count_records('user_info_category') + 1;
$data->id = $DB->insert_record('user_info_category', $data, true);
$createdcategory = $DB->get_record('user_info_category', array('id' => $data->id));
\core\event\user_info_category_created::create_from_category($createdcategory)->trigger();
} else {
$DB->update_record('user_info_category', $data);
$updatedcateogry = $DB->get_record('user_info_category', array('id' => $data->id));
\core\event\user_info_category_updated::create_from_category($updatedcateogry)->trigger();
}
profile_reorder_categories();
profile_save_category($data);
redirect($redirect);
}
if (empty($id)) {
@ -533,78 +555,73 @@ function profile_edit_category($id, $redirect) {
}
/**
* Save updated field definition or create a new field
*
* @param stdClass $data data from the form profile_field_form
* @param array $editors editors for this form field type
*/
function profile_save_field(stdClass $data, array $editors): void {
global $CFG;
require_once($CFG->dirroot.'/user/profile/field/'.$data->datatype.'/define.class.php');
$newfield = 'profile_define_'.$data->datatype;
/** @var profile_define_base $formfield */
$formfield = new $newfield();
// Collect the description and format back into the proper data structure from the editor.
// Note: This field will ALWAYS be an editor.
$data->descriptionformat = $data->description['format'];
$data->description = $data->description['text'];
// Check whether the default data is an editor, this is (currently) only the textarea field type.
if (is_array($data->defaultdata) && array_key_exists('text', $data->defaultdata)) {
// Collect the default data and format back into the proper data structure from the editor.
$data->defaultdataformat = $data->defaultdata['format'];
$data->defaultdata = $data->defaultdata['text'];
}
// Convert the data format for.
if (is_array($editors)) {
foreach ($editors as $editor) {
if (isset($field->$editor)) {
$field->{$editor.'format'} = $field->{$editor}['format'];
$field->$editor = $field->{$editor}['text'];
}
}
}
$formfield->define_save($data);
profile_reorder_fields();
profile_reorder_categories();
}
/**
* Edit a profile field.
*
* @deprecated since Moodle 3.11 MDL-71051 - please do not use this function any more.
* @todo MDL-71413 This will be deleted in Moodle 4.3.
* @see profile_save_field()
*
* @param int $id
* @param string $datatype
* @param string $redirect
*/
function profile_edit_field($id, $datatype, $redirect) {
global $CFG, $DB, $OUTPUT, $PAGE;
global $OUTPUT, $PAGE;
if (!$field = $DB->get_record('user_info_field', array('id' => $id))) {
$field = new stdClass();
$field->datatype = $datatype;
$field->description = '';
$field->descriptionformat = FORMAT_HTML;
$field->defaultdata = '';
$field->defaultdataformat = FORMAT_HTML;
}
debugging('Function profile_edit_field() is deprecated without replacement, see also profile_save_field()',
DEBUG_DEVELOPER);
// Clean and prepare description for the editor.
$field->description = clean_text($field->description, $field->descriptionformat);
$field->description = array('text' => $field->description, 'format' => $field->descriptionformat, 'itemid' => 0);
require_once($CFG->dirroot.'/user/profile/index_field_form.php');
$fieldform = new field_form(null, $field->datatype);
// Convert the data format for.
if (is_array($fieldform->editors())) {
foreach ($fieldform->editors() as $editor) {
if (isset($field->$editor)) {
$field->$editor = clean_text($field->$editor, $field->{$editor.'format'});
$field->$editor = array('text' => $field->$editor, 'format' => $field->{$editor.'format'}, 'itemid' => 0);
}
}
}
$fieldform->set_data($field);
$fieldform = new \core_user\form\profile_field_form();
$fieldform->set_data_for_dynamic_submission();
if ($fieldform->is_cancelled()) {
redirect($redirect);
} else {
if ($data = $fieldform->get_data()) {
require_once($CFG->dirroot.'/user/profile/field/'.$datatype.'/define.class.php');
$newfield = 'profile_define_'.$datatype;
$formfield = new $newfield();
// Collect the description and format back into the proper data structure from the editor.
// Note: This field will ALWAYS be an editor.
$data->descriptionformat = $data->description['format'];
$data->description = $data->description['text'];
// Check whether the default data is an editor, this is (currently) only the textarea field type.
if (is_array($data->defaultdata) && array_key_exists('text', $data->defaultdata)) {
// Collect the default data and format back into the proper data structure from the editor.
$data->defaultdataformat = $data->defaultdata['format'];
$data->defaultdata = $data->defaultdata['text'];
}
// Convert the data format for.
if (is_array($fieldform->editors())) {
foreach ($fieldform->editors() as $editor) {
if (isset($field->$editor)) {
$field->{$editor.'format'} = $field->{$editor}['format'];
$field->$editor = $field->{$editor}['text'];
}
}
}
$formfield->define_save($data);
profile_reorder_fields();
profile_reorder_categories();
profile_save_field($data, $fieldform->editors());
redirect($redirect);
}
@ -613,7 +630,7 @@ function profile_edit_field($id, $datatype, $redirect) {
if (empty($id)) {
$strheading = get_string('profilecreatenewfield', 'admin', $datatypes[$datatype]);
} else {
$strheading = get_string('profileeditfield', 'admin', format_string($field->name));
$strheading = get_string('profileeditfield', 'admin', format_string($fieldform->get_field_record()->name));
}
// Print the page.
@ -626,4 +643,11 @@ function profile_edit_field($id, $datatype, $redirect) {
}
}
/**
* Purge the cache for the user profile fields
*/
function profile_purge_user_fields_cache() {
$cache = \cache::make_from_params(cache_store::MODE_REQUEST, 'core_profile', 'customfields',
[], ['simplekeys' => true, 'simpledata' => true]);
$cache->purge();
}

View File

@ -276,13 +276,8 @@ class profilefield_checkbox_testcase extends provider_testcase {
* @return int The ID of the profile category
*/
private function add_profile_category() {
global $DB;
// Create a new profile category.
$cat = new stdClass();
$cat->name = 'Test category';
$cat->sortorder = 1;
return $DB->insert_record('user_info_category', $cat);
$cat = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Test category']);
return $cat->id;
}
/**
@ -293,20 +288,13 @@ class profilefield_checkbox_testcase extends provider_testcase {
* @return int The ID of the profile field
*/
private function add_profile_field($categoryid, $datatype) {
global $DB;
// Create a new profile field.
$data = new stdClass();
$data->datatype = $datatype;
$data->shortname = 'tstField';
$data->name = 'Test field';
$data->description = 'This is a test.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $categoryid;
return $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => $datatype,
'shortname' => 'tstField',
'name' => 'Test field',
'description' => 'This is a test.',
'categoryid' => $categoryid,
]);
return $data->id;
}
}

View File

@ -280,13 +280,8 @@ class profilefield_datetime_testcase extends provider_testcase {
* @return int The ID of the profile category
*/
private function add_profile_category() {
global $DB;
// Create a new profile category.
$cat = new stdClass();
$cat->name = 'Test category';
$cat->sortorder = 1;
return $DB->insert_record('user_info_category', $cat);
$cat = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Test category']);
return $cat->id;
}
/**
@ -297,20 +292,13 @@ class profilefield_datetime_testcase extends provider_testcase {
* @return int The ID of the profile field
*/
private function add_profile_field($categoryid, $datatype) {
global $DB;
// Create a new profile field.
$data = new stdClass();
$data->datatype = $datatype;
$data->shortname = 'tstField';
$data->name = 'Test field';
$data->description = 'This is a test.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $categoryid;
return $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => $datatype,
'shortname' => 'tstField',
'name' => 'Test field',
'description' => 'This is a test.',
'categoryid' => $categoryid,
]);
return $data->id;
}
}

View File

@ -277,13 +277,8 @@ class profilefield_menu_testcase extends provider_testcase {
* @return int The ID of the profile category
*/
private function add_profile_category() {
global $DB;
// Create a new profile category.
$cat = new stdClass();
$cat->name = 'Test category';
$cat->sortorder = 1;
return $DB->insert_record('user_info_category', $cat);
$cat = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Test category']);
return $cat->id;
}
/**
@ -294,20 +289,13 @@ class profilefield_menu_testcase extends provider_testcase {
* @return int The ID of the profile field
*/
private function add_profile_field($categoryid, $datatype) {
global $DB;
// Create a new profile field.
$data = new stdClass();
$data->datatype = $datatype;
$data->shortname = 'tstField';
$data->name = 'Test field';
$data->description = 'This is a test.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $categoryid;
return $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => $datatype,
'shortname' => 'tstField',
'name' => 'Test field',
'description' => 'This is a test.',
'categoryid' => $categoryid,
]);
return $data->id;
}
}

View File

@ -8,14 +8,16 @@ Feature: Social profile fields can not have a duplicate shortname.
Scenario: Verify you can edit social profile fields.
Given I log in as "admin"
When I navigate to "Users > Accounts > User profile fields" in site administration
And I set the field "datatype" to "Social"
And I click on "Create a new profile field" "link"
And I click on "Social" "link"
And I set the following fields to these values:
| Short name | yahoo |
| Networktype | Yahoo ID |
| Short name | yahoo |
And I click on "Save changes" "button"
And I set the field "datatype" to "Social"
And I click on "Create a new profile field" "link"
And I click on "Social" "link"
And I set the following fields to these values:
| Short name | yahoo |
| Networktype | Yahoo ID |
| Short name | yahoo |
And I click on "Save changes" "button"
Then I should see "This short name is already in use"

View File

@ -276,13 +276,8 @@ class profilefield_social_testcase extends provider_testcase {
* @return int The ID of the profile category
*/
private function add_profile_category() {
global $DB;
// Create a new profile category.
$cat = new stdClass();
$cat->name = 'Test category';
$cat->sortorder = 1;
return $DB->insert_record('user_info_category', $cat);
$cat = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Test category']);
return $cat->id;
}
/**
@ -293,20 +288,13 @@ class profilefield_social_testcase extends provider_testcase {
* @return int The ID of the profile field
*/
private function add_profile_field($categoryid, $datatype) {
global $DB;
// Create a new profile field.
$data = new stdClass();
$data->datatype = $datatype;
$data->shortname = 'icq';
$data->name = 'icq';
$data->description = '';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $categoryid;
return $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => $datatype,
'shortname' => 'icq',
'name' => 'icq',
'description' => '',
'categoryid' => $categoryid,
]);
return $data->id;
}
}

View File

@ -277,13 +277,8 @@ class profilefield_text_testcase extends provider_testcase {
* @return int The ID of the profile category
*/
private function add_profile_category() {
global $DB;
// Create a new profile category.
$cat = new stdClass();
$cat->name = 'Test category';
$cat->sortorder = 1;
return $DB->insert_record('user_info_category', $cat);
$cat = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Test category']);
return $cat->id;
}
/**
@ -294,20 +289,13 @@ class profilefield_text_testcase extends provider_testcase {
* @return int The ID of the profile field
*/
private function add_profile_field($categoryid, $datatype) {
global $DB;
// Create a new profile field.
$data = new stdClass();
$data->datatype = $datatype;
$data->shortname = 'tstField';
$data->name = 'Test field';
$data->description = 'This is a test.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $categoryid;
return $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => $datatype,
'shortname' => 'tstField',
'name' => 'Test field',
'description' => 'This is a test.',
'categoryid' => $categoryid,
]);
return $data->id;
}
}

View File

@ -276,13 +276,8 @@ class profilefield_textarea_testcase extends provider_testcase {
* @return int The ID of the profile category
*/
private function add_profile_category() {
global $DB;
// Create a new profile category.
$cat = new stdClass();
$cat->name = 'Test category';
$cat->sortorder = 1;
return $DB->insert_record('user_info_category', $cat);
$cat = $this->getDataGenerator()->create_custom_profile_field_category(['name' => 'Test category']);
return $cat->id;
}
/**
@ -293,20 +288,13 @@ class profilefield_textarea_testcase extends provider_testcase {
* @return int The ID of the profile field
*/
private function add_profile_field($categoryid, $datatype) {
global $DB;
// Create a new profile field.
$data = new stdClass();
$data->datatype = $datatype;
$data->shortname = 'tstField';
$data->name = 'Test field';
$data->description = 'This is a test.';
$data->required = false;
$data->locked = false;
$data->forceunique = false;
$data->signup = false;
$data->visible = '0';
$data->categoryid = $categoryid;
return $DB->insert_record('user_info_field', $data);
$data = $this->getDataGenerator()->create_custom_profile_field([
'datatype' => $datatype,
'shortname' => 'tstField',
'name' => 'Test field',
'description' => 'This is a test.',
'categoryid' => $categoryid,
]);
return $data->id;
}
}

View File

@ -32,10 +32,7 @@ $action = optional_param('action', '', PARAM_ALPHA);
$redirect = $CFG->wwwroot.'/user/profile/index.php';
$strchangessaved = get_string('changessaved');
$strcancelled = get_string('cancelled');
$strdefaultcategory = get_string('profiledefaultcategory', 'admin');
$strnofields = get_string('profilenofieldsdefined', 'admin');
$strcreatefield = get_string('profilecreatefield', 'admin');
@ -91,19 +88,6 @@ switch ($action) {
echo $OUTPUT->footer();
die;
break;
case 'editfield':
$id = optional_param('id', 0, PARAM_INT);
$datatype = optional_param('datatype', '', PARAM_ALPHA);
profile_edit_field($id, $datatype, $redirect);
die;
break;
case 'editcategory':
$id = optional_param('id', 0, PARAM_INT);
profile_edit_category($id, $redirect);
die;
break;
default:
// Normal form.
}
@ -124,14 +108,12 @@ if (empty($categories)) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('profilefields', 'admin'));
foreach ($categories as $category) {
$table = new html_table();
$table->head = array(get_string('profilefield', 'admin'), get_string('edit'));
$table->align = array('left', 'right');
$table->width = '95%';
$table->attributes['class'] = 'generaltable profilefield';
$table->data = array();
$outputcategories = [];
$options = profile_list_datatypes();
foreach ($categories as $category) {
// Category fields.
$outputfields = [];
if ($fields = $DB->get_records('user_info_field', array('categoryid' => $category->id), 'sortorder ASC')) {
foreach ($fields as $field) {
$fieldname = format_string($field->name);
@ -140,139 +122,46 @@ foreach ($categories as $category) {
if (class_exists($classname) && method_exists($classname, 'get_fieldname')) {
$fieldname = $classname::get_fieldname($field->name);
}
$table->data[] = array($fieldname, profile_field_icons($field));
$outputfields[] = [
'id' => $field->id,
'shortname' => $field->shortname,
'datatype' => $field->datatype,
'name' => $fieldname,
'isfirst' => !count($outputfields),
'islast' => count($outputfields) == count($fields) - 1,
];
}
}
echo $OUTPUT->heading(format_string($category->name) .' '.profile_category_icons($category));
if (count($table->data)) {
echo html_writer::table($table);
} else {
echo $OUTPUT->notification($strnofields);
// Add new field menu.
$menu = new \action_menu();
$menu->set_alignment(\action_menu::BL, \action_menu::BL);
$menu->set_menu_trigger($strcreatefield);
foreach ($options as $type => $fieldname) {
$action = new \action_menu_link_secondary(new \moodle_url('#'), null, $fieldname,
['data-action' => 'createfield', 'data-categoryid' => $category->id, 'data-datatype' => $type,
'data-datatypename' => $fieldname]);
$menu->add($action);
}
$menu->attributes['class'] .= ' float-left mr-1';
} // End of $categories foreach.
// Add category information to the template.
$outputcategories[] = [
'id' => $category->id,
'name' => format_string($category->name),
'fields' => $outputfields,
'hasfields' => count($outputfields),
'isfirst' => !count($outputcategories),
'islast' => count($outputcategories) == count($categories) - 1,
'candelete' => count($categories) > 1,
'addfieldmenu' => $menu->export_for_template($OUTPUT),
];
}
echo '<hr />';
echo '<div class="profileeditor">';
// Create a new field link.
$options = profile_list_datatypes();
$popupurl = new moodle_url('/user/profile/index.php?id=0&action=editfield');
echo $OUTPUT->single_select($popupurl, 'datatype', $options, '', array('' => get_string('choosedots')), 'newfieldform', array('label' => $strcreatefield));
// Add a div with a class so themers can hide, style or reposition the text.
html_writer::start_tag('div', array('class' => 'adminuseractionhint'));
echo get_string('or', 'lesson');
html_writer::end_tag('div');
// Create a new category link.
$options = array('action' => 'editcategory');
echo $OUTPUT->single_button(new moodle_url('index.php', $options), get_string('profilecreatecategory', 'admin'));
echo '</div>';
echo $OUTPUT->render_from_template('core_user/edit_profile_fields', [
'categories' => $outputcategories,
'sesskey' => sesskey(),
'baseurl' => (new moodle_url('/user/profile/index.php'))->out(false)
]);
echo $OUTPUT->footer();
die;
/***** Some functions relevant to this script *****/
/**
* Create a string containing the editing icons for the user profile categories
* @param stdClass $category the category object
* @return string the icon string
*/
function profile_category_icons($category) {
global $CFG, $USER, $DB, $OUTPUT;
$strdelete = get_string('delete');
$strmoveup = get_string('moveup');
$strmovedown = get_string('movedown');
$stredit = get_string('edit');
$categorycount = $DB->count_records('user_info_category');
$fieldcount = $DB->count_records('user_info_field', array('categoryid' => $category->id));
// Edit.
$editstr = '<a title="'.$stredit.'" href="index.php?id='.$category->id.'&amp;action=editcategory">' .
$OUTPUT->pix_icon('t/edit', $stredit) .'</a> ';
// Delete.
// Can only delete the last category if there are no fields in it.
if (($categorycount > 1) or ($fieldcount == 0)) {
$editstr .= '<a title="'.$strdelete.'"';
$editstr .= ' href="index.php?id='.$category->id.'&amp;action=deletecategory&amp;sesskey='.sesskey() . '">';
$editstr .= $OUTPUT->pix_icon('t/delete', $strdelete).'</a> ';
} else {
$editstr .= $OUTPUT->spacer() . ' ';
}
// Move up.
if ($category->sortorder > 1) {
$editstr .= '<a title="'.$strmoveup.'" ';
$editstr .= ' href="index.php?id='.$category->id.'&amp;action=movecategory&amp;dir=up&amp;sesskey='.sesskey().'">';
$editstr .= $OUTPUT->pix_icon('t/up', $strmoveup) . '</a> ';
} else {
$editstr .= $OUTPUT->spacer() . ' ';
}
// Move down.
if ($category->sortorder < $categorycount) {
$editstr .= '<a title="'.$strmovedown.'" ';
$editstr .= ' href="index.php?id='.$category->id.'&amp;action=movecategory&amp;dir=down&amp;sesskey='.sesskey().'">';
$editstr .= $OUTPUT->pix_icon('t/down', $strmovedown) . '</a> ';
} else {
$editstr .= $OUTPUT->spacer() . ' ';
}
return $editstr;
}
/**
* Create a string containing the editing icons for the user profile fields
* @param stdClass $field the field object
* @return string the icon string
*/
function profile_field_icons($field) {
global $CFG, $USER, $DB, $OUTPUT;
$strdelete = get_string('delete');
$strmoveup = get_string('moveup');
$strmovedown = get_string('movedown');
$stredit = get_string('edit');
$fieldcount = $DB->count_records('user_info_field', array('categoryid' => $field->categoryid));
$datacount = $DB->count_records('user_info_data', array('fieldid' => $field->id));
// Edit.
$editstr = '<a title="'.$stredit.'" href="index.php?id='.$field->id.'&amp;action=editfield">';
$editstr .= $OUTPUT->pix_icon('t/edit', $stredit) . '</a> ';
// Delete.
$editstr .= '<a title="'.$strdelete.'" href="index.php?id='.$field->id.'&amp;action=deletefield&amp;sesskey='.sesskey().'">';
$editstr .= $OUTPUT->pix_icon('t/delete', $strdelete) . '</a> ';
// Move up.
if ($field->sortorder > 1) {
$editstr .= '<a title="'.$strmoveup.'" ';
$editstr .= ' href="index.php?id='.$field->id.'&amp;action=movefield&amp;dir=up&amp;sesskey='.sesskey().'">';
$editstr .= $OUTPUT->pix_icon('t/up', $strmoveup) . '</a> ';
} else {
$editstr .= $OUTPUT->spacer() . ' ';
}
// Move down.
if ($field->sortorder < $fieldcount) {
$editstr .= '<a title="'.$strmovedown.'" ';
$editstr .= ' href="index.php?id='.$field->id.'&amp;action=movefield&amp;dir=down&amp;sesskey='.sesskey().'">';
$editstr .= $OUTPUT->pix_icon('t/down', $strmovedown) . '</a> ';
} else {
$editstr .= $OUTPUT->spacer() . ' ';
}
return $editstr;
}

View File

@ -1,95 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains the profile field category form.
*
* @package core_user
* @copyright 2007 onwards Shane Elliot {@link http://pukunui.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Class category_form
*
* @copyright 2007 onwards Shane Elliot {@link http://pukunui.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class category_form extends moodleform {
/**
* Define the form.
*/
public function definition () {
global $USER, $CFG;
$mform = $this->_form;
$strrequired = get_string('required');
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'editcategory');
$mform->setType('action', PARAM_ALPHANUMEXT);
$mform->addElement('text', 'name', get_string('profilecategoryname', 'admin'), 'maxlength="255" size="30"');
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', $strrequired, 'required', null, 'client');
$this->add_action_buttons(true);
}
/**
* Perform some moodle validation.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
global $CFG, $DB;
$errors = parent::validation($data, $files);
$data = (object)$data;
$duplicate = $DB->get_field('user_info_category', 'id', array('name' => $data->name));
// Check the name is unique.
if (!empty($data->id)) { // We are editing an existing record.
$olddata = $DB->get_record('user_info_category', array('id' => $data->id));
// Name has changed, new name in use, new name in use by another record.
$dupfound = (($olddata->name !== $data->name) && $duplicate && ($data->id != $duplicate));
} else { // New profile category.
$dupfound = $duplicate;
}
if ($dupfound ) {
$errors['name'] = get_string('profilecategorynamenotunique', 'admin');
}
return $errors;
}
}

View File

@ -1,100 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains the Field Form used for profile fields.
*
* @package core_user
* @copyright 2007 onwards Shane Elliot {@link http://pukunui.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Class field_form
*
* @copyright 2007 onwards Shane Elliot {@link http://pukunui.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_form extends moodleform {
/** @var profile_define_base $field */
public $field;
/**
* Define the form
*/
public function definition () {
global $CFG;
$mform = $this->_form;
// Everything else is dependant on the data type.
$datatype = $this->_customdata;
require_once($CFG->dirroot.'/user/profile/field/'.$datatype.'/define.class.php');
$newfield = 'profile_define_'.$datatype;
$this->field = new $newfield();
$strrequired = get_string('required');
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'editfield');
$mform->setType('action', PARAM_ALPHANUMEXT);
$mform->addElement('hidden', 'datatype', $datatype);
$mform->setType('datatype', PARAM_ALPHA);
$this->field->define_form($mform);
$this->add_action_buttons(true);
}
/**
* Alter definition based on existing or submitted data
*/
public function definition_after_data () {
$mform = $this->_form;
$this->field->define_after_data($mform);
}
/**
* Perform some moodle validation.
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
return $this->field->define_validate($data, $files);
}
/**
* Returns the defined editors for the field.
* @return mixed
*/
public function editors() {
return $this->field->define_editors();
}
}

View File

@ -84,7 +84,7 @@ class profile_field_base {
* Constructor method.
* @param int $fieldid id of the profile from the user_info_field table
* @param int $userid id of the user for whom we are displaying data
* @param object $fielddata optional data for the field object plus additional fields 'hasuserdata', 'data' and 'dataformat'
* @param stdClass $fielddata optional data for the field object plus additional fields 'hasuserdata', 'data' and 'dataformat'
* with user data. (If $fielddata->hasuserdata is empty, user data is not available and we should use default data).
* If this parameter is passed, constructor will not call load_data() at all.
*/
@ -130,7 +130,7 @@ class profile_field_base {
/**
* Abstract method: Adds the profile field to the moodle form class
* @abstract The following methods must be overwritten by child classes
* @param moodleform $mform instance of the moodleform class
* @param MoodleQuickForm $mform instance of the moodleform class
*/
public function edit_field_add($mform) {
print_error('mustbeoveride', 'debug', '', 'edit_field_add');
@ -148,7 +148,7 @@ class profile_field_base {
/**
* Print out the form field in the edit profile page
* @param moodleform $mform instance of the moodleform class
* @param MoodleQuickForm $mform instance of the moodleform class
* @return bool
*/
public function edit_field($mform) {
@ -164,7 +164,7 @@ class profile_field_base {
/**
* Tweaks the edit form
* @param moodleform $mform instance of the moodleform class
* @param MoodleQuickForm $mform instance of the moodleform class
* @return bool
*/
public function edit_after_data($mform) {
@ -179,7 +179,6 @@ class profile_field_base {
/**
* Saves the data coming from form
* @param stdClass $usernew data coming from the form
* @return mixed returns data id if success of db insert/update, false on fail, 0 if not permitted
*/
public function edit_save_data($usernew) {
global $DB;
@ -213,7 +212,7 @@ class profile_field_base {
* Validate the form field from profile page
*
* @param stdClass $usernew
* @return string contains error message otherwise null
* @return array error messages for the form validation
*/
public function edit_validate_field($usernew) {
global $DB;
@ -256,7 +255,7 @@ class profile_field_base {
/**
* Sets the default data for the field in the form object
* @param moodleform $mform instance of the moodleform class
* @param MoodleQuickForm $mform instance of the moodleform class
*/
public function edit_field_set_default($mform) {
if (!empty($this->field->defaultdata)) {
@ -267,7 +266,7 @@ class profile_field_base {
/**
* Sets the required flag for the field in the form object
*
* @param moodleform $mform instance of the moodleform class
* @param MoodleQuickForm $mform instance of the moodleform class
*/
public function edit_field_set_required($mform) {
global $USER;
@ -278,7 +277,7 @@ class profile_field_base {
/**
* HardFreeze the field if locked.
* @param moodleform $mform instance of the moodleform class
* @param MoodleQuickForm $mform instance of the moodleform class
*/
public function edit_field_set_locked($mform) {
if (!$mform->elementExists($this->inputname)) {
@ -389,6 +388,15 @@ class profile_field_base {
$this->categoryname = $categoryname;
}
/**
* Return field short name
*
* @return string
*/
public function get_shortname(): string {
return $this->field->shortname;
}
/**
* Returns the name of the profile category where this field is
*
@ -567,7 +575,7 @@ class profile_field_base {
* @param int $userid
* @return profile_field_base[]
*/
function profile_get_user_fields_with_data($userid) {
function profile_get_user_fields_with_data(int $userid): array {
global $DB, $CFG;
// Join any user info data present with each user info field for the user object.
@ -601,7 +609,7 @@ function profile_get_user_fields_with_data($userid) {
* @param int $userid
* @return profile_field_base[][]
*/
function profile_get_user_fields_with_data_by_category($userid) {
function profile_get_user_fields_with_data_by_category(int $userid): array {
$fields = profile_get_user_fields_with_data($userid);
$data = [];
foreach ($fields as $field) {
@ -614,9 +622,7 @@ function profile_get_user_fields_with_data_by_category($userid) {
* Loads user profile field data into the user object.
* @param stdClass $user
*/
function profile_load_data($user) {
global $CFG;
function profile_load_data(stdClass $user): void {
$fields = profile_get_user_fields_with_data($user->id);
foreach ($fields as $formfield) {
$formfield->edit_load_user_data($user);
@ -626,10 +632,10 @@ function profile_load_data($user) {
/**
* Print out the customisable categories and fields for a users profile
*
* @param moodleform $mform instance of the moodleform class
* @param int $userid id of user whose profile is being edited.
* @param MoodleQuickForm $mform instance of the moodleform class
* @param int $userid id of user whose profile is being edited or 0 for the new user
*/
function profile_definition($mform, $userid = 0) {
function profile_definition(MoodleQuickForm $mform, int $userid = 0): void {
$categories = profile_get_user_fields_with_data_by_category($userid);
foreach ($categories as $categoryid => $fields) {
// Check first if *any* fields will be displayed.
@ -655,12 +661,10 @@ function profile_definition($mform, $userid = 0) {
/**
* Adds profile fields to user edit forms.
* @param moodleform $mform
* @param MoodleQuickForm $mform
* @param int $userid
*/
function profile_definition_after_data($mform, $userid) {
global $CFG;
function profile_definition_after_data(MoodleQuickForm $mform, int $userid): void {
$userid = ($userid < 0) ? 0 : (int)$userid;
$fields = profile_get_user_fields_with_data($userid);
@ -673,11 +677,9 @@ function profile_definition_after_data($mform, $userid) {
* Validates profile data.
* @param stdClass $usernew
* @param array $files
* @return array
* @return array array of errors, same as in {@see moodleform::validation()}
*/
function profile_validation($usernew, $files) {
global $CFG;
function profile_validation(stdClass $usernew, array $files): array {
$err = array();
$fields = profile_get_user_fields_with_data($usernew->id);
foreach ($fields as $formfield) {
@ -690,7 +692,7 @@ function profile_validation($usernew, $files) {
* Saves profile data for a user.
* @param stdClass $usernew
*/
function profile_save_data($usernew) {
function profile_save_data(stdClass $usernew): void {
global $CFG;
$fields = profile_get_user_fields_with_data($usernew->id);
@ -701,10 +703,15 @@ function profile_save_data($usernew) {
/**
* Display profile fields.
*
* @deprecated since Moodle 3.11 MDL-71051 - please do not use this function any more.
* @todo MDL-71413 This will be deleted in Moodle 4.3.
*
* @param int $userid
*/
function profile_display_fields($userid) {
global $CFG, $USER, $DB;
debugging('Function profile_display_fields() is deprecated because it is no longer used and will be '.
'removed in future versions of Moodle', DEBUG_DEVELOPER);
$categories = profile_get_user_fields_with_data_by_category($userid);
foreach ($categories as $categoryid => $fields) {
@ -723,28 +730,16 @@ function profile_display_fields($userid) {
* @return array list of profile fields info
* @since Moodle 3.2
*/
function profile_get_signup_fields() {
global $CFG, $DB;
function profile_get_signup_fields(): array {
$profilefields = array();
// Only retrieve required custom fields (with category information)
// results are sort by categories, then by fields.
$sql = "SELECT uf.id as fieldid, ic.id as categoryid, ic.name as categoryname, uf.datatype
FROM {user_info_field} uf
JOIN {user_info_category} ic
ON uf.categoryid = ic.id AND uf.signup = 1 AND uf.visible<>0
ORDER BY ic.sortorder ASC, uf.sortorder ASC";
if ($fields = $DB->get_records_sql($sql)) {
foreach ($fields as $field) {
require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
$newfield = 'profile_field_'.$field->datatype;
$fieldobject = new $newfield($field->fieldid);
$fieldobjects = profile_get_user_fields_with_data(0);
foreach ($fieldobjects as $fieldobject) {
$field = (object)$fieldobject->get_field_config_for_external();
if ($fieldobject->get_category_name() !== null && $fieldobject->is_signup_field() && $field->visible <> 0) {
$profilefields[] = (object) array(
'categoryid' => $field->categoryid,
'categoryname' => $field->categoryname,
'fieldid' => $field->fieldid,
'categoryname' => $fieldobject->get_category_name(),
'fieldid' => $field->id,
'datatype' => $field->datatype,
'object' => $fieldobject
);
@ -756,9 +751,9 @@ function profile_get_signup_fields() {
/**
* Adds code snippet to a moodle form object for custom profile fields that
* should appear on the signup page
* @param moodleform $mform moodle form object
* @param MoodleQuickForm $mform moodle form object
*/
function profile_signup_fields($mform) {
function profile_signup_fields(MoodleQuickForm $mform): void {
if ($fields = profile_get_signup_fields()) {
foreach ($fields as $field) {
@ -774,13 +769,11 @@ function profile_signup_fields($mform) {
/**
* Returns an object with the custom profile fields set for the given user
* @param integer $userid
* @param int $userid
* @param bool $onlyinuserobject True if you only want the ones in $USER.
* @return stdClass
* @return stdClass object where properties names are shortnames of custom profile fields
*/
function profile_user_record($userid, $onlyinuserobject = true) {
global $CFG;
function profile_user_record(int $userid, bool $onlyinuserobject = true): stdClass {
$usercustomfields = new stdClass();
$fields = profile_get_user_fields_with_data($userid);
@ -807,33 +800,15 @@ function profile_user_record($userid, $onlyinuserobject = true) {
* @return array Array of field objects from database (indexed by id)
* @since Moodle 2.7.1
*/
function profile_get_custom_fields($onlyinuserobject = false) {
global $DB, $CFG;
// Get all the fields.
$fields = $DB->get_records('user_info_field', null, 'id ASC');
// If only doing the user object ones, unset the rest.
if ($onlyinuserobject) {
foreach ($fields as $id => $field) {
require_once($CFG->dirroot . '/user/profile/field/' .
$field->datatype . '/field.class.php');
$newfield = 'profile_field_' . $field->datatype;
$formfield = new $newfield();
if (!$formfield->is_user_object_data()) {
unset($fields[$id]);
}
function profile_get_custom_fields(bool $onlyinuserobject = false): array {
$fieldobjects = profile_get_user_fields_with_data(0);
$fields = [];
foreach ($fieldobjects as $fieldobject) {
if (!$onlyinuserobject || $fieldobject->is_user_object_data()) {
$fields[$fieldobject->fieldid] = (object)$fieldobject->get_field_config_for_external();
}
}
foreach ($fields as $index => $field) {
$component = 'profilefield_' . $field->datatype;
$classname = "\\$component\\helper";
if (class_exists($classname) && method_exists($classname, 'get_fieldname')) {
$fields[$index]->name = $classname::get_fieldname($field->name);
}
}
ksort($fields);
return $fields;
}
@ -855,8 +830,10 @@ function profile_load_custom_fields($user) {
function profile_save_custom_fields($userid, $profilefields) {
global $DB;
if ($fields = $DB->get_records('user_info_field')) {
foreach ($fields as $field) {
$fields = profile_get_user_fields_with_data(0);
if ($fields) {
foreach ($fields as $fieldobject) {
$field = (object)$fieldobject->get_field_config_for_external();
if (isset($profilefields[$field->shortname])) {
$conditions = array('fieldid' => $field->id, 'userid' => $userid);
$id = $DB->get_field('user_info_data', 'id', $conditions);
@ -877,26 +854,22 @@ function profile_save_custom_fields($userid, $profilefields) {
* current request for all fields so that it can be used quickly.
*
* @param string $shortname Shortname of custom profile field
* @return array Array with id, name, and visible fields
* @return stdClass|null Object with properties id, shortname, name, visible, datatype, categoryid, etc
*/
function profile_get_custom_field_data_by_shortname(string $shortname): array {
global $DB;
function profile_get_custom_field_data_by_shortname(string $shortname): ?stdClass {
$cache = \cache::make_from_params(cache_store::MODE_REQUEST, 'core_profile', 'customfields',
[], ['simplekeys' => true, 'simpledata' => true]);
$data = $cache->get($shortname);
if (!$data) {
if ($data === false) {
// If we don't have data, we get and cache it for all fields to avoid multiple DB requests.
$fields = $DB->get_records('user_info_field', null, '', 'id, shortname, name, visible');
$fields = profile_get_custom_fields();
$data = null;
foreach ($fields as $field) {
$cache->set($field->shortname, (array)$field);
$cache->set($field->shortname, $field);
if ($field->shortname === $shortname) {
$data = (array)$field;
$data = $field;
}
}
if (!$data) {
throw new \coding_exception('Unknown custom field: ' . $shortname);
}
}
return $data;
@ -946,15 +919,12 @@ function profile_view($user, $context, $course = null) {
* @return bool
*/
function profile_has_required_custom_fields_set($userid) {
global $DB;
$sql = "SELECT f.id
FROM {user_info_field} f
LEFT JOIN {user_info_data} d ON (d.fieldid = f.id AND d.userid = ?)
WHERE f.required = 1 AND f.visible > 0 AND f.locked = 0 AND d.id IS NULL";
if ($DB->record_exists_sql($sql, [$userid])) {
return false;
$profilefields = profile_get_user_fields_with_data($userid);
foreach ($profilefields as $profilefield) {
if ($profilefield->is_required() && !$profilefield->is_locked() &&
$profilefield->is_empty() && $profilefield->get_field_config_for_external()['visible']) {
return false;
}
}
return true;

View File

@ -0,0 +1,141 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_user/edit_profile_fields
UI for editing profile fields
Example context (json):
{
"baseurl": "index.php",
"sesskey": "12345",
"categories": [
{
"id": 1,
"name": "Cat1",
"fields": [
{"id": 1, "name": "Field1", "isfirst": true, "islast": false},
{"id": 2, "name": "Field2", "isfirst": false, "islast": false},
{"id": 3, "name": "Field3", "isfirst": false, "islast": true}
],
"hasfields": true,
"isfirst": true,
"candelete": true
},
{
"id": 2,
"name": "Cat2",
"candelete": true
},
{
"id": 3,
"name": "Cat3",
"islast": true,
"candelete": true
}
]
}
}}
<div class="row profileeditor">
<div class="col align-self-end">
<a tabindex="0" role="button" class="btn btn-secondary float-right" data-action="editcategory">{{#str}}profilecreatecategory, admin{{/str}}</a>
</div>
</div>
<div class="categorieslist">
{{#categories}}
<div data-category-id="{{id}}" id="category-{{id}}" class="mt-2">
<div class="row justify-content-between align-items-end">
<div class="col-6 categoryinstance">
<h3>
{{{name}}}
<a href="#" data-action="editcategory" data-id="{{id}}" data-name="{{name}}">
{{#pix}}t/edit, core, {{#str}}edit{{/str}}{{/pix}}</a>
{{#candelete}}
<a href="{{baseurl}}?action=deletecategory&id={{id}}&sesskey={{sesskey}}">
{{#pix}}t/delete, core, {{#str}}delete{{/str}}{{/pix}}</a>
{{/candelete}}
{{^isfirst}}
<a href="{{baseurl}}?id={{id}}&action=movecategory&dir=up&amp;sesskey={{sesskey}}">
{{#pix}}t/up, core, {{#str}}moveup{{/str}}{{/pix}}</a>
{{/isfirst}}
{{#isfirst}}{{#pix}}spacer, moodle{{/pix}}{{/isfirst}}
{{^islast}}
<a href="{{baseurl}}?id={{id}}&action=movecategory&dir=down&amp;sesskey={{sesskey}}">
{{#pix}}t/down, core, {{#str}}movedown{{/str}}{{/pix}}</a>
{{/islast}}
</h3>
</div>
<div class="col-auto text-right">
{{#addfieldmenu}}{{> core/action_menu}}{{/addfieldmenu}}
</div>
</div>
<table class="generaltable fullwidth profilefield">
{{#hasfields}}
<thead>
<tr>
<th scope="col" class="col-8">{{#str}}profilefield, admin{{/str}}</th>
<th scope="col" class="col-3 text-right">{{#str}}edit{{/str}}</th>
</tr>
</thead>
<tbody>
{{#fields}}
<tr>
<td class="col-8">
{{{name}}}
</td>
<td class="col-3 text-right">
<a href="#" data-action="editfield" data-id="{{id}}" data-name="{{name}}">
{{#pix}}t/edit, core, {{#str}}edit{{/str}}{{/pix}}</a>
<a href="{{baseurl}}?action=deletefield&id={{id}}&sesskey={{sesskey}}">
{{#pix}}t/delete, core, {{#str}}delete{{/str}}{{/pix}}</a>
{{^isfirst}}
<a href="{{baseurl}}?id={{id}}&action=movefield&dir=up&amp;sesskey={{sesskey}}">
{{#pix}}t/up, core, {{#str}}moveup{{/str}}{{/pix}}</a>
{{/isfirst}}
{{#isfirst}}{{#pix}}spacer, moodle{{/pix}}{{/isfirst}}
{{^islast}}
<a href="{{baseurl}}?id={{id}}&action=movefield&dir=down&amp;sesskey={{sesskey}}">
{{#pix}}t/down, core, {{#str}}movedown{{/str}}{{/pix}}</a>
{{/islast}}
{{#islast}}{{#pix}}spacer, moodle{{/pix}}{{/islast}}
</td>
</tr>
{{/fields}}
</tbody>
{{/hasfields}}
{{^hasfields}}
<thead>
<tr class="nofields alert alert-danger alert-block fade in">
<td>
{{#str}}profilenofieldsdefined, admin{{/str}}
</td>
</tr>
</thead>
{{/hasfields}}
</table>
</div>
{{/categories}}
</div>
{{#js}}
require(['core_user/edit_profile_fields'], function(s) {
s.init();
});
{{/js}}

View File

@ -17,7 +17,8 @@ Feature: Custom profile fields should be visible and editable by those with the
And I log in as "admin"
And I navigate to "Users > Accounts > User profile fields" in site administration
And I set the field "datatype" to "Text input"
And I click on "Create a new profile field" "link"
And I click on "Text input" "link"
And I set the following fields to these values:
| Short name | notvisible_field |
| Name | notvisible_field |
@ -25,7 +26,8 @@ Feature: Custom profile fields should be visible and editable by those with the
| Who is this field visible to? | Not visible |
And I click on "Save changes" "button"
And I set the field "datatype" to "Text input"
And I click on "Create a new profile field" "link"
And I click on "Text input" "link"
And I set the following fields to these values:
| Short name | uservisible_field |
| Name | uservisible_field |
@ -33,7 +35,8 @@ Feature: Custom profile fields should be visible and editable by those with the
| Who is this field visible to? | Visible to user |
And I click on "Save changes" "button"
And I set the field "datatype" to "Text input"
And I click on "Create a new profile field" "link"
And I click on "Text input" "link"
And I set the following fields to these values:
| Short name | everyonevisible_field |
| Name | everyonevisible_field |
@ -41,7 +44,8 @@ Feature: Custom profile fields should be visible and editable by those with the
| Who is this field visible to? | Visible to everyone |
And I click on "Save changes" "button"
And I set the field "datatype" to "Text input"
And I click on "Create a new profile field" "link"
And I click on "Text input" "link"
And I set the following fields to these values:
| Short name | teachervisible_field |
| Name | teachervisible_field |

View File

@ -39,16 +39,16 @@ class core_user_profilelib_testcase extends advanced_testcase {
* with profile_user_record.
*/
public function test_get_custom_fields() {
global $DB, $CFG;
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
// Add a custom field of textarea type.
$id1 = $DB->insert_record('user_info_field', array(
'shortname' => 'frogdesc', 'name' => 'Description of frog', 'categoryid' => 1,
'datatype' => 'textarea'));
$id1 = $this->getDataGenerator()->create_custom_profile_field([
'shortname' => 'frogdesc', 'name' => 'Description of frog',
'datatype' => 'textarea'])->id;
// Check the field is returned.
$result = profile_get_custom_fields();
@ -66,9 +66,9 @@ class core_user_profilelib_testcase extends advanced_testcase {
$this->assertObjectHasAttribute('frogdesc', profile_user_record($user->id, false));
// Add another custom field, this time of normal text type.
$id2 = $DB->insert_record('user_info_field', array(
'shortname' => 'frogname', 'name' => 'Name of frog', 'categoryid' => 1,
'datatype' => 'text'));
$id2 = $this->getDataGenerator()->create_custom_profile_field(array(
'shortname' => 'frogname', 'name' => 'Name of frog',
'datatype' => 'text'))->id;
// Check both are returned using normal option.
$result = profile_get_custom_fields();
@ -147,26 +147,26 @@ class core_user_profilelib_testcase extends advanced_testcase {
* Test that {@link user_not_fully_set_up()} takes required custom fields into account.
*/
public function test_profile_has_required_custom_fields_set() {
global $CFG, $DB;
global $CFG;
require_once($CFG->dirroot.'/mnet/lib.php');
$this->resetAfterTest();
// Add a required, visible, unlocked custom field.
$DB->insert_record('user_info_field', ['shortname' => 'house', 'name' => 'House', 'required' => 1,
'visible' => 1, 'locked' => 0, 'categoryid' => 1, 'datatype' => 'text']);
$this->getDataGenerator()->create_custom_profile_field(['shortname' => 'house', 'name' => 'House', 'required' => 1,
'visible' => 1, 'locked' => 0, 'datatype' => 'text']);
// Add an optional, visible, unlocked custom field.
$DB->insert_record('user_info_field', ['shortname' => 'pet', 'name' => 'Pet', 'required' => 0,
'visible' => 1, 'locked' => 0, 'categoryid' => 1, 'datatype' => 'text']);
$this->getDataGenerator()->create_custom_profile_field(['shortname' => 'pet', 'name' => 'Pet', 'required' => 0,
'visible' => 1, 'locked' => 0, 'datatype' => 'text']);
// Add required but invisible custom field.
$DB->insert_record('user_info_field', ['shortname' => 'secretid', 'name' => 'Secret ID', 'required' => 1,
'visible' => 0, 'locked' => 0, 'categoryid' => 1, 'datatype' => 'text']);
$this->getDataGenerator()->create_custom_profile_field(['shortname' => 'secretid', 'name' => 'Secret ID',
'required' => 1, 'visible' => 0, 'locked' => 0, 'datatype' => 'text']);
// Add required but locked custom field.
$DB->insert_record('user_info_field', ['shortname' => 'muggleborn', 'name' => 'Muggle-born', 'required' => 1,
'visible' => 1, 'locked' => 1, 'categoryid' => 1, 'datatype' => 'checkbox']);
$this->getDataGenerator()->create_custom_profile_field(['shortname' => 'muggleborn', 'name' => 'Muggle-born',
'required' => 1, 'visible' => 1, 'locked' => 1, 'datatype' => 'checkbox']);
// Create some student accounts.
$hermione = $this->getDataGenerator()->create_user();
@ -215,14 +215,14 @@ class core_user_profilelib_testcase extends advanced_testcase {
* Test that user generator sets the custom profile fields
*/
public function test_profile_fields_in_generator() {
global $CFG, $DB;
global $CFG;
require_once($CFG->dirroot.'/mnet/lib.php');
$this->resetAfterTest();
// Add a required, visible, unlocked custom field.
$DB->insert_record('user_info_field', ['shortname' => 'house', 'name' => 'House', 'required' => 1,
'visible' => 1, 'locked' => 0, 'categoryid' => 1, 'datatype' => 'text']);
$this->getDataGenerator()->create_custom_profile_field(['shortname' => 'house', 'name' => 'House', 'required' => 1,
'visible' => 1, 'locked' => 0, 'datatype' => 'text', 'defaultdata' => null]);
// Create some student accounts.
$hermione = $this->getDataGenerator()->create_user(['profile_field_house' => 'Gryffindor']);
@ -261,17 +261,17 @@ class core_user_profilelib_testcase extends advanced_testcase {
// Get the first field data and check it is correct.
$data = profile_get_custom_field_data_by_shortname('speciality');
$this->assertEquals('Speciality', $data['name']);
$this->assertEquals(PROFILE_VISIBLE_ALL, $data['visible']);
$this->assertEquals($field1->id, $data['id']);
$this->assertEquals('Speciality', $data->name);
$this->assertEquals(PROFILE_VISIBLE_ALL, $data->visible);
$this->assertEquals($field1->id, $data->id);
// Get the second field data, checking there is no database query this time.
$before = $DB->perf_get_queries();
$data = profile_get_custom_field_data_by_shortname('veggie');
$this->assertEquals($before, $DB->perf_get_queries());
$this->assertEquals('Vegetarian', $data['name']);
$this->assertEquals(PROFILE_VISIBLE_PRIVATE, $data['visible']);
$this->assertEquals($field2->id, $data['id']);
$this->assertEquals('Vegetarian', $data->name);
$this->assertEquals(PROFILE_VISIBLE_PRIVATE, $data->visible);
$this->assertEquals($field2->id, $data->id);
}
/**
@ -281,7 +281,6 @@ class core_user_profilelib_testcase extends advanced_testcase {
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
$this->expectExceptionMessage('Unknown custom field: speciality');
profile_get_custom_field_data_by_shortname('speciality');
$this->assertNull(profile_get_custom_field_data_by_shortname('speciality'));
}
}