MDL-71691 core_availability: add show more link

This commit is contained in:
Bas Brands 2021-10-14 15:05:22 +02:00
parent 51149a78cc
commit 70d86ac570
10 changed files with 342 additions and 27 deletions

View File

@ -0,0 +1,2 @@
define ("core_availability/availability_more",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;var b={regions:{availability:"[data-region=\"availability-multiple\"]"},actions:{showmorelink:"[data-action=\"showmore\"]"},classes:{hidden:"d-none",visible:"d-block"}},c=function(a){var c=a.target.closest(b.actions.showmorelink);if(null===c){return}var d=c.closest(b.regions.availability);d.querySelectorAll("."+b.classes.hidden).forEach(function(a){a.classList.remove(b.classes.hidden)});d.querySelectorAll("."+b.classes.visible).forEach(function(a){a.classList.remove(b.classes.visible);a.classList.add(b.classes.hidden)});a.preventDefault()};a.init=function init(){var a=document.querySelector("body");if(!a.dataset.showmoreactive){document.addEventListener("click",c);a.dataset.showmoreactive=1}}});
//# sourceMappingURL=availability_more.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../src/availability_more.js"],"names":["Selectors","regions","availability","actions","showmorelink","classes","hidden","visible","showMoreHandler","event","triggerElement","target","closest","container","querySelectorAll","forEach","node","classList","remove","add","preventDefault","init","body","document","querySelector","dataset","showmoreactive","addEventListener"],"mappings":"qJA0BMA,CAAAA,CAAS,CAAG,CACdC,OAAO,CAAE,CACLC,YAAY,CAAE,yCADT,CADK,CAIdC,OAAO,CAAE,CACLC,YAAY,CAAE,4BADT,CAJK,CAOdC,OAAO,CAAE,CACLC,MAAM,CAAE,QADH,CAELC,OAAO,CAAE,SAFJ,CAPK,C,CAmBZC,CAAe,CAAG,SAACC,CAAD,CAAW,CAC/B,GAAMC,CAAAA,CAAc,CAAGD,CAAK,CAACE,MAAN,CAAaC,OAAb,CAAqBZ,CAAS,CAACG,OAAV,CAAkBC,YAAvC,CAAvB,CACA,GAAuB,IAAnB,GAAAM,CAAJ,CAA6B,CACzB,MACH,CACD,GAAMG,CAAAA,CAAS,CAAGH,CAAc,CAACE,OAAf,CAAuBZ,CAAS,CAACC,OAAV,CAAkBC,YAAzC,CAAlB,CACAW,CAAS,CAACC,gBAAV,CAA2B,IAAMd,CAAS,CAACK,OAAV,CAAkBC,MAAnD,EAA2DS,OAA3D,CAAmE,SAASC,CAAT,CAAe,CAC9EA,CAAI,CAACC,SAAL,CAAeC,MAAf,CAAsBlB,CAAS,CAACK,OAAV,CAAkBC,MAAxC,CACH,CAFD,EAGAO,CAAS,CAACC,gBAAV,CAA2B,IAAMd,CAAS,CAACK,OAAV,CAAkBE,OAAnD,EAA4DQ,OAA5D,CAAoE,SAASC,CAAT,CAAe,CAC/EA,CAAI,CAACC,SAAL,CAAeC,MAAf,CAAsBlB,CAAS,CAACK,OAAV,CAAkBE,OAAxC,EACAS,CAAI,CAACC,SAAL,CAAeE,GAAf,CAAmBnB,CAAS,CAACK,OAAV,CAAkBC,MAArC,CACH,CAHD,EAIAG,CAAK,CAACW,cAAN,EACH,C,QAOmB,QAAPC,CAAAA,IAAO,EAAM,CACtB,GAAMC,CAAAA,CAAI,CAAGC,QAAQ,CAACC,aAAT,CAAuB,MAAvB,CAAb,CACA,GAAI,CAACF,CAAI,CAACG,OAAL,CAAaC,cAAlB,CAAkC,CAC9BH,QAAQ,CAACI,gBAAT,CAA0B,OAA1B,CAAmCnB,CAAnC,EACAc,CAAI,CAACG,OAAL,CAAaC,cAAb,CAA8B,CACjC,CACJ,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Show more action for availablity information.\n *\n * @module core_availability/availability_more\n * @copyright 2021 Bas Brands <bas@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Availability info selectors.\n */\nconst Selectors = {\n regions: {\n availability: '[data-region=\"availability-multiple\"]',\n },\n actions: {\n showmorelink: '[data-action=\"showmore\"]'\n },\n classes: {\n hidden: 'd-none',\n visible: 'd-block',\n\n }\n};\n\n/**\n * Displays all the availability information in case part of it is hidden.\n *\n * @param {Event} event the triggered event\n */\nconst showMoreHandler = (event) => {\n const triggerElement = event.target.closest(Selectors.actions.showmorelink);\n if (triggerElement === null) {\n return;\n }\n const container = triggerElement.closest(Selectors.regions.availability);\n container.querySelectorAll('.' + Selectors.classes.hidden).forEach(function(node) {\n node.classList.remove(Selectors.classes.hidden);\n });\n container.querySelectorAll('.' + Selectors.classes.visible).forEach(function(node) {\n node.classList.remove(Selectors.classes.visible);\n node.classList.add(Selectors.classes.hidden);\n });\n event.preventDefault();\n};\n\n/**\n * Initialise the eventlister for the showmore action on availability information.\n *\n * @method init\n */\nexport const init = () => {\n const body = document.querySelector('body');\n if (!body.dataset.showmoreactive) {\n document.addEventListener('click', showMoreHandler);\n body.dataset.showmoreactive = 1;\n }\n};\n"],"file":"availability_more.min.js"}

