MDL-76358 mod_data: Add options to preset menu

* Add "Preview" and "Use this preset" menus to the preset plugin page's
burger menu
* Refactor importmapping dialogue to use the 'data-action' selector
* Add a new set_kebab_trigger action menu method
* Use set_additional_classes method for action menu
* Remove redundant parameter to add_action_menu
This commit is contained in:
Laurent David 2022-11-24 11:36:58 +01:00
parent b8b905cd90
commit 45317d6db5
14 changed files with 149 additions and 73 deletions

View File

@ -128,9 +128,7 @@ class controlmenu implements named_templatable, renderable {
// Convert control array into an action_menu.
$menu = new action_menu();
$icon = $output->pix_icon('i/menu', get_string('edit'));
$menu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center');
$menu->set_kebab_trigger(get_string('edit'));
$menu->attributes['class'] .= ' section-cm-edit-actions commands';
// Prioritise the menu ahead of all other actions.

View File

@ -82,8 +82,7 @@ class controlmenu implements named_templatable, renderable {
// Convert control array into an action_menu.
$menu = new action_menu();
$icon = $output->pix_icon('i/menu', get_string('edit'));
$menu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center');
$menu->set_kebab_trigger(get_string('edit'));
$menu->attributes['class'] .= ' section-actions';
foreach ($controls as $value) {
$url = empty($value['url']) ? '' : $value['url'];

View File

@ -4304,6 +4304,31 @@ class action_menu implements renderable, templatable {
$this->triggerextraclasses = $extraclasses;
}
/**
* Classes for the trigger menu
*/
const DEFAULT_KEBAB_TRIGGER_CLASSES = 'btn btn-icon d-flex align-items-center justify-content-center';
/**
* Setup trigger as in the kebab menu.
*
* @param string|null $triggername
* @param core_renderer|null $output
* @param string|null $extraclasses extra classes for the trigger {@see self::set_menu_trigger()}
* @throws coding_exception
*/
public function set_kebab_trigger(?string $triggername = null, ?core_renderer $output = null,
?string $extraclasses = '') {
global $OUTPUT;
if (empty($output)) {
$output = $OUTPUT;
}
$label = $triggername ?? get_string('actions');
$triggerclasses = self::DEFAULT_KEBAB_TRIGGER_CLASSES . ' ' . $extraclasses;
$icon = $output->pix_icon('i/menu', $label);
$this->set_menu_trigger($icon, $triggerclasses);
}
/**
* Return true if there is at least one visible link in the menu.
*

View File

@ -14,6 +14,7 @@ information provided here is intended especially for developers.
* New DB parameter 'versionfromdb', only available for MySQL and MariaDB drivers. It allows to force the DB version to be
evaluated through an explicit call to VERSION() to skip the PHP client version which appears to be sometimes fooled by the
underlying infrastructure, e.g. PaaS on Azure.
* The actionmenu component has now a set_kebab_trigger that will setup the action menu for use in kebab menus.
* The useexternalyui configuration setting has been removed as a part of the migration away from YUI.
It only worked with http sites and did not officially support SSL, and is at risk of disappearing should Yahoo! decide
to remove it.
@ -715,7 +716,6 @@ filepath because some components, such as mod_page or mod_resource, add the revi
call the above function because the file is sent by a third party library, then you should add
the attribute data-double-submit-protection="off" to your form.
=== 3.7 ===
* Nodes in the navigation api can have labels for each group. See set/get_collectionlabel().

View File

@ -5,6 +5,6 @@ define("mod_data/importmappingdialogue",["exports","core/notification","core/aja
* @module mod_data/importmappingdialogue
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url),_templates=_interopRequireDefault(_templates),_modal_factory=_interopRequireDefault(_modal_factory),(0,_prefetch.prefetchStrings)("mod_data",["mapping:dialogtitle:usepreset"]);const selectors_selectPresetButton='input[name="selectpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const usepreset=event.target.closest(selectors_selectPresetButton);usepreset&&(event.preventDefault(),showMappingDialogue(usepreset))}))},showMappingDialogue=usepreset=>{const presetName=usepreset.dataset.presetname,cmId=usepreset.dataset.cmid;getMappingInformation(cmId,presetName).then((result=>(result.data&&result.data.needsmapping?buildModal({title:(0,_str.get_string)("mapping:dialogtitle:usepreset","mod_data",result.data.presetname),body:_templates.default.render("mod_data/fields_mapping_body",result.data),footer:_templates.default.render("mod_data/fields_mapping_footer",getMappingButtons(cmId,presetName)),large:!0}):window.location.href=_url.default.relativeUrl("mod/data/field.php",{id:cmId,mode:"usepreset",fullname:presetName},!1),!0))).catch(_notification.default.exception)},buildModal=params=>_modal_factory.default.create({...params,type:_modal_factory.default.types.DEFAULT}).then((modal=>(modal.show(),modal.showFooter(),modal.registerCloseOnCancel(),modal))).catch(_notification.default.exception),getMappingButtons=(cmId,presetName)=>{const data={};return data.mapfieldsbutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"select"},!1),data.applybutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"notmapping"},!1),data},getMappingInformation=(cmId,presetName)=>{const request={methodname:"mod_data_get_mapping_information",args:{cmid:cmId,importedpreset:presetName}};return _ajax.default.call([request])[0]}}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url),_templates=_interopRequireDefault(_templates),_modal_factory=_interopRequireDefault(_modal_factory),(0,_prefetch.prefetchStrings)("mod_data",["mapping:dialogtitle:usepreset"]);const selectors_selectPreset='[data-action="selectpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const preset=event.target.closest(selectors_selectPreset);preset&&(event.preventDefault(),showMappingDialogue(preset))}))},showMappingDialogue=usepreset=>{const presetName=usepreset.dataset.presetname,cmId=usepreset.dataset.cmid;getMappingInformation(cmId,presetName).then((result=>(result.data&&result.data.needsmapping?buildModal({title:(0,_str.get_string)("mapping:dialogtitle:usepreset","mod_data",result.data.presetname),body:_templates.default.render("mod_data/fields_mapping_body",result.data),footer:_templates.default.render("mod_data/fields_mapping_footer",getMappingButtons(cmId,presetName)),large:!0}):window.location.href=_url.default.relativeUrl("mod/data/field.php",{id:cmId,mode:"usepreset",fullname:presetName},!1),!0))).catch(_notification.default.exception)},buildModal=params=>_modal_factory.default.create({...params,type:_modal_factory.default.types.DEFAULT}).then((modal=>(modal.show(),modal.showFooter(),modal.registerCloseOnCancel(),modal))).catch(_notification.default.exception),getMappingButtons=(cmId,presetName)=>{const data={};return data.mapfieldsbutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"select"},!1),data.applybutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"notmapping"},!1),data},getMappingInformation=(cmId,presetName)=>{const request={methodname:"mod_data_get_mapping_information",args:{cmid:cmId,importedpreset:presetName}};return _ajax.default.call([request])[0]}}));
//# sourceMappingURL=importmappingdialogue.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -33,7 +33,7 @@ import {get_string as getString} from 'core/str';
prefetchStrings('mod_data', ['mapping:dialogtitle:usepreset']);
const selectors = {
selectPresetButton: 'input[name="selectpreset"]',
selectPreset: '[data-action="selectpreset"]',
};
/**
@ -48,10 +48,10 @@ export const init = () => {
*/
const registerEventListeners = () => {
document.addEventListener('click', (event) => {
const usepreset = event.target.closest(selectors.selectPresetButton);
if (usepreset) {
const preset = event.target.closest(selectors.selectPreset);
if (preset) {
event.preventDefault();
showMappingDialogue(usepreset);
showMappingDialogue(preset);
}
});
};

View File

@ -41,7 +41,7 @@ class presets implements templatable, renderable {
/** @var array $presets The array containing the existing presets. */
private $presets;
/** @var moodle_url $formactionurl The the action url for the form. */
/** @var moodle_url $formactionurl The action url for the form. */
private $formactionurl;
/** @var bool $manage Whether the manage preset options should be displayed. */
@ -135,77 +135,103 @@ class presets implements templatable, renderable {
private function get_preset_action_menu(renderer_base $output, $preset, ?int $userid): stdClass {
$actions = new stdClass();
$actionmenu = null;
$id = $this->manager->get_instance()->id;
// Only presets saved by users can be edited or removed (so the datapreset plugins shouldn't display these buttons).
if ($this->manage && !$preset->isplugin) {
$actionmenu = new action_menu();
$icon = $output->pix_icon('i/menu', get_string('actions'));
$actionmenu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center');
$actionmenu->set_action_label(get_string('actions'));
$actionmenu->attributes['class'] .= ' presets-actions';
$cmid = $this->manager->get_coursemodule()->id;
// If we cannot manage then return an empty menu.
if (!$this->manage) {
return $actions;
}
$actionmenu = new action_menu();
$actionmenu->set_kebab_trigger();
$actionmenu->set_action_label(get_string('actions'));
$actionmenu->set_additional_classes('presets-actions');
$canmanage = $preset->can_manage();
$canmanage = $preset->can_manage();
$usepreseturl = new moodle_url('/mod/data/preset.php', [
'action' => 'usepreset',
'cmid' => $cmid,
]);
$this->add_action_menu($actionmenu, get_string('usepreset', 'mod_data'), $usepreseturl, [
'data-action' => 'selectpreset',
'data-presetname' => $preset->get_fullname(),
'data-cmid' => $cmid,
]
);
// Attention: the id here is the cm->id, not d->id.
$previewpreseturl = new moodle_url('/mod/data/preset.php', [
'fullname' => $preset->get_fullname(),
'action' => 'preview',
'id' => $cmid,
]);
$this->add_action_menu($actionmenu, get_string('previewaction', 'mod_data'), $previewpreseturl, [
'data-action' => 'preview',
]
);
// Presets saved by users can be edited or removed.
if (!$preset->isplugin) {
// Edit.
if ($canmanage) {
$params = [
'd' => $id,
$editactionurl = new moodle_url('/mod/data/preset.php', [
'action' => 'edit',
];
$editactionurl = new moodle_url('/mod/data/preset.php', $params);
$attributes = [
'd' => $id,
]);
$this->add_action_menu($actionmenu, get_string('edit'), $editactionurl, [
'data-action' => 'editpreset',
'data-dataid' => $id,
"data-presetname" => $preset->name,
"data-presetdescription" => $preset->description,
];
$actionmenu->add(new action_menu_link_secondary(
$editactionurl,
null,
get_string('edit'),
$attributes
));
]);
}
// Export.
$params = [
'd' => $id,
$exporturl = new moodle_url('/mod/data/preset.php', [
'presetname' => $preset->name,
'action' => 'export',
];
$exporturl = new moodle_url('/mod/data/preset.php', $params);
$actionmenu->add(new action_menu_link_secondary(
$exporturl,
null,
get_string('export', 'mod_data'),
));
'd' => $id,
]);
$this->add_action_menu($actionmenu, get_string('export', 'mod_data'), $exporturl, [
'data-action' => 'exportpreset',
"data-presetname" => $preset->name,
"data-presetdescription" => $preset->description,
]);
// Delete.
if ($canmanage) {
$params = [
'd' => $id,
$deleteactionurl = new moodle_url('/mod/data/preset.php', [
'action' => 'delete',
];
$deleteactionurl = new moodle_url('/mod/data/preset.php', $params);
$attributes = [
'd' => $id,
]);
$this->add_action_menu($actionmenu, get_string('delete'), $deleteactionurl, [
'data-action' => 'deletepreset',
'data-dataid' => $id,
"data-presetname" => $preset->name,
];
$actionmenu->add(new action_menu_link_secondary(
$deleteactionurl,
null,
get_string('delete'),
$attributes,
));
]);
}
}
if (!is_null($actionmenu)) {
$actions = $actionmenu->export_for_template($output);
}
$actions = $actionmenu->export_for_template($output);
return $actions;
}
/**
* Add action to the action menu
*
* @param action_menu $actionmenu
* @param string $actionlabel
* @param moodle_url $actionurl
* @param array $otherattributes
* @return void
*/
private function add_action_menu(action_menu &$actionmenu, string $actionlabel, moodle_url $actionurl,
array $otherattributes) {
$attributes = [
'data-dataid' => $this->manager->get_instance()->id,
];
$actionmenu->add(new action_menu_link_secondary(
$actionurl,
null,
$actionlabel,
array_merge($attributes, $otherattributes),
));
}
}

View File

@ -718,10 +718,9 @@ class template {
global $OUTPUT, $CFG;
$actionmenu = new action_menu();
$icon = $OUTPUT->pix_icon('i/menu', get_string('actions'));
$actionmenu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center');
$actionmenu->set_kebab_trigger();
$actionmenu->set_action_label(get_string('actions'));
$actionmenu->attributes['class'] .= ' entry-actionsmenu';
$actionmenu->set_additional_classes('entry-actionsmenu');
// Show more.
if ($this->showmore) {

View File

@ -355,10 +355,9 @@ if (($mode == 'new') && (!empty($newtype))) { // Adding a new field.
));
$actionmenu = new action_menu();
$icon = $OUTPUT->pix_icon('i/menu', get_string('actions'));
$actionmenu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center');
$actionmenu->set_kebab_trigger();
$actionmenu->set_action_label(get_string('actions'));
$actionmenu->attributes['class'] .= ' fields-actions';
$actionmenu->set_additional_classes('fields-actions');
// It display a notification when the field type does not exist.
if ($field->type === 'unknown') {

View File

@ -352,6 +352,7 @@ $string['presetnotselected'] = 'No preset has been selected.';
$string['presets'] = 'Presets';
$string['presetshelp'] = 'Choose a preset to use as a starting point.';
$string['preview'] = 'Preview of {$a}';
$string['previewaction'] = 'Preview';
$string['privacy:metadata:commentpurpose'] = 'Comments on database records';
$string['privacy:metadata:data_content'] = 'The content of a field';
$string['privacy:metadata:data_content:fieldid'] = 'Field definition ID';

View File

@ -38,7 +38,7 @@
<input type="hidden" name="mode" value="usepreset">
<input type="hidden" name="action" value="select">
<input type="hidden" name="fullname" value="{{userid}}/{{shortname}}">
<input type="submit" name="selectpreset" value="{{#str}}usepreset, mod_data{{/str}}" class="btn btn-primary"
<input type="submit" name="selectpreset" data-action="selectpreset" value="{{#str}}usepreset, mod_data{{/str}}" class="btn btn-primary"
data-cmid="{{cmid}}" data-presetname="{{userid}}/{{shortname}}">
</form>
{{/ stickycontent }}

View File

@ -93,6 +93,7 @@
<input
type="submit"
name="selectpreset"
data-action="selectpreset"
value="{{#str}}usepreset, mod_data{{/str}}"
class="btn btn-secondary"
disabled

View File

@ -1,8 +1,8 @@
@mod @mod_data
Feature: Users can view and manage data presets
In order to use presets
As a user
I need to view, manage and use presets
In order to use presets
As a user
I need to view, manage and use presets
Background:
Given the following "users" exist:
@ -322,3 +322,31 @@ Feature: Users can view and manage data presets
| user |
| admin |
| teacher1 |
@javascript
Scenario Outline: Teachers can use "Use this preset" actions menu next to each preset.
Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1
And I follow "Presets"
And I open the action menu in "<Preset Name>" "table_row"
When I click on "Use this preset" "link" in the "<Preset Name>" "table_row"
Then I should see "Preset applied"
Examples:
| Preset Name |
| Image gallery |
| Saved preset 1 (Admin User) |
| Saved preset by teacher1 (Teacher 1) |
@javascript
Scenario Outline: Teachers can use "Preview" actions menu next to each preset.
Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1
And I follow "Presets"
And I open the action menu in "<Preset Name>" "table_row"
When I click on "Preview" "link" in the "<Preset Name>" "table_row"
Then I should see "Preview of <Preset preview name>"
Examples:
| Preset Name | Preset preview name |
| Image gallery | Image gallery |
| Saved preset 1 (Admin User) | Saved preset 1 |
| Saved preset by teacher1 (Teacher 1) | Saved preset by teacher1 |