course completion MDL-2631 Course completion feature - Thanks to Aaron Barnes and Catalyst IT for this significant contribution. It is now officially possible to finish a course!

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.
This commit is contained in:
Martin Dougiamas
2010-04-30 03:06:22 +00:00
parent 2589eb89f0
commit 2be4d090c0
46 changed files with 5879 additions and 42 deletions
+10
View File
@@ -242,6 +242,16 @@
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...');
+5 -5
View File
@@ -109,11 +109,11 @@ if ($hassiteconfig
$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
@@ -0,0 +1,198 @@
<?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 <[email protected]>
* @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;
}
}
+255
View File
@@ -0,0 +1,255 @@
<?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 <[email protected]>
* @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();
@@ -0,0 +1,7 @@
<?php
$string['completionprogressdetails'] = 'Completion progress details';
$string['completionstatus'] = 'Course Completion Status';
$string['pluginname'] = 'Course Completion Status';
$string['criteriagroup'] = 'Criteria group';
$string['requirement'] = 'Requirement';
@@ -0,0 +1,93 @@
<?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;
}
}
@@ -0,0 +1,5 @@
<?php
$string['selfcompletion'] = 'Self Completion';
$string['pluginname'] = 'Self Completion';
$string['completecourse'] = 'Complete Course';
+160
View File
@@ -0,0 +1,160 @@
<?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();
+214
View File
@@ -0,0 +1,214 @@
<?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;
}
}
?>
+8
View File
@@ -376,10 +376,18 @@ class course_edit_form extends moodleform {
$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);
}
//--------------------------------------------------------------------------------
+9 -9
View File
@@ -212,7 +212,7 @@ abstract class moodleform_mod extends moodleform {
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;
}
@@ -409,9 +409,9 @@ abstract class moodleform_mod extends moodleform {
// 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();
@@ -487,7 +487,7 @@ abstract class moodleform_mod extends moodleform {
$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?
@@ -495,7 +495,7 @@ abstract class moodleform_mod extends moodleform {
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
@@ -515,7 +515,7 @@ abstract class moodleform_mod extends moodleform {
$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);
@@ -525,7 +525,7 @@ abstract class moodleform_mod extends moodleform {
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;
}
@@ -534,7 +534,7 @@ abstract class moodleform_mod extends moodleform {
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;
}
@@ -557,7 +557,7 @@ abstract class moodleform_mod extends moodleform {
// 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);
}
+42
View File
@@ -0,0 +1,42 @@
<?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',
)
);
?>
+619
View File
@@ -0,0 +1,619 @@
<?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 <[email protected]>
* @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 ? '&amp;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.
'&amp;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.'&amp;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.'&amp;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.
'&amp;format=csv">'.get_string('csvdownload','completion').'</a></li>
<li><a href="index.php?course='.$course->id.'&amp;format=excelcsv">'.
get_string('excelcsvdownload','completion').'</a></li></ul>';
echo $OUTPUT->footer($course);
@@ -0,0 +1,5 @@
<?php
$string['completion:view'] = 'View course completion report';
$string['completiondate']='Completion date';
$string['pluginname']='Course completion';
+41
View File
@@ -0,0 +1,41 @@
<?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', ''));
}
}
+15
View File
@@ -0,0 +1,15 @@
<?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>';
}
}
?>
+68
View File
@@ -0,0 +1,68 @@
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);
+29
View File
@@ -0,0 +1,29 @@
<?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;
?>
@@ -23,5 +23,5 @@
* @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';
+2 -2
View File
@@ -44,6 +44,6 @@ function progress_report_extend_navigation($navigation, $course, $context) {
$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', ''));
}
}
}
+73 -2
View File
@@ -16,7 +16,8 @@
// 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
@@ -26,7 +27,77 @@ require_once('../config.php');
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);
+246
View File
@@ -111,6 +111,19 @@ if (has_capability('moodle/grade:viewall', $coursecontext)) {
$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);
}
@@ -350,6 +363,239 @@ switch ($mode) {
}
}
}
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 ;-)
+75 -6
View File
@@ -40,7 +40,6 @@ $string['completion-alt-manual-n'] = 'Not completed; select to mark as complete'
$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';
@@ -49,6 +48,7 @@ $string['completionreport'] = 'Completion progress report';
$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';
@@ -60,16 +60,85 @@ $string['err_noactivities'] = 'Completion information is not enabled for any act
$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';
+3 -2
View File
@@ -31,9 +31,11 @@ $string['availableuntil'] = 'Only available until end';
$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';
@@ -58,8 +60,6 @@ for the activity to appear.
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.';
@@ -74,6 +74,7 @@ $string['requires_grade_max'] = 'Not available unless you get an appropriate sco
$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): &lsquo;{$a}&rsquo;';
+2
View File
@@ -287,6 +287,8 @@ $string['coursebackup'] = 'Course backup';
$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}';
+114
View File
@@ -0,0 +1,114 @@
<?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 <[email protected]>
* @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;
}
}
}
+245
View File
@@ -0,0 +1,245 @@
<?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 <[email protected]>
* @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();
}
}
}
+231
View File
@@ -0,0 +1,231 @@
<?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 <[email protected]>
* @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();
}
}
@@ -0,0 +1,286 @@
<?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 <[email protected]>
* @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;
}
}
@@ -0,0 +1,210 @@
<?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 <[email protected]>
* @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);
}
}
@@ -0,0 +1,220 @@
<?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 <[email protected]>
* @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;
}
}
+207
View File
@@ -0,0 +1,207 @@
<?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 <[email protected]>
* @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;
}
}
@@ -0,0 +1,236 @@
<?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 <[email protected]>
* @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;
}
}
@@ -0,0 +1,248 @@
<?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 <[email protected]>
* @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;
}
}
+158
View File
@@ -0,0 +1,158 @@
<?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 <[email protected]>
* @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;
}
}
+146
View File
@@ -0,0 +1,146 @@
<?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 <[email protected]>
* @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;
}
}
@@ -0,0 +1,128 @@
<?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 <[email protected]>
* @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;
}
}
+345
View File
@@ -0,0 +1,345 @@
<?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 <[email protected]>
* @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;
}
}
}
+313
View File
@@ -0,0 +1,313 @@
<?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) {
}
}
+311 -9
View File
@@ -15,6 +15,12 @@
// 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')
@@ -81,7 +87,7 @@ define('COMPLETION_NOT_VIEWED', 0);
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);
@@ -89,6 +95,11 @@ 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.
*
@@ -99,9 +110,40 @@ define('COMPLETION_AND', true);
* @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.
*
@@ -109,6 +151,7 @@ class completion_info {
*/
public function __construct($course) {
$this->course = $course;
$this->course_id = $course->id;
}
/**
@@ -173,7 +216,194 @@ class completion_info {
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.
@@ -377,13 +607,12 @@ class completion_info {
* 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
@@ -394,6 +623,63 @@ class completion_info {
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.
*
@@ -416,6 +702,22 @@ class completion_info {
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));
}
}
/**
@@ -496,7 +798,7 @@ class completion_info {
// 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) {
@@ -635,7 +937,7 @@ class completion_info {
// 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();
}
@@ -664,11 +966,11 @@ class completion_info {
* @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)) {
+10
View File
@@ -1537,5 +1537,15 @@ $capabilities = array(
'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
)
)
);
+99 -4
View File
@@ -1,5 +1,5 @@
<?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"
>
@@ -110,7 +110,9 @@
<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"/>
@@ -121,7 +123,7 @@
<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"/>
@@ -141,7 +143,100 @@
<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', &gt; 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"/>
+162
View File
@@ -3644,6 +3644,7 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
upgrade_main_savepoint($result, 2010042800);
}
if ($result && $oldversion < 2010042801) {
// migrating old comments block content
$DB->execute("
@@ -3696,6 +3697,167 @@ AND EXISTS (SELECT 'x'
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;
}
+6
View File
@@ -2291,6 +2291,12 @@ class settings_navigation extends navigation_node {
// 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)) {
+17
View File
@@ -273,6 +273,23 @@
}
// 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.'&amp;user='.$user->id.'&amp;mode='.$coursecompletion,
get_string($coursecompletion)
);
}
/// Add second row to display if there is one
if (!empty($secondrow)) {
+1 -1
View File
@@ -6,7 +6,7 @@
// 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