diff --git a/course/format/amd/build/local/content/activity_header.min.js b/course/format/amd/build/local/content/activity_header.min.js new file mode 100644 index 00000000000..2cc101f8448 --- /dev/null +++ b/course/format/amd/build/local/content/activity_header.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/content/activity_header",["exports","core/reactive","core_courseformat/courseeditor","core_course/events"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;d=f(d);function e(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;e=function(){return a};return a}function f(a){if(a&&a.__esModule){return a}if(null===a||"object"!==g(a)&&"function"!=typeof a){return{default:a}}var b=e();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var h=d?Object.getOwnPropertyDescriptor(a,f):null;if(h&&(h.get||h.set)){Object.defineProperty(c,f,h)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}function g(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){g=function(a){return typeof a}}else{g=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return g(a)}function h(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function i(a,b){for(var c=0,d;c.\n\n/**\n * The activity header component.\n *\n * @module core_courseformat/local/content/activity_header\n * @class core_courseformat/local/content/activity_header\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport * as CourseEvents from 'core_course/events';\n\n// Global page selectors.\nconst SELECTORS = {\n ACTIVITY_HEADER: `[data-for='page-activity-header']`,\n};\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'activity_header';\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {string} target optional altentative DOM main element CSS selector\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n const elementselector = (target) ? target : SELECTORS.ACTIVITY_HEADER;\n return new Component({\n element: document.querySelector(elementselector),\n reactive: getCurrentCourseEditor(),\n selectors\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n }\n\n /**\n * Activity manual completion listener.\n *\n * @param {Event} event the custom event\n * @param {object} event.detail the event details\n */\n _completionHandler({detail}) {\n if (detail === undefined) {\n return;\n }\n this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);\n }\n}\n"],"file":"activity_header.min.js"} \ No newline at end of file diff --git a/course/format/amd/src/local/content/activity_header.js b/course/format/amd/src/local/content/activity_header.js new file mode 100644 index 00000000000..ec5ac52c5f1 --- /dev/null +++ b/course/format/amd/src/local/content/activity_header.js @@ -0,0 +1,84 @@ +// 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 . + +/** + * The activity header component. + * + * @module core_courseformat/local/content/activity_header + * @class core_courseformat/local/content/activity_header + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import {BaseComponent} from 'core/reactive'; +import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; +import * as CourseEvents from 'core_course/events'; + +// Global page selectors. +const SELECTORS = { + ACTIVITY_HEADER: `[data-for='page-activity-header']`, +}; + +export default class Component extends BaseComponent { + + /** + * Constructor hook. + */ + create() { + // Optional component name for debugging. + this.name = 'activity_header'; + } + + /** + * Static method to create a component instance form the mustache template. + * + * @param {string} target optional altentative DOM main element CSS selector + * @param {object} selectors optional css selector overrides + * @return {Component} + */ + static init(target, selectors) { + const elementselector = (target) ? target : SELECTORS.ACTIVITY_HEADER; + return new Component({ + element: document.querySelector(elementselector), + reactive: getCurrentCourseEditor(), + selectors + }); + } + + /** + * Initial state ready method. + */ + stateReady() { + // Capture completion events. + this.addEventListener( + this.element, + CourseEvents.manualCompletionToggled, + this._completionHandler + ); + } + + /** + * Activity manual completion listener. + * + * @param {Event} event the custom event + * @param {object} event.detail the event details + */ + _completionHandler({detail}) { + if (detail === undefined) { + return; + } + this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed); + } +} diff --git a/course/format/tests/behat/courseindex_completion.feature b/course/format/tests/behat/courseindex_completion.feature index 8a842c1bcb2..8d10341a7d6 100644 --- a/course/format/tests/behat/courseindex_completion.feature +++ b/course/format/tests/behat/courseindex_completion.feature @@ -40,7 +40,7 @@ Feature: Course index completion icons And "To do" "icon" should exist in the "courseindex-content" "region" @javascript - Scenario: Manual completion shoudl update the course index completion + Scenario: Manual completion should update the course index completion Given I am on the "C1" "Course" page logged in as "student1" And "To do" "icon" should exist in the "courseindex-content" "region" When I press "Mark as done" @@ -50,6 +50,17 @@ Feature: Course index completion icons And I wait until "Mark as done" "button" exists And "To do" "icon" should exist in the "courseindex-content" "region" + @javascript + Scenario: Manual completion in an activity page should update the course index + Given I am on the "sample1" "Activity" page logged in as "student1" + And "To do" "icon" should exist in the "courseindex-content" "region" + When I press "Mark as done" + And I wait until "Done" "button" exists + Then "Done" "icon" should exist in the "courseindex-content" "region" + And I press "Done" + And I wait until "Mark as done" "button" exists + And "To do" "icon" should exist in the "courseindex-content" "region" + @javascript Scenario: Refresh the page should keep the completion consistent Given I am on the "C1" "Course" page logged in as "student1" diff --git a/lib/classes/output/activity_header.php b/lib/classes/output/activity_header.php index af1bd2bf904..7af3340b0da 100644 --- a/lib/classes/output/activity_header.php +++ b/lib/classes/output/activity_header.php @@ -174,6 +174,14 @@ class activity_header implements \renderable, \templatable { $completion = $output->activity_information($this->page->cm, $completiondetails, $activitydates); } + $format = course_get_format($this->page->course); + if ($format->supports_components()) { + $this->page->requires->js_call_amd( + 'core_courseformat/local/content/activity_header', + 'init' + ); + } + return [ 'title' => $this->title, 'description' => $this->description, diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index b9a835cd861..afe8b9842b7 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -1041,11 +1041,12 @@ class page_requirements_manager { public function js_call_amd($fullmodule, $func = null, $params = array()) { global $CFG; - list($component, $module) = explode('/', $fullmodule, 2); + $modulepath = explode('/', $fullmodule); - $component = clean_param($component, PARAM_COMPONENT); - $module = clean_param($module, PARAM_ALPHANUMEXT); - $modname = "{$component}/{$module}"; + $modname = clean_param(array_shift($modulepath), PARAM_COMPONENT); + foreach ($modulepath as $module) { + $modname .= '/' . clean_param($module, PARAM_ALPHANUMEXT); + } $functioncode = []; if ($func !== null) { diff --git a/lib/templates/activity_header.mustache b/lib/templates/activity_header.mustache index 42339b6748f..149f32adfa9 100644 --- a/lib/templates/activity_header.mustache +++ b/lib/templates/activity_header.mustache @@ -70,7 +70,7 @@ {{#title}}

{{{title}}}

{{/title}} -
+
{{#completion}} {{#str}} overallaggregation, completion {{/str}} {{{completion}}}