diff --git a/admin/tool/uploaduser/tests/behat/upload_users.feature b/admin/tool/uploaduser/tests/behat/upload_users.feature
index 4cdfbd29b0ff1..04fb69b7898b9 100644
--- a/admin/tool/uploaduser/tests/behat/upload_users.feature
+++ b/admin/tool/uploaduser/tests/behat/upload_users.feature
@@ -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 |
diff --git a/availability/condition/profile/tests/behat/availability_profile.feature b/availability/condition/profile/tests/behat/availability_profile.feature
index 816e1fe73a81f..690bcd8dc2797 100644
--- a/availability/condition/profile/tests/behat/availability_profile.feature
+++ b/availability/condition/profile/tests/behat/availability_profile.feature
@@ -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 |
diff --git a/calendar/tests/calendartype_test.php b/calendar/tests/calendartype_test.php
index 4b32999de5b18..4e6e5dfc08c18 100644
--- a/calendar/tests/calendartype_test.php
+++ b/calendar/tests/calendartype_test.php
@@ -272,12 +272,13 @@ private function datetime_field_submission_test($type, $date) {
$formdata['name'] = 'Name';
$formdata['param1'] = $date['inputminyear'];
$formdata['param2'] = $date['inputmaxyear'];
+ $formdata['datatype'] = 'datetime';
// Mock submitting this.
\core_user\form\profile_field_form::mock_submit($formdata);
// Create the user datetime form.
- $form = new \core_user\form\profile_field_form(null, 'datetime');
+ $form = new \core_user\form\profile_field_form();
// Get the data from the submission.
$submissiondata = $form->get_data();
diff --git a/lang/en/admin.php b/lang/en/admin.php
index ae5cb2f0d630a..9f2e48fe7609c 100644
--- a/lang/en/admin.php
+++ b/lang/en/admin.php
@@ -989,7 +989,7 @@
$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).
Do you still wish to delete this category?';
$string['profileconfirmfielddeletion'] = 'There is/are {$a} user record/s for this field which will be deleted.
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';
diff --git a/user/amd/build/edit_profile_fields.min.js b/user/amd/build/edit_profile_fields.min.js
index 77c34c5bd718b..50d9ec4d57b99 100644
--- a/user/amd/build/edit_profile_fields.min.js
+++ b/user/amd/build/edit_profile_fields.min.js
@@ -1,2 +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\"]"}};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()}})}});
+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
diff --git a/user/amd/build/edit_profile_fields.min.js.map b/user/amd/build/edit_profile_fields.min.js.map
index 8b7e6ae93684d..1140d959d1a32 100644
--- a/user/amd/build/edit_profile_fields.min.js.map
+++ b/user/amd/build/edit_profile_fields.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/edit_profile_fields.js"],"names":["Selectors","actions","editCategory","init","document","addEventListener","e","element","target","closest","preventDefault","title","getAttribute","form","ModalForm","formClass","args","id","modalConfig","returnFocus","events","FORM_SUBMITTED","window","location","reload","show"],"mappings":"iLAeA,uD,GAYMA,CAAAA,CAAS,CAAG,CACdC,OAAO,CAAE,CACLC,YAAY,CAAE,gCADT,CADK,C,QAME,QAAPC,CAAAA,IAAO,EAAM,CACtBC,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAASC,CAAT,CAAY,CAC3C,GAAMC,CAAAA,CAAO,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiBT,CAAS,CAACC,OAAV,CAAkBC,YAAnC,CAAhB,CACA,GAAIK,CAAJ,CAAa,CACTD,CAAC,CAACI,cAAF,GADS,GAEHC,CAAAA,CAAK,CAAGJ,CAAO,CAACK,YAAR,CAAqB,SAArB,EACV,iBAAU,qBAAV,CAAiC,OAAjC,CAA0CL,CAAO,CAACK,YAAR,CAAqB,WAArB,CAA1C,CADU,CAEV,iBAAU,0BAAV,CAAsC,OAAtC,CAJK,CAKHC,CAAI,CAAG,GAAIC,UAAJ,CAAc,CACvBC,SAAS,CAAE,wCADY,CAEvBC,IAAI,CAAE,CAACC,EAAE,CAAEV,CAAO,CAACK,YAAR,CAAqB,SAArB,CAAL,CAFiB,CAGvBM,WAAW,CAAE,CAACP,KAAK,CAALA,CAAD,CAHU,CAIvBQ,WAAW,CAAEZ,CAJU,CAAd,CALJ,CAWTM,CAAI,CAACR,gBAAL,CAAsBQ,CAAI,CAACO,MAAL,CAAYC,cAAlC,CAAkD,iBAAMC,CAAAA,MAAM,CAACC,QAAP,CAAgBC,MAAhB,EAAN,CAAlD,EACAX,CAAI,CAACY,IAAL,EACH,CACJ,CAhBD,CAiBH,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * User profile fields editor\n *\n * @module core_user/edit_profile_fields\n * @package core_user\n * @copyright 2021 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n actions: {\n editCategory: '[data-action=\"editcategory\"]',\n },\n};\n\nexport const init = () => {\n document.addEventListener('click', function(e) {\n const element = e.target.closest(Selectors.actions.editCategory);\n if (element) {\n e.preventDefault();\n const title = element.getAttribute('data-id') ?\n getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :\n getString('profilecreatenewcategory', 'admin');\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_category_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n });\n};"],"file":"edit_profile_fields.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/edit_profile_fields.js"],"names":["Selectors","actions","editCategory","editField","createField","init","document","addEventListener","e","element","target","closest","preventDefault","title","getAttribute","form","ModalForm","formClass","args","id","modalConfig","returnFocus","events","FORM_SUBMITTED","window","location","reload","show","datatype","categoryid"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * User profile fields editor\n *\n * @module core_user/edit_profile_fields\n * @package core_user\n * @copyright 2021 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n actions: {\n editCategory: '[data-action=\"editcategory\"]',\n editField: '[data-action=\"editfield\"]',\n createField: '[data-action=\"createfield\"]',\n },\n};\n\nexport const init = () => {\n document.addEventListener('click', function(e) {\n let element = e.target.closest(Selectors.actions.editCategory);\n if (element) {\n e.preventDefault();\n const title = element.getAttribute('data-id') ?\n getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :\n getString('profilecreatenewcategory', 'admin');\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_category_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.editField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title: getString('profileeditfield', 'admin', element.getAttribute('data-name'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.createField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {datatype: element.getAttribute('data-datatype'), categoryid: element.getAttribute('data-categoryid')},\n modalConfig: {title: getString('profilecreatenewfield', 'admin', element.getAttribute('data-datatypename'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n });\n};"],"file":"edit_profile_fields.min.js"}
\ No newline at end of file
diff --git a/user/amd/src/edit_profile_fields.js b/user/amd/src/edit_profile_fields.js
index 29662356b19ce..04916db48253a 100644
--- a/user/amd/src/edit_profile_fields.js
+++ b/user/amd/src/edit_profile_fields.js
@@ -28,12 +28,14 @@ import {get_string as getString} from 'core/str';
const Selectors = {
actions: {
editCategory: '[data-action="editcategory"]',
+ editField: '[data-action="editfield"]',
+ createField: '[data-action="createfield"]',
},
};
export const init = () => {
document.addEventListener('click', function(e) {
- const element = e.target.closest(Selectors.actions.editCategory);
+ let element = e.target.closest(Selectors.actions.editCategory);
if (element) {
e.preventDefault();
const title = element.getAttribute('data-id') ?
@@ -48,5 +50,31 @@ export const init = () => {
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();
+ }
});
};
\ No newline at end of file
diff --git a/user/classes/form/profile_field_form.php b/user/classes/form/profile_field_form.php
index 589c38964ebf0..ff2935c95d009 100644
--- a/user/classes/form/profile_field_form.php
+++ b/user/classes/form/profile_field_form.php
@@ -14,49 +14,42 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * 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
- */
-
namespace core_user\form;
+use context;
+use core_form\dynamic_form;
+use moodle_url;
use profile_define_base;
-defined('MOODLE_INTERNAL') || die;
-
-require_once($CFG->dirroot.'/lib/formslib.php');
-
/**
- * Class field_form
+ * 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 \moodleform {
+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->_customdata;
+ $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();
- $strrequired = get_string('required');
-
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
@@ -66,8 +59,6 @@ public function definition () {
$mform->setType('datatype', PARAM_ALPHA);
$this->field->define_form($mform);
-
- $this->add_action_buttons(true);
}
@@ -92,10 +83,100 @@ public function validation($data, $files) {
/**
* Returns the defined editors for the field.
- * @return mixed
+ * @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.
+ $field->description = clean_text($field->description, $field->descriptionformat);
+ $field->description = array('text' => $field->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)) {
+ $field->$editor = clean_text($field->$editor, $field->{$editor.'format'});
+ $field->$editor = array('text' => $field->$editor, '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 editors() {
- return $this->field->define_editors();
+ 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;
}
}
diff --git a/user/profile/definelib.php b/user/profile/definelib.php
index ee654c6aa4c3d..f9c2e47d3661d 100644
--- a/user/profile/definelib.php
+++ b/user/profile/definelib.php
@@ -514,13 +514,18 @@ function profile_save_category(stdClass $data) {
/**
* 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;
- debugging('Function profile_edit_category() deprecated without replacement', DEBUG_DEVELOPER);
+ debugging('Function profile_edit_category() is deprecated without replacement, see also profile_save_category()',
+ DEBUG_DEVELOPER);
$categoryform = new \core_user\form\profile_category_form();
@@ -553,77 +558,72 @@ function profile_edit_category($id, $redirect) {
}
/**
- * Edit a profile field.
+ * Save updated field definition or create a new field
*
- * @param int $id
- * @param string $datatype
- * @param string $redirect
+ * @param stdClass $data data from the form profile_field_form
+ * @param array $editors editors for this form field type
*/
-function profile_edit_field($id, $datatype, $redirect) {
- global $CFG, $DB, $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;
+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'];
}
- // 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);
-
- $fieldform = new \core_user\form\profile_field_form(null, $field->datatype);
-
// Convert the data format for.
- if (is_array($fieldform->editors())) {
- foreach ($fieldform->editors() as $editor) {
+ if (is_array($editors)) {
+ foreach ($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);
+ $field->{$editor.'format'} = $field->{$editor}['format'];
+ $field->$editor = $field->{$editor}['text'];
}
}
}
- $fieldform->set_data($field);
+ $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 $OUTPUT, $PAGE;
+
+ debugging('Function profile_edit_field() is deprecated without replacement, see also profile_save_field()',
+ DEBUG_DEVELOPER);
+
+ $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;
- /** @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($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);
}
@@ -632,7 +632,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.
diff --git a/user/profile/index.php b/user/profile/index.php
index 26c701dd809e1..c2806e64fbf99 100644
--- a/user/profile/index.php
+++ b/user/profile/index.php
@@ -88,13 +88,6 @@
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;
default:
// Normal form.
}
@@ -116,7 +109,10 @@
echo $OUTPUT->heading(get_string('profilefields', 'admin'));
$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) {
@@ -129,6 +125,7 @@
$outputfields[] = [
'id' => $field->id,
'shortname' => $field->shortname,
+ 'datatype' => $field->datatype,
'name' => $fieldname,
'isfirst' => !count($outputfields),
'islast' => count($outputfields) == count($fields) - 1,
@@ -136,6 +133,19 @@
}
}
+ // 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';
+
+ // Add category information to the template.
$outputcategories[] = [
'id' => $category->id,
'name' => format_string($category->name),
@@ -144,18 +154,12 @@
'isfirst' => !count($outputcategories),
'islast' => count($outputcategories) == count($categories) - 1,
'candelete' => count($categories) > 1,
+ 'addfieldmenu' => $menu->export_for_template($OUTPUT),
];
}
-// Create a new field link.
-$options = profile_list_datatypes();
-$popupurl = new moodle_url('/user/profile/index.php?id=0&action=editfield');
-$singleselect = new single_select($popupurl, 'datatype', $options, '', ['' => get_string('choosedots')], 'newfieldform');
-$singleselect->set_label($strcreatefield);
-
echo $OUTPUT->render_from_template('core_user/edit_profile_fields', [
'categories' => $outputcategories,
- 'elementselect' => $singleselect->export_for_template($OUTPUT),
'sesskey' => sesskey(),
'baseurl' => (new moodle_url('/user/profile/index.php'))->out(false)
]);
diff --git a/user/templates/edit_profile_fields.mustache b/user/templates/edit_profile_fields.mustache
index 333743dcd3629..97faa9e4b8b42 100644
--- a/user/templates/edit_profile_fields.mustache
+++ b/user/templates/edit_profile_fields.mustache
@@ -47,30 +47,45 @@
"islast": true,
"candelete": true
}
- ],
- "elementselect": {"options": [{"name": "Choose..."}]}
+ ]
}
}}
+
+
+
{{#categories}}
-
+
+
+
+
+ {{#addfieldmenu}}{{> core/action_menu}}{{/addfieldmenu}}
+
+
+
+
{{/categories}}
-
-
-
-
{{#js}}
diff --git a/user/tests/behat/custom_profile_fields.feature b/user/tests/behat/custom_profile_fields.feature
index a17a063c470ba..31475fc59b7b0 100644
--- a/user/tests/behat/custom_profile_fields.feature
+++ b/user/tests/behat/custom_profile_fields.feature
@@ -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 |