diff --git a/course/user.php b/course/user.php index c257dcecea7..c7b9e309901 100644 --- a/course/user.php +++ b/course/user.php @@ -24,46 +24,34 @@ require_once("../config.php"); require_once("lib.php"); -require_once($CFG->libdir.'/completionlib.php'); $id = required_param('id',PARAM_INT); // course id $user = required_param('user',PARAM_INT); // user id $mode = optional_param('mode', "todaylogs", PARAM_ALPHA); -$page = optional_param('page', 0, PARAM_INT); -$perpage = optional_param('perpage', 100, PARAM_INT); $url = new moodle_url('/course/user.php', array('id'=>$id,'user'=>$user, 'mode'=>$mode)); -if ($page !== 0) { - $url->param('page', $page); -} -if ($perpage !== 100) { - $url->param('perpage', $perpage); -} -$PAGE->set_url($url); -if (!$course = $DB->get_record('course', array('id'=>$id))) { - print_error('invalidcourseid', 'error'); -} - -if (! $user = $DB->get_record("user", array("id"=>$user))) { - print_error('invaliduserid', 'error'); -} +$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST); +$user = $DB->get_record("user", array("id"=>$user, 'deleted'=>0), '*', MUST_EXIST); if ($mode === 'outline' or $mode === 'complete') { $url = new moodle_url('/report/outline/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$mode)); redirect($url); - -} else if ($mode === 'todaylogs' or $mode === 'alllogs') { +} +if ($mode === 'todaylogs' or $mode === 'alllogs') { $logmode = ($mode === 'todaylogs') ? 'today' : 'all'; $url = new moodle_url('/report/log/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$logmode)); redirect($url); -} else if ($mode === 'stats') { +} +if ($mode === 'stats') { $url = new moodle_url('/report/stats/user.php', array('id'=>$user->id, 'course'=>$course->id)); redirect($url); } +if ($mode === 'coursecompletions' or $mode === 'coursecompletion') { + $url = new moodle_url('/report/completion/user.php', array('id'=>$user->id, 'course'=>$course->id)); + redirect($url); +} - -require_login(); $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); $personalcontext = get_context_instance(CONTEXT_USER, $user->id); @@ -83,8 +71,6 @@ if ($user->deleted) { die; } -//TODO: all this is a hack - we can not link to plugins like this - all this must be abstracted to plugin callbacks! - // prepare list of allowed modes $myreports = ($course->showreports and $USER->id == $user->id); $anyreport = has_capability('moodle/user:viewuseractivitiesreport', $personalcontext); @@ -108,19 +94,6 @@ 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('report/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); } @@ -133,15 +106,6 @@ if (!in_array($mode, $modes)) { add_to_log($course->id, "course", "user report", "user.php?id=$course->id&user=$user->id&mode=$mode", "$user->id"); $stractivityreport = get_string("activityreport"); -$strparticipants = get_string("participants"); -$strcomplete = get_string("complete"); -$strmode = get_string($mode); -$fullname = fullname($user, true); - -$link = null; -if ($course->id != SITEID && has_capability('moodle/course:viewparticipants', $coursecontext)) { - $link = new moodle_url('/user/index.php', array('id'=>$course->id)); -} $PAGE->navigation->extend_for_user($user); $PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed. @@ -164,245 +128,6 @@ switch ($mode) { } break; - case "coursecompletion": - case "coursecompletions": - - // Display course completion user report - - // Grab all courses the user is enrolled in and their completion status - $sql = " - SELECT DISTINCT - c.id AS id - FROM - {course} c - INNER JOIN - {context} con - ON con.instanceid = c.id - INNER JOIN - {role_assignments} ra - ON ra.contextid = con.id - INNER JOIN - {enrol} e - ON c.id = e.courseid - INNER JOIN - {user_enrolments} ue - ON e.id = ue.enrolid AND ra.userid = ue.userid - AND ra.userid = {$user->id} - "; - - // Get roles that are tracked by course completion - if ($roles = $CFG->gradebookroles) { - $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 - $rs = $DB->get_recordset_sql($sql); - if (!$rs->valid()) { - - if ($course->id != 1) { - $error = get_string('nocompletions', 'report_completion'); - } else { - $error = get_string('nocompletioncoursesenroled', 'report_completion'); - } - - echo $OUTPUT->notification($error); - $rs->close(); // not going to loop (but break), close rs - 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(); // after loop, close rs - - // Loop through course status groups - foreach ($courses as $type => $infos) { - - // If there are courses with this status - if (!empty($infos)) { - - echo '

