MDL-83872 core_courseformat: prepare completion button for reuse
The current completion button has specific templates only for the course view page. However, from now on this component may be used in other places and it needs a main template to be rendered in a standard way.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
issueNumber: MDL-83872
|
||||
notes:
|
||||
core_course:
|
||||
- message: >-
|
||||
Now the core_courseformat\local\content\cm\completion output is more
|
||||
reusable. All the HTML has been moved to its own mustache file,
|
||||
and the output class has a new set_smallbutton method to decide wether
|
||||
to rendered it as a small button (like in the course page) or as a
|
||||
normal one (for other types of pages).
|
||||
type: improved
|
||||
+1
-1
@@ -6,6 +6,6 @@ define("core_course/manual_completion_toggle",["exports","core/templates","core/
|
||||
* @module core_course/manual_completion_toggle
|
||||
* @copyright 2021 Jun Pataleta <jun@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending);const SELECTORS_MANUAL_TOGGLE="button[data-action=toggle-manual-completion]",TOGGLE_TYPES_TOGGLE_MARK_DONE="manual:mark-done";let registered=!1;_exports.init=()=>{registered||(document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_MANUAL_TOGGLE);toggleButton&&(e.preventDefault(),toggleManualCompletionState(toggleButton).catch(_notification.default.exception))})),registered=!0)};const toggleManualCompletionState=async toggleButton=>{const pendingPromise=new _pending.default("core_course:toggleManualCompletionState"),originalInnerHtml=toggleButton.innerHTML;toggleButton.setAttribute("disabled","disabled");const toggleType=toggleButton.getAttribute("data-toggletype"),cmid=toggleButton.getAttribute("data-cmid"),activityname=toggleButton.getAttribute("data-activityname"),completed=toggleType===TOGGLE_TYPES_TOGGLE_MARK_DONE;_templates.default.renderForPromise("core/loading",{}).then((loadingHtml=>{_templates.default.replaceNodeContents(toggleButton,loadingHtml,"")})).catch((()=>{}));try{await(0,_repository.toggleManualCompletion)(cmid,completed);const templateContext={cmid:cmid,activityname:activityname,overallcomplete:completed,overallincomplete:!completed,istrackeduser:!0},renderObject=await _templates.default.renderForPromise("core_course/completion_manual",templateContext),newToggleButton=(await _templates.default.replaceNode(toggleButton,renderObject.html,renderObject.js)).pop(),withAvailability=toggleButton.getAttribute("data-withavailability"),toggledEvent=new CustomEvent(CourseEvents.manualCompletionToggled,{bubbles:!0,detail:{cmid:cmid,activityname:activityname,completed:completed,withAvailability:withAvailability}});newToggleButton.dispatchEvent(toggledEvent)}catch(exception){toggleButton.removeAttribute("disabled"),toggleButton.innerHTML=originalInnerHtml,_notification.default.exception(exception)}pendingPromise.resolve()}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending);const SELECTORS_MANUAL_TOGGLE="button[data-action=toggle-manual-completion]",TOGGLE_TYPES_TOGGLE_MARK_DONE="manual:mark-done";let registered=!1;_exports.init=()=>{registered||(document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_MANUAL_TOGGLE);toggleButton&&(e.preventDefault(),toggleManualCompletionState(toggleButton).catch(_notification.default.exception))})),registered=!0)};const toggleManualCompletionState=async toggleButton=>{const pendingPromise=new _pending.default("core_course:toggleManualCompletionState"),originalInnerHtml=toggleButton.innerHTML;toggleButton.setAttribute("disabled","disabled");const toggleType=toggleButton.getAttribute("data-toggletype"),cmid=toggleButton.getAttribute("data-cmid"),activityname=toggleButton.getAttribute("data-activityname"),completed=toggleType===TOGGLE_TYPES_TOGGLE_MARK_DONE;_templates.default.renderForPromise("core/loading",{}).then((loadingHtml=>{_templates.default.replaceNodeContents(toggleButton,loadingHtml,"")})).catch((()=>{}));try{await(0,_repository.toggleManualCompletion)(cmid,completed);const templateContext={cmid:cmid,activityname:activityname,overallcomplete:completed,overallincomplete:!completed,istrackeduser:!0,normalbutton:!toggleButton.classList.contains("btn-sm")},renderObject=await _templates.default.renderForPromise("core_course/completion_manual",templateContext),newToggleButton=(await _templates.default.replaceNode(toggleButton,renderObject.html,renderObject.js)).pop(),withAvailability=toggleButton.getAttribute("data-withavailability"),toggledEvent=new CustomEvent(CourseEvents.manualCompletionToggled,{bubbles:!0,detail:{cmid:cmid,activityname:activityname,completed:completed,withAvailability:withAvailability}});newToggleButton.dispatchEvent(toggledEvent)}catch(exception){toggleButton.removeAttribute("disabled"),toggleButton.innerHTML=originalInnerHtml,_notification.default.exception(exception)}pendingPromise.resolve()}}));
|
||||
|
||||
//# sourceMappingURL=manual_completion_toggle.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -110,6 +110,7 @@ const toggleManualCompletionState = async(toggleButton) => {
|
||||
overallcomplete: completed,
|
||||
overallincomplete: !completed,
|
||||
istrackeduser: true, // We know that we're tracking completion for this user given the presence of this button.
|
||||
normalbutton: !toggleButton.classList.contains('btn-sm'),
|
||||
};
|
||||
const renderObject = await Templates.renderForPromise('core_course/completion_manual', templateContext);
|
||||
|
||||
|
||||
@@ -42,8 +42,12 @@ class activity_completion implements renderable, templatable {
|
||||
* @param cm_completion_details $cmcompletion The course module information.
|
||||
*/
|
||||
public function __construct(
|
||||
/** @var cm_info $cminfo the activity cm_info. */
|
||||
protected cm_info $cminfo,
|
||||
protected cm_completion_details $cmcompletion
|
||||
/** @var cm_completion_details $cmcompletion the activity completion details. */
|
||||
protected cm_completion_details $cmcompletion,
|
||||
/** @var bool $smallbutton if the button is rendered small (like in course page). */
|
||||
protected bool $smallbutton = true,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -84,6 +88,8 @@ class activity_completion implements renderable, templatable {
|
||||
'overrideby' => $overrideby,
|
||||
'completiondetails' => $this->get_completion_details($overrideby),
|
||||
'accessibledescription' => $this->get_accessible_description($overrideby, $overallcompletion),
|
||||
// For backward compatibility, the template uses small button by default when not set normal size.
|
||||
'normalbutton' => !$this->smallbutton,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ class completion implements named_templatable, renderable {
|
||||
|
||||
use courseformat_named_templatable;
|
||||
|
||||
/** @var bool $smallbutton if the button is rendered small (like in course page). */
|
||||
private bool $smallbutton = true;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -52,6 +55,19 @@ class completion implements named_templatable, renderable {
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the button is rendered small.
|
||||
*
|
||||
* By default, the button is rendered small like in the course page. However,
|
||||
* in other pages like the activities overview, the button is rendered like
|
||||
* a regular button.
|
||||
*
|
||||
* @param bool $smallbutton if the button is rendered small (like in course page).
|
||||
*/
|
||||
public function set_smallbutton(bool $smallbutton): void {
|
||||
$this->smallbutton = $smallbutton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
@@ -76,7 +92,7 @@ class completion implements named_templatable, renderable {
|
||||
return null;
|
||||
}
|
||||
|
||||
$completion = new activity_completion($this->mod, $completiondetails);
|
||||
$completion = new activity_completion($this->mod, $completiondetails, $this->smallbutton);
|
||||
$completiondata = $completion->export_for_template($output);
|
||||
|
||||
if ($completiondata->isautomatic || ($completiondata->ismanual && !$completiondata->istrackeduser)) {
|
||||
@@ -108,6 +124,7 @@ class completion implements named_templatable, renderable {
|
||||
$buttoncontent = get_string('completionmenuitem', 'completion');
|
||||
$buttonclass = '';
|
||||
if ($completioninfo->istrackeduser) {
|
||||
$buttonclass = 'btn-outline-secondary';
|
||||
$buttoncontent = get_string('todo', 'completion');
|
||||
if ($completioninfo->overallcomplete) {
|
||||
$buttoncontent = $output->pix_icon('i/checked', '') . " " . get_string('completion_manual:done', 'core_course');
|
||||
@@ -115,9 +132,11 @@ class completion implements named_templatable, renderable {
|
||||
}
|
||||
}
|
||||
|
||||
$buttonclass .= $this->smallbutton ? ' btn-sm' : '';
|
||||
|
||||
$completiondialog = new dropdown_dialog($buttoncontent, $dialogcontent, [
|
||||
'classes' => 'completion-dropdown',
|
||||
'buttonclasses' => 'btn btn-sm dropdown-toggle icon-no-margin ' . $buttonclass,
|
||||
'buttonclasses' => 'btn dropdown-toggle icon-no-margin ' . $buttonclass,
|
||||
'dropdownposition' => dropdown_dialog::POSITION['end'],
|
||||
]);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"isautomatic": true,
|
||||
"showmanualcompletion": true,
|
||||
"completiondetails": [
|
||||
{
|
||||
{
|
||||
"statuscomplete": 1,
|
||||
"description": "Viewed"
|
||||
},
|
||||
@@ -48,23 +48,5 @@
|
||||
}
|
||||
}}
|
||||
<div data-region="activity-information" data-activityname="{{activityname}}" class="activity-information">
|
||||
{{#uservisible}}
|
||||
<div data-region="completion-info">
|
||||
{{! Completion dropdown dialog }}
|
||||
{{#completiondialog}}
|
||||
<div data-region="completionrequirements" aria-label="{{#str}}completionrequirements, core_course, {{activityname}}{{/str}}">
|
||||
{{> core/local/dropdown/dialog }}
|
||||
</div>
|
||||
{{/completiondialog}}
|
||||
|
||||
{{! Manual completion button }}
|
||||
{{#showmanualcompletion}}
|
||||
{{#istrackeduser}}
|
||||
{{$ core_course/completion_manual }}
|
||||
{{> core_course/completion_manual }}
|
||||
{{/ core_course/completion_manual }}
|
||||
{{/istrackeduser}}
|
||||
{{/showmanualcompletion}}
|
||||
</div>
|
||||
{{/uservisible}}
|
||||
{{> core_courseformat/local/content/cm/completion }}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_courseformat/local/content/cm/completion
|
||||
|
||||
TODO describe template completion
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"cmid": 123,
|
||||
"activityname": "Course announcements",
|
||||
"hascompletion": true,
|
||||
"uservisible": true,
|
||||
"completiondialog": {
|
||||
"buttoncontent": "To do",
|
||||
"dialogcontent": "You must view this activity to complete it.",
|
||||
"buttonclasses": "btn btn-sm btn-outline-secondary dropdown-toggle",
|
||||
"classes": "completion-dropdown"
|
||||
},
|
||||
"isautomatic": true,
|
||||
"showmanualcompletion": true,
|
||||
"completiondetails": [
|
||||
{
|
||||
"statuscomplete": 1,
|
||||
"description": "Viewed"
|
||||
},
|
||||
{
|
||||
"statusincomplete": 1,
|
||||
"description": "Receive a grade"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
{{#uservisible}}
|
||||
<div data-region="completion-info">
|
||||
{{! Completion dropdown dialog }}
|
||||
{{#completiondialog}}
|
||||
<div
|
||||
data-region="completionrequirements"
|
||||
aria-label="{{#str}}completionrequirements, core_course, {{activityname}}{{/str}}"
|
||||
>
|
||||
{{> core/local/dropdown/dialog }}
|
||||
</div>
|
||||
{{/completiondialog}}
|
||||
|
||||
{{! Manual completion button }}
|
||||
{{#showmanualcompletion}}
|
||||
{{#istrackeduser}}
|
||||
{{$ core_course/completion_manual }}
|
||||
{{> core_course/completion_manual }}
|
||||
{{/ core_course/completion_manual }}
|
||||
{{/istrackeduser}}
|
||||
{{/showmanualcompletion}}
|
||||
</div>
|
||||
{{/uservisible}}
|
||||
@@ -23,45 +23,63 @@
|
||||
{
|
||||
"cmid": 0,
|
||||
"overallcomplete": true,
|
||||
"overallincomplete": false
|
||||
"overallincomplete": false,
|
||||
"normalbutton": true
|
||||
}
|
||||
}}
|
||||
{{#istrackeduser}}
|
||||
{{#overallcomplete}}
|
||||
<button class="btn btn-success btn-sm text-nowrap" data-action="toggle-manual-completion" data-toggletype="manual:undo" data-cmid="{{cmid}}" data-activityname="{{activityname}}" data-withavailability="{{withavailability}}" {{!
|
||||
}}{{#accessibledescription}}{{!
|
||||
}}title="{{.}}" {{!
|
||||
}}aria-label="{{.}}" {{!
|
||||
}}{{/accessibledescription}}{{!
|
||||
}}{{^accessibledescription}}{{!
|
||||
}}title="{{#str}}completion_manual:aria:done, course, {{activityname}} {{/str}}" {{!
|
||||
}}aria-label="{{#str}}completion_manual:aria:done, course, {{activityname}} {{/str}}" {{!
|
||||
}}{{/accessibledescription}}>
|
||||
<button
|
||||
class="btn btn-success {{^normalbutton}}btn-sm{{/normalbutton}} text-nowrap"
|
||||
data-action="toggle-manual-completion"
|
||||
data-toggletype="manual:undo"
|
||||
data-cmid="{{cmid}}"
|
||||
data-activityname="{{activityname}}"
|
||||
data-withavailability="{{withavailability}}" {{!
|
||||
}}{{#accessibledescription}}{{!
|
||||
}}title="{{.}}" {{!
|
||||
}}aria-label="{{.}}" {{!
|
||||
}}{{/accessibledescription}}{{!
|
||||
}}{{^accessibledescription}}{{!
|
||||
}}title="{{#str}}completion_manual:aria:done, course, {{activityname}} {{/str}}" {{!
|
||||
}}aria-label="{{#str}}completion_manual:aria:done, course, {{activityname}} {{/str}}" {{!
|
||||
}}{{/accessibledescription}}
|
||||
>
|
||||
<i class="fa fa-check" aria-hidden="true"></i> {{#str}} completion_manual:done, core_course {{/str}}
|
||||
</button>
|
||||
{{/overallcomplete}}
|
||||
{{#overallincomplete}}
|
||||
<button class="btn btn-outline-secondary btn-sm text-nowrap" data-action="toggle-manual-completion" data-toggletype="manual:mark-done" data-cmid="{{cmid}}" data-activityname="{{activityname}}" data-withavailability="{{withavailability}}" {{!
|
||||
}}{{#accessibledescription}}{{!
|
||||
}}title="{{.}}" {{!
|
||||
}}aria-label="{{.}}" {{!
|
||||
}}{{/accessibledescription}}{{!
|
||||
}}{{^accessibledescription}}{{!
|
||||
}}title="{{#str}}completion_manual:aria:markdone, course, {{activityname}} {{/str}}" {{!
|
||||
}}aria-label="{{#str}}completion_manual:aria:markdone, course, {{activityname}} {{/str}}" {{!
|
||||
}}{{/accessibledescription}}>
|
||||
<button
|
||||
class="btn btn-outline-secondary {{^normalbutton}}btn-sm{{/normalbutton}} text-nowrap"
|
||||
data-action="toggle-manual-completion"
|
||||
data-toggletype="manual:mark-done"
|
||||
data-cmid="{{cmid}}"
|
||||
data-activityname="{{activityname}}"
|
||||
data-withavailability="{{withavailability}}" {{!
|
||||
}}{{#accessibledescription}}{{!
|
||||
}}title="{{.}}" {{!
|
||||
}}aria-label="{{.}}" {{!
|
||||
}}{{/accessibledescription}}{{!
|
||||
}}{{^accessibledescription}}{{!
|
||||
}}title="{{#str}}completion_manual:aria:markdone, course, {{activityname}} {{/str}}" {{!
|
||||
}}aria-label="{{#str}}completion_manual:aria:markdone, course, {{activityname}} {{/str}}" {{!
|
||||
}}{{/accessibledescription}}
|
||||
>
|
||||
{{#str}} completion_manual:markdone, core_course {{/str}}
|
||||
</button>
|
||||
{{/overallincomplete}}
|
||||
{{/istrackeduser}}
|
||||
{{^istrackeduser}}
|
||||
<button class="btn btn-outline-secondary btn-sm text-nowrap" disabled>
|
||||
<button
|
||||
class="btn btn-outline-secondary {{^normalbutton}}btn-sm{{/normalbutton}} text-nowrap"
|
||||
disabled
|
||||
>
|
||||
{{#str}} completion_manual:markdone, core_course {{/str}}
|
||||
</button>
|
||||
{{/istrackeduser}}
|
||||
|
||||
{{#js}}
|
||||
require(['core_course/manual_completion_toggle'], toggle => {
|
||||
toggle.init()
|
||||
toggle.init();
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
Reference in New Issue
Block a user