Merge branch 'MDL-70821-311-5' of git://github.com/junpataleta/moodle into MOODLE_311_STABLE

This commit is contained in:
Adrian Greeve
2021-04-12 14:44:58 +08:00
40 changed files with 487 additions and 261 deletions
@@ -52,5 +52,5 @@ Feature: availability_completion
Then I should not see "Page 2" in the "region-main" "region"
# Mark page 1 complete
When I click on ".togglecompletion .icon" "css_element"
When I toggle the manual completion state of "Page 1"
Then I should see "Page 2" in the "region-main" "region"
+2 -2
View File
@@ -242,7 +242,7 @@ Feature: Award badges
And I click on "Course 1" "link" in the "region-main" "region"
Then I should not see "badges"
And I am on "Course 1" course homepage
And I click on "Not completed: Test assignment name" "icon"
And I toggle the manual completion state of "Test assignment name"
And I follow "Profile" in the user menu
And I click on "Course 1" "link" in the "region-main" "region"
Then I should see "Course Badge"
@@ -296,7 +296,7 @@ Feature: Award badges
And I click on "Course 1" "link" in the "region-main" "region"
Then I should not see "badges"
And I am on "Course 1" course homepage
And I click on "Not completed: Test assignment name" "icon"
And I toggle the manual completion state of "Test assignment name"
And I log out
# Completion cron won't mark the whole course completed unless the
# individual criteria was marked completed more than a second ago. So
+2 -1
View File
@@ -53,7 +53,8 @@ Feature: Award badges based on activity completion
Scenario: Student earns a badge using activity completion, but does not get passing grade
When I log in as "student1"
And I am on "Course 1" course homepage
And the "Test quiz name" "quiz" activity with "auto" completion should be marked as not complete
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "failed"
And the "Receive a pass grade or complete all available attempts" completion condition of "Test quiz name" is displayed as "todo"
And I follow "Test quiz name"
And I press "Re-attempt quiz"
And I set the field "False" to "1"
+156
View File
@@ -153,6 +153,162 @@ class behat_completion extends behat_base {
);
}
/**
* Checks if the activity with specified name is maked as complete.
*
* @Given /^the "(?P<conditionname>(?:[^"]|\\")*)" completion condition of "(?P<activityname>(?:[^"]|\\")*)" is displayed as "(?P<completionstatus>(?:[^"]|\\")*)"$/
* @param string $conditionname The completion condition text.
* @param string $activityname The activity name.
* @param string $completionstatus The completion status. Must be either of the following: 'todo', 'done', 'failed'.
*/
public function activity_completion_condition_displayed_as(string $conditionname, string $activityname,
string $completionstatus): void {
if (!in_array($completionstatus, ['todo', 'done', 'failed'])) {
throw new coding_exception('Invalid completion status. It must be of type "todo", "done", or "failed".');
}
$text = get_string("completion_automatic:$completionstatus", 'core_course') . ' ' . $conditionname;
$conditionslistlabel = get_string('completionrequirements', 'core_course', $activityname);
$selector = "div[aria-label='$conditionslistlabel']";
$this->execute("behat_general::assert_element_contains_text", [$text, $selector, "css_element"]);
}
/**
* Checks if the activity with specified name is maked as complete.
*
* @Given /^the "(?P<conditionname>(?:[^"]|\\")*)" completion condition of "(?P<activityname>(?:[^"]|\\")*)" overridden by "(?P<username>(?:[^"]|\\")*)" is displayed as "(?P<completionstatus>(?:[^"]|\\")*)"$/
* @param string $conditionname The completion condition text.
* @param string $activityname The activity name.
* @param string $username The full name of the user overriding the student's activity completion.
* @param string $completionstatus The override completion status. Must be either of the following: 'todo', 'done'.
*/
public function overridden_activity_completion_condition_displayed_as(string $conditionname, string $activityname,
string $username, string $completionstatus): void {
if (!in_array($completionstatus, ['todo', 'done'])) {
throw new coding_exception('Invalid override completion status. It must be of type "todo" or "done".');
}
$conditionlabel = get_string('completion_setby:auto:' . $completionstatus, 'core_course', (object)[
'condition' => $conditionname,
'setby' => $username,
]);
$conditionbadge = "span[aria-label='$conditionlabel']";
$conditionslistlabel = get_string('completionrequirements', 'core_course', $activityname);
$completionconditions = "div[aria-label='$conditionslistlabel']";
$params = [$conditionbadge, 'css_element', $completionconditions, 'css_element'];
$this->execute("behat_general::should_exist_in_the", $params);
}
/**
* Checks the manual completion state of an activity.
*
* @Given /^the manual completion button of "(?P<activityname>(?:[^"]|\\")*)" is displayed as "(?P<completionstatus>(?:[^"]|\\")*)"$/
* @param string $activityname The activity name.
* @param string $completionstatus The completion status shown on the manual completion button.
* Must be either 'Mark as done' or 'Done'.
*/
public function manual_completion_button_displayed_as(string $activityname, string $completionstatus): void {
if (!in_array($completionstatus, ['Mark as done', 'Done'])) {
throw new coding_exception('Invalid completion status. It must be "Mark as done" or "Done".');
}
$langstringkey = $completionstatus === 'Done' ? 'done' : 'markdone';
$conditionslistlabel = get_string('completion_manual:aria:' . $langstringkey, 'core_course', $activityname);
$selector = "button[aria-label='$conditionslistlabel']";
$this->execute("behat_general::assert_element_contains_text", [$completionstatus, $selector, "css_element"]);
}
/**
* Checks the manual completion state of an activity.
*
* @Given /^the manual completion button of "(?P<activityname>(?:[^"]|\\")*)" overridden by "(?P<username>(?:[^"]|\\")*)" is displayed as "(?P<completionstatus>(?:[^"]|\\")*)"$/
* @param string $activityname The activity name.
* @param string $username The full name of the user overriding the student's activity completion.
* @param string $completionstatus The completion status shown on the manual completion button.
* Must be either 'Mark as done' or 'Done'.
*/
public function overridden_manual_completion_button_displayed_as(string $activityname, string $username,
string $completionstatus): void {
if (!in_array($completionstatus, ['Mark as done', 'Done'])) {
throw new coding_exception('Invalid completion status. It must be "Mark as done" or "Done".');
}
$langstringkey = $completionstatus === 'Done' ? 'done' : 'markdone';
$conditionslistlabel = get_string('completion_setby:manual:' . $langstringkey, 'core_course', (object)[
'activityname' => $activityname,
'setby' => $username,
]);
$selector = "button[aria-label='$conditionslistlabel']";
$this->execute("behat_general::assert_element_contains_text", [$completionstatus, $selector, "css_element"]);
}
/**
* Toggles the manual completion button for a given activity.
*
* @Given /^I toggle the manual completion state of "(?P<activityname>(?:[^"]|\\")*)"$/
* @param string $activityname The activity name.
*/
public function toggle_the_manual_completion_state(string $activityname): void {
$selector = "button[data-action=toggle-manual-completion][data-activityname='{$activityname}']";
$this->execute("behat_general::i_click_on", [$selector, "css_element"]);
}
/**
* Check that the activity does show completion information.
*
* @Given /^there should be no completion information shown for "(?P<activityname>(?:[^"]|\\")*)"$/
* @param string $activityname The activity name.
*/
public function there_should_be_no_completion_for_activity(string $activityname): void {
$containerselector = "div[data-region=activity-information][data-activityname='$activityname']";
try {
$this->find('css_element', $containerselector);
} catch (ElementNotFoundException $e) {
// If activity information container does not exist (activity dates not shown, completion info not shown), all good.
return;
}
// Otherwise, ensure that the completion information does not exist.
$elementselector = "div[data-region=completion-info]";
$params = [$elementselector, "css_element", $containerselector, "css_element"];
$this->execute("behat_general::should_not_exist_in_the", $params);
}
/**
* Check that the manual completion button for the activity is disabled.
*
* @Given /^the manual completion button for "(?P<activityname>(?:[^"]|\\")*)" should be disabled$/
* @param string $activityname The activity name.
*/
public function the_manual_completion_button_for_activity_should_be_disabled(string $activityname): void {
$selector = "div[data-activityname='$activityname'] button";
$params = [$selector, "css_element"];
$this->execute("behat_general::the_element_should_be_disabled", $params);
}
/**
* Check that the activity has the given automatic completion condition.
*
* @Given /^"(?P<activityname>(?:[^"]|\\")*)" should have the "(?P<conditionname>(?:[^"]|\\")*)" completion condition$/
* @param string $activityname The activity name.
* @param string $conditionname The automatic condition name.
*/
public function activity_should_have_the_completion_condition(string $activityname, string $conditionname): void {
$containerselector = "div[data-region=activity-information][data-activityname='$activityname']";
$params = [$conditionname, $containerselector, 'css_element'];
$this->execute("behat_general::assert_element_contains_text", $params);
}
/**
* Checks if the activity with specified name shows a information completion checkbox (i.e. showing the completion tracking
* configuration).
@@ -2,7 +2,7 @@
Feature: Show activity completion status or activity completion configuration on the course page
In order to understand the configuration or status of an activity's completion
As a user
I want to see an appropriate checkbox icon besides the activity
I need to see the appropriate completion information for each activity in the course homepage
Background:
Given the following "courses" exist:
@@ -39,27 +39,27 @@ Feature: Show activity completion status or activity completion configuration on
Given I log in as "student1"
And I am on "Course 1" course homepage
Then I should see "Your progress"
And the "Test forum name" "Forum" activity with "manual" completion shows a status completion checkbox
And the "Test assignment name" "Assign" activity with "auto" completion shows a status completion checkbox
And the "Test quiz name" "Quiz" activity does not show any completion checkbox
And the manual completion button of "Test forum name" is displayed as "Mark as done"
And the "View" completion condition of "Test assignment name" is displayed as "todo"
And there should be no completion information shown for "Test quiz name"
Scenario: Show completion configuration to editing teachers
Given I log in as "teacher1"
And I am on "Course 1" course homepage
Then I should not see "Your progress"
And the "Test forum name" "Forum" activity with "manual" completion shows a configuration completion checkbox
And the "Test assignment name" "Assign" activity with "auto" completion shows a configuration completion checkbox
And the "Test quiz name" "Quiz" activity does not show any completion checkbox
And the manual completion button for "Test forum name" should be disabled
And "Test assignment name" should have the "View" completion condition
And there should be no completion information shown for "Test quiz name"
And I am on "Course 1" course homepage with editing mode on
And I should not see "Your progress"
And the "Test forum name" "Forum" activity with "manual" completion shows a configuration completion checkbox
And the "Test assignment name" "Assign" activity with "auto" completion shows a configuration completion checkbox
And the "Test quiz name" "Quiz" activity does not show any completion checkbox
And the manual completion button for "Test forum name" should be disabled
And "Test assignment name" should have the "View" completion condition
And there should be no completion information shown for "Test quiz name"
Scenario: Show completion configuration to non-editing teachers
Given I log in as "teacher2"
And I am on "Course 1" course homepage
Then I should not see "Your progress"
And the "Test forum name" "Forum" activity with "manual" completion shows a configuration completion checkbox
And the "Test assignment name" "Assign" activity with "auto" completion shows a configuration completion checkbox
And the "Test quiz name" "Quiz" activity does not show any completion checkbox
And the manual completion button for "Test forum name" should be disabled
And "Test assignment name" should have the "View" completion condition
And there should be no completion information shown for "Test quiz name"
@@ -31,8 +31,8 @@ Feature: Allow students to manually mark an activity as complete
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I click on "Not completed: Test forum name. Select to mark as complete." "icon"
Then the "Test forum name" "forum" activity with "manual" completion should be marked as complete
When I toggle the manual completion state of "Test forum name"
Then the manual completion button of "Test forum name" is displayed as "Done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -45,7 +45,7 @@ Feature: Restrict sections availability through completion or grade conditions
And I am on "Course 1" course homepage
Then I should see "Not available unless: The activity Test label is marked complete"
And I should not see "Test page name"
And I click on "Not completed: Test label. Select to mark as complete." "icon"
And I toggle the manual completion state of "Test label"
And I should see "Test page name"
And I should not see "Not available unless: The activity Test label is marked complete"
+19
View File
@@ -1,6 +1,25 @@
This files describes API changes in /completion/* - completion,
information provided here is intended especially for developers.
=== 3.11 ===
* New Behat steps for activity completion in the behat_completion class:
- activity_completion_condition_displayed_as()
- Given the "<Completion condition>" completion condition of "<Activity name>" is displayed as "<Status>"
- activity_should_have_the_completion_condition()
- Given "<Activity name>" should have the "<Condition name>" completion condition
- manual_completion_button_displayed_as()
- Given the manual completion button of "<Activity name>" is displayed as "<Status>"
- the_manual_completion_button_for_activity_should_be_disabled()
- Given the manual completion button for "<Activity name>" should be disabled
- there_should_be_no_completion_for_activity()
- Given there should be no completion information shown for "<Activity name>"
- toggle_the_manual_completion_state()
- Given I toggle the manual completion state of "<Activity name>"
- overridden_manual_completion_button_displayed_as
- Given the manual completion button of "<Activity name>" overridden by "<User>" is displayed as "<Status>"
- overridden_activity_completion_condition_displayed_as
- Given the "<Completion condition>" completion condition of "<Activity name>" overridden by "<User>" is displayed as "<Status>"
=== 3.7 ===
* External function core_completion_external::get_activities_completion_status new returns the following additional field:
- valueused (indicates whether the completion state affects the availability of other content)
+1 -1
View File
@@ -1,2 +1,2 @@
define ("core_course/events",[],function(){return{favourited:"core_course:favourited",unfavorited:"core_course:unfavorited"}});
define ("core_course/events",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={favourited:"core_course:favourited",unfavorited:"core_course:unfavorited",manualCompletionToggled:"core_course:manualcompletiontoggled"};return a.default});
//# sourceMappingURL=events.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["../src/events.js"],"names":["define","favourited","unfavorited"],"mappings":"AAuBAA,OAAM,sBAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,UAAU,CAAE,wBADT,CAEHC,WAAW,CAAE,yBAFV,CAIV,CALK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Contain the events the course component can trigger.\n *\n * @module core_course/events\n * @package core_course\n * @copyright 2018 Simey Lameze <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n favourited: 'core_course:favourited',\n unfavorited: 'core_course:unfavorited',\n };\n});"],"file":"events.min.js"}
{"version":3,"sources":["../src/events.js"],"names":["favourited","unfavorited","manualCompletionToggled"],"mappings":"8IAuBe,CACXA,UAAU,CAAE,wBADD,CAEXC,WAAW,CAAE,yBAFF,CAGXC,uBAAuB,CAAE,qCAHd,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Contain the events the course component can trigger.\n *\n * @module core_course/events\n * @package core_course\n * @copyright 2018 Simey Lameze <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n favourited: 'core_course:favourited',\n unfavorited: 'core_course:unfavorited',\n manualCompletionToggled: 'core_course:manualcompletiontoggled',\n};\n"],"file":"events.min.js"}
+1 -1
View File
@@ -1,2 +1,2 @@
define ("core_course/manual_completion_toggle",["exports","core/templates","core/notification","core_course/repository"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=e(b);c=e(c);function e(a){return a&&a.__esModule?a:{default:a}}function f(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function g(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function g(a){f(i,d,e,g,h,"next",a)}function h(a){f(i,d,e,g,h,"throw",a)}g(void 0)})}}var h={MANUAL_TOGGLE:"button[data-action=toggle-manual-completion]"},i={TOGGLE_MARK_DONE:"manual:mark-done",TOGGLE_UNDO:"manual:undo"},j=!1,k=function(){if(j){return}document.addEventListener("click",function(a){var b=a.target.closest(h.MANUAL_TOGGLE);if(b){a.preventDefault();l(b).catch(c.default.exception)}});j=!0};a.init=k;var l=function(){var a=g(regeneratorRuntime.mark(function a(e){var f,g,h,j,k,l,m,n;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=e.innerHTML;e.setAttribute("disabled","disabled");g=e.getAttribute("data-toggletype");h=e.getAttribute("data-cmid");j=e.getAttribute("data-activityname");k=g===i.TOGGLE_MARK_DONE;a.next=8;return b.default.render("core/loading",{});case 8:l=a.sent;a.next=11;return b.default.replaceNodeContents(e,l,"");case 11:a.prev=11;a.next=14;return(0,d.toggleManualCompletion)(h,k);case 14:m={cmid:h,activityname:j,overallcomplete:k,overallincomplete:!k,istrackeduser:!0};a.next=17;return b.default.renderForPromise("core_course/completion_manual",m);case 17:n=a.sent;a.next=20;return b.default.replaceNode(e,n.html,n.js);case 20:a.next=27;break;case 22:a.prev=22;a.t0=a["catch"](11);e.removeAttribute("disabled");e.innerHTML=f;c.default.exception(a.t0);case 27:case"end":return a.stop();}}},a,null,[[11,22]])}));return function(){return a.apply(this,arguments)}}()});
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/manual_completion_toggle",["exports","core/templates","core/notification","core_course/repository","core_course/events"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=h(b);c=h(c);e=g(e);function f(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;f=function(){return a};return a}function g(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=f();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var g=d?Object.getOwnPropertyDescriptor(a,e):null;if(g&&(g.get||g.set)){Object.defineProperty(c,e,g)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function h(a){return a&&a.__esModule?a:{default:a}}function i(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function j(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){i(h,d,e,f,g,"next",a)}function g(a){i(h,d,e,f,g,"throw",a)}f(void 0)})}}var k={MANUAL_TOGGLE:"button[data-action=toggle-manual-completion]"},l={TOGGLE_MARK_DONE:"manual:mark-done",TOGGLE_UNDO:"manual:undo"},m=!1,n=function(){if(m){return}document.addEventListener("click",function(a){var b=a.target.closest(k.MANUAL_TOGGLE);if(b){a.preventDefault();o(b).catch(c.default.exception)}});m=!0};a.init=n;var o=function(){var a=j(regeneratorRuntime.mark(function a(f){var g,h,i,j,k,m,n,o,p,q,r,s;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:g=f.innerHTML;f.setAttribute("disabled","disabled");h=f.getAttribute("data-toggletype");i=f.getAttribute("data-cmid");j=f.getAttribute("data-activityname");k=h===l.TOGGLE_MARK_DONE;a.next=8;return b.default.render("core/loading",{});case 8:m=a.sent;a.next=11;return b.default.replaceNodeContents(f,m,"");case 11:a.prev=11;a.next=14;return(0,d.toggleManualCompletion)(i,k);case 14:n={cmid:i,activityname:j,overallcomplete:k,overallincomplete:!k,istrackeduser:!0};a.next=17;return b.default.renderForPromise("core_course/completion_manual",n);case 17:o=a.sent;a.next=20;return b.default.replaceNode(f,o.html,o.js);case 20:p=a.sent;q=p.pop();r=f.getAttribute("data-withavailability");s=new CustomEvent(e.manualCompletionToggled,{bubbles:!0,detail:{cmid:i,activityname:j,completed:k,withAvailability:r}});q.dispatchEvent(s);a.next=32;break;case 27:a.prev=27;a.t0=a["catch"](11);f.removeAttribute("disabled");f.innerHTML=g;c.default.exception(a.t0);case 32:case"end":return a.stop();}}},a,null,[[11,27]])}));return function(){return a.apply(this,arguments)}}()});
//# sourceMappingURL=manual_completion_toggle.min.js.map
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/view",["exports","core_course/events"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=d(b);function c(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;c=function(){return a};return a}function d(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=c();if(b&&b.has(a)){return b.get(a)}var d={},e=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=e?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(d,f,g)}else{d[f]=a[f]}}}d.default=a;if(b){b.set(a,d)}return d}var e=!1,f=function(){if(e){return}document.addEventListener(b.manualCompletionToggled,function(a){var b=parseInt(a.detail.withAvailability);if(b){window.location.reload()}});e=!0};a.init=f});
//# sourceMappingURL=view.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../src/view.js"],"names":["registered","init","document","addEventListener","CourseEvents","manualCompletionToggled","e","withAvailability","parseInt","detail","window","location","reload"],"mappings":"ybAwBA,O,yiBAOIA,CAAAA,CAAU,G,CAKDC,CAAI,CAAG,UAAM,CACtB,GAAID,CAAJ,CAAgB,CACZ,MACH,CAEDE,QAAQ,CAACC,gBAAT,CAA0BC,CAAY,CAACC,uBAAvC,CAAgE,SAACC,CAAD,CAAO,CACnE,GAAMC,CAAAA,CAAgB,CAAGC,QAAQ,CAACF,CAAC,CAACG,MAAF,CAASF,gBAAV,CAAjC,CACA,GAAIA,CAAJ,CAAsB,CAElBG,MAAM,CAACC,QAAP,CAAgBC,MAAhB,EACH,CACJ,CAND,EAOAZ,CAAU,GACb,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * JS module for the course homepage.\n *\n * @module core_course/view\n * @package core_course\n * @copyright 2021 Jun Pataleta <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as CourseEvents from 'core_course/events';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Function to intialise and register event listeners for this module.\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n // Listen for toggled manual completion states of activities.\n document.addEventListener(CourseEvents.manualCompletionToggled, (e) => {\n const withAvailability = parseInt(e.detail.withAvailability);\n if (withAvailability) {\n // Reload the page when the toggled manual completion button has availability conditions linked to it.\n window.location.reload();\n }\n });\n registered = true;\n};\n"],"file":"view.min.js"}
+5 -6
View File
@@ -21,9 +21,8 @@
* @copyright 2018 Simey Lameze <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([], function() {
return {
favourited: 'core_course:favourited',
unfavorited: 'core_course:unfavorited',
};
});
export default {
favourited: 'core_course:favourited',
unfavorited: 'core_course:unfavorited',
manualCompletionToggled: 'core_course:manualcompletiontoggled',
};
+17 -1
View File
@@ -26,6 +26,7 @@
import Templates from 'core/templates';
import Notification from 'core/notification';
import {toggleManualCompletion} from 'core_course/repository';
import * as CourseEvents from 'core_course/events';
/**
* Selectors in the manual completion template.
@@ -109,7 +110,22 @@ const toggleManualCompletionState = async(toggleButton) => {
const renderObject = await Templates.renderForPromise('core_course/completion_manual', templateContext);
// Replace the toggle button with the newly loaded template.
await Templates.replaceNode(toggleButton, renderObject.html, renderObject.js);
const replacedNode = await Templates.replaceNode(toggleButton, renderObject.html, renderObject.js);
const newToggleButton = replacedNode.pop();
// Build manualCompletionToggled custom event.
const withAvailability = toggleButton.getAttribute('data-withavailability');
const toggledEvent = new CustomEvent(CourseEvents.manualCompletionToggled, {
bubbles: true,
detail: {
cmid,
activityname,
completed,
withAvailability,
}
});
// Dispatch the manualCompletionToggled custom event.
newToggleButton.dispatchEvent(toggledEvent);
} catch (exception) {
// In case of an error, revert the original state and appearance of the button.
+50
View File
@@ -0,0 +1,50 @@
// 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 <http://www.gnu.org/licenses/>.
/**
* JS module for the course homepage.
*
* @module core_course/view
* @package core_course
* @copyright 2021 Jun Pataleta <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as CourseEvents from 'core_course/events';
/**
* Whether the event listener has already been registered for this module.
*
* @type {boolean}
*/
let registered = false;
/**
* Function to intialise and register event listeners for this module.
*/
export const init = () => {
if (registered) {
return;
}
// Listen for toggled manual completion states of activities.
document.addEventListener(CourseEvents.manualCompletionToggled, (e) => {
const withAvailability = parseInt(e.detail.withAvailability);
if (withAvailability) {
// Reload the page when the toggled manual completion button has availability conditions linked to it.
window.location.reload();
}
});
registered = true;
};
+19 -1
View File
@@ -29,6 +29,7 @@ use cm_info;
use completion_info;
use context;
use core\activity_dates;
use core_availability\info;
use core_completion\cm_completion_details;
use core_user;
use core_user\fields;
@@ -80,6 +81,7 @@ class activity_information implements renderable, templatable {
$data->cmid = $this->cminfo->id;
$data->activityname = $this->cminfo->name;
$data->activitydates = $this->activitydates;
$data->hasdates = !empty($this->activitydates);
return $data;
}
@@ -90,6 +92,8 @@ class activity_information implements renderable, templatable {
* @return stdClass
*/
protected function build_completion_data(): stdClass {
global $CFG;
$data = new stdClass();
$data->hascompletion = $this->cmcompletion->has_completion();
@@ -124,6 +128,19 @@ class activity_information implements renderable, templatable {
$data->accessibledescription = get_string($setbylangkey, 'course', $setbydata);
}
// Whether the completion of this activity controls the availability of other activities/sections in the course.
$data->withavailability = false;
$course = $this->cminfo->get_course();
// An activity with manual completion tracking which is used to enable access to other activities/sections in
// the course needs to refresh the page after having its completion state toggled. This withavailability flag will enable
// this functionality on the course homepage. Otherwise, the completion toggling will just happen normally via ajax.
if ($this->cmcompletion->has_completion() && !$this->cmcompletion->is_automatic()) {
$data->withavailability = !empty($CFG->enableavailability) && info::completion_value_used($course, $this->cminfo->id);
}
// Whether this activity is visible to the user. If not, completion information will not be shown.
$data->uservisible = $this->cminfo->uservisible;
// Build automatic completion details.
$details = [];
foreach ($this->cmcompletion->get_details() as $key => $detail) {
@@ -139,7 +156,8 @@ class activity_information implements renderable, templatable {
'condition' => $detail->description,
'setby' => $data->overrideby,
];
$detail->accessibledescription = get_string('completion_setby:auto', 'course', $setbydata);
$overridestatus = $detail->statuscomplete ? 'done' : 'todo';
$detail->accessibledescription = get_string('completion_setby:auto:' . $overridestatus, 'course', $setbydata);
}
// We don't need the status in the template.
-114
View File
@@ -1,114 +0,0 @@
M.core_completion = {};
M.core_completion.init = function(Y) {
// Check the reload-forcing
var changeDetector = Y.one('#completion_dynamic_change');
if (changeDetector.get('value') > 0) {
changeDetector.set('value', 0);
window.location.reload();
return;
}
var handle_success = function(id, o, args) {
Y.one('#completion_dynamic_change').set('value', 1);
if (o.responseText != 'OK') {
alert('An error occurred when attempting to save your tick mark.\n\n('+o.responseText+'.)'); //TODO: localize
} else {
var current = args.state.get('value');
var modulename = args.modulename.get('value'),
altstr,
iconkey,
button = args.image.get('parentNode');
if (current == 1) {
altstr = M.util.get_string('completion-alt-manual-y', 'completion', modulename);
iconkey = 'i/completion-manual-y';
args.state.set('value', 0);
} else {
altstr = M.util.get_string('completion-alt-manual-n', 'completion', modulename);
iconkey = 'i/completion-manual-n';
args.state.set('value', 1);
}
require(['core/templates', 'core/notification'], function(Templates, Notification) {
Templates.renderPix(iconkey, 'core', altstr).then(function(html) {
var id = button.get('id'),
postFocus = '$(document.getElementById("' + id + '")).focus();';
Templates.replaceNode(args.image.getDOMNode(), html, postFocus);
}).catch(Notification.exception);
});
}
args.ajax.remove();
};
var handle_failure = function(id, o, args) {
alert('An error occurred when attempting to save your tick mark.\n\n('+o.responseText+'.)'); //TODO: localize
args.ajax.remove();
};
var toggle = function(e) {
e.preventDefault();
var form = e.target;
var cmid = 0;
var completionstate = 0;
var state = null;
var image = null;
var modulename = null;
var inputs = Y.Node.getDOMNode(form).getElementsByTagName('input');
for (var i=0; i<inputs.length; i++) {
switch (inputs[i].name) {
case 'id':
cmid = inputs[i].value;
break;
case 'completionstate':
completionstate = inputs[i].value;
state = Y.one(inputs[i]);
break;
case 'modulename':
modulename = Y.one(inputs[i]);
break;
}
}
image = form.one('button .icon');
// start spinning the ajax indicator
var ajax = Y.Node.create('<div class="ajaxworking" />');
form.append(ajax);
var cfg = {
method: "POST",
data: 'id='+cmid+'&completionstate='+completionstate+'&fromajax=1&sesskey='+M.cfg.sesskey,
on: {
success: handle_success,
failure: handle_failure
},
arguments: {state: state, image: image, ajax: ajax, modulename: modulename}
};
Y.use('io-base', function(Y) {
Y.io(M.cfg.wwwroot+'/course/togglecompletion.php', cfg);
});
};
// register submit handlers on manual tick completion forms
Y.all('form.togglecompletion').each(function(form) {
if (!form.hasClass('preventjs')) {
Y.on('submit', toggle, form);
}
});
// hide the help if there are no completion toggles or icons
var help = Y.one('#completionprogressid');
if (help && !(Y.one('form.togglecompletion') || Y.one('.autocompletion'))) {
help.setStyle('display', 'none');
}
};
+20 -4
View File
@@ -425,6 +425,10 @@ class core_course_renderer extends plugin_renderer_base {
* If completion is manual, returns a form (with an icon inside) that allows user to
* toggle completion
*
* @deprecated since Moodle 3.11
* @todo MDL-71183 Final deprecation in Moodle 4.3.
* @see \core_renderer::activity_information
*
* @param stdClass $course course object
* @param completion_info $completioninfo completion info for the course, it is recommended
* to fetch once for all modules in course/section for performance
@@ -434,6 +438,10 @@ class core_course_renderer extends plugin_renderer_base {
*/
public function course_section_cm_completion($course, &$completioninfo, cm_info $mod, $displayoptions = array()) {
global $CFG, $DB, $USER;
debugging(__FUNCTION__ . ' is deprecated and is being replaced by the activity_information output component.',
DEBUG_DEVELOPER);
$output = '';
$istrackeduser = $completioninfo->is_tracked_user($USER->id);
@@ -828,7 +836,12 @@ class core_course_renderer extends plugin_renderer_base {
public function course_section_cm_list_item($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) {
$output = '';
if ($modulehtml = $this->course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions)) {
$modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->extraclasses;
$infoclass = '';
if (($course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS) || !empty($course->showactivitydates)) {
// This will apply styles to the course homepage when the activity information output component is displayed.
$infoclass = 'hasinfo';
}
$modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->extraclasses . ' ' . $infoclass;
$output .= html_writer::tag('li', $modulehtml, array('class' => $modclasses, 'id' => 'module-' . $mod->id));
}
return $output;
@@ -844,7 +857,6 @@ class core_course_renderer extends plugin_renderer_base {
* {@link core_course_renderer::course_section_cm_name()}
* {@link core_course_renderer::course_section_cm_text()}
* {@link core_course_renderer::course_section_cm_availability()}
* {@link core_course_renderer::course_section_cm_completion()}
* {@link course_get_cm_edit_actions()}
* {@link core_course_renderer::course_section_cm_edit_actions()}
*
@@ -856,6 +868,8 @@ class core_course_renderer extends plugin_renderer_base {
* @return string
*/
public function course_section_cm($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) {
global $USER;
$output = '';
// We return empty string (because course module will not be displayed at all)
// if:
@@ -925,12 +939,14 @@ class core_course_renderer extends plugin_renderer_base {
$modicons .= $mod->afterediticons;
}
$modicons .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
if (!empty($modicons)) {
$output .= html_writer::div($modicons, 'actions');
}
$completiondetails = \core_completion\cm_completion_details::get_instance($mod, $USER->id);
$activitydates = \core\activity_dates::get_dates_for_module($mod, $USER->id);
$output .= $this->output->activity_information($mod, $completiondetails, $activitydates);
// Show availability info (if module is not available).
$output .= $this->course_section_cm_availability($mod, $displayoptions);
+23 -16
View File
@@ -24,7 +24,10 @@
Example context (json):
{
"activityname": "Course announcements",
"hascompletion": true,
"uservisible": true,
"hasdates": true,
"isautomatic": true,
"istrackeduser": true,
"activitydates": [
@@ -45,24 +48,28 @@
]
}
}}
<div data-region="activity-information" class="activity-information">
<div class="mb-1">
<div data-region="activity-information" data-activityname="{{activityname}}" class="activity-information">
{{#hasdates}}
<div data-region="activity-dates" class="mb-1">
{{#activitydates}}
{{>core_course/activity_date}}
{{/activitydates}}
</div>
<div>
{{#hascompletion}}
{{#isautomatic}}
<div class="automatic-completion-conditions" data-region ="completionrequirements" role="list" aria-label="{{#str}}completionrequirements, core_course{{/str}}">
{{#completiondetails}}
{{> core_course/completion_automatic }}
{{/completiondetails}}
</div>
{{/isautomatic}}
{{^isautomatic}}
{{> core_course/completion_manual }}
{{/isautomatic}}
{{/hascompletion}}
</div>
{{/hasdates}}
{{#hascompletion}}
{{#uservisible}}
<div data-region="completion-info">
{{#isautomatic}}
<div class="automatic-completion-conditions" data-region ="completionrequirements" role="list" aria-label="{{#str}}completionrequirements, core_course, {{activityname}}{{/str}}">
{{#completiondetails}}
{{> core_course/completion_automatic }}
{{/completiondetails}}
</div>
{{/isautomatic}}
{{^isautomatic}}
{{> core_course/completion_manual }}
{{/isautomatic}}
</div>
{{/uservisible}}
{{/hascompletion}}
</div>
+2 -2
View File
@@ -28,7 +28,7 @@
}}
{{#istrackeduser}}
{{#overallcomplete}}
<button class="btn btn-outline-success" data-action="toggle-manual-completion" data-toggletype="manual:undo" data-cmid="{{cmid}}" data-activityname="{{activityname}}" {{!
<button class="btn btn-outline-success" data-action="toggle-manual-completion" data-toggletype="manual:undo" data-cmid="{{cmid}}" data-activityname="{{activityname}}" data-withavailability="{{withavailability}}" {{!
}}{{#accessibledescription}}{{!
}}title="{{.}}" {{!
}}aria-label="{{.}}" {{!
@@ -41,7 +41,7 @@
</button>
{{/overallcomplete}}
{{#overallincomplete}}
<button class="btn btn-outline-secondary" data-action="toggle-manual-completion" data-toggletype="manual:mark-done" data-cmid="{{cmid}}" data-activityname="{{activityname}}" {{!
<button class="btn btn-outline-secondary" data-action="toggle-manual-completion" data-toggletype="manual:mark-done" data-cmid="{{cmid}}" data-activityname="{{activityname}}" data-withavailability="{{withavailability}}" {{!
}}{{#accessibledescription}}{{!
}}title="{{.}}" {{!
}}aria-label="{{.}}" {{!
+49
View File
@@ -2035,4 +2035,53 @@ class behat_course extends behat_base {
$node = $this->get_selected_node('xpath_element', '//div[@data-region="modules"]');
$this->ensure_node_is_visible($node);
}
/**
* Checks the presence of the given text in the activity's displayed dates.
*
* @Given /^the activity date in "(?P<activityname>(?:[^"]|\\")*)" should contain "(?P<text>(?:[^"]|\\")*)"$/
* @param string $activityname The activity name.
* @param string $text The text to be searched in the activity date.
*/
public function activity_date_in_activity_should_contain_text(string $activityname, string $text): void {
$containerselector = "//div[@data-region='activity-information'][@data-activityname='$activityname']";
$containerselector .= " /div[@data-region='activity-dates']";
$params = [$text, $containerselector, 'xpath_element'];
$this->execute("behat_general::assert_element_contains_text", $params);
}
/**
* Checks the presence of activity dates information in the activity information output component.
*
* @Given /^the activity date information in "(?P<activityname>(?:[^"]|\\")*)" should exist$/
* @param string $activityname The activity name.
*/
public function activity_dates_information_in_activity_should_exist(string $activityname): void {
$containerselector = "//div[@data-region='activity-information'][@data-activityname='$activityname']";
$elementselector = "/div[@data-region='activity-dates']";
$params = [$elementselector, "xpath_element", $containerselector, "xpath_element"];
$this->execute("behat_general::should_exist_in_the", $params);
}
/**
* Checks the absence of activity dates information in the activity information output component.
*
* @Given /^the activity date information in "(?P<activityname>(?:[^"]|\\")*)" should not exist$/
* @param string $activityname The activity name.
*/
public function activity_dates_information_in_activity_should_not_exist(string $activityname): void {
$containerselector = "//div[@data-region='activity-information'][@data-activityname='$activityname']";
try {
$this->find('xpath_element', $containerselector);
} catch (ElementNotFoundException $e) {
// If activity information container does not exist (activity dates not shown, completion info not shown), all good.
return;
}
// Otherwise, ensure that the completion information does not exist.
$elementselector = "//div[@data-region='activity-dates']";
$params = [$elementselector, "xpath_element", $containerselector, "xpath_element"];
$this->execute("behat_general::should_not_exist_in_the", $params);
}
}
@@ -16,7 +16,7 @@ Feature: Allow teachers to edit the visibility of activity dates in a course
| teacher1 | C1 | editingteacher |
And the following "activities" exist:
| activity | course | idnumber | name | intro | timeopen | timeclose |
| choice | C1 | choice1 | Test choice 1 | Test choice description | ##yesterday## | ##tomorrow## |
| choice | C1 | choice1 | Test choice | Test choice description | ##yesterday## | ##tomorrow## |
Scenario: Activity dates setting can be enabled to display activity dates in a course
Given I log in as "teacher1"
@@ -26,11 +26,16 @@ Feature: Allow teachers to edit the visibility of activity dates in a course
| Show activity dates | Yes |
And I click on "Save and display" "button"
And I follow "Test choice"
Then I should see "Opened:" in the "[data-region=activity-information]" "css_element"
And I should see "Closes:" in the "[data-region=activity-information]" "css_element"
# TODO MDL-70821: Check activity dates display on course homepage.
Then the activity date information in "Test choice" should exist
And the activity date in "Test choice" should contain "Opened:"
And the activity date in "Test choice" should contain "Closes:"
And I am on "Course 1" course homepage
# When showactivitydates is enabled, activity dates should be shown on the course homepage.
And the activity date information in "Test choice" should exist
And the activity date in "Test choice" should contain "Opened:"
And the activity date in "Test choice" should contain "Closes:"
Scenario: Activity dates setting can be disabled to hidden activity dates in a course
Scenario: Activity dates setting can be disabled to hide activity dates in a course
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I navigate to "Edit settings" in current page administration
@@ -39,11 +44,12 @@ Feature: Allow teachers to edit the visibility of activity dates in a course
And I click on "Save and display" "button"
And I follow "Test choice"
# Activity dates are always shown in the module's view page.
Then I should see "Opened:"
And I should see "Closes:"
Then the activity date information in "Test choice" should exist
And the activity date in "Test choice" should contain "Opened:"
And the activity date in "Test choice" should contain "Closes:"
And I am on "Course 1" course homepage
And I should not see "Opened:"
And I should not see "Closes:"
# When showactivitydates is disabled, activity dates should not be shown on the course homepage.
And the activity date information in "Test choice" should not exist
Scenario: Default activity dates setting default value can changed to No
Given I log in as "admin"
+5 -25
View File
@@ -119,9 +119,7 @@ if ($courseid) {
}
}
$targetstate = required_param('completionstate', PARAM_INT);
$fromajax = optional_param('fromajax', 0, PARAM_INT);
$PAGE->set_url('/course/togglecompletion.php', array('id'=>$cmid, 'completionstate'=>$targetstate));
@@ -158,33 +156,15 @@ if (!$completion->is_enabled()) {
// Check completion state is manual
if($cm->completion != COMPLETION_TRACKING_MANUAL) {
error_or_ajax('cannotmanualctrack', $fromajax);
throw new moodle_exception('cannotmanualctrack');
}
$completion->update_state($cm, $targetstate);
// And redirect back to course
if ($fromajax) {
print 'OK';
// In case of use in other areas of code we allow a 'backto' parameter, otherwise go back to course page.
if ($backto = optional_param('backto', null, PARAM_URL)) {
redirect($backto);
} else {
// In case of use in other areas of code we allow a 'backto' parameter,
// otherwise go back to course page
if ($backto = optional_param('backto', null, PARAM_URL)) {
redirect($backto);
} else {
redirect(course_get_url($course, $cm->sectionnum));
}
}
// utility functions
function error_or_ajax($message, $fromajax) {
if ($fromajax) {
print get_string($message, 'error');
exit;
} else {
print_error($message);
}
redirect(course_get_url($course, $cm->sectionnum));
}
+9
View File
@@ -3,6 +3,15 @@ information provided here is intended especially for developers.
=== 3.11 ===
* A new callback xxx_coursemodule_definition_after_data that allows plugins to extend activity forms after the data is set.
* \core_course_renderer::course_section_cm_completion() has been deprecated. It is not being used anymore and is being replaced by
\core_renderer::activity_information().
* New Behat steps for checking activity date information in the \behat_course class:
- activity_date_in_activity_should_contain_text()
- Given the activity date in "<ActivityName>" should contain "<Text>"
- activity_dates_information_in_activity_should_exist()
- Given the activity date information in "<ActivityName>" should exist
- activity_dates_information_in_activity_should_not_exist()
- Given the activity date information in "<ActivityName>" should not exist
=== 3.10 ===
+6 -21
View File
@@ -215,14 +215,6 @@
redirect($CFG->wwwroot .'/');
}
$completion = new completion_info($course);
if ($completion->is_enabled()) {
$PAGE->requires->string_for_js('completion-alt-manual-y', 'completion');
$PAGE->requires->string_for_js('completion-alt-manual-n', 'completion');
$PAGE->requires->js_init_call('M.core_completion.init');
}
// Determine whether the user has permission to download course content.
$candownloadcourse = \core\content::can_export_context($context, $USER);
@@ -262,19 +254,6 @@
}
}
if ($completion->is_enabled()) {
// This value tracks whether there has been a dynamic change to the page.
// It is used so that if a user does this - (a) set some tickmarks, (b)
// go to another page, (c) clicks Back button - the page will
// automatically reload. Otherwise it would start with the wrong tick
// values.
echo html_writer::start_tag('form', array('action'=>'.', 'method'=>'get'));
echo html_writer::start_tag('div');
echo html_writer::empty_tag('input', array('type'=>'hidden', 'id'=>'completion_dynamic_change', 'name'=>'completion_dynamic_change', 'value'=>'0'));
echo html_writer::end_tag('div');
echo html_writer::end_tag('form');
}
// Course wrapper start.
echo html_writer::start_tag('div', array('class'=>'course-content'));
@@ -314,4 +293,10 @@
$PAGE->requires->js_call_amd('core_course/downloadcontent', 'init');
}
// Load the view JS module if completion tracking is enabled for this course.
$completion = new completion_info($course);
if ($completion->is_enabled()) {
$PAGE->requires->js_call_amd('core_course/view', 'init');
}
echo $OUTPUT->footer();
+3 -2
View File
@@ -53,10 +53,11 @@ $string['completion_manual:aria:done'] = '{$a} is marked as done. Press to undo.
$string['completion_manual:aria:markdone'] = 'Mark {$a} as done';
$string['completion_manual:done'] = 'Done';
$string['completion_manual:markdone'] = 'Mark as done';
$string['completion_setby:auto'] = 'Done: {$a->condition} (set by {$a->setby})';
$string['completion_setby:auto:done'] = 'Done: {$a->condition} (set by {$a->setby})';
$string['completion_setby:auto:todo'] = 'To do: {$a->condition} (set by {$a->setby})';
$string['completion_setby:manual:done'] = '{$a->activityname} is marked by {$a->setby} as done. Press to undo.';
$string['completion_setby:manual:markdone'] = '{$a->activityname} is marked by {$a->setby} as not done. Press to mark as done.';
$string['completionrequirements'] = 'Completion requirements for this activity';
$string['completionrequirements'] = 'Completion requirements for {$a}';
$string['coursealreadyfinished'] = 'Course already finished';
$string['coursenotyetstarted'] = 'The course has not yet started';
$string['coursenotyetfinished'] = 'The course has not yet finished';
-2
View File
@@ -787,8 +787,6 @@ class page_requirements_manager {
'requires' => array('node', 'cookie'));
break;
case 'core_completion':
$module = array('name' => 'core_completion',
'fullpath' => '/course/completion.js');
break;
case 'core_message':
$module = array('name' => 'core_message',
@@ -33,7 +33,7 @@ Feature: Set a certain number of discussions as a completion condition for a for
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
Then the "Test forum name" "forum" activity with "auto" completion should be marked as not complete
And the "Start discussions: 2" completion condition of "Test forum name" is displayed as "todo"
And I add a new discussion to "Test forum name" forum with:
| Subject | Post 1 subject |
| Message | Body 1 content |
@@ -41,7 +41,7 @@ Feature: Set a certain number of discussions as a completion condition for a for
| Subject | Post 2 subject |
| Message | Body 2 content |
And I am on "Course 1" course homepage
Then the "Test forum name" "forum" activity with "auto" completion should be marked as complete
Then the "Start discussions: 2" completion condition of "Test forum name" is displayed as "done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -48,11 +48,11 @@ Feature: Set end of lesson reached as a completion condition for a lesson
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
Then the "Test lesson" "lesson" activity with "auto" completion should be marked as not complete
Then the "Go through the activity to the end" completion condition of "Test lesson" is displayed as "todo"
And I follow "Test lesson"
And I press "Next page"
And I am on "Course 1" course homepage
Then the "Test lesson" "lesson" activity with "auto" completion should be marked as not complete
And the "Go through the activity to the end" completion condition of "Test lesson" is displayed as "todo"
And I am on "Course 1" course homepage
And I follow "Test lesson"
And I should see "You have seen more than one page of this lesson already."
@@ -61,7 +61,7 @@ Feature: Set end of lesson reached as a completion condition for a lesson
And I press "Next page"
And I press "Next page"
And I am on "Course 1" course homepage
Then the "Test lesson" "lesson" activity with "auto" completion should be marked as complete
And the "Go through the activity to the end" completion condition of "Test lesson" is displayed as "done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -50,7 +50,7 @@ Feature: Set time spent as a completion condition for a lesson
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
Then the "Test lesson" "lesson" activity with "auto" completion should be marked as not complete
Then the "Spend at least 10 secs on this activity" completion condition of "Test lesson" is displayed as "todo"
And I follow "Test lesson"
And I press "Next page"
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
@@ -59,7 +59,7 @@ Feature: Set time spent as a completion condition for a lesson
And I should see "You completed this lesson in"
And I should see ", which is less than the required time of 10 secs. You might need to attempt the lesson again."
And I am on "Course 1" course homepage
And the "Test lesson" "lesson" activity with "auto" completion should be marked as not complete
And the "Spend at least 10 secs on this activity" completion condition of "Test lesson" is displayed as "todo"
And I am on "Course 1" course homepage
And I follow "Test lesson"
And I press "Next page"
@@ -67,7 +67,7 @@ Feature: Set time spent as a completion condition for a lesson
And I press "Next page"
And I should not see "You might need to attempt the lesson again."
And I am on "Course 1" course homepage
And the "Test lesson" "lesson" activity with "auto" completion should be marked as complete
And the "Spend at least 10 secs on this activity" completion condition of "Test lesson" is displayed as "done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -37,14 +37,16 @@ Feature: Set a quiz to be marked complete when the student uses all attempts all
Scenario: student1 uses up both attempts without passing
When I log in as "student1"
And I am on "Course 1" course homepage
And the "Test quiz name" "quiz" activity with "auto" completion should be marked as not complete
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "failed"
And the "Receive a pass grade or complete all available attempts" completion condition of "Test quiz name" is displayed as "todo"
And I follow "Test quiz name"
And I press "Re-attempt quiz"
And I set the field "False" to "1"
And I press "Finish attempt ..."
And I press "Submit all and finish"
And I am on "Course 1" course homepage
Then "Completed: Test quiz name" "icon" should exist in the "li.modtype_quiz" "css_element"
Then the "Receive a grade" completion condition of "Test quiz name" is displayed as "failed"
And the "Receive a pass grade or complete all available attempts" completion condition of "Test quiz name" is displayed as "done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -41,14 +41,14 @@ Feature: Set a quiz to be marked complete when the student completes a minimum a
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And the "Test quiz name" "quiz" activity with "auto" completion should be marked as not complete
And the "Make attempts: 2" completion condition of "Test quiz name" is displayed as "todo"
And I follow "Test quiz name"
And I press "Re-attempt quiz"
And I set the field "False" to "1"
And I press "Finish attempt ..."
And I press "Submit all and finish"
And I am on "Course 1" course homepage
Then "Completed: Test quiz name" "icon" should exist in the "Test quiz name" "list_item"
Then the "Make attempts: 2" completion condition of "Test quiz name" is displayed as "done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -34,12 +34,14 @@ Feature: Set a quiz to be marked complete when the student passes
Scenario: student1 passes on the first try
When I log in as "student1"
And I am on "Course 1" course homepage
And the "Test quiz name" "quiz" activity with "auto" completion should be marked as not complete
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "todo"
And the "Receive a pass grade" completion condition of "Test quiz name" is displayed as "todo"
And user "student1" has attempted "Test quiz name" with responses:
| slot | response |
| 1 | True |
And I am on "Course 1" course homepage
Then "Completed: Test quiz name" "icon" should exist in the "li.modtype_quiz" "css_element"
Then the "Receive a grade" completion condition of "Test quiz name" is displayed as "done"
And the "Receive a pass grade" completion condition of "Test quiz name" is displayed as "done"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -30,10 +30,10 @@ Feature: A teacher can use activity completion to track a student progress
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And the "Test survey name" "survey" activity with "auto" completion should be marked as not complete
And the "View" completion condition of "Test survey name" is displayed as "todo"
And I follow "Test survey name"
And I am on "Course 1" course homepage
Then the "Test survey name" "survey" activity with "auto" completion should be marked as complete
Then the "View" completion condition of "Test survey name" is displayed as "done"
Scenario: Require survey submission
Given I add a "Survey" to section "1" and I fill the form with:
@@ -45,8 +45,8 @@ Feature: A teacher can use activity completion to track a student progress
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And the "Test survey name" "survey" activity with "auto" completion should be marked as not complete
And the "View" completion condition of "Test survey name" is displayed as "todo"
And I follow "Test survey name"
And I press "Click here to continue"
And I am on "Course 1" course homepage
Then the "Test survey name" "survey" activity with "auto" completion should be marked as complete
And the "View" completion condition of "Test survey name" is displayed as "done"
@@ -30,13 +30,12 @@ Feature: Teacher can view and override users' activity completion data via the p
# Student completes the activities, manual and automatic completion.
Given I log in as "student1"
And I am on "Course 1" course homepage
And "Not completed: my assignment. Select to mark as complete." "icon" should exist in the "my assignment" "list_item"
And "Not completed: my assignment 2" "icon" should exist in the "my assignment 2" "list_item"
And I click on "Not completed: my assignment. Select to mark as complete." "icon"
And "Completed: my assignment. Select to mark as not complete." "icon" should exist in the "my assignment" "list_item"
And the manual completion button of "my assignment" is displayed as "Mark as done"
And I toggle the manual completion state of "my assignment"
And the manual completion button of "my assignment" is displayed as "Done"
And I click on "my assignment 2" "link"
And I am on "Course 1" course homepage
And "Completed: my assignment 2" "icon" should exist in the "my assignment 2" "list_item"
And the "View" completion condition of "my assignment 2" is displayed as "done"
And I log out
# Teacher overrides the activity completion statuses to incomplete.
When I log in as "teacher1"
@@ -54,13 +53,13 @@ Feature: Teacher can view and override users' activity completion data via the p
# Student can now complete the activities again, via normal means.
Then I log in as "student1"
And I am on "Course 1" course homepage
And "Not completed: my assignment (set by Teacher One). Select to mark as complete." "icon" should exist in the "my assignment" "list_item"
And "Not completed: my assignment 2 (set by Teacher One)" "icon" should exist in the "my assignment 2" "list_item"
And I click on "Not completed: my assignment (set by Teacher One). Select to mark as complete." "icon"
And "Completed: my assignment. Select to mark as not complete." "icon" should exist in the "my assignment" "list_item"
And the manual completion button of "my assignment" overridden by "Teacher One" is displayed as "Mark as done"
And the "View" completion condition of "my assignment 2" overridden by "Teacher One" is displayed as "todo"
And I toggle the manual completion state of "my assignment"
And the manual completion button of "my assignment" is displayed as "Done"
And I click on "my assignment 2" "link"
And I am on "Course 1" course homepage
And "Completed: my assignment 2" "icon" should exist in the "my assignment 2" "list_item"
And the "View" completion condition of "my assignment 2" is displayed as "done"
And I log out
# And the activity completion report should show the same.
When I log in as "teacher1"
@@ -90,7 +89,7 @@ Feature: Teacher can view and override users' activity completion data via the p
# Then as a student, confirm that automatic completion checks are no longer triggered (such as after an assign submission).
Then I log in as "student1"
And I am on "Course 1" course homepage
And "Completed: my assignment 3 (set by Teacher One)" "icon" should exist in the "my assignment 3" "list_item"
And the "Receive a grade" completion condition of "my assignment 3" overridden by "Teacher One" is displayed as "done"
And I click on "my assignment 3" "link"
And I press "Add submission"
And I set the following fields to these values:
@@ -98,10 +97,9 @@ Feature: Teacher can view and override users' activity completion data via the p
And I press "Save changes"
And I should see "Submitted for grading"
And I am on "Course 1" course homepage
And "Completed: my assignment 3 (set by Teacher One)" "icon" should exist in the "my assignment 3" "list_item"
And the "Receive a grade" completion condition of "my assignment 3" overridden by "Teacher One" is displayed as "done"
# And Confirm that manual completion changes are still allowed.
And I am on "Course 1" course homepage
And "Completed: my assignment (set by Teacher One). Select to mark as not complete." "icon" should exist in the "my assignment" "list_item"
And I click on "Completed: my assignment (set by Teacher One). Select to mark as not complete." "icon"
And "Not completed: my assignment. Select to mark as complete." "icon" should exist in the "my assignment" "list_item"
And I log out
And the manual completion button of "my assignment" overridden by "Teacher One" is displayed as "Done"
And I toggle the manual completion state of "my assignment"
And the manual completion button of "my assignment" is displayed as "Mark as done"
+11
View File
@@ -207,6 +207,17 @@ body:not(.editing) .sitetopic ul.section {
.section li.activity {
padding: .2em;
clear: both;
&.hasinfo {
border-bottom: $border-width solid $table-border-color;
padding-top: map-get($spacers, 3);
padding-bottom: map-get($spacers, 3);
&:last-child {
border-bottom: 0;
padding-bottom: 0;
}
}
}
.section .activity .activityinstance .groupinglabel {
+7
View File
@@ -13377,6 +13377,13 @@ body:not(.editing) .sitetopic ul.section {
.section li.activity {
padding: .2em;
clear: both; }
.section li.activity.hasinfo {
border-bottom: 1px solid #dee2e6;
padding-top: 1rem;
padding-bottom: 1rem; }
.section li.activity.hasinfo:last-child {
border-bottom: 0;
padding-bottom: 0; }
.section .activity .activityinstance .groupinglabel {
padding-left: 30px; }
+7
View File
@@ -13591,6 +13591,13 @@ body:not(.editing) .sitetopic ul.section {
.section li.activity {
padding: .2em;
clear: both; }
.section li.activity.hasinfo {
border-bottom: 1px solid #dee2e6;
padding-top: 1rem;
padding-bottom: 1rem; }
.section li.activity.hasinfo:last-child {
border-bottom: 0;
padding-bottom: 0; }
.section .activity .activityinstance .groupinglabel {
padding-left: 30px; }