diff --git a/backup/moodle2/backup_course_task.class.php b/backup/moodle2/backup_course_task.class.php
index 7cb258bf7c5..f9ae5dca8b6 100644
--- a/backup/moodle2/backup_course_task.class.php
+++ b/backup/moodle2/backup_course_task.class.php
@@ -130,6 +130,9 @@ class backup_course_task extends backup_task {
// Generate the course competencies.
$this->add_step(new backup_course_competencies_structure_step('course_competencies', 'competencies.xml'));
+ // Annotate activity completion defaults.
+ $this->add_step(new backup_completion_defaults_structure_step('course_completion_defaults', 'completiondefaults.xml'));
+
// Generate the inforef file (must be after ALL steps gathering annotations of ANY type)
$this->add_step(new backup_inforef_structure_step('course', 'inforef.xml'));
diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php
index 83300f1c9a0..8855d58cedb 100644
--- a/backup/moodle2/backup_stepslib.php
+++ b/backup/moodle2/backup_stepslib.php
@@ -2612,3 +2612,49 @@ class backup_course_completion_structure_step extends backup_structure_step {
}
}
+
+/**
+ * Backup completion defaults for each module type.
+ *
+ * @package core_backup
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class backup_completion_defaults_structure_step extends backup_structure_step {
+
+ /**
+ * To conditionally decide if one step will be executed or no
+ */
+ protected function execute_condition() {
+ // No completion on front page.
+ if ($this->get_courseid() == SITEID) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * The structure of the course completion backup
+ *
+ * @return backup_nested_element
+ */
+ protected function define_structure() {
+
+ $cc = new backup_nested_element('course_completion_defaults');
+
+ $defaults = new backup_nested_element('course_completion_default', array('id'), array(
+ 'modulename', 'completion', 'completionview', 'completionusegrade', 'completionexpected', 'customrules'
+ ));
+
+ // Use module name instead of module id so we can insert into another site later.
+ $sourcesql = "SELECT d.id, m.name as modulename, d.completion, d.completionview, d.completionusegrade,
+ d.completionexpected, d.customrules
+ FROM {course_completion_defaults} d join {modules} m on d.module = m.id
+ WHERE d.course = ?";
+ $defaults->set_source_sql($sourcesql, array(backup::VAR_COURSEID));
+
+ $cc->add_child($defaults);
+ return $cc;
+
+ }
+}
diff --git a/backup/moodle2/restore_course_task.class.php b/backup/moodle2/restore_course_task.class.php
index 1c07e9ccd8a..74bfa6c17c3 100644
--- a/backup/moodle2/restore_course_task.class.php
+++ b/backup/moodle2/restore_course_task.class.php
@@ -118,6 +118,9 @@ class restore_course_task extends restore_task {
// Course competencies.
$this->add_step(new restore_course_competencies_structure_step('course_competencies', 'competencies.xml'));
+ // Activity completion defaults.
+ $this->add_step(new restore_completion_defaults_structure_step('course_completion_defaults', 'completiondefaults.xml'));
+
// At the end, mark it as built
$this->built = true;
}
diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php
index 5f4cf05685c..5a744e6d8d0 100644
--- a/backup/moodle2/restore_stepslib.php
+++ b/backup/moodle2/restore_stepslib.php
@@ -5428,3 +5428,80 @@ abstract class restore_questions_activity_structure_step extends restore_activit
}
}
}
+
+/**
+ * Restore completion defaults for each module type
+ *
+ * @package core_backup
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class restore_completion_defaults_structure_step extends restore_structure_step {
+ /**
+ * To conditionally decide if this step must be executed.
+ */
+ protected function execute_condition() {
+ global $CFG;
+
+ // Completion disabled in this site, don't execute.
+ if (empty($CFG->enablecompletion)) {
+ return false;
+ }
+
+ // No completion on the front page.
+ if ($this->get_courseid() == SITEID) {
+ return false;
+ }
+
+ // No default completion info found, don't execute.
+ $fullpath = $this->task->get_taskbasepath();
+ $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
+ if (!file_exists($fullpath)) {
+ return false;
+ }
+
+ // Arrived here, execute the step.
+ return true;
+ }
+
+ /**
+ * Function that will return the structure to be processed by this restore_step.
+ *
+ * @return restore_path_element[]
+ */
+ protected function define_structure() {
+ return [new restore_path_element('completion_defaults', '/course_completion_defaults/course_completion_default')];
+ }
+
+ /**
+ * Processor for path element 'completion_defaults'
+ *
+ * @param stdClass|array $data
+ */
+ protected function process_completion_defaults($data) {
+ global $DB;
+
+ $data = (array)$data;
+ $oldid = $data['id'];
+ unset($data['id']);
+
+ // Find the module by name since id may be different in another site.
+ if (!$mod = $DB->get_record('modules', ['name' => $data['modulename']])) {
+ return;
+ }
+ unset($data['modulename']);
+
+ // Find the existing record.
+ $newid = $DB->get_field('course_completion_defaults', 'id',
+ ['course' => $this->task->get_courseid(), 'module' => $mod->id]);
+ if (!$newid) {
+ $newid = $DB->insert_record('course_completion_defaults',
+ ['course' => $this->task->get_courseid(), 'module' => $mod->id] + $data);
+ } else {
+ $DB->update_record('course_completion_defaults', ['id' => $newid] + $data);
+ }
+
+ // Save id mapping for restoring associated events.
+ $this->set_mapping('course_completion_defaults', $oldid, $newid);
+ }
+}
\ No newline at end of file
diff --git a/completion/classes/bulkedit_form.php b/completion/classes/bulkedit_form.php
index c83dc8a781c..7e92918d6fc 100644
--- a/completion/classes/bulkedit_form.php
+++ b/completion/classes/bulkedit_form.php
@@ -15,7 +15,7 @@
// along with Moodle. If not, see .
/**
- * Bulk activity completion form
+ * Bulk edit activity completion form
*
* @package core_completion
* @copyright 2017 Marina Glancy
@@ -24,26 +24,18 @@
defined('MOODLE_INTERNAL') || die;
-require_once ($CFG->libdir.'/formslib.php');
-require_once($CFG->libdir.'/completionlib.php');
-require_once($CFG->dirroot.'/course/modlib.php');
-
/**
- * Bulk activity completion form
+ * Bulk edit activity completion form
*
* @package core_completion
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class core_completion_bulkedit_form extends moodleform {
+class core_completion_bulkedit_form extends core_completion_edit_base_form {
/** @var cm_info[] list of selected course modules */
protected $cms = [];
/** @var array Do not use directly, call $this->get_module_names() */
protected $_modnames = null;
- /** @var moodleform_mod Do not use directly, call $this->get_module_form() */
- protected $_moduleform = null;
- /** @var bool */
- protected $hascustomrules = false;
/**
* Returns list of types of selected modules
@@ -61,231 +53,27 @@ class core_completion_bulkedit_form extends moodleform {
return $this->_modnames;
}
- /**
- * Returns true if all selected modules support tracking view.
- *
- * @return bool
- */
- protected function support_views() {
- foreach ($this->get_module_names() as $modname => $modfullname) {
- if (!plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if all selected modules support grading.
- *
- * @return bool
- */
- protected function support_grades() {
- foreach ($this->get_module_names() as $modname => $modfullname) {
- if (!plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns an instance of component-specific module form for the first selected module
- *
- * @return moodleform_mod|null
- */
- protected function get_module_form() {
- global $CFG, $PAGE;
-
- if ($this->_moduleform) {
- return $this->_moduleform;
- }
-
- $cm = reset($this->cms);
- $modname = $cm->modname;
- $course = $cm->get_course();
-
- $modmoodleform = "$CFG->dirroot/mod/$modname/mod_form.php";
- if (file_exists($modmoodleform)) {
- require_once($modmoodleform);
- } else {
- print_error('noformdesc');
- }
-
- list($module, $context, $cw, $cmrec, $data) = prepare_new_moduleinfo_data($course, $modname, 0);
- //list($cm, $context, $module, $data, $cw) = get_moduleinfo_data($cm->get_course_module_record(), $course);
- $data->return = 0;
- $data->sr = 0;
- $data->add = $modname;
-
- // Initialise the form but discard all JS requirements it adds, our form has already added them.
- $mformclassname = 'mod_'.$modname.'_mod_form';
- if (!defined('PHPUNIT_TEST') || !PHPUNIT_TEST) {
- $PAGE->start_collecting_javascript_requirements();
- }
- $this->_moduleform = new $mformclassname($data, 0, $cmrec, $course);
- if (!defined('PHPUNIT_TEST') || !PHPUNIT_TEST) {
- $PAGE->end_collecting_javascript_requirements();
- }
-
- return $this->_moduleform;
- }
-
- /**
- * If all selected modules are of the same module type, adds custom completion rules from this module type
- *
- * @return array
- */
- protected function add_custom_completion_rules() {
- $modnames = array_keys($this->get_module_names());
- if (count($modnames) != 1 || !plugin_supports('mod', $modnames[0], FEATURE_COMPLETION_HAS_RULES, false)) {
- return [];
- }
-
- 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;
- }
- return $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 [];
- }
- }
-
- /**
- * Checks if at least one of the custom completion rules is enabled
- *
- * @param array $data Input data (not yet validated)
- * @return bool True if one or more rules is enabled, false if none are;
- * default returns false
- */
- protected function completion_rule_enabled($data) {
- if ($this->hascustomrules) {
- return $this->get_module_form()->completion_rule_enabled($data);
- }
- return false;
- }
-
- /**
- * 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).
- *
- * @return array
- */
- protected function get_modules_with_hidden_rules() {
- $modnames = $this->get_module_names();
- if (count($modnames) <= 1) {
- // No rules definitions conflicts if there is only one module type.
- return [];
- }
-
- $conflicts = [];
-
- if (!$this->support_views()) {
- // If we don't display views rule but at least one module supports it - we have conflicts.
- foreach ($modnames as $modname => $modfullname) {
- if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
- $conflicts[$modname] = $modfullname;
- }
- }
- }
-
- if (!$this->support_grades()) {
- // If we don't display grade rule but at least one module supports it - we have conflicts.
- foreach ($modnames as $modname => $modfullname) {
- if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
- $conflicts[$modname] = $modfullname;
- }
- }
- }
-
- foreach ($modnames as $modname => $modfullname) {
- // We do not display any custom completion rules, find modules that define them and add to conflicts list.
- if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_HAS_RULES, false)) {
- $conflicts[$modname] = $modfullname;
- }
- }
-
- return $conflicts;
- }
-
/**
* Form definition
*/
public function definition() {
$this->cms = $this->_customdata['cms'];
$cm = reset($this->cms); // First selected course module.
+ $this->course = $cm->get_course();
$mform = $this->_form;
- $mform->addElement('hidden', 'id', $cm->course);
- $mform->setType('id', PARAM_INT);
foreach ($this->cms as $cm) {
$mform->addElement('hidden', 'cmid['.$cm->id.']', $cm->id);
$mform->setType('cmid['.$cm->id.']', PARAM_INT);
}
- // Unlock completion automatically (this element can be used in validation).
- $mform->addElement('hidden', 'completionunlocked', 1);
- $mform->setType('completionunlocked', PARAM_INT);
-
- $mform->addElement('select', 'completion', get_string('completion', 'completion'),
- array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'),
- COMPLETION_TRACKING_MANUAL=>get_string('completion_manual', 'completion')));
- $mform->addHelpButton('completion', 'completion', 'completion');
- $mform->setDefault('completion', COMPLETION_TRACKING_NONE);
-
- // Automatic completion once you view it
- $autocompletionpossible = false;
- if ($this->support_views()) {
- $mform->addElement('advcheckbox', 'completionview', get_string('completionview', 'completion'),
- get_string('completionview_desc', 'completion'));
- $mform->disabledIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
- $autocompletionpossible = true;
- }
-
- // Automatic completion once it's graded
- if ($this->support_grades()) {
- $mform->addElement('advcheckbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
- get_string('completionusegrade_desc', 'completion'));
- $mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
- $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
- $autocompletionpossible = true;
- }
-
- // Automatic completion according to module-specific rules
- foreach ($this->add_custom_completion_rules() as $element) {
- $mform->disabledIf($element, 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
- $autocompletionpossible = true;
- }
-
- // Automatic option only appears if possible
- if ($autocompletionpossible) {
- $mform->getElement('completion')->addOption(
- get_string('completion_automatic', 'completion'),
- COMPLETION_TRACKING_AUTOMATIC);
- }
-
- // Completion expected at particular date? (For progress tracking)
- $mform->addElement('date_selector', 'completionexpected', get_string('completionexpected', 'completion'), ['optional' => true]);
- $mform->addHelpButton('completionexpected', 'completionexpected', 'completion');
- $mform->disabledIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE);
-
- if ($conflicts = $this->get_modules_with_hidden_rules()) {
- $mform->addElement('static', 'qwerty', '', get_string('hiddenrules', 'completion', join(', ', $conflicts)));
- }
-
- $this->add_action_buttons();
+ parent::definition();
$modform = $this->get_module_form();
if ($modform) {
// Pre-fill the form with the current completion rules of the first selected module.
- list($cmrec, $context, $module, $data, $cw) = get_moduleinfo_data($cm->get_course_module_record(), $cm->get_course());
+ list($cmrec, $context, $module, $data, $cw) = get_moduleinfo_data($cm->get_course_module_record(), $this->course);
$data = (array)$data;
$modform->data_preprocessing($data);
// Unset fields that will conflict with this form and set data to this form.
@@ -294,52 +82,4 @@ class core_completion_bulkedit_form extends moodleform {
$this->set_data($data);
}
}
-
- /**
- * Form validation
- *
- * @param array $data array of ("fieldname"=>value) of submitted data
- * @param array $files array of uploaded files "element_name"=>tmp_file_path
- * @return array of "element_name"=>"error_description" if there are errors,
- * or an empty array if everything is OK (true allowed for backwards compatibility too).
- */
- public function validation($data, $files) {
- $errors = parent::validation($data, $files);
-
- // Completion: Don't let them choose automatic completion without turning
- // on some conditions.
- if (array_key_exists('completion', $data) &&
- $data['completion'] == COMPLETION_TRACKING_AUTOMATIC) {
- if (empty($data['completionview']) && empty($data['completionusegrade']) &&
- !$this->completion_rule_enabled($data)) {
- $errors['completion'] = get_string('badautocompletion', 'completion');
- }
- }
-
- return $errors;
- }
-
- /**
- * Returns if this form has custom completion rules. This is only possible if all selected modules have the same
- * module type and this module type supports custom completion rules
- *
- * @return bool
- */
- public function has_custom_completion_rules() {
- return $this->hascustomrules;
- }
-
- /**
- * Return submitted data if properly submitted or returns NULL if validation fails or
- * if there is no submitted data.
- *
- * @return object submitted data; NULL if not valid or not submitted or cancelled
- */
- public function get_data() {
- $data = parent::get_data();
- if ($data && $this->hascustomrules) {
- $this->get_module_form()->data_postprocessing($data);
- }
- return $data;
- }
}
\ No newline at end of file
diff --git a/completion/classes/defaultedit_form.php b/completion/classes/defaultedit_form.php
new file mode 100644
index 00000000000..7a7b5547f33
--- /dev/null
+++ b/completion/classes/defaultedit_form.php
@@ -0,0 +1,87 @@
+.
+
+/**
+ * Default activity completion form
+ *
+ * @package core_completion
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Default activity completion form
+ *
+ * @package core_completion
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_completion_defaultedit_form extends core_completion_edit_base_form {
+ /** @var array */
+ protected $modules;
+ /** @var array */
+ protected $_modnames;
+
+ /**
+ * Returns list of types of selected modules
+ *
+ * @return array modname=>modfullname
+ */
+ protected function get_module_names() {
+ if ($this->_modnames !== null) {
+ return $this->_modnames;
+ }
+ $this->_modnames = [];
+ foreach ($this->modules as $module) {
+ $this->_modnames[$module->name] = $module->formattedname;
+ }
+ return $this->_modnames;
+ }
+
+ /**
+ * Form definition,
+ */
+ public function definition() {
+ $this->course = $this->_customdata['course'];
+ $this->modules = $this->_customdata['modules'];
+
+ $mform = $this->_form;
+
+ foreach ($this->modules as $modid => $module) {
+ $mform->addElement('hidden', 'modids['.$modid.']', $modid);
+ $mform->setType('modids['.$modid.']', PARAM_INT);
+ }
+
+ parent::definition();
+
+ $modform = $this->get_module_form();
+ if ($modform) {
+ $modnames = array_keys($this->get_module_names());
+ $modname = $modnames[0];
+ // Pre-fill the form with the current completion rules of the first selected module type.
+ list($module, $context, $cw, $cmrec, $data) = prepare_new_moduleinfo_data($this->course, $modname, 0);
+ $data = (array)$data;
+ $modform->data_preprocessing($data);
+ // Unset fields that will conflict with this form and set data to this form.
+ unset($data['cmid']);
+ unset($data['modids']);
+ unset($data['id']);
+ $this->set_data($data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/completion/classes/edit_base_form.php b/completion/classes/edit_base_form.php
new file mode 100644
index 00000000000..34e29ddcd6b
--- /dev/null
+++ b/completion/classes/edit_base_form.php
@@ -0,0 +1,316 @@
+.
+
+/**
+ * Base form for changing completion rules
+ *
+ * @package core_completion
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->dirroot.'/course/modlib.php');
+
+/**
+ * Base form for changing completion rules. Used in bulk editing activity completion and editing default activity completion
+ *
+ * @package core_completion
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class core_completion_edit_base_form extends moodleform {
+ /** @var moodleform_mod Do not use directly, call $this->get_module_form() */
+ protected $_moduleform = null;
+ /** @var bool */
+ protected $hascustomrules = false;
+ /** @var stdClass */
+ protected $course;
+
+ /**
+ * Returns list of types of selected module types
+ *
+ * @return array modname=>modfullname
+ */
+ abstract protected function get_module_names();
+
+ /**
+ * Returns true if all selected modules support tracking view.
+ *
+ * @return bool
+ */
+ protected function support_views() {
+ foreach ($this->get_module_names() as $modname => $modfullname) {
+ if (!plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if all selected modules support grading.
+ *
+ * @return bool
+ */
+ protected function support_grades() {
+ foreach ($this->get_module_names() as $modname => $modfullname) {
+ if (!plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns an instance of component-specific module form for the first selected module
+ *
+ * @return moodleform_mod|null
+ */
+ protected function get_module_form() {
+ global $CFG, $PAGE;
+
+ if ($this->_moduleform) {
+ return $this->_moduleform;
+ }
+
+ $modnames = array_keys($this->get_module_names());
+ $modname = $modnames[0];
+ $course = $this->course;
+
+ $modmoodleform = "$CFG->dirroot/mod/$modname/mod_form.php";
+ if (file_exists($modmoodleform)) {
+ require_once($modmoodleform);
+ } else {
+ print_error('noformdesc');
+ }
+
+ list($module, $context, $cw, $cmrec, $data) = prepare_new_moduleinfo_data($course, $modname, 0);
+ $data->return = 0;
+ $data->sr = 0;
+ $data->add = $modname;
+
+ // Initialise the form but discard all JS requirements it adds, our form has already added them.
+ $mformclassname = 'mod_'.$modname.'_mod_form';
+ if (!defined('PHPUNIT_TEST') || !PHPUNIT_TEST) {
+ $PAGE->start_collecting_javascript_requirements();
+ }
+ $this->_moduleform = new $mformclassname($data, 0, $cmrec, $course);
+ if (!defined('PHPUNIT_TEST') || !PHPUNIT_TEST) {
+ $PAGE->end_collecting_javascript_requirements();
+ }
+
+ return $this->_moduleform;
+ }
+
+ /**
+ * If all selected modules are of the same module type, adds custom completion rules from this module type
+ *
+ * @return array
+ */
+ protected function add_custom_completion_rules() {
+ $modnames = array_keys($this->get_module_names());
+ if (count($modnames) != 1 || !plugin_supports('mod', $modnames[0], FEATURE_COMPLETION_HAS_RULES, false)) {
+ return [];
+ }
+
+ 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;
+ }
+ return $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 [];
+ }
+ }
+
+ /**
+ * Checks if at least one of the custom completion rules is enabled
+ *
+ * @param array $data Input data (not yet validated)
+ * @return bool True if one or more rules is enabled, false if none are;
+ * default returns false
+ */
+ protected function completion_rule_enabled($data) {
+ if ($this->hascustomrules) {
+ return $this->get_module_form()->completion_rule_enabled($data);
+ }
+ return false;
+ }
+
+ /**
+ * 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).
+ *
+ * @return array
+ */
+ protected function get_modules_with_hidden_rules() {
+ $modnames = $this->get_module_names();
+ if (count($modnames) <= 1) {
+ // No rules definitions conflicts if there is only one module type.
+ return [];
+ }
+
+ $conflicts = [];
+
+ if (!$this->support_views()) {
+ // If we don't display views rule but at least one module supports it - we have conflicts.
+ foreach ($modnames as $modname => $modfullname) {
+ if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
+ $conflicts[$modname] = $modfullname;
+ }
+ }
+ }
+
+ if (!$this->support_grades()) {
+ // If we don't display grade rule but at least one module supports it - we have conflicts.
+ foreach ($modnames as $modname => $modfullname) {
+ if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_GRADE_HAS_GRADE, false)) {
+ $conflicts[$modname] = $modfullname;
+ }
+ }
+ }
+
+ foreach ($modnames as $modname => $modfullname) {
+ // We do not display any custom completion rules, find modules that define them and add to conflicts list.
+ if (empty($conflicts[$modname]) && plugin_supports('mod', $modname, FEATURE_COMPLETION_HAS_RULES, false)) {
+ $conflicts[$modname] = $modfullname;
+ }
+ }
+
+ return $conflicts;
+ }
+
+ /**
+ * Form definition
+ */
+ public function definition() {
+ $mform = $this->_form;
+
+ // Course id.
+ $mform->addElement('hidden', 'id', $this->course->id);
+ $mform->setType('id', PARAM_INT);
+
+ // Unlock completion automatically (this element can be used in validation).
+ $mform->addElement('hidden', 'completionunlocked', 1);
+ $mform->setType('completionunlocked', PARAM_INT);
+
+ $mform->addElement('select', 'completion', get_string('completion', 'completion'),
+ array(COMPLETION_TRACKING_NONE => get_string('completion_none', 'completion'),
+ COMPLETION_TRACKING_MANUAL => get_string('completion_manual', 'completion')));
+ $mform->addHelpButton('completion', 'completion', 'completion');
+ $mform->setDefault('completion', COMPLETION_TRACKING_NONE);
+
+ // Automatic completion once you view it.
+ $autocompletionpossible = false;
+ if ($this->support_views()) {
+ $mform->addElement('advcheckbox', 'completionview', get_string('completionview', 'completion'),
+ get_string('completionview_desc', 'completion'));
+ $mform->disabledIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $autocompletionpossible = true;
+ }
+
+ // Automatic completion once it's graded.
+ if ($this->support_grades()) {
+ $mform->addElement('advcheckbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
+ get_string('completionusegrade_desc', 'completion'));
+ $mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
+ $autocompletionpossible = true;
+ }
+
+ // Automatic completion according to module-specific rules.
+ foreach ($this->add_custom_completion_rules() as $element) {
+ $mform->disabledIf($element, 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $autocompletionpossible = true;
+ }
+
+ // Automatic option only appears if possible.
+ if ($autocompletionpossible) {
+ $mform->getElement('completion')->addOption(
+ get_string('completion_automatic', 'completion'),
+ COMPLETION_TRACKING_AUTOMATIC);
+ }
+
+ // Completion expected at particular date? (For progress tracking).
+ $mform->addElement('date_selector', 'completionexpected',
+ get_string('completionexpected', 'completion'), ['optional' => true]);
+ $mform->addHelpButton('completionexpected', 'completionexpected', 'completion');
+ $mform->disabledIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE);
+
+ if ($conflicts = $this->get_modules_with_hidden_rules()) {
+ $mform->addElement('static', 'qwerty', '', get_string('hiddenrules', 'completion', join(', ', $conflicts)));
+ }
+
+ $this->add_action_buttons();
+ }
+
+ /**
+ * Form validation
+ *
+ * @param array $data array of ("fieldname"=>value) of submitted data
+ * @param array $files array of uploaded files "element_name"=>tmp_file_path
+ * @return array of "element_name"=>"error_description" if there are errors,
+ * or an empty array if everything is OK (true allowed for backwards compatibility too).
+ */
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+
+ // Completion: Don't let them choose automatic completion without turning
+ // on some conditions.
+ if (array_key_exists('completion', $data) &&
+ $data['completion'] == COMPLETION_TRACKING_AUTOMATIC) {
+ if (empty($data['completionview']) && empty($data['completionusegrade']) &&
+ !$this->completion_rule_enabled($data)) {
+ $errors['completion'] = get_string('badautocompletion', 'completion');
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Returns if this form has custom completion rules. This is only possible if all selected modules have the same
+ * module type and this module type supports custom completion rules
+ *
+ * @return bool
+ */
+ public function has_custom_completion_rules() {
+ return $this->hascustomrules;
+ }
+
+ /**
+ * Return submitted data if properly submitted or returns NULL if validation fails or
+ * if there is no submitted data.
+ *
+ * @return object submitted data; NULL if not valid or not submitted or cancelled
+ */
+ public function get_data() {
+ $data = parent::get_data();
+ if ($data && $this->hascustomrules) {
+ $this->get_module_form()->data_postprocessing($data);
+ }
+ return $data;
+ }
+}
\ No newline at end of file
diff --git a/completion/classes/manager.php b/completion/classes/manager.php
index 5e30a660d5e..20c43d48fd0 100644
--- a/completion/classes/manager.php
+++ b/completion/classes/manager.php
@@ -28,6 +28,7 @@ namespace core_completion;
use stdClass;
use context_course;
+use cm_info;
/**
* Bulk activity completion manager class
@@ -65,8 +66,7 @@ class manager {
$sectionobject = new stdClass();
$sectionobject->sectionnumber = $sectionnumber;
$sectionobject->name = get_section_name($this->courseid, $sectioninfo);
- $activitiesdata = $this->get_activities($section, true);
- $sectionobject->activities = $activitiesdata->activities;
+ $sectionobject->activities = $this->get_activities($section, true);
$data->sections[] = $sectionobject;
}
return $data;
@@ -77,7 +77,7 @@ class manager {
*
* @param array $cmids list of course module ids
* @param bool $withcompletiondetails include completion details
- * @return \stdClass
+ * @return array
*/
public function get_activities($cmids, $withcompletiondetails = false) {
$moduleinfo = get_fast_modinfo($this->courseid);
@@ -96,34 +96,43 @@ class manager {
// Get activity completion information.
if ($moduleobject->canmanage) {
- $moduleobject->completionstatus = $this->get_completion_detail($mod); // This is a placeholder only. Must be replaced later.
+ $moduleobject->completionstatus = $this->get_completion_detail($mod);
} else {
$moduleobject->completionstatus = ['icon' => null, 'string' => null];
}
$activities[] = $moduleobject;
}
- return (object)['activities' => $activities];
+ return $activities;
}
- private function get_completion_detail(\cm_info $mod) {
+
+ /**
+ * Get completion information on the selected module or module type
+ *
+ * @param cm_info|stdClass $mod either instance of cm_info (with 'customcompletionrules' in customdata) or
+ * object with fields ->completion, ->completionview, ->completionexpected, ->completionusegrade
+ * and ->customdata['customcompletionrules']
+ * @return array
+ */
+ private function get_completion_detail($mod) {
global $OUTPUT;
$strings = [];
switch ($mod->completion) {
- case 0:
+ case COMPLETION_TRACKING_NONE:
$strings['string'] = get_string('none');
break;
- case 1:
+ case COMPLETION_TRACKING_MANUAL:
$strings['string'] = get_string('manual');
$strings['icon'] = $OUTPUT->pix_url('i/completion-manual-enabled')->out();
break;
- case 2:
+ case COMPLETION_TRACKING_AUTOMATIC:
$strings['string'] = get_string('withconditions');
// Get the descriptions for all the active completion rules for the module.
- if ($ruledescriptions = $mod->get_completion_active_rule_descriptions()) {
+ if ($ruledescriptions = $this->get_completion_active_rule_descriptions($mod)) {
foreach ($ruledescriptions as $ruledescription) {
$strings['string'] .= \html_writer::empty_tag('br') . $ruledescription;
}
@@ -139,8 +148,44 @@ class manager {
return $strings;
}
+ /**
+ * Get the descriptions for all active conditional completion rules for the current module.
+ *
+ * @param cm_info|stdClass $moduledata either instance of cm_info (with 'customcompletionrules' in customdata) or
+ * object with fields ->completion, ->completionview, ->completionexpected, ->completionusegrade
+ * and ->customdata['customcompletionrules']
+ * @return array $activeruledescriptions an array of strings describing the active completion rules.
+ */
+ protected function get_completion_active_rule_descriptions($moduledata) {
+ $activeruledescriptions = [];
+
+ // Generate the description strings for the core conditional completion rules (if set).
+ if (!empty($moduledata->completionview)) {
+ $activeruledescriptions[] = get_string('completionview_desc', 'core_completion');
+ }
+ if ($moduledata instanceof cm_info && !is_null($moduledata->completiongradeitemnumber) ||
+ ($moduledata instanceof stdClass && !empty($moduledata->completionusegrade))) {
+ $activeruledescriptions[] = get_string('completionusegrade_desc', 'core_completion');
+ }
+
+ // Now, ask the module to provide descriptions for its custom conditional completion rules.
+ if ($customruledescriptions = component_callback($moduledata->modname,
+ 'get_completion_active_rule_descriptions', [$moduledata])) {
+ $activeruledescriptions = array_merge($activeruledescriptions, $customruledescriptions);
+ }
+
+ if (!empty($moduledata->completionexpected)) {
+ $activeruledescriptions[] = get_string('completionexpecteddesc', 'core_completion',
+ userdate($moduledata->completionexpected));
+ }
+
+ return $activeruledescriptions;
+ }
+
public function get_activities_and_resources() {
- global $DB, $OUTPUT;
+ global $DB, $OUTPUT, $CFG;
+ require_once($CFG->dirroot.'/course/lib.php');
+
// Get enabled activities and resources.
$modules = $DB->get_records('modules', ['visible' => 1], 'name ASC');
$data = new stdClass();
@@ -150,9 +195,16 @@ class manager {
// Add icon information.
$data->modules = array_values($modules);
$coursecontext = context_course::instance($this->courseid);
+ $canmanage = has_capability('moodle/course:manageactivities', $coursecontext);
+ $course = get_course($this->courseid);
foreach ($data->modules as $module) {
$module->icon = $OUTPUT->pix_url('icon', $module->name)->out();
- $module->formatedname = format_string(get_string('pluginname', 'mod_' . $module->name), true, ['context' => $coursecontext]);
+ $module->formattedname = format_string(get_string('modulenameplural', 'mod_' . $module->name),
+ true, ['context' => $coursecontext]);
+ $module->canmanage = $canmanage && \course_allowed_module($course, $module->name);
+ $defaults = self::get_default_completion($course, $module, false);
+ $defaults->modname = $module->name;
+ $module->completionstatus = $this->get_completion_detail($defaults);
}
return $data;
@@ -186,7 +238,7 @@ class manager {
* Applies completion from the bulk edit form to all selected modules
*
* @param stdClass $data data received from the core_completion_bulkedit_form
- * @param bool $updateinstance if we need to update the instance tables of the module (i.e. 'assign', 'forum', etc.) -
+ * @param bool $updateinstances if we need to update the instance tables of the module (i.e. 'assign', 'forum', etc.) -
* if no module-specific completion rules were added to the form, update of the module table is not needed.
*/
public function apply_completion($data, $updateinstances) {
@@ -267,4 +319,93 @@ class manager {
return true;
}
+
+ /**
+ * Saves default completion from edit form to all selected module types
+ *
+ * @param stdClass $data data received from the core_completion_bulkedit_form
+ * @param bool $updatecustomrules if we need to update the custom rules of the module -
+ * if no module-specific completion rules were added to the form, update of the module table is not needed.
+ */
+ public function apply_default_completion($data, $updatecustomrules) {
+ global $DB;
+
+ $courseid = $data->id;
+ $coursecontext = context_course::instance($courseid);
+ if (!$modids = $data->modids) {
+ return;
+ }
+ $defaults = ['completion' => COMPLETION_DISABLED, 'completionview' => COMPLETION_VIEW_NOT_REQUIRED,
+ 'completionexpected' => 0, 'completionusegrade' => 0];
+
+ $data = (array)$data;
+
+ if ($updatecustomrules) {
+ $customdata = array_diff_key($data, $defaults);
+ $data['customrules'] = $customdata ? json_encode($customdata) : null;
+ $defaults['customrules'] = null;
+ }
+ $data = array_intersect_key($data, $defaults);
+
+ // Get names of the affected modules.
+ list($modidssql, $params) = $DB->get_in_or_equal($modids);
+ $params[] = 1;
+ $modules = $DB->get_records_select_menu('modules', 'id ' . $modidssql . ' and visible = ?', $params, '', 'id, name');
+
+ foreach ($modids as $modid) {
+ if (!array_key_exists($modid, $modules)) {
+ continue;
+ }
+ if ($defaultsid = $DB->get_field('course_completion_defaults', 'id', ['course' => $courseid, 'module' => $modid])) {
+ $DB->update_record('course_completion_defaults', $data + ['id' => $defaultsid]);
+ } else {
+ $defaultsid = $DB->insert_record('course_completion_defaults', $data + ['course' => $courseid, 'module' => $modid]);
+ }
+ // Trigger event.
+ \core\event\completion_defaults_updated::create([
+ 'objectid' => $defaultsid,
+ 'context' => $coursecontext,
+ 'other' => ['modulename' => $modules[$modid]],
+ ])->trigger();
+ // Add notification.
+ \core\notification::add(get_string('defaultcompletionupdated', 'completion',
+ get_string("modulenameplural", $modules[$modid])), \core\notification::SUCCESS);
+ }
+ }
+
+ /**
+ * Returns default completion rules for given module type in the given course
+ *
+ * @param stdClass $course
+ * @param stdClass $module
+ * @param bool $flatten if true all module custom completion rules become properties of the same object,
+ * otherwise they can be found as array in ->customdata['customcompletionrules']
+ * @return stdClass
+ */
+ public static function get_default_completion($course, $module, $flatten = true) {
+ global $DB, $CFG;
+ if ($data = $DB->get_record('course_completion_defaults', ['course' => $course->id, 'module' => $module->id],
+ 'completion, completionview, completionexpected, completionusegrade, customrules')) {
+ if ($data->customrules && ($customrules = @json_decode($data->customrules, true))) {
+ if ($flatten) {
+ foreach ($customrules as $key => $value) {
+ $data->$key = $value;
+ }
+ } else {
+ $data->customdata['customcompletionrules'] = $customrules;
+ }
+ }
+ unset($data->customrules);
+ } else {
+ $data = new stdClass();
+ $data->completion = COMPLETION_TRACKING_NONE;
+ if ($CFG->completiondefault) {
+ $completion = new \completion_info(get_fast_modinfo($course->id)->get_course());
+ if ($completion->is_enabled() && plugin_supports('mod', $module->name, FEATURE_MODEDIT_DEFAULT_COMPLETION, true)) {
+ $data->completion = COMPLETION_TRACKING_MANUAL;
+ }
+ }
+ }
+ return $data;
+ }
}
\ No newline at end of file
diff --git a/completion/tests/bulk_update_test.php b/completion/tests/bulk_update_test.php
index e6dff2793f7..2e660813bdf 100644
--- a/completion/tests/bulk_update_test.php
+++ b/completion/tests/bulk_update_test.php
@@ -52,7 +52,8 @@ class core_completion_bulk_update_testcase extends advanced_testcase {
'choice-2' => ['choice', ['completion' => COMPLETION_TRACKING_MANUAL]],
'data-1' => ['data', ['completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 1]],
'data-2' => ['data', ['completion' => COMPLETION_TRACKING_MANUAL]],
- 'feedback-1' => ['feedback', ['completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 0, 'completionsubmit' => 1]],
+ 'feedback-1' => ['feedback', ['completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 0,
+ 'completionsubmit' => 1]],
'feedback-2' => ['feedback', ['completion' => COMPLETION_TRACKING_MANUAL]],
'folder-1' => ['folder', ['completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 1]],
'folder-2' => ['folder', ['completion' => COMPLETION_TRACKING_MANUAL]],
@@ -210,7 +211,7 @@ class core_completion_bulk_update_testcase extends advanced_testcase {
* Use bulk completion edit for updating multiple modules
*
* @dataProvider bulk_form_submit_multiple_provider
- * @param array $data
+ * @param array $providerdata
*/
public function test_bulk_form_submit_multiple($providerdata) {
global $DB;
diff --git a/course/bulkcompletion.php b/course/bulkcompletion.php
index 2096b6f1c29..7f1c5602c1b 100644
--- a/course/bulkcompletion.php
+++ b/course/bulkcompletion.php
@@ -70,7 +70,7 @@ $renderer = $PAGE->get_renderer('core_course', 'bulk_activity_completion');
// Print the form.
echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('editcoursecompletionsettings', 'core_completion'));
+echo $OUTPUT->heading(get_string('bulkactivitycompletion', 'completion'));
echo $renderer->navigation($id, 'bulkcompletion');
diff --git a/course/classes/output/bulk_activity_completion_renderer.php b/course/classes/output/bulk_activity_completion_renderer.php
index b6ab22cb587..e35eb88308d 100644
--- a/course/classes/output/bulk_activity_completion_renderer.php
+++ b/course/classes/output/bulk_activity_completion_renderer.php
@@ -76,9 +76,45 @@ class core_course_bulk_activity_completion_renderer extends plugin_renderer_base
return parent::render_from_template('core_course/defaultactivitycompletion', $data);
}
- public function activities_list($data) {
- return html_writer::div(get_string('affectedactivities', 'completion', count($data->activities))).
- parent::render_from_template('core_course/activityinstance', $data);
+ /**
+ * Renders the form for bulk editing activities completion
+ *
+ * @param moodleform $form
+ * @param array $activities
+ * @return string
+ */
+ public function edit_bulk_completion($form, $activities) {
+ ob_start();
+ $form->display();
+ $formhtml = ob_get_contents();
+ ob_end_clean();
+
+ $data = (object)[
+ 'form' => $formhtml,
+ 'activities' => array_values($activities),
+ 'activitiescount' => count($activities),
+ ];
+ return parent::render_from_template('core_course/editbulkactivitycompletion', $data);
}
+ /**
+ * Renders the form for editing default completion
+ *
+ * @param moodleform $form
+ * @param array $modules
+ * @return string
+ */
+ public function edit_default_completion($form, $modules) {
+ ob_start();
+ $form->display();
+ $formhtml = ob_get_contents();
+ ob_end_clean();
+
+ $data = (object)[
+ 'form' => $formhtml,
+ 'modules' => array_values($modules),
+ 'modulescount' => count($modules),
+ ];
+ return parent::render_from_template('core_course/editdefaultcompletion', $data);
+ }
}
diff --git a/course/defaultcompletion.php b/course/defaultcompletion.php
index 9f595e0bd2b..f02f1b8fadd 100644
--- a/course/defaultcompletion.php
+++ b/course/defaultcompletion.php
@@ -28,9 +28,7 @@ require_once(__DIR__.'/../config.php');
require_once($CFG->dirroot.'/course/lib.php');
require_once($CFG->libdir.'/completionlib.php');
-$id = required_param('id', PARAM_INT); // course id
-// @TODO: Change this to module IDs.
-$cmids = optional_param_array('cmid', [], PARAM_INT);
+$id = required_param('id', PARAM_INT); // Course id.
// Perform some basic access control checks.
if ($id) {
@@ -67,7 +65,7 @@ $renderer = $PAGE->get_renderer('core_course', 'bulk_activity_completion');
// Print the form.
echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('editcoursecompletionsettings', 'core_completion'));
+echo $OUTPUT->heading(get_string('defaultcompletion', 'completion'));
echo $renderer->navigation($id, 'defaultcompletion');
diff --git a/course/dnduploadlib.php b/course/dnduploadlib.php
index 843ea9a9fe0..c6e2c598c0a 100644
--- a/course/dnduploadlib.php
+++ b/course/dnduploadlib.php
@@ -559,34 +559,12 @@ class dndupload_ajax_processor {
*/
protected function create_course_module() {
global $CFG;
+ require_once($CFG->dirroot.'/course/modlib.php');
+ list($module, $context, $cw, $cm, $data) =
+ prepare_new_moduleinfo_data($this->course, $this->module->name, $this->section);
- if (!course_allowed_module($this->course, $this->module->name)) {
- throw new coding_exception("The module {$this->module->name} is not allowed to be added to this course");
- }
-
- $this->cm = new stdClass();
- $this->cm->course = $this->course->id;
- $this->cm->section = $this->section;
- $this->cm->module = $this->module->id;
- $this->cm->modulename = $this->module->name;
- $this->cm->instance = 0; // This will be filled in after we create the instance.
- $this->cm->visible = 1;
- $this->cm->groupmode = $this->course->groupmode;
- $this->cm->groupingid = $this->course->defaultgroupingid;
-
- // Set the correct default for completion tracking.
- $this->cm->completion = COMPLETION_TRACKING_NONE;
- $completion = new completion_info($this->course);
- if ($completion->is_enabled() && $CFG->completiondefault) {
- if (plugin_supports('mod', $this->cm->modulename, FEATURE_MODEDIT_DEFAULT_COMPLETION, true)) {
- $this->cm->completion = COMPLETION_TRACKING_MANUAL;
- }
- }
-
- if (!$this->cm->id = add_course_module($this->cm)) {
- throw new coding_exception("Unable to create the course module");
- }
- $this->cm->coursemodule = $this->cm->id;
+ $data->coursemodule = $data->id = add_course_module($data);
+ $this->cm = $data;
}
/**
diff --git a/course/editbulkcompletion.php b/course/editbulkcompletion.php
index 259a1979134..728565cf32a 100644
--- a/course/editbulkcompletion.php
+++ b/course/editbulkcompletion.php
@@ -22,7 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-include __DIR__ . "/../config.php";
+require_once(__DIR__ . "/../config.php");
require_once($CFG->libdir . '/completionlib.php');
$courseid = required_param('id', PARAM_INT);
@@ -68,13 +68,11 @@ if ($form->is_cancelled()) {
$renderer = $PAGE->get_renderer('core_course', 'bulk_activity_completion');
echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('editcoursecompletionsettings', 'core_completion'));
+echo $OUTPUT->heading(get_string('bulkactivitycompletion', 'completion'));
echo $renderer->navigation($course->id, 'bulkcompletion');
-$form->display();
-
-echo $renderer->activities_list($manager->get_activities(array_keys($cms)));
+echo $renderer->edit_bulk_completion($form, $manager->get_activities(array_keys($cms)));
echo $OUTPUT->footer();
diff --git a/course/editdefaultcompletion.php b/course/editdefaultcompletion.php
new file mode 100644
index 00000000000..7f29bbddb29
--- /dev/null
+++ b/course/editdefaultcompletion.php
@@ -0,0 +1,75 @@
+.
+
+/**
+ * Bulk activity completion selection
+ *
+ * @package core_completion
+ * @copyright 2017 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(__DIR__ . "/../config.php");
+require_once($CFG->libdir . '/completionlib.php');
+
+$courseid = required_param('id', PARAM_INT);
+$modids = optional_param_array('modids', [], PARAM_INT);
+$course = get_course($courseid);
+require_login($course);
+
+navigation_node::override_active_url(new moodle_url('/course/completion.php', array('id' => $course->id)));
+$PAGE->set_url(new moodle_url('/course/editdefaultcompletion.php', ['id' => $courseid]));
+$PAGE->set_title($course->shortname);
+$PAGE->set_heading($course->fullname);
+$PAGE->set_pagelayout('admin');
+
+require_capability('moodle/course:update', context_course::instance($course->id));
+
+// Prepare list of selected modules.
+$manager = new \core_completion\manager($course->id);
+$allmodules = $manager->get_activities_and_resources();
+$modules = [];
+foreach ($allmodules->modules as $module) {
+ if ($module->canmanage && in_array($module->id, $modids)) {
+ $modules[$module->id] = $module;
+ }
+}
+
+$returnurl = new moodle_url('/course/defaultcompletion.php', ['id' => $course->id]);
+if (empty($modules)) {
+ redirect($returnurl);
+}
+
+$form = new core_completion_defaultedit_form(null, ['course' => $course, 'modules' => $modules]);
+
+if ($form->is_cancelled()) {
+ redirect($returnurl);
+} else if ($data = $form->get_data()) {
+ $manager->apply_default_completion($data, $form->has_custom_completion_rules());
+ redirect($returnurl);
+}
+
+$renderer = $PAGE->get_renderer('core_course', 'bulk_activity_completion');
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('defaultcompletion', 'completion'));
+
+echo $renderer->navigation($course->id, 'defaultcompletion');
+
+echo $renderer->edit_default_completion($form, $modules);
+
+echo $OUTPUT->footer();
+
diff --git a/course/modlib.php b/course/modlib.php
index 68ed661ad6a..d4cc82b3f9e 100644
--- a/course/modlib.php
+++ b/course/modlib.php
@@ -760,6 +760,12 @@ function prepare_new_moduleinfo_data($course, $modulename, $section) {
$data->instance = '';
$data->coursemodule = '';
+ // Apply completion defaults.
+ $defaults = \core_completion\manager::get_default_completion($course, $module);
+ foreach ($defaults as $key => $value) {
+ $data->$key = $value;
+ }
+
if (plugin_supports('mod', $data->modulename, FEATURE_MOD_INTRO, true)) {
$draftid_editor = file_get_submitted_draft_itemid('introeditor');
file_prepare_draft_area($draftid_editor, null, null, null, null, array('subdirs'=>true));
diff --git a/course/moodleform_mod.php b/course/moodleform_mod.php
index e37a231c3d6..c8ea479cf64 100644
--- a/course/moodleform_mod.php
+++ b/course/moodleform_mod.php
@@ -2,7 +2,7 @@
require_once ($CFG->libdir.'/formslib.php');
require_once($CFG->libdir.'/completionlib.php');
require_once($CFG->libdir.'/gradelib.php');
-require_once ($CFG->libdir.'/plagiarismlib.php');
+require_once($CFG->libdir.'/plagiarismlib.php');
/**
* This class adds extra methods to form wrapper specific to be used for module
@@ -931,7 +931,7 @@ abstract class moodleform_mod extends moodleform {
// If the 'show description' feature is enabled, this checkbox appears below the intro.
// We want to hide that when using the singleactivity course format because it is confusing.
if ($this->_features->showdescription && $this->courseformat->has_view_page()) {
- $mform->addElement('checkbox', 'showdescription', get_string('showdescription'));
+ $mform->addElement('advcheckbox', 'showdescription', get_string('showdescription'));
$mform->addHelpButton('showdescription', 'showdescription');
}
}
diff --git a/course/templates/activityinstance.mustache b/course/templates/activityinstance.mustache
index 326e9245c10..878dbfd7c66 100644
--- a/course/templates/activityinstance.mustache
+++ b/course/templates/activityinstance.mustache
@@ -24,20 +24,10 @@
"activities": [{
"cmid": "4",
"modname": "Test activity",
- "icon": {
- "attributes": [
- {"name": "src", "value": "https://raw.githubusercontent.com/moodle/moodle/master/pix/t/check.png"},
- {"name": "alt", "value": "Activity icon"}
- ]
- },
+ "icon": "https://raw.githubusercontent.com/moodle/moodle/master/mod/feedback/pix/icon.png",
"completionstatus": {
"string": "Manual",
- "icon": {
- "attributes": [
- {"name": "src", "value": "https://raw.githubusercontent.com/moodle/moodle/master/pix/t/check.png"},
- {"name": "alt", "value": "Completion icon"}
- ]
- }
+ "icon": "https://raw.githubusercontent.com/moodle/moodle/master/pix/i/completion-manual-enabled.png"
}
}]
}
@@ -50,7 +40,7 @@
{{#canmanage}}
{{/canmanage}}
-
+
{{modname}}
diff --git a/course/templates/bulkactivitycompletion.mustache b/course/templates/bulkactivitycompletion.mustache
index 410827f49e4..33937a021d4 100644
--- a/course/templates/bulkactivitycompletion.mustache
+++ b/course/templates/bulkactivitycompletion.mustache
@@ -29,20 +29,10 @@
"activities": [{
"cmid": "4",
"modname": "Test activity",
- "icon": {
- "attributes": [
- {"name": "src", "value": "https://raw.githubusercontent.com/moodle/moodle/master/pix/t/check.png"},
- {"name": "alt", "value": "module icon"}
- ]
- },
+ "icon": "https://raw.githubusercontent.com/moodle/moodle/master/mod/feedback/pix/icon.png",
"completionstatus": {
"string": "Manual",
- "icon": {
- "attributes": [
- {"name": "src", "value": "https://raw.githubusercontent.com/moodle/moodle/master/pix/t/check.png"},
- {"name": "alt", "value": "completion icon"}
- ]
- }
+ "icon": "https://raw.githubusercontent.com/moodle/moodle/master/pix/i/completion-manual-enabled.png"
}
}]
}]
diff --git a/course/templates/defaultactivitycompletion.mustache b/course/templates/defaultactivitycompletion.mustache
index 0e12248bcdd..d4985ba9ce6 100644
--- a/course/templates/defaultactivitycompletion.mustache
+++ b/course/templates/defaultactivitycompletion.mustache
@@ -25,12 +25,12 @@
"sesskey": "AAAAAA",
"modules": [{
"id": "10",
- "formatedname": "Assignment",
- "icon": {
- "attributes": [
- {"name": "src", "value": "https://raw.githubusercontent.com/moodle/moodle/master/pix/t/check.png"},
- {"name": "alt", "value": "Assignment icon"}
- ]
+ "formattedname": "Assignment",
+ "canmanage": true,
+ "icon": "https://raw.githubusercontent.com/moodle/moodle/master/mod/assign/pix/icon.png",
+ "completionstatus": {
+ "string": "Manual",
+ "icon": "https://raw.githubusercontent.com/moodle/moodle/master/pix/i/completion-manual-enabled.png"
}
}]
}
@@ -39,7 +39,7 @@
{{#str}}bulkactivitydetail, moodle{{/str}}
-