MDL-78204 course: Use 'showmore' component in course restrictions

- Create new availability template, so it can be reused from both
section/availability and cm/availability mustaches.

- When rendering activity or section restrictions with long or multiline text
display a condensed version (excerpt) and use 'showmore' component to add
showmore/showless behaviour.

Co-authored-by: Ferran Recio <ferran@moodle.com>
This commit is contained in:
Mikel Martín 2023-06-12 15:55:56 +02:00
parent fb49de8875
commit 045f8cb50d
8 changed files with 159 additions and 19 deletions

View File

@ -44,6 +44,7 @@ Feature: Confirm that conditions on completion no longer cause a bug
And I click on "Activity completion" "button" in the "Add restriction..." "dialogue" And I click on "Activity completion" "button" in the "Add restriction..." "dialogue"
And I set the field with xpath "//div[contains(concat(' ', normalize-space(@class), ' '), ' availability-item ')][preceding-sibling::div]//select[@name='cm']" to "Page2" And I set the field with xpath "//div[contains(concat(' ', normalize-space(@class), ' '), ' availability-item ')][preceding-sibling::div]//select[@name='cm']" to "Page2"
And I press "Save and return to course" And I press "Save and return to course"
And I click on "Show more" "button" in the "#section-1 .availabilityinfo" "css_element"
Then I should see "Not available unless:" in the ".activity.glossary" "css_element" Then I should see "Not available unless:" in the ".activity.glossary" "css_element"
And I should see "The activity Page1 is marked complete" in the ".activity.glossary" "css_element" And I should see "The activity Page1 is marked complete" in the ".activity.glossary" "css_element"
And I should see "The activity Page2 is marked complete" in the ".activity.glossary" "css_element" And I should see "The activity Page2 is marked complete" in the ".activity.glossary" "css_element"

View File

@ -73,13 +73,17 @@ Feature: display_availability
And I should see "2013" in the "#section-1 .availabilityinfo" "css_element" And I should see "2013" in the "#section-1 .availabilityinfo" "css_element"
And I should see "2013" in the "#section-1 .availabilityinfo" "css_element" And I should see "2013" in the "#section-1 .availabilityinfo" "css_element"
And "li" "css_element" should not exist in the "#section-1 .availabilityinfo" "css_element" And "li" "css_element" should not exist in the "#section-1 .availabilityinfo" "css_element"
And "Show more" "button" should not exist in the "#section-1 .availabilityinfo" "css_element"
# Page 2 should show in list format. # Page 2 should show in list format.
And "li" "css_element" should exist in the "#section-2 .availabilityinfo" "css_element" And "li" "css_element" should exist in the "#section-2 .availabilityinfo" "css_element"
And I should see "Not available unless:" in the "#section-2 .availabilityinfo" "css_element" And I should see "Not available unless:" in the "#section-2 .availabilityinfo" "css_element"
And I should see "It is before" in the "#section-2 .availabilityinfo" "css_element" And I should see "It is before" in the "#section-2 .availabilityinfo" "css_element"
And I should see "hidden otherwise" in the "#section-2 .availabilityinfo" "css_element" And I should see "hidden otherwise" in the "#section-2 .availabilityinfo" "css_element"
And I click on "Show more" "button" in the "Page 2" "activity"
And I should see "Email address" in the "#section-2 .availabilityinfo" "css_element" And I should see "Email address" in the "#section-2 .availabilityinfo" "css_element"
And I click on "Show less" "button" in the "#section-2 .availabilityinfo" "css_element"
And I should not see "Email address" in the "#section-2 .availabilityinfo" "css_element"
# Page 3 should not have available info. # Page 3 should not have available info.
And "#section-3 .availabilityinfo" "css_element" should not exist And "#section-3 .availabilityinfo" "css_element" should not exist
@ -114,6 +118,11 @@ Feature: display_availability
And I click on "Date" "button" in the "Add restriction..." "dialogue" And I click on "Date" "button" in the "Add restriction..." "dialogue"
And I set the field "direction" to "until" And I set the field "direction" to "until"
And I set the field "x[year]" to "2013" And I set the field "x[year]" to "2013"
And I press "Add restriction..."
And I click on "User profile" "button" in the "Add restriction..." "dialogue"
And I set the field "User profile field" to "Email address"
And I set the field "Value to compare against" to "email@example.com"
And I set the field "Method of comparison" to "is equal to"
And I press "Save changes" And I press "Save changes"
# Section 2 is the same but hidden from students # Section 2 is the same but hidden from students
@ -130,7 +139,7 @@ Feature: display_availability
And I am on "Course 1" course homepage And I am on "Course 1" course homepage
# Check display # Check display
Then I should see "Available until" in the "#section-1 .availabilityinfo" "css_element" Then I should see "Not available unless" in the "#section-1 .availabilityinfo" "css_element"
And I should see "Available until" in the "#section-2 .availabilityinfo" "css_element" And I should see "Available until" in the "#section-2 .availabilityinfo" "css_element"
And I should see "hidden otherwise" in the "#section-2 .availabilityinfo" "css_element" And I should see "hidden otherwise" in the "#section-2 .availabilityinfo" "css_element"
@ -144,7 +153,11 @@ Feature: display_availability
# Section 1 should be visible and show info. # Section 1 should be visible and show info.
And I should see "Topic 1" in the "region-main" "region" And I should see "Topic 1" in the "region-main" "region"
And I should see "Available until" in the "#section-1 .availabilityinfo" "css_element" And I should see "Not available unless" in the "#section-1 .availabilityinfo" "css_element"
And I click on "Show more" "button" in the "#section-1 .availabilityinfo" "css_element"
And I should see "Email address" in the "#section-1 .availabilityinfo" "css_element"
And I click on "Show less" "button" in the "#section-1 .availabilityinfo" "css_element"
And I should not see "Email address" in the "#section-1 .availabilityinfo" "css_element"
# Section 2 should not be available at all # Section 2 should not be available at all
And I should not see "Topic 2" in the "region-main" "region" And I should not see "Topic 2" in the "region-main" "region"

