2009-05-28 03:51:39 +00:00
|
|
|
<?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/>.
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
|
2005-05-06 06:24:04 +00:00
|
|
|
/**
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
* Library of functions for the quiz module.
|
|
|
|
*
|
|
|
|
* This contains functions that are called also from outside the quiz module
|
|
|
|
* Functions that are only called by the quiz module itself are in {@link locallib.php}
|
|
|
|
*
|
2011-02-21 16:13:25 +00:00
|
|
|
* @package mod
|
2011-02-03 18:06:10 +00:00
|
|
|
* @subpackage quiz
|
2011-02-21 16:13:25 +00:00
|
|
|
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
*/
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
|
2011-02-23 15:03:35 +00:00
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
require_once($CFG->libdir . '/eventslib.php');
|
2009-12-23 03:01:36 +00:00
|
|
|
require_once($CFG->dirroot . '/calendar/lib.php');
|
2004-07-21 13:01:08 +00:00
|
|
|
|
2006-03-20 20:59:46 +00:00
|
|
|
|
2008-08-26 01:05:01 +00:00
|
|
|
/**#@+
|
2011-02-03 18:06:10 +00:00
|
|
|
* Option controlling what options are offered on the quiz settings form.
|
2008-08-26 01:05:01 +00:00
|
|
|
*/
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
define('QUIZ_MAX_ATTEMPT_OPTION', 10);
|
|
|
|
define('QUIZ_MAX_QPP_OPTION', 50);
|
|
|
|
define('QUIZ_MAX_DECIMAL_OPTION', 5);
|
|
|
|
define('QUIZ_MAX_Q_DECIMAL_OPTION', 7);
|
2006-03-20 20:59:46 +00:00
|
|
|
/**#@-*/
|
|
|
|
|
2011-10-06 20:15:01 +01:00
|
|
|
/**#@+
|
|
|
|
* Options determining how the grades from individual attempts are combined to give
|
|
|
|
* the overall grade for a user
|
|
|
|
*/
|
|
|
|
define('QUIZ_GRADEHIGHEST', '1');
|
|
|
|
define('QUIZ_GRADEAVERAGE', '2');
|
|
|
|
define('QUIZ_ATTEMPTFIRST', '3');
|
|
|
|
define('QUIZ_ATTEMPTLAST', '4');
|
|
|
|
/**#@-*/
|
|
|
|
|
2006-03-20 20:59:46 +00:00
|
|
|
/**
|
2012-05-04 12:40:21 +01:00
|
|
|
* @var int If start and end date for the quiz are more than this many seconds apart
|
2006-03-20 20:59:46 +00:00
|
|
|
* they will be represented by two separate events in the calendar
|
|
|
|
*/
|
2012-05-04 12:40:21 +01:00
|
|
|
define('QUIZ_MAX_EVENT_LENGTH', 5*24*60*60); // 5 days.
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2012-01-20 16:52:17 -08:00
|
|
|
/**#@+
|
|
|
|
* Options for navigation method within quizzes.
|
|
|
|
*/
|
|
|
|
define('QUIZ_NAVMETHOD_FREE', 'free');
|
|
|
|
define('QUIZ_NAVMETHOD_SEQ', 'sequential');
|
|
|
|
/**#@-*/
|
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
/**
|
|
|
|
* Given an object containing all the necessary data,
|
2008-06-01 19:09:13 +00:00
|
|
|
* (defined by the form in mod_form.php) this function
|
2006-08-16 08:46:10 +00:00
|
|
|
* will create a new instance and return the id number
|
|
|
|
* of the new instance.
|
2006-12-19 07:03:08 +00:00
|
|
|
*
|
2006-08-16 08:46:10 +00:00
|
|
|
* @param object $quiz the data that came from the form.
|
2006-08-22 17:31:26 +00:00
|
|
|
* @return mixed the id of the new instance on success,
|
|
|
|
* false or a string error message on failure.
|
2006-08-16 08:46:10 +00:00
|
|
|
*/
|
2002-10-04 02:59:05 +00:00
|
|
|
function quiz_add_instance($quiz) {
|
2008-06-01 21:36:11 +00:00
|
|
|
global $DB;
|
2010-08-10 09:56:48 +00:00
|
|
|
$cmid = $quiz->coursemodule;
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Process the options from the form.
|
|
|
|
$quiz->created = time();
|
2006-08-03 16:30:38 +00:00
|
|
|
$quiz->questions = '';
|
2006-08-22 17:31:26 +00:00
|
|
|
$result = quiz_process_options($quiz);
|
|
|
|
if ($result && is_string($result)) {
|
|
|
|
return $result;
|
|
|
|
}
|
2005-02-05 17:28:06 +00:00
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Try to store it in the database.
|
2009-03-17 07:08:33 +00:00
|
|
|
$quiz->id = $DB->insert_record('quiz', $quiz);
|
2002-10-14 12:21:18 +00:00
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Do the processing required after an add or an update.
|
|
|
|
quiz_after_add_or_update($quiz);
|
2006-12-19 07:03:08 +00:00
|
|
|
|
2002-10-14 12:21:18 +00:00
|
|
|
return $quiz->id;
|
2002-10-04 02:59:05 +00:00
|
|
|
}
|
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
/**
|
|
|
|
* Given an object containing all the necessary data,
|
2008-06-01 19:09:13 +00:00
|
|
|
* (defined by the form in mod_form.php) this function
|
2006-08-16 08:46:10 +00:00
|
|
|
* will update an existing instance with new data.
|
2006-12-19 07:03:08 +00:00
|
|
|
*
|
2006-08-16 08:46:10 +00:00
|
|
|
* @param object $quiz the data that came from the form.
|
2006-08-22 17:31:26 +00:00
|
|
|
* @return mixed true on success, false or a string error message on failure.
|
2006-08-16 08:46:10 +00:00
|
|
|
*/
|
2009-03-17 07:08:33 +00:00
|
|
|
function quiz_update_instance($quiz, $mform) {
|
|
|
|
global $CFG, $DB;
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Process the options from the form.
|
2006-08-22 17:31:26 +00:00
|
|
|
$result = quiz_process_options($quiz);
|
|
|
|
if ($result && is_string($result)) {
|
|
|
|
return $result;
|
|
|
|
}
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2011-02-04 15:47:52 +00:00
|
|
|
$oldquiz = $DB->get_record('quiz', array('id' => $quiz->instance));
|
2011-02-03 18:06:10 +00:00
|
|
|
|
2009-03-17 07:08:33 +00:00
|
|
|
// Repaginate, if asked to.
|
|
|
|
if (!$quiz->shufflequestions && !empty($quiz->repaginatenow)) {
|
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
2011-07-07 12:37:40 +01:00
|
|
|
$quiz->questions = quiz_repaginate(quiz_clean_layout($oldquiz->questions, true),
|
|
|
|
$quiz->questionsperpage);
|
2009-03-17 07:08:33 +00:00
|
|
|
}
|
|
|
|
unset($quiz->repaginatenow);
|
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Update the database.
|
2002-10-04 02:59:05 +00:00
|
|
|
$quiz->id = $quiz->instance;
|
2009-03-17 07:08:33 +00:00
|
|
|
$DB->update_record('quiz', $quiz);
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Do the processing required after an add or an update.
|
|
|
|
quiz_after_add_or_update($quiz);
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
if ($oldquiz->grademethod != $quiz->grademethod) {
|
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
|
|
|
$quiz->sumgrades = $oldquiz->sumgrades;
|
|
|
|
$quiz->grade = $oldquiz->grade;
|
|
|
|
quiz_update_all_final_grades($quiz);
|
|
|
|
quiz_update_grades($quiz);
|
|
|
|
}
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Delete any previous preview attempts.
|
2009-03-04 08:35:05 +00:00
|
|
|
quiz_delete_previews($quiz);
|
2004-04-28 02:56:40 +00:00
|
|
|
|
2002-10-14 12:21:18 +00:00
|
|
|
return true;
|
2002-10-04 02:59:05 +00:00
|
|
|
}
|
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
|
|
|
* Given an ID of an instance of this module,
|
|
|
|
* this function will permanently delete the instance
|
|
|
|
* and any data that depends on it.
|
|
|
|
*
|
2011-02-03 18:06:10 +00:00
|
|
|
* @param int $id the id of the quiz to delete.
|
2011-02-23 16:25:25 +00:00
|
|
|
* @return bool success or failure.
|
2009-05-28 03:51:39 +00:00
|
|
|
*/
|
2002-10-04 02:59:05 +00:00
|
|
|
function quiz_delete_instance($id) {
|
2008-06-01 21:36:11 +00:00
|
|
|
global $DB;
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
$quiz = $DB->get_record('quiz', array('id' => $id), '*', MUST_EXIST);
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2009-03-04 08:35:05 +00:00
|
|
|
quiz_delete_all_attempts($quiz);
|
2010-03-08 16:01:38 +00:00
|
|
|
quiz_delete_all_overrides($quiz);
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2009-03-04 08:35:05 +00:00
|
|
|
$DB->delete_records('quiz_question_instances', array('quiz' => $quiz->id));
|
|
|
|
$DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2009-03-04 08:35:05 +00:00
|
|
|
$events = $DB->get_records('event', array('modulename' => 'quiz', 'instance' => $quiz->id));
|
2011-05-12 00:30:25 +01:00
|
|
|
foreach ($events as $event) {
|
2009-12-23 03:01:36 +00:00
|
|
|
$event = calendar_event::load($event);
|
|
|
|
$event->delete();
|
2004-05-17 17:04:01 +00:00
|
|
|
}
|
|
|
|
|
2007-06-10 22:52:41 +00:00
|
|
|
quiz_grade_item_delete($quiz);
|
2009-03-04 08:35:05 +00:00
|
|
|
$DB->delete_records('quiz', array('id' => $quiz->id));
|
2007-06-10 22:52:41 +00:00
|
|
|
|
2009-03-04 08:35:05 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-03-08 16:01:38 +00:00
|
|
|
/**
|
|
|
|
* Deletes a quiz override from the database and clears any corresponding calendar events
|
|
|
|
*
|
|
|
|
* @param object $quiz The quiz object.
|
2011-02-23 16:25:25 +00:00
|
|
|
* @param int $overrideid The id of the override being deleted
|
2010-03-08 16:01:38 +00:00
|
|
|
* @return bool true on success
|
|
|
|
*/
|
|
|
|
function quiz_delete_override($quiz, $overrideid) {
|
|
|
|
global $DB;
|
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
$override = $DB->get_record('quiz_overrides', array('id' => $overrideid), '*', MUST_EXIST);
|
2010-03-08 16:01:38 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Delete the events.
|
2011-02-03 18:06:10 +00:00
|
|
|
$events = $DB->get_records('event', array('modulename' => 'quiz',
|
|
|
|
'instance' => $quiz->id, 'groupid' => (int)$override->groupid,
|
|
|
|
'userid' => (int)$override->userid));
|
|
|
|
foreach ($events as $event) {
|
2010-03-08 16:01:38 +00:00
|
|
|
$eventold = calendar_event::load($event);
|
|
|
|
$eventold->delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
$DB->delete_records('quiz_overrides', array('id' => $overrideid));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes all quiz overrides from the database and clears any corresponding calendar events
|
|
|
|
*
|
|
|
|
* @param object $quiz The quiz object.
|
|
|
|
*/
|
|
|
|
function quiz_delete_all_overrides($quiz) {
|
|
|
|
global $DB;
|
|
|
|
|
|
|
|
$overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id), 'id');
|
|
|
|
foreach ($overrides as $override) {
|
|
|
|
quiz_delete_override($quiz, $override->id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a quiz object with override information for a user.
|
|
|
|
*
|
|
|
|
* Algorithm: For each quiz setting, if there is a matching user-specific override,
|
|
|
|
* then use that otherwise, if there are group-specific overrides, return the most
|
|
|
|
* lenient combination of them. If neither applies, leave the quiz setting unchanged.
|
|
|
|
*
|
|
|
|
* Special case: if there is more than one password that applies to the user, then
|
|
|
|
* quiz->extrapasswords will contain an array of strings giving the remaining
|
|
|
|
* passwords.
|
|
|
|
*
|
|
|
|
* @param object $quiz The quiz object.
|
2011-02-23 16:25:25 +00:00
|
|
|
* @param int $userid The userid.
|
2010-03-08 16:01:38 +00:00
|
|
|
* @return object $quiz The updated quiz object.
|
|
|
|
*/
|
|
|
|
function quiz_update_effective_access($quiz, $userid) {
|
|
|
|
global $DB;
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Check for user override.
|
2010-03-08 16:01:38 +00:00
|
|
|
$override = $DB->get_record('quiz_overrides', array('quiz' => $quiz->id, 'userid' => $userid));
|
|
|
|
|
|
|
|
if (!$override) {
|
2010-09-21 08:54:01 +00:00
|
|
|
$override = new stdClass();
|
2010-03-08 16:01:38 +00:00
|
|
|
$override->timeopen = null;
|
|
|
|
$override->timeclose = null;
|
|
|
|
$override->timelimit = null;
|
|
|
|
$override->attempts = null;
|
|
|
|
$override->password = null;
|
|
|
|
}
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Check for group overrides.
|
2010-03-08 16:01:38 +00:00
|
|
|
$groupings = groups_get_user_groups($quiz->course, $userid);
|
|
|
|
|
2010-05-10 22:15:17 +00:00
|
|
|
if (!empty($groupings[0])) {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Select all overrides that apply to the User's groups.
|
2010-05-10 22:15:17 +00:00
|
|
|
list($extra, $params) = $DB->get_in_or_equal(array_values($groupings[0]));
|
2010-03-08 16:01:38 +00:00
|
|
|
$sql = "SELECT * FROM {quiz_overrides}
|
|
|
|
WHERE groupid $extra AND quiz = ?";
|
2010-05-01 08:08:50 +00:00
|
|
|
$params[] = $quiz->id;
|
2010-03-08 16:01:38 +00:00
|
|
|
$records = $DB->get_records_sql($sql, $params);
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Combine the overrides.
|
2010-03-08 16:01:38 +00:00
|
|
|
$opens = array();
|
|
|
|
$closes = array();
|
|
|
|
$limits = array();
|
|
|
|
$attempts = array();
|
|
|
|
$passwords = array();
|
|
|
|
|
|
|
|
foreach ($records as $gpoverride) {
|
|
|
|
if (isset($gpoverride->timeopen)) {
|
|
|
|
$opens[] = $gpoverride->timeopen;
|
|
|
|
}
|
|
|
|
if (isset($gpoverride->timeclose)) {
|
|
|
|
$closes[] = $gpoverride->timeclose;
|
|
|
|
}
|
|
|
|
if (isset($gpoverride->timelimit)) {
|
|
|
|
$limits[] = $gpoverride->timelimit;
|
|
|
|
}
|
|
|
|
if (isset($gpoverride->attempts)) {
|
|
|
|
$attempts[] = $gpoverride->attempts;
|
|
|
|
}
|
|
|
|
if (isset($gpoverride->password)) {
|
|
|
|
$passwords[] = $gpoverride->password;
|
|
|
|
}
|
|
|
|
}
|
2012-05-04 12:40:21 +01:00
|
|
|
// If there is a user override for a setting, ignore the group override.
|
2010-03-08 16:01:38 +00:00
|
|
|
if (is_null($override->timeopen) && count($opens)) {
|
2011-02-03 18:06:10 +00:00
|
|
|
$override->timeopen = min($opens);
|
2010-03-08 16:01:38 +00:00
|
|
|
}
|
|
|
|
if (is_null($override->timeclose) && count($closes)) {
|
2011-02-03 18:06:10 +00:00
|
|
|
$override->timeclose = max($closes);
|
2010-03-08 16:01:38 +00:00
|
|
|
}
|
|
|
|
if (is_null($override->timelimit) && count($limits)) {
|
2011-02-03 18:06:10 +00:00
|
|
|
$override->timelimit = max($limits);
|
2010-03-08 16:01:38 +00:00
|
|
|
}
|
|
|
|
if (is_null($override->attempts) && count($attempts)) {
|
2011-02-03 18:06:10 +00:00
|
|
|
$override->attempts = max($attempts);
|
2010-03-08 16:01:38 +00:00
|
|
|
}
|
|
|
|
if (is_null($override->password) && count($passwords)) {
|
2011-02-03 18:06:10 +00:00
|
|
|
$override->password = array_shift($passwords);
|
2010-03-08 16:01:38 +00:00
|
|
|
if (count($passwords)) {
|
2011-02-03 18:06:10 +00:00
|
|
|
$override->extrapasswords = $passwords;
|
2010-03-08 16:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Merge with quiz defaults.
|
2011-02-03 18:06:10 +00:00
|
|
|
$keys = array('timeopen', 'timeclose', 'timelimit', 'attempts', 'password', 'extrapasswords');
|
2010-03-08 16:01:38 +00:00
|
|
|
foreach ($keys as $key) {
|
|
|
|
if (isset($override->{$key})) {
|
|
|
|
$quiz->{$key} = $override->{$key};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $quiz;
|
|
|
|
}
|
|
|
|
|
2009-03-04 08:35:05 +00:00
|
|
|
/**
|
|
|
|
* Delete all the attempts belonging to a quiz.
|
2009-05-28 03:51:39 +00:00
|
|
|
*
|
|
|
|
* @param object $quiz The quiz object.
|
2009-03-04 08:35:05 +00:00
|
|
|
*/
|
|
|
|
function quiz_delete_all_attempts($quiz) {
|
|
|
|
global $CFG, $DB;
|
2011-03-16 18:56:14 +00:00
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
|
|
|
question_engine::delete_questions_usage_by_activities(new qubaids_for_quiz($quiz->id));
|
2009-03-04 08:35:05 +00:00
|
|
|
$DB->delete_records('quiz_attempts', array('quiz' => $quiz->id));
|
|
|
|
$DB->delete_records('quiz_grades', array('quiz' => $quiz->id));
|
2002-10-04 02:59:05 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
/**
|
|
|
|
* Get the best current grade for a particular user in a quiz.
|
|
|
|
*
|
|
|
|
* @param object $quiz the quiz settings.
|
2011-02-23 16:25:25 +00:00
|
|
|
* @param int $userid the id of the user.
|
2011-05-12 00:30:25 +01:00
|
|
|
* @return float the user's current grade for this quiz, or null if this user does
|
2011-02-03 18:06:10 +00:00
|
|
|
* not have a grade on this quiz.
|
|
|
|
*/
|
|
|
|
function quiz_get_best_grade($quiz, $userid) {
|
|
|
|
global $DB;
|
2011-05-12 00:30:25 +01:00
|
|
|
$grade = $DB->get_field('quiz_grades', 'grade',
|
|
|
|
array('quiz' => $quiz->id, 'userid' => $userid));
|
2011-02-03 18:06:10 +00:00
|
|
|
|
2011-02-09 16:56:44 +00:00
|
|
|
// Need to detect errors/no result, without catching 0 grades.
|
2011-02-03 18:06:10 +00:00
|
|
|
if ($grade === false) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $grade + 0; // Convert to number.
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is this a graded quiz? If this method returns true, you can assume that
|
|
|
|
* $quiz->grade and $quiz->sumgrades are non-zero (for example, if you want to
|
|
|
|
* divide by them).
|
|
|
|
*
|
|
|
|
* @param object $quiz a row from the quiz table.
|
2011-02-23 16:25:25 +00:00
|
|
|
* @return bool whether this is a graded quiz.
|
2011-02-03 18:06:10 +00:00
|
|
|
*/
|
|
|
|
function quiz_has_grades($quiz) {
|
|
|
|
return $quiz->grade >= 0.000005 && $quiz->sumgrades >= 0.000005;
|
|
|
|
}
|
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
|
|
|
* Return a small object with summary information about what a
|
|
|
|
* user has done with a given particular instance of this module
|
|
|
|
* Used for user activity reports.
|
|
|
|
* $return->time = the time they did it
|
|
|
|
* $return->info = a short text description
|
|
|
|
*
|
|
|
|
* @param object $course
|
|
|
|
* @param object $user
|
|
|
|
* @param object $mod
|
|
|
|
* @param object $quiz
|
|
|
|
* @return object|null
|
|
|
|
*/
|
2002-10-04 02:59:05 +00:00
|
|
|
function quiz_user_outline($course, $user, $mod, $quiz) {
|
2009-10-27 12:37:15 +00:00
|
|
|
global $DB, $CFG;
|
|
|
|
require_once("$CFG->libdir/gradelib.php");
|
|
|
|
$grades = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $user->id);
|
|
|
|
|
|
|
|
if (empty($grades->items[0]->grades)) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
$grade = reset($grades->items[0]->grades);
|
2009-03-18 05:16:49 +00:00
|
|
|
}
|
|
|
|
|
2011-02-21 18:10:19 +00:00
|
|
|
$result = new stdClass();
|
2009-10-27 12:37:15 +00:00
|
|
|
$result->info = get_string('grade') . ': ' . $grade->str_long_grade;
|
2011-05-06 16:15:49 +08:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Datesubmitted == time created. dategraded == time modified or time overridden
|
|
|
|
// if grade was last modified by the user themselves use date graded. Otherwise use
|
|
|
|
// date submitted.
|
|
|
|
// TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704.
|
2011-05-06 16:15:49 +08:00
|
|
|
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
|
|
|
|
$result->time = $grade->dategraded;
|
|
|
|
} else {
|
|
|
|
$result->time = $grade->datesubmitted;
|
|
|
|
}
|
|
|
|
|
2009-03-18 05:16:49 +00:00
|
|
|
return $result;
|
2009-10-27 12:37:15 +00:00
|
|
|
}
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
|
|
|
* Print a detailed representation of what a user has done with
|
|
|
|
* a given particular instance of this module, for user activity reports.
|
|
|
|
*
|
|
|
|
* @param object $course
|
|
|
|
* @param object $user
|
|
|
|
* @param object $mod
|
|
|
|
* @param object $quiz
|
|
|
|
* @return bool
|
|
|
|
*/
|
2002-10-04 02:59:05 +00:00
|
|
|
function quiz_user_complete($course, $user, $mod, $quiz) {
|
2010-01-28 07:57:48 +00:00
|
|
|
global $DB, $CFG, $OUTPUT;
|
2012-04-24 15:01:12 +01:00
|
|
|
require_once($CFG->libdir . '/gradelib.php');
|
|
|
|
require_once($CFG->libdir . '/mod/quiz/locallib.php');
|
2011-02-03 18:06:10 +00:00
|
|
|
|
2009-10-27 12:37:15 +00:00
|
|
|
$grades = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $user->id);
|
|
|
|
if (!empty($grades->items[0]->grades)) {
|
|
|
|
$grade = reset($grades->items[0]->grades);
|
|
|
|
echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
|
|
|
|
if ($grade->str_feedback) {
|
|
|
|
echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
|
|
|
|
}
|
|
|
|
}
|
2002-10-04 02:59:05 +00:00
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
if ($attempts = $DB->get_records('quiz_attempts',
|
|
|
|
array('userid' => $user->id, 'quiz' => $quiz->id), 'attempt')) {
|
2005-05-06 06:24:04 +00:00
|
|
|
foreach ($attempts as $attempt) {
|
2012-06-01 08:45:41 -06:00
|
|
|
echo get_string('attempt', 'quiz', $attempt->attempt) . ': ';
|
2012-04-24 15:01:12 +01:00
|
|
|
if ($attempt->state != quiz_attempt::FINISHED) {
|
|
|
|
echo quiz_attempt_state_name($attempt->state);
|
2005-05-06 06:24:04 +00:00
|
|
|
} else {
|
2011-05-12 00:30:25 +01:00
|
|
|
echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' .
|
|
|
|
quiz_format_grade($quiz, $quiz->sumgrades);
|
2005-05-06 06:24:04 +00:00
|
|
|
}
|
|
|
|
echo ' - '.userdate($attempt->timemodified).'<br />';
|
|
|
|
}
|
|
|
|
} else {
|
2011-05-12 00:30:25 +01:00
|
|
|
print_string('noattempts', 'quiz');
|
2005-05-06 06:24:04 +00:00
|
|
|
}
|
|
|
|
|
2002-10-04 02:59:05 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
2011-12-07 16:29:49 +00:00
|
|
|
* Quiz periodic clean-up tasks.
|
2009-05-28 03:51:39 +00:00
|
|
|
*/
|
2008-12-10 02:11:42 +00:00
|
|
|
function quiz_cron() {
|
2012-04-24 15:01:12 +01:00
|
|
|
global $CFG;
|
2012-06-08 15:49:19 +01:00
|
|
|
mtrace('');
|
2011-12-07 16:29:49 +00:00
|
|
|
|
2012-04-25 12:25:06 +01:00
|
|
|
// Since the quiz specifies $module->cron = 60, so that the subplugins can
|
|
|
|
// have frequent cron if they need it, we now need to do our own scheduling.
|
|
|
|
$quizconfig = get_config('quiz');
|
|
|
|
if (!isset($quizconfig->overduelastrun)) {
|
|
|
|
$quizconfig->overduelastrun = 0;
|
|
|
|
$quizconfig->overduedoneto = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$timenow = time();
|
|
|
|
if ($timenow > $quizconfig->overduelastrun + 3600) {
|
|
|
|
require_once($CFG->dirroot . '/mod/quiz/cronlib.php');
|
|
|
|
$overduehander = new mod_quiz_overdue_attempt_updater();
|
|
|
|
|
|
|
|
$processto = $timenow - $quizconfig->graceperiodmin;
|
|
|
|
|
2012-06-08 15:49:19 +01:00
|
|
|
mtrace(' Looking for quiz overdue quiz attempts between ' .
|
|
|
|
userdate($quizconfig->overduedoneto) . ' and ' . userdate($processto) . '...');
|
|
|
|
|
|
|
|
list($count, $quizcount) = $overduehander->update_overdue_attempts($timenow, $quizconfig->overduedoneto, $processto);
|
2012-04-25 12:25:06 +01:00
|
|
|
set_config('overduelastrun', $timenow, 'quiz');
|
|
|
|
set_config('overduedoneto', $processto, 'quiz');
|
2012-06-08 15:49:19 +01:00
|
|
|
|
|
|
|
mtrace(' Considered ' . $count . ' attempts in ' . $quizcount . ' quizzes.');
|
2012-04-25 12:25:06 +01:00
|
|
|
}
|
|
|
|
|
2011-12-07 16:29:49 +00:00
|
|
|
// Run cron for our sub-plugin types.
|
|
|
|
cron_execute_plugin_type('quiz', 'quiz reports');
|
|
|
|
cron_execute_plugin_type('quizaccess', 'quiz access rules');
|
2012-03-29 15:45:45 +02:00
|
|
|
|
|
|
|
return true;
|
2002-10-04 02:59:05 +00:00
|
|
|
}
|
|
|
|
|
2007-09-21 11:05:04 +00:00
|
|
|
/**
|
2011-02-23 16:25:25 +00:00
|
|
|
* @param int $quizid the quiz id.
|
|
|
|
* @param int $userid the userid.
|
2007-09-21 11:05:04 +00:00
|
|
|
* @param string $status 'all', 'finished' or 'unfinished' to control
|
2009-05-28 03:51:39 +00:00
|
|
|
* @param bool $includepreviews
|
2011-05-12 00:30:25 +01:00
|
|
|
* @return an array of all the user's attempts at this quiz. Returns an empty
|
|
|
|
* array if there are none.
|
2007-09-21 11:05:04 +00:00
|
|
|
*/
|
2011-02-03 18:06:10 +00:00
|
|
|
function quiz_get_user_attempts($quizid, $userid, $status = 'finished', $includepreviews = false) {
|
2012-05-17 10:58:29 +01:00
|
|
|
global $DB, $CFG;
|
|
|
|
// TODO MDL-33071 it is very annoying to have to included all of locallib.php
|
|
|
|
// just to get the quiz_attempt::FINISHED constants, but I will try to sort
|
|
|
|
// that out properly for Moodle 2.4. For now, I will just do a quick fix for
|
|
|
|
// MDL-33048.
|
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
MDL-3030 quiz overdue handling: trigger automatic state transitions.
Here, we catch all the places where a student might be accessing their
own attempts, and make sure any automatic state transitions that
should happen, do happen, before the student sees the attempt.
The places where we need to check this are view.php, startattempt.php
and processattempt.php.
We do not really need to check attempt.php or summary.php, because if
the student is on one of those pages, the JavaScript timer will
auto-submit when time expires, taking them to processattempt.php,
which will do the acutal work.
We intentionally do not trigger state transition when a teacher is
looking at a student's quiz attemp. We will trigger state transitions
on cron, but that is still to do.
Also, the body of the process_... methods still needs to be written.
2012-04-18 19:18:55 +01:00
|
|
|
|
|
|
|
$params = array();
|
|
|
|
switch ($status) {
|
|
|
|
case 'all':
|
|
|
|
$statuscondition = '';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'finished':
|
|
|
|
$statuscondition = ' AND state IN (:state1, :state2)';
|
|
|
|
$params['state1'] = quiz_attempt::FINISHED;
|
|
|
|
$params['state2'] = quiz_attempt::ABANDONED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'unfinished':
|
|
|
|
$statuscondition = ' AND state IN (:state1, :state2)';
|
|
|
|
$params['state1'] = quiz_attempt::IN_PROGRESS;
|
|
|
|
$params['state2'] = quiz_attempt::OVERDUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-09-21 11:05:04 +00:00
|
|
|
$previewclause = '';
|
|
|
|
if (!$includepreviews) {
|
|
|
|
$previewclause = ' AND preview = 0';
|
|
|
|
}
|
MDL-3030 quiz overdue handling: trigger automatic state transitions.
Here, we catch all the places where a student might be accessing their
own attempts, and make sure any automatic state transitions that
should happen, do happen, before the student sees the attempt.
The places where we need to check this are view.php, startattempt.php
and processattempt.php.
We do not really need to check attempt.php or summary.php, because if
the student is on one of those pages, the JavaScript timer will
auto-submit when time expires, taking them to processattempt.php,
which will do the acutal work.
We intentionally do not trigger state transition when a teacher is
looking at a student's quiz attemp. We will trigger state transitions
on cron, but that is still to do.
Also, the body of the process_... methods still needs to be written.
2012-04-18 19:18:55 +01:00
|
|
|
|
|
|
|
$params['quizid'] = $quizid;
|
|
|
|
$params['userid'] = $userid;
|
2011-02-03 18:06:10 +00:00
|
|
|
return $DB->get_records_select('quiz_attempts',
|
MDL-3030 quiz overdue handling: trigger automatic state transitions.
Here, we catch all the places where a student might be accessing their
own attempts, and make sure any automatic state transitions that
should happen, do happen, before the student sees the attempt.
The places where we need to check this are view.php, startattempt.php
and processattempt.php.
We do not really need to check attempt.php or summary.php, because if
the student is on one of those pages, the JavaScript timer will
auto-submit when time expires, taking them to processattempt.php,
which will do the acutal work.
We intentionally do not trigger state transition when a teacher is
looking at a student's quiz attemp. We will trigger state transitions
on cron, but that is still to do.
Also, the body of the process_... methods still needs to be written.
2012-04-18 19:18:55 +01:00
|
|
|
'quiz = :quizid AND userid = :userid' . $previewclause . $statuscondition,
|
|
|
|
$params, 'attempt ASC');
|
2007-09-21 11:05:04 +00:00
|
|
|
}
|
2002-10-17 07:55:54 +00:00
|
|
|
|
2007-06-10 22:52:41 +00:00
|
|
|
/**
|
|
|
|
* Return grade for given user or all users.
|
|
|
|
*
|
|
|
|
* @param int $quizid id of quiz
|
|
|
|
* @param int $userid optional user id, 0 means all users
|
2008-08-15 06:42:38 +00:00
|
|
|
* @return array array of grades, false if none. These are raw grades. They should
|
|
|
|
* be processed with quiz_format_grade for display.
|
2007-06-10 22:52:41 +00:00
|
|
|
*/
|
2011-02-03 18:06:10 +00:00
|
|
|
function quiz_get_user_grades($quiz, $userid = 0) {
|
2008-06-09 10:00:35 +00:00
|
|
|
global $CFG, $DB;
|
2007-06-10 22:52:41 +00:00
|
|
|
|
2008-06-09 10:00:35 +00:00
|
|
|
$params = array($quiz->id);
|
2011-02-03 18:06:10 +00:00
|
|
|
$usertest = '';
|
2008-06-09 10:00:35 +00:00
|
|
|
if ($userid) {
|
|
|
|
$params[] = $userid;
|
2011-02-03 18:06:10 +00:00
|
|
|
$usertest = 'AND u.id = ?';
|
|
|
|
}
|
2011-02-08 15:02:23 +00:00
|
|
|
return $DB->get_records_sql("
|
2011-02-03 18:06:10 +00:00
|
|
|
SELECT
|
|
|
|
u.id,
|
|
|
|
u.id AS userid,
|
|
|
|
qg.grade AS rawgrade,
|
|
|
|
qg.timemodified AS dategraded,
|
|
|
|
MAX(qa.timefinish) AS datesubmitted
|
|
|
|
|
|
|
|
FROM {user} u
|
|
|
|
JOIN {quiz_grades} qg ON u.id = qg.userid
|
|
|
|
JOIN {quiz_attempts} qa ON qa.quiz = qg.quiz AND qa.userid = u.id
|
|
|
|
|
2011-02-08 15:02:23 +00:00
|
|
|
WHERE qg.quiz = ?
|
|
|
|
$usertest
|
|
|
|
GROUP BY u.id, qg.grade, qg.timemodified", $params);
|
2007-06-10 22:52:41 +00:00
|
|
|
}
|
|
|
|
|
2008-08-15 06:42:38 +00:00
|
|
|
/**
|
|
|
|
* Round a grade to to the correct number of decimal places, and format it for display.
|
|
|
|
*
|
|
|
|
* @param object $quiz The quiz table row, only $quiz->decimalpoints is used.
|
|
|
|
* @param float $grade The grade to round.
|
2009-05-28 03:51:39 +00:00
|
|
|
* @return float
|
2008-08-15 06:42:38 +00:00
|
|
|
*/
|
|
|
|
function quiz_format_grade($quiz, $grade) {
|
2011-02-03 18:06:10 +00:00
|
|
|
if (is_null($grade)) {
|
|
|
|
return get_string('notyetgraded', 'quiz');
|
|
|
|
}
|
2008-08-15 06:42:38 +00:00
|
|
|
return format_float($grade, $quiz->decimalpoints);
|
|
|
|
}
|
|
|
|
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
/**
|
|
|
|
* Round a grade to to the correct number of decimal places, and format it for display.
|
|
|
|
*
|
|
|
|
* @param object $quiz The quiz table row, only $quiz->decimalpoints is used.
|
|
|
|
* @param float $grade The grade to round.
|
2009-05-28 03:51:39 +00:00
|
|
|
* @return float
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
*/
|
|
|
|
function quiz_format_question_grade($quiz, $grade) {
|
2011-02-03 18:06:10 +00:00
|
|
|
if (empty($quiz->questiondecimalpoints)) {
|
|
|
|
$quiz->questiondecimalpoints = -1;
|
|
|
|
}
|
quiz settings: MDL-18485 Improve quiz settings form
* Reorder form fields to group things more logically.
** and on the corresponding admin page too.
* Set some options to be 'Advanced' by default:
** Apply penalties.
** Each attempt builds on the last.
** Decimal places for question grades.
** The five 'Extra restrictions on attempts' settings. (password, etc.)
* Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz.
* These new defaults are applied if the admin had not previously set any fields to be advanced.
* Disable some filds when they are not applicable:
** Grading method, if num attempts = 1
** Penaly scheme, if adaptive mode = no
** Each attempt builds of last, if num attempts = 1
** Review after quiz closed options, if no close date.
** Delay between 1st and 2nd attempts, if num attempts = 1
** Delay between later attempts, if num attempts < 3
* Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500).
** Including ensuring that backup and restore is backwards compatible.
* MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades.
** There is a 'Same as overall decimal points' option, which is the default.
* Improve some field labels.
* Make corresponding changes in the help files.
2009-03-10 08:39:51 +00:00
|
|
|
if ($quiz->questiondecimalpoints == -1) {
|
|
|
|
return format_float($grade, $quiz->decimalpoints);
|
|
|
|
} else {
|
|
|
|
return format_float($grade, $quiz->questiondecimalpoints);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-10 22:52:41 +00:00
|
|
|
/**
|
|
|
|
* Update grades in central gradebook
|
|
|
|
*
|
2012-01-06 11:52:46 +07:00
|
|
|
* @category grade
|
2011-02-03 18:06:10 +00:00
|
|
|
* @param object $quiz the quiz settings.
|
|
|
|
* @param int $userid specific user only, 0 means all users.
|
2012-01-06 11:52:46 +07:00
|
|
|
* @param bool $nullifnone If a single user is specified and $nullifnone is true a grade item with a null rawgrade will be inserted
|
2007-06-10 22:52:41 +00:00
|
|
|
*/
|
2011-02-03 18:06:10 +00:00
|
|
|
function quiz_update_grades($quiz, $userid = 0, $nullifnone = true) {
|
2008-06-09 10:00:35 +00:00
|
|
|
global $CFG, $DB;
|
2008-08-16 12:16:01 +00:00
|
|
|
require_once($CFG->libdir.'/gradelib.php');
|
2003-09-10 05:02:39 +00:00
|
|
|
|
2008-08-16 12:16:01 +00:00
|
|
|
if ($quiz->grade == 0) {
|
|
|
|
quiz_grade_item_update($quiz);
|
2007-06-10 22:52:41 +00:00
|
|
|
|
2008-08-16 12:16:01 +00:00
|
|
|
} else if ($grades = quiz_get_user_grades($quiz, $userid)) {
|
|
|
|
quiz_grade_item_update($quiz, $grades);
|
2007-12-25 20:51:23 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
} else if ($userid && $nullifnone) {
|
2010-09-21 08:37:36 +00:00
|
|
|
$grade = new stdClass();
|
2011-02-03 18:06:10 +00:00
|
|
|
$grade->userid = $userid;
|
|
|
|
$grade->rawgrade = null;
|
2008-08-16 12:16:01 +00:00
|
|
|
quiz_grade_item_update($quiz, $grade);
|
2007-06-10 22:52:41 +00:00
|
|
|
|
|
|
|
} else {
|
2008-08-16 12:16:01 +00:00
|
|
|
quiz_grade_item_update($quiz);
|
|
|
|
}
|
|
|
|
}
|
2009-08-10 05:00:41 +00:00
|
|
|
|
2008-08-16 12:16:01 +00:00
|
|
|
/**
|
|
|
|
* Update all grades in gradebook.
|
|
|
|
*/
|
|
|
|
function quiz_upgrade_grades() {
|
|
|
|
global $DB;
|
|
|
|
|
|
|
|
$sql = "SELECT COUNT('x')
|
|
|
|
FROM {quiz} a, {course_modules} cm, {modules} m
|
|
|
|
WHERE m.name='quiz' AND m.id=cm.module AND cm.instance=a.id";
|
|
|
|
$count = $DB->count_records_sql($sql);
|
|
|
|
|
|
|
|
$sql = "SELECT a.*, cm.idnumber AS cmidnumber, a.course AS courseid
|
|
|
|
FROM {quiz} a, {course_modules} cm, {modules} m
|
|
|
|
WHERE m.name='quiz' AND m.id=cm.module AND cm.instance=a.id";
|
2011-01-12 15:35:33 +01:00
|
|
|
$rs = $DB->get_recordset_sql($sql);
|
|
|
|
if ($rs->valid()) {
|
2008-08-16 12:16:01 +00:00
|
|
|
$pbar = new progress_bar('quizupgradegrades', 500, true);
|
|
|
|
$i=0;
|
|
|
|
foreach ($rs as $quiz) {
|
|
|
|
$i++;
|
2012-05-04 12:40:21 +01:00
|
|
|
upgrade_set_timeout(60*5); // Set up timeout, may also abort execution.
|
2008-08-16 12:16:01 +00:00
|
|
|
quiz_update_grades($quiz, 0, false);
|
|
|
|
$pbar->update($i, $count, "Updating Quiz grades ($i/$count).");
|
2007-06-10 22:52:41 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-12 15:35:33 +01:00
|
|
|
$rs->close();
|
2002-10-17 07:23:51 +00:00
|
|
|
}
|
|
|
|
|
2007-06-10 22:52:41 +00:00
|
|
|
/**
|
2012-01-06 11:52:46 +07:00
|
|
|
* Create or update the grade item for given quiz
|
2007-06-10 22:52:41 +00:00
|
|
|
*
|
2012-01-06 11:52:46 +07:00
|
|
|
* @category grade
|
2007-06-10 22:52:41 +00:00
|
|
|
* @param object $quiz object with extra cmidnumber
|
2009-05-28 03:51:39 +00:00
|
|
|
* @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
|
2007-06-10 22:52:41 +00:00
|
|
|
* @return int 0 if ok, error code otherwise
|
|
|
|
*/
|
2011-05-12 00:30:25 +01:00
|
|
|
function quiz_grade_item_update($quiz, $grades = null) {
|
2009-08-10 05:00:41 +00:00
|
|
|
global $CFG, $OUTPUT;
|
2011-08-05 08:50:08 +01:00
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
2011-02-03 18:06:10 +00:00
|
|
|
require_once($CFG->libdir.'/gradelib.php');
|
2007-06-10 22:52:41 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
if (array_key_exists('cmidnumber', $quiz)) { // May not be always present.
|
2011-02-03 18:06:10 +00:00
|
|
|
$params = array('itemname' => $quiz->name, 'idnumber' => $quiz->cmidnumber);
|
2007-06-10 22:52:41 +00:00
|
|
|
} else {
|
2011-02-03 18:06:10 +00:00
|
|
|
$params = array('itemname' => $quiz->name);
|
2007-06-10 22:52:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($quiz->grade > 0) {
|
|
|
|
$params['gradetype'] = GRADE_TYPE_VALUE;
|
|
|
|
$params['grademax'] = $quiz->grade;
|
|
|
|
$params['grademin'] = 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
$params['gradetype'] = GRADE_TYPE_NONE;
|
|
|
|
}
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// What this is trying to do:
|
2011-05-12 00:30:25 +01:00
|
|
|
// 1. If the quiz is set to not show grades while the quiz is still open,
|
|
|
|
// and is set to show grades after the quiz is closed, then create the
|
|
|
|
// grade_item with a show-after date that is the quiz close date.
|
|
|
|
// 2. If the quiz is set to not show grades at either of those times,
|
|
|
|
// create the grade_item as hidden.
|
|
|
|
// 3. If the quiz is set to show grades, create the grade_item visible.
|
2011-02-03 18:06:10 +00:00
|
|
|
$openreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
|
|
|
|
mod_quiz_display_options::LATER_WHILE_OPEN);
|
|
|
|
$closedreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
|
|
|
|
mod_quiz_display_options::AFTER_CLOSE);
|
2011-03-16 14:34:19 +00:00
|
|
|
if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
|
|
|
|
$closedreviewoptions->marks < question_display_options::MARK_AND_MAX) {
|
2007-10-04 22:57:14 +00:00
|
|
|
$params['hidden'] = 1;
|
|
|
|
|
2011-03-16 14:34:19 +00:00
|
|
|
} else if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
|
|
|
|
$closedreviewoptions->marks >= question_display_options::MARK_AND_MAX) {
|
2007-10-04 22:57:14 +00:00
|
|
|
if ($quiz->timeclose) {
|
|
|
|
$params['hidden'] = $quiz->timeclose;
|
|
|
|
} else {
|
|
|
|
$params['hidden'] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Either
|
2007-10-04 22:57:14 +00:00
|
|
|
// a) both open and closed enabled
|
2011-05-12 00:30:25 +01:00
|
|
|
// b) open enabled, closed disabled - we can not "hide after",
|
2012-05-04 12:40:21 +01:00
|
|
|
// grades are kept visible even after closing.
|
2007-10-04 22:57:14 +00:00
|
|
|
$params['hidden'] = 0;
|
|
|
|
}
|
|
|
|
|
2007-11-29 14:43:04 +00:00
|
|
|
if ($grades === 'reset') {
|
|
|
|
$params['reset'] = true;
|
2011-05-12 00:30:25 +01:00
|
|
|
$grades = null;
|
2007-11-29 14:43:04 +00:00
|
|
|
}
|
2008-06-09 10:00:35 +00:00
|
|
|
|
2008-02-21 12:22:42 +00:00
|
|
|
$gradebook_grades = grade_get_grades($quiz->course, 'mod', 'quiz', $quiz->id);
|
2008-03-19 12:21:56 +00:00
|
|
|
if (!empty($gradebook_grades->items)) {
|
|
|
|
$grade_item = $gradebook_grades->items[0];
|
|
|
|
if ($grade_item->locked) {
|
2012-05-04 12:40:21 +01:00
|
|
|
// NOTE: this is an extremely nasty hack! It is not a bug if this confirmation fails badly. --skodak.
|
2008-03-19 12:21:56 +00:00
|
|
|
$confirm_regrade = optional_param('confirm_regrade', 0, PARAM_INT);
|
|
|
|
if (!$confirm_regrade) {
|
|
|
|
$message = get_string('gradeitemislocked', 'grades');
|
2011-05-12 00:30:25 +01:00
|
|
|
$back_link = $CFG->wwwroot . '/mod/quiz/report.php?q=' . $quiz->id .
|
|
|
|
'&mode=overview';
|
2008-03-19 12:21:56 +00:00
|
|
|
$regrade_link = qualified_me() . '&confirm_regrade=1';
|
2009-08-10 05:00:41 +00:00
|
|
|
echo $OUTPUT->box_start('generalbox', 'notice');
|
2008-03-19 12:21:56 +00:00
|
|
|
echo '<p>'. $message .'</p>';
|
2009-08-20 08:45:47 +00:00
|
|
|
echo $OUTPUT->container_start('buttons');
|
2010-01-03 15:46:14 +00:00
|
|
|
echo $OUTPUT->single_button($regrade_link, get_string('regradeanyway', 'grades'));
|
|
|
|
echo $OUTPUT->single_button($back_link, get_string('cancel'));
|
2009-08-20 08:45:47 +00:00
|
|
|
echo $OUTPUT->container_end();
|
2009-08-10 05:00:41 +00:00
|
|
|
echo $OUTPUT->box_end();
|
2008-06-09 10:00:35 +00:00
|
|
|
|
2008-03-19 12:21:56 +00:00
|
|
|
return GRADE_UPDATE_ITEM_LOCKED;
|
|
|
|
}
|
2008-02-21 12:22:42 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-29 14:43:04 +00:00
|
|
|
|
2007-11-01 08:25:05 +00:00
|
|
|
return grade_update('mod/quiz', $quiz->course, 'mod', 'quiz', $quiz->id, 0, $grades, $params);
|
2007-06-10 22:52:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete grade item for given quiz
|
|
|
|
*
|
2012-01-06 11:52:46 +07:00
|
|
|
* @category grade
|
2007-06-10 22:52:41 +00:00
|
|
|
* @param object $quiz object
|
|
|
|
* @return object quiz
|
|
|
|
*/
|
|
|
|
function quiz_grade_item_delete($quiz) {
|
|
|
|
global $CFG;
|
2009-03-04 08:35:05 +00:00
|
|
|
require_once($CFG->libdir . '/gradelib.php');
|
2007-06-10 22:52:41 +00:00
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
return grade_update('mod/quiz', $quiz->course, 'mod', 'quiz', $quiz->id, 0,
|
|
|
|
null, array('deleted' => 1));
|
2007-06-10 22:52:41 +00:00
|
|
|
}
|
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
|
|
|
* This standard function will check all instances of this module
|
|
|
|
* and make sure there are up-to-date events created for each of them.
|
|
|
|
* If courseid = 0, then every quiz event in the site is checked, else
|
|
|
|
* only quiz events belonging to the course specified are checked.
|
|
|
|
* This function is used, in its new format, by restore_refresh_events()
|
|
|
|
*
|
|
|
|
* @param int $courseid
|
|
|
|
* @return bool
|
|
|
|
*/
|
2004-04-28 02:56:40 +00:00
|
|
|
function quiz_refresh_events($courseid = 0) {
|
2008-06-09 10:00:35 +00:00
|
|
|
global $DB;
|
2004-04-28 02:56:40 +00:00
|
|
|
|
|
|
|
if ($courseid == 0) {
|
2011-02-03 18:06:10 +00:00
|
|
|
if (!$quizzes = $DB->get_records('quiz')) {
|
2004-04-28 02:56:40 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
2011-02-03 18:06:10 +00:00
|
|
|
if (!$quizzes = $DB->get_records('quiz', array('course' => $courseid))) {
|
2004-04-28 02:56:40 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2004-06-02 18:03:05 +00:00
|
|
|
|
2004-04-28 02:56:40 +00:00
|
|
|
foreach ($quizzes as $quiz) {
|
2010-03-08 16:01:38 +00:00
|
|
|
quiz_update_events($quiz);
|
2004-04-28 02:56:40 +00:00
|
|
|
}
|
2010-03-08 16:01:38 +00:00
|
|
|
|
2004-04-28 02:56:40 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
/**
|
|
|
|
* Returns all quiz graded users since a given time for specified quiz
|
|
|
|
*/
|
2010-04-29 10:46:39 +00:00
|
|
|
function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
|
|
|
|
$courseid, $cmid, $userid = 0, $groupid = 0) {
|
2008-06-09 10:00:35 +00:00
|
|
|
global $CFG, $COURSE, $USER, $DB;
|
2010-04-29 10:46:39 +00:00
|
|
|
require_once('locallib.php');
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
if ($COURSE->id == $courseid) {
|
|
|
|
$course = $COURSE;
|
2005-01-13 03:04:48 +00:00
|
|
|
} else {
|
2008-06-09 10:00:35 +00:00
|
|
|
$course = $DB->get_record('course', array('id' => $courseid));
|
2005-01-13 03:04:48 +00:00
|
|
|
}
|
|
|
|
|
2012-03-18 18:37:24 +01:00
|
|
|
$modinfo = get_fast_modinfo($course);
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
$cm = $modinfo->cms[$cmid];
|
2010-04-29 10:46:39 +00:00
|
|
|
$quiz = $DB->get_record('quiz', array('id' => $cm->instance));
|
2008-06-09 10:00:35 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
if ($userid) {
|
2010-04-29 10:46:39 +00:00
|
|
|
$userselect = "AND u.id = :userid";
|
|
|
|
$params['userid'] = $userid;
|
2008-01-24 20:33:50 +00:00
|
|
|
} else {
|
2010-04-29 10:46:39 +00:00
|
|
|
$userselect = '';
|
2008-01-24 20:33:50 +00:00
|
|
|
}
|
2005-04-02 11:32:22 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
if ($groupid) {
|
2010-04-29 10:46:39 +00:00
|
|
|
$groupselect = 'AND gm.groupid = :groupid';
|
|
|
|
$groupjoin = 'JOIN {groups_members} gm ON gm.userid=u.id';
|
|
|
|
$params['groupid'] = $groupid;
|
2008-01-24 20:33:50 +00:00
|
|
|
} else {
|
2010-04-29 10:46:39 +00:00
|
|
|
$groupselect = '';
|
|
|
|
$groupjoin = '';
|
|
|
|
}
|
|
|
|
|
2010-07-26 22:20:51 +00:00
|
|
|
$params['timestart'] = $timestart;
|
|
|
|
$params['quizid'] = $quiz->id;
|
|
|
|
|
2010-04-29 10:46:39 +00:00
|
|
|
if (!$attempts = $DB->get_records_sql("
|
|
|
|
SELECT qa.*,
|
|
|
|
u.firstname, u.lastname, u.email, u.picture, u.imagealt
|
|
|
|
FROM {quiz_attempts} qa
|
|
|
|
JOIN {user} u ON u.id = qa.userid
|
|
|
|
$groupjoin
|
|
|
|
WHERE qa.timefinish > :timestart
|
|
|
|
AND qa.quiz = :quizid
|
|
|
|
AND qa.preview = 0
|
|
|
|
$userselect
|
|
|
|
$groupselect
|
|
|
|
ORDER BY qa.timefinish ASC", $params)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
|
|
|
$accessallgroups = has_capability('moodle/site:accessallgroups', $context);
|
|
|
|
$viewfullnames = has_capability('moodle/site:viewfullnames', $context);
|
2010-05-10 22:36:40 +00:00
|
|
|
$grader = has_capability('mod/quiz:viewreports', $context);
|
2008-01-24 20:33:50 +00:00
|
|
|
$groupmode = groups_get_activity_groupmode($cm, $course);
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
if (is_null($modinfo->groups)) {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Load all my groups and cache it in modinfo.
|
2011-05-12 00:30:25 +01:00
|
|
|
$modinfo->groups = groups_get_user_groups($course->id);
|
2008-01-24 20:33:50 +00:00
|
|
|
}
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2010-04-29 10:46:39 +00:00
|
|
|
$usersgroups = null;
|
2011-05-12 00:30:25 +01:00
|
|
|
$aname = format_string($cm->name, true);
|
2008-01-24 20:33:50 +00:00
|
|
|
foreach ($attempts as $attempt) {
|
|
|
|
if ($attempt->userid != $USER->id) {
|
|
|
|
if (!$grader) {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Grade permission required.
|
2008-01-24 20:33:50 +00:00
|
|
|
continue;
|
|
|
|
}
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2008-06-09 10:00:35 +00:00
|
|
|
if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
|
2010-04-29 10:46:39 +00:00
|
|
|
if (is_null($usersgroups)) {
|
|
|
|
$usersgroups = groups_get_all_groups($course->id,
|
|
|
|
$attempt->userid, $cm->groupingid);
|
|
|
|
if (is_array($usersgroups)) {
|
|
|
|
$usersgroups = array_keys($usersgroups);
|
|
|
|
} else {
|
|
|
|
$usersgroups = array();
|
|
|
|
}
|
2008-01-24 20:33:50 +00:00
|
|
|
}
|
2010-04-29 10:46:39 +00:00
|
|
|
if (!array_intersect($usersgroups, $modinfo->groups[$cm->id])) {
|
2008-01-24 20:33:50 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2010-04-29 10:46:39 +00:00
|
|
|
}
|
|
|
|
|
2011-02-09 19:07:57 +00:00
|
|
|
$options = quiz_get_review_options($quiz, $attempt, $context);
|
2008-01-24 20:33:50 +00:00
|
|
|
|
2011-02-21 18:10:19 +00:00
|
|
|
$tmpactivity = new stdClass();
|
2008-01-24 20:33:50 +00:00
|
|
|
|
2010-04-29 10:46:39 +00:00
|
|
|
$tmpactivity->type = 'quiz';
|
|
|
|
$tmpactivity->cmid = $cm->id;
|
|
|
|
$tmpactivity->name = $aname;
|
|
|
|
$tmpactivity->sectionnum = $cm->sectionnum;
|
|
|
|
$tmpactivity->timestamp = $attempt->timefinish;
|
2008-06-09 10:00:35 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
$tmpactivity->content->attemptid = $attempt->id;
|
|
|
|
$tmpactivity->content->attempt = $attempt->attempt;
|
2011-03-16 14:34:19 +00:00
|
|
|
if (quiz_has_grades($quiz) && $options->marks >= question_display_options::MARK_AND_MAX) {
|
2010-04-29 10:46:39 +00:00
|
|
|
$tmpactivity->content->sumgrades = quiz_format_grade($quiz, $attempt->sumgrades);
|
|
|
|
$tmpactivity->content->maxgrade = quiz_format_grade($quiz, $quiz->sumgrades);
|
|
|
|
} else {
|
|
|
|
$tmpactivity->content->sumgrades = null;
|
|
|
|
$tmpactivity->content->maxgrade = null;
|
|
|
|
}
|
2008-06-09 10:00:35 +00:00
|
|
|
|
2010-10-05 17:11:16 +00:00
|
|
|
$tmpactivity->user->id = $attempt->userid;
|
2010-04-29 10:46:39 +00:00
|
|
|
$tmpactivity->user->firstname = $attempt->firstname;
|
2011-02-03 18:06:10 +00:00
|
|
|
$tmpactivity->user->lastname = $attempt->lastname;
|
|
|
|
$tmpactivity->user->fullname = fullname($attempt, $viewfullnames);
|
|
|
|
$tmpactivity->user->picture = $attempt->picture;
|
|
|
|
$tmpactivity->user->imagealt = $attempt->imagealt;
|
|
|
|
$tmpactivity->user->email = $attempt->email;
|
2008-06-09 10:00:35 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
$activities[$index++] = $tmpactivity;
|
2005-01-13 03:04:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
function quiz_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
|
2009-07-02 12:07:58 +00:00
|
|
|
global $CFG, $OUTPUT;
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2010-04-29 10:46:39 +00:00
|
|
|
echo '<tr><td class="userpicture" valign="top">';
|
|
|
|
echo $OUTPUT->user_picture($activity->user, array('courseid' => $courseid));
|
|
|
|
echo '</td><td>';
|
2005-01-13 03:04:48 +00:00
|
|
|
|
|
|
|
if ($detail) {
|
2008-01-24 20:33:50 +00:00
|
|
|
$modname = $modnames[$activity->type];
|
|
|
|
echo '<div class="title">';
|
2010-04-29 10:46:39 +00:00
|
|
|
echo '<img src="' . $OUTPUT->pix_url('icon', $activity->type) . '" ' .
|
|
|
|
'class="icon" alt="' . $modname . '" />';
|
|
|
|
echo '<a href="' . $CFG->wwwroot . '/mod/quiz/view.php?id=' .
|
|
|
|
$activity->cmid . '">' . $activity->name . '</a>';
|
2008-01-24 20:33:50 +00:00
|
|
|
echo '</div>';
|
2005-01-13 03:04:48 +00:00
|
|
|
}
|
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
echo '<div class="grade">';
|
2010-04-29 10:46:39 +00:00
|
|
|
echo get_string('attempt', 'quiz', $activity->content->attempt);
|
|
|
|
if (isset($activity->content->maxgrade)) {
|
|
|
|
$grades = $activity->content->sumgrades . ' / ' . $activity->content->maxgrade;
|
|
|
|
echo ': (<a href="' . $CFG->wwwroot . '/mod/quiz/review.php?attempt=' .
|
|
|
|
$activity->content->attemptid . '">' . $grades . '</a>)';
|
|
|
|
}
|
2008-01-24 20:33:50 +00:00
|
|
|
echo '</div>';
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2008-01-24 20:33:50 +00:00
|
|
|
echo '<div class="user">';
|
2010-04-29 10:46:39 +00:00
|
|
|
echo '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $activity->user->id .
|
|
|
|
'&course=' . $courseid . '">' . $activity->user->fullname .
|
|
|
|
'</a> - ' . userdate($activity->timestamp);
|
2008-01-24 20:33:50 +00:00
|
|
|
echo '</div>';
|
2005-01-13 03:04:48 +00:00
|
|
|
|
2010-04-29 10:46:39 +00:00
|
|
|
echo '</td></tr></table>';
|
2005-01-13 03:04:48 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-05-06 06:24:04 +00:00
|
|
|
/**
|
2006-08-16 08:46:10 +00:00
|
|
|
* Pre-process the quiz options form data, making any necessary adjustments.
|
2008-09-11 10:37:50 +00:00
|
|
|
* Called by add/update instance in this file.
|
2007-09-14 19:32:50 +00:00
|
|
|
*
|
2006-08-16 08:46:10 +00:00
|
|
|
* @param object $quiz The variables set on the form.
|
|
|
|
*/
|
2011-02-03 18:06:10 +00:00
|
|
|
function quiz_process_options($quiz) {
|
|
|
|
global $CFG;
|
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
|
|
|
require_once($CFG->libdir . '/questionlib.php');
|
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
$quiz->timemodified = time();
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2007-08-13 11:19:51 +00:00
|
|
|
// Quiz name.
|
|
|
|
if (!empty($quiz->name)) {
|
|
|
|
$quiz->name = trim($quiz->name);
|
|
|
|
}
|
2006-12-19 07:03:08 +00:00
|
|
|
|
2007-04-27 08:33:11 +00:00
|
|
|
// Password field - different in form to stop browsers that remember passwords
|
|
|
|
// getting confused.
|
|
|
|
$quiz->password = $quiz->quizpassword;
|
|
|
|
unset($quiz->quizpassword);
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Quiz feedback.
|
2007-02-28 12:24:54 +00:00
|
|
|
if (isset($quiz->feedbacktext)) {
|
|
|
|
// Clean up the boundary text.
|
|
|
|
for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
|
2010-08-10 09:56:48 +00:00
|
|
|
if (empty($quiz->feedbacktext[$i]['text'])) {
|
|
|
|
$quiz->feedbacktext[$i]['text'] = '';
|
2007-02-28 12:24:54 +00:00
|
|
|
} else {
|
2010-08-10 09:56:48 +00:00
|
|
|
$quiz->feedbacktext[$i]['text'] = trim($quiz->feedbacktext[$i]['text']);
|
2007-02-28 12:24:54 +00:00
|
|
|
}
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
2007-09-14 19:32:50 +00:00
|
|
|
|
2007-02-28 12:24:54 +00:00
|
|
|
// Check the boundary value is a number or a percentage, and in range.
|
|
|
|
$i = 0;
|
|
|
|
while (!empty($quiz->feedbackboundaries[$i])) {
|
|
|
|
$boundary = trim($quiz->feedbackboundaries[$i]);
|
|
|
|
if (!is_numeric($boundary)) {
|
|
|
|
if (strlen($boundary) > 0 && $boundary[strlen($boundary) - 1] == '%') {
|
|
|
|
$boundary = trim(substr($boundary, 0, -1));
|
|
|
|
if (is_numeric($boundary)) {
|
|
|
|
$boundary = $boundary * $quiz->grade / 100.0;
|
|
|
|
} else {
|
|
|
|
return get_string('feedbackerrorboundaryformat', 'quiz', $i + 1);
|
|
|
|
}
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
|
|
|
}
|
2007-02-28 12:24:54 +00:00
|
|
|
if ($boundary <= 0 || $boundary >= $quiz->grade) {
|
|
|
|
return get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1);
|
|
|
|
}
|
|
|
|
if ($i > 0 && $boundary >= $quiz->feedbackboundaries[$i - 1]) {
|
|
|
|
return get_string('feedbackerrororder', 'quiz', $i + 1);
|
|
|
|
}
|
|
|
|
$quiz->feedbackboundaries[$i] = $boundary;
|
|
|
|
$i += 1;
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
2007-02-28 12:24:54 +00:00
|
|
|
$numboundaries = $i;
|
2007-09-14 19:32:50 +00:00
|
|
|
|
2007-02-28 12:24:54 +00:00
|
|
|
// Check there is nothing in the remaining unused fields.
|
2009-03-18 07:33:56 +00:00
|
|
|
if (!empty($quiz->feedbackboundaries)) {
|
|
|
|
for ($i = $numboundaries; $i < count($quiz->feedbackboundaries); $i += 1) {
|
2011-05-12 00:30:25 +01:00
|
|
|
if (!empty($quiz->feedbackboundaries[$i]) &&
|
|
|
|
trim($quiz->feedbackboundaries[$i]) != '') {
|
2009-03-18 07:33:56 +00:00
|
|
|
return get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1);
|
|
|
|
}
|
2007-02-28 12:24:54 +00:00
|
|
|
}
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
2007-02-28 12:24:54 +00:00
|
|
|
for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
|
2011-05-12 00:30:25 +01:00
|
|
|
if (!empty($quiz->feedbacktext[$i]['text']) &&
|
|
|
|
trim($quiz->feedbacktext[$i]['text']) != '') {
|
2007-02-28 12:24:54 +00:00
|
|
|
return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
|
|
|
|
}
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
2011-05-12 00:30:25 +01:00
|
|
|
// Needs to be bigger than $quiz->grade because of '<' test in quiz_feedback_for_grade().
|
|
|
|
$quiz->feedbackboundaries[-1] = $quiz->grade + 1;
|
2007-02-28 12:24:54 +00:00
|
|
|
$quiz->feedbackboundaries[$numboundaries] = 0;
|
|
|
|
$quiz->feedbackboundarycount = $numboundaries;
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
2006-12-19 07:03:08 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
// Combing the individual settings into the review columns.
|
|
|
|
$quiz->reviewattempt = quiz_review_option_form_to_db($quiz, 'attempt');
|
|
|
|
$quiz->reviewcorrectness = quiz_review_option_form_to_db($quiz, 'correctness');
|
|
|
|
$quiz->reviewmarks = quiz_review_option_form_to_db($quiz, 'marks');
|
|
|
|
$quiz->reviewspecificfeedback = quiz_review_option_form_to_db($quiz, 'specificfeedback');
|
|
|
|
$quiz->reviewgeneralfeedback = quiz_review_option_form_to_db($quiz, 'generalfeedback');
|
|
|
|
$quiz->reviewrightanswer = quiz_review_option_form_to_db($quiz, 'rightanswer');
|
|
|
|
$quiz->reviewoverallfeedback = quiz_review_option_form_to_db($quiz, 'overallfeedback');
|
|
|
|
$quiz->reviewattempt |= mod_quiz_display_options::DURING;
|
|
|
|
$quiz->reviewoverallfeedback &= ~mod_quiz_display_options::DURING;
|
|
|
|
}
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
/**
|
|
|
|
* Helper function for {@link quiz_process_options()}.
|
|
|
|
* @param object $fromform the sumbitted form date.
|
|
|
|
* @param string $field one of the review option field names.
|
|
|
|
*/
|
|
|
|
function quiz_review_option_form_to_db($fromform, $field) {
|
|
|
|
static $times = array(
|
|
|
|
'during' => mod_quiz_display_options::DURING,
|
|
|
|
'immediately' => mod_quiz_display_options::IMMEDIATELY_AFTER,
|
|
|
|
'open' => mod_quiz_display_options::LATER_WHILE_OPEN,
|
|
|
|
'closed' => mod_quiz_display_options::AFTER_CLOSE,
|
|
|
|
);
|
2007-07-30 10:05:11 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
$review = 0;
|
|
|
|
foreach ($times as $whenname => $when) {
|
|
|
|
$fieldname = $field . $whenname;
|
|
|
|
if (isset($fromform->$fieldname)) {
|
|
|
|
$review |= $when;
|
|
|
|
unset($fromform->$fieldname);
|
|
|
|
}
|
2006-08-11 14:59:18 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
return $review;
|
2006-08-16 08:46:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function is called at the end of quiz_add_instance
|
|
|
|
* and quiz_update_instance, to do the common processing.
|
2006-12-19 07:03:08 +00:00
|
|
|
*
|
2006-08-16 08:46:10 +00:00
|
|
|
* @param object $quiz the quiz object.
|
|
|
|
*/
|
|
|
|
function quiz_after_add_or_update($quiz) {
|
2008-06-01 21:36:11 +00:00
|
|
|
global $DB;
|
2010-08-10 09:56:48 +00:00
|
|
|
$cmid = $quiz->coursemodule;
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// We need to use context now, so we need to make sure all needed info is already in db.
|
2010-08-10 09:56:48 +00:00
|
|
|
$DB->set_field('course_modules', 'instance', $quiz->id, array('id'=>$cmid));
|
|
|
|
$context = get_context_instance(CONTEXT_MODULE, $cmid);
|
2006-08-16 08:46:10 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Save the feedback.
|
2009-03-04 08:35:05 +00:00
|
|
|
$DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
|
2006-12-19 07:03:08 +00:00
|
|
|
|
2010-08-10 09:56:48 +00:00
|
|
|
for ($i = 0; $i <= $quiz->feedbackboundarycount; $i++) {
|
2011-02-21 18:10:19 +00:00
|
|
|
$feedback = new stdClass();
|
2006-08-22 17:31:26 +00:00
|
|
|
$feedback->quizid = $quiz->id;
|
2010-08-10 09:56:48 +00:00
|
|
|
$feedback->feedbacktext = $quiz->feedbacktext[$i]['text'];
|
|
|
|
$feedback->feedbacktextformat = $quiz->feedbacktext[$i]['format'];
|
2006-08-22 17:31:26 +00:00
|
|
|
$feedback->mingrade = $quiz->feedbackboundaries[$i];
|
|
|
|
$feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
|
2010-08-10 09:56:48 +00:00
|
|
|
$feedback->id = $DB->insert_record('quiz_feedback', $feedback);
|
2011-05-12 00:30:25 +01:00
|
|
|
$feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'],
|
|
|
|
$context->id, 'mod_quiz', 'feedback', $feedback->id,
|
|
|
|
array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0),
|
|
|
|
$quiz->feedbacktext[$i]['text']);
|
|
|
|
$DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext,
|
|
|
|
array('id' => $feedback->id));
|
2006-08-22 17:31:26 +00:00
|
|
|
}
|
|
|
|
|
2011-10-05 11:47:37 +01:00
|
|
|
// Store any settings belonging to the access rules.
|
|
|
|
quiz_access_manager::save_settings($quiz);
|
|
|
|
|
2006-08-16 08:46:10 +00:00
|
|
|
// Update the events relating to this quiz.
|
2010-03-08 16:01:38 +00:00
|
|
|
quiz_update_events($quiz);
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Update related grade item.
|
2010-03-08 16:01:38 +00:00
|
|
|
quiz_grade_item_update($quiz);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function updates the events associated to the quiz.
|
|
|
|
* If $override is non-zero, then it updates only the events
|
|
|
|
* associated with the specified override.
|
|
|
|
*
|
|
|
|
* @uses QUIZ_MAX_EVENT_LENGTH
|
|
|
|
* @param object $quiz the quiz object.
|
|
|
|
* @param object optional $override limit to a specific override
|
|
|
|
*/
|
|
|
|
function quiz_update_events($quiz, $override = null) {
|
|
|
|
global $DB;
|
|
|
|
|
|
|
|
// Load the old events relating to this quiz.
|
|
|
|
$conds = array('modulename'=>'quiz',
|
|
|
|
'instance'=>$quiz->id);
|
|
|
|
if (!empty($override)) {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Only load events for this override.
|
2010-03-08 16:01:38 +00:00
|
|
|
$conds['groupid'] = isset($override->groupid)? $override->groupid : 0;
|
|
|
|
$conds['userid'] = isset($override->userid)? $override->userid : 0;
|
|
|
|
}
|
|
|
|
$oldevents = $DB->get_records('event', $conds);
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Now make a todo list of all that needs to be updated.
|
2010-03-08 16:01:38 +00:00
|
|
|
if (empty($override)) {
|
|
|
|
// We are updating the primary settings for the quiz, so we
|
2012-05-04 12:40:21 +01:00
|
|
|
// need to add all the overrides.
|
2010-03-08 16:01:38 +00:00
|
|
|
$overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id));
|
2012-05-04 12:40:21 +01:00
|
|
|
// As well as the original quiz (empty override).
|
2011-02-21 18:10:19 +00:00
|
|
|
$overrides[] = new stdClass();
|
2011-05-12 00:30:25 +01:00
|
|
|
} else {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Just do the one override.
|
2010-03-08 16:01:38 +00:00
|
|
|
$overrides = array($override);
|
2006-08-16 08:46:10 +00:00
|
|
|
}
|
|
|
|
|
2010-03-08 16:01:38 +00:00
|
|
|
foreach ($overrides as $current) {
|
|
|
|
$groupid = isset($current->groupid)? $current->groupid : 0;
|
|
|
|
$userid = isset($current->userid)? $current->userid : 0;
|
|
|
|
$timeopen = isset($current->timeopen)? $current->timeopen : $quiz->timeopen;
|
|
|
|
$timeclose = isset($current->timeclose)? $current->timeclose : $quiz->timeclose;
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Only add open/close events for an override if they differ from the quiz default.
|
2010-03-08 16:01:38 +00:00
|
|
|
$addopen = empty($current->id) || !empty($current->timeopen);
|
|
|
|
$addclose = empty($current->id) || !empty($current->timeclose);
|
|
|
|
|
2011-02-21 18:10:19 +00:00
|
|
|
$event = new stdClass();
|
2012-05-04 15:17:23 +01:00
|
|
|
$event->description = format_module_intro('quiz', $quiz, $quiz->coursemodule);
|
2012-05-04 12:40:21 +01:00
|
|
|
// Events module won't show user events when the courseid is nonzero.
|
2011-05-12 00:30:25 +01:00
|
|
|
$event->courseid = ($userid) ? 0 : $quiz->course;
|
2010-03-08 16:01:38 +00:00
|
|
|
$event->groupid = $groupid;
|
|
|
|
$event->userid = $userid;
|
|
|
|
$event->modulename = 'quiz';
|
|
|
|
$event->instance = $quiz->id;
|
|
|
|
$event->timestart = $timeopen;
|
|
|
|
$event->timeduration = max($timeclose - $timeopen, 0);
|
|
|
|
$event->visible = instance_is_visible('quiz', $quiz);
|
|
|
|
$event->eventtype = 'open';
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Determine the event name.
|
2010-03-08 16:01:38 +00:00
|
|
|
if ($groupid) {
|
2011-02-21 18:10:19 +00:00
|
|
|
$params = new stdClass();
|
2010-03-08 16:01:38 +00:00
|
|
|
$params->quiz = $quiz->name;
|
|
|
|
$params->group = groups_get_group_name($groupid);
|
|
|
|
if ($params->group === false) {
|
2012-05-04 12:40:21 +01:00
|
|
|
// Group doesn't exist, just skip it.
|
2010-03-08 16:01:38 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$eventname = get_string('overridegroupeventname', 'quiz', $params);
|
2011-05-12 00:30:25 +01:00
|
|
|
} else if ($userid) {
|
2011-02-21 18:10:19 +00:00
|
|
|
$params = new stdClass();
|
2010-03-08 16:01:38 +00:00
|
|
|
$params->quiz = $quiz->name;
|
|
|
|
$eventname = get_string('overrideusereventname', 'quiz', $params);
|
|
|
|
} else {
|
|
|
|
$eventname = $quiz->name;
|
|
|
|
}
|
|
|
|
if ($addopen or $addclose) {
|
|
|
|
if ($timeclose and $timeopen and $event->timeduration <= QUIZ_MAX_EVENT_LENGTH) {
|
|
|
|
// Single event for the whole quiz.
|
|
|
|
if ($oldevent = array_shift($oldevents)) {
|
|
|
|
$event->id = $oldevent->id;
|
2011-05-12 00:30:25 +01:00
|
|
|
} else {
|
2010-03-08 16:01:38 +00:00
|
|
|
unset($event->id);
|
|
|
|
}
|
|
|
|
$event->name = $eventname;
|
2012-05-04 12:40:21 +01:00
|
|
|
// The method calendar_event::create will reuse a db record if the id field is set.
|
2010-03-08 16:01:38 +00:00
|
|
|
calendar_event::create($event);
|
|
|
|
} else {
|
|
|
|
// Separate start and end events.
|
|
|
|
$event->timeduration = 0;
|
|
|
|
if ($timeopen && $addopen) {
|
|
|
|
if ($oldevent = array_shift($oldevents)) {
|
|
|
|
$event->id = $oldevent->id;
|
2011-05-12 00:30:25 +01:00
|
|
|
} else {
|
2010-03-08 16:01:38 +00:00
|
|
|
unset($event->id);
|
|
|
|
}
|
|
|
|
$event->name = $eventname.' ('.get_string('quizopens', 'quiz').')';
|
2012-05-04 12:40:21 +01:00
|
|
|
// The method calendar_event::create will reuse a db record if the id field is set.
|
2010-03-08 16:01:38 +00:00
|
|
|
calendar_event::create($event);
|
|
|
|
}
|
|
|
|
if ($timeclose && $addclose) {
|
|
|
|
if ($oldevent = array_shift($oldevents)) {
|
|
|
|
$event->id = $oldevent->id;
|
2011-05-12 00:30:25 +01:00
|
|
|
} else {
|
2010-03-08 16:01:38 +00:00
|
|
|
unset($event->id);
|
|
|
|
}
|
|
|
|
$event->name = $eventname.' ('.get_string('quizcloses', 'quiz').')';
|
|
|
|
$event->timestart = $timeclose;
|
|
|
|
$event->eventtype = 'close';
|
|
|
|
calendar_event::create($event);
|
|
|
|
}
|
|
|
|
}
|
2006-08-16 08:46:10 +00:00
|
|
|
}
|
|
|
|
}
|
2007-06-10 22:52:41 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Delete any leftover events.
|
2010-03-08 16:01:38 +00:00
|
|
|
foreach ($oldevents as $badevent) {
|
|
|
|
$badevent = calendar_event::load($badevent);
|
|
|
|
$badevent->delete();
|
|
|
|
}
|
2005-05-06 06:24:04 +00:00
|
|
|
}
|
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2005-09-01 04:14:31 +00:00
|
|
|
function quiz_get_view_actions() {
|
2008-12-14 13:32:45 +00:00
|
|
|
return array('view', 'view all', 'report', 'review');
|
2005-09-01 04:14:31 +00:00
|
|
|
}
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2009-05-28 03:51:39 +00:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2005-09-01 04:14:31 +00:00
|
|
|
function quiz_get_post_actions() {
|
2011-05-12 00:30:25 +01:00
|
|
|
return array('attempt', 'close attempt', 'preview', 'editquestions',
|
|
|
|
'delete attempt', 'manualgrade');
|
2005-09-01 04:14:31 +00:00
|
|
|
}
|
2005-05-06 06:24:04 +00:00
|
|
|
|
2006-03-21 09:06:34 +00:00
|
|
|
/**
|
2011-02-03 18:06:10 +00:00
|
|
|
* @param array $questionids of question ids.
|
2011-02-23 16:25:25 +00:00
|
|
|
* @return bool whether any of these questions are used by any instance of this module.
|
2006-03-21 09:06:34 +00:00
|
|
|
*/
|
2011-02-03 18:06:10 +00:00
|
|
|
function quiz_questions_in_use($questionids) {
|
2011-03-07 16:23:11 +00:00
|
|
|
global $DB, $CFG;
|
|
|
|
require_once($CFG->libdir . '/questionlib.php');
|
2011-02-03 18:06:10 +00:00
|
|
|
list($test, $params) = $DB->get_in_or_equal($questionids);
|
|
|
|
return $DB->record_exists_select('quiz_question_instances',
|
2011-03-07 16:23:11 +00:00
|
|
|
'question ' . $test, $params) || question_engine::questions_in_use(
|
2011-03-16 18:56:14 +00:00
|
|
|
$questionids, new qubaid_join('{quiz_attempts} quiza',
|
2011-03-07 16:23:11 +00:00
|
|
|
'quiza.uniqueid', 'quiza.preview = 0'));
|
2006-03-21 09:06:34 +00:00
|
|
|
}
|
|
|
|
|
2007-06-19 22:17:47 +00:00
|
|
|
/**
|
|
|
|
* Implementation of the function for printing the form elements that control
|
|
|
|
* whether the course reset functionality affects the quiz.
|
2009-08-10 05:00:41 +00:00
|
|
|
*
|
2011-02-03 18:06:10 +00:00
|
|
|
* @param $mform the course reset form that is being built.
|
2007-11-29 14:43:04 +00:00
|
|
|
*/
|
2011-02-03 18:06:10 +00:00
|
|
|
function quiz_reset_course_form_definition($mform) {
|
2009-03-23 03:57:45 +00:00
|
|
|
$mform->addElement('header', 'quizheader', get_string('modulenameplural', 'quiz'));
|
2011-05-12 00:30:25 +01:00
|
|
|
$mform->addElement('advcheckbox', 'reset_quiz_attempts',
|
|
|
|
get_string('removeallquizattempts', 'quiz'));
|
2007-11-29 14:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Course reset form defaults.
|
2011-02-03 18:06:10 +00:00
|
|
|
* @return array the defaults.
|
2007-11-29 14:43:04 +00:00
|
|
|
*/
|
|
|
|
function quiz_reset_course_form_defaults($course) {
|
2011-02-03 18:06:10 +00:00
|
|
|
return array('reset_quiz_attempts' => 1);
|
2007-11-29 14:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes all grades from gradebook
|
2009-05-28 03:51:39 +00:00
|
|
|
*
|
2007-11-29 14:43:04 +00:00
|
|
|
* @param int $courseid
|
|
|
|
* @param string optional type
|
2007-06-19 22:17:47 +00:00
|
|
|
*/
|
2007-11-29 14:43:04 +00:00
|
|
|
function quiz_reset_gradebook($courseid, $type='') {
|
2008-06-09 10:00:35 +00:00
|
|
|
global $CFG, $DB;
|
2007-11-29 14:43:04 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
$quizzes = $DB->get_records_sql("
|
|
|
|
SELECT q.*, cm.idnumber as cmidnumber, q.course as courseid
|
|
|
|
FROM {modules} m
|
|
|
|
JOIN {course_modules} cm ON m.id = cm.module
|
|
|
|
JOIN {quiz} q ON cm.instance = q.id
|
|
|
|
WHERE m.name = 'quiz' AND cm.course = ?", array($courseid));
|
2007-11-29 14:43:04 +00:00
|
|
|
|
2011-02-03 18:06:10 +00:00
|
|
|
foreach ($quizzes as $quiz) {
|
|
|
|
quiz_grade_item_update($quiz, 'reset');
|
2007-11-29 14:43:04 +00:00
|
|
|
}
|
2007-06-19 22:17:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-09-18 12:08:16 +00:00
|
|
|
* Actual implementation of the reset course functionality, delete all the
|
2007-06-19 22:17:47 +00:00
|
|
|
* quiz attempts for course $data->courseid, if $data->reset_quiz_attempts is
|
|
|
|
* set and true.
|
2007-09-19 15:38:14 +00:00
|
|
|
*
|
|
|
|
* Also, move the quiz open and close dates, if the course start date is changing.
|
2009-05-28 03:51:39 +00:00
|
|
|
*
|
|
|
|
* @param object $data the data submitted from the reset course.
|
2007-11-29 14:43:04 +00:00
|
|
|
* @return array status array
|
2007-06-19 22:17:47 +00:00
|
|
|
*/
|
2007-11-29 14:43:04 +00:00
|
|
|
function quiz_reset_userdata($data) {
|
2009-03-04 08:35:05 +00:00
|
|
|
global $CFG, $DB;
|
|
|
|
require_once($CFG->libdir.'/questionlib.php');
|
2008-12-10 02:11:42 +00:00
|
|
|
|
2007-11-29 14:43:04 +00:00
|
|
|
$componentstr = get_string('modulenameplural', 'quiz');
|
|
|
|
$status = array();
|
2007-09-14 19:32:50 +00:00
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
// Delete attempts.
|
2007-09-19 15:38:14 +00:00
|
|
|
if (!empty($data->reset_quiz_attempts)) {
|
2011-03-16 18:56:14 +00:00
|
|
|
require_once($CFG->libdir . '/questionlib.php');
|
|
|
|
|
|
|
|
question_engine::delete_questions_usage_by_activities(new qubaid_join(
|
|
|
|
'{quiz_attempts} quiza JOIN {quiz} quiz ON quiza.quiz = quiz.id',
|
|
|
|
'quiza.uniqueid', 'quiz.course = :quizcourseid',
|
|
|
|
array('quizcourseid' => $data->courseid)));
|
2011-02-03 18:06:10 +00:00
|
|
|
|
|
|
|
$DB->delete_records_select('quiz_attempts',
|
|
|
|
'quiz IN (SELECT id FROM {quiz} WHERE course = ?)', array($data->courseid));
|
|
|
|
$status[] = array(
|
|
|
|
'component' => $componentstr,
|
2011-05-12 00:30:25 +01:00
|
|
|
'item' => get_string('attemptsdeleted', 'quiz'),
|
2011-02-03 18:06:10 +00:00
|
|
|
'error' => false);
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Remove all grades from gradebook.
|
2011-10-04 22:47:32 +01:00
|
|
|
$DB->delete_records_select('quiz_grades',
|
|
|
|
'quiz IN (SELECT id FROM {quiz} WHERE course = ?)', array($data->courseid));
|
2007-11-29 14:43:04 +00:00
|
|
|
if (empty($data->reset_gradebook_grades)) {
|
|
|
|
quiz_reset_gradebook($data->courseid);
|
2007-06-19 22:17:47 +00:00
|
|
|
}
|
2011-02-03 18:06:10 +00:00
|
|
|
$status[] = array(
|
|
|
|
'component' => $componentstr,
|
2011-10-04 22:47:32 +01:00
|
|
|
'item' => get_string('gradesdeleted', 'quiz'),
|
2011-02-03 18:06:10 +00:00
|
|
|
'error' => false);
|
2007-06-19 22:17:47 +00:00
|
|
|
}
|
2007-09-19 15:38:14 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Updating dates - shift may be negative too.
|
2007-11-29 14:43:04 +00:00
|
|
|
if ($data->timeshift) {
|
2011-05-12 00:30:25 +01:00
|
|
|
shift_course_mod_dates('quiz', array('timeopen', 'timeclose'),
|
|
|
|
$data->timeshift, $data->courseid);
|
2011-02-03 18:06:10 +00:00
|
|
|
$status[] = array(
|
|
|
|
'component' => $componentstr,
|
|
|
|
'item' => get_string('openclosedatesupdated', 'quiz'),
|
|
|
|
'error' => false);
|
2007-06-19 22:17:47 +00:00
|
|
|
}
|
2007-11-29 14:43:04 +00:00
|
|
|
|
|
|
|
return $status;
|
2007-06-19 22:17:47 +00:00
|
|
|
}
|
2007-06-20 15:12:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the current user is allowed to view a file uploaded in a quiz.
|
|
|
|
* Teachers can view any from their courses, students can only view their own.
|
2007-09-14 19:32:50 +00:00
|
|
|
*
|
2008-05-21 11:10:06 +00:00
|
|
|
* @param int $attemptuniqueid int attempt id
|
2007-06-20 15:12:36 +00:00
|
|
|
* @param int $questionid int question id
|
2011-02-23 16:25:25 +00:00
|
|
|
* @return bool to indicate access granted or denied
|
2007-06-20 15:12:36 +00:00
|
|
|
*/
|
2010-08-10 09:56:48 +00:00
|
|
|
function quiz_check_file_access($attemptuniqueid, $questionid, $context = null) {
|
|
|
|
global $USER, $DB, $CFG;
|
|
|
|
require_once(dirname(__FILE__).'/attemptlib.php');
|
|
|
|
require_once(dirname(__FILE__).'/locallib.php');
|
2007-09-14 19:32:50 +00:00
|
|
|
|
2008-06-22 12:57:44 +00:00
|
|
|
$attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $attemptuniqueid));
|
2010-08-10 09:56:48 +00:00
|
|
|
$attemptobj = quiz_attempt::create($attempt->id);
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Does the question exist?
|
2010-08-10 09:56:48 +00:00
|
|
|
if (!$question = $DB->get_record('question', array('id' => $questionid))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($context === null) {
|
|
|
|
$quiz = $DB->get_record('quiz', array('id' => $attempt->quiz));
|
|
|
|
$cm = get_coursemodule_from_id('quiz', $quiz->id);
|
|
|
|
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load those questions and the associated states.
|
|
|
|
$attemptobj->load_questions(array($questionid));
|
|
|
|
$attemptobj->load_question_states(array($questionid));
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Obtain the state.
|
2010-08-10 09:56:48 +00:00
|
|
|
$state = $attemptobj->get_question_state($questionid);
|
2012-05-04 12:40:21 +01:00
|
|
|
// Obtain the question.
|
2010-08-10 09:56:48 +00:00
|
|
|
$question = $attemptobj->get_question($questionid);
|
2007-09-14 19:32:50 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Access granted if the current user submitted this file.
|
2010-08-10 09:56:48 +00:00
|
|
|
if ($attempt->userid != $USER->id) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-05-04 12:40:21 +01:00
|
|
|
// Access granted if the current user has permission to grade quizzes in this course.
|
2011-05-12 00:30:25 +01:00
|
|
|
if (!(has_capability('mod/quiz:viewreports', $context) ||
|
|
|
|
has_capability('mod/quiz:grade', $context))) {
|
2010-08-10 09:56:48 +00:00
|
|
|
return false;
|
2007-06-20 15:12:36 +00:00
|
|
|
}
|
2007-09-14 19:32:50 +00:00
|
|
|
|
2010-08-10 09:56:48 +00:00
|
|
|
return array($question, $state, array());
|
2007-06-20 15:12:36 +00:00
|
|
|
}
|
2007-09-21 11:05:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints quiz summaries on MyMoodle Page
|
2009-05-28 03:51:39 +00:00
|
|
|
* @param arry $courses
|
|
|
|
* @param array $htmlarray
|
2007-09-21 11:05:04 +00:00
|
|
|
*/
|
|
|
|
function quiz_print_overview($courses, &$htmlarray) {
|
|
|
|
global $USER, $CFG;
|
2012-05-04 12:40:21 +01:00
|
|
|
// These next 6 Lines are constant in all modules (just change module name).
|
2007-09-21 11:05:04 +00:00
|
|
|
if (empty($courses) || !is_array($courses) || count($courses) == 0) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:23:55 +00:00
|
|
|
if (!$quizzes = get_all_instances_in_courses('quiz', $courses)) {
|
2007-09-21 11:05:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
// Fetch some language strings outside the main loop.
|
2007-09-21 11:05:04 +00:00
|
|
|
$strquiz = get_string('modulename', 'quiz');
|
|
|
|
$strnoattempts = get_string('noattempts', 'quiz');
|
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
// We want to list quizzes that are currently available, and which have a close date.
|
|
|
|
// This is the same as what the lesson does, and the dabate is in MDL-10568.
|
2007-10-30 19:29:45 +00:00
|
|
|
$now = time();
|
2007-12-12 17:23:55 +00:00
|
|
|
foreach ($quizzes as $quiz) {
|
2007-09-21 11:05:04 +00:00
|
|
|
if ($quiz->timeclose >= $now && $quiz->timeopen < $now) {
|
2011-05-12 00:30:25 +01:00
|
|
|
// Give a link to the quiz, and the deadline.
|
2007-09-21 11:05:04 +00:00
|
|
|
$str = '<div class="quiz overview">' .
|
2011-05-12 00:30:25 +01:00
|
|
|
'<div class="name">' . $strquiz . ': <a ' .
|
|
|
|
($quiz->visible ? '' : ' class="dimmed"') .
|
|
|
|
' href="' . $CFG->wwwroot . '/mod/quiz/view.php?id=' .
|
|
|
|
$quiz->coursemodule . '">' .
|
2007-09-21 11:05:04 +00:00
|
|
|
$quiz->name . '</a></div>';
|
2011-05-12 00:30:25 +01:00
|
|
|
$str .= '<div class="info">' . get_string('quizcloseson', 'quiz',
|
|
|
|
userdate($quiz->timeclose)) . '</div>';
|
2007-09-21 11:05:04 +00:00
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
// Now provide more information depending on the uers's role.
|
2007-09-21 11:05:04 +00:00
|
|
|
$context = get_context_instance(CONTEXT_MODULE, $quiz->coursemodule);
|
|
|
|
if (has_capability('mod/quiz:viewreports', $context)) {
|
2011-05-12 00:30:25 +01:00
|
|
|
// For teacher-like people, show a summary of the number of student attempts.
|
2008-06-09 10:00:35 +00:00
|
|
|
// The $quiz objects returned by get_all_instances_in_course have the necessary $cm
|
2007-12-12 17:23:55 +00:00
|
|
|
// fields set to make the following call work.
|
2011-05-12 00:30:25 +01:00
|
|
|
$str .= '<div class="info">' .
|
|
|
|
quiz_num_attempt_summary($quiz, $quiz, true) . '</div>';
|
|
|
|
} else if (has_any_capability(array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
|
|
|
|
$context)) { // Student
|
|
|
|
// For student-like people, tell them how many attempts they have made.
|
|
|
|
if (isset($USER->id) &&
|
|
|
|
($attempts = quiz_get_user_attempts($quiz->id, $USER->id))) {
|
2007-09-21 11:05:04 +00:00
|
|
|
$numattempts = count($attempts);
|
2011-05-12 00:30:25 +01:00
|
|
|
$str .= '<div class="info">' .
|
|
|
|
get_string('numattemptsmade', 'quiz', $numattempts) . '</div>';
|
2007-09-21 11:05:04 +00:00
|
|
|
} else {
|
|
|
|
$str .= '<div class="info">' . $strnoattempts . '</div>';
|
|
|
|
}
|
|
|
|
} else {
|
2011-05-12 00:30:25 +01:00
|
|
|
// For ayone else, there is no point listing this quiz, so stop processing.
|
2007-09-21 11:05:04 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
// Add the output for this quiz to the rest.
|
2007-09-21 11:05:04 +00:00
|
|
|
$str .= '</div>';
|
|
|
|
if (empty($htmlarray[$quiz->course]['quiz'])) {
|
|
|
|
$htmlarray[$quiz->course]['quiz'] = $str;
|
|
|
|
} else {
|
|
|
|
$htmlarray[$quiz->course]['quiz'] .= $str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-10-30 19:29:45 +00:00
|
|
|
|
|
|
|
/**
|
2011-02-03 18:06:10 +00:00
|
|
|
* Return a textual summary of the number of attempts that have been made at a particular quiz,
|
2011-05-09 17:47:59 +01:00
|
|
|
* returns '' if no attempts have been made yet, unless $returnzero is passed as true.
|
2009-05-28 03:51:39 +00:00
|
|
|
*
|
2007-10-30 19:29:45 +00:00
|
|
|
* @param object $quiz the quiz object. Only $quiz->id is used at the moment.
|
2011-05-12 00:30:25 +01:00
|
|
|
* @param object $cm the cm object. Only $cm->course, $cm->groupmode and
|
|
|
|
* $cm->groupingid fields are used at the moment.
|
|
|
|
* @param bool $returnzero if false (default), when no attempts have been
|
|
|
|
* made '' is returned instead of 'Attempts: 0'.
|
2007-12-12 17:23:55 +00:00
|
|
|
* @param int $currentgroup if there is a concept of current group where this method is being called
|
|
|
|
* (e.g. a report) pass it in here. Default 0 which means no current group.
|
|
|
|
* @return string a string like "Attempts: 123", "Attemtps 123 (45 from your groups)" or
|
|
|
|
* "Attemtps 123 (45 from this group)".
|
2007-10-30 19:29:45 +00:00
|
|
|
*/
|
2007-12-12 17:23:55 +00:00
|
|
|
function quiz_num_attempt_summary($quiz, $cm, $returnzero = false, $currentgroup = 0) {
|
2010-07-28 15:08:34 +00:00
|
|
|
global $DB, $USER;
|
2008-06-09 10:00:35 +00:00
|
|
|
$numattempts = $DB->count_records('quiz_attempts', array('quiz'=> $quiz->id, 'preview'=>0));
|
2007-10-30 19:29:45 +00:00
|
|
|
if ($numattempts || $returnzero) {
|
2007-12-12 17:23:55 +00:00
|
|
|
if (groups_get_activity_groupmode($cm)) {
|
2012-01-15 21:49:30 +01:00
|
|
|
$a = new stdClass();
|
2007-12-12 17:23:55 +00:00
|
|
|
$a->total = $numattempts;
|
|
|
|
if ($currentgroup) {
|
2011-12-09 12:40:42 +00:00
|
|
|
$a->group = $DB->count_records_sql('SELECT COUNT(DISTINCT qa.id) FROM ' .
|
2008-06-09 10:00:35 +00:00
|
|
|
'{quiz_attempts} qa JOIN ' .
|
|
|
|
'{groups_members} gm ON qa.userid = gm.userid ' .
|
2011-05-12 00:30:25 +01:00
|
|
|
'WHERE quiz = ? AND preview = 0 AND groupid = ?',
|
|
|
|
array($quiz->id, $currentgroup));
|
2007-12-12 17:23:55 +00:00
|
|
|
return get_string('attemptsnumthisgroup', 'quiz', $a);
|
2008-06-09 10:00:35 +00:00
|
|
|
} else if ($groups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid)) {
|
|
|
|
list($usql, $params) = $DB->get_in_or_equal(array_keys($groups));
|
2011-12-09 12:40:42 +00:00
|
|
|
$a->group = $DB->count_records_sql('SELECT COUNT(DISTINCT qa.id) FROM ' .
|
2008-06-09 10:00:35 +00:00
|
|
|
'{quiz_attempts} qa JOIN ' .
|
|
|
|
'{groups_members} gm ON qa.userid = gm.userid ' .
|
|
|
|
'WHERE quiz = ? AND preview = 0 AND ' .
|
|
|
|
"groupid $usql", array_merge(array($quiz->id), $params));
|
2007-12-12 17:23:55 +00:00
|
|
|
return get_string('attemptsnumyourgroups', 'quiz', $a);
|
|
|
|
}
|
|
|
|
}
|
2007-10-30 19:29:45 +00:00
|
|
|
return get_string('attemptsnum', 'quiz', $numattempts);
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
}
|
2008-07-24 21:59:13 +00:00
|
|
|
|
2008-07-28 12:31:29 +00:00
|
|
|
/**
|
2010-07-28 15:08:34 +00:00
|
|
|
* Returns the same as {@link quiz_num_attempt_summary()} but wrapped in a link
|
|
|
|
* to the quiz reports.
|
|
|
|
*
|
|
|
|
* @param object $quiz the quiz object. Only $quiz->id is used at the moment.
|
2011-05-12 00:30:25 +01:00
|
|
|
* @param object $cm the cm object. Only $cm->course, $cm->groupmode and
|
|
|
|
* $cm->groupingid fields are used at the moment.
|
2010-07-28 15:08:34 +00:00
|
|
|
* @param object $context the quiz context.
|
2011-05-12 00:30:25 +01:00
|
|
|
* @param bool $returnzero if false (default), when no attempts have been made
|
|
|
|
* '' is returned instead of 'Attempts: 0'.
|
2010-07-28 15:08:34 +00:00
|
|
|
* @param int $currentgroup if there is a concept of current group where this method is being called
|
|
|
|
* (e.g. a report) pass it in here. Default 0 which means no current group.
|
|
|
|
* @return string HTML fragment for the link.
|
|
|
|
*/
|
2011-05-12 00:30:25 +01:00
|
|
|
function quiz_attempt_summary_link_to_reports($quiz, $cm, $context, $returnzero = false,
|
|
|
|
$currentgroup = 0) {
|
2010-07-28 15:08:34 +00:00
|
|
|
global $CFG;
|
|
|
|
$summary = quiz_num_attempt_summary($quiz, $cm, $returnzero, $currentgroup);
|
|
|
|
if (!$summary) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
|
|
|
|
$url = new moodle_url('/mod/quiz/report.php', array(
|
|
|
|
'id' => $cm->id, 'mode' => quiz_report_default_report($context)));
|
|
|
|
return html_writer::link($url, $summary);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-07-28 12:31:29 +00:00
|
|
|
* @param string $feature FEATURE_xx constant for requested feature
|
|
|
|
* @return bool True if quiz supports feature
|
|
|
|
*/
|
|
|
|
function quiz_supports($feature) {
|
|
|
|
switch($feature) {
|
2012-07-02 12:49:57 +08:00
|
|
|
case FEATURE_GROUPS: return true;
|
|
|
|
case FEATURE_GROUPINGS: return true;
|
|
|
|
case FEATURE_GROUPMEMBERSONLY: return true;
|
|
|
|
case FEATURE_MOD_INTRO: return true;
|
|
|
|
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
|
|
|
|
case FEATURE_GRADE_HAS_GRADE: return true;
|
|
|
|
case FEATURE_GRADE_OUTCOMES: return false;
|
|
|
|
case FEATURE_BACKUP_MOODLE2: return true;
|
|
|
|
case FEATURE_SHOW_DESCRIPTION: return true;
|
|
|
|
case FEATURE_CONTROLS_GRADE_VISIBILITY: return true;
|
2009-04-21 08:57:20 +00:00
|
|
|
|
2008-07-28 15:58:50 +00:00
|
|
|
default: return null;
|
2008-07-28 12:31:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-24 21:59:13 +00:00
|
|
|
/**
|
2008-09-10 05:28:25 +00:00
|
|
|
* @return array all other caps used in module
|
2008-07-24 21:59:13 +00:00
|
|
|
*/
|
|
|
|
function quiz_get_extra_capabilities() {
|
2010-12-02 16:51:52 +00:00
|
|
|
global $CFG;
|
2008-12-10 02:11:42 +00:00
|
|
|
require_once($CFG->libdir.'/questionlib.php');
|
2008-09-10 05:28:25 +00:00
|
|
|
$caps = question_get_all_capabilities();
|
|
|
|
$caps[] = 'moodle/site:accessallgroups';
|
|
|
|
return $caps;
|
2008-07-24 21:59:13 +00:00
|
|
|
}
|
2009-09-16 05:15:22 +00:00
|
|
|
|
|
|
|
/**
|
2010-07-22 09:43:30 +00:00
|
|
|
* This fucntion extends the global navigation for the site.
|
2009-09-16 05:15:22 +00:00
|
|
|
* It is important to note that you should not rely on PAGE objects within this
|
|
|
|
* body of code as there is no guarantee that during an AJAX request they are
|
|
|
|
* available
|
|
|
|
*
|
2010-05-21 03:15:48 +00:00
|
|
|
* @param navigation_node $quiznode The quiz node within the global navigation
|
2011-02-21 18:10:19 +00:00
|
|
|
* @param object $course The course object returned from the DB
|
|
|
|
* @param object $module The module object returned from the DB
|
|
|
|
* @param object $cm The course module instance returned from the DB
|
2009-09-16 05:15:22 +00:00
|
|
|
*/
|
2010-05-21 03:15:48 +00:00
|
|
|
function quiz_extend_navigation($quiznode, $course, $module, $cm) {
|
|
|
|
global $CFG;
|
|
|
|
|
|
|
|
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
|
|
|
|
|
|
|
if (has_capability('mod/quiz:view', $context)) {
|
|
|
|
$url = new moodle_url('/mod/quiz/view.php', array('id'=>$cm->id));
|
2010-07-28 15:08:34 +00:00
|
|
|
$quiznode->add(get_string('info', 'quiz'), $url, navigation_node::TYPE_SETTING,
|
|
|
|
null, null, new pix_icon('i/info', ''));
|
2010-05-21 03:15:48 +00:00
|
|
|
}
|
|
|
|
|
2011-05-20 18:25:07 +01:00
|
|
|
if (has_any_capability(array('mod/quiz:viewreports', 'mod/quiz:grade'), $context)) {
|
2010-05-21 03:15:48 +00:00
|
|
|
require_once($CFG->dirroot.'/mod/quiz/report/reportlib.php');
|
|
|
|
$reportlist = quiz_report_list($context);
|
2010-07-28 15:08:34 +00:00
|
|
|
|
2011-05-12 00:30:25 +01:00
|
|
|
$url = new moodle_url('/mod/quiz/report.php',
|
|
|
|
array('id' => $cm->id, 'mode' => reset($reportlist)));
|
|
|
|
$reportnode = $quiznode->add(get_string('results', 'quiz'), $url,
|
|
|
|
navigation_node::TYPE_SETTING,
|
2010-07-28 15:08:34 +00:00
|
|
|
null, null, new pix_icon('i/report', ''));
|
|
|
|
|
2010-05-21 03:15:48 +00:00
|
|
|
foreach ($reportlist as $report) {
|
2011-05-12 00:30:25 +01:00
|
|
|
$url = new moodle_url('/mod/quiz/report.php',
|
|
|
|
array('id' => $cm->id, 'mode' => $report));
|
|
|
|
$reportnode->add(get_string($report, 'quiz_'.$report), $url,
|
|
|
|
navigation_node::TYPE_SETTING,
|
2010-08-03 11:10:38 +00:00
|
|
|
null, 'quiz_report_' . $report, new pix_icon('i/item', ''));
|
2010-05-21 03:15:48 +00:00
|
|
|
}
|
|
|
|
}
|
2009-09-16 05:15:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function extends the settings navigation block for the site.
|
|
|
|
*
|
|
|
|
* It is safe to rely on PAGE here as we will only ever be within the module
|
|
|
|
* context when this is called
|
|
|
|
*
|
2010-03-22 03:04:00 +00:00
|
|
|
* @param settings_navigation $settings
|
|
|
|
* @param navigation_node $quiznode
|
2009-09-16 05:15:22 +00:00
|
|
|
*/
|
2010-03-22 03:04:00 +00:00
|
|
|
function quiz_extend_settings_navigation($settings, $quiznode) {
|
|
|
|
global $PAGE, $CFG;
|
2009-09-16 05:15:22 +00:00
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// Require {@link questionlib.php}
|
|
|
|
// Included here as we only ever want to include this file if we really need to.
|
2010-05-21 03:15:48 +00:00
|
|
|
require_once($CFG->libdir . '/questionlib.php');
|
2009-09-16 05:15:22 +00:00
|
|
|
|
2011-06-18 16:27:21 +01:00
|
|
|
// We want to add these new nodes after the Edit settings node, and before the
|
|
|
|
// Locally assigned roles node. Of course, both of those are controlled by capabilities.
|
|
|
|
$keys = $quiznode->get_children_key_list();
|
2011-06-21 01:12:45 +02:00
|
|
|
$beforekey = null;
|
2011-06-18 16:27:21 +01:00
|
|
|
$i = array_search('modedit', $keys);
|
2011-06-21 01:12:45 +02:00
|
|
|
if ($i === false and array_key_exists(0, $keys)) {
|
2011-06-18 16:27:21 +01:00
|
|
|
$beforekey = $keys[0];
|
|
|
|
} else if (array_key_exists($i + 1, $keys)) {
|
|
|
|
$beforekey = $keys[$i + 1];
|
|
|
|
}
|
|
|
|
|
2010-05-21 03:15:48 +00:00
|
|
|
if (has_capability('mod/quiz:manageoverrides', $PAGE->cm->context)) {
|
|
|
|
$url = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$PAGE->cm->id));
|
2011-06-18 16:27:21 +01:00
|
|
|
$node = navigation_node::create(get_string('groupoverrides', 'quiz'),
|
2011-05-12 00:30:25 +01:00
|
|
|
new moodle_url($url, array('mode'=>'group')),
|
2011-06-18 16:27:21 +01:00
|
|
|
navigation_node::TYPE_SETTING, null, 'mod_quiz_groupoverrides');
|
|
|
|
$quiznode->add_node($node, $beforekey);
|
|
|
|
|
|
|
|
$node = navigation_node::create(get_string('useroverrides', 'quiz'),
|
2011-05-12 00:30:25 +01:00
|
|
|
new moodle_url($url, array('mode'=>'user')),
|
2011-06-18 16:27:21 +01:00
|
|
|
navigation_node::TYPE_SETTING, null, 'mod_quiz_useroverrides');
|
|
|
|
$quiznode->add_node($node, $beforekey);
|
2009-09-16 05:15:22 +00:00
|
|
|
}
|
2010-05-21 03:15:48 +00:00
|
|
|
|
2009-09-16 05:15:22 +00:00
|
|
|
if (has_capability('mod/quiz:manage', $PAGE->cm->context)) {
|
2011-06-18 16:27:21 +01:00
|
|
|
$node = navigation_node::create(get_string('editquiz', 'quiz'),
|
|
|
|
new moodle_url('/mod/quiz/edit.php', array('cmid'=>$PAGE->cm->id)),
|
|
|
|
navigation_node::TYPE_SETTING, null, 'mod_quiz_edit',
|
|
|
|
new pix_icon('t/edit', ''));
|
|
|
|
$quiznode->add_node($node, $beforekey);
|
2009-09-16 05:15:22 +00:00
|
|
|
}
|
2010-05-21 03:15:48 +00:00
|
|
|
|
|
|
|
if (has_capability('mod/quiz:preview', $PAGE->cm->context)) {
|
2011-05-12 00:30:25 +01:00
|
|
|
$url = new moodle_url('/mod/quiz/startattempt.php',
|
|
|
|
array('cmid'=>$PAGE->cm->id, 'sesskey'=>sesskey()));
|
2011-06-18 16:27:21 +01:00
|
|
|
$node = navigation_node::create(get_string('preview', 'quiz'), $url,
|
|
|
|
navigation_node::TYPE_SETTING, null, 'mod_quiz_preview',
|
|
|
|
new pix_icon('t/preview', ''));
|
|
|
|
$quiznode->add_node($node, $beforekey);
|
2009-09-16 05:15:22 +00:00
|
|
|
}
|
2010-05-21 03:15:48 +00:00
|
|
|
|
2010-08-03 11:10:38 +00:00
|
|
|
question_extend_settings_navigation($quiznode, $PAGE->cm->context)->trim_if_empty();
|
2010-05-21 03:15:48 +00:00
|
|
|
}
|
2010-08-10 09:56:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Serves the quiz files.
|
|
|
|
*
|
2012-02-15 12:48:57 +08:00
|
|
|
* @package mod_quiz
|
|
|
|
* @category files
|
|
|
|
* @param stdClass $course course object
|
|
|
|
* @param stdClass $cm course module object
|
|
|
|
* @param stdClass $context context object
|
|
|
|
* @param string $filearea file area
|
|
|
|
* @param array $args extra arguments
|
|
|
|
* @param bool $forcedownload whether or not force download
|
2012-04-18 15:56:09 +02:00
|
|
|
* @param array $options additional options affecting the file serving
|
2010-08-10 09:56:48 +00:00
|
|
|
* @return bool false if file not found, does not return if found - justsend the file
|
|
|
|
*/
|
2012-04-18 15:56:09 +02:00
|
|
|
function quiz_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
|
2010-08-10 09:56:48 +00:00
|
|
|
global $CFG, $DB;
|
|
|
|
|
|
|
|
if ($context->contextlevel != CONTEXT_MODULE) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
require_login($course, false, $cm);
|
|
|
|
|
|
|
|
if (!$quiz = $DB->get_record('quiz', array('id'=>$cm->instance))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-04 12:40:21 +01:00
|
|
|
// The 'intro' area is served by pluginfile.php.
|
2010-08-10 09:56:48 +00:00
|
|
|
$fileareas = array('feedback');
|
|
|
|
if (!in_array($filearea, $fileareas)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$feedbackid = (int)array_shift($args);
|
|
|
|
if (!$feedback = $DB->get_record('quiz_feedback', array('id'=>$feedbackid))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$fs = get_file_storage();
|
|
|
|
$relativepath = implode('/', $args);
|
|
|
|
$fullpath = "/$context->id/mod_quiz/$filearea/$feedbackid/$relativepath";
|
|
|
|
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-04-18 15:56:09 +02:00
|
|
|
send_stored_file($file, 0, 0, true, $options);
|
2010-08-10 09:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called via pluginfile.php -> question_pluginfile to serve files belonging to
|
|
|
|
* a question in a question_attempt when that attempt is a quiz attempt.
|
|
|
|
*
|
2012-02-15 12:48:57 +08:00
|
|
|
* @package mod_quiz
|
|
|
|
* @category files
|
|
|
|
* @param stdClass $course course settings object
|
|
|
|
* @param stdClass $context context object
|
2010-08-10 09:56:48 +00:00
|
|
|
* @param string $component the name of the component we are serving files for.
|
|
|
|
* @param string $filearea the name of the file area.
|
2012-02-15 12:48:57 +08:00
|
|
|
* @param int $qubaid the attempt usage id.
|
|
|
|
* @param int $slot the id of a question in this quiz attempt.
|
2010-08-10 09:56:48 +00:00
|
|
|
* @param array $args the remaining bits of the file path.
|
|
|
|
* @param bool $forcedownload whether the user must be forced to download the file.
|
2012-04-18 15:56:09 +02:00
|
|
|
* @param array $options additional options affecting the file serving
|
2010-08-10 09:56:48 +00:00
|
|
|
* @return bool false if file not found, does not return if found - justsend the file
|
|
|
|
*/
|
2012-06-11 13:34:47 +08:00
|
|
|
function quiz_question_pluginfile($course, $context, $component,
|
2012-04-18 15:56:09 +02:00
|
|
|
$filearea, $qubaid, $slot, $args, $forcedownload, array $options=array()) {
|
2012-01-30 17:47:11 +00:00
|
|
|
global $CFG;
|
2010-08-10 09:56:48 +00:00
|
|
|
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
|
|
|
|
2011-02-09 20:33:51 +00:00
|
|
|
$attemptobj = quiz_attempt::create_from_usage_id($qubaid);
|
2012-04-22 17:41:47 +02:00
|
|
|
require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
|
2010-08-10 09:56:48 +00:00
|
|
|
|
|
|
|
if ($attemptobj->is_own_attempt() && !$attemptobj->is_finished()) {
|
|
|
|
// In the middle of an attempt.
|
|
|
|
if (!$attemptobj->is_preview_user()) {
|
|
|
|
$attemptobj->require_capability('mod/quiz:attempt');
|
|
|
|
}
|
|
|
|
$isreviewing = false;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Reviewing an attempt.
|
|
|
|
$attemptobj->check_review_capability();
|
|
|
|
$isreviewing = true;
|
|
|
|
}
|
|
|
|
|
2011-02-09 20:33:51 +00:00
|
|
|
if (!$attemptobj->check_file_access($slot, $isreviewing, $context->id,
|
2010-08-10 09:56:48 +00:00
|
|
|
$component, $filearea, $args, $forcedownload)) {
|
|
|
|
send_file_not_found();
|
|
|
|
}
|
|
|
|
|
|
|
|
$fs = get_file_storage();
|
|
|
|
$relativepath = implode('/', $args);
|
|
|
|
$fullpath = "/$context->id/$component/$filearea/$relativepath";
|
|
|
|
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
|
|
|
|
send_file_not_found();
|
|
|
|
}
|
|
|
|
|
2012-04-18 15:56:09 +02:00
|
|
|
send_stored_file($file, 0, 0, $forcedownload, $options);
|
2010-08-10 09:56:48 +00:00
|
|
|
}
|
MDL-26105 Block settings should contains less options, and be more user friendly
AMOS BEGIN
MOV [page-blog-index, pagetype], [page-blog-index, blog]
MOV [page-blog-x, pagetype], [page-blog-x, blog]
MOV [page-tag-x, pagetype], [page-tag-x, tag]
MOV [page-course-view-weeks, pagetype], [page-course-view-weeks, format_weeks]
MOV [page-course-view-weeks-x, pagetype], [page-course-view-weeks-x, format_weeks]
MOV [page-course-view-topics, pagetype], [page-course-view-topics, format_topics]
MOV [page-course-view-topics-x, pagetype], [page-course-view-topics-x, format_topics]
AMOS END
2011-04-28 11:20:30 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a list of page types
|
|
|
|
* @param string $pagetype current page type
|
|
|
|
* @param stdClass $parentcontext Block's parent context
|
|
|
|
* @param stdClass $currentcontext Current context of block
|
|
|
|
*/
|
2011-06-17 16:23:10 +08:00
|
|
|
function quiz_page_type_list($pagetype, $parentcontext, $currentcontext) {
|
2011-07-01 15:43:26 +08:00
|
|
|
$module_pagetype = array(
|
|
|
|
'mod-quiz-*'=>get_string('page-mod-quiz-x', 'quiz'),
|
|
|
|
'mod-quiz-edit'=>get_string('page-mod-quiz-edit', 'quiz'));
|
MDL-26105 Block settings should contains less options, and be more user friendly
AMOS BEGIN
MOV [page-blog-index, pagetype], [page-blog-index, blog]
MOV [page-blog-x, pagetype], [page-blog-x, blog]
MOV [page-tag-x, pagetype], [page-tag-x, tag]
MOV [page-course-view-weeks, pagetype], [page-course-view-weeks, format_weeks]
MOV [page-course-view-weeks-x, pagetype], [page-course-view-weeks-x, format_weeks]
MOV [page-course-view-topics, pagetype], [page-course-view-topics, format_topics]
MOV [page-course-view-topics-x, pagetype], [page-course-view-topics-x, format_topics]
AMOS END
2011-04-28 11:20:30 +08:00
|
|
|
return $module_pagetype;
|
|
|
|
}
|
2012-01-20 16:52:17 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the options for quiz navigation.
|
|
|
|
*/
|
|
|
|
function quiz_get_navigation_options() {
|
|
|
|
return array(
|
|
|
|
QUIZ_NAVMETHOD_FREE => get_string('navmethod_free', 'quiz'),
|
|
|
|
QUIZ_NAVMETHOD_SEQ => get_string('navmethod_seq', 'quiz')
|
|
|
|
);
|
|
|
|
}
|