diff --git a/.upgradenotes/MDL-82259-2024072314533865.yml b/.upgradenotes/MDL-82259-2024072314533865.yml new file mode 100644 index 00000000000..038dd388d33 --- /dev/null +++ b/.upgradenotes/MDL-82259-2024072314533865.yml @@ -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 diff --git a/course/format/classes/local/cmactions.php b/course/format/classes/local/cmactions.php index 3e9f1e54c69..fe300b1008c 100644 --- a/course/format/classes/local/cmactions.php +++ b/course/format/classes/local/cmactions.php @@ -17,6 +17,7 @@ namespace core_courseformat\local; +use core_courseformat\sectiondelegatemodule; use course_modinfo; /** * 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 */ 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. + * * @param int $cmid the course module id. * @param string $name the new name. * @return bool true if the course module was renamed, false otherwise. @@ -64,12 +101,16 @@ class cmactions extends baseactions { ] ); $cm->name = $name; + $fields = new \stdClass(); + $fields->name = $name; \core\event\course_module_updated::create_from_cm($cm)->trigger(); course_modinfo::purge_course_module_cache($cm->course, $cm->id); rebuild_course_cache($cm->course, false, true); + $this->update_delegated($cm, ['name' => $name]); + // Modules may add some logic to renaming. $modinfo = get_fast_modinfo($cm->course); \core\di::get(\core\hook\manager::class)->dispatch( @@ -87,4 +128,100 @@ class cmactions extends baseactions { 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; + } } diff --git a/course/format/classes/local/sectionactions.php b/course/format/classes/local/sectionactions.php index f0e5657a9e1..9730fcae82e 100644 --- a/course/format/classes/local/sectionactions.php +++ b/course/format/classes/local/sectionactions.php @@ -404,6 +404,18 @@ class sectionactions extends baseactions { $modules = explode(',', $sectioninfo->sequence); $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) { $cm = get_coursemodule_from_id(null, $moduleid, $this->course->id); if (!$cm) { diff --git a/course/format/classes/output/local/content/cm/delegatedcontrolmenu.php b/course/format/classes/output/local/content/cm/delegatedcontrolmenu.php index 4a0a0e32ec5..3634eedd332 100644 --- a/course/format/classes/output/local/content/cm/delegatedcontrolmenu.php +++ b/course/format/classes/output/local/content/cm/delegatedcontrolmenu.php @@ -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. // Only show the view link if we are not already in the section view page. if (!$isheadersection && $hasmanageactivities) { diff --git a/course/format/classes/sectiondelegatemodule.php b/course/format/classes/sectiondelegatemodule.php index c91ba739812..675de5bf233 100644 --- a/course/format/classes/sectiondelegatemodule.php +++ b/course/format/classes/sectiondelegatemodule.php @@ -149,7 +149,7 @@ abstract class sectiondelegatemodule extends sectiondelegate { controlmenu $controlmenu, renderer_base $output, ): ?action_menu { - $controlmenuclass = $format->get_output_classname('content\\cm\\controlmenu'); + $controlmenuclass = $format->get_output_classname('content\\cm\\delegatedcontrolmenu'); $controlmenu = new $controlmenuclass( $format, $this->sectioninfo, diff --git a/course/format/classes/stateactions.php b/course/format/classes/stateactions.php index 8ee286a717c..ba93a2e778a 100644 --- a/course/format/classes/stateactions.php +++ b/course/format/classes/stateactions.php @@ -542,8 +542,18 @@ class stateactions { course_modinfo::purge_course_modules_cache($course->id, $ids); rebuild_course_cache($course->id, false, true); + $delegatedsections = []; foreach ($cms as $cm) { $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); } } diff --git a/course/format/templates/local/content/cm/controlmenu.mustache b/course/format/templates/local/content/cm/controlmenu.mustache index 9f3bcd82b13..7a9b5e6f3fe 100644 --- a/course/format/templates/local/content/cm/controlmenu.mustache +++ b/course/format/templates/local/content/cm/controlmenu.mustache @@ -21,7 +21,7 @@ Example context (json): { - "menu": "Edit", + "menu": "Edit", "hasmenu": true } }} diff --git a/course/lib.php b/course/lib.php index 7115a7c78ce..8c9fd69c266 100644 --- a/course/lib.php +++ b/course/lib.php @@ -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 * (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 $visibleoncoursepage state of the module on the course page * @param bool $rebuildcache If true (default), perform a partial cache purge and rebuild. * @return bool false when the module was not found, true otherwise */ -function set_coursemodule_visible($id, $visible, $visibleoncoursepage = 1, bool $rebuildcache = true) { - global $DB, $CFG; - require_once($CFG->libdir.'/gradelib.php'); - 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; +function set_coursemodule_visible($cmid, $visible, $visibleoncoursepage = 1, bool $rebuildcache = true) { + $coursecontext = context_module::instance($cmid)->get_course_context(); + return formatactions::cm($coursecontext->instanceid)->set_visibility($cmid, $visible, $visibleoncoursepage, $rebuildcache); } /** diff --git a/course/tests/behat/behat_course.php b/course/tests/behat/behat_course.php index 28a0ca7eb1e..9b0a0ecd95e 100644 --- a/course/tests/behat/behat_course.php +++ b/course/tests/behat/behat_course.php @@ -305,26 +305,26 @@ class behat_course extends behat_base { /** * Opens a section edit menu if it is not already opened. * - * @Given /^I open section "(?P\d+)" edit menu$/ + * @Given /^I open section "(?P
(?:[^"]|\\")*)" edit menu$/ * @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()) { throw new DriverException('Section edit menu not available when Javascript is disabled'); } // 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. - $xpath = $this->section_exists($sectionnumber); - $xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@data-toggle, 'dropdown')]"; + $xpath = $this->section_exists($section); + $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->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. * - * @Given /^I show section "(?P\d+)"$/ - * @param int $sectionnumber + * @Given /^I show section "(?P
(?:[^"]|\\")*)"$/ + * @param int|string $section */ - public function i_show_section($sectionnumber) { - $showlink = $this->show_section_link_exists($sectionnumber); + public function i_show_section($section) { + // 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()) { - $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()) { $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. * - * @Given /^I hide section "(?P\d+)"$/ - * @param int $sectionnumber + * @Given /^I hide section "(?P
(?:[^"]|\\")*)"$/ + * @param int|string $section */ - public function i_hide_section($sectionnumber) { + public function i_hide_section($section) { // 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. $courseformat = $this->get_course_format(); 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 ($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', array($strhide, "link", $this->escape($xpath), "xpath_element") ); if ($this->running_javascript()) { $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 * and we will manage it from other step definitions. * - * @Given /^I wait until section "(?P\d+)" is available$/ - * @param int $sectionnumber + * @Given /^I wait until section "(?P
(?:[^"]|\\")*)" is available$/ + * @param int|string $section * @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. - $sectionxpath = $this->section_exists($sectionnumber); + $sectionxpath = $this->section_exists($section); $hiddenlightboxxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]" . " | " . $sectionxpath . "[count(child::div[contains(@class, 'lightbox')]) = 0]"; @@ -1282,10 +1293,26 @@ class behat_course extends behat_base { * Checks if the course section exists. * * @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 * @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. $xpath = "//li[@id='section-" . $sectionnumber . "']"; @@ -1295,17 +1322,39 @@ class behat_course extends behat_base { 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. * * @throws ElementNotFoundException Thrown by behat_base::find - * @param int $sectionnumber + * @param int|string $section Section number or name to look for. * @return NodeElement */ - protected function show_section_link_exists($sectionnumber) { + protected function show_section_link_exists($section) { // 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. $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. * * @throws ElementNotFoundException Thrown by behat_base::find - * @param int $sectionnumber + * @param int|string $section Section number or name to look for. * @return NodeElement */ - protected function hide_section_link_exists($sectionnumber) { + protected function hide_section_link_exists($section) { // 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. $courseformat = $this->get_course_format(); @@ -1370,6 +1419,10 @@ class behat_course extends behat_base { 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); } diff --git a/mod/subsection/tests/behat/subsection_actionmenu.feature b/mod/subsection/tests/behat/subsection_actionmenu.feature index 7c180323305..d0d5ff3cfe4 100644 --- a/mod/subsection/tests/behat/subsection_actionmenu.feature +++ b/mod/subsection/tests/behat/subsection_actionmenu.feature @@ -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 "Highlight" 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 "Duplicate" - And I should not see "Hide" + And I should see "Hide" # Delete option for subsection page is not implemented yet. And I should not see "Delete" 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 see "View" 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 "Duplicate" - And I should not see "Hide" + And I should see "Hide" And I should see "Delete" - And I should see "Permalink" @javascript 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 see "View" 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 "Duplicate" - And I should not see "Hide" + And I should see "Hide" And I should see "Delete" 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. 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" + + @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" diff --git a/mod/subsection/tests/courseformat/sectiondelegate_test.php b/mod/subsection/tests/courseformat/sectiondelegate_test.php index 5d3a5bf0ced..450b2bc8a66 100644 --- a/mod/subsection/tests/courseformat/sectiondelegate_test.php +++ b/mod/subsection/tests/courseformat/sectiondelegate_test.php @@ -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. // 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())) { $streditsection = get_string('editsection', 'format_'.$format->get_format()); } else { @@ -75,9 +75,12 @@ final class sectiondelegate_test extends \advanced_testcase { $allowedoptions = [ get_string('view'), $streditsection, + get_string('hidefromothers', 'format_' . $course->format), + get_string('showfromothers', 'format_' . $course->format), get_string('delete'), get_string('sectionlink', 'course'), ]; + // The default section menu should be different for the delegated section menu. $result = $delegated->get_section_action_menu($format, $controlmenu, $renderer); foreach ($result->get_secondary_actions() as $secondaryaction) {