diff --git a/course/format/classes/output/local/content/cm/completion.php b/course/format/classes/output/local/content/cm/completion.php
index 999452f894e..aac38a1aa66 100644
--- a/course/format/classes/output/local/content/cm/completion.php
+++ b/course/format/classes/output/local/content/cm/completion.php
@@ -102,16 +102,21 @@ class completion implements named_templatable, renderable {
* @return array the completion dialog exported for template
*/
private function get_completion_dialog(\renderer_base $output, stdClass $completioninfo): array {
+ $dialogcontent = $output->render_from_template('core_courseformat/local/content/cm/completion_dialog', $completioninfo);
+ $buttoncontent = get_string('completionmenuitem', 'completion');
+ $buttonclass = '';
+
if ($completioninfo->istrackeduser) {
$buttoncontent = get_string('todo', 'completion');
- } else {
- $buttoncontent = get_string('completionmenuitem', 'completion');
+ if ($completioninfo->overallcomplete) {
+ $buttoncontent = $output->pix_icon('i/checked', '') . " " . get_string('completion_manual:done', 'core_course');
+ $buttonclass = 'btn-success';
+ }
}
- $content = $output->render_from_template('core_courseformat/local/content/cm/completion_dialog', $completioninfo);
- $completiondialog = new dropdown_dialog($buttoncontent, $content, [
+ $completiondialog = new dropdown_dialog($buttoncontent, $dialogcontent, [
'classes' => 'completion-dropdown',
- 'buttonclasses' => 'btn btn-sm btn-outline-secondary dropdown-toggle',
+ 'buttonclasses' => 'btn btn-sm dropdown-toggle icon-no-margin ' . $buttonclass,
'dropdownposition' => dropdown_dialog::POSITION['end'],
]);
diff --git a/course/format/templates/local/content/cm/activity.mustache b/course/format/templates/local/content/cm/activity.mustache
index 259a1af6f7b..3f36b708b39 100644
--- a/course/format/templates/local/content/cm/activity.mustache
+++ b/course/format/templates/local/content/cm/activity.mustache
@@ -117,7 +117,7 @@
{{! Completion }}
{{#activityinfo}}
{{#hascompletion}}
-
+
{{$ core_courseformat/local/content/cm/activity_info}}
{{> core_courseformat/local/content/cm/activity_info}}
{{/ core_courseformat/local/content/cm/activity_info}}
diff --git a/course/format/tests/behat/behat_courseformat.php b/course/format/tests/behat/behat_courseformat.php
new file mode 100644
index 00000000000..36a5798aab9
--- /dev/null
+++ b/course/format/tests/behat/behat_courseformat.php
@@ -0,0 +1,43 @@
+.
+
+declare(strict_types=1);
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
+
+/**
+ * Behat step definitions for Course format
+ *
+ * @package core_courseformat
+ * @copyright 2023 Mikel MartÃn
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_courseformat extends behat_base {
+
+ /**
+ * Return the list of partial named selectors
+ *
+ * @return behat_component_named_selector[]
+ */
+ public static function get_partial_named_selectors(): array {
+ return [
+ new behat_component_named_selector('Activity completion', [
+ ".//*[@data-activityname=%locator%]//*[@data-region='completionrequirements']",
+ ]),
+ ];
+ }
+}
diff --git a/course/format/tests/behat/coursepage_completion.feature b/course/format/tests/behat/coursepage_completion.feature
new file mode 100644
index 00000000000..d973a91f633
--- /dev/null
+++ b/course/format/tests/behat/coursepage_completion.feature
@@ -0,0 +1,55 @@
+@core @core_course @core_courseformat @core_completion @javascript
+Feature: Course page activities completion
+ In order to check activities completions
+ As a student
+ I need to see the activity completion criterias dropdown.
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ | student1 | Student | 1 | student1@example.com |
+ And the following "courses" exist:
+ | shortname | fullname | enablecompletion |
+ | C1 | Course 1 | 1 |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber | section | completion | completionview |
+ | assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 | 1 | 0 |
+ | assign | Activity sample 2 | Test assignment description | C1 | sample2 | 1 | 2 | 1 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | teacher1 | C1 | editingteacher |
+
+ Scenario: Teacher does not see manual completion button
+ When I am on the "C1" "Course" page logged in as "teacher1"
+ Then "Mark as done" "button" should not exist in the "Activity sample 1" "activity"
+ And I click on "Completion" "button" in the "Activity sample 1" "activity"
+ And I should see "Mark as done" in the "Activity sample 1" "core_courseformat > Activity completion"
+
+ Scenario: Student should see the manual completion button
+ When I am on the "C1" "Course" page logged in as "student1"
+ Then the manual completion button for "Activity sample 1" should exist
+ And the manual completion button of "Activity sample 1" is displayed as "Mark as done"
+ And I toggle the manual completion state of "Activity sample 1"
+ And the manual completion button of "Activity sample 1" is displayed as "Done"
+
+ Scenario: Teacher should see the automatic completion criterias of activities
+ When I am on the "C1" "Course" page logged in as "teacher1"
+ And I click on "Completion" "button" in the "Activity sample 2" "activity"
+ Then I should see "View" in the "Activity sample 2" "core_courseformat > Activity completion"
+ # After viewing the activity, the completion criteria dropdown should still display "Completion".
+ And I am on the "sample2" Activity page
+ And I am on the "Course 1" course page
+ And "Completion" "button" should exist in the "Activity sample 2" "activity"
+
+ Scenario: Student should see the automatic completion criterias statuses of activities
+ When I am on the "C1" "Course" page logged in as "student1"
+ And I click on "To do" "button" in the "Activity sample 2" "activity"
+ Then I should see "View" in the "Activity sample 2" "core_courseformat > Activity completion"
+ # After viewing the activity, the completion criteria dropdown should display "Done" instead of "To do".
+ And I am on the "sample2" Activity page
+ And I am on the "Course 1" course page
+ And "To do" "button" should not exist in the "Activity sample 2" "activity"
+ And I click on "Done" "button" in the "Activity sample 2" "activity"
+ And I should see "View" in the "Activity sample 2" "core_courseformat > Activity completion"
diff --git a/theme/boost/scss/moodle/course.scss b/theme/boost/scss/moodle/course.scss
index 388a374a709..56c78d67eb4 100644
--- a/theme/boost/scss/moodle/course.scss
+++ b/theme/boost/scss/moodle/course.scss
@@ -1266,6 +1266,26 @@ $activity-item-hover: theme-color-level('primary', -12) !default;
$activity-item-border-radius: 1rem !default;
$activity-add-hover: theme-color-level('primary', -10) !default;
+/* Functions/Mixins definition */
+
+// Activity completion button custom styling mixin.
+@mixin completion-button() {
+ &.btn {
+ border: $border-width solid $gray-400;
+ font-weight: bold;
+ @include border-radius();
+ .icon {
+ font-size: inherit;
+ }
+ }
+ @each $color, $value in $theme-colors {
+ &.btn-#{$color} {
+ $bg-color: theme-color-level($color, $alert-bg-level);
+ @include alert-variant($bg-color, $bg-color, $value);
+ }
+ }
+}
+
/* Activity & Block 'add' buttons */
.activity-add,
@@ -1558,15 +1578,13 @@ $activity-add-hover: theme-color-level('primary', -10) !default;
}
}
- .activity-info {
+ .activity-completion {
grid-area: completion;
justify-self: end;
// Re-style completion buttons (mark as done & dropdown).
button,
a[role="button"] {
- border: $border-width solid $border-color;
- @include border-radius();
- font-weight: bold;
+ @include completion-button();
}
@include media-breakpoint-down(xs) {
width: 100%;
@@ -1687,21 +1705,6 @@ $activity-add-hover: theme-color-level('primary', -10) !default;
display: none;
}
-// Revisit these styles when MDL-78289 lands. Probably not needed anymore.
-.automatic-completion-conditions .badge {
- font-size: 80%;
- padding: map-get($spacers, 2);
- margin-top: map-get($spacers, 1);
- .icon {
- width: 0.7rem;
- height: 0.7rem;
- font-size: 0.7rem;
- }
- &:first-child {
- margin-top: 0;
- }
-}
-
/* Activity divider */
.activity:focus-within + .activity div.divider button,
diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css
index 8e6e922073a..dbf92e094c9 100644
--- a/theme/boost/style/moodle.css
+++ b/theme/boost/style/moodle.css
@@ -28954,6 +28954,7 @@ span.editinstructions .alert-link {
}
/* Variables definition*/
+/* Functions/Mixins definition */
/* Activity & Block 'add' buttons */
.activity-add,
.block-add {
@@ -29188,31 +29189,147 @@ span.editinstructions .alert-link {
font-size: 0.875em;
padding: 0;
}
-.activity-item .activity-info {
+.activity-item .activity-completion {
grid-area: completion;
justify-self: end;
}
-.activity-item .activity-info button,
-.activity-item .activity-info a[role=button] {
- border: 1px solid #dee2e6;
- border-radius: 0.5rem;
+.activity-item .activity-completion button.btn,
+.activity-item .activity-completion a[role=button].btn {
+ border: 1px solid #ced4da;
font-weight: bold;
+ border-radius: 0.5rem;
+}
+.activity-item .activity-completion button.btn .icon,
+.activity-item .activity-completion a[role=button].btn .icon {
+ font-size: inherit;
+}
+.activity-item .activity-completion button.btn-primary,
+.activity-item .activity-completion a[role=button].btn-primary {
+ color: #0f6cbf;
+ background-color: #cfe2f2;
+ border-color: #cfe2f2;
+}
+.activity-item .activity-completion button.btn-primary hr,
+.activity-item .activity-completion a[role=button].btn-primary hr {
+ border-top-color: #bbd6ed;
+}
+.activity-item .activity-completion button.btn-primary .alert-link,
+.activity-item .activity-completion a[role=button].btn-primary .alert-link {
+ color: #0b5190;
+}
+.activity-item .activity-completion button.btn-secondary,
+.activity-item .activity-completion a[role=button].btn-secondary {
+ color: #ced4da;
+ background-color: #f5f6f8;
+ border-color: #f5f6f8;
+}
+.activity-item .activity-completion button.btn-secondary hr,
+.activity-item .activity-completion a[role=button].btn-secondary hr {
+ border-top-color: #e6e9ee;
+}
+.activity-item .activity-completion button.btn-secondary .alert-link,
+.activity-item .activity-completion a[role=button].btn-secondary .alert-link {
+ color: #b1bbc4;
+}
+.activity-item .activity-completion button.btn-success,
+.activity-item .activity-completion a[role=button].btn-success {
+ color: #357a32;
+ background-color: #d7e4d6;
+ border-color: #d7e4d6;
+}
+.activity-item .activity-completion button.btn-success hr,
+.activity-item .activity-completion a[role=button].btn-success hr {
+ border-top-color: #c8dac7;
+}
+.activity-item .activity-completion button.btn-success .alert-link,
+.activity-item .activity-completion a[role=button].btn-success .alert-link {
+ color: #255623;
+}
+.activity-item .activity-completion button.btn-info,
+.activity-item .activity-completion a[role=button].btn-info {
+ color: #008196;
+ background-color: #cce6ea;
+ border-color: #cce6ea;
+}
+.activity-item .activity-completion button.btn-info hr,
+.activity-item .activity-completion a[role=button].btn-info hr {
+ border-top-color: #badde3;
+}
+.activity-item .activity-completion button.btn-info .alert-link,
+.activity-item .activity-completion a[role=button].btn-info .alert-link {
+ color: #005563;
+}
+.activity-item .activity-completion button.btn-warning,
+.activity-item .activity-completion a[role=button].btn-warning {
+ color: #f0ad4e;
+ background-color: #fcefdc;
+ border-color: #fcefdc;
+}
+.activity-item .activity-completion button.btn-warning hr,
+.activity-item .activity-completion a[role=button].btn-warning hr {
+ border-top-color: #fae4c5;
+}
+.activity-item .activity-completion button.btn-warning .alert-link,
+.activity-item .activity-completion a[role=button].btn-warning .alert-link {
+ color: #ec971f;
+}
+.activity-item .activity-completion button.btn-danger,
+.activity-item .activity-completion a[role=button].btn-danger {
+ color: #ca3120;
+ background-color: #f4d6d2;
+ border-color: #f4d6d2;
+}
+.activity-item .activity-completion button.btn-danger hr,
+.activity-item .activity-completion a[role=button].btn-danger hr {
+ border-top-color: #efc3be;
+}
+.activity-item .activity-completion button.btn-danger .alert-link,
+.activity-item .activity-completion a[role=button].btn-danger .alert-link {
+ color: #9e2619;
+}
+.activity-item .activity-completion button.btn-light,
+.activity-item .activity-completion a[role=button].btn-light {
+ color: #f8f9fa;
+ background-color: #fefefe;
+ border-color: #fefefe;
+}
+.activity-item .activity-completion button.btn-light hr,
+.activity-item .activity-completion a[role=button].btn-light hr {
+ border-top-color: #f1f1f1;
+}
+.activity-item .activity-completion button.btn-light .alert-link,
+.activity-item .activity-completion a[role=button].btn-light .alert-link {
+ color: #dae0e5;
+}
+.activity-item .activity-completion button.btn-dark,
+.activity-item .activity-completion a[role=button].btn-dark {
+ color: #343a40;
+ background-color: #d6d8d9;
+ border-color: #d6d8d9;
+}
+.activity-item .activity-completion button.btn-dark hr,
+.activity-item .activity-completion a[role=button].btn-dark hr {
+ border-top-color: #c9cbcd;
+}
+.activity-item .activity-completion button.btn-dark .alert-link,
+.activity-item .activity-completion a[role=button].btn-dark .alert-link {
+ color: #1d2124;
}
@media (max-width: 575.98px) {
- .activity-item .activity-info {
+ .activity-item .activity-completion {
width: 100%;
margin-top: 0.5rem;
}
- .activity-item .activity-info button {
+ .activity-item .activity-completion button {
width: 100%;
}
}
-.activity-item .activity-info .completion-dialog {
+.activity-item .activity-completion .completion-dialog {
color: #495057;
font-size: 0.8203125rem;
min-width: 12rem;
}
-.activity-item .activity-info .completion-dialog .icon {
+.activity-item .activity-completion .completion-dialog .icon {
font-size: 0.8203125rem;
width: 0.8203125rem;
height: 0.8203125rem;
@@ -29314,20 +29431,6 @@ span.editinstructions .alert-link {
display: none;
}
-.automatic-completion-conditions .badge {
- font-size: 80%;
- padding: 0.5rem;
- margin-top: 0.25rem;
-}
-.automatic-completion-conditions .badge .icon {
- width: 0.7rem;
- height: 0.7rem;
- font-size: 0.7rem;
-}
-.automatic-completion-conditions .badge:first-child {
- margin-top: 0;
-}
-
/* Activity divider */
.activity:focus-within + .activity div.divider button,
.course-section-header:focus-within + .content .section .activity:first-child div.divider button,
diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css
index e1bb6842f9a..c86ae7f37ce 100644
--- a/theme/classic/style/moodle.css
+++ b/theme/classic/style/moodle.css
@@ -28954,6 +28954,7 @@ span.editinstructions .alert-link {
}
/* Variables definition*/
+/* Functions/Mixins definition */
/* Activity & Block 'add' buttons */
.activity-add,
.block-add {
@@ -29188,31 +29189,147 @@ span.editinstructions .alert-link {
font-size: 0.875em;
padding: 0;
}
-.activity-item .activity-info {
+.activity-item .activity-completion {
grid-area: completion;
justify-self: end;
}
-.activity-item .activity-info button,
-.activity-item .activity-info a[role=button] {
- border: 1px solid #dee2e6;
- border-radius: 0.25rem;
+.activity-item .activity-completion button.btn,
+.activity-item .activity-completion a[role=button].btn {
+ border: 1px solid #ced4da;
font-weight: bold;
+ border-radius: 0.25rem;
+}
+.activity-item .activity-completion button.btn .icon,
+.activity-item .activity-completion a[role=button].btn .icon {
+ font-size: inherit;
+}
+.activity-item .activity-completion button.btn-primary,
+.activity-item .activity-completion a[role=button].btn-primary {
+ color: #0f6cbf;
+ background-color: #cfe2f2;
+ border-color: #cfe2f2;
+}
+.activity-item .activity-completion button.btn-primary hr,
+.activity-item .activity-completion a[role=button].btn-primary hr {
+ border-top-color: #bbd6ed;
+}
+.activity-item .activity-completion button.btn-primary .alert-link,
+.activity-item .activity-completion a[role=button].btn-primary .alert-link {
+ color: #0b5190;
+}
+.activity-item .activity-completion button.btn-secondary,
+.activity-item .activity-completion a[role=button].btn-secondary {
+ color: #ced4da;
+ background-color: #f5f6f8;
+ border-color: #f5f6f8;
+}
+.activity-item .activity-completion button.btn-secondary hr,
+.activity-item .activity-completion a[role=button].btn-secondary hr {
+ border-top-color: #e6e9ee;
+}
+.activity-item .activity-completion button.btn-secondary .alert-link,
+.activity-item .activity-completion a[role=button].btn-secondary .alert-link {
+ color: #b1bbc4;
+}
+.activity-item .activity-completion button.btn-success,
+.activity-item .activity-completion a[role=button].btn-success {
+ color: #357a32;
+ background-color: #d7e4d6;
+ border-color: #d7e4d6;
+}
+.activity-item .activity-completion button.btn-success hr,
+.activity-item .activity-completion a[role=button].btn-success hr {
+ border-top-color: #c8dac7;
+}
+.activity-item .activity-completion button.btn-success .alert-link,
+.activity-item .activity-completion a[role=button].btn-success .alert-link {
+ color: #255623;
+}
+.activity-item .activity-completion button.btn-info,
+.activity-item .activity-completion a[role=button].btn-info {
+ color: #008196;
+ background-color: #cce6ea;
+ border-color: #cce6ea;
+}
+.activity-item .activity-completion button.btn-info hr,
+.activity-item .activity-completion a[role=button].btn-info hr {
+ border-top-color: #badde3;
+}
+.activity-item .activity-completion button.btn-info .alert-link,
+.activity-item .activity-completion a[role=button].btn-info .alert-link {
+ color: #005563;
+}
+.activity-item .activity-completion button.btn-warning,
+.activity-item .activity-completion a[role=button].btn-warning {
+ color: #f0ad4e;
+ background-color: #fcefdc;
+ border-color: #fcefdc;
+}
+.activity-item .activity-completion button.btn-warning hr,
+.activity-item .activity-completion a[role=button].btn-warning hr {
+ border-top-color: #fae4c5;
+}
+.activity-item .activity-completion button.btn-warning .alert-link,
+.activity-item .activity-completion a[role=button].btn-warning .alert-link {
+ color: #ec971f;
+}
+.activity-item .activity-completion button.btn-danger,
+.activity-item .activity-completion a[role=button].btn-danger {
+ color: #ca3120;
+ background-color: #f4d6d2;
+ border-color: #f4d6d2;
+}
+.activity-item .activity-completion button.btn-danger hr,
+.activity-item .activity-completion a[role=button].btn-danger hr {
+ border-top-color: #efc3be;
+}
+.activity-item .activity-completion button.btn-danger .alert-link,
+.activity-item .activity-completion a[role=button].btn-danger .alert-link {
+ color: #9e2619;
+}
+.activity-item .activity-completion button.btn-light,
+.activity-item .activity-completion a[role=button].btn-light {
+ color: #f8f9fa;
+ background-color: #fefefe;
+ border-color: #fefefe;
+}
+.activity-item .activity-completion button.btn-light hr,
+.activity-item .activity-completion a[role=button].btn-light hr {
+ border-top-color: #f1f1f1;
+}
+.activity-item .activity-completion button.btn-light .alert-link,
+.activity-item .activity-completion a[role=button].btn-light .alert-link {
+ color: #dae0e5;
+}
+.activity-item .activity-completion button.btn-dark,
+.activity-item .activity-completion a[role=button].btn-dark {
+ color: #343a40;
+ background-color: #d6d8d9;
+ border-color: #d6d8d9;
+}
+.activity-item .activity-completion button.btn-dark hr,
+.activity-item .activity-completion a[role=button].btn-dark hr {
+ border-top-color: #c9cbcd;
+}
+.activity-item .activity-completion button.btn-dark .alert-link,
+.activity-item .activity-completion a[role=button].btn-dark .alert-link {
+ color: #1d2124;
}
@media (max-width: 575.98px) {
- .activity-item .activity-info {
+ .activity-item .activity-completion {
width: 100%;
margin-top: 0.5rem;
}
- .activity-item .activity-info button {
+ .activity-item .activity-completion button {
width: 100%;
}
}
-.activity-item .activity-info .completion-dialog {
+.activity-item .activity-completion .completion-dialog {
color: #495057;
font-size: 0.8203125rem;
min-width: 12rem;
}
-.activity-item .activity-info .completion-dialog .icon {
+.activity-item .activity-completion .completion-dialog .icon {
font-size: 0.8203125rem;
width: 0.8203125rem;
height: 0.8203125rem;
@@ -29314,20 +29431,6 @@ span.editinstructions .alert-link {
display: none;
}
-.automatic-completion-conditions .badge {
- font-size: 80%;
- padding: 0.5rem;
- margin-top: 0.25rem;
-}
-.automatic-completion-conditions .badge .icon {
- width: 0.7rem;
- height: 0.7rem;
- font-size: 0.7rem;
-}
-.automatic-completion-conditions .badge:first-child {
- margin-top: 0;
-}
-
/* Activity divider */
.activity:focus-within + .activity div.divider button,
.course-section-header:focus-within + .content .section .activity:first-child div.divider button,