MDL-87895 customfield: Improve custom fields management UI
- Refactor core_customfield\output\management to improve maintainability. - Separate list.mustache inner contents into category.mustache and field.mustache to improve maintainability. - Convert customfield categories containers into '.card'. - Update templates to improve the UI. - Separate component custom field categories and shared categories into different containers to improve clarity. - Add limitedwidth class to course field management pages.
This commit is contained in:
@@ -5,10 +5,6 @@ notes:
|
||||
A new WebService `core_customfield_convert_category` has been added.
|
||||
It allows the conversion of any entity custom field category to a shared category.
|
||||
type: improved
|
||||
- message: >-
|
||||
The WebService `core_customfield_reload_template` now returns a new
|
||||
parameter "canconvert".
|
||||
type: changed
|
||||
- message: >-
|
||||
The Javascript module `core_customfield/repository/toggle_shared` has been deprecated.
|
||||
Please, use `core_customfield/repository` instead.
|
||||
|
||||
@@ -30,6 +30,8 @@ require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('cohort_customfield');
|
||||
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
$output = $PAGE->get_renderer('core_customfield');
|
||||
$handler = cohort_handler::create();
|
||||
$outputpage = new management($handler);
|
||||
|
||||
@@ -27,6 +27,8 @@ require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('course_customfield');
|
||||
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
$output = $PAGE->get_renderer('core_customfield');
|
||||
$handler = core_course\customfield\course_handler::create();
|
||||
$outputpage = new \core_customfield\output\management($handler);
|
||||
|
||||
@@ -28,8 +28,14 @@ use core_customfield\api;
|
||||
use core_customfield\customfield\shared_handler;
|
||||
use core_customfield\handler;
|
||||
use core_customfield\shared;
|
||||
use renderable;
|
||||
use templatable;
|
||||
use core_customfield\field_controller;
|
||||
use core\url;
|
||||
use core\output\action_menu;
|
||||
use core\output\pix_icon;
|
||||
use core\output\renderer_base;
|
||||
use core\output\renderable;
|
||||
use core\output\templatable;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
@@ -56,134 +62,297 @@ class management implements renderable, templatable {
|
||||
*
|
||||
* @param \core_customfield\handler $handler
|
||||
*/
|
||||
public function __construct(\core_customfield\handler $handler) {
|
||||
public function __construct(handler $handler) {
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export for template
|
||||
*
|
||||
* @param \renderer_base $output
|
||||
* @return array|object|\stdClass
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(\renderer_base $output) {
|
||||
global $DB;
|
||||
$data = new \stdClass();
|
||||
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
$fieldtypes = $this->handler->get_available_field_types();
|
||||
|
||||
$data->component = $this->handler->get_component();
|
||||
$data->area = $this->handler->get_area();
|
||||
$data->itemid = $this->handler->get_itemid();
|
||||
$data->usescategories = $this->handler->uses_categories();
|
||||
$categories = $this->handler->get_categories_with_fields(true);
|
||||
$component = $this->handler->get_component();
|
||||
$area = $this->handler->get_area();
|
||||
$itemid = $this->handler->get_itemid();
|
||||
$sharedhandler = shared_handler::get_handler('core_customfield', 'shared');
|
||||
|
||||
// Get all enabled shared categories at once.
|
||||
$sharedcategoriesenabled = shared::get_records([
|
||||
'component' => $data->component,
|
||||
'area' => $data->area,
|
||||
'itemid' => $data->itemid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
]);
|
||||
foreach ($sharedcategoriesenabled as $record) {
|
||||
$sharedcategoriesenabledbyid[$record->get('categoryid')] = $record;
|
||||
}
|
||||
|
||||
$categoriesarray = array();
|
||||
$categoriesdata = [];
|
||||
$movablefieldscount = 0;
|
||||
$movablecategoriescount = 0;
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$categoryid = $category->get('id');
|
||||
$categoryname = $category->get_formatted_name();
|
||||
$canedit = $component === $category->get('component') && $area === $category->get('area');
|
||||
$canconvert = $sharedhandler->can_configure() && !$category->get('shared');
|
||||
$nameeditable = $canedit
|
||||
? $output->render(api::get_category_inplace_editable($category, true))
|
||||
: $categoryname;
|
||||
$sharedtogglestatus = !empty($sharedcategoriesenabledbyid[$categoryid]);
|
||||
$hasduplicatecustomfield = false;
|
||||
|
||||
$canedit = $data->component === $category->get('component') && $data->area === $category->get('area');
|
||||
|
||||
$categoryarray = array();
|
||||
$categoryarray['id'] = $category->get('id');
|
||||
$categoryarray['name'] = $category->get_formatted_name();
|
||||
$categoryarray['nameeditable'] = $canedit ? $output->render(api::get_category_inplace_editable($category, true)) :
|
||||
$category->get_formatted_name();
|
||||
$categoryarray['movetitle'] = get_string('movecategory', 'core_customfield',
|
||||
$category->get_formatted_name());
|
||||
$categoryarray['canedit'] = $canedit;
|
||||
|
||||
$toggleenabled = (bool) array_filter(
|
||||
$sharedcategoriesenabled,
|
||||
fn($record) => $record->get('categoryid') === $category->get('id')
|
||||
);
|
||||
$attributes = [
|
||||
['name' => 'data-id', 'value' => $category->get('id')],
|
||||
['name' => 'data-action', 'value' => 'shared-toggle'],
|
||||
['name' => 'data-state', 'value' => $toggleenabled],
|
||||
['name' => 'data-component', 'value' => $data->component],
|
||||
['name' => 'data-area', 'value' => $data->area],
|
||||
['name' => 'data-itemid', 'value' => $data->itemid],
|
||||
];
|
||||
if (!$canedit) {
|
||||
$categoryarray['toggle'] = $output->render_from_template('core/toggle', [
|
||||
'id' => 'shared-toggle-' . $category->get('id'),
|
||||
'checked' => $toggleenabled,
|
||||
'extraattributes' => $attributes,
|
||||
'label' => get_string('enableplugin', 'core_admin', $category->get_formatted_name()),
|
||||
'labelclasses' => 'visually-hidden',
|
||||
]);
|
||||
if ($canedit) {
|
||||
$movablecategoriescount++;
|
||||
}
|
||||
|
||||
$sharedhandler = shared_handler::get_handler('core_customfield', 'shared');
|
||||
$categoryarray['canconvert'] = $sharedhandler->can_configure() && !$category->get('shared');
|
||||
$categoryarray['hasduplicatecustomfield'] = false;
|
||||
|
||||
$categoryarray['fields'] = array();
|
||||
$categoryaddfieldmenu = $this->get_create_new_field_action_menu($fieldtypes, $categoryid, $canedit);
|
||||
$categorysharetoggle = $this->get_share_category_toggle(
|
||||
$output,
|
||||
$categoryid,
|
||||
$categoryname,
|
||||
$sharedtogglestatus,
|
||||
$component,
|
||||
$area,
|
||||
$itemid,
|
||||
$canedit
|
||||
);
|
||||
|
||||
$categoryfields = [];
|
||||
foreach ($category->get_fields() as $field) {
|
||||
|
||||
$fieldname = $field->get_formatted_name();
|
||||
$fieldarray['type'] = $fieldtypes[$field->get('type')];
|
||||
$fieldarray['id'] = $field->get('id');
|
||||
$fieldarray['name'] = $fieldname;
|
||||
$fieldarray['shortname'] = $field->get('shortname');
|
||||
$fieldarray['movetitle'] = get_string('movefield', 'core_customfield', $fieldname);
|
||||
$categoryarray['canedit'] = $canedit;
|
||||
|
||||
$categoryarray['fields'][] = $fieldarray;
|
||||
|
||||
if ($categoryarray['canconvert'] && !$categoryarray['hasduplicatecustomfield']) {
|
||||
$fieldisunique = api::is_shortname_unique($this->handler, $fieldarray['shortname'], $fieldarray['id']);
|
||||
$categoryarray['hasduplicatecustomfield'] = !$fieldisunique;
|
||||
if ($canconvert && !$hasduplicatecustomfield) {
|
||||
$fieldisunique = api::is_shortname_unique($this->handler, $field->get('shortname'), $field->get('id'));
|
||||
$hasduplicatecustomfield = !$fieldisunique;
|
||||
}
|
||||
if ($canedit) {
|
||||
$movablefieldscount++;
|
||||
}
|
||||
$categoryfields[] = $this->build_field_data(
|
||||
$field,
|
||||
$fieldtypes,
|
||||
$output,
|
||||
$canedit,
|
||||
);
|
||||
}
|
||||
|
||||
if ($canedit) {
|
||||
$menu = new \action_menu();
|
||||
$menu->set_menu_trigger(get_string('createnewcustomfield', 'core_customfield'));
|
||||
$categoryactionsmenu = $this->get_category_action_menu($categoryid, $canedit, $canconvert, $hasduplicatecustomfield);
|
||||
|
||||
foreach ($fieldtypes as $type => $fieldname) {
|
||||
$params = [
|
||||
'data-role' => 'addfield',
|
||||
'data-categoryid' => $category->get('id'),
|
||||
'data-type' => $type,
|
||||
'data-typename' => $fieldname,
|
||||
];
|
||||
$action = new \action_menu_link_secondary(new \core\url('#'), null, $fieldname, $params);
|
||||
$menu->add($action);
|
||||
}
|
||||
$menu->attributes['class'] .= ' float-start me-1';
|
||||
$categorydata = [
|
||||
'id' => $categoryid,
|
||||
'name' => $categoryname,
|
||||
'nameeditable' => $nameeditable,
|
||||
'movetitle' => get_string('movecategory', 'core_customfield', $categoryname),
|
||||
'canedit' => $canedit,
|
||||
'actionsmenu' => $categoryactionsmenu ? $output->render($categoryactionsmenu) : '',
|
||||
'addfieldmenu' => $categoryaddfieldmenu ? $output->render($categoryaddfieldmenu) : '',
|
||||
'toggle' => $categorysharetoggle,
|
||||
'fields' => $categoryfields,
|
||||
];
|
||||
|
||||
$categoryarray['addfieldmenu'] = $output->render($menu);
|
||||
$movablecategoriescount++;
|
||||
} else {
|
||||
$categoryarray['addfieldmenu'] = '';
|
||||
}
|
||||
|
||||
$categoriesarray[] = $categoryarray;
|
||||
$categoriesdata[] = $categorydata;
|
||||
}
|
||||
|
||||
$data->categories = $categoriesarray;
|
||||
$data->canmovecategories = $movablecategoriescount > 1;
|
||||
// Can move fields if there are more than one field or if there are multiple categories.
|
||||
$data->canmovefields = $movablefieldscount > 1 || $data->canmovecategories;
|
||||
return (object)[
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
'usescategories' => $this->handler->uses_categories(),
|
||||
'hascategories' => $movablecategoriescount > 0,
|
||||
'hassharedcategories' => $movablecategoriescount < count($categoriesdata),
|
||||
'categories' => $categoriesdata,
|
||||
'canmovecategories' => $movablecategoriescount > 1,
|
||||
'canmovefields' => $movablefieldscount > 1 || $movablecategoriescount > 1,
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($data->categories)) {
|
||||
$data->nocategories = get_string('nocategories', 'core_customfield');
|
||||
/**
|
||||
* Get the action menu for a custom field category.
|
||||
*
|
||||
* @param int $categoryid The category ID.
|
||||
* @param bool $canedit Whether the user can edit the category.
|
||||
* @param bool $canconvert Whether the user can convert the category.
|
||||
* @param bool $hasduplicatecustomfield Whether the category has duplicated custom field short names.
|
||||
* @return action_menu|null The action menu.
|
||||
*/
|
||||
private function get_category_action_menu(
|
||||
int $categoryid,
|
||||
bool $canedit,
|
||||
bool $canconvert,
|
||||
bool $hasduplicatecustomfield
|
||||
): ?action_menu {
|
||||
if (!$canedit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
$menu = new action_menu();
|
||||
$menu->set_kebab_trigger(triggername: get_string('actions'));
|
||||
$menu->add(new \action_menu_link_secondary(
|
||||
url: new url('#'),
|
||||
icon: new pix_icon('t/delete', 'core'),
|
||||
text: get_string('delete'),
|
||||
attributes: [
|
||||
'data-role' => 'deletecategory',
|
||||
'data-id' => $categoryid,
|
||||
]
|
||||
));
|
||||
|
||||
if ($canconvert) {
|
||||
$menu->add(new \action_menu_link_secondary(
|
||||
url: new url('#'),
|
||||
icon: new pix_icon('i/siteevent', 'core'),
|
||||
text: get_string('convertcategory', 'core_customfield'),
|
||||
attributes: [
|
||||
'data-role' => !$hasduplicatecustomfield ? 'convertcategory' : 'hasduplicatecustomfield',
|
||||
'data-id' => $categoryid,
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action menu for a custom field.
|
||||
*
|
||||
* @param field_controller $field The custom field.
|
||||
* @param bool $canedit Whether the user can edit the field.
|
||||
* @return action_menu|null The action menu.
|
||||
*/
|
||||
private function get_customfield_action_menu(field_controller $field, bool $canedit): ?action_menu {
|
||||
if (!$canedit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menu = new action_menu();
|
||||
$menu->set_additional_classes('d-flex justify-content-end');
|
||||
$menu->set_kebab_trigger(triggername: get_string('actions'), extraclasses: '');
|
||||
|
||||
$fieldname = $field->get_formatted_name();
|
||||
|
||||
$menu->add(new \action_menu_link_secondary(
|
||||
url: new url('#'),
|
||||
icon: new pix_icon('t/edit', 'core'),
|
||||
text: get_string('edit'),
|
||||
attributes: ['data-role' => 'editfield', 'data-name' => $fieldname, 'data-id' => $field->get('id')]
|
||||
));
|
||||
$menu->add(new \action_menu_link_secondary(
|
||||
url: new url('#'),
|
||||
icon: new pix_icon('t/delete', 'core'),
|
||||
text: get_string('delete'),
|
||||
attributes: ['data-role' => 'deletefield', 'data-id' => $field->get('id')]
|
||||
));
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "Create new field" action menu.
|
||||
*
|
||||
* @param array $fieldtypes The available field types.
|
||||
* @param int $categoryid The category ID.
|
||||
* @param bool $canedit Whether the user can edit the category.
|
||||
* @return action_menu|null The action menu.
|
||||
*/
|
||||
private function get_create_new_field_action_menu(array $fieldtypes, int $categoryid, bool $canedit): ?action_menu {
|
||||
if (!$canedit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menu = new action_menu();
|
||||
$menu->set_menu_trigger(
|
||||
get_string('createnewcustomfield', 'core_customfield'),
|
||||
'btn btn-sm btn-subtle-body dropdown-toggle'
|
||||
);
|
||||
|
||||
foreach ($fieldtypes as $type => $fieldname) {
|
||||
$params = [
|
||||
'data-role' => 'addfield',
|
||||
'data-categoryid' => $categoryid,
|
||||
'data-type' => $type,
|
||||
'data-typename' => $fieldname,
|
||||
];
|
||||
$action = new \action_menu_link_secondary(new url('#'), null, $fieldname, $params);
|
||||
$menu->add($action);
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the field array for a custom field.
|
||||
*
|
||||
* @param field_controller $field The custom field.
|
||||
* @param array $fieldtypes The available field types.
|
||||
* @param \renderer_base $output The renderer.
|
||||
* @param bool $canedit Whether the field can be edited.
|
||||
* @return array The field data.
|
||||
*/
|
||||
private function build_field_data(
|
||||
field_controller $field,
|
||||
array $fieldtypes,
|
||||
renderer_base $output,
|
||||
bool $canedit,
|
||||
): array {
|
||||
$fieldname = $field->get_formatted_name();
|
||||
$actionsmenu = $this->get_customfield_action_menu($field, $canedit);
|
||||
|
||||
$fieldarray = [
|
||||
'type' => $fieldtypes[$field->get('type')],
|
||||
'id' => $field->get('id'),
|
||||
'name' => $fieldname,
|
||||
'shortname' => $field->get('shortname'),
|
||||
'movetitle' => get_string('movefield', 'core_customfield', $fieldname),
|
||||
'actionsmenu' => $actionsmenu ? $output->render($actionsmenu) : '',
|
||||
];
|
||||
|
||||
return $fieldarray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the shared toggle for a category.
|
||||
*
|
||||
* @param renderer_base $output The renderer.
|
||||
* @param int $categoryid The category ID.
|
||||
* @param string $categoryname The category name.
|
||||
* @param bool $sharedenabled Whether sharing is enabled.
|
||||
* @param string $component The component name.
|
||||
* @param string $area The area name.
|
||||
* @param int $itemid The item ID.
|
||||
* @param bool $canedit Whether the category can be edited.
|
||||
* @return string The rendered toggle HTML.
|
||||
*/
|
||||
private function get_share_category_toggle(
|
||||
renderer_base $output,
|
||||
int $categoryid,
|
||||
string $categoryname,
|
||||
bool $sharedenabled,
|
||||
string $component,
|
||||
string $area,
|
||||
int $itemid,
|
||||
bool $canedit
|
||||
): string {
|
||||
if ($canedit) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$attributes = [
|
||||
['name' => 'data-id', 'value' => $categoryid],
|
||||
['name' => 'data-action', 'value' => 'shared-toggle'],
|
||||
['name' => 'data-state', 'value' => $sharedenabled ? '1' : '0'],
|
||||
['name' => 'data-component', 'value' => $component],
|
||||
['name' => 'data-area', 'value' => $area],
|
||||
['name' => 'data-itemid', 'value' => $itemid],
|
||||
];
|
||||
|
||||
return $output->render_from_template('core/toggle', [
|
||||
'id' => 'shared-toggle-' . $categoryid,
|
||||
'checked' => $sharedenabled,
|
||||
'extraattributes' => $attributes,
|
||||
'label' => get_string('enableplugin', 'core_admin', $categoryname),
|
||||
'labelclasses' => 'visually-hidden',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('sharedcustomfields');
|
||||
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
/** @var \core_customfield\output\renderer $output */
|
||||
$output = $PAGE->get_renderer('core_customfield');
|
||||
$handler = \core_customfield\customfield\shared_handler::create();
|
||||
|
||||
@@ -114,6 +114,8 @@ class core_customfield_external extends external_api {
|
||||
'area' => new external_value(PARAM_ALPHANUMEXT, 'area'),
|
||||
'itemid' => new external_value(PARAM_INT, 'itemid'),
|
||||
'usescategories' => new external_value(PARAM_BOOL, 'view has categories'),
|
||||
'hascategories' => new external_value(PARAM_BOOL, 'has categories'),
|
||||
'hassharedcategories' => new external_value(PARAM_BOOL, 'has shared categories'),
|
||||
'categories' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
@@ -121,9 +123,9 @@ class core_customfield_external extends external_api {
|
||||
'name' => new external_value(PARAM_TEXT, 'name'),
|
||||
'nameeditable' => new external_value(PARAM_RAW, 'inplace editable name'),
|
||||
'addfieldmenu' => new external_value(PARAM_RAW, 'addfieldmenu'),
|
||||
'actionsmenu' => new external_value(PARAM_RAW, 'actionsmenu'),
|
||||
'movetitle' => new external_value(PARAM_TEXT, 'movetitle'),
|
||||
'canedit' => new external_value(PARAM_BOOL, 'can edit'),
|
||||
'canconvert' => new external_value(PARAM_BOOL, 'can convert to shared category'),
|
||||
'hasduplicatecustomfield' => new external_value(PARAM_BOOL, 'category has a duplicate field name?'),
|
||||
'fields' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
@@ -131,10 +133,13 @@ class core_customfield_external extends external_api {
|
||||
'shortname' => new external_value(PARAM_NOTAGS, 'shortname'),
|
||||
'type' => new external_value(PARAM_NOTAGS, 'type'),
|
||||
'id' => new external_value(PARAM_INT, 'id'),
|
||||
'actionsmenu' => new external_value(PARAM_RAW, 'actionsmenu'),
|
||||
'movetitle' => new external_value(PARAM_TEXT, 'movetitle'),
|
||||
)
|
||||
)
|
||||
, '', VALUE_OPTIONAL),
|
||||
'toggle' => new external_value(PARAM_RAW, 'toggle', VALUE_OPTIONAL),
|
||||
'extraclasses' => new external_value(PARAM_TEXT, 'extra classes', VALUE_OPTIONAL),
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
{{!
|
||||
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_customfield/category
|
||||
|
||||
This template renders a customfield category.
|
||||
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"id": "0",
|
||||
"name": "Other fields",
|
||||
"nameeditable": "Other fields",
|
||||
"addfieldmenu": "<button class=\"btn btn-secondary\">Add field</button>",
|
||||
"canedit": true,
|
||||
"canmovecategories": true,
|
||||
"usescategories": true,
|
||||
"actionsmenu": "<button class=\"btn btn-secondary\">Actions</button>",
|
||||
"toggle": "<input type=\"checkbox\" class=\"form-check-input\">",
|
||||
"fields": [
|
||||
{ "id": 0, "name": "Field name", "shortname": "shortname", "type": "Text" },
|
||||
{ "id": 0, "name": "Another field", "shortname": "checkme", "type": "Checkbox" }
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="categoryinstance card mt-4 {{#extraclasses}}{{extraclasses}}{{/extraclasses}}"
|
||||
data-category-id="{{id}}"
|
||||
data-category-name="{{name}}"
|
||||
id="category-{{id}}"
|
||||
>
|
||||
<div class="card-header d-flex align-items-center px-2">
|
||||
{{#usescategories}}
|
||||
<div class="d-flex align-items-center">
|
||||
{{#canmovecategories}}
|
||||
{{#canedit}}<span class="movecategory icon-size-3">{{> core/drag_handle}}</span>{{/canedit}}
|
||||
{{/canmovecategories}}
|
||||
<h3 class="h4 ms-2 mb-0">{{{nameeditable}}}</h3>
|
||||
</div>
|
||||
<div class="ms-auto d-flex align-items-center gap-2">
|
||||
{{{addfieldmenu}}}
|
||||
{{{actionsmenu}}}
|
||||
{{#toggle}}
|
||||
<div class="ms-auto" data-bs-toggle="tooltip" data-bs-placement="top" title="{{#str}} sharedcategoryhelptext, customfield {{/str}}">
|
||||
{{{.}}}
|
||||
</div>
|
||||
{{/toggle}}
|
||||
</div>
|
||||
{{/usescategories}}
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table generaltable table-striped table-hover mb-0 {{#canedit}}fieldslist{{/canedit}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{#str}} customfield, core_customfield {{/str}}</th>
|
||||
<th scope="col">{{#str}} shortname, core_customfield {{/str}}</th>
|
||||
<th scope="col">{{#str}} type, core_customfield {{/str}}</th>
|
||||
{{#canedit}}
|
||||
<th scope="col"><span class="visually-hidden">{{#str}}actions{{/str}}</span></th>
|
||||
{{/canedit}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#fields}}
|
||||
{{> core_customfield/field }}
|
||||
{{/fields}}
|
||||
{{^fields}}
|
||||
{{> core_customfield/nofields }}
|
||||
{{/fields}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
{{!
|
||||
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_customfield/field
|
||||
|
||||
This template renders a customfield category field.
|
||||
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Field name",
|
||||
"shortname": "shortname",
|
||||
"type": "Text",
|
||||
"canedit": true,
|
||||
"canmovefields": true,
|
||||
"actionsmenu": "<button class=\"btn btn-secondary\">Actions</button>"
|
||||
}
|
||||
}}
|
||||
<tr data-field-name="{{name}}" data-field-id="{{id}}" class="field">
|
||||
<td>
|
||||
{{#canmovefields}}
|
||||
{{#canedit}}<span class="movefield">{{> core/drag_handle}}</span>{{/canedit}}
|
||||
{{/canmovefields}}
|
||||
{{{name}}}
|
||||
</td>
|
||||
<td>{{{shortname}}}</td>
|
||||
<td>{{{type}}}</td>
|
||||
{{#canedit}}
|
||||
<td>{{{actionsmenu}}}</td>
|
||||
{{/canedit}}
|
||||
</tr>
|
||||
@@ -13,156 +13,91 @@
|
||||
|
||||
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_customfield/list
|
||||
@template core_customfield/list
|
||||
|
||||
Moodle list template.
|
||||
Moodle list template.
|
||||
The purpose of this template is to render a list.
|
||||
|
||||
The purpose of this template is to render a list.
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
Data attributes required for JS:
|
||||
* data-component
|
||||
* data-area
|
||||
* data-itemid
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-component
|
||||
* data-area
|
||||
* data-itemid
|
||||
Context variables required for this template:
|
||||
* attributes Array of name / value pairs.
|
||||
|
||||
Context variables required for this template:
|
||||
* attributes Array of name / value pairs.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"component": "core_nonexisting",
|
||||
"area": "course",
|
||||
"itemid": 0,
|
||||
"usescategories": 1,
|
||||
"canmovecategories": 1,
|
||||
"canmovefields": 1,
|
||||
"categories": [
|
||||
{ "id": "0",
|
||||
"name": "Other fields",
|
||||
"nameeditable": "Other fields",
|
||||
"addfieldmenu": "Add field",
|
||||
"canedit": true,
|
||||
"canconvert": true,
|
||||
"hasduplicatecustomfield": false,
|
||||
"fields": [
|
||||
{ "id": 0, "name": "Field name", "shortname": "shortname", "type": "Text" },
|
||||
{ "id": 0, "name": "Another field", "shortname": "checkme", "type": "Checkbox" }
|
||||
]
|
||||
},
|
||||
{ "id": "00",
|
||||
"name": "Empty category",
|
||||
"nameeditable": "Empty category",
|
||||
"addfieldmenu": "Add field",
|
||||
"canedit": true,
|
||||
"fields": [] }
|
||||
],
|
||||
"singleselect" : "select"
|
||||
}
|
||||
Example context (json):
|
||||
{
|
||||
"component": "core_nonexisting",
|
||||
"area": "course",
|
||||
"itemid": 0,
|
||||
"usescategories": 1,
|
||||
"canmovecategories": 1,
|
||||
"canmovefields": 1,
|
||||
"hascategories": true,
|
||||
"hassharedcategories": true,
|
||||
"categories": [
|
||||
{
|
||||
"id": "0",
|
||||
"name": "Other fields",
|
||||
"nameeditable": "Other fields",
|
||||
"addfieldmenu": "Add field",
|
||||
"canedit": true,
|
||||
"fields": [
|
||||
{ "id": 0, "name": "Field name", "shortname": "shortname", "type": "Text" },
|
||||
{ "id": 0, "name": "Another field", "shortname": "checkme", "type": "Checkbox" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "00",
|
||||
"name": "Empty category",
|
||||
"nameeditable": "Empty category",
|
||||
"addfieldmenu": "Add field",
|
||||
"canedit": true,
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
|
||||
{{{alert}}}
|
||||
|
||||
<div data-region="list-page" id="customfield_catlist" data-component="{{component}}" data-area="{{area}}" data-itemid="{{itemid}}">
|
||||
<div class="row">
|
||||
<div class="col align-self-end">
|
||||
{{#usescategories}}
|
||||
<a tabindex="0" role="button" class="btn btn-secondary float-end" data-role="addnewcategory">{{#str}}addnewcategory, core_customfield{{/str}}</a>
|
||||
{{/usescategories}}
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
{{#usescategories}}
|
||||
<a tabindex="0" role="button" class="btn btn-primary" data-role="addnewcategory">{{#str}}addnewcategory, core_customfield{{/str}}</a>
|
||||
{{/usescategories}}
|
||||
</div>
|
||||
|
||||
{{^categories}}
|
||||
{{{nocategories}}}
|
||||
{{/categories}}
|
||||
{{^hascategories}}
|
||||
<div class="alert alert-info mt-3">{{#str}}nocategories, core_customfield{{/str}}</div>
|
||||
{{/hascategories}}
|
||||
|
||||
{{#categories}}
|
||||
{{#canedit}}<div class="categorieslist">{{/canedit}}
|
||||
<div data-category-id="{{id}}" data-category-name="{{name}}" id="category-{{id}}" class="mt-2">
|
||||
<div class="row justify-content-between align-items-end">
|
||||
<div class="col-6 categoryinstance d-flex align-items-center gap-3">
|
||||
{{#usescategories}}
|
||||
<h3>
|
||||
{{#canmovecategories}}
|
||||
{{#canedit}}<span class="movecategory icon-size-3">{{> core/drag_handle}}</span>{{/canedit}}
|
||||
{{/canmovecategories}}
|
||||
{{{nameeditable}}}
|
||||
{{#canedit}}
|
||||
{{#canconvert}}
|
||||
{{^hasduplicatecustomfield}}
|
||||
<button type="button" class="btn btn-icon icon-size-3" data-role="convertcategory" data-id="{{id}}" title="{{#str}} convertcategory, customfield {{/str}}">
|
||||
{{#pix}} i/siteevent, core {{/pix}}
|
||||
<span class="visually-hidden">{{#str}} convertcategory, customfield {{/str}}</span>
|
||||
</button>
|
||||
{{/hasduplicatecustomfield}}
|
||||
{{#hasduplicatecustomfield}}
|
||||
<button type="button" class="btn btn-icon icon-size-3" data-role="hasduplicatecustomfield" data-id="{{id}}" title="{{#str}} convertcategory, customfield {{/str}}">
|
||||
{{#pix}} i/siteevent, core {{/pix}}
|
||||
<span class="visually-hidden">{{#str}} convertcategory, customfield {{/str}}</span>
|
||||
</button>
|
||||
{{/hasduplicatecustomfield}}
|
||||
{{/canconvert}}
|
||||
<button type="button" class="btn btn-icon icon-size-3" data-role="deletecategory" data-id="{{id}}" title="{{#str}} deletecategory, customfield, {{name}} {{/str}}" aria-label="{{#str}} deletecategory, customfield, {{name}} {{/str}}">
|
||||
{{#pix}} t/delete, core {{/pix}}
|
||||
</button>
|
||||
{{/canedit}}
|
||||
</h3>
|
||||
{{#toggle}}
|
||||
<div data-bs-toggle="tooltip" data-bs-placement="top" title="{{#str}} sharedcategoryhelptext, customfield {{/str}}">
|
||||
{{{.}}}
|
||||
</div>
|
||||
{{/toggle}}
|
||||
{{/usescategories}}
|
||||
</div>
|
||||
<div class="col-auto text-end">
|
||||
{{{addfieldmenu}}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<table class="table generaltable table-striped table-hover {{#canedit}}fieldslist{{/canedit}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-5">{{#str}} customfield, core_customfield {{/str}}</th>
|
||||
<th scope="col" class="col-3">{{#str}} shortname, core_customfield {{/str}}</th>
|
||||
<th scope="col" class="col-2">{{#str}} type, core_customfield {{/str}}</th>
|
||||
<th scope="col" class="col-2 text-end">{{#str}} action, core_customfield {{/str}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#fields}}
|
||||
<tr data-field-name="{{name}}" data-field-id="{{id}}" class="field">
|
||||
<td class="col-5">
|
||||
{{#canmovefields}}
|
||||
{{#canedit}}<span class="movefield">{{> core/drag_handle}}</span>{{/canedit}}
|
||||
{{/canmovefields}}
|
||||
{{{name}}}
|
||||
</td>
|
||||
<td class="col-3">{{{shortname}}}</td>
|
||||
<td class="col-2">{{{type}}}</td>
|
||||
<td class="col-2 text-end">
|
||||
{{#canedit}}
|
||||
<button type="button" class="btn btn-link btn-icon" data-role="editfield" data-name="{{name}}" data-id="{{id}}" title="{{#str}} editfield, customfield, {{name}} {{/str}}" aria-label="{{#str}} editfield, customfield, {{name}} {{/str}}">
|
||||
{{#pix}} t/edit, core {{/pix}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-link btn-icon" data-id="{{id}}" data-role="deletefield" title="{{#str}} deletefield, customfield, {{name}} {{/str}}" aria-label="{{#str}} deletefield, customfield, {{name}} {{/str}}">
|
||||
{{#pix}} t/delete, core {{/pix}}
|
||||
</button>
|
||||
{{/canedit}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/fields}}
|
||||
{{^fields}}
|
||||
{{> core_customfield/nofields }}
|
||||
{{/fields}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{{#canedit}}</div>{{/canedit}}
|
||||
{{/categories}}
|
||||
{{#hascategories}}
|
||||
<div class="categorieslist">
|
||||
{{#categories}}
|
||||
{{#canedit}}
|
||||
{{> core_customfield/category}}
|
||||
{{/canedit}}
|
||||
{{/categories}}
|
||||
</div>
|
||||
{{/hascategories}}
|
||||
|
||||
{{#hassharedcategories}}
|
||||
<h2 class="mt-5">{{#str}} sharedcustomfields, customfield {{/str}}</h2>
|
||||
<div class="sharedcategorieslist">
|
||||
{{#categories}}
|
||||
{{^canedit}}
|
||||
{{> core_customfield/category}}
|
||||
{{/canedit}}
|
||||
{{/categories}}
|
||||
</div>
|
||||
{{/hassharedcategories}}
|
||||
</div>
|
||||
|
||||
{{#js}}
|
||||
|
||||
@@ -36,4 +36,10 @@
|
||||
{
|
||||
}
|
||||
}}
|
||||
<tr class="nofields"><td colspan="4">{{# str }} therearenofields, core_customfield {{/ str }}</td></tr>
|
||||
<tr class="nofields">
|
||||
<td colspan="4">
|
||||
<div class="nofields alert alert-info m-0">
|
||||
{{# str }} therearenofields, core_customfield {{/ str }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -34,10 +34,10 @@ $string['componentsettings'] = 'Component settings';
|
||||
$string['confirmdeletecategory'] = 'Are you sure you want to delete this category? All fields inside the category will also be deleted and all data associated with them. This action cannot be undone.';
|
||||
$string['confirmdeletefield'] = 'Are you sure you want to delete this field and all associated data? This action cannot be undone.';
|
||||
$string['conversionerror'] = 'Cannot convert to shared custom fields';
|
||||
$string['convertcategory'] = 'Convert to shared custom fields';
|
||||
$string['convertcategory'] = 'Convert to shared';
|
||||
$string['convertcategoryconfirm'] = 'Are you sure you want to convert this category and all its fields to shared custom fields? This will make these fields available to all entities that support custom fields, such as courses, groups, cohorts, etc. <br/><br/>Once converted, the shared category will be enabled by default but can be disabled manually. </br><br/>This action cannot be undone.';
|
||||
$string['convertcategorytitle'] = 'Convert to shared custom fields?';
|
||||
$string['createnewcustomfield'] = 'Add a new custom field';
|
||||
$string['createnewcustomfield'] = 'Add field';
|
||||
$string['customfield'] = 'Custom field';
|
||||
$string['customfielddata'] = 'Custom fields data';
|
||||
$string['customfields'] = 'Custom fields';
|
||||
|
||||
@@ -28,6 +28,8 @@ require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('qbank_customfields');
|
||||
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
$output = $PAGE->get_renderer('core_customfield');
|
||||
$customfieldhandler = qbank_customfields\customfield\question_handler::create();
|
||||
$outputpage = new \core_customfield\output\management($customfieldhandler);
|
||||
|
||||
@@ -27,6 +27,8 @@ require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('reportbuildercustomfields');
|
||||
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
/** @var \core_customfield\output\renderer $output */
|
||||
$output = $PAGE->get_renderer('core_customfield');
|
||||
$handler = core_reportbuilder\customfield\report_handler::create();
|
||||
|
||||
Reference in New Issue
Block a user