mirror of
https://github.com/moodle/moodle.git
synced 2025-08-08 10:26:40 +02:00
Merge branch 'MDL-82259-main' of https://github.com/aanabit/moodle
This commit is contained in:
commit
d11eaeefb9
11 changed files with 370 additions and 108 deletions
10
.upgradenotes/MDL-82259-2024072314533865.yml
Normal file
10
.upgradenotes/MDL-82259-2024072314533865.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
issueNumber: MDL-82259
|
||||||
|
notes:
|
||||||
|
core_course:
|
||||||
|
- message: >-
|
||||||
|
i_open_section_edit_menu(), i_show_section(), i_hide_section(),
|
||||||
|
i_wait_until_section_is_available(),
|
||||||
|
show_section_link_exists(), hide_section_link_exists() and
|
||||||
|
section_exists() functions have been improved to accept not only section
|
||||||
|
number but also section name.
|
||||||
|
type: improved
|
|
@ -17,6 +17,7 @@
|
||||||
namespace core_courseformat\local;
|
namespace core_courseformat\local;
|
||||||
|
|
||||||
|
|
||||||
|
use core_courseformat\sectiondelegatemodule;
|
||||||
use course_modinfo;
|
use course_modinfo;
|
||||||
/**
|
/**
|
||||||
* Course module course format actions.
|
* Course module course format actions.
|
||||||
|
@ -26,8 +27,44 @@ use course_modinfo;
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
class cmactions extends baseactions {
|
class cmactions extends baseactions {
|
||||||
|
/**
|
||||||
|
* Update a course delegated section linked to the given module.
|
||||||
|
*
|
||||||
|
* @param \stdClass $cm
|
||||||
|
* @param array $sectionfields to change in section database record.
|
||||||
|
* @param bool $rebuildcache If true (default), perform a partial cache purge and rebuild.
|
||||||
|
* @return bool true if any delegated section has been updated, false otherwise.
|
||||||
|
*/
|
||||||
|
protected function update_delegated(
|
||||||
|
\stdClass $cm,
|
||||||
|
array $sectionfields,
|
||||||
|
bool $rebuildcache = true
|
||||||
|
): bool {
|
||||||
|
|
||||||
|
if (!sectiondelegatemodule::has_delegate_class('mod_' . $cm->modname)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate the changes to delegated section.
|
||||||
|
$cminfo = \cm_info::create($cm);
|
||||||
|
if (!$delegatedsection = $cminfo->get_delegated_section_info()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sectionactions = new sectionactions($this->course);
|
||||||
|
$sectionactions->update($delegatedsection, $sectionfields);
|
||||||
|
|
||||||
|
if ($rebuildcache) {
|
||||||
|
course_modinfo::purge_course_section_cache_by_id($cm->course, $delegatedsection->id);
|
||||||
|
rebuild_course_cache($cm->course, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rename a course module.
|
* Rename a course module.
|
||||||
|
*
|
||||||
* @param int $cmid the course module id.
|
* @param int $cmid the course module id.
|
||||||
* @param string $name the new name.
|
* @param string $name the new name.
|
||||||
* @return bool true if the course module was renamed, false otherwise.
|
* @return bool true if the course module was renamed, false otherwise.
|
||||||
|
@ -64,12 +101,16 @@ class cmactions extends baseactions {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$cm->name = $name;
|
$cm->name = $name;
|
||||||
|
$fields = new \stdClass();
|
||||||
|
$fields->name = $name;
|
||||||
|
|
||||||
\core\event\course_module_updated::create_from_cm($cm)->trigger();
|
\core\event\course_module_updated::create_from_cm($cm)->trigger();
|
||||||
|
|
||||||
course_modinfo::purge_course_module_cache($cm->course, $cm->id);
|
course_modinfo::purge_course_module_cache($cm->course, $cm->id);
|
||||||
rebuild_course_cache($cm->course, false, true);
|
rebuild_course_cache($cm->course, false, true);
|
||||||
|
|
||||||
|
$this->update_delegated($cm, ['name' => $name]);
|
||||||
|
|
||||||
// Modules may add some logic to renaming.
|
// Modules may add some logic to renaming.
|
||||||
$modinfo = get_fast_modinfo($cm->course);
|
$modinfo = get_fast_modinfo($cm->course);
|
||||||
\core\di::get(\core\hook\manager::class)->dispatch(
|
\core\di::get(\core\hook\manager::class)->dispatch(
|
||||||
|
@ -87,4 +128,100 @@ class cmactions extends baseactions {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a course module.
|
||||||
|
*
|
||||||
|
* @param int $cmid the course module id.
|
||||||
|
* @param int $visible state of the module
|
||||||
|
* @param int $visibleoncoursepage state of the module on the course page
|
||||||
|
* @param bool $rebuildcache If true (default), perform a partial cache purge and rebuild.
|
||||||
|
* @return bool whether course module was updated
|
||||||
|
*/
|
||||||
|
public function set_visibility(int $cmid, int $visible, int $visibleoncoursepage = 1, bool $rebuildcache = true): bool {
|
||||||
|
global $DB, $CFG;
|
||||||
|
require_once($CFG->libdir.'/gradelib.php');
|
||||||
|
require_once($CFG->dirroot.'/calendar/lib.php');
|
||||||
|
|
||||||
|
if (!$cm = get_coursemodule_from_id('', $cmid, 0, false, MUST_EXIST)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create events and propagate visibility to associated grade items if the value has changed.
|
||||||
|
// Only do this if it's changed to avoid accidently overwriting manual showing/hiding of student grades.
|
||||||
|
if ($cm->visible == $visible && $cm->visibleoncoursepage == $visibleoncoursepage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$modulename = $DB->get_field('modules', 'name', ['id' => $cm->module])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updating visible and visibleold to keep them in sync. Only changing a section visibility will
|
||||||
|
// affect visibleold to allow for an original visibility restore. See set_section_visible().
|
||||||
|
$cminfo = (object)[
|
||||||
|
'id' => $cmid,
|
||||||
|
'visible' => $visible,
|
||||||
|
'visibleoncoursepage' => $visibleoncoursepage,
|
||||||
|
'visibleold' => $visible,
|
||||||
|
];
|
||||||
|
|
||||||
|
$DB->update_record('course_modules', $cminfo);
|
||||||
|
$DB->update_record(
|
||||||
|
$cm->modname,
|
||||||
|
(object)[
|
||||||
|
'id' => $cm->instance,
|
||||||
|
'timemodified' => time(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$fields = ['visible' => $visible, 'visibleold' => $visible];
|
||||||
|
$this->update_delegated($cm, $fields, false);
|
||||||
|
|
||||||
|
if ($rebuildcache) {
|
||||||
|
\course_modinfo::purge_course_module_cache($cm->course, $cm->id);
|
||||||
|
rebuild_course_cache($cm->course, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cm->visible == $visible) {
|
||||||
|
// There is nothing else to change.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($events = $DB->get_records('event', ['instance' => $cm->instance, 'modulename' => $modulename])) {
|
||||||
|
foreach ($events as $event) {
|
||||||
|
if ($visible) {
|
||||||
|
$event = new \calendar_event($event);
|
||||||
|
$event->toggle_visibility(true);
|
||||||
|
} else {
|
||||||
|
$event = new \calendar_event($event);
|
||||||
|
$event->toggle_visibility(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there.
|
||||||
|
// Note that this must be done after updating the row in course_modules, in case
|
||||||
|
// the modules grade_item_update function needs to access $cm->visible.
|
||||||
|
$supportsgrade = plugin_supports('mod', $modulename, FEATURE_CONTROLS_GRADE_VISIBILITY) &&
|
||||||
|
component_callback_exists('mod_' . $modulename, 'grade_item_update');
|
||||||
|
if ($supportsgrade) {
|
||||||
|
$instance = $DB->get_record($modulename, ['id' => $cm->instance], '*', MUST_EXIST);
|
||||||
|
component_callback('mod_' . $modulename, 'grade_item_update', [$instance]);
|
||||||
|
} else {
|
||||||
|
$gradeitems = \grade_item::fetch_all([
|
||||||
|
'itemtype' => 'mod',
|
||||||
|
'itemmodule' => $modulename,
|
||||||
|
'iteminstance' => $cm->instance,
|
||||||
|
'courseid' => $cm->course,
|
||||||
|
]);
|
||||||
|
if ($gradeitems) {
|
||||||
|
foreach ($gradeitems as $gradeitem) {
|
||||||
|
$gradeitem->set_hidden(!$visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,6 +404,18 @@ class sectionactions extends baseactions {
|
||||||
|
|
||||||
$modules = explode(',', $sectioninfo->sequence);
|
$modules = explode(',', $sectioninfo->sequence);
|
||||||
$cmids = [];
|
$cmids = [];
|
||||||
|
|
||||||
|
// In case the section is delegated by a module, we change also the visibility for the source module.
|
||||||
|
if ($sectioninfo->is_delegated()) {
|
||||||
|
$delegateinstance = $sectioninfo->get_component_instance();
|
||||||
|
// We only return sections delegated by course modules. Sections delegated to other
|
||||||
|
// types of components must implement their own methods to get the section.
|
||||||
|
if ($delegateinstance && ($delegateinstance instanceof \core_courseformat\sectiondelegatemodule)) {
|
||||||
|
$delegator = $delegateinstance->get_cm();
|
||||||
|
$modules[] = $delegator->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($modules as $moduleid) {
|
foreach ($modules as $moduleid) {
|
||||||
$cm = get_coursemodule_from_id(null, $moduleid, $this->course->id);
|
$cm = get_coursemodule_from_id(null, $moduleid, $this->course->id);
|
||||||
if (!$cm) {
|
if (!$cm) {
|
||||||
|
|
|
@ -121,6 +121,52 @@ class delegatedcontrolmenu extends basecontrolmenu {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide/Show uses module functionality.
|
||||||
|
// Hide/Show options will be available for subsections inside visible sections only.
|
||||||
|
$parentsection = $cm->get_section_info();
|
||||||
|
$availablevisibility = has_capability('moodle/course:sectionvisibility', $coursecontext, $user) && $parentsection->visible;
|
||||||
|
if ($availablevisibility) {
|
||||||
|
$url = clone($baseurl);
|
||||||
|
if (!is_null($sectionreturn)) {
|
||||||
|
$url->param('sr', $format->get_sectionid());
|
||||||
|
}
|
||||||
|
$strhidefromothers = get_string('hidefromothers', 'format_' . $course->format);
|
||||||
|
$strshowfromothers = get_string('showfromothers', 'format_' . $course->format);
|
||||||
|
if ($section->visible) { // Show the hide/show eye.
|
||||||
|
$url->param('hide', $section->section);
|
||||||
|
$controls['visiblity'] = [
|
||||||
|
'url' => $url,
|
||||||
|
'icon' => 'i/show',
|
||||||
|
'name' => $strhidefromothers,
|
||||||
|
'pixattr' => ['class' => ''],
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'editing_showhide',
|
||||||
|
'data-sectionreturn' => $sectionreturn,
|
||||||
|
'data-action' => ($usecomponents) ? 'sectionHide' : 'hide',
|
||||||
|
'data-id' => $section->id,
|
||||||
|
'data-swapname' => $strshowfromothers,
|
||||||
|
'data-swapicon' => 'i/show',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$url->param('show', $section->section);
|
||||||
|
$controls['visiblity'] = [
|
||||||
|
'url' => $url,
|
||||||
|
'icon' => 'i/hide',
|
||||||
|
'name' => $strshowfromothers,
|
||||||
|
'pixattr' => ['class' => ''],
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'editing_showhide',
|
||||||
|
'data-sectionreturn' => $sectionreturn,
|
||||||
|
'data-action' => ($usecomponents) ? 'sectionShow' : 'show',
|
||||||
|
'data-id' => $section->id,
|
||||||
|
'data-swapname' => $strhidefromothers,
|
||||||
|
'data-swapicon' => 'i/hide',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete deletes the module.
|
// Delete deletes the module.
|
||||||
// Only show the view link if we are not already in the section view page.
|
// Only show the view link if we are not already in the section view page.
|
||||||
if (!$isheadersection && $hasmanageactivities) {
|
if (!$isheadersection && $hasmanageactivities) {
|
||||||
|
|
|
@ -149,7 +149,7 @@ abstract class sectiondelegatemodule extends sectiondelegate {
|
||||||
controlmenu $controlmenu,
|
controlmenu $controlmenu,
|
||||||
renderer_base $output,
|
renderer_base $output,
|
||||||
): ?action_menu {
|
): ?action_menu {
|
||||||
$controlmenuclass = $format->get_output_classname('content\\cm\\controlmenu');
|
$controlmenuclass = $format->get_output_classname('content\\cm\\delegatedcontrolmenu');
|
||||||
$controlmenu = new $controlmenuclass(
|
$controlmenu = new $controlmenuclass(
|
||||||
$format,
|
$format,
|
||||||
$this->sectioninfo,
|
$this->sectioninfo,
|
||||||
|
|
|
@ -542,8 +542,18 @@ class stateactions {
|
||||||
course_modinfo::purge_course_modules_cache($course->id, $ids);
|
course_modinfo::purge_course_modules_cache($course->id, $ids);
|
||||||
rebuild_course_cache($course->id, false, true);
|
rebuild_course_cache($course->id, false, true);
|
||||||
|
|
||||||
|
$delegatedsections = [];
|
||||||
foreach ($cms as $cm) {
|
foreach ($cms as $cm) {
|
||||||
$updates->add_cm_put($cm->id);
|
$updates->add_cm_put($cm->id);
|
||||||
|
if (!$delegatedsection = $cm->get_delegated_section_info()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!in_array($delegatedsection->id, $delegatedsections)) {
|
||||||
|
$delegatedsections[] = $delegatedsection->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($delegatedsections as $sectionid => $section) {
|
||||||
|
$updates->add_section_put($sectionid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
Example context (json):
|
Example context (json):
|
||||||
{
|
{
|
||||||
"menu": "<a href=\"#\" class=\"d-inline-block dropdown-toggle icon-no-margin\">Edit<b class=\"caret\"></b></a>",
|
"menu": "<a href=\"#\" data-toggle=\"dropdown\" class=\"d-inline-block dropdown-toggle icon-no-margin\">Edit<b class=\"caret\"></b></a>",
|
||||||
"hasmenu": true
|
"hasmenu": true
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -679,74 +679,15 @@ function set_downloadcontent(int $id, bool $downloadcontent): bool {
|
||||||
* and rebuilt as appropriate. Consider using this if set_coursemodule_visible is called multiple times
|
* and rebuilt as appropriate. Consider using this if set_coursemodule_visible is called multiple times
|
||||||
* (e.g. in a loop).
|
* (e.g. in a loop).
|
||||||
*
|
*
|
||||||
* @param int $id of the module
|
* @param int $cmid course module id
|
||||||
* @param int $visible state of the module
|
* @param int $visible state of the module
|
||||||
* @param int $visibleoncoursepage state of the module on the course page
|
* @param int $visibleoncoursepage state of the module on the course page
|
||||||
* @param bool $rebuildcache If true (default), perform a partial cache purge and rebuild.
|
* @param bool $rebuildcache If true (default), perform a partial cache purge and rebuild.
|
||||||
* @return bool false when the module was not found, true otherwise
|
* @return bool false when the module was not found, true otherwise
|
||||||
*/
|
*/
|
||||||
function set_coursemodule_visible($id, $visible, $visibleoncoursepage = 1, bool $rebuildcache = true) {
|
function set_coursemodule_visible($cmid, $visible, $visibleoncoursepage = 1, bool $rebuildcache = true) {
|
||||||
global $DB, $CFG;
|
$coursecontext = context_module::instance($cmid)->get_course_context();
|
||||||
require_once($CFG->libdir.'/gradelib.php');
|
return formatactions::cm($coursecontext->instanceid)->set_visibility($cmid, $visible, $visibleoncoursepage, $rebuildcache);
|
||||||
require_once($CFG->dirroot.'/calendar/lib.php');
|
|
||||||
|
|
||||||
if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create events and propagate visibility to associated grade items if the value has changed.
|
|
||||||
// Only do this if it's changed to avoid accidently overwriting manual showing/hiding of student grades.
|
|
||||||
if ($cm->visible == $visible && $cm->visibleoncoursepage == $visibleoncoursepage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (($cm->visible != $visible) &&
|
|
||||||
($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $modulename)))) {
|
|
||||||
foreach($events as $event) {
|
|
||||||
if ($visible) {
|
|
||||||
$event = new calendar_event($event);
|
|
||||||
$event->toggle_visibility(true);
|
|
||||||
} else {
|
|
||||||
$event = new calendar_event($event);
|
|
||||||
$event->toggle_visibility(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating visible and visibleold to keep them in sync. Only changing a section visibility will
|
|
||||||
// affect visibleold to allow for an original visibility restore. See set_section_visible().
|
|
||||||
$cminfo = new stdClass();
|
|
||||||
$cminfo->id = $id;
|
|
||||||
$cminfo->visible = $visible;
|
|
||||||
$cminfo->visibleoncoursepage = $visibleoncoursepage;
|
|
||||||
$cminfo->visibleold = $visible;
|
|
||||||
$DB->update_record('course_modules', $cminfo);
|
|
||||||
|
|
||||||
// Hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there.
|
|
||||||
// Note that this must be done after updating the row in course_modules, in case
|
|
||||||
// the modules grade_item_update function needs to access $cm->visible.
|
|
||||||
if ($cm->visible != $visible &&
|
|
||||||
plugin_supports('mod', $modulename, FEATURE_CONTROLS_GRADE_VISIBILITY) &&
|
|
||||||
component_callback_exists('mod_' . $modulename, 'grade_item_update')) {
|
|
||||||
$instance = $DB->get_record($modulename, array('id' => $cm->instance), '*', MUST_EXIST);
|
|
||||||
component_callback('mod_' . $modulename, 'grade_item_update', array($instance));
|
|
||||||
} else if ($cm->visible != $visible) {
|
|
||||||
$grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
|
|
||||||
if ($grade_items) {
|
|
||||||
foreach ($grade_items as $grade_item) {
|
|
||||||
$grade_item->set_hidden(!$visible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($rebuildcache) {
|
|
||||||
\course_modinfo::purge_course_module_cache($cm->course, $cm->id);
|
|
||||||
rebuild_course_cache($cm->course, false, true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -305,26 +305,26 @@ class behat_course extends behat_base {
|
||||||
/**
|
/**
|
||||||
* Opens a section edit menu if it is not already opened.
|
* Opens a section edit menu if it is not already opened.
|
||||||
*
|
*
|
||||||
* @Given /^I open section "(?P<section_number>\d+)" edit menu$/
|
* @Given /^I open section "(?P<section>(?:[^"]|\\")*)" edit menu$/
|
||||||
* @throws DriverException The step is not available when Javascript is disabled
|
* @throws DriverException The step is not available when Javascript is disabled
|
||||||
* @param string $sectionnumber
|
* @param string|int $section
|
||||||
*/
|
*/
|
||||||
public function i_open_section_edit_menu($sectionnumber) {
|
public function i_open_section_edit_menu($section) {
|
||||||
if (!$this->running_javascript()) {
|
if (!$this->running_javascript()) {
|
||||||
throw new DriverException('Section edit menu not available when Javascript is disabled');
|
throw new DriverException('Section edit menu not available when Javascript is disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for section to be available, before clicking on the menu.
|
// Wait for section to be available, before clicking on the menu.
|
||||||
$this->i_wait_until_section_is_available($sectionnumber);
|
$this->i_wait_until_section_is_available($section);
|
||||||
|
|
||||||
// If it is already opened we do nothing.
|
// If it is already opened we do nothing.
|
||||||
$xpath = $this->section_exists($sectionnumber);
|
$xpath = $this->section_exists($section);
|
||||||
$xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@data-toggle, 'dropdown')]";
|
$xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[@data-toggle='dropdown']";
|
||||||
|
|
||||||
$exception = new ExpectationException('Section "' . $sectionnumber . '" was not found', $this->getSession());
|
$exception = new ExpectationException('Section "' . $section . '" was not found', $this->getSession());
|
||||||
$menu = $this->find('xpath', $xpath, $exception);
|
$menu = $this->find('xpath', $xpath, $exception);
|
||||||
$menu->click();
|
$menu->click();
|
||||||
$this->i_wait_until_section_is_available($sectionnumber);
|
$this->i_wait_until_section_is_available($section);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -404,34 +404,45 @@ class behat_course extends behat_base {
|
||||||
/**
|
/**
|
||||||
* Shows the specified hidden section. You need to be in the course page and on editing mode.
|
* Shows the specified hidden section. You need to be in the course page and on editing mode.
|
||||||
*
|
*
|
||||||
* @Given /^I show section "(?P<section_number>\d+)"$/
|
* @Given /^I show section "(?P<section>(?:[^"]|\\")*)"$/
|
||||||
* @param int $sectionnumber
|
* @param int|string $section
|
||||||
*/
|
*/
|
||||||
public function i_show_section($sectionnumber) {
|
public function i_show_section($section) {
|
||||||
$showlink = $this->show_section_link_exists($sectionnumber);
|
// Ensures the section exists.
|
||||||
|
$xpath = $this->section_exists($section);
|
||||||
|
// We need to know the course format as the text strings depends on them.
|
||||||
|
$courseformat = $this->get_course_format();
|
||||||
|
$strshow = get_string('showfromothers', $courseformat);
|
||||||
|
|
||||||
// Ensure section edit menu is open before interacting with it.
|
|
||||||
|
// If javascript is on, link is inside a menu.
|
||||||
if ($this->running_javascript()) {
|
if ($this->running_javascript()) {
|
||||||
$this->i_open_section_edit_menu($sectionnumber);
|
$this->i_open_section_edit_menu($section);
|
||||||
}
|
}
|
||||||
$showlink->click();
|
|
||||||
|
// Ensure the click is using the action menu and not the visibility badge.
|
||||||
|
$xpath .= "//*[@role='menu']";
|
||||||
|
|
||||||
|
// Click on hide link.
|
||||||
|
$this->execute('behat_general::i_click_on_in_the',
|
||||||
|
[$strshow, "link", $this->escape($xpath), "xpath_element"]
|
||||||
|
);
|
||||||
|
|
||||||
if ($this->running_javascript()) {
|
if ($this->running_javascript()) {
|
||||||
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||||
$this->i_wait_until_section_is_available($sectionnumber);
|
$this->i_wait_until_section_is_available($section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the specified visible section. You need to be in the course page and on editing mode.
|
* Hides the specified visible section. You need to be in the course page and on editing mode.
|
||||||
*
|
*
|
||||||
* @Given /^I hide section "(?P<section_number>\d+)"$/
|
* @Given /^I hide section "(?P<section>(?:[^"]|\\")*)"$/
|
||||||
* @param int $sectionnumber
|
* @param int|string $section
|
||||||
*/
|
*/
|
||||||
public function i_hide_section($sectionnumber) {
|
public function i_hide_section($section) {
|
||||||
// Ensures the section exists.
|
// Ensures the section exists.
|
||||||
$xpath = $this->section_exists($sectionnumber);
|
$xpath = $this->section_exists($section);
|
||||||
|
|
||||||
// We need to know the course format as the text strings depends on them.
|
// We need to know the course format as the text strings depends on them.
|
||||||
$courseformat = $this->get_course_format();
|
$courseformat = $this->get_course_format();
|
||||||
if (get_string_manager()->string_exists('hidefromothers', $courseformat)) {
|
if (get_string_manager()->string_exists('hidefromothers', $courseformat)) {
|
||||||
|
@ -442,17 +453,17 @@ class behat_course extends behat_base {
|
||||||
|
|
||||||
// If javascript is on, link is inside a menu.
|
// If javascript is on, link is inside a menu.
|
||||||
if ($this->running_javascript()) {
|
if ($this->running_javascript()) {
|
||||||
$this->i_open_section_edit_menu($sectionnumber);
|
$this->i_open_section_edit_menu($section);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click on delete link.
|
// Click on hide link.
|
||||||
$this->execute('behat_general::i_click_on_in_the',
|
$this->execute('behat_general::i_click_on_in_the',
|
||||||
array($strhide, "link", $this->escape($xpath), "xpath_element")
|
array($strhide, "link", $this->escape($xpath), "xpath_element")
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($this->running_javascript()) {
|
if ($this->running_javascript()) {
|
||||||
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||||
$this->i_wait_until_section_is_available($sectionnumber);
|
$this->i_wait_until_section_is_available($section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1234,14 +1245,14 @@ class behat_course extends behat_base {
|
||||||
* Hopefully we would not require test writers to use this step
|
* Hopefully we would not require test writers to use this step
|
||||||
* and we will manage it from other step definitions.
|
* and we will manage it from other step definitions.
|
||||||
*
|
*
|
||||||
* @Given /^I wait until section "(?P<section_number>\d+)" is available$/
|
* @Given /^I wait until section "(?P<section>(?:[^"]|\\")*)" is available$/
|
||||||
* @param int $sectionnumber
|
* @param int|string $section
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function i_wait_until_section_is_available($sectionnumber) {
|
public function i_wait_until_section_is_available($section) {
|
||||||
|
|
||||||
// Looks for a hidden lightbox or a non-existent lightbox in that section.
|
// Looks for a hidden lightbox or a non-existent lightbox in that section.
|
||||||
$sectionxpath = $this->section_exists($sectionnumber);
|
$sectionxpath = $this->section_exists($section);
|
||||||
$hiddenlightboxxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]" .
|
$hiddenlightboxxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]" .
|
||||||
" | " .
|
" | " .
|
||||||
$sectionxpath . "[count(child::div[contains(@class, 'lightbox')]) = 0]";
|
$sectionxpath . "[count(child::div[contains(@class, 'lightbox')]) = 0]";
|
||||||
|
@ -1282,10 +1293,26 @@ class behat_course extends behat_base {
|
||||||
* Checks if the course section exists.
|
* Checks if the course section exists.
|
||||||
*
|
*
|
||||||
* @throws ElementNotFoundException Thrown by behat_base::find
|
* @throws ElementNotFoundException Thrown by behat_base::find
|
||||||
|
* @param int|string $section Section number or name to look for.
|
||||||
|
* @return string The xpath of the section.
|
||||||
|
*/
|
||||||
|
protected function section_exists($section) {
|
||||||
|
|
||||||
|
if (is_numeric($section)) {
|
||||||
|
return $this->section_number_exists($section);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->section_name_exists($section);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the course section number exists.
|
||||||
|
*
|
||||||
|
* @throws ElementNotFoundException Thrown by behat_base::find
|
||||||
* @param int $sectionnumber
|
* @param int $sectionnumber
|
||||||
* @return string The xpath of the section.
|
* @return string The xpath of the section.
|
||||||
*/
|
*/
|
||||||
protected function section_exists($sectionnumber) {
|
protected function section_number_exists(int $sectionnumber): string {
|
||||||
|
|
||||||
// Just to give more info in case it does not exist.
|
// Just to give more info in case it does not exist.
|
||||||
$xpath = "//li[@id='section-" . $sectionnumber . "']";
|
$xpath = "//li[@id='section-" . $sectionnumber . "']";
|
||||||
|
@ -1295,17 +1322,39 @@ class behat_course extends behat_base {
|
||||||
return $xpath;
|
return $xpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the section name exists.
|
||||||
|
*
|
||||||
|
* @throws ElementNotFoundException Thrown by behat_base::find
|
||||||
|
* @param string $sectionname
|
||||||
|
* @return string The xpath of the section.
|
||||||
|
*/
|
||||||
|
protected function section_name_exists(string $sectionname): string {
|
||||||
|
// Let's try to find section or subsection in course page.
|
||||||
|
$xpath = "//li[@data-for='section']//*[@data-for='section_title' and contains(normalize-space(.), '" . $sectionname ."')]";
|
||||||
|
$exception = new ElementNotFoundException($this->getSession(), "Section $sectionname ");
|
||||||
|
try {
|
||||||
|
$this->find('xpath', $xpath, $exception);
|
||||||
|
} catch (ElementNotFoundException $e) {
|
||||||
|
// Let's try to find section in section page.
|
||||||
|
$xpath = "//header[@id='page-header' and contains(normalize-space(.), '" . $sectionname ."')]";
|
||||||
|
$this->find('xpath', $xpath, $exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $xpath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the show section icon or throws an exception.
|
* Returns the show section icon or throws an exception.
|
||||||
*
|
*
|
||||||
* @throws ElementNotFoundException Thrown by behat_base::find
|
* @throws ElementNotFoundException Thrown by behat_base::find
|
||||||
* @param int $sectionnumber
|
* @param int|string $section Section number or name to look for.
|
||||||
* @return NodeElement
|
* @return NodeElement
|
||||||
*/
|
*/
|
||||||
protected function show_section_link_exists($sectionnumber) {
|
protected function show_section_link_exists($section) {
|
||||||
|
|
||||||
// Gets the section xpath and ensure it exists.
|
// Gets the section xpath and ensure it exists.
|
||||||
$xpath = $this->section_exists($sectionnumber);
|
$xpath = $this->section_exists($section);
|
||||||
|
|
||||||
// We need to know the course format as the text strings depends on them.
|
// We need to know the course format as the text strings depends on them.
|
||||||
$courseformat = $this->get_course_format();
|
$courseformat = $this->get_course_format();
|
||||||
|
@ -1324,13 +1373,13 @@ class behat_course extends behat_base {
|
||||||
* Returns the hide section icon link if it exists or throws exception.
|
* Returns the hide section icon link if it exists or throws exception.
|
||||||
*
|
*
|
||||||
* @throws ElementNotFoundException Thrown by behat_base::find
|
* @throws ElementNotFoundException Thrown by behat_base::find
|
||||||
* @param int $sectionnumber
|
* @param int|string $section Section number or name to look for.
|
||||||
* @return NodeElement
|
* @return NodeElement
|
||||||
*/
|
*/
|
||||||
protected function hide_section_link_exists($sectionnumber) {
|
protected function hide_section_link_exists($section) {
|
||||||
|
|
||||||
// Gets the section xpath and ensure it exists.
|
// Gets the section xpath and ensure it exists.
|
||||||
$xpath = $this->section_exists($sectionnumber);
|
$xpath = $this->section_exists($section);
|
||||||
|
|
||||||
// We need to know the course format as the text strings depends on them.
|
// We need to know the course format as the text strings depends on them.
|
||||||
$courseformat = $this->get_course_format();
|
$courseformat = $this->get_course_format();
|
||||||
|
@ -1370,6 +1419,10 @@ class behat_course extends behat_base {
|
||||||
throw $exception;
|
throw $exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strstr($bodyid, 'page-course-view-section-') !== false) {
|
||||||
|
return 'format_' . str_replace('page-course-view-section-', '', $bodyid);
|
||||||
|
}
|
||||||
|
|
||||||
return 'format_' . str_replace('page-course-view-', '', $bodyid);
|
return 'format_' . str_replace('page-course-view-', '', $bodyid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,10 @@ Feature: The module menu replaces the delegated section menu
|
||||||
And I should not see "Assign roles"
|
And I should not see "Assign roles"
|
||||||
And I should not see "Highlight"
|
And I should not see "Highlight"
|
||||||
And I should see "Edit settings"
|
And I should see "Edit settings"
|
||||||
# Duplicate, Move and Show/Hide are not implemented yet.
|
# Duplicate and Move are not implemented yet.
|
||||||
And I should not see "Move"
|
And I should not see "Move"
|
||||||
And I should not see "Duplicate"
|
And I should not see "Duplicate"
|
||||||
And I should not see "Hide"
|
And I should see "Hide"
|
||||||
# Delete option for subsection page is not implemented yet.
|
# Delete option for subsection page is not implemented yet.
|
||||||
And I should not see "Delete"
|
And I should not see "Delete"
|
||||||
And I should see "Permalink"
|
And I should see "Permalink"
|
||||||
|
@ -50,12 +50,11 @@ Feature: The module menu replaces the delegated section menu
|
||||||
And I should not see "Highlight"
|
And I should not see "Highlight"
|
||||||
And I should see "View"
|
And I should see "View"
|
||||||
And I should see "Edit settings"
|
And I should see "Edit settings"
|
||||||
# Duplicate, Move and Show/Hide are not implemented yet.
|
# Duplicate and Move are not implemented yet.
|
||||||
And I should not see "Move"
|
And I should not see "Move"
|
||||||
And I should not see "Duplicate"
|
And I should not see "Duplicate"
|
||||||
And I should not see "Hide"
|
And I should see "Hide"
|
||||||
And I should see "Delete"
|
And I should see "Delete"
|
||||||
And I should see "Permalink"
|
|
||||||
|
|
||||||
@javascript
|
@javascript
|
||||||
Scenario: The action menu for subsection module in section page has less options than a regular activity
|
Scenario: The action menu for subsection module in section page has less options than a regular activity
|
||||||
|
@ -67,10 +66,10 @@ Feature: The module menu replaces the delegated section menu
|
||||||
And I should not see "Highlight"
|
And I should not see "Highlight"
|
||||||
And I should see "View"
|
And I should see "View"
|
||||||
And I should see "Edit settings"
|
And I should see "Edit settings"
|
||||||
# Duplicate, Move and Show/Hide are not implemented yet.
|
# Duplicate and Move are not implemented yet.
|
||||||
And I should not see "Move"
|
And I should not see "Move"
|
||||||
And I should not see "Duplicate"
|
And I should not see "Duplicate"
|
||||||
And I should not see "Hide"
|
And I should see "Hide"
|
||||||
And I should see "Delete"
|
And I should see "Delete"
|
||||||
And I should see "Permalink"
|
And I should see "Permalink"
|
||||||
|
|
||||||
|
@ -157,3 +156,54 @@ Feature: The module menu replaces the delegated section menu
|
||||||
# Subsection page. Open the section header action menu.
|
# Subsection page. Open the section header action menu.
|
||||||
And I click on "Edit" "icon" in the "[data-region='header-actions-container']" "css_element"
|
And I click on "Edit" "icon" in the "[data-region='header-actions-container']" "css_element"
|
||||||
And "Delete" "link" should not exist in the "[data-region='header-actions-container']" "css_element"
|
And "Delete" "link" should not exist in the "[data-region='header-actions-container']" "css_element"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: Hide/Show option in subsection action menu
|
||||||
|
Given I turn editing mode on
|
||||||
|
And I should not see "Hidden from students"
|
||||||
|
And I open "Subsection1" actions menu
|
||||||
|
When I choose "Hide" in the open action menu
|
||||||
|
Then I should see "Hidden from students"
|
||||||
|
Given I am on the "C1 > Subsection1" "course > section" page
|
||||||
|
And I should see "Hidden from students"
|
||||||
|
# Subsection page. Open the section header action menu.
|
||||||
|
And I click on "Edit" "icon" in the "[data-region='header-actions-container']" "css_element"
|
||||||
|
And I choose "Show" in the open action menu
|
||||||
|
And I should not see "Hidden from students"
|
||||||
|
And I click on "Section 1" "link" in the ".breadcrumb" "css_element"
|
||||||
|
And I should not see "Hidden from students"
|
||||||
|
# Section page. Open Subsection1 module action menu.
|
||||||
|
And I open "Subsection1" actions menu
|
||||||
|
And I choose "Hide" in the open action menu
|
||||||
|
And I should see "Hidden from students"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: Hide/Show option in course page action menu for subsections
|
||||||
|
Given I am on the "C1" "Course" page
|
||||||
|
And I turn editing mode on
|
||||||
|
When I hide section "Subsection1"
|
||||||
|
Then I should see "Hidden from students"
|
||||||
|
And I show section "Subsection1"
|
||||||
|
And I should not see "Hidden from students"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: Hide/Show option in subsection page action menu for subsections
|
||||||
|
Given I am on the "C1 > Subsection1" "course > section" page
|
||||||
|
And I turn editing mode on
|
||||||
|
When I hide section "Subsection1"
|
||||||
|
Then I should see "Hidden from students"
|
||||||
|
And I show section "Subsection1"
|
||||||
|
And I should not see "Hidden from students"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: Subsections can't change visibility in hidden sections.
|
||||||
|
Given I am on the "C1" "Course" page
|
||||||
|
And I turn editing mode on
|
||||||
|
And I hide section "Section 1"
|
||||||
|
When I open section "Subsection1" edit menu
|
||||||
|
Then I should not see "Hide"
|
||||||
|
And I should not see "Show"
|
||||||
|
And I am on the "C1 > Section 1" "course > section" page
|
||||||
|
And I open section "Subsection1" edit menu
|
||||||
|
And I should not see "Hide"
|
||||||
|
And I should not see "Show"
|
||||||
|
|
|
@ -66,7 +66,7 @@ final class sectiondelegate_test extends \advanced_testcase {
|
||||||
|
|
||||||
// Highlight is only present in section menu (not module), so they shouldn't be found in the result.
|
// Highlight is only present in section menu (not module), so they shouldn't be found in the result.
|
||||||
// Duplicate is not implemented yet, so they shouldn't be found in the result.
|
// Duplicate is not implemented yet, so they shouldn't be found in the result.
|
||||||
// The possible options are: View, Edit, Delete and Permalink.
|
// The possible options are: View, Edit, Show, Hide, Delete and Permalink.
|
||||||
if (get_string_manager()->string_exists('editsection', 'format_'.$format->get_format())) {
|
if (get_string_manager()->string_exists('editsection', 'format_'.$format->get_format())) {
|
||||||
$streditsection = get_string('editsection', 'format_'.$format->get_format());
|
$streditsection = get_string('editsection', 'format_'.$format->get_format());
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,9 +75,12 @@ final class sectiondelegate_test extends \advanced_testcase {
|
||||||
$allowedoptions = [
|
$allowedoptions = [
|
||||||
get_string('view'),
|
get_string('view'),
|
||||||
$streditsection,
|
$streditsection,
|
||||||
|
get_string('hidefromothers', 'format_' . $course->format),
|
||||||
|
get_string('showfromothers', 'format_' . $course->format),
|
||||||
get_string('delete'),
|
get_string('delete'),
|
||||||
get_string('sectionlink', 'course'),
|
get_string('sectionlink', 'course'),
|
||||||
];
|
];
|
||||||
|
|
||||||
// The default section menu should be different for the delegated section menu.
|
// The default section menu should be different for the delegated section menu.
|
||||||
$result = $delegated->get_section_action_menu($format, $controlmenu, $renderer);
|
$result = $delegated->get_section_action_menu($format, $controlmenu, $renderer);
|
||||||
foreach ($result->get_secondary_actions() as $secondaryaction) {
|
foreach ($result->get_secondary_actions() as $secondaryaction) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue