MDL-78527 core_completion: Improving core completion styling

This commit is contained in:
Amaia Anabitarte 2023-08-23 15:12:19 +02:00
parent f367759123
commit 85d07564cb
14 changed files with 234 additions and 72 deletions

View File

@ -99,18 +99,23 @@ abstract class core_completion_edit_base_form extends moodleform {
*
* @return array
*/
protected function add_completion_rules() {
protected function add_custom_completion(string $function): array {
$modnames = array_keys($this->get_module_names());
if (count($modnames) != 1 || !plugin_supports('mod', $modnames[0], FEATURE_COMPLETION_HAS_RULES, false)) {
return [];
return [false, []];
}
$component = "mod_{$modnames[0]}";
$itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component);
$hascustomrules = count($itemnames) > 1;
try {
// Add completion rules from the module form to this form.
$moduleform = $this->get_module_form();
$moduleform->_form = $this->_form;
if ($customcompletionelements = $moduleform->add_completion_rules()) {
$this->hascustomrules = true;
if ($customcompletionelements = $moduleform->{$function}()) {
$hascustomrules = true;
foreach ($customcompletionelements as $customcompletionelement) {
// Instead of checking for the suffix at the end of the element name, we need to check for its presence
// because some modules, like SCORM, are adding things at the end.
@ -123,16 +128,35 @@ abstract class core_completion_edit_base_form extends moodleform {
$moduleform->_form->removeElement($customcompletionelement);
}
}
}
return $customcompletionelements;
return [$hascustomrules, $customcompletionelements];
} catch (Exception $e) {
debugging('Could not add custom completion rule of module ' . $modnames[0] .
' to this form, this has to be fixed by the developer', DEBUG_DEVELOPER);
return [];
return [$hascustomrules, $customcompletionelements];
}
}
/**
* If all selected modules are of the same module type, adds custom completion rules from this module type
*
* @return array
*/
protected function add_completion_rules() {
list($hascustomrules, $customcompletionelements) = $this->add_custom_completion('add_completion_rules');
if (!$this->hascustomrules && $hascustomrules) {
$this->hascustomrules = true;
}
$component = "mod_{$this->get_module_name()}";
$itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component);
if (count($itemnames) > 1) {
$customcompletionelements[] = 'completiongradeitemnumber';
}
return $customcompletionelements;
}
/**
* Checks if at least one of the custom completion rules is enabled
*
@ -147,6 +171,20 @@ abstract class core_completion_edit_base_form extends moodleform {
return false;
}
/**
* If all selected modules are of the same module type, adds custom completion rules from this module type
*
* @return array
*/
public function add_completiongrade_rules(): array {
list($hascustomrules, $customcompletionelements) = $this->add_custom_completion('add_completiongrade_rules');
if (!$this->hascustomrules && $hascustomrules) {
$this->hascustomrules = true;
}
return $customcompletionelements;
}
/**
* Returns list of modules that have automatic completion rules that are not shown on this form
* (because they are not present in at least one other selected module).

View File

@ -142,35 +142,50 @@ trait form_trait {
$completionel = 'completion' . $suffix;
$mform->addElement(
'select',
'radio',
$completionel,
get_string('completion', 'completion'),
[
COMPLETION_TRACKING_NONE => get_string('completion_none', 'completion'),
COMPLETION_TRACKING_MANUAL => get_string('completion_manual', 'completion'),
]
'',
get_string('completion_none', 'completion'),
COMPLETION_TRACKING_NONE,
['class' => 'left-indented']
);
$mform->setDefault($completionel, $trackingdefault);
$mform->addHelpButton($completionel, 'completion', 'completion');
$mform->addElement(
'radio',
$completionel,
'',
get_string('completion_manual', 'completion'),
COMPLETION_TRACKING_MANUAL,
['class' => 'left-indented']
);
$allconditionsel = 'allconditions' . $suffix;
$allconditions = $mform->createElement(
'static',
$allconditionsel,
'',
get_string('allconditions', 'completion'));
$conditionsgroupel = 'conditionsgroup' . $suffix;
$mform->addGroup([$allconditions], $conditionsgroupel, '', null, false);
$mform->hideIf($conditionsgroupel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->setType($completionel, PARAM_INT);
$mform->setDefault($completionel, COMPLETION_TRACKING_NONE);
// Automatic completion once you view it.
$autocompletionpossible = false;
if ($supportviews) {
$completionviewel = 'completionview' . $suffix;
$mform->addElement('checkbox', $completionviewel, get_string('completionview', 'completion'),
get_string('completionview_desc', 'completion'));
$mform->addElement(
'checkbox',
$completionviewel,
'',
get_string('completionview_desc', 'completion')
);
$mform->hideIf($completionviewel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
// Check by default if automatic completion tracking is set.
if ($trackingdefault == COMPLETION_TRACKING_AUTOMATIC) {
$mform->setDefault($completionviewel, 1);
}
$autocompletionpossible = true;
}
// If the activity supports grading, the grade elements must be added.
if ($supportgrades) {
$autocompletionpossible = true;
$this->add_completiongrade_elements($modname, $rating);
}
// Automatic completion according to module-specific rules.
@ -183,14 +198,26 @@ trait form_trait {
foreach ($customcompletionelements as $element) {
$mform->hideIf($element, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
}
$autocompletionpossible = $autocompletionpossible || count($customcompletionelements) > 0;
}
// If the activity supports grading, the grade elements must be added.
if ($supportgrades) {
$this->add_completiongrade_elements($modname, $rating);
}
$autocompletionpossible = $supportviews || $supportgrades || (count($customcompletionelements) > 0);
// Automatic option only appears if possible.
if ($autocompletionpossible) {
$mform->getElement($completionel)->addOption(
$automatic = $mform->createElement(
'radio',
$completionel,
'',
get_string('completion_automatic', 'completion'),
COMPLETION_TRACKING_AUTOMATIC);
COMPLETION_TRACKING_AUTOMATIC,
['class' => 'left-indented']
);
$mform->insertElementBefore($automatic, $conditionsgroupel);
}
// Completion expected at particular date? (For progress tracking).
@ -201,7 +228,7 @@ trait form_trait {
['optional' => true]);
$a = get_string('pluginname', $modname);
$mform->addHelpButton($completionexpectedel, 'completionexpected', 'completion', '', false, $a);
$mform->hideIf($completionexpectedel, 'completion', 'eq', COMPLETION_TRACKING_NONE);
$mform->hideIf($completionexpectedel, $completionel, 'eq', COMPLETION_TRACKING_NONE);
}
}
@ -224,27 +251,42 @@ trait form_trait {
$completionelementexists = $mform->elementExists($completionel);
$component = "mod_{$modname}";
$itemnames = component_gradeitems::get_itemname_mapping_for_component($component);
$indentation = ['parentclass' => 'ml-2'];
$receiveagradeel = 'receiveagrade' . $suffix;
$completionusegradeel = 'completionusegrade' . $suffix;
$completionpassgradeel = 'completionpassgrade' . $suffix;
if (count($itemnames) === 1) {
// Only one gradeitem in this activity.
// We use the completionusegrade field here.
$completionusegradeel = 'completionusegrade' . $suffix;
$mform->addElement(
'checkbox',
$completionusegradeel,
get_string('completionusegrade', 'completion'),
'',
get_string('completionusegrade_desc', 'completion')
);
$mform->addHelpButton($completionusegradeel, 'completionusegrade', 'completion');
// Complete if the user has reached any grade.
$mform->addElement(
'radio',
$completionpassgradeel,
null,
get_string('completionanygrade_desc', 'completion'),
0,
$indentation
);
// Complete if the user has reached the pass grade.
$completionpassgradeel = 'completionpassgrade' . $suffix;
$mform->addElement(
'checkbox',
$completionpassgradeel, null,
get_string('completionpassgrade_desc', 'completion')
'radio',
$completionpassgradeel,
null,
get_string('completionpassgrade_desc', 'completion'),
1,
$indentation
);
$mform->disabledIf($completionpassgradeel, $completionusegradeel, 'notchecked');
$mform->addHelpButton($completionpassgradeel, 'completionpassgrade', 'completion');
$mform->hideIf($completionpassgradeel, $completionusegradeel, 'notchecked');
if ($completionelementexists) {
$mform->hideIf($completionpassgradeel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
@ -254,51 +296,89 @@ trait form_trait {
// The disabledIf logic differs between ratings and other grade items due to different field types.
if ($rating) {
// If using the rating system, there is no grade unless ratings are enabled.
$mform->disabledIf($completionusegradeel, 'assessed', 'eq', 0);
$mform->disabledIf($completionusegradeel, 'assessed', 'eq', 0);
$mform->hideIf($completionusegradeel, 'assessed', 'eq', 0);
$mform->hideIf($completionusegradeel, 'assessed', 'eq', 0);
} else {
// All other field types use the '$gradefieldname' field's modgrade_type.
$itemnumbers = array_keys($itemnames);
$itemnumber = array_shift($itemnumbers);
$gradefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'grade');
$mform->disabledIf($completionusegradeel, "{$gradefieldname}[modgrade_type]", 'eq', 'none');
$mform->disabledIf($completionusegradeel, "{$gradefieldname}[modgrade_type]", 'eq', 'none');
$mform->hideIf($completionusegradeel, "{$gradefieldname}[modgrade_type]", 'eq', 'none');
$mform->hideIf($completionusegradeel, "{$gradefieldname}[modgrade_type]", 'eq', 'none');
}
} else if (count($itemnames) > 1) {
// There are multiple grade items in this activity.
// Show them all.
$options = [
'' => get_string('activitygradenotrequired', 'completion'),
];
$options = [];
foreach ($itemnames as $itemnumber => $itemname) {
$options[$itemnumber] = get_string("grade_{$itemname}_name", $component);
}
$group = [$mform->createElement(
'checkbox',
$completionusegradeel,
null,
get_string('completionusegrade_desc', 'completion')
)];
$completiongradeitemnumberel = 'completiongradeitemnumber' . $suffix;
$mform->addElement(
$group[] =& $mform->createElement(
'select',
$completiongradeitemnumberel,
get_string('completionusegrade', 'completion'),
'',
$options
);
$receiveagradegroupel = 'receiveagradegroup' . $suffix;
$mform->addGroup($group, $receiveagradegroupel, '', [' '], false);
if ($completionelementexists) {
$mform->hideIf($completionusegradeel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->hideIf($receiveagradegroupel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
}
$mform->hideIf($completiongradeitemnumberel, $completionusegradeel, 'notchecked');
// Complete if the user has reached the pass grade.
$completionpassgradeel = 'completionpassgrade' . $suffix;
// Complete if the user has reached any grade.
$mform->addElement(
'checkbox',
$completionpassgradeel, null,
get_string('completionpassgrade_desc', 'completion')
'radio',
$completionpassgradeel,
null,
get_string('completionanygrade_desc', 'completion'),
0,
$indentation
);
$mform->disabledIf($completionpassgradeel, $completiongradeitemnumberel, 'eq', '');
$mform->addHelpButton($completionpassgradeel, 'completionpassgrade', 'completion');
// Complete if the user has reached the pass grade.
$mform->addElement(
'radio',
$completionpassgradeel,
null,
get_string('completionpassgrade_desc', 'completion'),
1,
$indentation
);
$mform->hideIf($completionpassgradeel, $completionusegradeel, 'notchecked');
if ($completionelementexists) {
$mform->hideIf($completiongradeitemnumberel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->hideIf($completionpassgradeel, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
}
}
$customgradingelements = $this->add_completiongrade_rules();
if (property_exists($this, '_customcompletionelements')) {
$this->_customcompletionelements = array_merge($this->_customcompletionelements, $customgradingelements);
}
if ($completionelementexists) {
foreach ($customgradingelements as $customgradingelement) {
$mform->hideIf($customgradingelement, $completionel, 'ne', COMPLETION_TRACKING_AUTOMATIC);
}
}
}
/**
* Add completion grading elements to the form and return the list of element ids.
*
* @return array Array of string IDs of added items, empty array if none
*/
abstract public function add_completiongrade_rules(): array;
/**
* Perform some extra validation for completion settings.
*

View File

@ -519,6 +519,9 @@ class manager {
if (!array_key_exists('completionpassgrade', $data)) {
$data['completionpassgrade'] = 0;
}
if ($data['completionusegrade'] == 0) {
$data['completionpassgrade'] = 0;
}
if ($updatecustomrules) {
$customdata = array_diff_key($data, $defaults);

View File

@ -464,7 +464,10 @@ function set_moduleinfo_defaults($moduleinfo) {
}
// Convert the 'use grade' checkbox into a grade-item number: 0 if checked, null if not.
if (isset($moduleinfo->completionusegrade) && $moduleinfo->completionusegrade) {
if (isset($moduleinfo->completionusegrade) &&
$moduleinfo->completionusegrade &&
!isset($moduleinfo->completiongradeitemnumber
)) {
$moduleinfo->completiongradeitemnumber = 0;
} else if (!isset($moduleinfo->completiongradeitemnumber)) {
// If there is no gradeitemnumber set, make sure to disable completionpassgrade.

View File

@ -231,6 +231,12 @@ abstract class moodleform_mod extends moodleform {
$default_values['ratingtime']=
($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0;
}
// Amend completion settings.
if (isset($default_values['completiongradeitemnumber']) &&
!is_null($default_values['completiongradeitemnumber'])) {
$default_values['receiveagrade'] = 1;
}
}
/**
@ -813,6 +819,15 @@ abstract class moodleform_mod extends moodleform {
return false;
}
/**
* Add completion grading elements to the form and return the list of element ids.
*
* @return array Array of string IDs of added items, empty array if none
*/
public function add_completiongrade_rules(): array {
return [];
}
function standard_hidden_coursemodule_elements(){
$mform =& $this->_form;
$mform->addElement('hidden', 'course', 0);

View File

@ -33,18 +33,18 @@ $string['activityaggregation_all'] = 'ALL selected activities to be completed';
$string['activityaggregation_any'] = 'ANY selected activities to be completed';
$string['activitiescompleted'] = 'Activity completion';
$string['activitiescompletednote'] = 'Note: Activity completion must be set for an activity to appear in the above list.';
$string['activitycompletion'] = 'Activity completion';
$string['activitycompletion'] = 'Completion conditions';
$string['activitycompletionupdated'] = 'Changes saved';
$string['activitygradenotrequired'] = 'Grade not required';
$string['activitygradetopassnotset'] = 'This activity does not have a valid grade to pass set. It may be set in the Grade section of the activity settings.';
$string['addconditions'] = 'Add conditions';
$string['affectedactivities'] = 'The changes will affect the following <b>{$a}</b> activities or resources:';
$string['aggregationmethod'] = 'Aggregation method';
$string['all'] = 'All';
$string['allconditions'] = 'Activity is completed when students do all the following:';
$string['any'] = 'Any';
$string['approval'] = 'Approval';
$string['areyousureoverridecompletion'] = 'Are you sure you want to override the current completion state of this activity for this user and mark it "{$a}"?';
$string['badautocompletion'] = 'When you select automatic completion, you must also enable at least one requirement (below).';
$string['badautocompletion'] = 'You must select at least one condition.';
$string['badcompletiongradeitemnumber'] = 'Require grade can\'t be enabled for <b>{$a}</b> because grading by {$a} is not enabled.';
$string['bulkactivitycompletion'] = 'Bulk edit activity completion';
$string['bulkactivitydetail'] = 'Select the activities you wish to bulk edit.';
@ -82,12 +82,13 @@ $string['completion-n-override'] = 'Not completed (set by {$a})';
$string['completion-pass'] = 'Completed (achieved pass grade)';
$string['completion-y'] = 'Completed';
$string['completion-y-override'] = 'Completed (set by {$a})';
$string['completion_automatic'] = 'Show activity as complete when conditions are met';
$string['completion_automatic'] = 'Add requirements';
$string['completion_help'] = 'If enabled, activity completion is tracked, either manually or automatically, based on certain conditions. Multiple conditions may be set if desired. If so, the activity will only be considered complete when ALL conditions are met.';
$string['completion_link'] = 'activity/completion';
$string['completion_manual'] = 'Students can manually mark the activity as completed';
$string['completion_none'] = 'Do not indicate activity completion';
$string['completion_manual'] = 'Students must manually mark the activity as done';
$string['completion_none'] = 'None';
$string['completionactivitydefault'] = 'Use activity default';
$string['completionanygrade_desc'] = 'Any grade';
$string['completiondisabled'] = 'Disabled, not shown in activity settings';
$string['completionenabled'] = 'Enabled, control via completion and activity settings';
$string['completionexpected'] = 'Set reminder in Timeline';
@ -109,14 +110,11 @@ $string['completionondatevalue'] = 'Date when course will be marked as complete'
$string['completionduration'] = 'Enrolment';
$string['completionsettingslocked'] = 'Completion settings locked';
$string['completionpassgrade'] = 'Require passing grade';
$string['completionpassgrade_desc'] = 'Student must receive a passing grade to complete this activity';
$string['completionpassgrade_help'] = 'If enabled, the activity is considered complete when a student receives a passing grade.';
$string['completionpassgrade_desc'] = 'Passing grade';
$string['completionusegrade'] = 'Require grade';
$string['completionusegrade_desc'] = 'Student must receive a grade to complete this activity';
$string['completionusegrade_help'] = 'If enabled, the activity is considered complete when a student receives a grade. If a pass grade for the activity is set, then pass and fail icons are displayed in the activity completion report.';
$string['completionusegrade_desc'] = 'Receive a grade';
$string['completionupdated'] = 'Updated completion for activity <b>{$a}</b>';
$string['completionview'] = 'Require view';
$string['completionview_desc'] = 'Student must view this activity to complete it';
$string['completionview_desc'] = 'View the activity';
$string['configenablecompletion'] = 'If enabled, course and activity completion conditions may be set. Setting activity completion conditions is recommended so that meaningful data is displayed for users in their course overview on the Dashboard.';
$string['confirmselfcompletion'] = 'Confirm self completion';
$string['courseaggregation'] = 'Condition requires';
@ -264,3 +262,7 @@ $string['yourprogress'] = 'Your progress';
$string['editcoursecompletionsettings'] = 'Edit course completion settings';
$string['completiondefault'] = 'Default completion tracking';
$string['configcompletiondefault'] = 'The default setting for completion tracking when creating new activities.';
$string['completionview'] = 'Require view';
$string['activitygradenotrequired'] = 'Grade not required';
$string['completionpassgrade_help'] = 'If enabled, the activity is considered complete when a student receives a passing grade.';
$string['completionusegrade_help'] = 'If enabled, the activity is considered complete when a student receives a grade. If a pass grade for the activity is set, then pass and fail icons are displayed in the activity completion report.';

View File

@ -97,3 +97,9 @@ dateintervaldayshoursmins,core_langconfig
completiondefault,core_completion
configcompletiondefault,core_completion
aria:courseimage,core_course
completionview,core_completion
completionview,mod_bigbluebuttonbn
completionview_desc,mod_bigbluebuttonbn
activitygradenotrequired,core_completion
completionpassgrade_help,core_completion
completionusegrade_help,core_completion

View File

@ -1,4 +1,4 @@
<div class="form-group row {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}}">
<div class="form-group row {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}} {{{element.parentclasses}}}">
<div class="col-md-3 col-form-label pb-0 pt-0">
{{#text}}
<label class="d-inline word-break" for="{{element.id}}">

View File

@ -1,4 +1,4 @@
<div class="form-group row {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}}">
<div class="form-group row {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}} {{{element.parentclasses}}}">
<div class="col-md-3 col-form-label pb-0 pt-0">
{{#text}}
<label class="d-inline word-break" for="{{element.id}}">

View File

@ -10,7 +10,7 @@
}
}}
<div class="form-group row {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}}">
<div class="form-group row {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.parentclasses}}}">
<div class="col-md-3 col-form-label pb-0 pt-0">
{{#text}}
<label class="d-inline word-break" for="{{element.id}}">
@ -19,7 +19,7 @@
{{/text}}
</div>
<div class="col-md-9 checkbox">
<div class="form-check d-flex">
<div class="form-check d-flex {{{element.extraclasses}}}">
<label class="form-check-label">
{{^element.hardfrozen}}{{#element.frozen}}{{#element.checked}}
<input type="hidden" name="{{element.name}}" value="{{element.value}}">

View File

@ -37,7 +37,7 @@
}}
{{< core_form/element-template }}
{{$element}}
<div class="form-control-static">
<div class="form-control-static {{{element.extraclasses}}}">
{{{element.html}}}
</div>
{{/element}}

View File

@ -505,3 +505,10 @@ textarea[data-auto-rows] {
}
}
}
// For form elements aligned to the left with no padding (e.g. Completion conditions activity settings).
.form-check {
&.left-indented {
padding-left: 0px;
}
}

View File

@ -33394,6 +33394,10 @@ textarea[data-auto-rows] {
right: 0;
}
.form-check.left-indented {
padding-left: 0px;
}
.pagelayout-login #region-main {
border: 0;
background-color: inherit;

View File

@ -33394,6 +33394,10 @@ textarea[data-auto-rows] {
right: 0;
}
.form-check.left-indented {
padding-left: 0px;
}
.pagelayout-login #region-main {
border: 0;
background-color: inherit;