View File

@ -0,0 +1,73 @@
// 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/>.
/**
* Show more action for availablity information.
*
* @module core_availability/availability_more
* @copyright 2021 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Availability info selectors.
*/
const Selectors = {
regions: {
availability: '[data-region="availability-multiple"]',
},
actions: {
showmorelink: '[data-action="showmore"]'
},
classes: {
hidden: 'd-none',
visible: 'd-block',
}
};
/**
* Displays all the availability information in case part of it is hidden.
*
* @param {Event} event the triggered event
*/
const showMoreHandler = (event) => {
const triggerElement = event.target.closest(Selectors.actions.showmorelink);
if (triggerElement === null) {
return;
}
const container = triggerElement.closest(Selectors.regions.availability);
container.querySelectorAll('.' + Selectors.classes.hidden).forEach(function(node) {
node.classList.remove(Selectors.classes.hidden);
});
container.querySelectorAll('.' + Selectors.classes.visible).forEach(function(node) {
node.classList.remove(Selectors.classes.visible);
node.classList.add(Selectors.classes.hidden);
});
event.preventDefault();
};
/**
* Initialise the eventlister for the showmore action on availability information.
*
* @method init
*/
export const init = () => {
const body = document.querySelector('body');
if (!body.dataset.showmoreactive) {
document.addEventListener('click', showMoreHandler);
body.dataset.showmoreactive = 1;
}
};

View File

