<?php // This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/ // // VPL for 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. // // VPL for 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 VPL for Moodle. If not, see <http://www.gnu.org/licenses/>. /** * VPL class definition * * @package mod_vpl * @copyright 2013 onwards Juan Carlos RodrÃguez-del-Pino * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @author Juan Carlos RodrÃguez-del-Pino <jcrodriguez@dis.ulpgc.es> */ /** * Module instance files * path= vpl_data/vpl_instance# * General info * path/required_files.lst * path/required_files/ * path/execution_files.lst * path/execution_files/ * path/execution_files/vpl_run.sh * path/execution_files/vpl_debug.sh * path/execution_files/vpl_evaluate.sh * * Submission info * path/usersdata/userid#/submissionid#/submissionfiles/ * path/usersdata/userid#/submissionid#/grade_comments.txt * path/usersdata/userid#/submissionid#/teachertest.txt * path/usersdata/userid#/submissionid#/studenttest.txt */ defined('MOODLE_INTERNAL') || die(); require_once(dirname(__FILE__).'/filegroup.class.php'); require_once(dirname(__FILE__).'/lib.php'); class file_group_execution extends file_group_process { /** * Name of fixed file names * * @var string[] */ protected static $basefiles = array ( 'vpl_run.sh', 'vpl_debug.sh', 'vpl_evaluate.sh', 'vpl_evaluate.cases' ); /** * Number of $basefiles elements * * @var int */ protected static $numbasefiles; /** * Constructor * * @param string $filelistname * @param string $dir */ public function __construct($filelistname, $dir) { self::$numbasefiles = count( self::$basefiles ); parent::__construct( $filelistname, $dir, 1000, self::$numbasefiles ); } /** * Get list of files * * @return string[] */ public function getfilelist() { return array_values( array_unique( array_merge( self::$basefiles, parent::getfilelist() ) ) ); } /** * Get the file comment by number * * @param int $num * @return string */ public function getfilecomment($num) { if ($num < self::$numbasefiles) { return get_string( self::$basefiles [$num], VPL ); } else { return get_string( 'file' ) . ' ' . ($num + 1 - self::$numbasefiles); } } /** * Get list of files to keep when running * * @return string[] */ public function getfilekeeplist() { return file_group_process::read_list( $this->filelistname . '.keep' ); } /** * Set the file list to keep when running * * @param string[] $filelist */ public function setfilekeeplist($filelist) { file_group_process::write_list( $this->filelistname . '.keep', $filelist ); } } class mod_vpl { /** * Internal var for course_module * * @var object $cm */ protected $cm; /** * Internal var for course * * @var object $course */ protected $course; /** * Internal var for vpl * * @var object $instance */ protected $instance; /** * Internal var array of object to file group manager * * @var array of object of file group manager */ protected $requiredfgm; /** * Internal var object to execution file group manager * * @var object of file group manager */ protected $fgm; /** * Constructor * * @param $id int * optional course_module id * @param $a int * optional module instance id */ public function __construct($id, $a = null) { global $DB; if ($id) { if (! $this->cm = get_coursemodule_from_id( VPL, $id )) { print_error( 'invalidcoursemodule'); } if (! $this->course = $DB->get_record( "course", array ( "id" => $this->cm->course ) )) { print_error( 'unknowncourseidnumber', '', $this->cm->course ); } if (! $this->instance = $DB->get_record( VPL, array ( "id" => $this->cm->instance ) )) { print_error( 'module instance id unknow' ); } $this->instance->cmidnumber = $this->cm->id; } else { if (! $this->instance = $DB->get_record( VPL, array ( "id" => $a ) )) { print_error( 'module instance id unknow' ); } if (! $this->course = $DB->get_record( "course", array ( "id" => $this->instance->course ) )) { print_error( 'unknowncourseidnumber', '', $this->instance->course ); } if (! $this->cm = get_coursemodule_from_instance( VPL, $this->instance->id, $this->course->id )) { vpl_notice( get_string( 'invalidcoursemodule', 'error' ) . ' VPL id=' . $a ); // Don't stop on error. This let delete a corrupted course. } else { $this->instance->cmidnumber = $this->cm->id; } } $this->fgm = array(); } /** * * @return Object of module DB instance */ public function get_instance() { return $this->instance; } /** * * @return Object of course DB instance * */ public function get_course() { return $this->course; } /** * * @return Object of course_module DB instance * */ public function get_course_module() { return $this->cm; } /** * Delete a vpl instance * * @return bool true if all OK */ public function delete_all() { global $DB; // Delete all data files. vpl_delete_dir( $this->get_data_directory() ); // Delete grade_item. vpl_delete_grade_item( $this->instance ); // Delete event. $DB->delete_records( 'event', array ( 'modulename' => VPL, 'instance' => $this->instance->id ) ); // Delete all submissions records. $DB->delete_records( 'vpl_submissions', array ( 'vpl' => $this->instance->id ) ); // Delete vpl record. $DB->delete_records( VPL, array ( 'id' => $this->instance->id ) ); // Allways true. return true; } /** * Update a VPL instance including timemodified field * * @return bool true if all OK */ public function update() { global $DB; $this->instance->timemodified = time(); return $DB->update_record( VPL, $this->instance ); } /** * Get data directory path * @return string data directory path */ public function get_data_directory() { global $CFG; return $CFG->dataroot . '/vpl_data/' . $this->instance->id; } /** * Get config data directory path * @return string config data directory path */ public function get_users_data_directory() { return $this->get_data_directory() . '/usersdata'; } /** * * @param $type string * @return directory to stored initial files */ public function get_files_directory($type) { return $this->get_data_directory() . '/'.$type.'_files/'; } /** * Get path to filename to store required files * @return string path to filename to store required files */ public function get_files_filename($type) { return $this->get_data_directory() . '/'.$type.'_files.lst'; } /** * Get array of files required file names * @return array of strings */ public function get_files($type) { return file_group_process::read_list( $this->get_files_filename($type) ); } /** * * @param $files array * of required files */ public function set_files($type, $files) { file_group_process::write_list( $this->get_files_filename($type), $files ); } /** * * @return file_group_process file group manager for specified files */ public function get_fgm($type) { if (! isset($this->fgm[$type])) { if ($type == 'execution') { $this->fgm[$type] = new file_group_execution( $this->get_files_filename($type) , $this->get_files_directory($type) ); } else { $this->fgm[$type] = new file_group_process( $this->get_files_filename($type) , $this->get_files_directory($type) , $this->instance->maxfiles ); } } return $this->fgm[$type]; } /** * get instance name with groupping name if available * * @return string with name+(grouping name) */ public function get_printable_name() { global $CFG; $ret = $this->instance->name; if (! empty( $CFG->enablegroupings ) && ($this->cm->groupingid > 0)) { $grouping = groups_get_grouping( $this->cm->groupingid ); if ($grouping !== false) { $ret .= ' (' . $grouping->name . ')'; } } return $ret; } /** * Get fulldescription * * @return string fulldescription * */ public function get_fulldescription() { $instance = $this->get_instance(); if ($instance->intro) { return format_module_intro( VPL, $this->get_instance(), $this->get_course_module()->id ); } else { return ''; } } /** * Get fulldescription adding basedon descriptions * * @return string fulldescription * */ public function get_fulldescription_with_basedon() { $ret = ''; if ($this->instance->basedon) { // Show recursive varaitions. $basevpl = new mod_vpl( false, $this->instance->basedon ); $ret .= $basevpl->get_fulldescription_with_basedon(); } return $ret . $this->get_fulldescription(); } /** * Return maximum file size allowed * * @return int * */ public function get_maxfilesize() { $plugincfg = get_config('mod_vpl'); $max = vpl_get_max_post_size(); if ($plugincfg->maxfilesize > 0 && $plugincfg->maxfilesize < $max) { $max = $plugincfg->maxfilesize; } if ($this->instance->maxfilesize > 0 && $this->instance->maxfilesize < $max) { $max = $this->instance->maxfilesize; } return $max; } /** * Get grading information help * * @return string grade comments summary in html format */ public function get_grading_help() { $list = array (); $submissions = $this->all_last_user_submission(); foreach ($submissions as $submission) { $sub = new mod_vpl_submission( $this, $submission ); $sub->filter_feedback( $list ); } // TODO show evaluation criteria with show hidde button. $all = array (); foreach ($list as $text => $info) { $astext = s( addslashes_js( $text ) ); $html = ''; $html .= s( $text ); foreach ($info->grades as $grade => $nothing) { if ($grade >= 0) { // No grade. $jscript = 'VPL.addComment(\'' . $astext . '\')'; } else { $jscript = 'VPL.addComment(\'' . $astext . ' (' . $grade . ')\')'; } $link = '<a href="javascript:void(0)" onclick="' . $jscript . '">' . $grade . '</a>'; $html .= ' (' . $link . ')'; } $html .= '<br />' . "\n"; if (isset( $all [$info->count] )) { $all [$info->count] .= '(' . $info->count . ') ' . $html; } else { $all [$info->count] = '(' . $info->count . ') ' . $html; } } // Sort comments by number of occurrences. krsort( $all ); $html = ''; foreach ($all as $info) { $html .= $info; } // TODO show info about others review with show hidde button. return $html; } /** * Get password */ protected function get_password() { return trim( $this->instance->password ); } /** * Get password md5 */ protected function get_password_md5() { return md5( $this->instance->id . (sesskey()) ); } /** * Get is use as base **/ public function is_use_as_base() { return $this->instance->usableasbase == 1; } /** * Check if pass password restriction */ public function pass_password_check($passset = '') { $password = $this->get_password(); if ($password > '') { global $SESSION; $passwordmd5 = $this->get_password_md5(); $passvar = 'vpl_password_' . $this->instance->id; $passattempt = 'vpl_password_attempt' . $this->instance->id; if (isset( $SESSION->$passvar ) && $SESSION->$passvar == $passwordmd5) { return true; } if ($passset == '') { $passset = optional_param( 'password', '', PARAM_TEXT ); } if ($passset > '') { if ($passset == $password) { $SESSION->$passvar = $passwordmd5; unset( $SESSION->$passattempt ); return true; } if (isset( $SESSION->$passattempt )) { $SESSION->$passattempt ++; } else { $SESSION->$passattempt = 1; } // Wait vpl_attempt_number seg to limit force brute crack. sleep( $SESSION->$passattempt ); } return false; } return true; } /** * Check password restriction */ public function password_check() { global $SESSION; if (! $this->pass_password_check()) { if ( constant( 'AJAX_SCRIPT' ) ) { throw new Exception( get_string( 'requiredpassword', VPL ) ); } require_once('forms/password_form.php'); $this->print_header(); $mform = new mod_vpl_password_form( $_SERVER ['SCRIPT_NAME'], $this); $passattempt = 'vpl_password_attempt' . $this->get_instance()->id; if (isset( $SESSION->$passattempt)) { vpl_notice( get_string( 'attemptnumber', VPL, $SESSION->$passattempt), 'warning'); } $mform->display(); $this->print_footer(); die(); } } /** * Check network restriction and return true o false * @return boolean */ public function pass_network_check() { return vpl_check_network( $this->instance->requirednet ); } /** * Check netword restriction and show error if not passed * @return void */ public function network_check() { if (! $this->pass_network_check()) { $str = get_string( 'opnotallowfromclient', VPL ) . ' ' . getremoteaddr(); if ( constant( 'AJAX_SCRIPT') ) { throw new Exception( $str ); } $this->print_header(); vpl_notice( $str , 'warning'); $this->print_footer(); die(); } } /** * Checks if SEB key is valid * @return void */ protected function is_sebkey_valid() { global $FULLME; $keys = trim($this->get_instance()->sebkeys); if ( $keys == '') { return true; } if ( ! isset($_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH']) ) { return false; } $key = $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH']; foreach (preg_split('/\s+/', $keys) as $testkey) { if (hash('sha256', $FULLME . $testkey) === $key) { return true; } } return false; } /** * Checks SEB restrictions and shows error if not passed * @return void */ protected function seb_check() { $inst = $this->get_instance(); $fail = $inst->sebrequired > 0; $fail = $fail && strpos($_SERVER['HTTP_USER_AGENT'], 'SEB') === false; $fail = $fail || ! $this->is_sebkey_valid(); if ( $fail ) { $str = get_string( 'sebrequired_help', VPL ); if ( constant( 'AJAX_SCRIPT') ) { throw new Exception( $str ); } $this->print_header(); vpl_notice( $str , 'warning'); $this->print_footer(); die(); } } /** * Return true if is set to use SEB * @return void */ protected function use_seb() { $inst = $this->get_instance(); return $inst->sebrequired || $inst->sebkeys > ''; } /** * Checks all restrictions and shows error if not passed * @return void */ public function restrictions_check() { $this->network_check(); $this->password_check(); $this->seb_check(); } /** * Check submission restriction * * @param $data Object * with submitted data * @param & $error * string * @return bool * */ public function pass_submission_restriction(& $alldata, & $error) { $max = $this->get_maxfilesize(); $rfn = $this->get_fgm('required'); $list = $rfn->getFilelist(); $error = ''; if (count( $alldata ) > $this->instance->maxfiles) { $error .= get_string( 'maxfilesexceeded', VPL ) . "\n"; } $lr = count( $list ); $lad = count( $alldata ); $i = 0; foreach ($alldata as $name => $data) { if (strlen( $data ) > $max) { $error .= '"' . s( $name ) . '" ' . get_string( 'maxfilesizeexceeded', VPL ) . "<br />"; } if (! vpl_is_valid_path_name( $name )) { $error .= '"' . s( $name ) . '" ' . get_string( 'incorrect_file_name', VPL ) . "<br />"; } if ($i < $lr && $list [$i] != $name) { $a = new stdClass(); $a->expected = $list [$i]; $a->found = $name; $error .= s( get_string( 'unexpected_file_name', VPL, $a ) ) . "<br />"; } $i++; } return strlen( $error ) == 0; } /** * Check and submission * * @param * $userid * @param $data Object * with submitted data * @param & $error * string * @return false or submission id */ public function add_submission($userid, & $files, $comments, & $error) { global $USER, $DB; if (! $this->pass_submission_restriction( $files, $error )) { return false; } $group = $this->get_usergroup($userid); if ($this->is_group_activity() && $group === false) { $error = get_string( 'notsaved', VPL ) . "\n" . get_string( 'inconsistentgroup', VPL ); return false; } $submittedby = ''; if ($USER->id != $userid ) { if ($this->has_capability( VPL_MANAGE_CAPABILITY ) || ($this->has_capability( VPL_GRADE_CAPABILITY ) )) { if (! $this->is_group_activity() ) { $user = $DB->get_record( 'user', array ( 'id' => $USER->id ) ); $submittedby = get_string( 'submittedby', VPL, fullname( $user ) ) . "\n"; if (strpos($comments, $submittedby) === 0 ) { $submittedby = ''; } } } else { $error = get_string( 'notsaved', VPL ) . "\n" . get_string( 'inconsistentgroup', VPL ); return false; } } $saveduserid = $this->is_group_activity() ? $USER->id : $userid; $lastsub = false; $lock = new \mod_vpl\util\lock($this->get_users_data_directory() . '/' . $saveduserid); if (($lastsubins = $this->last_user_submission( $userid )) !== false) { $lastsub = new mod_vpl_submission( $this, $lastsubins ); if ($lastsub->is_equal_to( $files, $submittedby . $comments )) { return $lastsubins->id; } } ignore_user_abort( true ); // Create submission record. $submissiondata = new stdClass(); $submissiondata->vpl = $this->get_instance()->id; $submissiondata->userid = $saveduserid; $submissiondata->datesubmitted = time(); $submissiondata->comments = $submittedby . $comments; if ( $lastsubins !== false ) { $submissiondata->nevaluations = $lastsubins->nevaluations; } if ( $group !== false ) { $submissiondata->groupid = $group->id; } $submissionid = $DB->insert_record( 'vpl_submissions', $submissiondata, true ); if (! $submissionid) { $error = get_string( 'notsaved', VPL ) . "\ninserting vpl_submissions record"; return false; } // Save files. $submission = new mod_vpl_submission( $this, $submissionid ); $submission->set_submitted_file( $files, $lastsub ); $submission->remove_grade(); // If no submitted by grader and not group activity, remove near submmissions. if ($USER->id == $userid) { $this->delete_overflow_submissions( $userid ); } return $submissionid; } /** * Get user submissions, order reverse submission id * * @param $userid int the user id to retrieve submissions * @param $groupifga boolean if group activity get group submissions. default true * @return FALSE/array of objects */ public function user_submissions($userid, $groupifga = true) { global $DB; if ($groupifga && $this->is_group_activity()) { $group = $this->get_usergroup($userid); if ($group) { $select = '(groupid = ?) AND (vpl = ?)'; $parms = array ( $group->id, $this->instance->id ); } else { return array (); } } else { $select = '(userid = ?) AND (vpl = ?)'; $parms = array ( $userid, $this->instance->id ); } return $DB->get_records_select( 'vpl_submissions', $select, $parms, 'id DESC' ); } /** * Get the last submission of all users * * @param string $fields conma separeted * to retrieve from submissions table, default s.*. userid is always retrieved * @return object array */ public function all_last_user_submission($fields = 's.*') { // Get last submissions records for this vpl module. global $DB; $id = $this->get_instance()->id; if ($this->is_group_activity()) { $idfield = 'groupid'; } else { $idfield = 'userid'; } $query = "SELECT s.$idfield, $fields FROM {vpl_submissions} s"; $query .= ' inner join '; $query .= ' (SELECT max(id) as maxid FROM {vpl_submissions} '; $query .= ' WHERE {vpl_submissions}.vpl=? '; $query .= " GROUP BY {vpl_submissions}.$idfield) as ls"; $query .= ' on s.id = ls.maxid'; return $DB->get_records_sql( $query, array( $id ) ); } /** * Get all saved submission of all users * * @param string $fields fields conma separeted * to retrieve from submissions table, default s.* * @return object array */ public function all_user_submission($fields = 's.*') { global $DB; $id = $this->get_instance()->id; $query = "SELECT s.id, $fields FROM {vpl_submissions} s WHERE vpl=?"; return $DB->get_records_sql( $query, array ( $id ) ); } /** * Get number of user submissions * * @return Array of objects with 'submissions' atribute as number of submissions saved */ public function get_submissions_number() { global $DB; if ( $this->is_group_activity() ) { $field = 'groupid'; } else { $field = 'userid'; } $id = $this->get_instance()->id; $query = "SELECT $field, COUNT(*) as submissions FROM {vpl_submissions}"; $query .= " WHERE {vpl_submissions}.vpl=$id"; $query .= " GROUP BY {vpl_submissions}.$field"; return $DB->get_records_sql( $query ); } /** * This is for compatibility to old group scheme. * Update the submission groupid for VPL version <= 3.2 * * Set the correct groupid when groupid = 0 * @param int $groupid. * If no $groupid => update $groupid of all groups of the activity * @return void */ public function update_group_v32($groupid='') { global $DB; if ( ! $this->is_group_activity() ) { return; } // All groups. if ($groupid == '') { $cm = $this->get_course_module(); $groups = groups_get_all_groups($this->get_course()->id, 0, $cm->groupingid); foreach ($groups as $cgroup) { $this->update_group_v32($cgroup->id); } } $students = $this->get_students($groupid); if (count($students) > 0) { $studentsids = array_keys($students); $insql = $DB->get_in_or_equal($studentsids); $vplid = $this->get_instance()->id; $select = 'userid ' . ($insql[0]) . ' and vpl = ? and groupid = 0'; $params = $insql[1]; $params[] = $vplid; $DB->set_field_select(VPL_SUBMISSIONS, 'groupid', $groupid, $select, $params); } } /** * Get last user submission * * @param int $userid * @return FALSE/object * */ public function last_user_submission($userid) { global $DB; if ($this->is_group_activity()) { $group = $this->get_usergroup($userid); if ($group !== false) { $select = "(groupid = ?) AND (vpl = ?)"; $params = array ( $group->id, $this->instance->id ); $res = $DB->get_records_select( 'vpl_submissions', $select, $params, 'id DESC', '*', 0, 1 ); foreach ($res as $sub) { return $sub; } $this->update_group_v32($group->id); $res = $DB->get_records_select( 'vpl_submissions', $select, $params, 'id DESC', '*', 0, 1 ); foreach ($res as $sub) { return $sub; } } return false; } $select = "(userid = ?) AND (vpl = ?)"; $params = array ( $userid, $this->instance->id ); $res = $DB->get_records_select( 'vpl_submissions', $select, $params, 'id DESC', '*', 0, 1 ); foreach ($res as $sub) { return $sub; } return false; } protected static $context = array (); /** * Return context object for this module instance * * @return object */ public function get_context() { if (! isset( self::$context [$this->cm->id] )) { self::$context [$this->cm->id] = context_module::instance( $this->cm->id ); } return self::$context [$this->cm->id]; } /** * Requiere the current user has the capability of performing $capability in this module instance * * @param string $capability * capability name * @param bool $alert * if true show a JavaScript alert message * @return void */ public function require_capability($capability, $alert = false) { if ($alert && ! ($this->has_capability( $capability ))) { global $OUTPUT; echo $OUTPUT->header(); vpl_js_alert( get_string( 'notavailable' ) ); } require_capability( $capability, $this->get_context() ); } /** * Check if the user has the capability of performing $capability in this module instance * * @param string $capability * capability name * @param int $userid * default null => current user * @return bool */ public function has_capability($capability, $userid = null) { return has_capability( $capability, $this->get_context(), $userid ); } /** * Delete overflow submissions. If three submissions within the period central is delete * * @param * $userid * @return void * */ public function delete_overflow_submissions($userid) { global $DB; $plugincfg = get_config('mod_vpl'); if (! isset( $plugincfg->discard_submission_period )) { return; } if ($plugincfg->discard_submission_period == 0) { // Keep all submissions. return; } if ($plugincfg->discard_submission_period > 0) { $select = "(userid = ?) AND (vpl = ?)"; $params = array ( $userid, $this->instance->id ); $res = $DB->get_records_select( VPL_SUBMISSIONS, $select, $params, 'id DESC', '*', 0, 3 ); if (count( $res ) == 3) { $i = 0; foreach ($res as $sub) { switch ($i) { case 0 : $last = $sub; break; case 1 : $second = $sub; break; case 2 : $first = $sub; break; } $i ++; } // Check time consistence. if (! ($last->datesubmitted > $second->datesubmitted && $second->datesubmitted > $first->datesubmitted)) { return; } if (($last->datesubmitted - $first->datesubmitted) < $plugincfg->discard_submission_period) { // Remove second submission. $submission = new mod_vpl_submission( $this, $second ); $submission->delete(); } } } } /** * Check if it is submission period * * @return bool * */ public function is_submission_period() { $now = time(); $ret = $this->instance->startdate <= $now; return $ret && ($this->instance->duedate == 0 || $this->instance->duedate >= $now); } /** * is visible this vpl instance * * @return bool */ public function is_visible() { $cm = $this->get_course_module(); $modinfo = get_fast_modinfo( $cm->course ); $ret = true; $ret = $ret && $modinfo->get_cm( $cm->id )->uservisible; $ret = $ret && $this->has_capability( VPL_VIEW_CAPABILITY ); // Grader and manager always view. $ret = $ret || $this->has_capability( VPL_GRADE_CAPABILITY ); $ret = $ret || $this->has_capability( VPL_MANAGE_CAPABILITY ); return $ret; } /** * this vpl instance admit submission * * @return bool */ public function is_submit_able() { $cm = $this->get_course_module(); $modinfo = get_fast_modinfo( $cm->course ); $instance = $this->get_instance(); $ret = true; $ret = $ret && $this->has_capability( VPL_SUBMIT_CAPABILITY ); $ret = $ret && $this->is_submission_period(); $ret = $ret && $modinfo->get_cm( $cm->id )->uservisible; // Manager can always submit. $ret = $ret || $this->has_capability( VPL_MANAGE_CAPABILITY ); return $ret; } /** * is group activity * * @return bool */ public function is_group_activity() { if (! isset( $this->group_activity )) { $cm = $this->get_course_module(); $this->group_activity = $cm->groupingid > 0 && $this->get_instance()->worktype == 1; // TODO check groups_get_activity_groupmode($cm)==SEPARATEGROUPS. } return $this->group_activity; } /** * * @param * user object return HTML code to show user picture * @return String */ public function user_fullname_picture($user, $withlink = true) { return $this->user_picture( $user ) . ' ' . $this->fullname( $user, $withlink ); } /** * * @param * user object return HTML code to show user picture * @return String */ public function user_picture($user) { global $OUTPUT; if ($this->is_group_activity()) { $group = $this->get_usergroup( $user->id ); $picture = print_group_picture( $group, $this->get_course()->id, false, true ); if (!$picture) { $url = vpl_abs_href( '/group/overview.php', 'id', $this->get_course()->id, 'group', $group->id); $picture = '<a href="' . $url . '" style="color: initial;">' . $OUTPUT->render(new pix_icon('group', '', 'mod_vpl')) . '</a>'; } return $picture; } else { $options = array('courseid' => $this->get_instance()->course, 'link' => ! $this->use_seb()); return $OUTPUT->user_picture( $user, $options); } } /** * return formated name of user or group * * @param * user object * @param * withlink boolean. if true and is group add link to group. Default true * @return String */ public function fullname($user, $withlink = true) { if ($this->is_group_activity()) { $group = $this->get_usergroup( $user->id ); if ($group !== false) { if ($withlink) { $url = vpl_abs_href( '/group/overview.php', 'id', $this->get_course()->id, 'group', $group->id); return '<a href="' . $url . '">' . $group->name . '</a>'; } else { return $group->name; } } return ''; } else { $fullname = fullname( $user ); if ($withlink) { $url = vpl_abs_href( '/user/view.php', 'id', $user->id, 'course', $this->get_course()->id); $html = "<a href=\"$url\" title=\"$fullname\">$fullname</a>"; } else { $html = $fullname; } return $html; } } /** * Get array of graders for this activity and group (optional) * * @param string $group optional parm with group to search for * @return array */ public function get_graders($group = '') { if (! isset( $this->graders )) { $this->graders = get_users_by_capability( $this->get_context(), VPL_GRADE_CAPABILITY, user_picture::fields( 'u' ), 'u.lastname ASC', '', '', $group ); } return $this->graders; } /** * Get array of students for this activity. If group is set return only group members * * @param string $group optional parm with group to search for * @return array of objects */ public function get_students($group = '') { if ( isset( $this->students ) && $group == '') { return $this->students; } // Generate array of graders indexed. $nostudents = array (); foreach ($this->get_graders($group) as $user) { $nostudents [$user->id] = true; } $students = array (); $all = get_users_by_capability( $this->get_context(), VPL_SUBMIT_CAPABILITY, user_picture::fields( 'u' ), 'u.lastname ASC', '', '', $group ); // TODO the following code is too slow. foreach ($all as $user) { if (! isset( $nostudents [$user->id] )) { $students [$user->id] = $user; } } if ($group != '') { // Don't cache if group request. $this->students = $students; } return $students; } /** * Check for user consistency. * This checks that both users are the same, or if this is a group activity, that they belong to the same group. * * @return bool */ public function is_inconsistent_user($current, $real) { if ($this->is_group_activity()) { $g1 = $this->get_usergroup($current); $g2 = $this->get_usergroup($real); if ($g1 === false || $g2 === false) { return $current != $real; } return $g1->id != $g2->id; } else { return $current != $real; } } /** * If is a group activity search for a group leader for the group of the userid (0 is not found) * * @return Integer userid */ public function get_group_leaderid($userid) { $leaderid = $userid; $group = $this->get_usergroup($userid); if ($group) { foreach ($this->get_usergroup_members( $group->id ) as $user) { if ($user->id < $leaderid) { $leaderid = $user->id; } } } return $leaderid; } /** * If is a group activity return the group of the userid * * @return Object/false */ public function get_usergroup($userid) { if ($this->is_group_activity()) { $courseid = $this->get_course()->id; $groupingid = $this->get_course_module()->groupingid; $groups = groups_get_all_groups( $courseid, $userid, $groupingid ); if ($groups === false || count( $groups ) > 1) { return false; } return reset( $groups ); } return false; } protected static $usergroupscache = array (); /** * If is a group activity return group members for the groupid * * @return Array of user objects */ public function get_group_members($groupid) { if (! isset( self::$usergroupscache [$groupid] )) { $gm = groups_get_members( $groupid ); if ($gm) { self::$usergroupscache [$groupid] = $gm; } else { self::$usergroupscache [$groupid] = array(); } } return self::$usergroupscache [$groupid]; } /** * If is a group activity return group members for the group of the userid * * @return Array of user objects */ public function get_usergroup_members($userid) { $group = $this->get_usergroup( $userid ); if ($group !== false) { return $this->get_group_members($group->id); } return array (); } /** * Return scale record if grade < 0 * * @return Object or false */ public function get_scale() { global $DB; if (! isset( $this->scale )) { if ($this->get_grade() < 0) { $gradeid = - $this->get_grade(); $this->scale = $DB->get_record( 'scale', array ( 'id' => $gradeid ) ); } else { $this->scale = false; } } return $this->scale; } /** * Return grade info take from gradebook * * @return Object or false */ public function get_grade_info() { global $CFG, $USER; if (! isset( $this->grade_info )) { $this->grade_info = false; if ($this->get_instance()->grade != 0) { // If 0 then NO GRADE. $userid = ($this->has_capability( VPL_GRADE_CAPABILITY ) || $this->has_capability( VPL_MANAGE_CAPABILITY )) ? null : $USER->id; require_once($CFG->libdir . '/gradelib.php'); $gradinginfo = grade_get_grades( $this->get_course()->id, 'mod', 'vpl', $this->get_instance()->id, $userid ); foreach ($gradinginfo->items as $gi) { $this->grade_info = $gi; } } } return $this->grade_info; } /** * Return visiblegrade from gradebook and for every user * * @return boolean */ public function get_visiblegrade() { if ($gi = $this->get_grade_info()) { if (is_array( $gi->grades )) { $usergi = reset( $gi->grades ); return ! ($gi->hidden || (is_object( $usergi ) && $usergi->hidden)); } else { return ! ($gi->hidden); } } else { return false; } } /** * Return grade (=0 => no grade, >0 max grade, <0 scaleid) * * @return int */ public function get_grade() { return $this->instance->grade; } public function get_max_grades() { global $DB; if ( $this->is_group_activity() ) { $field = 'groupid'; } else { $field = 'userid'; } $id = $this->get_instance()->id; $query = "SELECT $field, max({vpl_submissions}.grade) as maxgrade FROM {vpl_submissions}"; $query .= " WHERE {vpl_submissions}.vpl=$id and ({vpl_submissions}.grade is not null)"; $query .= " GROUP BY {vpl_submissions}.$field"; return $DB->get_records_sql( $query ); } /** * Format given grade as grade/maxgrade * @param float $grade * @return string Printable grade */ public function format_grade($grade) { return format_float($grade, 2, true, false) . ' / ' . format_float($this->get_grade(), 2, true, false); } /** * print end of page */ public function print_footer() { global $OUTPUT; if (! $this->use_seb() ) { $style = "float:right; right:10px; padding:8px; background-color: white;text-align:center;"; echo '<div style="' . $style . '">'; echo '<a href="http://vpl.dis.ulpgc.es/">'; echo 'VPL '. vpl_get_version(); echo '</a>'; echo '</div>'; } echo $OUTPUT->footer(); } /** * print end of page */ public function print_footer_simple() { global $OUTPUT; echo $OUTPUT->footer(); } /** * prepare_page initialy */ public function prepare_page($url = false, $parms = array()) { global $PAGE; $PAGE->set_cm( $this->get_course_module(), $this->get_course(), $this->get_instance() ); $PAGE->set_context( $this->get_context() ); if ($url) { $PAGE->set_url( '/mod/vpl/' . $url, $parms ); } } protected static $headerisout = false; public static function header_is_out() { return self::$headerisout; } /** * print header * * @param $info string title and last nav option */ public function print_header($info = '') { global $PAGE, $OUTPUT; if (self::$headerisout) { return; } $tittle = $this->get_printable_name(); if ($info) { $tittle .= ' ' . $info; } $PAGE->set_title( $this->get_course()->fullname . ' ' . $tittle ); $PAGE->set_pagelayout( 'incourse' ); $PAGE->set_heading( $this->get_course()->fullname ); if ( $this->use_seb() && ! $this->has_capability(VPL_GRADE_CAPABILITY)) { $PAGE->set_popup_notification_allowed(false); $PAGE->set_pagelayout('secure'); } echo $OUTPUT->header(); self::$headerisout = true; } public function print_header_simple($info = '') { global $OUTPUT, $PAGE; if (self::$headerisout) { return; } $tittle = $this->get_printable_name(); if ($info) { $tittle .= ' ' . $info; } $PAGE->set_title( $this->get_course()->fullname . ' ' . $tittle ); $PAGE->set_pagelayout( 'popup' ); if ( $this->use_seb() && ! $this->has_capability(VPL_GRADE_CAPABILITY)) { $PAGE->set_popup_notification_allowed(false); $PAGE->set_pagelayout('secure'); } echo $OUTPUT->header(); self::$headerisout = true; } /** * Print heading action with help * * @param $action string * base text and help */ public function print_heading_with_help($action) { global $OUTPUT; $title = get_string( $action, VPL ) . ': ' . $this->get_printable_name(); echo $OUTPUT->heading_with_help( vpl_get_awesome_icon($action) . $title, $action, 'vpl'); self::$headerisout = true; } /** * Create tabs to view_description/submit/view_submission/edit * * @param string $path to get the active tab * */ public function print_view_tabs($path) { // TODO refactor using functions. global $USER, $DB, $PAGE; $active = basename( $path ); $cmid = $this->cm->id; $userid = optional_param( 'userid', null, PARAM_INT ); $copy = optional_param( 'privatecopy', false, PARAM_INT ); $viewer = $this->has_capability( VPL_VIEW_CAPABILITY ); $submiter = $this->has_capability( VPL_SUBMIT_CAPABILITY ); $similarity = $this->has_capability( VPL_SIMILARITY_CAPABILITY ); $grader = $this->has_capability( VPL_GRADE_CAPABILITY ); $manager = $this->has_capability( VPL_MANAGE_CAPABILITY ); $example = $this->instance->example; if (! $userid || ! $grader || $copy) { $userid = $USER->id; } $level2 = $grader || $manager || $similarity; $maintabs = array (); $tabs = array (); $href = vpl_mod_href( 'view.php', 'id', $cmid, 'userid', $userid ); $viewtab = vpl_create_tabobject('view.php', $href, 'description' ); if ($level2) { if ($viewer) { $maintabs [] = $viewtab; } $href = vpl_mod_href( 'views/submissionslist.php', 'id', $cmid ); $maintabs [] = vpl_create_tabobject( 'submissionslist.php', $href, 'submissionslist' ); // Similarity. if ($similarity) { if ($active == 'listwatermark.php' || $active == 'similarity_form.php' || $active == 'listsimilarity.php') { $tabname = $active; } else { $tabname = 'similarity'; } $href = vpl_mod_href( 'similarity/similarity_form.php', 'id', $cmid ); $maintabs [] = vpl_create_tabobject( $tabname, $href, 'similarity' ); } // Test. if ($grader || $manager) { if ($active == 'submission.php' || $active == 'edit.php' || $active == 'submissionview.php' || $active == 'gradesubmission.php' || $active == 'previoussubmissionslist.php') { $tabname = $active; } else { $tabname = 'test'; } $href = vpl_mod_href( 'forms/submissionview.php', 'id', $cmid, 'userid', $userid ); if ($userid == $USER->id) { $maintabs [] = vpl_create_tabobject( $tabname, $href, 'test' ); } else { $user = $DB->get_record( 'user', array ( 'id' => $userid ) ); if ($this->is_group_activity()) { $text = get_string( 'group' ) . ' '; $icon = vpl_get_awesome_icon('group') . ' '; } else { $text = get_string( 'user' ) . ' '; $icon = vpl_get_awesome_icon('user') . ' '; } $text .= $this->fullname( $user, false ); $url = $PAGE->url->out( false, array( 'userid' => $USER->id ) ); $buttonexit = html_writer::tag('span', vpl_get_awesome_icon('exitrole'), array( 'class' => 'exit-activity-student-role btn-link', 'title' => get_string('returntoownactivity', VPL), 'onclick' => 'event.preventDefault(); window.location.href=\'' . $url . '\';' )); $maintabs [] = new tabobject( $tabname, $href, $icon . $text . $buttonexit, $text ); } } } switch ($active) { case 'view.php' : if ($level2) { // TODO replace by $OUTPUT->tabtree. print_tabs( array ( $maintabs, $tabs ), $active ); return; } case 'submission.php' : case 'edit.php' : case 'submissionview.php' : case 'gradesubmission.php' : case 'previoussubmissionslist.php' : $subinstance = $this->last_user_submission( $userid ); if ($viewer && ! $level2) { $tabs [] = $viewtab; } if ($manager || ($grader && $USER->id == $userid) || (! $grader && $submiter && $this->is_submit_able() && ! $this->instance->restrictededitor && ! $example)) { $href = vpl_mod_href( 'forms/submission.php', 'id', $cmid, 'userid', $userid ); $tabs [] = vpl_create_tabobject( 'submission.php', $href, 'submission' ); } if ($manager || ($grader && $USER->id == $userid) || (! $grader && $submiter && $this->is_submit_able())) { $href = vpl_mod_href( 'forms/edit.php', 'id', $cmid, 'userid', $userid ); $tabs [] = vpl_create_tabobject( 'edit.php', $href, 'edit'); } if (! $example && $submiter) { $href = vpl_mod_href( 'forms/submissionview.php', 'id', $cmid, 'userid', $userid ); $tabs [] = vpl_create_tabobject( 'submissionview.php', $href, 'submissionview'); if ($grader && $this->get_grade() != 0 && $subinstance && ($subinstance->dategraded == 0 || $subinstance->grader == $USER->id || $subinstance->grader == 0)) { $href = vpl_mod_href( 'forms/gradesubmission.php', 'id', $cmid, 'userid', $userid ); $text = get_string( 'grade' ); $tabs [] = vpl_create_tabobject( 'gradesubmission.php', $href, 'grade', 'moodle' ); } if ($subinstance /*&& ($grader || $similarity|| $this->instance->allowshowprevious)*/) { $href = vpl_mod_href( 'views/previoussubmissionslist.php', 'id', $cmid, 'userid', $userid ); $tabs [] = vpl_create_tabobject( 'previoussubmissionslist.php', $href, 'previoussubmissionslist' ); } } // Show user picture if this activity require password. if (! isset( $user ) && $this->instance->password > '') { $user = $DB->get_record( 'user', array ( 'id' => $userid ) ); } if (isset( $user )) { echo '<div style="position:absolute; right:50px; z-index:50;">'; echo $this->user_picture( $user ); echo '</div>'; } if ($level2) { print_tabs( array ( $maintabs, $tabs ), $active ); return; } else { print_tabs( array ( $tabs ), $active ); return; } break; case 'submissionslist.php' : print_tabs( array ( $maintabs ), $active ); return; case 'listwatermark.php' : case 'similarity_form.php' : case 'listsimilarity.php' : if ($similarity) { $href = vpl_mod_href( 'similarity/similarity_form.php', 'id', $cmid ); $tabs [] = vpl_create_tabobject( 'similarity_form.php', $href, 'similarity' ); if ($active == 'listsimilarity.php') { $tabs [] = vpl_create_tabobject( 'listsimilarity.php', '', 'listsimilarity' ); } $plugincfg = get_config('mod_vpl'); $watermark = isset( $plugincfg->use_watermarks ) && $plugincfg->use_watermarks; if ($watermark) { $href = vpl_mod_href( 'similarity/listwatermark.php', 'id', $cmid ); $tabs [] = vpl_create_tabobject( 'listwatermark.php', $href, 'listwatermarks' ); } } print_tabs( array ( $maintabs, $tabs ), $active ); break; } } /** * Show vpl name */ public function print_name() { echo '<h2>'; p( $this->get_printable_name() ); echo '</h2>'; } public function str_restriction($str, $value = null, $raw = false) { $html = '<b>'; if ($raw) { $html .= s( $str ); } else { $html .= s( get_string( $str, VPL ) ); } $html .= '</b>: '; if ($value === null) { $value = $this->instance->$str; } $html .= $value; return $html; } /** * Print one VPL setting * @param string $str setting string i18n to get descriptoin * @param string $value setting value, default null * @param boolean $raw if true $str if raw string, default false * @param boolean $newline if true print new line after setting, default false */ public function print_restriction($str, $value = null, $raw = false, $newline = true) { echo $this->str_restriction($str, $value, $raw); if ( $newline ) { echo '<br>'; } else { echo '. '; } } /** * Show vpl submission period */ public function print_submission_period() { if ($this->instance->startdate == 0 && $this->instance->duedate == 0) { return; } if ($this->instance->startdate) { $this->print_restriction( 'startdate', userdate( $this->instance->startdate ) ); } if ($this->instance->duedate) { $this->print_restriction( 'duedate', userdate( $this->instance->duedate ) ); } } /** * Show vpl submission restriction */ public function print_submission_restriction() { global $CFG, $USER; $filegroup = $this->get_fgm('required'); $files = $filegroup->getfilelist(); if (count( $files )) { $text = ''; $needcomma = false; foreach ($files as $file) { if ($needcomma) { $text .= ', '; } $text .= s( $file ); $needcomma = true; } $link = ' (<a href="'; $link .= vpl_mod_href( 'views/downloadfiles.php', 'id', $this->get_course_module()->id, 'type', 'required'); $link .= '">'; $link .= get_string( 'download', VPL ); $link .= '</a>)'; $this->print_restriction( 'requiredfiles', $text . $link ); } $instance = $this->get_instance(); if (count( $files ) != $instance->maxfiles) { $this->print_restriction( 'maxfiles' ); } if ($instance->maxfilesize) { $mfs = $this->get_maxfilesize(); $this->print_restriction( 'maxfilesize', vpl_conv_size_to_string( $mfs ) ); } $worktype = $instance->worktype; $values = array ( 0 => get_string( 'individualwork', VPL ), 1 => get_string( 'groupwork', VPL ) ); if ($worktype) { $this->print_restriction( 'worktype', $values [$worktype] . ' ' . $this->fullname( $USER ) ); } else { $this->print_restriction( 'worktype', $values [$worktype] ); } $stryes = get_string( 'yes' ); $strno = get_string( 'no' ); if ($instance->example) { $this->print_restriction( 'isexample', $stryes ); } $grader = $this->has_capability( VPL_GRADE_CAPABILITY ); if ($grader) { require_once($CFG->libdir . '/gradelib.php'); if ($gie = $this->get_grade_info()) { if ($gie->scaleid == 0) { $info = get_string('grademax', 'core_grades') . ': ' . format_float($gie->grademax, 5, true, true); $info .= $gie->hidden ? (' <b>' . get_string( 'hidden', 'core_grades' )) . '</b>' : ''; $info .= $gie->locked ? (' <b>' . get_string( 'locked', 'core_grades' )) . '</b>' : ''; } else { $info = get_string( 'typescale', 'core_grades' ); } $this->print_restriction( get_string( 'gradessettings', 'core_grades' ), $info, true ); } else { $this->print_restriction( get_string( 'gradessettings', 'core_grades' ), get_string( 'nograde' ), true ); } } $this->print_gradereduction(); if ($grader) { if (trim( $instance->password ) > '') { $this->print_restriction( get_string( 'password' ), $stryes, true ); } if (trim( $instance->requirednet ) > '') { $this->print_restriction( 'requirednet', s( $instance->requirednet )); } if ( $instance->sebrequired > 0) { $this->print_restriction('sebrequired', $stryes ); } if (trim( $instance->sebkeys ) > '') { $this->print_restriction('sebkeys', $stryes ); } if ($instance->restrictededitor) { $this->print_restriction( 'restrictededitor', $stryes ); } if (! $this->get_course_module()->visible) { $this->print_restriction( get_string( 'visible' ), $strno, true ); } if ($instance->basedon) { try { $basedon = new mod_vpl( null, $instance->basedon ); $link = '<a href="'; $link .= vpl_mod_href( 'view.php', 'id', $basedon->cm->id ); $link .= '">'; $link .= $basedon->get_printable_name(); $link .= '</a>'; $this->print_restriction( 'basedon', $link ); } catch (Exception $e) { $this->print_restriction( 'basedon', $e->getMessage() ); } } $noyes = array ( $strno, $stryes ); $this->print_restriction( 'run', $noyes [$instance->run], false, false ); if ($instance->runscript) { $this->print_restriction( 'runscript', strtoupper($instance->runscript), false, false ); } if ($instance->debug) { $this->print_restriction( 'debug', $noyes [1], false, false ); } if ($instance->debugscript) { $this->print_restriction( 'debugscript', strtoupper($instance->debugscript), false, false ); } $this->print_restriction( 'evaluate', $noyes [$instance->evaluate], false, ! ($instance->evaluate && $instance->evaluateonsubmission) ); if ($instance->evaluate && $instance->evaluateonsubmission) { $this->print_restriction( 'evaluateonsubmission', $noyes [1] ); } if ($instance->automaticgrading) { $this->print_restriction( 'automaticgrading', $noyes [1], false, false ); } if ($instance->maxexetime) { $this->print_restriction( 'maxexetime', format_time($instance->maxexetime), false, false ); } if ($instance->maxexememory) { $this->print_restriction( 'maxexememory', vpl_conv_size_to_string( $instance->maxexememory ), false, false ); } if ($instance->maxexefilesize) { $this->print_restriction( 'maxexefilesize', vpl_conv_size_to_string( $instance->maxexefilesize ), false, false ); } if ($instance->maxexeprocesses) { $this->print_restriction( 'maxexeprocesses', null, false, false ); } } } /** * Show short description */ public function print_shordescription() { global $OUTPUT; if ($this->instance->shortdescription) { echo $OUTPUT->box_start(); echo format_text( $this->instance->shortdescription, FORMAT_PLAIN ); echo $OUTPUT->box_end(); } } /** * Show short description */ public function print_gradereduction($return = false) { if ($this->instance->reductionbyevaluation > 0) { $html = $this->str_restriction( 'reductionbyevaluation', $this->instance->reductionbyevaluation); if ( $this->instance->freeevaluations > 0) { $html .= ' ' . $this->str_restriction( 'freeevaluations', $this->instance->freeevaluations); } if ( $return ) { return $html; } echo $html . '<br>'; } } /** * Show full description */ public function print_fulldescription() { global $OUTPUT; $full = $this->get_fulldescription_with_basedon(); if ($full > '') { echo $OUTPUT->box( $full ); } else { $this->print_shordescription(); } } /** * Print variations in vpl instance */ public function print_variations() { global $OUTPUT; global $DB; require_once(dirname( __FILE__ ) . '/views/show_hide_div.class.php'); $variations = $DB->get_records( VPL_VARIATIONS, array ( 'vpl' => $this->instance->id ) ); if (count( $variations ) > 0) { $div = new vpl_hide_show_div(); echo '<br /><b>' . get_string( 'variations', VPL ) . $div->generate( true ) . '</b><br />'; $div->begin_div(); if (! $this->instance->usevariations) { echo '<b>' . get_string( 'variations_unused', VPL ) . '</b><br />'; } if ($this->instance->variationtitle) { echo '<b>' . get_string( 'variationtitle', VPL ) . ': ' . s( $this->instance->variationtitle ) . '</b><br />'; } $number = 1; foreach ($variations as $variation) { echo '<b>' . get_string( 'variation', VPL, $number ) . '</b><br />'; echo $OUTPUT->box( $variation->description ); $number ++; } $div->end_div(); } } /** * Get user variation. Assign one if needed */ public function get_variation($userid) { global $DB; if ($this->is_group_activity()) { $userid = $this->get_group_leaderid( $userid ); if ($userid == 0) { return false; } } $varassigned = $DB->get_record( VPL_ASSIGNED_VARIATIONS, array ( 'vpl' => $this->instance->id, 'userid' => $userid ) ); if ($varassigned === false) { // Variation not assigned. $variations = $DB->get_records( VPL_VARIATIONS, array ( 'vpl' => $this->instance->id ) ); if (count( $variations ) == 0) { // No variation set. return false; } // Select a random variation. shuffle( $variations ); $variation = $variations [0]; $assign = new stdClass(); $assign->vpl = $this->instance->id; $assign->variation = $variation->id; $assign->userid = $userid; if (! $DB->insert_record( VPL_ASSIGNED_VARIATIONS, $assign )) { print_error( 'vpl variation not assigned' ); } } else { if ($varassigned === false || $varassigned->vpl != $this->instance->id) { // Test consistency. // TODO repair inconsistence? print_error( 'vpl assigned variation inconsistency' ); } $variation = $DB->get_record( VPL_VARIATIONS, array ( 'id' => $varassigned->variation ) ); } return $variation; } /** * Show variations if actived and defined */ public function print_variation($userid = 0, $already = array()) { global $OUTPUT; if (isset( $already [$this->instance->id] )) { // Avoid infinite recursion. return; } $already [$this->instance->id] = true; // Mark as visited. if ($this->instance->basedon) { // Show recursive varaitions. $basevpl = new mod_vpl( false, $this->instance->basedon ); $basevpl->print_variation( $userid, $already ); } // If user with grade or manage capability print all variations. if ($this->has_capability( VPL_GRADE_CAPABILITY, $userid ) || $this->has_capability( VPL_MANAGE_CAPABILITY, $userid )) { $this->print_variations(); } // Show user variation if active. if ($this->instance->usevariations) { // Variations actived. $variation = $this->get_variation( $userid ); if ($variation !== false) { // Variations defined. if ($this->instance->variationtitle > '') { echo '<b>' . format_text( $this->instance->variationtitle, FORMAT_HTML ) . '</b><br />'; } echo $OUTPUT->box( $variation->description ); } } } /** * return an array with variations for this user */ public function get_variation_identification($userid = 0, &$already = array()) { if (! ($this->instance->usevariations) || isset( $already [$this->instance->id] )) { // Avoid infinite recursion. return array (); } $already [$this->instance->id] = true; if ($this->instance->basedon) { $basevpl = new mod_vpl( false, $this->instance->basedon ); $ret = $basevpl->get_variation_identification( $userid, $already ); } else { $ret = array (); } $variation = $this->get_variation( $userid ); if ($variation !== false) { $ret [] = $variation->identification; } return $ret; } public function count_graded() { $numsubs = 0; $numgraded = 0; $subs = $this->all_last_user_submission( 's.dategraded, s.userid' ); if ($this->is_group_activity()) { // Fixes group activity userid. foreach ($subs as $sub) { $group = $this->get_group_members($sub->groupid); if ( count($group) ) { $user = reset($group); $sub->userid = $user->id; } } } $students = $this->get_students(); foreach ($subs as $sub) { if (isset( $students [$sub->userid] )) { $numsubs ++; if ($sub->dategraded > 0) { // Is graded. $numgraded ++; } } } return array ( 'submissions' => $numsubs, 'graded' => $numgraded ); } }