Even though I've tweaked this and done some polishing, there is still plenty to do on it now in HEAD, mostly to do with UI, usability and strings, so we'll keep working on it. Aaron already has a number of improvements to add to it.
events_cron();
mtrace('done.');
+
+ if ($CFG->enablecompletion) {
+ // Completion cron
+ mtrace('Starting the completion cron...');
+ require_once($CFG->libdir . '/completion/cron.php');
+ completion_cron();
+ mtrace('done');
+ }
+
+
if ($CFG->enableportfolios) {
// Portfolio cron
mtrace('Starting the portfolio cron...');
$languages += get_string_manager()->get_list_of_translations();
$temp->add(new admin_setting_configselect('moodlecourse/lang', get_string('forcelanguage'), '',key($languages),$languages));
- if (!empty($CFG->enablecompletion)) {
- $temp->add(new admin_setting_heading('progress', get_string('progress','completion'), ''));
- $temp->add(new admin_setting_configselect('moodlecourse/enablecompletion', get_string('completion','completion'), '',
- 1, array(0 => get_string('completiondisabled','completion'), 1 => get_string('completionenabled','completion'))));
- }
+ $temp->add(new admin_setting_heading('progress', get_string('progress','completion'), ''));
+ $temp->add(new admin_setting_configselect('moodlecourse/enablecompletion', get_string('completion','completion'), '',
+ 1, array(0 => get_string('completiondisabled','completion'), 1 => get_string('completionenabled','completion'))));
+
+ $temp->add(new admin_setting_configcheckbox('moodlecourse/completionstartonenrol', get_string('completionstartonenrol','completion'), get_string('completionstartonenrolhelp', 'completion'), 1));
$ADMIN->add('courses', $temp);
/// "courserequests" settingpage
--- /dev/null
+<?php
+
+// 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/>.
+
+
+/**
+ * Block for displayed logged in user's course completion status
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once($CFG->libdir.'/completionlib.php');
+
+/**
+ * Course completion status
+ * Displays overall, and individual criteria status for logged in user
+ */
+class block_completionstatus extends block_base {
+
+ public function init() {
+ $this->title = get_string('completionstatus', 'block_completionstatus');
+ $this->version = 2009072800;
+ }
+
+ public function get_content() {
+ global $USER, $CFG, $DB, $COURSE;
+
+ // If content is cached
+ if ($this->content !== NULL) {
+ return $this->content;
+ }
+
+ // Create empty content
+ $this->content = new stdClass;
+
+ // Don't display if completion isn't enabled!
+ if (!$this->page->course->enablecompletion) {
+ return $this->content;
+ }
+
+ // Load criteria to display
+ $info = new completion_info($this->page->course);
+ $completions = $info->get_completions($USER->id);
+
+ // Check if this course has any criteria
+ if (empty($completions)) {
+ return $this->content;
+ }
+
+ // Check this user is enroled
+ $users = $info->internal_get_tracked_users(true);
+ if (!in_array($USER->id, array_keys($users))) {
+
+ // If not enrolled, but are can view the report:
+ if (has_capability('coursereport/completion:view', get_context_instance(CONTEXT_COURSE, $COURSE->id))) {
+ $this->content->text = '<a href="'.$CFG->wwwroot.'/course/report/completion/index.php?course='.$COURSE->id.
+ '">'.get_string('viewcoursereport', 'completion').'</a>';
+ return $this->content;
+ }
+
+ // Otherwise, show error
+ $this->content->text = get_string('notenroled', 'completion');
+ return $this->content;
+ }
+
+ // Generate markup for criteria statuses
+ $shtml = '';
+
+ // For aggregating activity completion
+ $activities = array();
+ $activities_complete = 0;
+
+ // For aggregating course prerequisites
+ $prerequisites = array();
+ $prerequisites_complete = 0;
+
+ // Flag to set if current completion data is inconsistent with
+ // what is stored in the database
+ $pending_update = false;
+
+ // Loop through course criteria
+ foreach ($completions as $completion) {
+
+ $criteria = $completion->get_criteria();
+ $complete = $completion->is_complete();
+
+ if (!$pending_update && $criteria->is_pending($completion)) {
+ $pending_update = true;
+ }
+
+ // Activities are a special case, so cache them and leave them till last
+ if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
+ $activities[$criteria->moduleinstance] = $complete;
+
+ if ($complete) {
+ $activities_complete++;
+ }
+
+ continue;
+ }
+
+ // Prerequisites are also a special case, so cache them and leave them till last
+ if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
+ $prerequisites[$criteria->courseinstance] = $complete;
+
+ if ($complete) {
+ $prerequisites_complete++;
+ }
+
+ continue;
+ }
+
+ $shtml .= '<tr><td>';
+ $shtml .= $criteria->get_title();
+ $shtml .= '</td><td style="text-align: right">';
+ $shtml .= $completion->get_status();
+ $shtml .= '</td></tr>';
+ }
+
+ // Aggregate activities
+ if (!empty($activities)) {
+
+ $shtml .= '<tr><td>';
+ $shtml .= get_string('activitiescompleted', 'completion');
+ $shtml .= '</td><td style="text-align: right">';
+ $shtml .= $activities_complete.' of '.count($activities);
+ $shtml .= '</td></tr>';
+ }
+
+ // Aggregate prerequisites
+ if (!empty($prerequisites)) {
+
+ $phtml = '<tr><td>';
+ $phtml .= get_string('prerequisitescompleted', 'completion');
+ $phtml .= '</td><td style="text-align: right">';
+ $phtml .= $prerequisites_complete.' of '.count($prerequisites);
+ $phtml .= '</td></tr>';
+
+ $shtml = $phtml . $shtml;
+ }
+
+ // Display completion status
+ $this->content->text = '<table width="100%" style="font-size: 90%;"><tbody>';
+ $this->content->text .= '<tr><td colspan="2"><b>'.get_string('status').':</b> ';
+
+ // Is course complete?
+ $coursecomplete = $info->is_course_complete($USER->id);
+
+ // Has this user completed any criteria?
+ $criteriacomplete = $info->count_course_user_data($USER->id);
+
+ if ($pending_update) {
+ $this->content->text .= '<i>'.get_string('pending', 'completion').'</i>';
+ } else if ($coursecomplete) {
+ $this->content->text .= get_string('complete');
+ } else if (!$criteriacomplete) {
+ $this->content->text .= '<i>'.get_string('notyetstarted', 'completion').'</i>';
+ } else {
+ $this->content->text .= '<i>'.get_string('inprogress','completion').'</i>';
+ }
+
+ $this->content->text .= '</td></tr>';
+ $this->content->text .= '<tr><td colspan="2">';
+
+ // Get overall aggregation method
+ $overall = $info->get_aggregation_method();
+
+ if ($overall == COMPLETION_AGGREGATION_ALL) {
+ $this->content->text .= get_string('criteriarequiredall', 'completion');
+ } else {
+ $this->content->text .= get_string('criteriarequiredany', 'completion');
+ }
+
+ $this->content->text .= ':</td></tr>';
+ $this->content->text .= '<tr><td><b>'.get_string('requiredcriteria', 'completion').'</b></td><td style="text-align: right"><b>'.get_string('status').'</b></td></tr>';
+ $this->content->text .= $shtml.'</tbody></table>';
+
+ // Display link to detailed view
+ $this->content->footer = '<br><a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$COURSE->id.'">More details</a>';
+
+ return $this->content;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+
+/**
+ * Block for displayed logged in user's course completion status
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once('../../config.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+
+// TODO: Make this page Moodle 2.0 compliant
+
+
+///
+/// Load data
+///
+$id = required_param('course', PARAM_INT);
+// User id
+$userid = optional_param('user', 0, PARAM_INT);
+
+// Load course
+$course = $DB->get_record('course', array('id' => $id));
+
+// Load user
+if ($userid) {
+ if (!$user = get_record('user', 'id', $userid)) {
+ error('User ID incorrect');
+ }
+} else {
+ $user =& $USER;
+}
+
+
+// Check permissions
+require_login($course);
+
+$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+$personalcontext = get_context_instance(CONTEXT_USER, $user->id);
+
+$can_view = false;
+
+// Can view own report
+if ($USER->id == $user->id) {
+ $can_view = true;
+}
+elseif (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
+ $can_view = true;
+}
+elseif (has_capability('coursereport/completion:view', $coursecontext)) {
+ $can_view = true;
+}
+elseif (has_capability('coursereport/completion:view', $personalcontext)) {
+ $can_view = true;
+}
+
+if (!$can_view) {
+ error('You do not have permissions to view this report');
+}
+
+
+// Don't display if completion isn't enabled!
+if (!$course->enablecompletion) {
+ error('completion not enabled');
+}
+
+// Load criteria to display
+$info = new completion_info($course);
+$completions = $info->get_completions($user->id);
+
+// Check if this course has any criteria
+if (empty($completions)) {
+ error('no criteria');
+}
+
+// Check this user is enroled
+$users = $info->internal_get_tracked_users(true);
+if (!in_array($user->id, array_keys($users))) {
+ error(get_string('notenroled', 'completion'));
+}
+
+
+///
+/// Display page
+///
+
+// Print header
+$page = get_string('completionprogressdetails', 'block_completionstatus');
+$title = format_string($course->fullname) . ': ' . $page;
+
+$navlinks[] = array('name' => $page, 'link' => null, 'type' => 'misc');
+$navigation = build_navigation($navlinks);
+
+$PAGE->set_url('/blocks/completionstatus/details.php', array('course' => $course->id));
+$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_heading($title);
+echo $OUTPUT->header();
+
+
+// Display completion status
+echo '<table class="generalbox boxaligncenter"><tbody>';
+
+// If not display logged in user, show user name
+if ($USER->id != $user->id) {
+ echo '<tr><td colspan="2"><b>'.get_string('showinguser', 'completion').'</b>: ';
+ echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&course='.$course->id.'">'.fullname($user).'</a>';
+ echo '</td></tr>';
+}
+
+echo '<tr><td colspan="2"><b>'.get_string('status').':</b> ';
+
+// Is course complete?
+$coursecomplete = $info->is_course_complete($user->id);
+
+// Has this user completed any criteria?
+$criteriacomplete = $info->count_course_user_data($user->id);
+
+if ($coursecomplete) {
+ echo get_string('complete');
+} else if (!$criteriacomplete) {
+ echo '<i>'.get_string('notyetstarted', 'completion').'</i>';
+} else {
+ echo '<i>'.get_string('inprogress','completion').'</i>';
+}
+
+echo '</td></tr>';
+echo '<tr><td colspan="2"><b>'.get_string('required').':</b> ';
+
+// Get overall aggregation method
+$overall = $info->get_aggregation_method();
+
+if ($overall == COMPLETION_AGGREGATION_ALL) {
+ echo get_string('criteriarequiredall', 'completion');
+} else {
+ echo get_string('criteriarequiredany', 'completion');
+}
+
+echo '</td></tr></tbody></table>';
+
+// Generate markup for criteria statuses
+echo '<table class="generalbox boxaligncenter" cellpadding="3"><tbody>';
+echo '<tr class="ccheader">';
+echo '<th class="c0 header" scope="col">'.get_string('criteriagroup', 'block_completionstatus').'</th>';
+echo '<th class="c1 header" scope="col">'.get_string('criteria', 'completion').'</th>';
+echo '<th class="c2 header" scope="col">'.get_string('requirement', 'block_completionstatus').'</th>';
+echo '<th class="c3 header" scope="col">'.get_string('status').'</th>';
+echo '<th class="c4 header" scope="col">'.get_string('complete').'</th>';
+echo '<th class="c5 header" scope="col">'.get_string('completiondate', 'coursereport_completion').'</th>';
+echo '</tr>';
+
+// Save row data
+$rows = array();
+
+global $COMPLETION_CRITERIA_TYPES;
+
+// Loop through course criteria
+foreach ($completions as $completion) {
+ $criteria = $completion->get_criteria();
+ $complete = $completion->is_complete();
+
+ $row = array();
+ $row['type'] = $criteria->criteriatype;
+ $row['title'] = $criteria->get_title();
+ $row['status'] = $completion->get_status();
+ $row['timecompleted'] = $completion->timecompleted;
+ $row['details'] = $criteria->get_details($completion);
+ $rows[] = $row;
+}
+
+// Print table
+$last_type = '';
+$agg_type = false;
+
+foreach ($rows as $row) {
+
+ // Criteria group
+ echo '<td class="c0">';
+ if ($last_type !== $row['details']['type']) {
+ $last_type = $row['details']['type'];
+ echo $last_type;
+
+ // Reset agg type
+ $agg_type = true;
+ } else {
+ // Display aggregation type
+ if ($agg_type) {
+ $agg = $info->get_aggregation_method($row['type']);
+
+ echo '(<i>';
+
+ if ($agg == COMPLETION_AGGREGATION_ALL) {
+ echo strtolower(get_string('all', 'completion'));
+ } else {
+ echo strtolower(get_string('any', 'completion'));
+ }
+
+ echo '</i> '.strtolower(get_string('required')).')';
+ $agg_type = false;
+ }
+ }
+ echo '</td>';
+
+ // Criteria title
+ echo '<td class="c1">';
+ echo $row['details']['criteria'];
+ echo '</td>';
+
+ // Requirement
+ echo '<td class="c2">';
+ echo $row['details']['requirement'];
+ echo '</td>';
+
+ // Status
+ echo '<td class="c3">';
+ echo $row['details']['status'];
+ echo '</td>';
+
+ // Is complete
+ echo '<td class="c4">';
+ echo ($row['status'] === 'Yes') ? 'Yes' : 'No';
+ echo '</td>';
+
+ // Completion data
+ echo '<td class="c5">';
+ if ($row['timecompleted']) {
+ echo userdate($row['timecompleted'], '%e %B %G');
+ } else {
+ echo '-';
+ }
+ echo '</td>';
+ echo '</tr>';
+}
+
+echo '</tbody></table>';
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+$string['completionprogressdetails'] = 'Completion progress details';
+$string['completionstatus'] = 'Course Completion Status';
+$string['pluginname'] = 'Course Completion Status';
+$string['criteriagroup'] = 'Criteria group';
+$string['requirement'] = 'Requirement';
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+require_once($CFG->libdir.'/completionlib.php');
+
+/**
+ * Self course completion marking
+ * Let's a user manually complete a course
+ *
+ * Will only display if the course has completion enabled,
+ * there is a self completion criteria, and the logged in user is yet
+ * to complete the course.
+ */
+class block_selfcompletion extends block_base {
+
+ public function init() {
+ $this->title = get_string('selfcompletion', 'block_selfcompletion');
+ $this->version = 2009072800;
+ }
+
+ public function get_content() {
+ global $USER;
+
+ // If content is cached
+ if ($this->content !== NULL) {
+ return $this->content;
+ }
+
+ global $CFG;
+
+ // Create empty content
+ $this->content = new stdClass;
+
+ // Don't display if completion isn't enabled!
+ if (!$this->page->course->enablecompletion) {
+ return $this->content;
+ }
+
+ // Get course completion data
+ $info = new completion_info($this->page->course);
+ $completion = $info->get_completion($USER->id, COMPLETION_CRITERIA_TYPE_SELF);
+
+ // Is course complete?
+ if ($info->is_course_complete($USER->id)) {
+ return $this->content;
+ }
+
+ // Check if self completion is one of this course's criteria
+ if (empty($completion)) {
+ return $this->content;
+ }
+
+ // Check this user is enroled
+ $users = $info->internal_get_tracked_users(true);
+ if (!in_array($USER->id, array_keys($users))) {
+ $this->content->text = get_string('notenroled', 'completion');
+ return $this->content;
+ }
+
+ // Check if the user has already marked themselves as complete
+ if ($completion->is_complete()) {
+ return $this->content;
+ } else {
+ $this->content->text = '';
+ $this->content->footer = '<br /><a href="'.$CFG->wwwroot.'/course/togglecompletion.php?course='.$this->page->course->id.'">'.
+ get_string('completecourse', 'block_selfcompletion').'</a>...';
+ }
+
+ return $this->content;
+ }
+}
--- /dev/null
+<?php
+
+$string['selfcompletion'] = 'Self Completion';
+$string['pluginname'] = 'Self Completion';
+$string['completecourse'] = 'Complete Course';
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+// Edit course completion settings
+
+require_once('../config.php');
+require_once('lib.php');
+require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->libdir.'/completion/completion_criteria_self.php');
+require_once($CFG->libdir.'/completion/completion_criteria_date.php');
+require_once($CFG->libdir.'/completion/completion_criteria_unenrol.php');
+require_once($CFG->libdir.'/completion/completion_criteria_activity.php');
+require_once($CFG->libdir.'/completion/completion_criteria_duration.php');
+require_once($CFG->libdir.'/completion/completion_criteria_grade.php');
+require_once($CFG->libdir.'/completion/completion_criteria_role.php');
+require_once($CFG->libdir.'/completion/completion_criteria_course.php');
+require_once $CFG->libdir.'/gradelib.php';
+require_once('completion_form.php');
+
+$id = required_param('id', PARAM_INT); // course id
+
+/// basic access control checks
+if ($id) { // editing course
+
+ if($id == SITEID){
+ // don't allow editing of 'site course' using this from
+ print_error('cannoteditsiteform');
+ }
+
+ if (!$course = $DB->get_record('course', array('id'=>$id))) {
+ print_error('invalidcourseid');
+ }
+ require_login($course->id);
+ require_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id));
+
+} else {
+ require_login();
+ print_error('needcourseid');
+}
+
+/// Set up the page
+$streditcompletionsettings = get_string("editcoursecompletionsettings", 'completion');
+$PAGE->set_course($course);
+$PAGE->set_url('/course/completion.php', array('id' => $course->id));
+//$PAGE->navbar->add($streditcompletionsettings);
+$PAGE->set_title($course->shortname);
+$PAGE->set_heading($course->fullname);
+
+/// first create the form
+$form = new course_completion_form('completion.php?id='.$id, compact('course'));
+
+// now override defaults if course already exists
+if ($form->is_cancelled()){
+ redirect($CFG->wwwroot.'/course/view.php?id='.$course->id);
+
+} else if ($data = $form->get_data()) {
+
+ $completion = new completion_info($course);
+
+/// process criteria unlocking if requested
+ if (!empty($data->settingsunlock)) {
+
+ $completion->delete_course_completion_data();
+
+ // Return to form (now unlocked)
+ redirect($CFG->wwwroot."/course/completion.php?id=$course->id");
+ }
+
+/// process data if submitted
+ // Delete old criteria
+ $completion->clear_criteria();
+
+ // Loop through each criteria type and run update_config
+ global $COMPLETION_CRITERIA_TYPES;
+ foreach ($COMPLETION_CRITERIA_TYPES as $type) {
+ $class = 'completion_criteria_'.$type;
+ $criterion = new $class();
+ $criterion->update_config($data);
+ }
+
+ // Handle aggregation methods
+ // Overall aggregation
+ $aggregation = new completion_aggregation();
+ $aggregation->course = $data->id;
+ $aggregation->criteriatype = null;
+ $aggregation->setMethod($data->overall_aggregation);
+ $aggregation->insert();
+
+ // Activity aggregation
+ if (empty($data->activity_aggregation)) {
+ $data->activity_aggregation = 0;
+ }
+
+ $aggregation = new completion_aggregation();
+ $aggregation->course = $data->id;
+ $aggregation->criteriatype = COMPLETION_CRITERIA_TYPE_ACTIVITY;
+ $aggregation->setMethod($data->activity_aggregation);
+ $aggregation->insert();
+
+ // Course aggregation
+ if (empty($data->course_aggregation)) {
+ $data->course_aggregation = 0;
+ }
+
+ $aggregation = new completion_aggregation();
+ $aggregation->course = $data->id;
+ $aggregation->criteriatype = COMPLETION_CRITERIA_TYPE_COURSE;
+ $aggregation->setMethod($data->course_aggregation);
+ $aggregation->insert();
+
+ // Role aggregation
+ $aggregation = new completion_aggregation();
+ $aggregation->course = $data->id;
+ $aggregation->criteriatype = COMPLETION_CRITERIA_TYPE_ROLE;
+ $aggregation->setMethod($data->role_aggregation);
+ $aggregation->insert();
+
+ // Update course total passing grade
+ if ($grade_item = grade_category::fetch_course_category($course->id)->grade_item) {
+ $grade_item->gradepass = $data->criteria_grade_value;
+ if (method_exists($grade_item, 'update')) {
+ $grade_item->update('course/completion.php');
+ }
+ }
+
+ redirect($CFG->wwwroot."/course/view.php?id=$course->id", get_string('changessaved'));
+}
+
+
+/// Print the form
+
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($streditcompletionsettings);
+
+$form->display();
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+require_once($CFG->libdir.'/formslib.php');
+
+class course_completion_form extends moodleform {
+
+ function definition() {
+ global $USER, $CFG, $DB, $js_enabled;
+
+ $courseconfig = get_config('moodlecourse');
+ $mform =& $this->_form;
+
+ $course = $this->_customdata['course'];
+ $completion = new completion_info($course);
+
+ $params = array(
+ 'course' => $course->id
+ );
+
+
+/// form definition
+//--------------------------------------------------------------------------------
+
+ // Check if there is existing criteria completions
+ if ($completion->is_course_locked()) {
+ $mform->addElement('header', '', get_string('completionsettingslocked', 'completion'));
+ $mform->addElement('static', '', '', get_string('err_settingslocked', 'completion'));
+ $mform->addElement('submit', 'settingsunlock', get_string('unlockcompletiondelete', 'completion'));
+ }
+
+ // Get array of all available aggregation methods
+ $aggregation_methods = $completion->get_aggregation_methods();
+
+ // Overall criteria aggregation
+ $mform->addElement('header', 'overallcriteria', get_string('overallcriteriaaggregation', 'completion'));
+ $mform->addElement('select', 'overall_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('overall_aggregation', $completion->get_aggregation_method());
+
+ // Course prerequisite completion criteria
+ $mform->addElement('header', 'courseprerequisites', get_string('courseprerequisites', 'completion'));
+
+ // Get applicable courses
+ $courses = $DB->get_records_sql(
+ "
+ SELECT DISTINCT
+ c.id,
+ c.category,
+ c.fullname,
+ cc.id AS selected
+ FROM
+ {course} c
+ LEFT JOIN
+ {course_completion_criteria} cc
+ ON cc.courseinstance = c.id
+ AND cc.course = {$course->id}
+ INNER JOIN
+ {course_completion_criteria} ccc
+ ON ccc.course = c.id
+ WHERE
+ c.enablecompletion = ".COMPLETION_ENABLED."
+ AND c.id <> {$course->id}
+ "
+ );
+
+ if (!empty($courses)) {
+ if (count($courses) > 1) {
+ $mform->addElement('select', 'course_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('course_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE));
+ }
+
+ // Get category list
+ $list = array();
+ $parents = array();
+ make_categories_list($list, $parents);
+
+ // Get course list for select box
+ $selectbox = array();
+ $selected = array();
+ foreach ($courses as $c) {
+ $selectbox[$c->id] = $list[$c->category] . ' / ' . s($c->fullname);
+
+ // If already selected
+ if ($c->selected) {
+ $selected[] = $c->id;
+ }
+ }
+
+ // Show multiselect box
+ $mform->addElement('select', 'criteria_course', get_string('coursesavailable', 'completion'), $selectbox, array('multiple' => 'multiple', 'size' => 6));
+
+ // Select current criteria
+ $mform->setDefault('criteria_course', $selected);
+
+ // Explain list
+ $mform->addElement('static', 'criteria_courses_explaination', '', get_string('coursesavailableexplaination', 'completion'));
+
+ } else {
+ $mform->addElement('static', 'nocourses', '', get_string('err_nocourses', 'completion'));
+ }
+
+ // Manual self completion
+ $mform->addElement('header', 'manualselfcompletion', get_string('manualselfcompletion', 'completion'));
+ $criteria = new completion_criteria_self($params);
+ $criteria->config_form_display($mform);
+
+ // Role completion criteria
+ $mform->addElement('header', 'roles', get_string('manualcompletionby', 'completion'));
+
+ $roles = get_roles_with_capability('moodle/course:markcomplete', CAP_ALLOW, get_context_instance(CONTEXT_COURSE, $course->id));
+
+ if (!empty($roles)) {
+ $mform->addElement('select', 'role_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('role_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ROLE));
+
+ foreach ($roles as $role) {
+ $params_a = array('role' => $role->id);
+ $criteria = new completion_criteria_role(array_merge($params, $params_a));
+ $criteria->config_form_display($mform, $role);
+ }
+ } else {
+ $mform->addElement('static', 'noroles', '', get_string('err_noroles', 'completion'));
+ }
+
+ // Activity completion criteria
+ $mform->addElement('header', 'activitiescompleted', get_string('activitiescompleted', 'completion'));
+
+ $activities = $completion->get_activities();
+ if (!empty($activities)) {
+ if (count($activities) > 1) {
+ $mform->addElement('select', 'activity_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('activity_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ACTIVITY));
+ }
+
+ foreach ($activities as $activity) {
+ $params_a = array('moduleinstance' => $activity->id);
+ $criteria = new completion_criteria_activity(array_merge($params, $params_a));
+ $criteria->config_form_display($mform, $activity);
+ }
+ } else {
+ $mform->addElement('static', 'noactivities', '', get_string('err_noactivities', 'completion'));
+ }
+
+ // Completion on date
+ $mform->addElement('header', 'date', get_string('date'));
+ $criteria = new completion_criteria_date($params);
+ $criteria->config_form_display($mform);
+
+ // Completion after enrolment duration
+ $mform->addElement('header', 'duration', get_string('durationafterenrolment', 'completion'));
+ $criteria = new completion_criteria_duration($params);
+ $criteria->config_form_display($mform);
+
+ // Completion on course grade
+ $mform->addElement('header', 'grade', get_string('grade'));
+
+ // Grade enable and passing grade
+ $course_grade = $DB->get_field('grade_items', 'gradepass', array('courseid' => $course->id, 'itemtype' => 'course'));
+ $criteria = new completion_criteria_grade($params);
+ $criteria->config_form_display($mform, $course_grade);
+
+ // Completion on unenrolment
+ $mform->addElement('header', 'unenrolment', get_string('unenrolment', 'completion'));
+ $criteria = new completion_criteria_unenrol($params);
+ $criteria->config_form_display($mform);
+
+
+//--------------------------------------------------------------------------------
+ $this->add_action_buttons();
+//--------------------------------------------------------------------------------
+ $mform->addElement('hidden', 'id', $course->id);
+ $mform->setType('id', PARAM_INT);
+
+ // If the criteria are locked, freeze values and submit button
+ if ($completion->is_course_locked()) {
+ $except = array('settingsunlock');
+ $mform->hardFreezeAllVisibleExcept($except);
+ $mform->addElement('cancel');
+ }
+ }
+
+
+/// perform some extra moodle validation
+ function validation($data, $files) {
+ global $DB, $CFG;
+
+ $errors = parent::validation($data, $files);
+
+ return $errors;
+ }
+}
+?>
$mform->addElement('select', 'enablecompletion', get_string('completion','completion'),
array(0=>get_string('completiondisabled','completion'), 1=>get_string('completionenabled','completion')));
$mform->setDefault('enablecompletion', $courseconfig->enablecompletion);
+
+ $mform->addElement('checkbox', 'completionstartonenrol', get_string('completionstartonenrol', 'completion'));
+ $mform->setDefault('completionstartonenrol', $courseconfig->completionstartonenrol);
+ $mform->disabledIf('completionstartonenrol', 'enablecompletion', 'eq', 0);
} else {
$mform->addElement('hidden', 'enablecompletion');
$mform->setType('enablecompletion', PARAM_INT);
$mform->setDefault('enablecompletion',0);
+
+ $mform->addElement('hidden', 'completionstartonenrol');
+ $mform->setType('completionstartonenrol', PARAM_INT);
+ $mform->setDefault('completionstartonenrol',0);
}
//--------------------------------------------------------------------------------
get_string('completedwarning', 'completion'),
get_string('completedwarningtext', 'completion', $completedcount)),
'unlockcompletion');
- $mform->setHelpButton('completedwarning', array('completionlocked', get_string('help_completionlocked', 'completion'), 'completion'));
+ $mform->setHelpButton('completedwarning', array('completionlocked', get_string('completionlocked_help', 'completion'), 'completion'));
$freeze = true;
}
// Conditional availability
$mform->addElement('header', '', get_string('availabilityconditions', 'condition'));
$mform->addElement('date_selector', 'availablefrom', get_string('availablefrom', 'condition'), array('optional'=>true));
- $mform->setHelpButton('availablefrom', array('conditiondates', get_string('help_conditiondates', 'condition'), 'condition'));
+ $mform->setHelpButton('availablefrom', array('conditiondates', get_string('conditiondates_help', 'condition'), 'condition'));
$mform->addElement('date_selector', 'availableuntil', get_string('availableuntil', 'condition'), array('optional'=>true));
- $mform->setHelpButton('availableuntil', array('conditiondates', get_string('help_conditiondates', 'condition'), 'condition'));
+ $mform->setHelpButton('availableuntil', array('conditiondates', get_string('conditiondates_help', 'condition'), 'condition'));
// Conditions based on grades
$gradeoptions = array();
$this->repeat_elements(array($group),$count,array(),
'conditioncompletionrepeats','conditioncompletionadds',2,
get_string('addcompletions','condition'),true);
- $mform->setHelpButton('conditioncompletiongroup[0]', array('completioncondition', get_string('help_completioncondition', 'condition'), 'condition'));
+ $mform->setHelpButton('conditioncompletiongroup[0]', array('completioncondition', get_string('completioncondition_help', 'condition'), 'condition'));
}
// Do we display availability info to students?
array(CONDITION_STUDENTVIEW_SHOW=>get_string('showavailability_show', 'condition'),
CONDITION_STUDENTVIEW_HIDE=>get_string('showavailability_hide', 'condition')));
$mform->setDefault('showavailability', CONDITION_STUDENTVIEW_SHOW);
- $mform->setHelpButton('showavailability', array('showavailability', get_string('help_showavailability', 'condition'), 'condition'));
+ $mform->setHelpButton('showavailability', array('showavailability', get_string('showavailability_help', 'condition'), 'condition'));
}
// Conditional activities: completion tracking section
$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->setHelpButton('completion', array('completion', get_string('help_completion', 'completion'), 'completion'));
+ $mform->setHelpButton('completion', array('completion', get_string('completion_help', 'completion'), 'completion'));
$mform->setDefault('completion', $this->_features->defaultcompletion
? COMPLETION_TRACKING_MANUAL
: COMPLETION_TRACKING_NONE);
if (plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
$mform->addElement('checkbox', 'completionview', get_string('completionview', 'completion'),
get_string('completionview_text', 'completion'));
- $mform->setHelpButton('completionview', array('completionview', get_string('help_completionview', 'completion'), 'completion'));
+ $mform->setHelpButton('completionview', array('completionview', get_string('completionview_help', 'completion'), 'completion'));
$mform->disabledIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$gotcompletionoptions = true;
}
if (plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false)) {
$mform->addElement('checkbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
get_string('completionusegrade_text', 'completion'));
- $mform->setHelpButton('completionusegrade', array('completionusegrade', get_string('help_completionusegrade', 'completion'), 'completion'));
+ $mform->setHelpButton('completionusegrade', array('completionusegrade', get_string('completionusegrade_help', 'completion'), 'completion'));
$mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$gotcompletionoptions = true;
}
// Completion expected at particular date? (For progress tracking)
$mform->addElement('date_selector', 'completionexpected', get_string('completionexpected', 'completion'), array('optional'=>true));
- $mform->setHelpButton('completionexpected', array('completionexpected', get_string('help_completionexpected', 'completion'), 'completion'));
+ $mform->setHelpButton('completionexpected', array('completionexpected', get_string('completionexpected_help', 'completion'), 'completion'));
$mform->disabledIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE);
}
--- /dev/null
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+$capabilities = array(
+
+ 'coursereport/completion:view' => array(
+ 'riskbitmask' => RISK_PERSONAL,
+ 'captype' => 'read',
+ 'contextlevel' => CONTEXT_COURSE,
+ 'legacy' => array(
+ 'teacher' => CAP_ALLOW,
+ 'editingteacher' => CAP_ALLOW,
+ 'admin' => CAP_ALLOW
+ ),
+
+ 'clonepermissionsfrom' => 'moodle/site:viewreports',
+ )
+);
+
+?>
--- /dev/null
+<?php
+
+// 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/>.
+
+
+/**
+ * Course completion progress report
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once('../../../config.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+
+/**
+ * Configuration
+ */
+define('COMPLETION_REPORT_PAGE', 25);
+define('COMPLETION_REPORT_COL_TITLES', true);
+
+
+/**
+ * Setup page, check permissions
+ */
+
+// Get course
+$course = $DB->get_record('course', array('id' => required_param('course', PARAM_INT)));
+if(!$course) {
+ print_error('invalidcourseid');
+}
+
+// Non-js edit
+$edituser = optional_param('edituser', 0, PARAM_INT);
+
+// Sort (default lastname, optionally firstname)
+$sort = optional_param('sort','',PARAM_ALPHA);
+$firstnamesort = $sort == 'firstname';
+
+// CSV format
+$format = optional_param('format','',PARAM_ALPHA);
+$excel = $format == 'excelcsv';
+$csv = $format == 'csv' || $excel;
+
+// Whether to start at a particular position
+$start = optional_param('start',0,PARAM_INT);
+
+// Whether to show idnumber
+$idnumbers = $CFG->grade_report_showuseridnumber;
+
+// Function for quoting csv cell values
+function csv_quote($value) {
+ global $excel;
+ if($excel) {
+ $tl=textlib_get_instance();
+ return $tl->convert('"'.str_replace('"',"'",$value).'"','UTF-8','UTF-16LE');
+ } else {
+ return '"'.str_replace('"',"'",$value).'"';
+ }
+}
+
+
+// Check permissions
+require_login($course);
+
+$context=get_context_instance(CONTEXT_COURSE, $course->id);
+require_capability('coursereport/completion:view', $context);
+
+// Get group mode
+$group = groups_get_course_group($course, true); // Supposed to verify group
+if($group === 0 && $course->groupmode == SEPARATEGROUPS) {
+ require_capability('moodle/site:accessallgroups',$context);
+}
+
+
+$url = new moodle_url('/course/report/completion/index.php', array('course'=>$course->id));
+$PAGE->set_url($url);
+
+/**
+ * Load data
+ */
+
+// Get criteria for course
+$completion = new completion_info($course);
+
+if (!$completion->has_criteria()) {
+ print_error('err_nocriteria', 'completion', $CFG->wwwroot.'/course/report.php?id='.$course->id);
+}
+
+// Get criteria and put in correct order
+$criteria = array();
+
+foreach ($completion->get_criteria(COMPLETION_CRITERIA_TYPE_COURSE) as $criterion) {
+ $criteria[] = $criterion;
+}
+
+foreach ($completion->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY) as $criterion) {
+ $criteria[] = $criterion;
+}
+
+foreach ($completion->get_criteria() as $criterion) {
+ if (!in_array($criterion->criteriatype, array(
+ COMPLETION_CRITERIA_TYPE_COURSE, COMPLETION_CRITERIA_TYPE_ACTIVITY))) {
+ $criteria[] = $criterion;
+ }
+}
+
+// Can logged in user mark users as complete?
+// (if the logged in user has a role defined in the role criteria)
+$allow_marking = false;
+$allow_marking_criteria = null;
+
+if (!$csv) {
+ // Get role criteria
+ $rcriteria = $completion->get_criteria(COMPLETION_CRITERIA_TYPE_ROLE);
+
+ if (!empty($rcriteria)) {
+
+ foreach ($rcriteria as $rcriterion) {
+ $users = get_role_users($rcriterion->role, $context, true);
+
+ // If logged in user has this role, allow marking complete
+ if ($users && in_array($USER->id, array_keys($users))) {
+ $allow_marking = true;
+ $allow_marking_criteria = $rcriterion->id;
+ break;
+ }
+ }
+ }
+}
+
+// Get user data
+$progress = $completion->get_progress_all(
+ $firstnamesort, $group,
+ $csv ? 0 : COMPLETION_REPORT_PAGE,
+ $csv ? 0 : $start);
+
+
+/**
+ * Setup page header
+ */
+if ($csv) {
+ header('Content-Disposition: attachment; filename=progress.'.
+ preg_replace('/[^a-z0-9-]/','_',strtolower($course->shortname)).'.csv');
+ // Unicode byte-order mark for Excel
+ if($excel) {
+ header('Content-Type: text/csv; charset=UTF-16LE');
+ print chr(0xFF).chr(0xFE);
+ $sep="\t".chr(0);
+ $line="\n".chr(0);
+ } else {
+ header('Content-Type: text/csv; charset=UTF-8');
+ $sep=",";
+ $line="\n";
+ }
+
+} else {
+ // Navigation and header
+ $strcompletion = get_string('completionreport','completion');
+
+ $PAGE->set_title($strcompletion);
+ $PAGE->set_heading($course->fullname);
+
+ echo $OUTPUT->header();
+
+ $PAGE->requires->yui2_lib(
+ array(
+ 'yahoo',
+ 'dom',
+ 'element',
+ 'event',
+ )
+ );
+
+ $PAGE->requires->js('/course/report/completion/textrotate.js');
+
+ // Handle groups (if enabled)
+ groups_print_course_menu($course, $CFG->wwwroot.'/course/report/progress/?course='.$course->id);
+}
+
+// Do we need a paging bar?
+if($progress->total > COMPLETION_REPORT_PAGE) {
+ $pagingbar='<div class="completion_pagingbar">';
+
+ if($start>0) {
+ $newstart=$start-COMPLETION_REPORT_PAGE;
+ if($newstart<0) {
+ $newstart=0;
+ }
+ $pagingbar.=link_arrow_left(get_string('previous'),'./?course='.$course->id.
+ ($newstart ? '&start='.$newstart : ''),false,'completion_prev');
+ }
+
+ $a=new StdClass;
+ $a->from=$start+1;
+ $a->to=$start+COMPLETION_REPORT_PAGE;
+ $a->total=$progress->total;
+ $pagingbar.='<p>'.get_string('reportpage','completion',$a).'</p>';
+
+ if($start+COMPLETION_REPORT_PAGE < $progress->total) {
+ $pagingbar.=link_arrow_right(get_string('next'),'./?course='.$course->id.
+ '&start='.($start+COMPLETION_REPORT_PAGE),false,'completion_next');
+ }
+
+ $pagingbar.='</div>';
+} else {
+ $pagingbar='';
+}
+
+
+/**
+ * Draw table header
+ */
+
+// Start of table
+if(!$csv) {
+ print '<br class="clearer"/>'; // ugh
+
+ if(count($progress->users)==0) {
+ echo $OUTPUT->box_start('errorbox errorboxcontent boxaligncenter boxwidthnormal');
+ print '<p class="nousers">'.get_string('err_nousers','completion').'</p>';
+ print '<p><a href="'.$CFG->wwwroot.'/course/report.php?id='.$course->id.'">'.get_string('continue').'</a></p>';
+ echo $OUTPUT->box_end();
+ echo $OUTPUT->footer($course);
+ exit;
+ }
+
+ print $pagingbar;
+ print '<table id="completion-progress" class="generaltable flexible boxaligncenter completionreport" style="text-align: left" cellpadding="5" border="1">';
+
+ // Print criteria group names
+ print PHP_EOL.'<tr style="vertical-align: top">';
+ print '<th scope="row" class="rowheader">'.get_string('criteriagroup', 'completion').'</th>';
+
+ $current_group = false;
+ $col_count = 0;
+ for ($i = 0; $i <= count($criteria); $i++) {
+
+ if (isset($criteria[$i])) {
+ $criterion = $criteria[$i];
+
+ if ($current_group && $criterion->criteriatype === $current_group->criteriatype) {
+ ++$col_count;
+ continue;
+ }
+ }
+
+ // Print header cell
+ if ($col_count) {
+ print '<th scope="col" colspan="'.$col_count.'" class="colheader criteriagroup">'.$current_group->get_type_title().'</th>';
+ }
+
+ if (isset($criteria[$i])) {
+ // Move to next criteria type
+ $current_group = $criterion;
+ $col_count = 1;
+ }
+ }
+
+ // Overall course completion status
+ print '<th style="text-align: center;">'.get_string('course').'</th>';
+
+ print '</tr>';
+
+ // Print aggregation methods
+ print PHP_EOL.'<tr style="vertical-align: top">';
+ print '<th scope="row" class="rowheader">'.get_string('aggregationmethod', 'completion').'</th>';
+
+ $current_group = false;
+ $col_count = 0;
+ for ($i = 0; $i <= count($criteria); $i++) {
+
+ if (isset($criteria[$i])) {
+ $criterion = $criteria[$i];
+
+ if ($current_group && $criterion->criteriatype === $current_group->criteriatype) {
+ ++$col_count;
+ continue;
+ }
+ }
+
+ // Print header cell
+ if ($col_count) {
+ $has_agg = array(
+ COMPLETION_CRITERIA_TYPE_COURSE,
+ COMPLETION_CRITERIA_TYPE_ACTIVITY,
+ COMPLETION_CRITERIA_TYPE_ROLE,
+ );
+
+ if (in_array($current_group->criteriatype, $has_agg)) {
+ // Try load a aggregation method
+ $method = $completion->get_aggregation_method($current_group->criteriatype);
+
+ $method = $method == 1 ? 'All' : 'Any';
+
+ } else {
+ $method = '-';
+ }
+
+ print '<th scope="col" colspan="'.$col_count.'" class="colheader aggheader">'.$method.'</th>';
+ }
+
+ if (isset($criteria[$i])) {
+ // Move to next criteria type
+ $current_group = $criterion;
+ $col_count = 1;
+ }
+ }
+
+ // Overall course aggregation method
+ print '<th scope="col" class="colheader aggheader aggcriteriacourse">';
+
+ // Get course aggregation
+ $method = $completion->get_aggregation_method();
+
+ print $method == 1 ? 'All' : 'Any';
+ print '</th>';
+
+ print '</tr>';
+
+
+ // Print criteria titles
+ if (COMPLETION_REPORT_COL_TITLES) {
+
+ print PHP_EOL.'<tr>';
+ print '<th scope="row" class="rowheader">'.get_string('criteria', 'completion').'</th>';
+
+ foreach ($criteria as $criterion) {
+ // Get criteria details
+ $details = $criterion->get_title_detailed();
+ print '<th scope="col" class="colheader criterianame">';
+ print '<span class="completion-criterianame">'.$details.'</span>';
+ print '</th>';
+ }
+
+ // Overall course completion status
+ print '<th scope="col" class="colheader criterianame">';
+
+ print '<span class="completion-criterianame">'.get_string('coursecomplete', 'completion').'</span>';
+
+ print '</th></tr>';
+ }
+
+ // Print user heading and icons
+ print '<tr>';
+
+ // User heading / sort option
+ print '<th scope="col" class="completion-sortchoice" style="clear: both;">';
+ if($firstnamesort) {
+ print
+ get_string('firstname').' / <a href="./?course='.$course->id.'">'.
+ get_string('lastname').'</a>';
+ } else {
+ print '<a href="./?course='.$course->id.'&sort=firstname">'.
+ get_string('firstname').'</a> / '.
+ get_string('lastname');
+ }
+ print '</th>';
+
+
+ // Print user id number column
+ if($idnumbers) {
+ print '<th>'.get_string('idnumber').'</th>';
+ }
+
+ ///
+ /// Print criteria icons
+ ///
+ foreach ($criteria as $criterion) {
+
+ // Generate icon details
+ $icon = '';
+ $iconlink = '';
+ $icontitle = ''; // Required if $iconlink set
+ $iconalt = ''; // Required
+ switch ($criterion->criteriatype) {
+
+ case COMPLETION_CRITERIA_TYPE_ACTIVITY:
+ // Load activity
+ $activity = $criterion->get_mod_instance();
+
+ // Display icon
+ $icon = $OUTPUT->pix_url('icon', $criterion->module).'/icon.gif';
+ $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$activity->id;
+ $icontitle = $activity->name;
+ $iconalt = get_string('modulename', $criterion->module);
+ break;
+
+ case COMPLETION_CRITERIA_TYPE_COURSE:
+ // Load course
+ $crs = get_record('course', 'id', $criterion->courseinstance);
+
+ // Display icon
+ $iconlink = $CFG->wwwroot.'/course/view.php?id='.$criterion->courseinstance;
+ $icontitle = $crs->fullname;
+ $iconalt = $crs->shortname;
+ break;
+
+ case COMPLETION_CRITERIA_TYPE_ROLE:
+ // Load role
+ $role = $DB->get_record('role', array('id' => $criterion->role));
+
+ // Display icon
+ $iconalt = $role->name;
+ break;
+ }
+
+ // Print icon and cell
+ print '<th class="criteriaicon">';
+
+ // Create icon if not supplied
+ if (!$icon) {
+ $icon = $OUTPUT->pix_url('i/'.$COMPLETION_CRITERIA_TYPES[$criterion->criteriatype].'.gif');
+ }
+
+ print ($iconlink ? '<a href="'.$iconlink.'" title="'.$icontitle.'">' : '');
+ print '<img src="'.$icon.'" class="icon" alt="'.$iconalt.'" '.(!$iconlink ? 'title="'.$iconalt.'"' : '').' />';
+ print ($iconlink ? '</a>' : '');
+
+ print '</th>';
+ }
+
+ // Overall course completion status
+ print '<th class="criteriaicon">';
+ print '<img src="'.$OUTPUT->pix_url('i/course.gif').'" class="icon" alt="Course" title="Course Complete" />';
+ print '</th>';
+
+ print '</tr>';
+
+
+} else {
+ // TODO
+ if($idnumbers) {
+ print $sep;
+ }
+}
+
+
+///
+/// Display a row for each user
+///
+foreach($progress->users as $user) {
+
+ // User name
+ if($csv) {
+ print csv_quote(fullname($user));
+ if($idnumbers) {
+ print $sep.csv_quote($user->idnumber);
+ }
+ } else {
+ print PHP_EOL.'<tr id="user-'.$user->id.'">';
+
+ print '<th scope="row"><a href="'.$CFG->wwwroot.'/user/view.php?id='.
+ $user->id.'&course='.$course->id.'">'.fullname($user).'</a></th>';
+ if($idnumbers) {
+ print '<td>'.htmlspecialchars($user->idnumber).'</td>';
+ }
+ }
+
+ // Progress for each course completion criteria
+ foreach ($criteria as $criterion) {
+
+ // Handle activity completion differently
+ if ($criterion->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
+
+ // Load activity
+ $mod = $criterion->get_mod_instance();
+ $activity = $DB->get_record('course_modules', array('id' => $criterion->moduleinstance));
+ $activity->name = $mod->name;
+
+
+ // Get progress information and state
+ if(array_key_exists($activity->id,$user->progress)) {
+ $thisprogress=$user->progress[$activity->id];
+ $state=$thisprogress->completionstate;
+ $date=userdate($thisprogress->timemodified);
+ } else {
+ $state=COMPLETION_INCOMPLETE;
+ $date='';
+ }
+
+ $criteria_completion = $completion->get_user_completion($user->id, $criterion);
+
+ // Work out how it corresponds to an icon
+ switch($state) {
+ case COMPLETION_INCOMPLETE : $completiontype='n'; break;
+ case COMPLETION_COMPLETE : $completiontype='y'; break;
+ case COMPLETION_COMPLETE_PASS : $completiontype='pass'; break;
+ case COMPLETION_COMPLETE_FAIL : $completiontype='fail'; break;
+ }
+
+ $completionicon='completion-'.
+ ($activity->completion==COMPLETION_TRACKING_AUTOMATIC ? 'auto' : 'manual').
+ '-'.$completiontype;
+
+ $describe=get_string('completion-alt-auto-'.$completiontype,'completion');
+ $a=new StdClass;
+ $a->state=$describe;
+ $a->date=$date;
+ $a->user=fullname($user);
+ $a->activity=strip_tags($activity->name);
+ $fulldescribe=get_string('progress-title','completion',$a);
+
+ if($csv) {
+ print $sep.csv_quote($describe).$sep.csv_quote($date);
+ } else {
+ print '<td class="completion-progresscell">';
+
+ print '<img src="'.$OUTPUT->pix_url('i/'.$completionicon).
+ '" alt="'.$describe.'" class="icon" title="'.$fulldescribe.'" />';
+
+ print '</td>';
+ }
+
+ continue;
+ }
+
+ // Handle all other criteria
+ $criteria_completion = $completion->get_user_completion($user->id, $criterion);
+ $is_complete = $criteria_completion->is_complete();
+
+ $completiontype = $is_complete ? 'y' : 'n';
+ $completionicon = 'completion-auto-'.$completiontype;
+
+ $describe = get_string('completion-alt-auto-'.$completiontype, 'completion');
+
+ $a = new Object();
+ $a->state = $describe;
+ $a->date = $is_complete ? userdate($criteria_completion->timecompleted) : '';
+ $a->user = fullname($user);
+ $a->activity = strip_tags($criterion->get_title());
+ $fulldescribe = get_string('progress-title', 'completion', $a);
+
+ if ($csv) {
+ print $sep.csv_quote($describe);
+ } else {
+
+ if ($allow_marking_criteria === $criterion->id) {
+ $describe = get_string('completion-alt-auto-'.$completiontype,'completion');
+
+ print '<td class="completion-progresscell">'.
+ '<a href="'.$CFG->wwwroot.'/course/togglecompletion.php?user='.$user->id.'&course='.$course->id.'&rolec='.$allow_marking_criteria.'">'.
+ '<img src="'.$OUTPUT->pix_url('i/completion-manual-'.($is_complete ? 'y' : 'n')).
+ '" alt="'.$describe.'" class="icon" title="Mark as complete" /></a></td>';
+ } else {
+ print '<td class="completion-progresscell">'.
+ '<img src="'.$OUTPUT->pix_url('i/'.$completionicon).
+ '" alt="'.$describe.'" class="icon" title="'.$fulldescribe.'" /></td>';
+ }
+ }
+ }
+
+ // Handle overall course completion
+
+ // Load course completion
+ $params = array(
+ 'userid' => $user->id,
+ 'course' => $course->id
+ );
+
+ $ccompletion = new completion_completion($params);
+ $completiontype = $ccompletion->is_complete() ? 'y' : 'n';
+
+ $describe = get_string('completion-alt-auto-'.$completiontype, 'completion');
+
+ $a = new StdClass;
+ $a->state = $describe;
+ $a->date = '';
+ $a->user = fullname($user);
+ $a->activity = strip_tags(get_string('coursecomplete', 'completion'));
+ $fulldescribe = get_string('progress-title', 'completion', $a);
+
+ if ($csv) {
+ print $sep.csv_quote($describe);
+ } else {
+
+ print '<td class="completion-progresscell">';
+
+ // Display course completion status icon
+ print '<img src="'.$OUTPUT->pix_url('i/completion-auto-'.$completiontype).
+ '" alt="'.$describe.'" class="icon" title="'.$fulldescribe.'" />';
+
+ print '</td>';
+ }
+
+ if($csv) {
+ print $line;
+ } else {
+ print '</tr>';
+ }
+}
+
+if($csv) {
+ exit;
+}
+print '</table>';
+print $pagingbar;
+
+print '<ul class="progress-actions"><li><a href="index.php?course='.$course->id.
+ '&format=csv">'.get_string('csvdownload','completion').'</a></li>
+ <li><a href="index.php?course='.$course->id.'&format=excelcsv">'.
+ get_string('excelcsvdownload','completion').'</a></li></ul>';
+
+echo $OUTPUT->footer($course);
--- /dev/null
+<?php
+
+$string['completion:view'] = 'View course completion report';
+$string['completiondate']='Completion date';
+$string['pluginname']='Course completion';
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * This file contains functions used by the progress report
+ *
+ * @since 2.0
+ * @package course-report
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * This function extends the navigation with the report items
+ *
+ * @param navigation_node $navigation The navigation node to extend
+ * @param stdClass $course The course to object for the report
+ * @param stdClass $context The context of the course
+ */
+function completion_report_extend_navigation($navigation, $course, $context) {
+ global $CFG, $OUTPUT;
+
+ if (has_capability('coursereport/completion:view', $context)) {
+ $url = new moodle_url('/course/report/completion/index.php', array('course'=>$course->id));
+ $navigation->add(get_string('pluginname','coursereport_completion'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
+ }
+}
--- /dev/null
+<?php //$Id$
+
+ if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); // It must be included from a Moodle page
+ }
+
+ if (has_capability('coursereport/completion:view', $context)) {
+ $completion = new completion_info($course);
+ if ($completion->is_enabled()) {
+ echo '<p>';
+ echo '<a href="'.$CFG->wwwroot.'/course/report/completion/index.php?course='.$course->id.'">'.get_string('coursecompletionreport','completion').'</a>';
+ echo '</p>';
+ }
+ }
+?>
--- /dev/null
+var SVGNS='http://www.w3.org/2000/svg',XLINKNS='http://www.w3.org/1999/xlink';
+
+function textrotate_make_svg(el)
+{
+ var string=el.firstChild.nodeValue;
+
+ // Add absolute-positioned string (to measure length)
+ var abs=document.createElement('div');
+ abs.appendChild(document.createTextNode(string));
+ abs.style.position='absolute';
+ el.parentNode.insertBefore(abs,el);
+ var textWidth=abs.offsetWidth,textHeight=abs.offsetHeight;
+ el.parentNode.removeChild(abs);
+
+ // Create SVG
+ var svg=document.createElementNS(SVGNS,'svg');
+ svg.setAttribute('version','1.1');
+ svg.setAttribute('width',20);
+ svg.setAttribute('height',textWidth);
+
+ // Add text
+ var text=document.createElementNS(SVGNS,'text');
+ svg.appendChild(text);
+ text.setAttribute('x',textWidth);
+ text.setAttribute('y',-textHeight/4);
+ text.setAttribute('text-anchor','end');
+ text.setAttribute('transform','rotate(90)');
+
+ if (el.className.indexOf('completion-rplheader') != -1) {
+ text.setAttribute('fill','#238E23');
+ }
+
+ text.appendChild(document.createTextNode(string));
+
+ // Is there an icon near the text?
+ var icon=el.parentNode.firstChild;
+ if(icon.nodeName.toLowerCase()=='img') {
+ el.parentNode.removeChild(icon);
+ var image=document.createElementNS(SVGNS,'image');
+ var iconx=el.offsetHeight/4;
+ if(iconx>width-16) iconx=width-16;
+ image.setAttribute('x',iconx);
+ image.setAttribute('y',textWidth+4);
+ image.setAttribute('width',16);
+ image.setAttribute('height',16);
+ image.setAttributeNS(XLINKNS,'href',icon.src);
+ svg.appendChild(image);
+ }
+
+ // Replace original content with this new SVG
+ el.parentNode.insertBefore(svg,el);
+ el.parentNode.removeChild(el);
+}
+
+function textrotate_init() {
+ var elements=YAHOO.util.Dom.getElementsByClassName('completion-criterianame', 'span');
+ for(var i=0;i<elements.length;i++)
+ {
+ var el=elements[i];
+ el.parentNode.style.verticalAlign='bottom';
+ el.parentNode.style.width='20px';
+
+ textrotate_make_svg(el);
+ }
+}
+
+YAHOO.util.Event.onDOMReady(textrotate_init);
+
--- /dev/null
+<?PHP // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+$plugin->version = 2007101501;
+$plugin->requires = 2007101532;
+
+?>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$string['pluginname'] = 'Course progress';
-$string['progress:view'] = 'View course progress report';
+$string['pluginname'] = 'Activity completion';
+$string['progress:view'] = 'View activity completion reports';
$showonnavigation = ($showonnavigation && $completion->is_enabled() && count($completion->get_activities())>0);
if ($showonnavigation) {
$url = new moodle_url('/course/report/progress/index.php', array('course'=>$course->id));
- $navigation->add(get_string('completionreport','completion'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
+ $navigation->add(get_string('pluginname','coursereport_progress'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
}
-}
\ No newline at end of file
+}
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * Toggles the manual completion flag for a particular activity and the current user.
+ * Toggles the manual completion flag for a particular activity or course completion
+ * and the current user.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package course
require_once($CFG->libdir.'/completionlib.php');
// Parameters
-$cmid = required_param('id', PARAM_INT);
+$cmid = optional_param('id', 0, PARAM_INT);
+$courseid = optional_param('course', 0, PARAM_INT);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+if (!$cmid && !$courseid) {
+ print_error('invalidarguments');
+}
+
+// Process self completion
+if ($courseid) {
+
+ // Check user is logged in
+ $course = $DB->get_record('course', array('id' => $courseid));
+ require_login($course);
+
+ $completion = new completion_info($course);
+
+ // Check if we are marking a user complete via the completion report
+ $user = optional_param('user', 0, PARAM_INT);
+ $rolec = optional_param('rolec', 0, PARAM_INT);
+
+ if ($user && $rolec) {
+
+ $criteria = completion_criteria::factory((object) array('id'=>$rolec, 'criteriatype'=>COMPLETION_CRITERIA_TYPE_ROLE));
+ $criteria_completions = $completion->get_completions($user, COMPLETION_CRITERIA_TYPE_ROLE);
+
+ foreach ($criteria_completions as $criteria_completion) {
+ if ($criteria_completion->criteriaid == $rolec) {
+ $criteria->complete($criteria_completion);
+ break;
+ }
+ }
+
+ // Return to previous page
+ if (!empty($_SERVER['HTTP_REFERER'])) {
+ redirect($_SERVER['HTTP_REFERER']);
+ } else {
+ redirect('view.php?id='.$course->id);
+ }
+
+ } else {
+
+ // Confirm with user
+ if ($confirm) {
+ $completion = $completion->get_completion($USER->id, COMPLETION_CRITERIA_TYPE_SELF);
+
+ if (!$completion) {
+ print_error('noselfcompletioncriteria', 'completion');
+ }
+
+ // Check if the user has already marked themselves as complete
+ if ($completion->is_complete()) {
+ print_error('useralreadymarkedcomplete', 'completion');
+ }
+
+ $completion->mark_complete();
+
+ redirect($CFG->wwwroot.'/course/view.php?id='.$courseid);
+ return;
+ }
+
+ $strconfirm = get_string('confirmselfcompletion', 'completion');
+ print_header_simple($strconfirm, '', build_navigation(array(array('name' => $strconfirm, 'link' => '', 'type' => 'misc'))));
+ notice_yesno($strconfirm, $CFG->wwwroot.'/course/togglecompletion.php?course='.$courseid.'&confirm=1', $CFG->wwwroot.'/course/view.php?id='.$courseid);
+ print_simple_box_end();
+ print_footer($course);
+ exit;
+ }
+}
+
+
$targetstate = required_param('completionstate', PARAM_INT);
$fromajax = optional_param('fromajax', 0, PARAM_INT);
$modes[] = 'grade';
}
+// Course completion tab
+if (!empty($CFG->enablecompletion) && ($course->id == SITEID || !empty($course->enablecompletion)) && // completion enabled
+ ($myreports || $anyreport || ($course->id == SITEID || has_capability('coursereport/completion:view', $coursecontext)))) { // permissions to view the report
+
+ // Decide if singular or plural
+ if ($course->id == SITEID) {
+ $modes[] = 'coursecompletions';
+ } else {
+ $modes[] = 'coursecompletion';
+ }
+}
+
+
if (empty($modes)) {
require_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
}
}
}
}
+ break;
+ case "coursecompletion":
+ case "coursecompletions":
+
+ // Display course completion user report
+ require_once $CFG->libdir.'/completionlib.php';
+
+ // Grab all courses the user is enrolled in and their completion status
+ $sql = "
+ SELECT DISTINCT
+ c.id AS id
+ FROM
+ {$CFG->prefix}course c
+ INNER JOIN
+ {$CFG->prefix}context con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {$CFG->prefix}role_assignments ra
+ ON ra.contextid = con.id
+ AND ra.userid = {$user->id}
+ ";
+
+ // Get roles that are tracked by course completion
+ if ($roles = $CFG->progresstrackedroles) {
+ $sql .= '
+ AND ra.roleid IN ('.$roles.')
+ ';
+ }
+
+ $sql .= '
+ WHERE
+ con.contextlevel = '.CONTEXT_COURSE.'
+ AND c.enablecompletion = 1
+ ';
+
+
+ // If we are looking at a specific course
+ if ($course->id != 1) {
+ $sql .= '
+ AND c.id = '.(int)$course->id.'
+ ';
+ }
+
+ // Check if result is empty
+ if (!$rs = get_recordset_sql($sql)) {
+
+ if ($course->id != 1) {
+ $error = get_string('nocompletions', 'coursereport_completion');
+ } else {
+ $error = get_string('nocompletioncoursesenroled', 'coursereport_completion');
+ }
+
+ echo $OUTPUT->notification($error);
+ break;
+ }
+
+ // Categorize courses by their status
+ $courses = array(
+ 'inprogress' => array(),
+ 'complete' => array(),
+ 'unstarted' => array()
+ );
+
+ // Sort courses by the user's status in each
+ foreach ($rs as $course_completion) {
+ $c_info = new completion_info((object)$course_completion);
+
+ // Is course complete?
+ $coursecomplete = $c_info->is_course_complete($user->id);
+
+ // Has this user completed any criteria?
+ $criteriacomplete = $c_info->count_course_user_data($user->id);
+
+ if ($coursecomplete) {
+ $courses['complete'][] = $c_info;
+ } else if ($criteriacomplete) {
+ $courses['inprogress'][] = $c_info;
+ } else {
+ $courses['unstarted'][] = $c_info;
+ }
+ }
+
+ $rs->close();
+
+ // Loop through course status groups
+ foreach ($courses as $type => $infos) {
+
+ // If there are courses with this status
+ if (!empty($infos)) {
+
+ echo '<h1 align="center">'.get_string($type, 'coursereport_completion').'</h1>';
+ echo '<table class="generalbox boxaligncenter">';
+ echo '<tr class="ccheader">';
+ echo '<th class="c0 header" scope="col">'.get_string('course').'</th>';
+ echo '<th class="c1 header" scope="col">'.get_string('requiredcriteria', 'completion').'</th>';
+ echo '<th class="c2 header" scope="col">'.get_string('status').'</th>';
+ echo '<th class="c3 header" scope="col" width="15%">'.get_string('info').'</th>';
+
+ if ($type === 'complete') {
+ echo '<th class="c4 header" scope="col">'.get_string('completiondate', 'coursereport_completion').'</th>';
+ }
+
+ echo '</tr>';
+
+ // For each course
+ foreach ($infos as $c_info) {
+
+ // Get course info
+ $c_course = get_record('course', 'id', $c_info->course_id);
+ $course_name = $c_course->fullname;
+
+ // Get completions
+ $completions = $c_info->get_completions($user->id);
+
+ // Save row data
+ $rows = array();
+
+ // For aggregating activity completion
+ $activities = array();
+ $activities_complete = 0;
+
+ // For aggregating prerequisites
+ $prerequisites = array();
+ $prerequisites_complete = 0;
+
+ // Loop through course criteria
+ foreach ($completions as $completion) {
+ $criteria = $completion->get_criteria();
+ $complete = $completion->is_complete();
+
+ // Activities are a special case, so cache them and leave them till last
+ if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
+ $activities[$criteria->moduleinstance] = $complete;
+
+ if ($complete) {
+ $activities_complete++;
+ }
+
+ continue;
+ }
+
+ // Prerequisites are also a special case, so cache them and leave them till last
+ if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
+ $prerequisites[$criteria->courseinstance] = $complete;
+
+ if ($complete) {
+ $prerequisites_complete++;
+ }
+
+ continue;
+ }
+
+ $row = array();
+ $row['title'] = $criteria->get_title();
+ $row['status'] = $completion->get_status();
+ $rows[] = $row;
+ }
+
+ // Aggregate activities
+ if (!empty($activities)) {
+
+ $row = array();
+ $row['title'] = get_string('activitiescomplete', 'coursereport_completion');
+ $row['status'] = $activities_complete.' of '.count($activities);
+ $rows[] = $row;
+ }
+
+ // Aggregate prerequisites
+ if (!empty($prerequisites)) {
+
+ $row = array();
+ $row['title'] = get_string('prerequisitescompleted', 'completion');
+ $row['status'] = $prerequisites_complete.' of '.count($prerequisites);
+ array_splice($rows, 0, 0, array($row));
+ }
+
+ $first_row = true;
+
+ // Print table
+ foreach ($rows as $row) {
+
+ // Display course name on first row
+ if ($first_row) {
+ echo '<tr><td class="c0"><a href="'.$CFG->wwwroot.'/course/view.php?id='.$c_course->id.'">'.format_string($course_name).'</a></td>';
+ } else {
+ echo '<tr><td class="c0"></td>';
+ }
+
+ echo '<td class="c1">';
+ echo $row['title'];
+ echo '</td><td class="c2">';
+
+ switch ($row['status']) {
+ case 'Yes':
+ echo get_string('complete');
+ break;
+
+ case 'No':
+ echo get_string('incomplete', 'coursereport_completion');
+ break;
+
+ default:
+ echo $row['status'];
+ }
+
+ // Display link on first row
+ echo '</td><td class="c3">';
+ if ($first_row) {
+ echo '<a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$c_course->id.'&user='.$user->id.'">'.get_string('detailedview', 'coursereport_completion').'</a>';
+ }
+ echo '</td>';
+
+ // Display completion date for completed courses on first row
+ if ($type === 'complete' && $first_row) {
+ $params = array(
+ 'userid' => $user->id,
+ 'course' => $c_course->id
+ );
+
+ $ccompletion = new completion_completion($params);
+ echo '<td class="c4">'.userdate($ccompletion->timecompleted, '%e %B %G').'</td>';
+ }
+
+ $first_row = false;
+ echo '</tr>';
+ }
+ }
+
+ echo '</table>';
+ }
+
+ }
+
break;
default:
// can not be reached ;-)
$string['completion-alt-manual-y'] = 'Completed; select to mark as not complete';
$string['completion_automatic'] = 'Show activity as complete when conditions are met';
$string['completiondisabled'] = 'Disabled, not shown in activity settings';
-$string['completionenabled'] = 'Enabled, control via activity settings';
$string['completionexpected'] = 'Expect completed on';
$string['completionicons'] = 'progress tick boxes';
$string['completion_manual'] = 'Users can manually mark the activity as completed';
$string['completion-title-manual-n'] = 'Mark as complete';
$string['completion-title-manual-y'] = 'Mark as not complete';
$string['completionusegrade'] = 'Require grade';
+$string['completionusegrade_help'] = 'User must receive a grade to complete this activity';
$string['completionusegrade_text'] = 'User must receive a grade';
$string['completionview'] = 'Require view';
$string['completionview_text'] = 'User must view activity';
$string['err_nousers'] = 'There are no users on this course or group for whom completion information is displayed. (By default, completion information is displayed only for students, so if there are no students, you will see this error. Administrators can alter this option via the admin screens.)';
$string['err_system'] = 'An internal error occurred in the completion system. (System administrators can enable debugging information to see more detail.)';
$string['excelcsvdownload'] = 'Download in Excel-compatible format (.csv)';
-$string['help_completion'] = 'completion tracking';
-$string['help_completionexpected'] = 'the date completion is expected';
-$string['help_completionlocked'] = 'locked completion options';
-$string['help_completionview'] = 'requiring view to complete';
+$string['completion_help'] = 'Completion tracking';
+$string['completionlocked_help'] = 'Completion options are locked because some users have already completed this activity.';
+$string['completionview_help'] = 'Users must view this activity to complete it';
$string['progress'] = 'Student progress';
$string['progress-title'] = '{$a->user}, {$a->activity}: {$a->state} {$a->date}';
-$string['progresstrackedroles'] = 'Progress-tracked roles';
$string['reportpage'] = 'Showing users {$a->from} to {$a->to} of {$a->total}.';
$string['restoringcompletiondata'] = 'Writing completion data';
$string['saved'] = 'Saved';
$string['unlockcompletion'] = 'Unlock completion options';
$string['writingcompletiondata'] = 'Writing completion data';
$string['yourprogress'] = 'Your progress';
+
+$string['achievinggrade']='Achieving grade';
+$string['activities']='Activities';
+$string['activitiescompleted']='Activities completed';
+$string['activitycompletionreport']='Activity completion progress report';
+$string['addcourseprerequisite']='Add course prerequisite';
+$string['afterspecifieddate']='After specified date';
+$string['aggregationmethod']='Aggregation method';
+$string['all']='All';
+$string['any']='Any';
+$string['approval']='Approval';
+$string['completionenabled']='Enabled, control via completion and activity settings';
+$string['completionmenuitem']='Completion';
+$string['completionsettingslocked']='Completion settings locked';
+$string['completionstartonenrol']='Completion tracking begins on enrolment';
+$string['completionstartonenrolhelp']='Begin tracking a user\'s progress in course completion after course enrolment';
+$string['confirmselfcompletion']='Confirm self completion';
+$string['coursecomplete']='Course Complete';
+$string['coursecompleted']='Course Completed';
+$string['coursecompletionreport']='Course completion progress report';
+$string['coursegrade']='Course grade';
+$string['courseprerequisites']='Course prerequisites';
+$string['coursesavailable']='Courses available';
+$string['coursesavailableexplaination']='<i>Course completion criteria must be set for a course to appear in this list</i>';
+$string['criteria']='Criteria';
+$string['criteriagroup']='Criteria group';
+$string['criteriarequiredall']='All criteria below are required';
+$string['criteriarequiredany']='Any criteria below are required';
+$string['days']='Days';
+$string['editcoursecompletionsettings']='Edit Course Completion Settings';
+$string['enrolmentduration']='Days left';
+$string['err_nocourses']='Course completion is not enabled for any other courses, so none can be displayed. You can enable course completion in the course settings.';
+$string['err_nograde']='A course pass grade has not been set for this course. To enable this criteria type you must create a pass grade for this course.';
+$string['err_noroles']='There are no roles with the capability \'moodle/course:markcomplete\' in this course. You can enable this criteria type by adding this capability to role(s).';
+$string['err_settingslocked']='One or more users have already completed a criteria so the settings have been locked. Unlocking the completion criteria settings will delete any existing user data and may cause confusion.';
+$string['datepassed']='Date passed';
+$string['daysafterenrolment']='Days after enrolment';
+$string['durationafterenrolment']='Duration after enrolment';
+$string['fraction']='Fraction';
+$string['completionexpected_help']='This is the date that completion of this activity is expected';
+$string['inprogress']='In progress';
+$string['manualcompletionby']='Manual completion by';
+$string['manualselfcompletion']='Manual self completion';
+$string['markcomplete']='Mark complete';
+$string['markedcompleteby']='Marked complete by $a';
+$string['markingyourselfcomplete']='Marking yourself complete';
+$string['notenroled']='You are not enroled as a student in this course';
+$string['notyetstarted']='Not yet started';
+$string['overallcriteriaaggregation']='Overall critieria type aggregation';
+$string['passinggrade']='Passing grade';
+$string['pending']='Pending';
+$string['periodpostenrolment']='Period post enrolment';
+$string['prerequisites']='Prerequisites';
+$string['prerequisitescompleted']='Prerequisites completed';
+$string['progresstrackedroles']='Progress-tracked roles';
+$string['recognitionofpriorlearning']='Recognition of prior learning';
+$string['remainingenroledfortime']='Remaining enroled for a specified period of time';
+$string['remainingenroleduntildate']='Remaining enroled until a specified date';
+$string['requiredcriteria']='Required Criteria';
+$string['seedetails']='See details';
+$string['self']='Self';
+$string['selfcompletion']='Self completion';
+$string['showinguser']='Showing user';
+$string['unit']='Unit';
+$string['unenrolingfromcourse']='Unenroling from course';
+$string['unenrolment']='Unenrolment';
+$string['unlockcompletiondelete']='Unlock completion options and delete user completion data';
+$string['usealternateselector']='Use the alternate course selector';
+$string['viewcoursereport']='View course report';
+$string['viewingactivity']='Viewing the $a';
+$string['xdays']='$a days';
$string['badavailabledates'] = 'Invalid dates. If you set both dates, the \'available from\' date should be before the \'until\' date.';
$string['completion_complete'] = 'must be marked complete';
$string['completioncondition'] = 'Activity completion condition';
+$string['completioncondition_help'] = 'Not available until the user has completed another activity in a particular way';
$string['completion_fail'] = 'must be complete with fail grade';
$string['completion_incomplete'] = 'must not be marked complete';
$string['completion_pass'] = 'must be complete with pass grade';
+$string['conditiondates_help'] = 'available dates';
$string['configenableavailability'] = 'When enabled, this lets you set conditions (based on date, grade, or completion) that control whether an activity is available.';
$string['enableavailability'] = 'Enable conditional availability';
$string['grade_atleast'] = 'must be at least';
to 10. This guarantees that everyone with a grade will see one or other.</li>
</ul>';
$string['grade_upto'] = 'and less than';
-$string['help_conditiondates'] = 'available dates';
-$string['help_showavailability'] = 'display of unavailable activities';
$string['none'] = '(none)';
$string['notavailableyet'] = 'Not available yet';
$string['requires_completion_0'] = 'Not available unless the activity <strong>{$a}</strong> is incomplete.';
$string['requires_grade_min'] = 'Not available until you achieve a required score in <strong>{$a}</strong>.';
$string['requires_grade_range'] = 'Not available unless you get a particular score in <strong>{$a}</strong>.';
$string['showavailability'] = 'Before activity is available';
+$string['showavailability_help'] = 'display of unavailable activities';
$string['showavailability_hide'] = 'Hide activity entirely';
$string['showavailability_show'] = 'Show activity greyed-out, with restriction information';
$string['userrestriction_hidden'] = 'Restricted (completely hidden, no message): ‘{$a}’';
$string['coursecategories'] = 'Course categories';
$string['coursecategory'] = 'Course category';
$string['coursecategorydeleted'] = 'Deleted course category {$a}';
+$string['coursecompletion'] = 'Course completion';
+$string['coursecompletions'] = 'Course completions';
$string['coursecreators'] = 'Course creator';
$string['coursecreatorsdescription'] = 'Course creators can create new courses.';
$string['coursedeleted'] = 'Deleted course {$a}';
--- /dev/null
+<?php
+
+// 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/>.
+
+
+/**
+ * Course completion critieria aggregation
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once($CFG->libdir.'/completion/data_object.php');
+
+/**
+ * Course completion critieria aggregation
+ */
+class completion_aggregation extends data_object {
+
+ /**
+ * DB Table
+ * @var string $table
+ */
+ public $table = 'course_completion_aggr_methd';
+
+ /**
+ * Array of required table fields, must start with 'id'.
+ * @var array $required_fields
+ */
+ public $required_fields = array('id', 'course', 'criteriatype', 'method', 'value');
+
+ /**
+ * Course id
+ * @access public
+ * @var int
+ */
+ public $course;
+
+ /**
+ * Criteria type this aggregation method applies to, or NULL for overall course aggregation
+ * @access public
+ * @var int
+ */
+ public $criteriatype;
+
+ /**
+ * Aggregation method (COMPLETION_AGGREGATION_* constant)
+ * @access public
+ * @var int
+ */
+ public $method;
+
+ /**
+ * Method value
+ * @access public
+ * @var mixed
+ */
+ public $value;
+
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ return self::fetch_helper('course_completion_aggr_methd', __CLASS__, $params);
+ }
+
+
+ /**
+ * Finds and returns all data_object instances based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return array array of data_object insatnces or false if none found.
+ */
+ public static function fetch_all($params) {}
+
+ /**
+ * Set the aggregation method
+ * @access public
+ * @param $method int
+ * @return void
+ */
+ public function setMethod($method) {
+ $methods = array(
+ COMPLETION_AGGREGATION_ALL,
+ COMPLETION_AGGREGATION_ANY,
+ );
+
+ if (in_array($method, $methods)) {
+ $this->method = $method;
+ } else {
+ $this->method = COMPLETION_AGGREGATION_ALL;
+ }
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+
+/**
+ * Course completion status for a particular user/course
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once($CFG->libdir.'/completion/data_object.php');
+
+
+/**
+ * Course completion status for a particular user/course
+ */
+class completion_completion extends data_object {
+
+ /**
+ * DB Table
+ * @var string $table
+ */
+ public $table = 'course_completions';
+
+ /**
+ * Array of required table fields, must start with 'id'.
+ * @var array $required_fields
+ */
+ public $required_fields = array('id', 'userid', 'course', 'deleted', 'timenotified',
+ 'timeenrolled', 'timestarted', 'timecompleted', 'reaggregate');
+
+ /**
+ * User ID
+ * @access public
+ * @var int
+ */
+ public $userid;
+
+ /**
+ * Course ID
+ * @access public
+ * @var int
+ */
+ public $course;
+
+ /**
+ * Set to 1 if this record has been deleted
+ * @access public
+ * @var int
+ */
+ public $deleted;
+
+ /**
+ * Timestamp the interested parties were notified
+ * of this user's completion
+ * @access public
+ * @var int
+ */
+ public $timenotified;
+
+ /**
+ * Time of course enrolment
+ * @see completion_completion::mark_enrolled()
+ * @access public
+ * @var int
+ */
+ public $timeenrolled;
+
+ /**
+ * Time the user started their course completion
+ * @see completion_completion::mark_inprogress()
+ * @access public
+ * @var int
+ */
+ public $timestarted;
+
+ /**
+ * Timestamp of course completion
+ * @see completion_completion::mark_complete()
+ * @access public
+ * @var int
+ */
+ public $timecompleted;
+
+ /**
+ * Flag to trigger cron aggregation (timestamp)
+ * @access public
+ * @var int
+ */
+ public $reaggregate;
+
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['deleted'] = null;
+ return self::fetch_helper('course_completions', __CLASS__, $params);
+ }
+
+ /**
+ * Return status of this completion
+ * @access public
+ * @return boolean
+ */
+ public function is_complete() {
+ return (bool) $this->timecompleted;
+ }
+
+ /**
+ * Mark this user as started (or enrolled) in this course
+ *
+ * If the user is already marked as started, no change will occur
+ *
+ * @access public
+ * @param integer $timeenrolled Time enrolled (optional)
+ * @return void
+ */
+ public function mark_enrolled($timeenrolled = null) {
+
+ if (!$this->timeenrolled) {
+
+ if (!$timeenrolled) {
+ $timeenrolled = time();
+ }
+
+ $this->timeenrolled = $timeenrolled;
+ }
+
+ $this->_save();
+ }
+
+ /**
+ * Mark this user as inprogress in this course
+ *
+ * If the user is already marked as inprogress,
+ * the time will not be changed
+ *
+ * @access public
+ * @param integer $timestarted Time started (optional)
+ * @return void
+ */
+ public function mark_inprogress($timestarted = null) {
+
+ $timenow = time();
+
+ // Set reaggregate flag
+ $this->reaggregate = $timenow;
+
+ if (!$this->timestarted) {
+
+ if (!$timestarted) {
+ $timestarted = $timenow;
+ }
+
+ $this->timestarted = $timestarted;
+ }
+
+ $this->_save();
+ }
+
+ /**
+ * Mark this user complete in this course
+ *
+ * This generally happens when the required completion criteria
+ * in the course are complete.
+ *
+ * @access public
+ * @param integer $timecomplete Time completed (optional)
+ * @return void
+ */
+ public function mark_complete($timecomplete = null) {
+
+ // Never change a completion time
+ if ($this->timecompleted) {
+ return;
+ }
+
+ // Use current time if nothing supplied
+ if (!$timecomplete) {
+ $timecomplete = time();
+ }
+
+ // Set time complete
+ $this->timecompleted = $timecomplete;
+
+ // Save record
+ $this->_save();
+ }
+
+ /**
+ * Save course completion status
+ *
+ * This method creates a course_completions record if none exists
+ * @access public
+ * @return void
+ */
+ private function _save() {
+
+ global $DB;
+
+ if (!$this->timeenrolled) {
+ // Get users timenrolled
+ // Can't find a more efficient way of doing this without alter get_users_by_capability()
+ $context = get_context_instance(CONTEXT_COURSE, $this->course);
+ if ($roleassignment = $DB->get_record('role_assignments', array('contextid' => $context->id, 'userid' => $this->userid))) {
+ $this->timeenrolled = $roleassignment->timestart;
+ } else {
+ $this->timeenrolled = 0;
+ }
+ }
+
+ // Save record
+ if ($this->id) {
+ $this->update();
+ } else {
+ // Make sure reaggregate field is not null
+ if (!$this->reaggregate) {
+ $this->reaggregate = 0;
+ }
+
+ $this->insert();
+ }
+ }
+}
--- /dev/null
+<?php
+
+
+// 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/>.
+
+
+/**
+ * Course completion criteria
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once($CFG->libdir.'/completion/data_object.php');
+require_once($CFG->libdir.'/completion/completion_criteria_completion.php');
+
+
+/**
+ * Criteria type constants
+ * Primarily for storing criteria type in the database
+ */
+define('COMPLETION_CRITERIA_TYPE_SELF', 1);
+define('COMPLETION_CRITERIA_TYPE_DATE', 2);
+define('COMPLETION_CRITERIA_TYPE_UNENROL', 3);
+define('COMPLETION_CRITERIA_TYPE_ACTIVITY', 4);
+define('COMPLETION_CRITERIA_TYPE_DURATION', 5);
+define('COMPLETION_CRITERIA_TYPE_GRADE', 6);
+define('COMPLETION_CRITERIA_TYPE_ROLE', 7);
+define('COMPLETION_CRITERIA_TYPE_COURSE', 8);
+
+/**
+ * Criteria type constant to class name mapping
+ */
+global $COMPLETION_CRITERIA_TYPES;
+$COMPLETION_CRITERIA_TYPES = array(
+ COMPLETION_CRITERIA_TYPE_SELF => 'self',
+ COMPLETION_CRITERIA_TYPE_DATE => 'date',
+ COMPLETION_CRITERIA_TYPE_UNENROL => 'unenrol',
+ COMPLETION_CRITERIA_TYPE_ACTIVITY => 'activity',
+ COMPLETION_CRITERIA_TYPE_DURATION => 'duration',
+ COMPLETION_CRITERIA_TYPE_GRADE => 'grade',
+ COMPLETION_CRITERIA_TYPE_ROLE => 'role',
+ COMPLETION_CRITERIA_TYPE_COURSE => 'course',
+);
+
+
+/**
+ * Completion criteria abstract definition
+ */
+abstract class completion_criteria extends data_object {
+ /**
+ * DB Table
+ * @var string $table
+ */
+ public $table = 'course_completion_criteria';
+
+ /**
+ * Array of required table fields, must start with 'id'.
+ * @var array $required_fields
+ */
+ public $required_fields = array('id', 'course', 'criteriatype', 'module', 'moduleinstance', 'courseinstance', 'enrolperiod', 'date', 'gradepass', 'role');
+
+ /**
+ * Course id
+ * @var int
+ */
+ public $course;
+
+ /**
+ * Criteria type
+ * One of the COMPLETION_CRITERIA_TYPE_* constants
+ * @var int
+ */
+ public $criteriatype;
+
+ /**
+ * Module type this criteria relates to (for activity criteria)
+ * @var string
+ */
+ public $module;
+
+ /**
+ * Course module instance id this criteria relates to (for activity criteria)
+ * @var int
+ */
+ public $moduleinstance;
+
+ /**
+ * Period after enrolment completion will be triggered (for period criteria)
+ * @var int (days)
+ */
+ public $enrolperiod;
+
+ /**
+ * Date of course completion (for date criteria)
+ * @var int (timestamp)
+ */
+ public $date;
+
+ /**
+ * Passing grade required to complete course (for grade completion)
+ * @var float
+ */
+ public $gradepass;
+
+ /**
+ * Role ID that has the ability to mark a user as complete (for role completion)
+ * @var int
+ */
+ public $role;
+
+ /**
+ * Finds and returns all data_object instances based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return array array of data_object insatnces or false if none found.
+ */
+ public static function fetch_all($params) {}
+
+ /**
+ * Factory method for creating correct class object
+ * @static
+ * @param array
+ * @return object
+ */
+ public static function factory($params) {
+ global $CFG, $COMPLETION_CRITERIA_TYPES;
+
+ if (!isset($params->criteriatype) || !isset($COMPLETION_CRITERIA_TYPES[$params->criteriatype])) {
+ error('invalidcriteriatype', 'completion');
+ }
+
+ $class = 'completion_criteria_'.$COMPLETION_CRITERIA_TYPES[$params->criteriatype];
+ require_once($CFG->libdir.'/completion/'.$class.'.php');
+
+ return new $class($params, false);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ abstract public function config_form_display(&$mform, $data = null);
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ abstract public function update_config(&$data);
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ abstract public function review($completion, $mark = true);
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ abstract public function get_title();
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ abstract public function get_title_detailed();
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ abstract public function get_type_title();
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ abstract public function get_details($completion);
+
+ /**
+ * Return criteria status text for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return string
+ */
+ public function get_status($completion) {
+ return $completion->is_complete() ? get_string('yes') : get_string('no');
+ }
+
+ /**
+ * Return true if the criteria's current status is different to what is sorted
+ * in the database, e.g. pending an update
+ *
+ * @param object $completion The user's criteria completion record
+ * @return bool
+ */
+ public function is_pending($completion) {
+ $review = $this->review($completion, false);
+
+ return $review !== $completion->is_complete();
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - completion on activity completion
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_activity extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_ACTIVITY;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_ACTIVITY;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+ $mform->addElement('checkbox', 'criteria_activity['.$data->id.']', ucfirst(self::get_mod_name($data->module)).' - '.$data->name);
+
+ if ($this->id) {
+ $mform->setDefault('criteria_activity['.$data->id.']', 1);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+ global $DB;
+
+ if (!empty($data->criteria_activity) && is_array($data->criteria_activity)) {
+
+ $this->course = $data->id;
+
+ foreach (array_keys($data->criteria_activity) as $activity) {
+
+ $module = $DB->get_record('course_modules', array('id' => $activity));
+ $this->module = self::get_mod_name($module->module);
+ $this->moduleinstance = $activity;
+ $this->id = NULL;
+ $this->insert();
+ }
+ }
+ }
+
+ /**
+ * Get module instance module type
+ * @static
+ * @access public
+ * @param int $type Module type id
+ * @return string
+ */
+ public static function get_mod_name($type) {
+ static $types;
+
+ if (!is_array($types)) {
+ global $DB;
+ $types = $DB->get_records('modules');
+ }
+
+ return $types[$type]->name;
+ }
+
+ /**
+ * Get module instance
+ * @access public
+ * @return object|false
+ */
+ public function get_mod_instance() {
+ global $DB;
+
+ return $DB->get_record_sql(
+ "
+ SELECT
+ m.*
+ FROM
+ {{$this->module}} m
+ INNER JOIN
+ {course_modules} cm
+ ON cm.id = {$this->moduleinstance}
+ AND m.id = cm.instance
+ "
+ );
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true) {
+ global $DB;
+
+ $course = $DB->get_record('course', array('id' => $completion->course));
+ $cm = $DB->get_record('course_modules', array('id' => $this->moduleinstance));
+ $info = new completion_info($course);
+
+ $data = $info->get_data($cm, false, $completion->userid);
+
+ // If the activity is complete
+ if (in_array($data->completionstate, array(COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS))) {
+ if ($mark) {
+ $completion->mark_complete();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('activitiescompleted', 'completion');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ global $DB;
+ $module = $DB->get_record('course_modules', array('id' => $this->moduleinstance));
+ $activity = $DB->get_record($this->module, array('id' => $module->instance));
+
+ return shorten_text(urldecode($activity->name));
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('activities', 'completion');
+ }
+
+ /**
+ * Find user's who have completed this criteria
+ * @access public
+ * @return void
+ */
+ public function cron() {
+ global $DB;
+
+ // Get all users who meet this criteria
+ $sql = '
+ SELECT DISTINCT
+ c.id AS course,
+ cr.date AS date,
+ cr.id AS criteriaid,
+ ra.userid AS userid,
+ mc.timemodified AS timecompleted
+ FROM
+ {course_completion_criteria} cr
+ INNER JOIN
+ {course} c
+ ON cr.course = c.id
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ INNER JOIN
+ {course_modules_completion} mc
+ ON mc.coursemoduleid = cr.moduleinstance
+ AND mc.userid = ra.userid
+ LEFT JOIN
+ {course_completion_crit_compl} cc
+ ON cc.criteriaid = cr.id
+ AND cc.userid = ra.userid
+ WHERE
+ cr.criteriatype = '.COMPLETION_CRITERIA_TYPE_ACTIVITY.'
+ AND con.contextlevel = '.CONTEXT_COURSE.'
+ AND c.enablecompletion = 1
+ AND cc.id IS NULL
+ AND (
+ mc.completionstate = '.COMPLETION_COMPLETE.'
+ OR mc.completionstate = '.COMPLETION_COMPLETE_PASS.'
+ )
+ ';
+
+ // Loop through completions, and mark as complete
+ if ($rs = $DB->get_recordset_sql($sql)) {
+ foreach ($rs as $record) {
+
+ $completion = new completion_criteria_completion((array)$record);
+ $completion->mark_complete($record->timecompleted);
+ }
+
+ $rs->close();
+ }
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ global $DB, $CFG;
+
+ // Get completion info
+ $course = new object();
+ $course->id = $completion->course;
+ $info = new completion_info($course);
+
+ $module = $DB->get_record('course_modules', array('id' => $this->moduleinstance));
+ $data = $info->get_data($module, false, $completion->userid);
+
+ $activity = $DB->get_record($this->module, array('id' => $module->instance));
+
+ $details = array();
+ $details['type'] = $this->get_title();
+ $details['criteria'] = '<a href="'.$CFG->wwwroot.'/mod/'.$this->module.'/view.php?id='.$this->moduleinstance.'">'.$activity->name.'</a>';
+
+ // Build requirements
+ $details['requirement'] = array();
+
+ if ($module->completion == 1) {
+ $details['requirement'][] = get_string('markingyourselfcomplete', 'completion');
+ } elseif ($module->completion == 2) {
+ if ($module->completionview) {
+ $details['requirement'][] = get_string('viewingactivity', 'completion', $this->module);
+ }
+
+ if ($module->completiongradeitemnumber) {
+ $details['requirement'][] = get_string('achievinggrade', 'completion');
+ }
+ }
+
+ $details['requirement'] = implode($details['requirement'], ', ');
+
+ $details['status'] = '';
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Completion data for a specific user, course and critieria
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once($CFG->libdir.'/completion/data_object.php');
+
+
+/**
+ * Completion data for a specific user, course and critieria
+ */
+class completion_criteria_completion extends data_object {
+
+ /**
+ * DB Table
+ * @var string $table
+ */
+ public $table = 'course_completion_crit_compl';
+
+ /**
+ * Array of required table fields, must start with 'id'.
+ * @var array $required_fields
+ */
+ public $required_fields = array('id', 'userid', 'course', 'criteriaid', 'gradefinal', 'rpl', 'deleted', 'unenroled', 'timecompleted');
+
+ /**
+ * User ID
+ * @access public
+ * @var int
+ */
+ public $userid;
+
+ /**
+ * Course ID
+ * @access public
+ * @var int
+ */
+ public $course;
+
+ /**
+ * The id of the course completion criteria this completion references
+ * @access public
+ * @var int
+ */
+ public $criteriaid;
+
+ /**
+ * The final grade for the user in the course (if completing a grade criteria)
+ * @access public
+ * @var float
+ */
+ public $gradefinal;
+
+ /**
+ * Record of prior learning, leave blank if none
+ * @access public
+ * @var string
+ */
+ public $rpl;
+
+ /**
+ * Course deleted flag
+ * @access public
+ * @var boolean
+ */
+ public $deleted;
+
+ /**
+ * Timestamp of user unenrolment (if completing a unenrol criteria)
+ * @access public
+ * @var int (timestamp)
+ */
+ public $unenroled;
+
+ /**
+ * Timestamp of course criteria completion
+ * @see completion_criteria_completion::mark_complete()
+ * @access public
+ * @var int (timestamp)
+ */
+ public $timecompleted;
+
+ /**
+ * Associated criteria object
+ * @access private
+ * @var object completion_criteria
+ */
+ private $_criteria;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['deleted'] = null;
+ return self::fetch_helper('course_completion_crit_compl', __CLASS__, $params);
+ }
+
+ /**
+ * Finds and returns all data_object instances based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return array array of data_object insatnces or false if none found.
+ */
+ public static function fetch_all($params) {}
+
+ /**
+ * Return status of this criteria completion
+ * @access public
+ * @return boolean
+ */
+ public function is_complete() {
+ return (bool) $this->timecompleted;
+ }
+
+ /**
+ * Mark this criteria complete for the associated user
+ *
+ * This method creates a course_completion_crit_compl record
+ * @access public
+ * @return void
+ */
+ public function mark_complete() {
+ // Create record
+ $this->timecompleted = time();
+
+ // Save record
+ if ($this->id) {
+ $this->update();
+ } else {
+ $this->insert();
+ }
+
+ // Mark course completion record as started (if not already)
+ $cc = array(
+ 'course' => $this->course,
+ 'userid' => $this->userid
+ );
+ $ccompletion = new completion_completion($cc);
+ $ccompletion->mark_inprogress($this->timecompleted);
+ }
+
+ /**
+ * Attach a preloaded criteria object to this object
+ * @access public
+ * @param $criteria object completion_criteria
+ * @return void
+ */
+ public function attach_criteria(completion_criteria $criteria) {
+ $this->_criteria = $criteria;
+ }
+
+ /**
+ * Return the associated criteria with this completion
+ * If nothing attached, load from the db
+ * @access public
+ * @return object completion_criteria
+ */
+ public function get_criteria() {
+
+ if (!$this->_criteria)
+ {
+ global $DB;
+
+ $params = array(
+ 'id' => $this->criteriaid
+ );
+
+ $record = $DB->get_record('course_completion_criteria', $params);
+
+ $this->attach_criteria(completion_criteria::factory($record));
+ }
+
+ return $this->_criteria;
+ }
+
+ /**
+ * Return criteria status text for display in reports
+ * @see completion_criteria::get_status()
+ * @access public
+ * @return string
+ */
+ public function get_status() {
+ return $this->_criteria->get_status($this);
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - completion on course completion
+ *
+ * This course completion criteria depends on another course with
+ * completion enabled to be marked as complete for this user
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_course extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_COURSE;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_COURSE;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+ global $CFG;
+
+ $link = "<a href=\"{$CFG->wwwroot}/course/view.php?id={$data->id}\">".s($data->fullname).'</a>';
+ $mform->addElement('checkbox', 'criteria_course['.$data->id.']', $link);
+
+ if ($this->id) {
+ $mform->setDefault('criteria_course['.$data->id.']', 1);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+
+ if (!empty($data->criteria_course) && is_array($data->criteria_course)) {
+
+ $this->course = $data->id;
+
+ foreach ($data->criteria_course as $course) {
+
+ $this->courseinstance = $course;
+ $this->id = NULL;
+ $this->insert();
+ }
+ }
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true) {
+
+ $course = get_record('course', 'id', $this->courseinstance);
+ $info = new completion_info($course);
+
+ // If the course is complete
+ if ($info->is_course_complete($completion->userid)) {
+
+ if ($mark) {
+ $completion->mark_complete();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('prerequisitescompleted', 'completion');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ $prereq = get_record('course', 'id', $this->courseinstance);
+ return shorten_text(urldecode($prereq->fullname));
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('prerequisites', 'completion');
+ }
+
+ /**
+ * Find user's who have completed this criteria
+ * @access public
+ * @return void
+ */
+ public function cron() {
+
+ global $DB;
+
+ // Get all users who meet this criteria
+ $sql = "
+ SELECT DISTINCT
+ c.id AS course,
+ cr.id AS criteriaid,
+ ra.userid AS userid,
+ cc.timecompleted AS timecompleted
+ FROM
+ {course_completion_criteria} cr
+ INNER JOIN
+ {course} c
+ ON cr.course = c.id
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ INNER JOIN
+ {course_completions} cc
+ ON cc.course = cr.courseinstance
+ AND cc.userid = ra.userid
+ LEFT JOIN
+ {course_completion_crit_compl} ccc
+ ON ccc.criteriaid = cr.id
+ AND ccc.userid = ra.userid
+ WHERE
+ cr.criteriatype = ".COMPLETION_CRITERIA_TYPE_COURSE."
+ AND con.contextlevel = ".CONTEXT_COURSE."
+ AND c.enablecompletion = 1
+ AND ccc.id IS NULL
+ AND cc.timecompleted IS NOT NULL
+ ";
+
+ // Loop through completions, and mark as complete
+ if ($rs = $DB->get_recordset_sql($sql)) {
+ foreach ($rs as $record) {
+ $completion = new completion_criteria_completion((array)$record);
+ $completion->mark_complete($record->timecompleted);
+ }
+
+ $rs->close();
+ }
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ global $CFG;
+
+ // Get completion info
+ $course = new object();
+ $course->id = $completion->course;
+ $info = new completion_info($course);
+
+ $prereq = get_record('course', 'id', $this->courseinstance);
+ $prereq_info = new completion_info($prereq);
+
+ $details = array();
+ $details['type'] = $this->get_title();
+ $details['criteria'] = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$this->courseinstance.'">'.s($prereq->fullname).'</a>';
+ $details['requirement'] = get_string('coursecompleted', 'completion');
+ $details['status'] = '<a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$this->courseinstance.'">'.get_string('seedetails', 'completion').'</a>';
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - completion on specified date
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_date extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_DATE;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_DATE;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null)
+ {
+ $mform->addElement('checkbox', 'criteria_date', get_string('enable'));
+ $mform->addElement('date', 'criteria_date_value', get_string('afterspecifieddate', 'completion'));
+
+ // If instance of criteria exists
+ if ($this->id) {
+ $mform->setDefault('criteria_date', 1);
+ $mform->setDefault('criteria_date_value', $this->timeend);
+ } else {
+ $mform->setDefault('criteria_date_value', time() + 3600 * 24);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+
+ if (!empty($data->criteria_date)) {
+ $this->course = $data->id;
+ $date = $data->criteria_date_value;
+ $this->timeend = strtotime($date['Y'].'-'.$date['M'].'-'.$date['d']);
+ $this->insert();
+ }
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true)
+ {
+ // If current time is past timeend
+ if ($this->timeend && $this->timeend < time()) {
+ if ($mark) {
+ $completion->mark_complete();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('date');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ return userdate($this->timeend, '%d-%h-%y');
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('date');
+ }
+
+
+ /**
+ * Return criteria status text for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return string
+ */
+ public function get_status($completion) {
+ return $completion->is_complete() ? get_string('yes') : userdate($this->timeend, '%d-%h-%y');
+ }
+
+ /**
+ * Find user's who have completed this criteria
+ * @access public
+ * @return void
+ */
+ public function cron() {
+ global $DB;
+
+ // Get all users who match meet this criteria
+ $sql = '
+ SELECT DISTINCT
+ c.id AS course,
+ cr.timeend AS timeend,
+ cr.id AS criteriaid,
+ ra.userid AS userid
+ FROM
+ {course_completion_criteria} cr
+ INNER JOIN
+ {course} c
+ ON cr.course = c.id
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ LEFT JOIN
+ {course_completion_crit_compl} cc
+ ON cc.criteriaid = cr.id
+ AND cc.userid = ra.userid
+ WHERE
+ cr.criteriatype = '.COMPLETION_CRITERIA_TYPE_DATE.'
+ AND con.contextlevel = '.CONTEXT_COURSE.'
+ AND c.enablecompletion = 1
+ AND cc.id IS NULL
+ AND cr.timeend < ?
+ ';
+
+ // Loop through completions, and mark as complete
+ if ($rs = $DB->get_recordset_sql($sql, array(time()))) {
+ foreach ($rs as $record) {
+
+ $completion = new completion_criteria_completion((array)$record);
+ $completion->mark_complete($record->timeend);
+ }
+
+ $rs->close();
+ }
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ $details = array();
+ $details['type'] = get_string('datepassed', 'completion');
+ $details['criteria'] = get_string('remainingenroleduntildate', 'completion');
+ $details['requirement'] = userdate($this->timeend, '%d %B %Y');
+ $details['status'] = '';
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - completion after specific duration from course enrolment
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_duration extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_DURATION;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_DURATION;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+
+ $mform->addElement('checkbox', 'criteria_duration', get_string('enable'));
+
+ $thresholdmenu=array();
+ for ($i=1; $i<=30; $i++) {
+ $seconds = $i * 86400;
+ $thresholdmenu[$seconds] = get_string('numdays', '', $i);
+ }
+ $mform->addElement('select', 'criteria_duration_days', get_string('daysafterenrolment', 'completion'), $thresholdmenu);
+
+ if ($this->id) {
+ $mform->setDefault('criteria_duration', 1);
+ $mform->setDefault('criteria_duration_days', $this->enrolperiod);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+
+ if (!empty($data->criteria_duration)) {
+ $this->course = $data->id;
+ $this->enrolperiod = $data->criteria_duration_days;
+ $this->insert();
+ }
+ }
+
+ /**
+ * Get the time this user was enroled
+ * @param object $completion
+ * @return int
+ */
+ private function get_timeenrolled($completion) {
+ global $DB;
+
+ $context = get_context_instance(CONTEXT_COURSE, $this->course);
+ return $DB->get_field('role_assignments', 'timestart', array('contextid' => $context->id, 'userid' => $completion->userid));
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true) {
+ $timeenrolled = $this->get_timeenrolled($completion);
+
+ // If duration since enrollment has passed
+ if (!$this->enrolperiod || !$timeenrolled) {
+ return false;
+ }
+
+ if (time() > ($timeenrolled + $this->enrolperiod)) {
+ if ($mark) {
+ $completion->mark_complete();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('enrolmentduration', 'completion');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ return ceil($this->enrolperiod / (60 * 60 * 24)) . ' days';
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('days', 'completion');
+ }
+
+ /**
+ * Return criteria status text for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return string
+ */
+ public function get_status($completion) {
+ $timeenrolled = $this->get_timeenrolled($completion);
+ $timeleft = $timeenrolled + $this->enrolperiod - time();
+ $enrolperiod = ceil($this->enrolperiod / (60 * 60 * 24));
+
+ $daysleft = ceil($timeleft / (60 * 60 * 24));
+
+ return ($daysleft > 0 ? $daysleft : 0).' of '.$enrolperiod;
+ }
+
+ /**
+ * Find user's who have completed this criteria
+ * @access public
+ * @return void
+ */
+ public function cron() {
+ global $DB;
+
+ // Get all users who match meet this criteria
+ $sql = '
+ SELECT DISTINCT
+ c.id AS course,
+ cr.date AS date,
+ cr.id AS criteriaid,
+ ra.userid AS userid,
+ (ra.timestart + cr.enrolperiod) AS timecompleted
+ FROM
+ {course_completion_criteria} cr
+ INNER JOIN
+ {course} c
+ ON cr.course = c.id
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ LEFT JOIN
+ {course_completion_crit_compl} cc
+ ON cc.criteriaid = cr.id
+ AND cc.userid = ra.userid
+ WHERE
+ cr.criteriatype = '.COMPLETION_CRITERIA_TYPE_DURATION.'
+ AND con.contextlevel = '.CONTEXT_COURSE.'
+ AND c.enablecompletion = 1
+ AND cc.id IS NULL
+ AND ra.timestart + cr.enrolperiod < ?
+ ';
+
+ // Loop through completions, and mark as complete
+ if ($rs = $DB->get_recordset_sql($sql, array(time()))) {
+ foreach ($rs as $record) {
+
+ $completion = new completion_criteria_completion((array)$record);
+ $completion->mark_complete($record->timecompleted);
+ }
+
+ $rs->close();
+ }
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ $details = array();
+ $details['type'] = get_string('periodpostenrolment', 'completion');
+ $details['criteria'] = get_string('remainingenroledfortime', 'completion');
+ $details['requirement'] = get_string('xdays', 'completion', ceil($this->enrolperiod / (60*60*24)));
+
+ // Get status
+ $timeenrolled = $this->get_timeenrolled($completion);
+ $timepassed = time() - $timeenrolled;
+ $details['status'] = get_string('xdays', 'completion', floor($timepassed / (60*60*24)));
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - completion on achieving course grade
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once $CFG->dirroot.'/grade/lib.php';
+require_once $CFG->dirroot.'/grade/querylib.php';
+
+/**
+ * Course completion critieria - completion on achieving course grade
+ */
+class completion_criteria_grade extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_GRADE;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_GRADE;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+ $mform->addElement('checkbox', 'criteria_grade', get_string('enable'));
+ $mform->addElement('text', 'criteria_grade_value', get_string('passinggrade', 'completion'));
+ $mform->setDefault('criteria_grade_value', $data);
+
+ if ($this->id) {
+ $mform->setDefault('criteria_grade', 1);
+ $mform->setDefault('criteria_grade_value', $this->gradepass);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+
+ // TODO validation
+ if (!empty($data->criteria_grade) && is_numeric($data->criteria_grade_value))
+ {
+ $this->course = $data->id;
+ $this->gradepass = $data->criteria_grade_value;
+ $this->insert();
+ }
+ }
+
+ /**
+ * Get user's course grade in this course
+ * @static
+ * @access private
+ * @param object $completion
+ * @return float
+ */
+ private function get_grade($completion) {
+ $grade = grade_get_course_grade($completion->userid, $this->course);
+ return $grade->grade;
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true) {
+ // Get user's course grade
+ $grade = $this->get_grade($completion);
+
+ // If user's current course grade is higher than the required pass grade
+ if ($this->gradepass && $this->gradepass <= $grade) {
+ if ($mark) {
+ $completion->gradefinal = $grade;
+ $completion->mark_complete();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('grade');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ return (float) $this->gradepass . '% required';
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('grade');
+ }
+
+ /**
+ * Return criteria status text for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return string
+ */
+ public function get_status($completion) {
+ // Cast as floats to get rid of excess decimal places
+ $grade = (float) $this->get_grade($completion);
+ $gradepass = (float) $this->gradepass;
+
+ if ($grade) {
+ return $grade.'% ('.$gradepass.'% to pass)';
+ } else {
+ return $gradepass.'% to pass';
+ }
+ }
+
+ /**
+ * Find user's who have completed this criteria
+ * @access public
+ * @return void
+ */
+ public function cron() {
+ global $DB;
+
+ // Get all users who meet this criteria
+ $sql = '
+ SELECT DISTINCT
+ c.id AS course,
+ cr.date AS date,
+ cr.id AS criteriaid,
+ ra.userid AS userid,
+ gg.finalgrade AS gradefinal,
+ gg.timemodified AS timecompleted
+ FROM
+ {course_completion_criteria} cr
+ INNER JOIN
+ {course} c
+ ON cr.course = c.id
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ INNER JOIN
+ {grade_items} gi
+ ON gi.courseid = c.id
+ AND gi.itemtype = \'course\'
+ INNER JOIN
+ {grade_grades} gg
+ ON gg.itemid = gi.id
+ AND gg.userid = ra.userid
+ LEFT JOIN
+ {course_completion_crit_compl} cc
+ ON cc.criteriaid = cr.id
+ AND cc.userid = ra.userid
+ WHERE
+ cr.criteriatype = '.COMPLETION_CRITERIA_TYPE_GRADE.'
+ AND con.contextlevel = '.CONTEXT_COURSE.'
+ AND c.enablecompletion = 1
+ AND cc.id IS NULL
+ AND gg.finalgrade >= cr.gradepass
+ ';
+
+ // Loop through completions, and mark as complete
+ if ($rs = $DB->get_recordset_sql($sql)) {
+ foreach ($rs as $record) {
+
+ $completion = new completion_criteria_completion((array)$record);
+ $completion->mark_complete($record->timecompleted);
+ }
+
+ $rs->close();
+ }
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ $details = array();
+ $details['type'] = get_string('coursegrade', 'completion');
+ $details['criteria'] = get_string('passinggrade', 'completion');
+ $details['requirement'] = ((float)$this->gradepass).'%';
+ $details['status'] = '';
+
+ $grade = (float)$this->get_grade($completion);
+ if ($grade) {
+ $details['status'] = $grade.'%';
+ }
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - marked by role
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_role extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_ROLE;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_ROLE;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+
+ $mform->addElement('checkbox', 'criteria_role['.$data->id.']', $data->name);
+
+ if ($this->id) {
+ $mform->setDefault('criteria_role['.$data->id.']', 1);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+
+ if (!empty($data->criteria_role) && is_array($data->criteria_role)) {
+
+ $this->course = $data->id;
+
+ foreach (array_keys($data->criteria_role) as $role) {
+
+ $this->role = $role;
+ $this->id = NULL;
+ $this->insert();
+ }
+ }
+ }
+
+ /**
+ * Mark this criteria as complete
+ * @access public
+ * @param object $completion The user's completion record
+ * @return void
+ */
+ public function complete($completion) {
+ $this->review($completion, true, true);
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true, $is_complete = false) {
+ // If we are marking this as complete
+ if ($is_complete && $mark)
+ {
+ $completion->completedself = 1;
+ $completion->mark_complete();
+
+ return true;
+ }
+
+ return $completion->is_complete();
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ global $DB;
+ $role = $DB->get_field('role', 'name', array('id' => $this->role));
+ return $role;
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ global $DB;
+ return $DB->get_field('role', 'name', array('id' => $this->role));
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('approval', 'completion');
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ $details = array();
+ $details['type'] = get_string('manualcompletionby', 'completion');
+ $details['criteria'] = $this->get_title();
+ $details['requirement'] = get_string('markedcompleteby', 'completion', $details['criteria']);
+ $details['status'] = '';
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - student self marked
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_self extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_SELF;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_SELF;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+ $mform->addElement('checkbox', 'criteria_self', get_string('enable'));
+
+ if ($this->id ) {
+ $mform->setDefault('criteria_self', 1);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+ if (!empty($data->criteria_self)) {
+ $this->course = $data->id;
+ $this->insert();
+ }
+ }
+
+ /**
+ * Mark this criteria as complete
+ * @access public
+ * @param object $completion The user's completion record
+ * @return void
+ */
+ public function complete($completion) {
+ $this->review($completion, true, true);
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true, $is_complete = false) {
+ // If we are marking this as complete
+ if ($is_complete && $mark)
+ {
+ $completion->completedself = 1;
+ $completion->mark_complete();
+
+ return true;
+ }
+
+ return $completion->is_complete();
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('selfcompletion', 'completion');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ return $this->get_title();
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('self', 'completion');
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ $details = array();
+ $details['type'] = $this->get_title();
+ $details['criteria'] = $this->get_title();
+ $details['requirement'] = get_string('markingyourselfcomplete', 'completion');
+ $details['status'] = '';
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+/**
+ * Course completion critieria - completion on unenrolment
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class completion_criteria_unenrol extends completion_criteria {
+
+ /**
+ * Criteria type constant
+ * @var int
+ */
+ public $criteriatype = COMPLETION_CRITERIA_TYPE_UNENROL;
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static function fetch($params) {
+ $params['criteriatype'] = COMPLETION_CRITERIA_TYPE_UNENROL;
+ return self::fetch_helper('course_completion_criteria', __CLASS__, $params);
+ }
+
+ /**
+ * Add appropriate form elements to the critieria form
+ * @access public
+ * @param object $mform Moodle forms object
+ * @param mixed $data optional
+ * @return void
+ */
+ public function config_form_display(&$mform, $data = null) {
+ $mform->addElement('checkbox', 'criteria_unenrol', 'Completion on unenrolment');
+
+ if ($this->id) {
+ $mform->setDefault('criteria_unenrol', 1);
+ }
+ }
+
+ /**
+ * Update the criteria information stored in the database
+ * @access public
+ * @param array $data Form data
+ * @return void
+ */
+ public function update_config(&$data) {
+ if (!empty($data->criteria_unenrol)) {
+ $this->course = $data->id;
+ $this->insert();
+ }
+ }
+
+ /**
+ * Review this criteria and decide if the user has completed
+ * @access public
+ * @param object $completion The user's completion record
+ * @param boolean $mark Optionally set false to not save changes to database
+ * @return boolean
+ */
+ public function review($completion, $mark = true) {
+ // Check enrolment
+ return false;
+ }
+
+ /**
+ * Return criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title() {
+ return get_string('unenrol');
+ }
+
+ /**
+ * Return a more detailed criteria title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_title_detailed() {
+ return $this->get_title();
+ }
+
+ /**
+ * Return criteria type title for display in reports
+ * @access public
+ * @return string
+ */
+ public function get_type_title() {
+ return get_string('unenrol');
+ }
+
+ /**
+ * Return criteria progress details for display in reports
+ * @access public
+ * @param object $completion The user's completion record
+ * @return array
+ */
+ public function get_details($completion) {
+ $details = array();
+ $details['type'] = get_string('unenrolment', 'completion');
+ $details['criteria'] = get_string('unenrolment', 'completion');
+ $details['requirement'] = get_string('unenrolingfromcourse', 'completion');
+ $details['status'] = '';
+
+ return $details;
+ }
+}
--- /dev/null
+<?php
+
+// 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/>.
+
+
+/**
+ * Cron job for reviewing and aggregating course completion criteria
+ *
+ * @package moodlecore
+ * @copyright 2009 Catalyst IT Ltd
+ * @author Aaron Barnes <aaronb@catalyst.net.nz>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once $CFG->libdir.'/completionlib.php';
+
+
+/**
+ * Update user's course completion statuses
+ *
+ * First update all criteria completions, then
+ * aggregate all criteria completions and update
+ * overall course completions
+ *
+ * @return void
+ */
+function completion_cron() {
+
+ completion_cron_mark_started();
+
+ completion_cron_criteria();
+
+ completion_cron_completions();
+}
+
+/**
+ * Mark users as started if the config option is set
+ *
+ * @return void
+ */
+function completion_cron_mark_started() {
+ global $CFG, $DB;
+
+ if (debugging()) {
+ mtrace('Marking users as started');
+ }
+
+ $roles = '';
+ if (!empty($CFG->progresstrackedroles)) {
+ $roles = 'AND ra.roleid IN ('.$CFG->progresstrackedroles.')';
+ }
+
+ $sql = "
+ SELECT DISTINCT
+ c.id AS course,
+ ra.userid AS userid,
+ crc.id AS completionid,
+ MIN(ra.timestart) AS timestarted
+ FROM
+ {course} c
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ LEFT JOIN
+ {course_completions} crc
+ ON crc.course = c.id
+ AND crc.userid = ra.userid
+ WHERE
+ con.contextlevel = ".CONTEXT_COURSE."
+ AND c.enablecompletion = 1
+ AND c.completionstartonenrol = 1
+ AND crc.timeenrolled IS NULL
+ AND (ra.timeend IS NULL OR ra.timeend > ".time().")
+ {$roles}
+ GROUP BY
+ c.id,
+ ra.userid,
+ crc.id
+ ORDER BY
+ course,
+ userid
+ ";
+
+ // Check if result is empty
+ if (!$rs = $DB->get_recordset_sql($sql)) {
+ return;
+ }
+
+ // Grab records for current user/course
+ foreach ($rs as $record) {
+ $completion = new completion_completion();
+ $completion->userid = $record->userid;
+ $completion->course = $record->course;
+ $completion->timeenrolled = $record->timestarted;
+
+ if ($record->completionid) {
+ $completion->id = $record->completionid;
+ }
+
+ $completion->mark_enrolled();
+
+ if (debugging()) {
+ mtrace('Marked started user '.$record->userid.' in course '.$record->course);
+ }
+ }
+
+ $rs->close();
+}
+
+/**
+ * Run installed criteria's data aggregation methods
+ *
+ * Loop through each installed criteria and run the
+ * cron() method if it exists
+ *
+ * @return void
+ */
+function completion_cron_criteria() {
+
+ // Process each criteria type
+ global $CFG, $COMPLETION_CRITERIA_TYPES;
+
+ foreach ($COMPLETION_CRITERIA_TYPES as $type) {
+
+ $object = 'completion_criteria_'.$type;
+ require_once $CFG->libdir.'/completion/'.$object.'.php';
+
+ $class = new $object();
+
+ // Run the criteria type's cron method, if it has one
+ if (method_exists($class, 'cron')) {
+
+ if (debugging()) {
+ mtrace('Running '.$object.'->cron()');
+ }
+ $class->cron();
+ }
+ }
+}
+
+/**
+ * Aggregate each user's criteria completions
+ *
+ * @return void
+ */
+function completion_cron_completions() {
+ global $DB;
+
+ if (debugging()) {
+ mtrace('Aggregating completions');
+ }
+
+ // Save time started
+ $timestarted = time();
+
+ // Grab all criteria and their associated criteria completions
+ $sql = '
+ SELECT DISTINCT
+ c.id AS course,
+ cr.id AS criteriaid,
+ ra.userid AS userid,
+ cr.criteriatype AS criteriatype,
+ cc.timecompleted AS timecompleted
+ FROM
+ {course_completion_criteria} cr
+ INNER JOIN
+ {course} c
+ ON cr.course = c.id
+ INNER JOIN
+ {context} con
+ ON con.instanceid = c.id
+ INNER JOIN
+ {role_assignments} ra
+ ON ra.contextid = con.id
+ LEFT JOIN
+ {course_completion_crit_compl} cc
+ ON cc.criteriaid = cr.id
+ AND cc.userid = ra.userid
+ LEFT JOIN
+ {course_completions} crc
+ ON crc.course = c.id
+ AND crc.userid = ra.userid
+ WHERE
+ con.contextlevel = '.CONTEXT_COURSE.'
+ AND c.enablecompletion = 1
+ AND crc.timecompleted IS NULL
+ AND crc.reaggregate > 0
+ ORDER BY
+ course,
+ userid
+ ';
+
+ // Check if result is empty
+ if (!$rs = $DB->get_recordset_sql($sql)) {
+ return;
+ }
+
+ $current_user = null;
+ $current_course = null;
+ $completions = array();
+
+ while (1) {
+
+ // Grab records for current user/course
+ foreach ($rs as $record) {
+ // If we are still grabbing the same users completions
+ if ($record->userid === $current_user && $record->course === $current_course) {
+ $completions[$record->criteriaid] = $record;
+ } else {
+ break;
+ }
+ }
+
+ // Aggregate
+ if (!empty($completions)) {
+
+ if (debugging()) {
+ mtrace('Aggregating completions for user '.$current_user.' in course '.$current_course);
+ }
+
+ // Get course info object
+ $info = new completion_info((object)array('id' => $current_course));
+
+ // Setup aggregation
+ $overall = $info->get_aggregation_method();
+ $activity = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ACTIVITY);
+ $prerequisite = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE);
+ $role = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ROLE);
+
+ $overall_status = null;
+ $activity_status = null;
+ $prerequisite_status = null;
+ $role_status = null;
+
+ // Get latest timecompleted
+ $timecompleted = null;
+
+ // Check each of the criteria
+ foreach ($completions as $params) {
+ $timecompleted = max($timecompleted, $params->timecompleted);
+
+ $completion = new completion_criteria_completion($params, false);
+
+ // Handle aggregation special cases
+ if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
+ completion_cron_aggregate($activity, $completion->is_complete(), $activity_status);
+ } else if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
+ completion_cron_aggregate($prerequisite, $completion->is_complete(), $prerequisite_status);
+ } else if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ROLE) {
+ completion_cron_aggregate($role, $completion->is_complete(), $role_status);
+ } else {
+ completion_cron_aggregate($overall, $completion->is_complete(), $overall_status);
+ }
+ }
+
+ // Include role criteria aggregation in overall aggregation
+ if ($role_status !== null) {
+ completion_cron_aggregate($overall, $role_status, $overall_status);
+ }
+
+ // Include activity criteria aggregation in overall aggregation
+ if ($activity_status !== null) {
+ completion_cron_aggregate($overall, $activity_status, $overall_status);
+ }
+
+ // Include prerequisite criteria aggregation in overall aggregation
+ if ($prerequisite_status !== null) {
+ completion_cron_aggregate($overall, $prerequisite_status, $overall_status);
+ }
+
+ // If aggregation status is true, mark course complete for user
+ if ($overall_status) {
+ if (debugging()) {
+ mtrace('Marking complete');
+ }
+
+ $ccompletion = new completion_completion(array('course' => $params->course, 'userid' => $params->userid));
+ $ccompletion->mark_complete($timecompleted);
+ }
+ }
+
+ // If this is the end of the recordset, break the loop
+ if (!$rs->valid()) {
+ $rs->close();
+ break;
+ }
+
+ // New/next user, update user details, reset completions
+ $current_user = $record->userid;
+ $current_course = $record->course;
+ $completions = array();
+ $completions[$record->criteriaid] = $record;
+ }
+
+ // Mark all users as aggregated
+ $sql = "
+ UPDATE
+ {course_completions}
+ SET
+ reaggregate = 0
+ WHERE
+ reaggregate < {$timestarted}
+ ";
+
+ $DB->execute($sql);
+}
+
+/**
+ * Aggregate criteria status's as per configured aggregation method
+ *
+ * @param int $method COMPLETION_AGGREGATION_* constant
+ * @param bool $data Criteria completion status
+ * @param bool|null $state Aggregation state
+ * @return void
+ */
+function completion_cron_aggregate($method, $data, &$state) {
+ if ($method == COMPLETION_AGGREGATION_ALL) {
+ if ($data && $state !== false) {
+ $state = true;
+ } else {
+ $state = false;
+ }
+ } elseif ($method == COMPLETION_AGGREGATION_ANY) {
+ if ($data) {
+ $state = true;
+ } else if (!$data && $state === null) {
+ $state = false;
+ }
+ }
+}
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * A data abstraction object that holds methods and attributes
+ * @abstract
+ */
+abstract class data_object {
+ /**
+ * Table that the class maps to in the database
+ * @var string $table
+ */
+ public $table;
+
+ /**
+ * Array of required table fields, must start with 'id'.
+ * @var array $required_fields
+ */
+ public $required_fields = array('id');
+
+ /**
+ * Array of optional fields with default values - usually long text information that is not always needed.
+ * If you want to create an instance without optional fields use: new data_object($only_required_fields, false);
+ * @var array $optional_fields
+ */
+ public $optional_fields = array();
+
+ /**
+ * The PK.
+ * @var int $id
+ */
+ public $id;
+
+ /**
+ * Constructor. Optionally (and by default) attempts to fetch corresponding row from DB.
+ * @param array $params an array with required parameters for this data object.
+ * @param boolean $fetch Whether to fetch corresponding row from DB or not,
+ * optional fields might not be defined if false used
+ */
+ public function __construct($params=NULL, $fetch=true) {
+ if (!empty($params) and (is_array($params) or is_object($params))) {
+ if ($fetch) {
+ if ($data = $this->fetch($params)) {
+ self::set_properties($this, $data);
+ } else {
+ self::set_properties($this, $this->optional_fields);//apply defaults for optional fields
+ self::set_properties($this, $params);
+ }
+
+ } else {
+ self::set_properties($this, $params);
+ }
+
+ } else {
+ self::set_properties($this, $this->optional_fields);//apply defaults for optional fields
+ }
+ }
+
+ /**
+ * Makes sure all the optional fields are loaded.
+ * If id present (==instance exists in db) fetches data from db.
+ * Defaults are used for new instances.
+ */
+ public function load_optional_fields() {
+ global $DB;
+ foreach ($this->optional_fields as $field=>$default) {
+ if (array_key_exists($field, $this)) {
+ continue;
+ }
+ if (empty($this->id)) {
+ $this->$field = $default;
+ } else {
+ $this->$field = $DB->get_field($this->table, $field, array('id', $this->id));
+ }
+ }
+ }
+
+ /**
+ * Finds and returns a data_object instance based on params.
+ * @static abstract
+ *
+ * @param array $params associative arrays varname=>value
+ * @return object data_object instance or false if none found.
+ */
+ public static abstract function fetch($params);
+
+ /**
+ * Finds and returns all data_object instances based on params.
+ *
+ * @param array $params associative arrays varname=>value
+ * @return array array of data_object insatnces or false if none found.
+ */
+ public static function fetch_all($params) {}
+
+ /**
+ * Factory method - uses the parameters to retrieve matching instance from the DB.
+ * @static final protected
+ * @return mixed object instance or false if not found
+ */
+ protected static function fetch_helper($table, $classname, $params) {
+ if ($instances = self::fetch_all_helper($table, $classname, $params)) {
+ if (count($instances) > 1) {
+ // we should not tolerate any errors here - problems might appear later
+ print_error('morethanonerecordinfetch','debug');
+ }
+ return reset($instances);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Factory method - uses the parameters to retrieve all matching instances from the DB.
+ * @static final protected
+ * @return mixed array of object instances or false if not found
+ */
+ public static function fetch_all_helper($table, $classname, $params) {
+ $instance = new $classname();
+
+ $classvars = (array)$instance;
+ $params = (array)$params;
+
+ $wheresql = array();
+
+ foreach ($params as $var=>$value) {
+ if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
+ continue;
+ }
+ if (is_null($value)) {
+ $wheresql[] = " $var IS NULL ";
+ } else {
+ $wheresql[] = " $var = ? ";
+ $params[] = $value;
+ }
+ }
+
+ if (empty($wheresql)) {
+ $wheresql = '';
+ } else {
+ $wheresql = implode("AND", $wheresql);
+ }
+
+ global $DB;
+ if ($datas = $DB->get_records_select($table, $wheresql, $params)) {
+
+ $result = array();
+ foreach($datas as $data) {
+ $instance = new $classname();
+ self::set_properties($instance, $data);
+ $result[$instance->id] = $instance;
+ }
+ return $result;
+
+ } else {
+
+ return false;
+ }
+ }
+
+ /**
+ * Updates this object in the Database, based on its object variables. ID must be set.
+ * @return boolean success
+ */
+ public function update() {
+ global $DB;
+
+ if (empty($this->id)) {
+ debugging('Can not update data object, no id!');
+ return false;
+ }
+
+ $data = $this->get_record_data();
+
+ $DB->update_record($this->table, $data);
+
+ $this->notify_changed(false);
+ return true;
+ }
+
+ /**
+ * Deletes this object from the database.
+ * @return boolean success
+ */
+ public function delete() {
+ global $DB;
+
+ if (empty($this->id)) {
+ debugging('Can not delete data object, no id!');
+ return false;
+ }
+
+ $data = $this->get_record_data();
+
+ if ($DB->delete_records($this->table, array('id'=>$this->id))) {
+ $this->notify_changed(true);
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns object with fields and values that are defined in database
+ */
+ public function get_record_data() {
+ $data = new object();
+
+ foreach ($this as $var=>$value) {
+ if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) {
+ if (is_object($value) or is_array($value)) {
+ debugging("Incorrect property '$var' found when inserting data object");
+ } else {
+ $data->$var = $value;
+ }
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Records this object in the Database, sets its id to the returned value, and returns that value.
+ * If successful this function also fetches the new object data from database and stores it
+ * in object properties.
+ * @return int PK ID if successful, false otherwise
+ */
+ public function insert() {
+ global $DB;
+
+ if (!empty($this->id)) {
+ debugging("Data object already exists!");
+ return false;
+ }
+
+ $data = $this->get_record_data();
+
+ $this->id = $DB->insert_record($this->table, $data);
+
+ // set all object properties from real db data
+ $this->update_from_db();
+
+ $this->notify_changed(false);
+ return $this->id;
+ }
+
+ /**
+ * Using this object's id field, fetches the matching record in the DB, and looks at
+ * each variable in turn. If the DB has different data, the db's data is used to update
+ * the object. This is different from the update() function, which acts on the DB record
+ * based on the object.
+ */
+ public function update_from_db() {
+ if (empty($this->id)) {
+ debugging("The object could not be used in its state to retrieve a matching record from the DB, because its id field is not set.");
+ return false;
+ }
+ global $DB;
+ if (!$params = $DB->get_record($this->table, array('id' => $this->id))) {
+ debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!");
+ return false;
+ }
+
+ self::set_properties($this, $params);
+
+ return true;
+ }
+
+ /**
+ * Given an associated array or object, cycles through each key/variable
+ * and assigns the value to the corresponding variable in this object.
+ * @static final
+ */
+ public static function set_properties(&$instance, $params) {
+ $params = (array) $params;
+ foreach ($params as $var => $value) {
+ if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) {
+ $instance->$var = $value;
+ }
+ }
+ }
+
+ /**
+ * Called immediately after the object data has been inserted, updated, or
+ * deleted in the database. Default does nothing, can be overridden to
+ * hook in special behaviour.
+ *
+ * @param bool $deleted
+ */
+ function notify_changed($deleted) {
+ }
+}
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+require_once $CFG->libdir.'/completion/completion_aggregation.php';
+require_once $CFG->libdir.'/completion/completion_criteria.php';
+require_once $CFG->libdir.'/completion/completion_completion.php';
+require_once $CFG->libdir.'/completion/completion_criteria_completion.php';
+
+
/**
* Contains a class used for tracking whether activities have been completed
* by students ('completion')
define('COMPLETION_CACHE_EXPIRY', 10*60);
// Combining completion condition. This is also the value you should return
-// if you don't have any applicable conditions.
+// if you don't have any applicable conditions. Used for activity completion.
/** Completion details should be ORed together and you should return false if
none apply */
define('COMPLETION_OR', false);
none apply */
define('COMPLETION_AND', true);
+// Course completion criteria aggregation methods
+define('COMPLETION_AGGREGATION_ALL', 1);
+define('COMPLETION_AGGREGATION_ANY', 2);
+
+
/**
* Class represents completion information for a course.
*
* @package moodlecore
*/
class completion_info {
- /** @var object Passed during construction */
+ /**
+ * Course object passed during construction
+ * @access private
+ * @var object
+ */
private $course;
+ /**
+ * Course id
+ * @access public
+ * @var int
+ */
+ public $course_id;
+
+ /**
+ * Completion criteria
+ * @access private
+ * @var array
+ * @see completion_info->get_criteria()
+ */
+ private $criteria;
+
+ /**
+ * Return array of aggregation methods
+ * @access public
+ * @return array
+ */
+ public static function get_aggregation_methods() {
+ return array(
+ COMPLETION_AGGREGATION_ALL => get_string('all'),
+ COMPLETION_AGGREGATION_ANY => get_string('any', 'completion'),
+ );
+ }
+
/**
* Constructs with course details.
*
*/
public function __construct($course) {
$this->course = $course;
+ $this->course_id = $course->id;
}
/**
echo '</span>';
}
}
- // OU specific end
+
+ /**
+ * Get a course completion for a user
+ * @access public
+ * @param $user_id int User id
+ * @param $criteriatype int Specific criteria type to return
+ * @return false|completion_criteria_completion
+ */
+ public function get_completion($user_id, $criteriatype) {
+ $completions = $this->get_completions($user_id, $criteriatype);
+
+ if (empty($completions)) {
+ return false;
+ } elseif (count($completions) > 1) {
+ print_error('multipleselfcompletioncriteria', 'completion');
+ }
+
+ return $completions[0];
+ }
+
+ /**
+ * Get all course criteria's completion objects for a user
+ * @access public
+ * @param $user_id int User id
+ * @param $criteriatype int optional Specific criteria type to return
+ * @return array
+ */
+ public function get_completions($user_id, $criteriatype = null) {
+ $criterion = $this->get_criteria($criteriatype);
+
+ $completions = array();
+
+ foreach ($criterion as $criteria) {
+ $params = array(
+ 'course' => $this->course_id,
+ 'userid' => $user_id,
+ 'criteriaid' => $criteria->id
+ );
+
+ $completion = new completion_criteria_completion($params);
+ $completion->attach_criteria($criteria);
+
+ $completions[] = $completion;
+ }
+
+ return $completions;
+ }
+
+ /**
+ * Get completion object for a user and a criteria
+ * @access public
+ * @param $user_id int User id
+ * @param $criteria completion_criteria Criteria object
+ * @return completion_criteria_completion
+ */
+ public function get_user_completion($user_id, $criteria) {
+ $params = array(
+ 'criteriaid' => $criteria->id,
+ 'userid' => $user_id
+ );
+
+ $completion = new completion_criteria_completion($params);
+ return $completion;
+ }
+
+ /**
+ * Check if course has completion criteria set
+ *
+ * @access public
+ * @return bool
+ */
+ public function has_criteria() {
+ $criteria = $this->get_criteria();
+
+ return (bool) count($criteria);
+ }
+
+
+ /**
+ * Get course completion criteria
+ * @access public
+ * @param $criteriatype int optional Specific criteria type to return
+ * @return void
+ */
+ public function get_criteria($criteriatype = null) {
+
+ // Fill cache if empty
+ if (!is_array($this->criteria)) {
+ global $DB;
+
+ $params = array(
+ 'course' => $this->course->id
+ );
+
+ // Load criteria from database
+ $records = (array)$DB->get_records('course_completion_criteria', $params);
+
+ // Build array of criteria objects
+ $this->criteria = array();
+ foreach ($records as $record) {
+ $this->criteria[$record->id] = completion_criteria::factory($record);
+ }
+ }
+
+ // If after all criteria
+ if ($criteriatype === null) {
+ return $this->criteria;
+ }
+
+ // If we are only after a specific criteria type
+ $criteria = array();
+ foreach ($this->criteria as $criterion) {
+
+ if ($criterion->criteriatype != $criteriatype) {
+ continue;
+ }
+
+ $criteria[$criterion->id] = $criterion;
+ }
+
+ return $criteria;
+ }
+
+ /**
+ * Get aggregation method
+ * @access public
+ * @param $criteriatype int optional If none supplied, get overall aggregation method
+ * @return int
+ */
+ public function get_aggregation_method($criteriatype = null) {
+ $params = array(
+ 'course' => $this->course_id,
+ 'criteriatype' => $criteriatype
+ );
+
+ $aggregation = new completion_aggregation($params);
+
+ if (!$aggregation->id) {
+ $aggregation->method = COMPLETION_AGGREGATION_ALL;
+ }
+
+ return $aggregation->method;
+ }
+
+ /**
+ * Get incomplete course completion criteria
+ * @access public
+ * @return void
+ */
+ public function get_incomplete_criteria() {
+ $incomplete = array();
+
+ foreach ($this->get_criteria() as $criteria) {
+ if (!$criteria->is_complete()) {
+ $incomplete[] = $criteria;
+ }
+ }
+
+ return $incomplete;
+ }
+
+ /**
+ * Clear old course completion criteria
+ */
+ public function clear_criteria() {
+ global $DB;
+ $DB->delete_records('course_completion_criteria', array('course' => $this->course_id));
+ $DB->delete_records('course_completion_aggr_methd', array('course' => $this->course_id));
+
+ $this->delete_course_completion_data();
+ }
+
+ /**
+ * Has the supplied user completed this course
+ * @access public
+ * @param $user_id int User's id
+ * @return boolean
+ */
+ public function is_course_complete($user_id) {
+ $params = array(
+ 'userid' => $user_id,
+ 'course' => $this->course_id
+ );
+
+ $ccompletion = new completion_completion($params);
+ return $ccompletion->is_complete();
+ }
+
/**
* Updates (if necessary) the completion state of activity $cm for the given
* user.
* editing form.
*
* @global object
- * @global object
* @param object $cm Activity
* @return int The number of users who have completion data stored for this
* activity, 0 if none
*/
public function count_user_data($cm) {
- global $CFG, $DB;
+ global $DB;
return $DB->get_field_sql("
SELECT
coursemoduleid=? AND completionstate<>0", array($cm->id));
}
+ /**
+ * Determines how much course completion data exists for a course. This is used when
+ * deciding whether completion information should be 'locked' in the completion
+ * settings form and activity completion settings.
+ *
+ * @global object
+ * @param int $user_id Optionally only get course completion data for a single user
+ * @return int The number of users who have completion data stored for this
+ * course, 0 if none
+ */
+ public function count_course_user_data($user_id = null) {
+ global $DB;
+
+ $sql = '
+ SELECT
+ COUNT(1)
+ FROM
+ {course_completion_crit_compl}
+ WHERE
+ course = ?
+ ';
+
+ $params = array($this->course_id);
+
+ // Limit data to a single user if an ID is supplied
+ if ($user_id) {
+ $sql .= ' AND userid = ?';
+ $params[] = $user_id;
+ }
+
+ return $DB->get_field_sql($sql, $params);
+ }
+
+ /**
+ * Check if this course's completion criteria should be locked
+ *
+ * @return boolean
+ */
+ public function is_course_locked() {
+ return (bool) $this->count_course_user_data();
+ }
+
+ /**
+ * Deletes all course completion completion data.
+ *
+ * Intended to be used when unlocking completion criteria settings.
+ *
+ * @global object
+ * @return void
+ */
+ public function delete_course_completion_data() {
+ global $DB;
+
+ $DB->delete_records('course_completions', array('course' => $this->course_id));
+ $DB->delete_records('course_completion_crit_compl', array('course' => $this->course_id));
+ }
+
/**
* Deletes completion state related to an activity for all users.
*
unset($SESSION->completioncache[$cm->course][$cm->id]);
}
+
+ // Check if there is an associated course completion criteria
+ $criteria = $this->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY);
+ $acriteria = false;
+ foreach ($criteria as $criterion) {
+ if ($criterion->moduleinstance == $cm->id) {
+ $acriteria = $criterion;
+ break;
+ }
+ }
+
+ if ($acriteria) {
+ // Delete all criteria completions relating to this activity
+ $DB->delete_records('course_completion_crit_compl', array('course' => $this->course_id, 'criteriaid' => $acriteria->id));
+ $DB->delete_records('course_completions', array('course' => $this->course_id));
+ }
}
/**
// Is this the current user?
$currentuser = $userid==$USER->id;
- if ($currentuser) {
+ if ($currentuser && is_object($SESSION)) {
// Make sure cache is present and is for current user (loginas
// changes this)
if (!isset($SESSION->completioncache) || $SESSION->completioncacheuserid!=$USER->id) {
// Obtain those activities which have completion turned on
$withcompletion = $DB->get_records_select('course_modules', 'course='.$this->course->id.
' AND completion<>'.COMPLETION_TRACKING_NONE);
- if (count($withcompletion) == 0) {
+ if (!$withcompletion) {
return array();
}
* @global object
* @global object
* @uses CONTEXT_COURSE
- * @param bool $sortfirstname True to sort with firstname
+ * @param bool $sortfirstname Optional True to sort with firstname
* @param int $groupid Optionally restrict to groupid
* @return array Array of user objects containing id, firstname, lastname (empty if none)
*/
- function internal_get_tracked_users($sortfirstname, $groupid=0) {
+ function internal_get_tracked_users($sortfirstname = false, $groupid = 0) {
global $CFG, $DB;
if (!empty($CFG->progresstrackedroles)) {
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
+ ),
+ 'moodle/course:markcomplete' => array(
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_COURSE,
+ 'legacy' => array(
+ 'teacher' => CAP_ALLOW,
+ 'editingteacher' => CAP_ALLOW,
+ 'coursecreator' => CAP_ALLOW,
+ 'manager' => CAP_ALLOW
+ )
)
);
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20100418" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20100429" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
<FIELD NAME="enrolenddate" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="enrolstartdate" NEXT="enrol"/>
<FIELD NAME="enrol" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" PREVIOUS="enrolenddate" NEXT="defaultrole"/>
<FIELD NAME="defaultrole" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="The default role given to participants who self-enrol" PREVIOUS="enrol" NEXT="enablecompletion"/>
- <FIELD NAME="enablecompletion" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'completion' progress-tracking on this course. 0 = disable completion tracking on this course." PREVIOUS="defaultrole"/>
+ <FIELD NAME="enablecompletion" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'activty completion' progress-tracking on this course. 0 = disable activity completion tracking on this course." PREVIOUS="defaultrole" NEXT="completionstartonenrol"/>
+ <FIELD NAME="completionstartonenrol" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'activty completion' progress-tracking on this course. 0 = disable activity completion tracking on this course." PREVIOUS="enablecompletion" NEXT="completionnotify"/>
+ <FIELD NAME="completionnotify" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Notify users when they complete this course" PREVIOUS="completionstartonenrol"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<INDEX NAME="shortname" UNIQUE="false" FIELDS="shortname" PREVIOUS="idnumber"/>
</INDEXES>
</TABLE>
- <TABLE NAME="course_categories" COMMENT="Course categories" PREVIOUS="course" NEXT="course_display">
+ <TABLE NAME="course_categories" COMMENT="Course categories" PREVIOUS="course" NEXT="course_completion_aggr_methd">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="name"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="description"/>
<KEY NAME="parent" TYPE="foreign" FIELDS="parent" REFTABLE="course_categories" REFFIELDS="id" COMMENT="note that to make this recursive FK working someday, the parent field must be declared NULL" PREVIOUS="primary"/>
</KEYS>
</TABLE>
- <TABLE NAME="course_display" COMMENT="Stores info about how to display the course" PREVIOUS="course_categories" NEXT="course_meta">
+ <TABLE NAME="course_completion_aggr_methd" COMMENT="Course completion aggregation methods for criteria" PREVIOUS="course_categories" NEXT="course_completion_criteria">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
+ <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="criteriatype"/>
+ <FIELD NAME="criteriatype" TYPE="int" LENGTH="20" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="The criteria type we are aggregating, or NULL if complete course aggregation" PREVIOUS="course" NEXT="method"/>
+ <FIELD NAME="method" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = all, 2 = any, 3 = fraction, 4 = unit" PREVIOUS="criteriatype" NEXT="value"/>
+ <FIELD NAME="value" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="5" COMMENT="NULL = all/any, 0..1 for method 'fraction', > 0 for method 'unit'" PREVIOUS="method"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="course" UNIQUE="false" FIELDS="course" NEXT="criteriatype"/>
+ <INDEX NAME="criteriatype" UNIQUE="false" FIELDS="criteriatype" PREVIOUS="course"/>
+ </INDEXES>
+ </TABLE>
+ <TABLE NAME="course_completion_criteria" COMMENT="Course completion criteria" PREVIOUS="course_completion_aggr_methd" NEXT="course_completion_crit_compl">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
+ <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="criteriatype"/>
+ <FIELD NAME="criteriatype" TYPE="int" LENGTH="20" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Type of criteria" PREVIOUS="course" NEXT="module"/>
+ <FIELD NAME="module" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" COMMENT="Type of module (if using module criteria type)" PREVIOUS="criteriatype" NEXT="moduleinstance"/>
+ <FIELD NAME="moduleinstance" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Module instance id (if using module criteria type)" PREVIOUS="module" NEXT="courseinstance"/>
+ <FIELD NAME="courseinstance" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Course instance id (if using course criteria type)" PREVIOUS="moduleinstance" NEXT="enrolperiod"/>
+ <FIELD NAME="enrolperiod" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Number of days after enrolment the course is completed (if using enrolperiod criteria type)" PREVIOUS="courseinstance" NEXT="timeend"/>
+ <FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Timestamp of the date for course completion (if using date criteria type)" PREVIOUS="enrolperiod" NEXT="gradepass"/>
+ <FIELD NAME="gradepass" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="5" COMMENT="The minimum grade needed to pass the course (if passing grade criteria enabled)" PREVIOUS="timeend" NEXT="role"/>
+ <FIELD NAME="role" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="gradepass"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
+ </INDEXES>
+ </TABLE>
+ <TABLE NAME="course_completion_crit_compl" COMMENT="Course completion user records" PREVIOUS="course_completion_criteria" NEXT="course_completion_notify">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="userid"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="course"/>
+ <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="criteriaid"/>
+ <FIELD NAME="criteriaid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Completion criteria this references" PREVIOUS="course" NEXT="gradefinal"/>
+ <FIELD NAME="gradefinal" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="5" COMMENT="The final grade for the course (included regardless of whether a passing grade was required)" PREVIOUS="criteriaid" NEXT="unenroled"/>
+ <FIELD NAME="unenroled" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Timestamp when the user was unenroled" PREVIOUS="gradefinal" NEXT="deleted"/>
+ <FIELD NAME="deleted" TYPE="int" LENGTH="1" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="unenroled" NEXT="timecompleted"/>
+ <FIELD NAME="timecompleted" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="deleted"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="userid" UNIQUE="false" FIELDS="userid" NEXT="course"/>
+ <INDEX NAME="course" UNIQUE="false" FIELDS="course" PREVIOUS="userid" NEXT="criteriaid"/>
+ <INDEX NAME="criteriaid" UNIQUE="false" FIELDS="criteriaid" PREVIOUS="course" NEXT="timecompleted"/>
+ <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="criteriaid"/>
+ </INDEXES>
+ </TABLE>
+ <TABLE NAME="course_completion_notify" COMMENT="Course completion notification emails" PREVIOUS="course_completion_crit_compl" NEXT="course_completions">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
+ <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="role"/>
+ <FIELD NAME="role" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="ID of the role to receive this notification message when a course has been completed" PREVIOUS="course" NEXT="message"/>
+ <FIELD NAME="message" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="HTML formatted message to be sent" PREVIOUS="role" NEXT="timesent"/>
+ <FIELD NAME="timesent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="message"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
+ </INDEXES>
+ </TABLE>
+ <TABLE NAME="course_completions" COMMENT="Course completion records" PREVIOUS="course_completion_notify" NEXT="course_display">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="userid"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="course"/>
+ <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="deleted"/>
+ <FIELD NAME="deleted" TYPE="int" LENGTH="1" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="course" NEXT="timenotified"/>
+ <FIELD NAME="timenotified" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="deleted" NEXT="timeenrolled"/>
+ <FIELD NAME="timeenrolled" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timenotified" NEXT="timestarted"/>
+ <FIELD NAME="timestarted" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeenrolled" NEXT="timecompleted"/>
+ <FIELD NAME="timecompleted" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="timestarted" NEXT="reaggregate"/>
+ <FIELD NAME="reaggregate" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecompleted"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="userid" UNIQUE="false" FIELDS="userid" NEXT="course"/>
+ <INDEX NAME="course" UNIQUE="false" FIELDS="course" PREVIOUS="userid" NEXT="timecompleted"/>
+ <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="course"/>
+ </INDEXES>
+ </TABLE>
+ <TABLE NAME="course_display" COMMENT="Stores info about how to display the course" PREVIOUS="course_completions" NEXT="course_meta">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="userid"/>
upgrade_main_savepoint($result, 2010042800);
}
+
if ($result && $oldversion < 2010042801) {
// migrating old comments block content
$DB->execute("
upgrade_main_savepoint($result, 2010042802);
}
+
+ if ($result && $oldversion < 2010043000) { // Adding new course completion feature
+
+ /// Add course completion tables
+ /// Define table course_completion_aggr_methd to be created
+ $table = new xmldb_table('course_completion_aggr_methd');
+
+ /// Adding fields to table course_completion_aggr_methd
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('criteriatype', XMLDB_TYPE_INTEGER, '20', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('method', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('value', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null);
+
+ /// Adding keys to table course_completion_aggr_methd
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ /// Adding indexes to table course_completion_aggr_methd
+ $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
+ $table->add_index('criteriatype', XMLDB_INDEX_NOTUNIQUE, array('criteriatype'));
+
+ /// Conditionally launch create table for course_completion_aggr_methd
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+
+ /// Define table course_completion_criteria to be created
+ $table = new xmldb_table('course_completion_criteria');
+
+ /// Adding fields to table course_completion_criteria
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('criteriatype', XMLDB_TYPE_INTEGER, '20', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('module', XMLDB_TYPE_CHAR, '100', null, null, null, null);
+ $table->add_field('moduleinstance', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('courseinstance', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('enrolperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('timeend', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('gradepass', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null);
+ $table->add_field('role', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+
+ /// Adding keys to table course_completion_criteria
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ /// Adding indexes to table course_completion_criteria
+ $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
+
+ /// Conditionally launch create table for course_completion_criteria
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+
+ /// Define table course_completion_crit_compl to be created
+ $table = new xmldb_table('course_completion_crit_compl');
+
+ /// Adding fields to table course_completion_crit_compl
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('criteriaid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('gradefinal', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null);
+ $table->add_field('unenroled', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('deleted', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('timecompleted', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+
+ /// Adding keys to table course_completion_crit_compl
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ /// Adding indexes to table course_completion_crit_compl
+ $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, array('userid'));
+ $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
+ $table->add_index('criteriaid', XMLDB_INDEX_NOTUNIQUE, array('criteriaid'));
+ $table->add_index('timecompleted', XMLDB_INDEX_NOTUNIQUE, array('timecompleted'));
+
+ /// Conditionally launch create table for course_completion_crit_compl
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+
+ /// Define table course_completion_notify to be created
+ $table = new xmldb_table('course_completion_notify');
+
+ /// Adding fields to table course_completion_notify
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('role', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('message', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('timesent', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+
+ /// Adding keys to table course_completion_notify
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ /// Adding indexes to table course_completion_notify
+ $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
+
+ /// Conditionally launch create table for course_completion_notify
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ /// Define table course_completions to be created
+ $table = new xmldb_table('course_completions');
+
+ /// Adding fields to table course_completions
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('deleted', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('timenotified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('timeenrolled', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('timestarted', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ $table->add_field('timecompleted', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+ $table->add_field('reaggregate', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+
+ /// Adding keys to table course_completions
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ /// Adding indexes to table course_completions
+ $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, array('userid'));
+ $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
+ $table->add_index('timecompleted', XMLDB_INDEX_NOTUNIQUE, array('timecompleted'));
+
+ /// Conditionally launch create table for course_completions
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+
+ /// Add cols to course table
+ /// Define field enablecompletion to be added to course
+ $table = new xmldb_table('course');
+ $field = new xmldb_field('enablecompletion', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'defaultrole');
+
+ /// Conditionally launch add field enablecompletion
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ /// Define field completionstartonenrol to be added to course
+ $field = new xmldb_field('completionstartonenrol', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'enablecompletion');
+
+ /// Conditionally launch add field completionstartonenrol
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ /// Define field completionnotify to be added to course
+ $field = new xmldb_field('completionnotify', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'enablecompletion');
+
+ /// Conditionally launch add field completionnotify
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ upgrade_main_savepoint($result, 2010043000);
+ }
+
+
return $result;
}
// Add the course settings link
$url = new moodle_url('/course/edit.php', array('id'=>$course->id));
$coursenode->add(get_string('settings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
+
+ // Add the course completion settings link
+ if ($CFG->enablecompletion && $course->enablecompletion) {
+ $url = new moodle_url('/course/completion.php', array('id'=>$course->id));
+ $coursenode->add(get_string('completion', 'completion'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
+ }
}
if (has_capability('moodle/role:assign', $coursecontext)) {
}
+ // Course completion tab
+ if (!empty($CFG->enablecompletion) && ($course->id == SITEID || !empty($course->enablecompletion)) && // completion enabled
+ ($myreports || $anyreport || ($course->id == SITEID || has_capability('coursereport/completion:view', $coursecontext)))) { // permissions to view the report
+
+ // Decide if singular or plural
+ $coursecompletion = $course->id == SITEID ? 'coursecompletions' : 'coursecompletion';
+
+ // Add tab
+ $reportsecondrow[] = new tabobject(
+ 'completion',
+ $CFG->wwwroot.'/course/user.php?id='.$course->id.'&user='.$user->id.'&mode='.$coursecompletion,
+ get_string($coursecompletion)
+ );
+ }
+
+
+
/// Add second row to display if there is one
if (!empty($secondrow)) {
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
- $version = 2010042802; // YYYYMMDD = date of the last version bump
+ $version = 2010042900; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0 dev (Build: 20100430)'; // Human-friendly version name