@ -719,14 +719,14 @@ abstract class info {
* @return string Correctly formatted info string
*/
public static function format_info($inforenderable, $courseorid) {
global $PAGE;
global $PAGE, $OUTPUT;
// Use renderer if required.
if (is_string($inforenderable)) {
$info = $inforenderable;
} else {
$renderer = $PAGE->get_renderer('core', 'availability');
$info = $renderer->render($inforenderable);
$renderable = new \core_availability\output\availability_info($inforenderable);
$info = $OUTPUT->render($renderable);
}
// Don't waste time if there are no special tags.

View File

@ -0,0 +1,126 @@
<?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/>.
/**
* Renderable for the availability info.
*
* @package core_availability
* @copyright 2021 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_availability\output;
use core_availability_multiple_messages;
use renderable;
use templatable;
use stdClass;
/**
* Base class to render availability info.
*
* @package core_availability
* @copyright 2021 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class availability_info implements renderable, templatable {
/** @var core_availability_multiple_messages availabilitymessages the course format class */
protected $availabilitymessages;
/** @var int counts number of conditions */
protected $count = 0;
/** @var int Maximum number of lines of availability info */
protected const MAXVISIBLE = 4;
/**
* Constructor.
*
* @param core_availability_multiple_messages $renderable the availability messages
*/
public function __construct(core_availability_multiple_messages $renderable) {
$this->availabilitymessages = $renderable;
$this->count = 0;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @return stdClass data context for a mustache template
*/
public function export_for_template(\renderer_base $output): stdClass {
$template = $this->get_item_template($this->availabilitymessages);
$template->id = uniqid();
if ($this->count >= self::MAXVISIBLE) {
$template->showmorelink = true;
}
return $template;
}
/**
* Get the item base template.
*
* @return stdClass the template base
*/
protected function get_item_base_template(): stdClass {
return (object)[
'hidden' => $this->count > self::MAXVISIBLE,
'abbreviate' => $this->count === self::MAXVISIBLE,
'id' => false,
'items' => [],
'hasitems' => false,
'showmorelink' => false,
];
}
/**
* Get the item template.
*
* @param core_availability_multiple_messages $availability the availability messages
* @return stdClass the template
*/
protected function get_item_template(core_availability_multiple_messages $availability): stdClass {
$template = $this->get_item_base_template();
$template->header = get_string(
'list_' . ($availability->root ? 'root_' : '') .
($availability->andoperator ? 'and' : 'or') .
($availability->treehidden ? '_hidden' : ''),
'availability'
);
foreach ($availability->items as $item) {
$this->count++;
if (is_string($item)) {
$simple_item = $this->get_item_base_template();
$simple_item->header = $item;
$template->items[] = $simple_item;
} else {
$template->items[] = $this->get_item_template($item);
}
}
$template->hasitems = !empty($template->items);
return $template;
}
}

View File

@ -40,11 +40,17 @@ class core_availability_renderer extends plugin_renderer_base {
*
* This function will not be called unless there are at least two messages.
*
* @deprecated since Moodle 4.0 MDL-716916 - please do not use this function any more.
* @param core_availability_multiple_messages $renderable Multiple messages
* @return string Combined HTML
*/
public function render_core_availability_multiple_messages(
core_availability_multiple_messages $renderable) {
debugging(
'render_core_availability_multiple_messages is deprecated. Use core_availability\\output\\multiple_messages instead',
DEBUG_DEVELOPER
);
// Get initial message.
$out = get_string('list_' . ($renderable->root ? 'root_' : '') .
($renderable->andoperator ? 'and' : 'or') . ($renderable->treehidden ? '_hidden' : ''),

View File

@ -0,0 +1,98 @@
{{!
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_availability/availability_info
Renders the availability tree for course activties
Example context (json):
{
"id" : "123456",
"header": "Not available unless:",
"items" : [
{
"showmorelink": 0,
"abbreviate" : 0,
"hidden" : 0,
"hasitems": 0,
"header": "You belong to Green Group"
},
{
"showmorelink": 0,
"abbreviate" : 0,
"hidden" : 0,
"hasitems": 0,
"header": "You belong to Red Group"
},
{
"showmorelink": 0,
"abbreviate" : 1,
"hidden" : 0,
"hasitems": 0,
"header": "You belong to Orange Group"
},
{
"showmorelink": 0,
"abbreviate" : 0,
"hidden" : 1,
"hasitems": 0,
"header": "You belong to Pink Group"
},
{
"showmorelink": 0,
"abbreviate" : 0,
"hidden" : 1,
"hasitems": 0,
"header": "You belong to Red Group"
}
],
"hasitems": 1,
"showmorelink": 1
}
}}
{{{header}}}
{{#hasitems}}
<ul {{#id}} id="availability-tree-{{id}}" {{/id}} data-region="availability-multiple">
{{#items}}
<li class="{{#hidden}}d-none{{/hidden}}">
{{#abbreviate}}
<span class="d-none">
{{> core_availability/availability_info }}
</span><span class="d-block">...</span>
{{/abbreviate}}
{{^abbreviate}}
{{> core_availability/availability_info }}
{{/abbreviate}}
</li>
{{/items}}
{{#showmorelink}}
<li data-action="showmore" class="d-block showmore">
<a role="button" aria-expanded="false" aria-controls="availability-tree-{{id}}" href="#" >
{{#str}}showmore, availability{{/str}}
</a>
</li>
{{/showmorelink}}
</ul>
{{/hasitems}}
{{#showmorelink}}
{{#js}}
require(['core_availability/availability_more'], function(availabilityMore) {
availabilityMore.init();
});
{{/js}}
{{/showmorelink}}

View File

@ -104,7 +104,7 @@ Feature: display_availability
# Page 1 display still there but should be dimmed and not a link.
Then I should see "Page 1" in the "#section-1 .dimmed_text" "css_element"
And ".activityinstance a" "css_element" should not exist in the "Topic 1" "section"
And ".activity-instance a" "css_element" should not exist in the "Topic 1" "section"
# Date display should be present.
And I should see "Available until" in the "Topic 1" "section"
@ -114,7 +114,7 @@ Feature: display_availability
# Page 3 display and link
And I should see "Page 3" in the "region-main" "region"
And ".activityinstance a" "css_element" should exist in the "Topic 3" "section"
And ".activity-instance a" "css_element" should exist in the "Topic 3" "section"
@javascript
Scenario: Section availability display

View File

@ -341,17 +341,32 @@ class tree_testcase extends \advanced_testcase {
* @param int $userid User id
*/
protected function get_available_results($structure, \core_availability\info $info, $userid) {
global $PAGE;
global $PAGE, $OUTPUT;
$tree = new tree($structure);
$result = $tree->check_available(false, $info, true, $userid);
$information = $tree->get_result_information($info, $result);
if (!is_string($information)) {
$renderer = $PAGE->get_renderer('core', 'availability');
$information = $renderer->render($information);
$renderable = new \core_availability\output\availability_info($information);
$information = str_replace(array("\r", "\n"), '', $OUTPUT->render($renderable));
}
return array($result->is_available(), $information);
}
/**
* Shortcut function to render the full availability information.
*
* @param stdClass $structure Tree structure
* @param \core_availability\info $info Location info
*/
protected function render_full_information($structure, \core_availability\info $info) {
global $OUTPUT;
$tree = new tree($structure);
$information = $tree->get_full_information($info);
$renderable = new \core_availability\output\availability_info($information);
$html = $OUTPUT->render($renderable);
return str_replace(array("\r", "\n"), '', $html);
}
/**
* Tests the is_available_for_all() function.
*/
@ -396,7 +411,6 @@ class tree_testcase extends \advanced_testcase {
*/
public function test_get_full_information() {
global $PAGE;
$renderer = $PAGE->get_renderer('core', 'availability');
// Setup.
$info = new \core_availability\mock_info();
@ -423,48 +437,43 @@ class tree_testcase extends \advanced_testcase {
self::mock(array('m' => '1')),
self::mock(array('m' => '2'))), tree::OP_AND),
self::mock(array('m' => 3)));
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~<ul.*<ul.*<li.*1.*<li.*2.*</ul>.*<li.*3~',
$renderer->render($tree->get_full_information($info)));
$this->render_full_information($structure, $info));
// Test intro messages before list. First, OR message.
$structure->c = array(
self::mock(array('m' => '1')),
self::mock(array('m' => '2'))
);
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~Not available unless any of:.*<ul>~',
$renderer->render($tree->get_full_information($info)));
$this->assertMatchesRegularExpression('~Not available unless any of:.*<ul~',
$this->render_full_information($structure, $info));
// Now, OR message when not shown.
$structure->show = false;
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~hidden.*<ul>~',
$renderer->render($tree->get_full_information($info)));
$this->assertMatchesRegularExpression('~hidden.*<ul~',
$this->render_full_information($structure, $info));
// AND message.
$structure->op = '&';
unset($structure->show);
$structure->showc = array(false, false);
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~Not available unless:.*<ul>~',
$renderer->render($tree->get_full_information($info)));
$this->assertMatchesRegularExpression('~Not available unless:.*<ul~',
$this->render_full_information($structure, $info));
// Hidden markers on items.
$this->assertMatchesRegularExpression('~1.*hidden.*2.*hidden~',
$renderer->render($tree->get_full_information($info)));
$this->render_full_information($structure, $info));
// Hidden markers on child tree and items.
$structure->c[1] = tree::get_nested_json(array(
self::mock(array('m' => '2')),
self::mock(array('m' => '3'))), tree::OP_AND);
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~1.*hidden.*All of \(hidden.*2.*3~',
$renderer->render($tree->get_full_information($info)));
$this->render_full_information($structure, $info));
$structure->c[1]->op = '|';
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~1.*hidden.*Any of \(hidden.*2.*3~',
$renderer->render($tree->get_full_information($info)));
$this->render_full_information($structure, $info));
// Hidden markers on single-item display, AND and OR.
$structure->showc = array(false);
@ -486,9 +495,8 @@ class tree_testcase extends \advanced_testcase {
$structure->c[0] = tree::get_nested_json(array(
self::mock(array('m' => '1')),
self::mock(array('m' => '2'))), tree::OP_AND);
$tree = new tree($structure);
$this->assertMatchesRegularExpression('~Not available \(hidden.*1.*2~',
$renderer->render($tree->get_full_information($info)));
$this->render_full_information($structure, $info));
// Single item tree containing single item.
unset($structure->c[0]->c[1]);

View File

@ -65,4 +65,5 @@ $string['privacy:metadata'] = 'The Access restriction subsystem does not store a
$string['restrictaccess'] = 'Restrict access';
$string['restrictbygroup'] = 'Add group/grouping access restriction';
$string['setheading'] = '{$a->number} Set of {$a->count} restriction(s)';
$string['showmore'] = 'Show more';
$string['unknowncondition'] = 'Unknown condition (deleted condition plugin)';