. /** * Course completion progress report * * @package report * @subpackage completion * @copyright 2009 Catalyst IT Ltd * @author Aaron Barnes * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require('../../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 $courseid = required_param('course', PARAM_INT); $format = optional_param('format','',PARAM_ALPHA); $sort = optional_param('sort','',PARAM_ALPHA); $edituser = optional_param('edituser', 0, PARAM_INT); $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); $context = context_course::instance($course->id); $url = new moodle_url('/report/completion/index.php', array('course'=>$course->id)); $PAGE->set_url($url); $PAGE->set_pagelayout('report'); $firstnamesort = ($sort == 'firstname'); $excel = ($format == 'excelcsv'); $csv = ($format == 'csv' || $excel); // Paging $start = optional_param('start', 0, PARAM_INT); $sifirst = optional_param('sifirst', 'all', PARAM_ALPHA); $silast = optional_param('silast', 'all', PARAM_ALPHA); // Whether to show extra user identity information $extrafields = get_extra_user_fields($context); $leftcols = 1 + count($extrafields); // 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); require_capability('report/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); } /** * Load data */ // Retrieve course_module data for all modules in the course $modinfo = get_fast_modinfo($course); // 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; } } } } /* * Setup page header */ if ($csv) { $shortname = format_string($course->shortname, true, array('context' => $context)); $textlib = textlib_get_instance(); header('Content-Disposition: attachment; filename=progress.'. preg_replace('/[^a-z0-9-]/','_',$textlib->strtolower(strip_tags($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('coursecompletion'); $PAGE->set_title($strcompletion); $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); $PAGE->requires->yui2_lib( array( 'yahoo', 'dom', 'element', 'event', ) ); $PAGE->requires->js('/report/completion/textrotate.js'); // Handle groups (if enabled) groups_print_course_menu($course, $CFG->wwwroot.'/report/completion/?course='.$course->id); } // Generate where clause $where = array(); $where_params = array(); if ($sifirst !== 'all') { $where[] = $DB->sql_like('u.firstname', ':sifirst', false); $where_params['sifirst'] = $sifirst.'%'; } if ($silast !== 'all') { $where[] = $DB->sql_like('u.lastname', ':silast', false); $where_params['silast'] = $silast.'%'; } // Get user match count $total = $completion->get_num_tracked_users(implode(' AND ', $where), $where_params, $group); // Total user count $grandtotal = $completion->get_num_tracked_users('', array(), $group); // If no users in this course what-so-ever if (!$grandtotal) { echo $OUTPUT->container(get_string('err_nousers', 'completion'), 'errorbox errorboxcontent'); echo $OUTPUT->footer(); exit; } // Get user data $progress = array(); if ($total) { $progress = $completion->get_progress_all( implode(' AND ', $where), $where_params, $group, $firstnamesort ? 'u.firstname ASC' : 'u.lastname ASC', $csv ? 0 : COMPLETION_REPORT_PAGE, $csv ? 0 : $start, $context ); } // Build link for paging $link = $CFG->wwwroot.'/report/completion/?course='.$course->id; if (strlen($sort)) { $link .= '&sort='.$sort; } $link .= '&start='; // Build the the page by Initial bar $initials = array('first', 'last'); $alphabet = explode(',', get_string('alphabet', 'langconfig')); $pagingbar = ''; foreach ($initials as $initial) { $var = 'si'.$initial; $othervar = $initial == 'first' ? 'silast' : 'sifirst'; $othervar = $$othervar != 'all' ? "&{$othervar}={$$othervar}" : ''; $pagingbar .= '
'; $pagingbar .= get_string($initial.'name').': '; if ($$var == 'all') { $pagingbar .= ''.get_string('all').' '; } else { $pagingbar .= "".get_string('all').' '; } foreach ($alphabet as $letter) { if ($$var === $letter) { $pagingbar .= ''.$letter.' '; } else { $pagingbar .= "$letter "; } } $pagingbar .= '
'; } // Do we need a paging bar? if ($total > COMPLETION_REPORT_PAGE) { // Paging bar $pagingbar .= '
'; $pagingbar .= get_string('page').': '; $sistrings = array(); if ($sifirst != 'all') { $sistrings[] = "sifirst={$sifirst}"; } if ($silast != 'all') { $sistrings[] = "silast={$silast}"; } $sistring = !empty($sistrings) ? '&'.implode('&', $sistrings) : ''; // Display previous link if ($start > 0) { $pstart = max($start - COMPLETION_REPORT_PAGE, 0); $pagingbar .= "(".get_string('previous').') '; } // Create page links $curstart = 0; $curpage = 0; while ($curstart < $total) { $curpage++; if ($curstart == $start) { $pagingbar .= ' '.$curpage.' '; } else { $pagingbar .= " $curpage "; } $curstart += COMPLETION_REPORT_PAGE; } // Display next link $nstart = $start + COMPLETION_REPORT_PAGE; if ($nstart < $total) { $pagingbar .= " (".get_string('next').')'; } $pagingbar .= '
'; } /* * Draw table header */ // Start of table if (!$csv) { print '
'; // ugh $total_header = ($total == $grandtotal) ? $total : "{$total}/{$grandtotal}"; echo $OUTPUT->heading(get_string('allparticipants').": {$total_header}", 3); print $pagingbar; if (!$total) { echo $OUTPUT->heading(get_string('nothingtodisplay'), 2); echo $OUTPUT->footer(); exit; } print '
'; print ''; // Print criteria group names print PHP_EOL.''; echo ''; $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 ''; } if (isset($criteria[$i])) { // Move to next criteria type $current_group = $criterion; $col_count = 1; } } // Overall course completion status print ''; print ''; // Print aggregation methods print PHP_EOL.''; echo ''; $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 ? get_string('all') : get_string('any'); } else { $method = '-'; } print ''; } if (isset($criteria[$i])) { // Move to next criteria type $current_group = $criterion; $col_count = 1; } } // Overall course aggregation method print ''; print ''; // Print criteria titles if (COMPLETION_REPORT_COL_TITLES) { print PHP_EOL.''; echo ''; foreach ($criteria as $criterion) { // Get criteria details $details = $criterion->get_title_detailed(); print ''; } // Overall course completion status print ''; } // Print user heading and icons print ''; // User heading / sort option print ''; // Print user identity columns foreach ($extrafields as $field) { echo ''; } /// /// 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: // Display icon $icon = $OUTPUT->pix_url('icon', $criterion->module); $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$criterion->moduleinstance; $icontitle = $modinfo->cms[$criterion->moduleinstance]->name; $iconalt = get_string('modulename', $criterion->module); break; case COMPLETION_CRITERIA_TYPE_COURSE: // Load course $crs = $DB->get_record('course', array('id' => $criterion->courseinstance)); // Display icon $iconlink = $CFG->wwwroot.'/course/view.php?id='.$criterion->courseinstance; $icontitle = format_string($crs->fullname, true, array('context' => get_context_instance(CONTEXT_COURSE, $crs->id, MUST_EXIST))); $iconalt = format_string($crs->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $crs->id))); 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 ''; } // Overall course completion status print ''; print ''; } else { // The CSV file does not contain any headers } /// /// Display a row for each user /// foreach ($progress as $user) { // User name if ($csv) { print csv_quote(fullname($user)); foreach ($extrafields as $field) { echo $sep . csv_quote($user->{$field}); } } else { print PHP_EOL.''; print ''; foreach ($extrafields as $field) { echo ''; } } // Progress for each course completion criteria foreach ($criteria as $criterion) { // Handle activity completion differently if ($criterion->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) { // Load activity $activity = $modinfo->cms[$criterion->moduleinstance]; // 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-' . $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 ''; } 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-' . $completiontype, 'completion'); $a = new stdClass(); $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-' . $completiontype, 'completion'); print ''; } else { print ''; } } } // 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-' . $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 ''; } if ($csv) { print $line; } else { print ''; } } if ($csv) { exit; } print '
' . get_string('criteriagroup', 'completion') . ''.$current_group->get_type_title().''.get_string('course').'
' . get_string('aggregationmethod', 'completion').''.$method.''; // Get course aggregation $method = $completion->get_aggregation_method(); print $method == 1 ? get_string('all') : get_string('any'); print '
' . get_string('criteria', 'completion') . ''; print ''.$details.''; print ''; print ''.get_string('coursecomplete', 'completion').''; print '
'; $sistring = "&silast={$silast}&sifirst={$sifirst}"; if ($firstnamesort) { print get_string('firstname')." / id}{$sistring}\">". get_string('lastname').''; } else { print "id}&sort=firstname{$sistring}\">". get_string('firstname').' / '. get_string('lastname'); } print '' . get_user_field_name($field) . ''; // Create icon if not supplied if (!$icon) { $icon = $OUTPUT->pix_url('i/'.$COMPLETION_CRITERIA_TYPES[$criterion->criteriatype]); } print ($iconlink ? '' : ''); print ''.$iconalt.''; print ($iconlink ? '' : ''); print ''; print ''.get_string('course').''; print '
'.fullname($user).'' . s($user->{$field}) . ''; print ''.$describe.''; print ''. ''. ''.$describe.''. ''.$describe.''; // Display course completion status icon print ''.$describe.''; print '
'; print '
'; print $pagingbar; print ''; echo $OUTPUT->footer($course);