diff --git a/course/format/classes/output/local/content/cm/controlmenu.php b/course/format/classes/output/local/content/cm/controlmenu.php index 01ca4404b00..ce8f73b4081 100644 --- a/course/format/classes/output/local/content/cm/controlmenu.php +++ b/course/format/classes/output/local/content/cm/controlmenu.php @@ -134,7 +134,7 @@ class controlmenu implements named_templatable, renderable { // Prioritise the menu ahead of all other actions. $menu->prioritise = true; - $ownerselector = $displayoptions['ownerselector'] ?? '#module-' . $mod->id; + $ownerselector = $this->displayoptions['ownerselector'] ?? '#module-' . $mod->id; $menu->set_owner_selector($ownerselector); foreach ($controls as $control) { diff --git a/course/format/classes/output/local/content/cm/visibility.php b/course/format/classes/output/local/content/cm/visibility.php index d5bd5ccac15..8a6cbc330f6 100644 --- a/course/format/classes/output/local/content/cm/visibility.php +++ b/course/format/classes/output/local/content/cm/visibility.php @@ -24,6 +24,8 @@ namespace core_courseformat\output\local\content\cm; +use action_menu_link_secondary; +use core\output\local\action_menu\subpanel as action_menu_subpanel; use cm_info; use core_courseformat\base as course_format; use core_courseformat\output\local\courseformat_named_templatable; @@ -162,6 +164,43 @@ class visibility implements named_templatable, renderable { return $choice; } + /** + * Return the cm availability menu item. + * + * By default, the cm availability is displayed as a menu item subpanel. + * However, it can be simplified when there is only one option and + * it is not stealth (stealth require a subpanel to inform the user). + * + * @return action_menu_link_secondary|action_menu_subpanel|null + */ + public function get_menu_item(): action_menu_link_secondary|action_menu_subpanel|null { + $choice = $this->get_choice_list(); + $selectableoptions = $choice->get_selectable_options(); + + if (count($selectableoptions) === 0) { + return null; + } + + // Visible activities in hidden sections are always considered stealth. + if ($this->section->visible && count($selectableoptions) === 1) { + $option = reset($selectableoptions); + $actionlabel = $option->value === 'show' ? 'modshow' : 'modhide'; + return new action_menu_link_secondary( + $option->url, + $option->icon, + get_string($actionlabel, 'moodle'), + $choice->get_option_extras($option->value) + ); + } + + return new action_menu_subpanel( + get_string('availability', 'moodle'), + $choice, + ['class' => 'editing_availability'], + new pix_icon('t/hide', '', 'moodle', ['class' => 'iconsmall']) + ); + } + /** * Get the selected choice value depending on the course, section and stealth settings. * @return string diff --git a/course/format/tests/behat/activity_visibility.feature b/course/format/tests/behat/activity_visibility.feature index a08a616d3bb..824e3501946 100644 --- a/course/format/tests/behat/activity_visibility.feature +++ b/course/format/tests/behat/activity_visibility.feature @@ -27,14 +27,14 @@ Feature: Verify activity visibility interface. Scenario: Teacher can hide an activity using the actions menu. Given I should not see "Hidden from students" in the "Activity sample 1" "activity" When I open "Activity sample 1" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu Then I should see "Hidden from students" in the "Activity sample 1" "core_courseformat > Activity visibility" @javascript Scenario: Teacher can show an activity using the actions menu. Given I should see "Hidden from students" in the "Activity sample 2" "core_courseformat > Activity visibility" When I open "Activity sample 2" actions menu - And I choose "Availability > Show on course page" in the open action menu + And I choose "Show" in the open action menu Then I should not see "Hidden from students" in the "Activity sample 2" "activity" @javascript diff --git a/course/format/tests/behat/course_courseindex.feature b/course/format/tests/behat/course_courseindex.feature index 63cbfffbf05..2106f571fc4 100644 --- a/course/format/tests/behat/course_courseindex.feature +++ b/course/format/tests/behat/course_courseindex.feature @@ -77,7 +77,7 @@ Feature: Course index depending on role And I am on "Course 1" course homepage with editing mode on And I hide section "2" And I open "Activity sample 3" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And I log out And I log in as "teacher1" When I am on "Course 1" course homepage @@ -94,7 +94,7 @@ Feature: Course index depending on role And I am on "Course 1" course homepage with editing mode on And I hide section "2" And I open "Activity sample 3" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And I log out And I log in as "student1" When I am on "Course 1" course homepage diff --git a/course/format/tests/behat/section_visibility.feature b/course/format/tests/behat/section_visibility.feature index fc36f05738a..396696eedbd 100644 --- a/course/format/tests/behat/section_visibility.feature +++ b/course/format/tests/behat/section_visibility.feature @@ -43,7 +43,7 @@ Feature: Varify section visibility interface @javascript Scenario: Hiden activities in hidden sections stay hidden when the section is shown. Given I open "Activity sample 1" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And I should see "Hidden from students" in the "Activity sample 1" "activity" And I hide section "1" And I should see "Hidden from students" in the "Activity sample 1" "activity" diff --git a/course/lib.php b/course/lib.php index 40010f4f5f9..14ad8033ead 100644 --- a/course/lib.php +++ b/course/lib.php @@ -1443,7 +1443,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) { $str = get_strings( [ 'delete', 'move', 'moveright', 'moveleft', 'editsettings', - 'duplicate', 'availability' + 'duplicate', 'availability', ], 'moodle' ); @@ -1543,14 +1543,9 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) { $availabilityclass = $courseformat->get_output_classname('content\\cm\\visibility'); /** @var core_courseformat\output\local\content\cm\visibility */ $availability = new $availabilityclass($courseformat, $sectioninfo, $mod); - $availabilitychoice = $availability->get_choice_list(); - if ($availabilitychoice->count_options() > 1) { - $actions['availability'] = new action_menu_subpanel( - $str->availability, - $availabilitychoice, - ['class' => 'editing_availability'], - new pix_icon('t/hide', '', 'moodle', array('class' => 'iconsmall')) - ); + $availabilityitem = $availability->get_menu_item(); + if ($availabilityitem) { + $actions['availability'] = $availabilityitem; } } diff --git a/course/tests/behat/activities_visibility_icons.feature b/course/tests/behat/activities_visibility_icons.feature index a35da5cbade..382110cc13e 100644 --- a/course/tests/behat/activities_visibility_icons.feature +++ b/course/tests/behat/activities_visibility_icons.feature @@ -34,13 +34,13 @@ Feature: Toggle activities visibility from the course page And I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on When I open "Test forum name" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu Then "Test forum name" activity should be hidden And I open "Test forum name" actions menu - And I choose "Availability > Show on course page" in the open action menu + And I choose "Show" in the open action menu And "Test forum name" activity should be visible And I open "Test forum name" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And "Test forum name" activity should be hidden And I reload the page And "Test forum name" activity should be hidden diff --git a/course/tests/behat/course_controls.feature b/course/tests/behat/course_controls.feature index 3e3c31cd4bd..efad6380a6e 100644 --- a/course/tests/behat/course_controls.feature +++ b/course/tests/behat/course_controls.feature @@ -55,7 +55,7 @@ Feature: Course activity controls works as expected And I click on "Cancel" "button" And "section" should not exist And I open "Test forum name 1" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And "section" should not exist And I delete "Test forum name 1" activity And I should not see "Test forum name 1" in the "region-main" "region" @@ -125,7 +125,7 @@ Feature: Course activity controls works as expected And I should see "Display description on course page" And I press "Save and return to course" And "section" should not exist - And I click on "Hide on course page" "link" in the "Test forum name 1" activity + And I click on "Hide" "link" in the "Test forum name 1" activity And "section" should not exist And I delete "Test forum name 1" activity And "section" should not exist diff --git a/grade/report/grader/tests/behat/switch_views.feature b/grade/report/grader/tests/behat/switch_views.feature index 340d85dc8b4..8560e94ac0f 100644 --- a/grade/report/grader/tests/behat/switch_views.feature +++ b/grade/report/grader/tests/behat/switch_views.feature @@ -40,7 +40,7 @@ Feature: We can change what we are viewing on the grader report Scenario: View and minimise the grader report containing hidden activities When I am on "Course 1" course homepage with editing mode on And I open "Test assignment name 2" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And I am on "Course 1" course homepage with editing mode off And I navigate to "View > Grader report" in the course gradebook And I should see "Test assignment name 1" in the "user-grades" "table" @@ -73,7 +73,7 @@ Feature: We can change what we are viewing on the grader report Scenario: Minimise the grader report containing hidden activities without the 'moodle/grade:viewhidden' capability Given I am on "Course 1" course homepage with editing mode on And I open "Test assignment name 2" actions menu - And I choose "Availability > Hide on course page" in the open action menu + And I choose "Hide" in the open action menu And the following "role capability" exists: | role | editingteacher | | moodle/grade:viewhidden | prevent | diff --git a/lib/classes/output/choicelist.php b/lib/classes/output/choicelist.php index 02905a255f4..e01d949badb 100644 --- a/lib/classes/output/choicelist.php +++ b/lib/classes/output/choicelist.php @@ -94,6 +94,23 @@ class choicelist implements renderable, named_templatable { return count($this->options); } + /** + * Get the selectable options. + * + * This method returns an array of options that are selectable, excluding the selected option and any disabled options. + * + * @return \stdClass[] + */ + public function get_selectable_options(): array { + $selectableOptions = []; + foreach ($this->options as $option) { + if ($option['value'] !== $this->selected && !$option['disabled']) { + $selectableOptions[] = (object) $option; + } + } + return $selectableOptions; + } + /** * Set the selected option. * @@ -206,6 +223,23 @@ class choicelist implements renderable, named_templatable { } } + /** + * Retrieves the HTML attributes for a given value from the options array. + + * @param string $value The value for which to retrieve the extras. + * @return array an array of HTML attributes of the option (attribute => value). + */ + public function get_option_extras(string $value): array { + if (!isset($this->options[$value]) || !isset($this->options[$value]['extras'])) { + return []; + } + $result = []; + foreach ($this->options[$value]['extras'] as $extra) { + $result[$extra['attribute']] = $extra['value']; + } + return $result; + } + /** * Get the selected option HTML. * diff --git a/lib/tests/output/choicelist_test.php b/lib/tests/output/choicelist_test.php index 3742aa99271..3672aa80267 100644 --- a/lib/tests/output/choicelist_test.php +++ b/lib/tests/output/choicelist_test.php @@ -232,4 +232,45 @@ class choicelist_test extends advanced_testcase { $this->assertTrue($choice->get_allow_empty()); $this->assertEquals('option2', $choice->get_selected_value()); } + + /** + * Test for a choice with option selected. + * + * @covers ::get_selectable_options + */ + public function test_get_selectable_options(): void { + $choice = new choicelist('Choose an option'); + $choice->add_option('option1', 'Option 1'); + $choice->add_option('option2', 'Option 2'); + $choice->add_option('option3', 'Option 3', ['disabled' => true]); + $choice->add_option('option4', 'Option 4', ['disabled' => true]); + $choice->add_option('option5', 'Option 5'); + + $selectable = $choice->get_selectable_options(); + $this->assertCount(3, $selectable); + $this->assertEquals('option1', $selectable[0]->value); + $this->assertEquals('option2', $selectable[1]->value); + $this->assertEquals('option5', $selectable[2]->value); + + $choice->set_selected_value('option2'); + $selectable = $choice->get_selectable_options(); + $this->assertCount(2, $selectable); + $this->assertEquals('option1', $selectable[0]->value); + $this->assertEquals('option5', $selectable[1]->value); + } + + /** + * Test for a choice with option selected. + * + * @covers ::get_option_extras + */ + public function test_get_option_extras(): void { + $choice = new choicelist('Choose an option'); + $choice->add_option('option1', 'Option 1'); + $choice->add_option('option2', 'Option 2', ['extras' => ['data-attribute' => 'value2']]); + + $this->assertEquals([], $choice->get_option_extras('option1')); + $this->assertEquals(['data-attribute' => 'value2'], $choice->get_option_extras('option2')); + $this->assertEquals([], $choice->get_option_extras('inexistent')); + } } diff --git a/mod/h5pactivity/tests/behat/locking.feature b/mod/h5pactivity/tests/behat/locking.feature index 04b0be25bfc..d00f085114b 100644 --- a/mod/h5pactivity/tests/behat/locking.feature +++ b/mod/h5pactivity/tests/behat/locking.feature @@ -86,7 +86,7 @@ Feature: Add H5P activity context locking Scenario: Access participant report in a hidden activity Given I log in as "admin" And I am on "Course 1" course homepage with editing mode on - And I click on "Hide on course page" "link" in the "H5P package" activity + And I click on "Hide" "link" in the "H5P package" activity When I am on the "H5P package" "h5pactivity activity" page And I navigate to "Attempts report" in current page administration Then I should see "Student 1"