diff --git a/blocks/site_main_menu/tests/behat/edit_activities.feature b/blocks/site_main_menu/tests/behat/edit_activities.feature
index ea61a2f13dd..ff163f5f090 100644
--- a/blocks/site_main_menu/tests/behat/edit_activities.feature
+++ b/blocks/site_main_menu/tests/behat/edit_activities.feature
@@ -69,7 +69,7 @@ Feature: Edit activities in Additional activities block
And I log in as "admin"
And I am on site homepage
And I turn editing mode on
- And I should see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should see "My forum name" in the "Additional activities" "block"
And I should not see "My forum name" in the "region-main" "region"
When I open "My forum name" actions menu
And I click on "Move" "link" in the "My forum name" activity
@@ -78,7 +78,7 @@ Feature: Edit activities in Additional activities block
And I should see "Site" in the "Move activity" "dialogue"
And I click on "Site" "link" in the "Move activity" "dialogue"
Then I should see "My forum name" in the "region-main" "region"
- And I should not see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should not see "My forum name" in the "Additional activities" "block"
@javascript
Scenario: The move activity modal allow to move from the main content to the Additional activities block
@@ -94,7 +94,7 @@ Feature: Edit activities in Additional activities block
And I log in as "admin"
And I am on site homepage
And I turn editing mode on
- And I should not see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should not see "My forum name" in the "Additional activities" "block"
And I should see "My forum name" in the "region-main" "region"
When I open "My forum name" actions menu
And I click on "Move" "link" in the "My forum name" activity
@@ -103,7 +103,7 @@ Feature: Edit activities in Additional activities block
And I should see "Site" in the "Move activity" "dialogue"
And I click on "Block" "link" in the "Move activity" "dialogue"
Then I should not see "My forum name" in the "region-main" "region"
- And I should see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should see "My forum name" in the "Additional activities" "block"
@javascript
Scenario: Admin can delete an activity in the Additional activities block
@@ -118,11 +118,11 @@ Feature: Edit activities in Additional activities block
And I log in as "admin"
And I am on site homepage
And I turn editing mode on
- And I should see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should see "My forum name" in the "Additional activities" "block"
When I open "My forum name" actions menu
And I choose "Delete" in the open action menu
And I click on "Delete" "button" in the "Delete activity?" "dialogue"
- Then I should not see "My forum name" in the "block_site_main_menu_section" "region"
+ Then I should not see "My forum name" in the "Additional activities" "block"
@javascript
Scenario: Admin can duplicate an activity in the Additional activities block
@@ -137,10 +137,10 @@ Feature: Edit activities in Additional activities block
And I log in as "admin"
And I am on site homepage
And I turn editing mode on
- And I should see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should see "My forum name" in the "Additional activities" "block"
When I open "My forum name" actions menu
And I choose "Duplicate" in the open action menu
- Then I should see "My forum name (copy)" in the "block_site_main_menu_section" "region"
+ Then I should see "My forum name (copy)" in the "Additional activities" "block"
@javascript
Scenario: Admin can move right and left an activity in the Additional activities block
@@ -155,7 +155,7 @@ Feature: Edit activities in Additional activities block
And I log in as "admin"
And I am on site homepage
And I turn editing mode on
- And I should see "My forum name" in the "block_site_main_menu_section" "region"
+ And I should see "My forum name" in the "Additional activities" "block"
When I open "My forum name" actions menu
And "Move right" "link" should be visible
And "Move left" "link" should not be visible
diff --git a/course/format/classes/stateactions.php b/course/format/classes/stateactions.php
index c2e4ac14c71..5f62719b719 100644
--- a/course/format/classes/stateactions.php
+++ b/course/format/classes/stateactions.php
@@ -1106,12 +1106,6 @@ class stateactions {
throw new moodle_exception('emptysectionids', 'core', null, $info);
}
- // No section actions are allowed if course format does not support sections.
- $courseformat = course_get_format($course->id);
- if (!$courseformat->uses_sections()) {
- throw new moodle_exception('sectionactionnotsupported', 'core', null, $info);
- }
-
list($insql, $inparams) = $DB->get_in_or_equal($sectionids, SQL_PARAMS_NAMED);
// Check if all the given sections exist.
diff --git a/course/format/singleactivity/format.php b/course/format/singleactivity/format.php
index 5996ef44b2f..908a12f8310 100644
--- a/course/format/singleactivity/format.php
+++ b/course/format/singleactivity/format.php
@@ -27,5 +27,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+defined('MOODLE_INTERNAL') || die();
+
+include_course_ajax($course);
+
$courserenderer = $PAGE->get_renderer('format_singleactivity');
echo $courserenderer->display($course, $section != 0);
diff --git a/course/format/singleactivity/lang/en/format_singleactivity.php b/course/format/singleactivity/lang/en/format_singleactivity.php
index ad2ae685b7b..5e9813abd73 100644
--- a/course/format/singleactivity/lang/en/format_singleactivity.php
+++ b/course/format/singleactivity/lang/en/format_singleactivity.php
@@ -27,10 +27,7 @@ $string['activitytype_help'] = 'Choose the type of activity or resource to use i
$string['defactivitytype'] = 'Default type of activity';
$string['defactivitytypedesc'] = 'Specify the activity type that will be selected by default when creating a new course';
$string['erroractivitytype'] = 'Type of activity is not set up in Course settings';
-$string['orphaned'] = 'Orphaned';
-$string['orphanedwarning'] = 'These activities are unreachable by users!';
+$string['plugin_description'] = 'The course displays only one activity on the course page. You can add more activities in the Additional activities block';
$string['pluginname'] = 'Single activity';
-$string['plugin_description'] = 'The course contains only one activity or resource.';
$string['sectionname'] = '';
-$string['warningchangeformat'] = 'When changing the existing course format to "Single activity" make sure that you removed all extra activities from the course including "Announcements". Note that sections structure may be modified.';
$string['privacy:metadata'] = 'The Single activity format plugin does not store any personal data.';
diff --git a/course/format/singleactivity/lib.php b/course/format/singleactivity/lib.php
index 55e0a92a6d6..ae5f1c2f08d 100644
--- a/course/format/singleactivity/lib.php
+++ b/course/format/singleactivity/lib.php
@@ -14,14 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see
Course summary
| topics | + | fullname | shortname | format | activitytype | + | Course 1 | C1 | singleactivity | assign | And the following "course enrolments" exist: - | user | course | role | - | teacher1 | C1 | editingteacher | + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | section | + | assign | Assignment 1 | Test assignment description | C1 | assign1 | 0 | + | forum | Forum 1 | Test forum description | C1 | forum1 | 0 | + + @javascript + Scenario: Add subsections and move activities in Single activity course format + Given I log in as "teacher1" + When I am on "Course 1" course homepage with editing mode on + And I click on "Add content" "button" in the ".block_site_main_menu .footer" "css_element" + And I click on "Subsection" "link" in the ".dropdown-menu.show" "css_element" + Then I should see "New subsection" in the "Additional activities" "block" + And I open "Forum 1" actions menu + And I click on "Move" "link" in the "Forum 1" activity + And I click on "New subsection" "link" in the "Move activity" "dialogue" + And I should see "Forum 1" in the "New subsection" "section" + + Scenario: Change activity type in Single activity course format + # Navigate to any course page, like groups, to guarantee that Course settings are displayed in boost and classic themes. + Given I am on the "Course 1" "groups" page logged in as "teacher1" + When I navigate to "Settings" in current page administration + # If there is an existing activity, it will be used as main activity. + And I set the following fields to these values: + | Type of activity | Forum | + And I press "Save and display" + Then I should see "Test forum description" in the "page-content" "region" + # However, if there is no existing activity, the new activity form will be displayed. + But I am on the "Course 1" "groups" page logged in as "teacher1" + And I navigate to "Settings" in current page administration + And I set the following fields to these values: + | Type of activity | Quiz | + And I press "Save and display" + And I should see "New Quiz" + + Scenario: Edit a format course as a teacher + Given the following "courses" exist: + | fullname | shortname | summary | format | + | Course 2 | C2 | Course summary | topics | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C2 | editingteacher | And I log in as "teacher1" - And I am on "Course 1" course homepage + And I am on "Course 2" course homepage When I navigate to "Settings" in current page administration And I set the following fields to these values: | Course full name | My first course | - | Course short name | myfirstcourse | - | Format | Single activity | + | Course short name | myfirstcourse | + | Format | Single activity | And I press "Update format" Then I should see "Forum" in the "Type of activity" "field" # Check that not all the activity types are in the dropdown. diff --git a/course/format/singleactivity/tests/format_singleactivity_test.php b/course/format/singleactivity/tests/format_singleactivity_test.php index 9e7a7424672..b3414e8fcba 100644 --- a/course/format/singleactivity/tests/format_singleactivity_test.php +++ b/course/format/singleactivity/tests/format_singleactivity_test.php @@ -38,7 +38,7 @@ final class format_singleactivity_test extends \advanced_testcase { // Generate a course with two sections (0 and 1) and two modules. $generator = $this->getDataGenerator(); $course1 = $generator->create_course(['format' => 'singleactivity']); - course_create_sections_if_missing($course1, [0, 1]); + course_create_sections_if_missing($course1, [0]); $data = (object)['id' => $course1->id]; $format = course_get_format($course1); diff --git a/course/format/tests/external/get_state_test.php b/course/format/tests/external/get_state_test.php index 6e8eb93e02e..c5a05469363 100644 --- a/course/format/tests/external/get_state_test.php +++ b/course/format/tests/external/get_state_test.php @@ -110,8 +110,8 @@ final class get_state_test extends \externallib_advanced_testcase { // User won't see the hidden sections. Remove them from the total. $visiblesections = $visiblesections - count($hiddensections); } - if ($format == 'social') { - $visiblesections = 1; // But Social format has one section visible. + if ($format == 'social' || $format == 'singleactivity') { + $visiblesections = 1; // But Social and Single activity formats have only one section visible. } // Social course format automatically creates a forum activity. diff --git a/course/format/tests/output/local/state/state_test.php b/course/format/tests/output/local/state/state_test.php index a40532ee794..6fd4404da33 100644 --- a/course/format/tests/output/local/state/state_test.php +++ b/course/format/tests/output/local/state/state_test.php @@ -64,11 +64,11 @@ final class state_test extends \advanced_testcase { $this->setAdminUser(); $courseformat = course_get_format($course->id); $modinfo = $courseformat->get_modinfo(); - $issocialformat = $courseformat->get_format() === 'social'; + $supportsections = $format !== 'social' && $format !== 'singleactivity'; // Only create activities if the course format is not social. // There's no course home page (and sections) for social course format. - if (!$issocialformat || $format == 'theunittest') { + if ($supportsections) { // Add some activities to the course. $this->getDataGenerator()->create_module('page', ['course' => $course->id], ['section' => 1, 'visible' => 1]); @@ -111,7 +111,7 @@ final class state_test extends \advanced_testcase { $sections = $modinfo->get_section_info_all(); foreach ($sections as $key => $section) { - if (!$issocialformat || $format == 'theunittest') { + if ($supportsections) { $this->assertEquals($section->id, $result->course->sectionlist[$key]); if (!empty($section->uservisible)) { $sectionstate = new $sectionclass($courseformat, $section); diff --git a/course/format/tests/stateactions_test.php b/course/format/tests/stateactions_test.php index 0d23d056a59..ceb2a21b086 100644 --- a/course/format/tests/stateactions_test.php +++ b/course/format/tests/stateactions_test.php @@ -110,7 +110,9 @@ final class stateactions_test extends \advanced_testcase { private function translate_references(array $references, array $values): array { $result = []; foreach ($values as $value) { - $result[] = $references[$value]; + if (array_key_exists($value, $references)) { + $result[] = $references[$value]; + } } return $result; } @@ -209,8 +211,13 @@ final class stateactions_test extends \advanced_testcase { ): void { $this->resetAfterTest(); - // Create a course with 3 sections, 1 of them hidden. - $course = $this->create_course($format, 3, [2]); + if ($format === 'singleactivity') { + // Single activity format does not have sections. + $course = $this->create_course($format, 0, []); + } else { + // Create a course with 3 sections, 1 of them hidden. + $course = $this->create_course($format, 3, [2]); + } $references = $this->course_references($course); @@ -226,11 +233,19 @@ final class stateactions_test extends \advanced_testcase { $count++; } - // Add some activities to the course. One visible and one hidden in both sections 1 and 2. - $references["cm0"] = $this->create_activity($course->id, 'assign', 1, true); - $references["cm1"] = $this->create_activity($course->id, 'book', 1, false); - $references["cm2"] = $this->create_activity($course->id, 'glossary', 2, true); - $references["cm3"] = $this->create_activity($course->id, 'page', 2, false); + if ($format === 'singleactivity') { + // Add some activities to the course. One visible and one hidden in section 0. + $references['cm0'] = $this->create_activity($course->id, 'forum', 0, true); + $references['cm1'] = $this->create_activity($course->id, 'book', 0, false); + } else { + // Add some activities to the course. One visible and one hidden in sections 0, 1 and 2. + $references['cm0'] = $this->create_activity($course->id, 'forum', 0, true); + $references['cm1'] = $this->create_activity($course->id, 'book', 0, false); + $references['cm2'] = $this->create_activity($course->id, 'assign', 1, true); + $references['cm3'] = $this->create_activity($course->id, 'book', 1, false); + $references['cm4'] = $this->create_activity($course->id, 'glossary', 2, true); + $references['cm5'] = $this->create_activity($course->id, 'page', 2, false); + } if ($expectedexception) { $this->expectException(moodle_exception::class); @@ -315,17 +330,23 @@ final class stateactions_test extends \advanced_testcase { * @return array the testing scenarios */ public static function course_state_provider(string $format): array { - $expectedexception = ($format === 'singleactivity'); + $expectedexception = false; - $cms = ['cm0', 'cm1', 'cm2', 'cm3']; + $studentcms = ['cm0', 'cm2']; // All sections and cms that the user can access to. - $usersections = ['section0', 'section1', 'section2', 'section3']; - - $studentcms = ['cm0']; - if ($format === 'social') { - $cms = ['initialcm0', 'cm0', 'cm1', 'cm2', 'cm3']; - $studentcms = ['initialcm0', 'cm0']; - $usersections = ['section0']; // Social format only uses section 0 (for all users). + if ($format === 'singleactivity') { + // Single activity format does not have sections. + $cms = ['cm0', 'cm1']; + $usersections = ['section0']; + $studentcms = ['cm0']; + } else if ($format === 'social') { + // Social format only uses section 0 (for all users). + $cms = ['initialcm0', 'cm0', 'cm1', 'cm2', 'cm3', 'cm4', 'cm5']; + $studentcms = ['initialcm0', 'cm0', 'cm2']; + $usersections = ['section0']; + } else { + $cms = ['cm0', 'cm1', 'cm2', 'cm3', 'cm4', 'cm5']; + $usersections = ['section0', 'section1', 'section2', 'section3']; } return [ @@ -383,20 +404,23 @@ final class stateactions_test extends \advanced_testcase { * @return array the testing scenarios */ public static function section_state_provider(string $format, string $role): array { - // Social format will raise an exception and debug messages because it does not - // use sections and it does not provide a renderer. $expectedexception = ($format === 'singleactivity'); // All sections and cms that the user can access to. $usersections = ['section0', 'section1', 'section2', 'section3']; - $usercms = ['cm0', 'cm1', 'cm2', 'cm3']; + $usercms = ['cm0', 'cm1', 'cm2', 'cm3', 'cm4', 'cm5']; if ($role == 'student') { $usersections = ['section0', 'section1', 'section3']; - $usercms = ['cm0']; + $usercms = ['cm0', 'cm2']; } if ($format === 'social') { + // Social format only uses section 0 (for all users). + $usersections = ['section0']; $usercms = ['initialcm0', ...$usercms]; - $usersections = ['section0']; // Social format only uses section 0 (for all users). + } else if ($format === 'singleactivity') { + // Single activity format does not have sections. + $usersections = ['section0']; + $usercms = ($role == 'student') ? ['cm0'] : ['cm0', 'cm1']; } return [ @@ -420,9 +444,9 @@ final class stateactions_test extends \advanced_testcase { 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section0'], $usersections), - 'cm' => ($format == 'social') ? ['initialcm0'] : [], + 'cm' => array_intersect(['initialcm0', 'cm0', 'cm1'], $usercms), ], - 'expectedexception' => $expectedexception, + 'expectedexception' => false, ], "$role $format section_state visible section" => [ 'format' => $format, @@ -434,7 +458,7 @@ final class stateactions_test extends \advanced_testcase { 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section1'], $usersections), - 'cm' => array_intersect(['cm0', 'cm1'], $usercms), + 'cm' => array_intersect(['cm2', 'cm3'], $usercms), ], 'expectedexception' => $expectedexception, ], @@ -448,7 +472,7 @@ final class stateactions_test extends \advanced_testcase { 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section2'], $usersections), - 'cm' => array_intersect(['cm2', 'cm3'], $usercms), + 'cm' => array_intersect(['cm4', 'cm5'], $usercms), ], 'expectedexception' => $expectedexception, ], @@ -462,7 +486,7 @@ final class stateactions_test extends \advanced_testcase { 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section1', 'section3'], $usersections), - 'cm' => array_intersect(['cm0', 'cm1'], $usercms), + 'cm' => array_intersect(['cm2', 'cm3'], $usercms), ], 'expectedexception' => $expectedexception, ], @@ -486,7 +510,7 @@ final class stateactions_test extends \advanced_testcase { 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section1', 'section3'], $usersections), - 'cm' => array_intersect(['cm0', 'cm1'], $usercms), + 'cm' => array_intersect(['cm2', 'cm3'], $usercms), ], 'expectedexception' => $expectedexception, ], @@ -495,12 +519,12 @@ final class stateactions_test extends \advanced_testcase { 'role' => $role, 'method' => 'section_state', 'params' => [ - 'ids' => ['section3'], 'targetsectionid' => null, 'targetcmid' => 'cm1' + 'ids' => ['section3'], 'targetsectionid' => null, 'targetcmid' => 'cm3', ], 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section3'], $usersections), - 'cm' => array_intersect(['cm1'], $usercms), + 'cm' => array_intersect(['cm3'], $usercms), ], 'expectedexception' => $expectedexception, ], @@ -515,16 +539,22 @@ final class stateactions_test extends \advanced_testcase { * @return array the testing scenarios */ public static function cm_state_provider(string $format, string $role): array { + $expectedexception = ($format === 'singleactivity'); + // All sections and cms that the user can access to. $usersections = ['section0', 'section1', 'section2', 'section3']; - $usercms = ['cm0', 'cm1', 'cm2', 'cm3']; + $usercms = ['cm0', 'cm1', 'cm2', 'cm3', 'cm4', 'cm5']; if ($role == 'student') { $usersections = ['section0', 'section1', 'section3']; - $usercms = ['cm0']; + $usercms = ['cm0', 'cm2']; } if ($format === 'social') { $usercms = ['initialcm0', ...$usercms]; $usersections = ['section0']; // Social format only uses section 0 (for all users). + } else if ($format === 'singleactivity') { + // Single activity format does not have sections. + $usersections = ['section0']; + $usercms = ($role == 'student') ? ['cm0'] : ['cm0', 'cm1']; } return [ @@ -547,7 +577,7 @@ final class stateactions_test extends \advanced_testcase { ], 'expectedresults' => [ 'course' => [], - 'section' => array_intersect(['section1'], $usersections), + 'section' => array_intersect(['section0'], $usersections), 'cm' => array_intersect(['cm0'], $usercms), ], 'expectedexception' => false, @@ -561,7 +591,7 @@ final class stateactions_test extends \advanced_testcase { ], 'expectedresults' => [ 'course' => [], - 'section' => array_intersect(['section1'], $usersections), + 'section' => array_intersect(['section0'], $usersections), 'cm' => array_intersect(['cm1'], $usercms), ], 'expectedexception' => false, @@ -571,42 +601,42 @@ final class stateactions_test extends \advanced_testcase { 'role' => $role, 'method' => 'cm_state', 'params' => [ - 'ids' => ['cm0', 'cm2'], 'targetsectionid' => null, 'targetcmid' => null + 'ids' => ['cm2', 'cm4'], 'targetsectionid' => null, 'targetcmid' => null, ], 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section1', 'section2'], $usersections), - 'cm' => array_intersect(['cm0', 'cm2'], $usercms), + 'cm' => array_intersect(['cm2', 'cm4'], $usercms), ], - 'expectedexception' => false, + 'expectedexception' => $expectedexception, ], "$role $format cm_state using targetsection" => [ 'format' => $format, 'role' => $role, 'method' => 'cm_state', 'params' => [ - 'ids' => ['cm0'], 'targetsectionid' => 'section2', 'targetcmid' => null + 'ids' => ['cm2'], 'targetsectionid' => 'section2', 'targetcmid' => null, ], 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section1', 'section2'], $usersections), - 'cm' => array_intersect(['cm0'], $usercms), + 'cm' => array_intersect(['cm2'], $usercms), ], - 'expectedexception' => ($format === 'singleactivity'), + 'expectedexception' => $expectedexception, ], "$role $format cm_state using targetcm" => [ 'format' => $format, 'role' => $role, 'method' => 'cm_state', 'params' => [ - 'ids' => ['cm0'], 'targetsectionid' => null, 'targetcmid' => 'cm3' + 'ids' => ['cm2'], 'targetsectionid' => null, 'targetcmid' => 'cm5', ], 'expectedresults' => [ 'course' => [], 'section' => array_intersect(['section1', 'section2'], $usersections), - 'cm' => array_intersect(['cm0', 'cm3'], $usercms), + 'cm' => array_intersect(['cm2', 'cm5'], $usercms), ], - 'expectedexception' => false, + 'expectedexception' => $expectedexception, ], "$role $format cm_state using an invalid cm" => [ 'format' => $format, diff --git a/mod/subsection/tests/permission_test.php b/mod/subsection/tests/permission_test.php index 9171665ede8..dbb70c6cf1c 100644 --- a/mod/subsection/tests/permission_test.php +++ b/mod/subsection/tests/permission_test.php @@ -103,12 +103,12 @@ final class permission_test extends advanced_testcase { 'format' => 'topics', 'expected' => false, ], - 'Format does not support components' => [ + 'Single activity supports subsections too' => [ 'ismoddisabled' => false, 'missingcapability' => false, 'isdelegated' => false, 'format' => 'singleactivity', - 'expected' => false, + 'expected' => true, ], 'Plugin enabled, with capability, not inside a delegated section' => [ 'ismoddisabled' => false, @@ -119,4 +119,27 @@ final class permission_test extends advanced_testcase { ], ]; } + + /** + * Test that subsection cannot be added when the course format does not support components. + * + * @return void + */ + public function test_can_add_subsection_unsupported_components( + ): void { + global $SITE; + + $this->resetAfterTest(); + + course_create_sections_if_missing($SITE, [0, 1]); + $courseformat = course_get_format($SITE); + $targetsection = $courseformat->get_modinfo()->get_section_info(0); + + $user = $this->getDataGenerator()->create_and_enrol($SITE, 'editingteacher'); + + $this->setUser($user); + $this->assertEquals(false, permission::can_add_subsection($targetsection, (int)$user->id)); + } + + }