'.get_string($type, 'report_completion').'

'; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - - if ($type === 'complete') { - echo ''; - } - - echo ''; - - // For each course - foreach ($infos as $c_info) { - - // Get course info - $c_course = $DB->get_record('course', array('id' => $c_info->course_id)); - $course_context = get_context_instance(CONTEXT_COURSE, $c_course->id, MUST_EXIST); - $course_name = format_string($c_course->fullname, true, array('context' => $course_context)); - - // 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', 'report_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 ''; - } else { - echo ''; - } - - echo ''; - - // 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 ''; - } - - $first_row = false; - echo ''; - } - } - - echo '
'.get_string('course').''.get_string('requiredcriteria', 'completion').''.get_string('status').''.get_string('info').''.get_string('completiondate', 'report_completion').'
'.$course_name.'
'; - echo $row['title']; - echo ''; - - switch ($row['status']) { - case 'Yes': - echo get_string('complete'); - break; - - case 'No': - echo get_string('incomplete', 'report_completion'); - break; - - default: - echo $row['status']; - } - - // Display link on first row - echo ''; - if ($first_row) { - echo ''.get_string('detailedview', 'report_completion').''; - } - echo ''.userdate($ccompletion->timecompleted, '%e %B %G').'
'; - } - - } - break; default: // can not be reached ;-) diff --git a/report/completion/lib.php b/report/completion/lib.php index 3bb40de9e80..12efc71bbdf 100644 --- a/report/completion/lib.php +++ b/report/completion/lib.php @@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die; * @param stdClass $context The context of the course */ function report_completion_extend_navigation_course($navigation, $course, $context) { - global $CFG, $OUTPUT; + global $CFG; require_once($CFG->libdir.'/completionlib.php'); @@ -46,6 +46,64 @@ function report_completion_extend_navigation_course($navigation, $course, $conte } } +/** + * This function extends the course navigation with the report items + * + * @param navigation_node $navigation The navigation node to extend + * @param stdClass $user + * @param stdClass $course The course to object for the report + */ +function report_completion_extend_navigation_user($navigation, $user, $course) { + + return; //TODO: this plugin was not linked from navigation in 2.0, let's keep it that way for now --skodak + + if (report_completion_can_access_user_report($user, $course)) { + $url = new moodle_url('/report/completion/user.php', array('id'=>$user->id, 'course'=>$course->id)); + $navigation->add(get_string('coursecompletion'), $url); + } +} + +/** + * Is current user allowed to access this report + * + * @private defined in lib.php for performance reasons + * + * @param stdClass $user + * @param stdClass $course + * @return bool + */ +function report_completion_can_access_user_report($user, $course) { + global $USER, $CFG; + + if (empty($CFG->enablecompletion)) { + return false; + } + + if ($course->id != SITEID and !$course->enablecompletion) { + return; + } + + $coursecontext = context_course::instance($course->id); + $personalcontext = context_user::instance($user->id); + + if (has_capability('report/completion:view', $coursecontext)) { + return true; + } + + if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) { + if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) { + return true; + } + + } else if ($user->id == $USER->id) { + if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) { + return true; + } + } + + return false; +} + /** * Return a list of page types * @param string $pagetype current page type diff --git a/report/completion/user.php b/report/completion/user.php new file mode 100644 index 00000000000..0a7988d7b7d --- /dev/null +++ b/report/completion/user.php @@ -0,0 +1,303 @@ +. + +/** + * Display user completion report + * + * @package report + * @subpackage completion + * @copyright 2009 Catalyst IT Ltd + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../../config.php'); +require_once($CFG->dirroot.'/report/completion/lib.php'); +require_once($CFG->libdir.'/completionlib.php'); + +$userid = required_param('id', PARAM_INT); +$courseid = required_param('course', PARAM_INT); + +$user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0), '*', MUST_EXIST); +$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST); + +$coursecontext = context_course::instance($course->id); +$personalcontext = context_user::instance($user->id); + +if ($USER->id != $user->id and has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) + and !is_enrolled($coursecontext, $USER) and is_enrolled($coursecontext, $user)) { + //TODO: do not require parents to be enrolled in courses - this is a hack! + require_login(); + $PAGE->set_course($course); +} else { + require_login($course); +} + +if (!report_completion_can_access_user_report($user, $course, true)) { + // this should never happen + error('Can not access user completion report'); +} + +add_to_log($course->id, 'course', 'report completion', "report/completion/user.php?id=$user->id&course=$course->id", $course->id); + +$stractivityreport = get_string('activityreport'); + +$PAGE->set_pagelayout('admin'); +$PAGE->set_url('/report/completion/user.php', array('id'=>$user->id, 'course'=>$course->id)); +$PAGE->navigation->extend_for_user($user); +$PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed. +$PAGE->set_title("$course->shortname: $stractivityreport"); +$PAGE->set_heading($course->fullname); +echo $OUTPUT->header(); + + +// Display course completion user report + +// Grab all courses the user is enrolled in and their completion status +$sql = " + SELECT DISTINCT + c.id AS id + FROM + {course} c + INNER JOIN + {context} con + ON con.instanceid = c.id + INNER JOIN + {role_assignments} ra + ON ra.contextid = con.id + INNER JOIN + {enrol} e + ON c.id = e.courseid + INNER JOIN + {user_enrolments} ue + ON e.id = ue.enrolid AND ra.userid = ue.userid + AND ra.userid = {$user->id} +"; + +// Get roles that are tracked by course completion +if ($roles = $CFG->gradebookroles) { + $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 +$rs = $DB->get_recordset_sql($sql); +if (!$rs->valid()) { + + if ($course->id != 1) { + $error = get_string('nocompletions', 'report_completion'); // TODO: missing string + } else { + $error = get_string('nocompletioncoursesenroled', 'report_completion'); // TODO: missing string + } + + echo $OUTPUT->notification($error); + $rs->close(); // not going to loop (but break), close rs + echo $OUTPUT->footer(); + die(); +} + +// 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(); // after loop, close rs + +// Loop through course status groups +foreach ($courses as $type => $infos) { + + // If there are courses with this status + if (!empty($infos)) { + + echo '

'.get_string($type, 'report_completion').'

'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + if ($type === 'complete') { + echo ''; + } + + echo ''; + + // For each course + foreach ($infos as $c_info) { + + // Get course info + $c_course = $DB->get_record('course', array('id' => $c_info->course_id)); + $course_context = get_context_instance(CONTEXT_COURSE, $c_course->id, MUST_EXIST); + $course_name = format_string($c_course->fullname, true, array('context' => $course_context)); + + // 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', 'report_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 ''; + } else { + echo ''; + } + + echo ''; + + // 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 ''; + } + + $first_row = false; + echo ''; + } + } + + echo '
'.get_string('course').''.get_string('requiredcriteria', 'completion').''.get_string('status').''.get_string('info').''.get_string('completiondate', 'report_completion').'
'.$course_name.'
'; + echo $row['title']; + echo ''; + + switch ($row['status']) { + case 'Yes': + echo get_string('complete'); + break; + + case 'No': + echo get_string('incomplete', 'report_completion'); + break; + + default: + echo $row['status']; + } + + // Display link on first row + echo ''; + if ($first_row) { + echo ''.get_string('detailedview', 'report_completion').''; + } + echo ''.userdate($ccompletion->timecompleted, '%e %B %G').'
'; + } +} + + +echo $OUTPUT->footer();