diff --git a/course/externallib.php b/course/externallib.php index f8f565620d3..20a948e35f8 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -278,6 +278,7 @@ class core_course_external extends external_api { $module['downloadcontent'] = $cm->downloadcontent; $module['noviewlink'] = plugin_supports('mod', $cm->modname, FEATURE_NO_VIEW_LINK, false); $module['dates'] = $activitydates; + $module['groupmode'] = $cm->groupmode; // Check module completion. $completion = $completioninfo->is_enabled($cm); @@ -488,6 +489,7 @@ class core_course_external extends external_api { VALUE_DEFAULT, [] ), + 'groupmode' => new external_value(PARAM_INT, 'Group mode value', VALUE_OPTIONAL), 'contents' => new external_multiple_structure( new external_single_structure( array( diff --git a/course/format/classes/output/local/content/cm.php b/course/format/classes/output/local/content/cm.php index 22b66173ce8..2dff8d8a455 100644 --- a/course/format/classes/output/local/content/cm.php +++ b/course/format/classes/output/local/content/cm.php @@ -119,6 +119,7 @@ class cm implements named_templatable, renderable { $haspartials['alternative'] = $this->add_alternative_content_data($data, $output); $haspartials['completion'] = $this->add_completion_data($data, $output); $haspartials['editor'] = $this->add_editor_data($data, $output); + $haspartials['groupmode'] = $this->add_groupmode_data($data, $output); $this->add_format_data($data, $haspartials, $output); // Calculated fields. @@ -267,6 +268,7 @@ class cm implements named_templatable, renderable { $this->mod->has_custom_cmlist_item() && !$haspartials['availability'] && !$haspartials['completion'] && + !$haspartials['groupmode'] && !isset($data->modhiddenfromstudents) && !isset($data->modstealth) && !$this->format->show_editor() @@ -304,6 +306,42 @@ class cm implements named_templatable, renderable { return true; } + /** + * Add group mode information to the data structure. + * + * @param stdClass $data the current cm data reference + * @param renderer_base $output typically, the renderer that's calling this function + * @return bool the module has group mode information + */ + protected function add_groupmode_data(stdClass &$data, renderer_base $output): bool { + if (!plugin_supports('mod', $this->mod->modname, FEATURE_GROUPS, false)) { + return false; + } + + if (!has_capability('moodle/course:manageactivities', $this->mod->context)) { + return false; + } + + switch ($this->mod->effectivegroupmode) { + case SEPARATEGROUPS: + $groupicon = 'i/groups'; + $groupalt = get_string('groupsseparate', 'group'); + break; + case VISIBLEGROUPS: + $groupicon = 'i/groupv'; + $groupalt = get_string('groupsvisible', 'group'); + break; + default: + return false; + } + + $data->groupmodeinfo = (object) [ + 'groupicon' => $groupicon, + 'groupalt' => $groupalt, + ]; + return true; + } + /** * Returns the CSS classes for the activity name/content * diff --git a/course/format/templates/local/content/cm/activity.mustache b/course/format/templates/local/content/cm/activity.mustache index 887650f9efe..532d9e0ef28 100644 --- a/course/format/templates/local/content/cm/activity.mustache +++ b/course/format/templates/local/content/cm/activity.mustache @@ -102,6 +102,15 @@ {{> core_courseformat/local/content/cm/badges }} {{/ core_courseformat/local/content/cm/badges }} + {{! Group mode }} + {{#groupmodeinfo}} +
+ {{$ core_courseformat/local/content/cm/groupmode_info}} + {{> core_courseformat/local/content/cm/groupmode_info}} + {{/ core_courseformat/local/content/cm/groupmode_info}} +
+ {{/groupmodeinfo}} + {{! Completion }} {{#activityinfo}} {{#hascompletion}} diff --git a/course/format/templates/local/content/cm/groupmode_info.mustache b/course/format/templates/local/content/cm/groupmode_info.mustache new file mode 100644 index 00000000000..342741f30c8 --- /dev/null +++ b/course/format/templates/local/content/cm/groupmode_info.mustache @@ -0,0 +1,39 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core_courseformat/local/content/cm/groupmode_info + + Container to display group mode information on the course page. + + Example context (json): + { + "activityname": "Course announcements", + "groupicon": "i/groupv", + "groupalt": "Visible groups" + } +}} +
+ {{#groupicon}} + {{#pix}}{{groupicon}}, core, {{groupalt}}{{/pix}} + {{/groupicon}} + {{#groupalt}} +
{{groupalt}}
+ {{/groupalt}} +
diff --git a/course/tests/behat/activity_groupmode.feature b/course/tests/behat/activity_groupmode.feature new file mode 100644 index 00000000000..cf7a5252ba5 --- /dev/null +++ b/course/tests/behat/activity_groupmode.feature @@ -0,0 +1,162 @@ +@core @core_course @javascript +Feature: Activities group mode icons behavior in course page + + Scenario Outline: Teachers should see group mode icons in both view and edit mode + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | groupmode | + | forum | No groups forum | Forum with groupmode = 0 | C1 | forum1 | 0 | + | data | Visible groups database | Database with groupmode = 2 | C1 | data1 | 2 | + | assign | Separate groups assignment | Assignment with groupmode = 1 | C1 | assign1 | 1 | + And I log in as "teacher1" + When I am on "Course 1" course homepage with editing mode + Then "Separate groups" "icon" should not exist in the "No groups forum" "activity" + And "Visible groups" "icon" should not exist in the "No groups forum" "activity" + And "Separate groups" "icon" should not exist in the "Visible groups database" "activity" + And "Visible groups" "icon" should exist in the "Visible groups database" "activity" + And "Separate groups" "icon" should exist in the "Separate groups assignment" "activity" + And "Visible groups" "icon" should not exist in the "Separate groups assignment" "activity" + + Examples: + | editmode | courseformat | + | off | topics | + | on | topics | + | off | weeks | + | on | weeks | + + Scenario Outline: Students should not see group mode icons in both view and edit mode + Given the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | groupmode | + | forum | No groups forum | Forum with groupmode = 0 | C1 | forum1 | 0 | + | data | Visible groups database | Database with groupmode = 2 | C1 | data1 | 2 | + | assign | Separate groups assignment | Assignment with groupmode = 1 | C1 | assign1 | 1 | + When I am on the "C1" "Course" page logged in as "student1" + Then "Separate groups" "icon" should not exist in the "No groups forum" "activity" + And "Visible groups" "icon" should not exist in the "No groups forum" "activity" + And "Separate groups" "icon" should not exist in the "Visible groups database" "activity" + And "Visible groups" "icon" should not exist in the "Visible groups database" "activity" + And "Separate groups" "icon" should not exist in the "Separate groups assignment" "activity" + And "Visible groups" "icon" should not exist in the "Separate groups assignment" "activity" + # Giving moodle/course:manageactivities capability would let them see the icons. + And the following "role capability" exists: + | role | student | + | moodle/course:manageactivities | allow | + And I am on the "C1" "Course" page + And "Visible groups" "icon" should exist in the "Visible groups database" "activity" + And "Separate groups" "icon" should exist in the "Separate groups assignment" "activity" + + Examples: + | courseformat | + | topics | + | weeks | + + Scenario Outline: Resources don't support group mode never show groupmode icon in the course page + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | groupmode | + | page | No groups page | Page with groupmode = 0 | C1 | page0 | 0 | + | page | Visible groups page | Page with groupmode = 2 | C1 | page2 | 2 | + | page | Separate groups page | Page with groupmode = 1 | C1 | page1 | 1 | + And I log in as "teacher1" + When I am on "Course 1" course homepage with editing mode on + Then "Separate groups" "icon" should not exist in the "No groups page" "activity" + And "Visible groups" "icon" should not exist in the "No groups page" "activity" + And "Separate groups" "icon" should not exist in the "Visible groups page" "activity" + And "Visible groups" "icon" should not exist in the "Visible groups page" "activity" + And "Separate groups" "icon" should not exist in the "Separate groups page" "activity" + And "Visible groups" "icon" should not exist in the "Separate groups page" "activity" + + Examples: + | courseformat | + | topics | + | weeks | + + Scenario Outline: Group mode icon behavior in the course page when forcing group mode in course settings + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | groupmodeforce | groupmode | + | Course 1 | C1 | | 1 | | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | groupmode | + | forum | No groups forum | Forum with groupmode = 0 | C1 | forum1 | 0 | + | data | Visible groups database | Database with groupmode = 2 | C1 | data1 | 2 | + | assign | Separate groups assignment | Assignment with groupmode = 1 | C1 | assign1 | 1 | + | page | No groups page | Doesn't support groupmode | C1 | page | 1 | + And I log in as "teacher1" + When I am on "Course 1" course homepage with editing mode on + Then "Separate groups" "icon" in the "No groups forum" "activity" + And "Visible groups" "icon" in the "No groups forum" "activity" + And "Separate groups" "icon" in the "Visible groups database" "activity" + And "Visible groups" "icon" in the "Visible groups database" "activity" + And "Separate groups" "icon" in the "Separate groups assignment" "activity" + And "Visible groups" "icon" in the "Separate groups assignment" "activity" + And "Separate groups" "icon" should not exist in the "No groups page" "activity" + And "Visible groups" "icon" should not exist in the "No groups page" "activity" + + Examples: + | courseformat | groupmode | separate | visible | + | topics | 0 | should not exist | should not exist | + | topics | 1 | should exist | should not exist | + | topics | 2 | should not exist | should exist | + | weeks | 0 | should not exist | should not exist | + | weeks | 1 | should exist | should not exist | + | weeks | 2 | should not exist | should exist | + + Scenario Outline: Group mode icon in the course page in small devices + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | groupmode | + | forum | Forum | Forum with groupmode = 0 | C1 | forum1 | 0 | + | data | Database | Database with groupmode = 2 | C1 | data1 | 2 | + | assign | Assignment | Assignment with groupmode = 1 | C1 | assign1 | 1 | + | page | Page | Doesn't support groupmode | C1 | page | 1 | + And I log in as "teacher1" + When I am on "Course 1" course homepage with editing mode on + And I should not see "Separate groups" + And I should not see "Visible groups" + And I change viewport size to "480x800" + And I should see "Separate groups" + And I should see "Visible groups" + + Examples: + | courseformat | + | topics | + | weeks | diff --git a/lib/classes/output/icon_system_fontawesome.php b/lib/classes/output/icon_system_fontawesome.php index e1bfc220ca7..8b491a3da0f 100644 --- a/lib/classes/output/icon_system_fontawesome.php +++ b/lib/classes/output/icon_system_fontawesome.php @@ -255,8 +255,6 @@ class icon_system_fontawesome extends icon_system_font { 'core:i/groupevent' => 'fa-group', 'core:i/groupn' => 'fa-user', 'core:i/group' => 'fa-users', - 'core:i/groups' => 'fa-user-circle', - 'core:i/groupv' => 'fa-user-circle-o', 'core:i/home' => 'fa-home', 'core:i/hide' => 'fa-eye', 'core:i/hierarchylock' => 'fa-lock', diff --git a/pix/i/groups.png b/pix/i/groups.png index 3b49636e4af..42405ef18d7 100644 Binary files a/pix/i/groups.png and b/pix/i/groups.png differ diff --git a/pix/i/groups.svg b/pix/i/groups.svg index f990dfcc8ca..0a855f21d0a 100644 --- a/pix/i/groups.svg +++ b/pix/i/groups.svg @@ -1,3 +1,11 @@ - -]> \ No newline at end of file + + + + + + + + + + + diff --git a/pix/i/groupv.png b/pix/i/groupv.png index 6ebf6c14b3e..294b092f8b7 100644 Binary files a/pix/i/groupv.png and b/pix/i/groupv.png differ diff --git a/pix/i/groupv.svg b/pix/i/groupv.svg index 25005123187..082b963fa2c 100644 --- a/pix/i/groupv.svg +++ b/pix/i/groupv.svg @@ -1,3 +1,3 @@ - -]> \ No newline at end of file + + + diff --git a/theme/boost/scss/moodle/course.scss b/theme/boost/scss/moodle/course.scss index 44c4a467d1d..3fbe65b7023 100644 --- a/theme/boost/scss/moodle/course.scss +++ b/theme/boost/scss/moodle/course.scss @@ -1448,21 +1448,23 @@ $activity-add-hover: theme-color-level('primary', -10) !default; .activity-grid { display: grid; align-items: center; - grid-template-columns: min-content 1fr min-content min-content; + grid-template-columns: min-content 1fr min-content min-content min-content; grid-template-rows: 1fr repeat(5, min-content); grid-template-areas: - "icon name completion actions" - "icon visibility completion actions" - "icon dates completion actions" - "altcontent altcontent altcontent altcontent" - "afterlink afterlink afterlink afterlink" - "availability availability availability availability"; + "icon name groupmode completion actions" + "icon visibility groupmode completion actions" + "icon dates groupmode completion actions" + "altcontent altcontent altcontent altcontent altcontent" + "afterlink afterlink afterlink afterlink afterlink" + "availability availability availability availability availability"; @include media-breakpoint-down(xs) { - grid-template-columns: min-content 1fr min-content; + grid-template-columns: min-content 1fr min-content min-content min-content; + grid-template-rows: 1fr repeat(4, min-content); grid-template-areas: "icon name actions" "icon visibility actions" "dates dates dates" + "groupmode groupmode groupmode" "completion completion completion" "altcontent altcontent altcontent" "afterlink afterlink afterlink" @@ -1471,17 +1473,18 @@ $activity-add-hover: theme-color-level('primary', -10) !default; } // Activity card specific grid layout for activities without name. .activity-grid.noname-grid { - grid-template-columns: min-content 1fr min-content; + grid-template-columns: min-content min-content 1fr min-content; grid-template-areas: - "visibility completion actions" - "altcontent altcontent altcontent" - "afterlink afterlink afterlink" - "availability availability availability"; + "visibility groupmode completion actions" + "altcontent altcontent altcontent altcontent" + "afterlink afterlink afterlink afterlink" + "availability availability availability availability"; @include media-breakpoint-down(xs) { grid-template-columns: 1fr min-content; grid-template-areas: "visibility actions" "altcontent altcontent" + "groupmode groupmode" "afterlink afterlink" "completion completion" "availability availability"; @@ -1559,6 +1562,26 @@ $activity-add-hover: theme-color-level('primary', -10) !default; } } + .activity-groupmode-info { + grid-area: groupmode; + justify-self: end; + .groupmode-information { + height: ($icon-width + 15px); + .groupmode-icon-info { + display: none; + } + } + @include media-breakpoint-down(xs) { + width: 100%; + margin-top: map-get($spacers, 2); + padding-top: map-get($spacers, 2); + border-top: $border-width solid $border-color; + .groupmode-information .groupmode-icon-info { + display: inline; + } + } + } + .activity-badges { grid-area: visibility; .badge { diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 8df8bbeac45..c98b420111d 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -29064,24 +29064,25 @@ span.editinstructions .alert-link { .activity-item .activity-grid { display: grid; align-items: center; - grid-template-columns: min-content 1fr min-content min-content; + grid-template-columns: min-content 1fr min-content min-content min-content; grid-template-rows: 1fr repeat(5, min-content); - grid-template-areas: "icon name completion actions" "icon visibility completion actions" "icon dates completion actions" "altcontent altcontent altcontent altcontent" "afterlink afterlink afterlink afterlink" "availability availability availability availability"; + grid-template-areas: "icon name groupmode completion actions" "icon visibility groupmode completion actions" "icon dates groupmode completion actions" "altcontent altcontent altcontent altcontent altcontent" "afterlink afterlink afterlink afterlink afterlink" "availability availability availability availability availability"; } @media (max-width: 575.98px) { .activity-item .activity-grid { - grid-template-columns: min-content 1fr min-content; - grid-template-areas: "icon name actions" "icon visibility actions" "dates dates dates" "completion completion completion" "altcontent altcontent altcontent" "afterlink afterlink afterlink" "availability availability availability"; + grid-template-columns: min-content 1fr min-content min-content min-content; + grid-template-rows: 1fr repeat(4, min-content); + grid-template-areas: "icon name actions" "icon visibility actions" "dates dates dates" "groupmode groupmode groupmode" "completion completion completion" "altcontent altcontent altcontent" "afterlink afterlink afterlink" "availability availability availability"; } } .activity-item .activity-grid.noname-grid { - grid-template-columns: min-content 1fr min-content; - grid-template-areas: "visibility completion actions" "altcontent altcontent altcontent" "afterlink afterlink afterlink" "availability availability availability"; + grid-template-columns: min-content min-content 1fr min-content; + grid-template-areas: "visibility groupmode completion actions" "altcontent altcontent altcontent altcontent" "afterlink afterlink afterlink afterlink" "availability availability availability availability"; } @media (max-width: 575.98px) { .activity-item .activity-grid.noname-grid { grid-template-columns: 1fr min-content; - grid-template-areas: "visibility actions" "altcontent altcontent" "afterlink afterlink" "completion completion" "availability availability"; + grid-template-areas: "visibility actions" "altcontent altcontent" "groupmode groupmode" "afterlink afterlink" "completion completion" "availability availability"; } } .activity-item .activity-actions { @@ -29147,6 +29148,27 @@ span.editinstructions .alert-link { width: 100%; } } +.activity-item .activity-groupmode-info { + grid-area: groupmode; + justify-self: end; +} +.activity-item .activity-groupmode-info .groupmode-information { + height: 31px; +} +.activity-item .activity-groupmode-info .groupmode-information .groupmode-icon-info { + display: none; +} +@media (max-width: 575.98px) { + .activity-item .activity-groupmode-info { + width: 100%; + margin-top: 0.5rem; + padding-top: 0.5rem; + border-top: 1px solid #dee2e6; + } + .activity-item .activity-groupmode-info .groupmode-information .groupmode-icon-info { + display: inline; + } +} .activity-item .activity-badges { grid-area: visibility; } diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 521a1d43235..500a060bcc7 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -29064,24 +29064,25 @@ span.editinstructions .alert-link { .activity-item .activity-grid { display: grid; align-items: center; - grid-template-columns: min-content 1fr min-content min-content; + grid-template-columns: min-content 1fr min-content min-content min-content; grid-template-rows: 1fr repeat(5, min-content); - grid-template-areas: "icon name completion actions" "icon visibility completion actions" "icon dates completion actions" "altcontent altcontent altcontent altcontent" "afterlink afterlink afterlink afterlink" "availability availability availability availability"; + grid-template-areas: "icon name groupmode completion actions" "icon visibility groupmode completion actions" "icon dates groupmode completion actions" "altcontent altcontent altcontent altcontent altcontent" "afterlink afterlink afterlink afterlink afterlink" "availability availability availability availability availability"; } @media (max-width: 575.98px) { .activity-item .activity-grid { - grid-template-columns: min-content 1fr min-content; - grid-template-areas: "icon name actions" "icon visibility actions" "dates dates dates" "completion completion completion" "altcontent altcontent altcontent" "afterlink afterlink afterlink" "availability availability availability"; + grid-template-columns: min-content 1fr min-content min-content min-content; + grid-template-rows: 1fr repeat(4, min-content); + grid-template-areas: "icon name actions" "icon visibility actions" "dates dates dates" "groupmode groupmode groupmode" "completion completion completion" "altcontent altcontent altcontent" "afterlink afterlink afterlink" "availability availability availability"; } } .activity-item .activity-grid.noname-grid { - grid-template-columns: min-content 1fr min-content; - grid-template-areas: "visibility completion actions" "altcontent altcontent altcontent" "afterlink afterlink afterlink" "availability availability availability"; + grid-template-columns: min-content min-content 1fr min-content; + grid-template-areas: "visibility groupmode completion actions" "altcontent altcontent altcontent altcontent" "afterlink afterlink afterlink afterlink" "availability availability availability availability"; } @media (max-width: 575.98px) { .activity-item .activity-grid.noname-grid { grid-template-columns: 1fr min-content; - grid-template-areas: "visibility actions" "altcontent altcontent" "afterlink afterlink" "completion completion" "availability availability"; + grid-template-areas: "visibility actions" "altcontent altcontent" "groupmode groupmode" "afterlink afterlink" "completion completion" "availability availability"; } } .activity-item .activity-actions { @@ -29147,6 +29148,27 @@ span.editinstructions .alert-link { width: 100%; } } +.activity-item .activity-groupmode-info { + grid-area: groupmode; + justify-self: end; +} +.activity-item .activity-groupmode-info .groupmode-information { + height: 31px; +} +.activity-item .activity-groupmode-info .groupmode-information .groupmode-icon-info { + display: none; +} +@media (max-width: 575.98px) { + .activity-item .activity-groupmode-info { + width: 100%; + margin-top: 0.5rem; + padding-top: 0.5rem; + border-top: 1px solid #dee2e6; + } + .activity-item .activity-groupmode-info .groupmode-information .groupmode-icon-info { + display: inline; + } +} .activity-item .activity-badges { grid-area: visibility; }