View File

@ -108,11 +108,7 @@ class availability extends section_avalability {
} }
$info = []; $info = [];
$formattedinfo = \core_availability\info::format_info( $info[] = $this->get_availability_data($output, $this->mod->availableinfo, 'isrestricted');
$this->mod->availableinfo,
$this->mod->get_course()
);
$info[] = $this->availability_info($formattedinfo, 'isrestricted');
return $info; return $info;
} }
@ -151,11 +147,7 @@ class availability extends section_avalability {
if (!$mod->visible) { if (!$mod->visible) {
$hidinfoclass .= ' hide'; $hidinfoclass .= ' hide';
} }
$formattedinfo = info::format_info( $info[] = $this->get_availability_data($output, $fullinfo, $hidinfoclass);
$fullinfo,
$mod->get_course()
);
$info[] = $this->availability_info($formattedinfo, $hidinfoclass);
return $info; return $info;
} }

View File

@ -25,6 +25,7 @@
namespace core_courseformat\output\local\content\section; namespace core_courseformat\output\local\content\section;
use context_course; use context_course;
use core_availability_multiple_messages;
use core\output\named_templatable; use core\output\named_templatable;
use core_availability\info; use core_availability\info;
use core_availability\info_section; use core_availability\info_section;
@ -57,6 +58,9 @@ class availability implements named_templatable, renderable {
/** @var stdClass|null the instance export data */ /** @var stdClass|null the instance export data */
protected $data = null; protected $data = null;
/** @var int Availability excerpt text max size treshold */
protected const AVAILABILITY_EXCERPT_MAXSIZE = 100;
/** /**
* Constructor. * Constructor.
* *
@ -124,7 +128,7 @@ class availability implements named_templatable, renderable {
* are going to be unavailable etc). This logic is the same as for * are going to be unavailable etc). This logic is the same as for
* activities. * activities.
* *
* @param renderer_base $output typically, the renderer that's calling this function * @param \renderer_base $output typically, the renderer that's calling this function
* @return stdclass data context for a mustache template * @return stdclass data context for a mustache template
*/ */
protected function get_info(\renderer_base $output): array { protected function get_info(\renderer_base $output): array {
@ -142,30 +146,115 @@ class availability implements named_templatable, renderable {
if ($section->availableinfo) { if ($section->availableinfo) {
// Note: We only get to this function if availableinfo is non-empty, // Note: We only get to this function if availableinfo is non-empty,
// so there is definitely something to print. // so there is definitely something to print.
$formattedinfo = info::format_info($section->availableinfo, $section->course); $info[] = $this->get_availability_data($output, $section->availableinfo, 'isrestricted');
$info[] = $this->availability_info($formattedinfo, 'isrestricted');
} }
} else if ($canviewhidden && !empty($CFG->enableavailability)) { } else if ($canviewhidden && !empty($CFG->enableavailability)) {
// Check if there is an availability restriction. // Check if there is an availability restriction.
$ci = new info_section($section); $ci = new info_section($section);
$fullinfo = $ci->get_full_information(); $fullinfo = $ci->get_full_information();
if ($fullinfo) { if ($fullinfo) {
$formattedinfo = info::format_info($fullinfo, $section->course); $info[] = $this->get_availability_data($output, $fullinfo, 'isrestricted isfullinfo');
$info[] = $this->availability_info($formattedinfo, 'isrestricted isfullinfo');
} }
} }
return $info; return $info;
} }
/**
* Get the basic availability information data.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param string|core_availability_multiple_messages $availabilityinfo the avalability info
* @param string $additionalclasses additional css classes
* @return stdClass the availability information data
*/
protected function get_availability_data($output, $availabilityinfo, $additionalclasses = ''): stdClass {
// At this point, availabilityinfo is either a string or a renderable. We need to handle both cases in a different way.
if (is_string($availabilityinfo)) {
$data = $this->availability_info_from_string($output, $availabilityinfo);
} else {
$data = $this->availability_info_from_output($output, $availabilityinfo);
}
$data->classes = $additionalclasses;
$additionalclasses = array_filter(explode(' ', $additionalclasses));
if (in_array('ishidden', $additionalclasses)) {
$data->ishidden = 1;
} else if (in_array('isstealth', $additionalclasses)) {
$data->isstealth = 1;
} else if (in_array('isrestricted', $additionalclasses)) {
$data->isrestricted = 1;
if (in_array('isfullinfo', $additionalclasses)) {
$data->isfullinfo = 1;
}
}
return $data;
}
/**
* Generate the basic availability information data from a string.
* Just shorten availability text to generate the excerpt text.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param string $availabilityinfo the avalability info
* @return stdClass the availability information data
*/
protected function availability_info_from_string(\renderer_base $output, string $availabilityinfo): stdClass {
$course = $this->format->get_course();
$text = info::format_info($availabilityinfo, $course);
$data = ['text' => $text];
if (strlen(html_to_text($text)) > self::AVAILABILITY_EXCERPT_MAXSIZE) {
$data['excerpt'] = shorten_text($text, self::AVAILABILITY_EXCERPT_MAXSIZE);
}
return (object) $data;
}
/**
* Generate the basic availability information data from a renderable.
* Use the header and the first item to generate the excerpt text.
*
* @param \renderer_base $output typically, the renderer that's calling this function
* @param core_availability_multiple_messages $availabilityinfo the avalability info
* @return stdClass the availability information data
*/
protected function availability_info_from_output(
\renderer_base $output,
core_availability_multiple_messages $availabilityinfo
): stdClass {
$course = $this->format->get_course();
$renderable = new \core_availability\output\availability_info($availabilityinfo);
// We need to export_for_template() instead of directly render, to reuse the info for both 'text' and 'excerpt'.
$info = $renderable->export_for_template($output);
$text = $output->render_from_template('core_availability/availability_info', $info);
$data = ['text' => info::format_info($text, $course)];
if (!empty($info->items)) {
$excerpttext = $info->header . ' ' . $info->items[0]->header;
$data['excerpt'] = info::format_info($excerpttext, $course);
}
return (object) $data;
}
/** /**
* Generate the basic availability information data. * Generate the basic availability information data.
* *
* @deprecated since Moodle 4.3 MDL-78204. Please use {@see self::get_availability_data} instead.
* @todo MDL-78489 This will be deleted in Moodle 4.7.
* @param string $text the formatted avalability text * @param string $text the formatted avalability text
* @param string $additionalclasses additional css classes * @param string $additionalclasses additional css classes
* @return stdClass the availability information data * @return stdClass the availability information data
*/ */
protected function availability_info($text, $additionalclasses = ''): stdClass { protected function availability_info($text, $additionalclasses = ''): stdClass {
debugging('Use of ' . __FUNCTION__ . '() have been deprecated, ' .
'please use core_courseformat\output\local\content\section\availability::get_availability_data()', DEBUG_DEVELOPER);
$data = (object)[ $data = (object)[
'text' => $text, 'text' => $text,

View File

@ -0,0 +1,43 @@
{{!
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_courseformat/local/content/availability
Displays the availability contents with excerpt version if needed.
Example context (json):
{
"text": "Not available unless: <ul><li>It is on or after <strong>8 June 2012</strong></li><li>You achieve higher than a certain score in <strong>Course total</strong></li></ul>",
"excerpt": "Not available unless: It is on or after <strong>8 June 2012</strong>"
}
}}
{{^excerpt}}
{{#pix}}t/unlock, core{{/pix}} {{{text}}}
{{/excerpt}}
{{#excerpt}}
{{< core/showmore }}
{{$collapsedcontent}}
{{#pix}}t/unlock, core{{/pix}} <span class="availability-excerpt"> {{{excerpt}}} ... </span>
{{/collapsedcontent}}
{{$expandedcontent}}
{{#pix}}t/unlock, core{{/pix}} {{{text}}}
{{/expandedcontent}}
{{$buttonextraclasses}}font-weight-bold{{/buttonextraclasses}}
{{$collapsedextraclasses}}text-truncate pr-2{{/collapsedextraclasses}}
{{$expandedextraclasses}}py-2{{/expandedextraclasses}}
{{/core/showmore }}
{{/excerpt}}

View File

@ -41,7 +41,7 @@
<span class="badge badge-pill badge-warning">{{{text}}}</span> <span class="badge badge-pill badge-warning">{{{text}}}</span>
{{/isrestricted}} {{/isrestricted}}
{{#isrestricted}} {{#isrestricted}}
<div> {{#pix}}t/unlock, core{{/pix}} {{{text}}} </div> {{> core_courseformat/local/content/availability }}
{{/isrestricted}} {{/isrestricted}}
</div> </div>
{{/info}} {{/info}}

View File

@ -41,7 +41,7 @@
<span class="badge badge-pill badge-warning">{{{text}}}</span> <span class="badge badge-pill badge-warning">{{{text}}}</span>
{{/isrestricted}} {{/isrestricted}}
{{#isrestricted}} {{#isrestricted}}
<div> {{#pix}}t/unlock, core{{/pix}} {{{text}}} </div> {{> core_courseformat/local/content/availability }}
{{/isrestricted}} {{/isrestricted}}
</div> </div>
{{/info}} {{/info}}

View File

@ -12,6 +12,8 @@ Some considerations about the activitybadge feature:
- An optional URL to redirect the user when the badge is clicked. - An optional URL to redirect the user when the badge is clicked.
- An optional ID to add the element in case the module wants to add some JS to the badge events. - An optional ID to add the element in case the module wants to add some JS to the badge events.
- Optionally, any other extra HTML attributes to the badge element (for example, data attributes). - Optionally, any other extra HTML attributes to the badge element (for example, data attributes).
* Protected function `core_courseformat\output\local\content\section\availability::availability_info()` has been deprecated,
`core_courseformat\output\local\content\section\availability::get_availability_data()` should be used instead.
=== 4.2 === === 4.2 ===
* New core_courseformat\base::get_context() to get the course context directly from the format instance. * New core_courseformat\base::get_context() to get the course context directly from the format instance.