mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
Merge branch 'MDL-74923' of https://github.com/timhunt/moodle
This commit is contained in:
commit
ee48f4ab99
@ -309,11 +309,17 @@ class manager {
|
||||
|
||||
$classname = null;
|
||||
|
||||
// Getting the appropiate class to get the correct setting value.
|
||||
// Getting the appropriate class to get the correct setting value.
|
||||
$settingtype = get_class($settingdata);
|
||||
|
||||
// Check if it is a setting from a plugin.
|
||||
$plugindata = explode('_', $settingtype);
|
||||
$namespacedata = explode('\\', $settingtype);
|
||||
if (count($namespacedata) > 1) {
|
||||
$plugindata = explode('_', $namespacedata[0]);
|
||||
$settingtype = end($namespacedata);
|
||||
} else {
|
||||
$plugindata = explode('_', $settingtype, 2);
|
||||
}
|
||||
|
||||
$types = \core_component::get_plugin_types();
|
||||
if (array_key_exists($plugindata[0], $types)) {
|
||||
$plugins = \core_component::get_plugin_list($plugindata[0]);
|
||||
|
@ -133,8 +133,8 @@ class manager_test extends \advanced_testcase {
|
||||
$settingpage = $adminroot->locate('modsettingquiz');
|
||||
$settingdata = $settingpage->settings->quizbrowsersecurity;;
|
||||
$result = $manager->get_setting($settingdata, '');
|
||||
$this->assertInstanceOf('\mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_browsersecurity', $result);
|
||||
$this->assertNotEquals('core_adminpresets\local\setting\adminpresets_setting', get_class($result));
|
||||
$this->assertInstanceOf(\mod_quiz\adminpresets\adminpresets_browser_security_setting::class, $result);
|
||||
$this->assertNotEquals(\core_adminpresets\local\setting\adminpresets_setting::class, get_class($result));
|
||||
|
||||
// Check the adminpresets_setting class is returned when no specific class exists.
|
||||
$settingpage = $adminroot->locate('managecustomfields');
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
/**
|
||||
* This class keeps track of the various access rules that apply to a particular
|
||||
@ -528,7 +529,7 @@ class quiz_access_manager {
|
||||
}
|
||||
|
||||
$when = quiz_attempt_state($this->quizobj->get_quiz(), $attempt);
|
||||
$reviewoptions = mod_quiz_display_options::make_from_quiz(
|
||||
$reviewoptions = display_options::make_from_quiz(
|
||||
$this->quizobj->get_quiz(), $when);
|
||||
|
||||
if (!$reviewoptions->attempt) {
|
||||
|
@ -28,6 +28,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_quiz\question\bank\qbank_helper;
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
|
||||
/**
|
||||
@ -476,7 +477,7 @@ class quiz {
|
||||
* If $reviewoptions->attempt is false, meaning that students can't review this
|
||||
* attempt at the moment, return an appropriate string explaining why.
|
||||
*
|
||||
* @param int $when One of the mod_quiz_display_options::DURING,
|
||||
* @param int $when One of the display_options::DURING,
|
||||
* IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants.
|
||||
* @param bool $short if true, return a shorter string.
|
||||
* @return string an appropraite message.
|
||||
@ -491,11 +492,11 @@ class quiz {
|
||||
$dateformat = '';
|
||||
}
|
||||
|
||||
if ($when == mod_quiz_display_options::DURING ||
|
||||
$when == mod_quiz_display_options::IMMEDIATELY_AFTER) {
|
||||
if ($when == display_options::DURING ||
|
||||
$when == display_options::IMMEDIATELY_AFTER) {
|
||||
return '';
|
||||
} else if ($when == mod_quiz_display_options::LATER_WHILE_OPEN && $this->quiz->timeclose &&
|
||||
$this->quiz->reviewattempt & mod_quiz_display_options::AFTER_CLOSE) {
|
||||
} else if ($when == display_options::LATER_WHILE_OPEN && $this->quiz->timeclose &&
|
||||
$this->quiz->reviewattempt & display_options::AFTER_CLOSE) {
|
||||
return get_string('noreviewuntil' . $langstrsuffix, 'quiz',
|
||||
userdate($this->quiz->timeclose, $dateformat));
|
||||
} else {
|
||||
@ -631,7 +632,7 @@ class quiz_attempt {
|
||||
/** @var array slot => page number for this slot. */
|
||||
protected $questionpages;
|
||||
|
||||
/** @var mod_quiz_display_options cache for the appropriate review options. */
|
||||
/** @var display_options cache for the appropriate review options. */
|
||||
protected $reviewoptions = null;
|
||||
|
||||
// Constructor =============================================================
|
||||
@ -1080,7 +1081,7 @@ class quiz_attempt {
|
||||
* If not, prints an error.
|
||||
*/
|
||||
public function check_review_capability() {
|
||||
if ($this->get_attempt_state() == mod_quiz_display_options::IMMEDIATELY_AFTER) {
|
||||
if ($this->get_attempt_state() == display_options::IMMEDIATELY_AFTER) {
|
||||
$capability = 'mod/quiz:attempt';
|
||||
} else {
|
||||
$capability = 'mod/quiz:reviewmyattempts';
|
||||
@ -1132,7 +1133,7 @@ class quiz_attempt {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int one of the mod_quiz_display_options::DURING,
|
||||
* @return int one of the display_options::DURING,
|
||||
* IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants.
|
||||
*/
|
||||
public function get_attempt_state() {
|
||||
@ -1140,7 +1141,7 @@ class quiz_attempt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that the correct mod_quiz_display_options for this quiz at the
|
||||
* Wrapper that the correct display_options for this quiz at the
|
||||
* moment.
|
||||
*
|
||||
* @param bool $reviewing true for options when reviewing, false for when attempting.
|
||||
@ -1160,15 +1161,15 @@ class quiz_attempt {
|
||||
return $this->reviewoptions;
|
||||
|
||||
} else {
|
||||
$options = mod_quiz_display_options::make_from_quiz($this->get_quiz(),
|
||||
mod_quiz_display_options::DURING);
|
||||
$options = display_options::make_from_quiz($this->get_quiz(),
|
||||
display_options::DURING);
|
||||
$options->flags = quiz_get_flag_option($this->attempt, $this->quizobj->get_context());
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that the correct mod_quiz_display_options for this quiz at the
|
||||
* Wrapper that the correct display_options for this quiz at the
|
||||
* moment.
|
||||
*
|
||||
* @param bool $reviewing true for review page, else attempt page.
|
||||
|
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
/**
|
||||
* Structure step to restore one quiz activity
|
||||
*
|
||||
@ -162,70 +164,70 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
|
||||
$oldreview = $data->review;
|
||||
|
||||
$data->reviewattempt =
|
||||
mod_quiz_display_options::DURING |
|
||||
display_options::DURING |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_RESPONSES ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_RESPONSES ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_RESPONSES ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
|
||||
$data->reviewcorrectness =
|
||||
mod_quiz_display_options::DURING |
|
||||
display_options::DURING |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
|
||||
$data->reviewmarks =
|
||||
mod_quiz_display_options::DURING |
|
||||
display_options::DURING |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
|
||||
$data->reviewspecificfeedback =
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ?
|
||||
mod_quiz_display_options::DURING : 0) |
|
||||
display_options::DURING : 0) |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_FEEDBACK ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_FEEDBACK ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
|
||||
$data->reviewgeneralfeedback =
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ?
|
||||
mod_quiz_display_options::DURING : 0) |
|
||||
display_options::DURING : 0) |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_GENERALFEEDBACK ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_GENERALFEEDBACK ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
|
||||
$data->reviewrightanswer =
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ?
|
||||
mod_quiz_display_options::DURING : 0) |
|
||||
display_options::DURING : 0) |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_ANSWERS ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_ANSWERS ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
|
||||
$data->reviewoverallfeedback =
|
||||
0 |
|
||||
($oldreview & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_OVERALLFEEDBACK ?
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER : 0) |
|
||||
display_options::IMMEDIATELY_AFTER : 0) |
|
||||
($oldreview & QUIZ_OLD_OPEN & QUIZ_OLD_OVERALLFEEDBACK ?
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN : 0) |
|
||||
display_options::LATER_WHILE_OPEN : 0) |
|
||||
($oldreview & QUIZ_OLD_CLOSED & QUIZ_OLD_OVERALLFEEDBACK ?
|
||||
mod_quiz_display_options::AFTER_CLOSE : 0);
|
||||
display_options::AFTER_CLOSE : 0);
|
||||
}
|
||||
|
||||
// The old popup column from from <= 2.1 need to be mapped to
|
||||
|
@ -14,27 +14,19 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz browser security option.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2008 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
namespace mod_quiz\admin;
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz browser security option.
|
||||
*
|
||||
* Just so we can lazy-load the choices.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category admin
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_admin_setting_browsersecurity extends admin_setting_configselect_with_advanced {
|
||||
class browser_security_setting extends \admin_setting_configselect_with_advanced {
|
||||
public function load_choices() {
|
||||
global $CFG;
|
||||
|
||||
@ -43,7 +35,7 @@ class mod_quiz_admin_setting_browsersecurity extends admin_setting_configselect_
|
||||
}
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
$this->choices = quiz_access_manager::get_browser_security_choices();
|
||||
$this->choices = \quiz_access_manager::get_browser_security_choices();
|
||||
|
||||
return true;
|
||||
}
|
@ -14,27 +14,19 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz grading method.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2008 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
namespace mod_quiz\admin;
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz grading method.
|
||||
*
|
||||
* Just so we can lazy-load the choices.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @package mod_quiz
|
||||
* @category admin
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_admin_setting_grademethod extends admin_setting_configselect_with_advanced {
|
||||
class grade_method_setting extends \admin_setting_configselect_with_advanced {
|
||||
public function load_choices() {
|
||||
global $CFG;
|
||||
|
@ -14,27 +14,19 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz overdue attempt handling method.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2008 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
namespace mod_quiz\admin;
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz overdue attempt handling method.
|
||||
*
|
||||
* Just so we can lazy-load the choices.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @package mod_quiz
|
||||
* @category admin
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_admin_setting_overduehandling extends admin_setting_configselect_with_advanced {
|
||||
class overdue_handling_setting extends \admin_setting_configselect_with_advanced {
|
||||
public function load_choices() {
|
||||
global $CFG;
|
||||
|
@ -14,46 +14,38 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_quiz\admin;
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz review options.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2008 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Admin settings class for the quiz review options.
|
||||
*
|
||||
* @category admin
|
||||
* @copyright 2008 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_admin_review_setting extends admin_setting {
|
||||
class review_setting extends \admin_setting {
|
||||
/**
|
||||
* @var integer should match the constants defined in
|
||||
* {@link mod_quiz_display_options}. Copied for performance reasons.
|
||||
* {@see display_options}. Copied for performance reasons.
|
||||
*/
|
||||
const DURING = 0x10000;
|
||||
|
||||
/**
|
||||
* @var integer should match the constants defined in
|
||||
* {@link mod_quiz_display_options}. Copied for performance reasons.
|
||||
* {@see display_options}. Copied for performance reasons.
|
||||
*/
|
||||
const IMMEDIATELY_AFTER = 0x01000;
|
||||
|
||||
/**
|
||||
* @var integer should match the constants defined in
|
||||
* {@link mod_quiz_display_options}. Copied for performance reasons.
|
||||
* {@see display_options}. Copied for performance reasons.
|
||||
*/
|
||||
const LATER_WHILE_OPEN = 0x00100;
|
||||
|
||||
/**
|
||||
* @var integer should match the constants defined in
|
||||
* {@link mod_quiz_display_options}. Copied for performance reasons.
|
||||
* {@see display_options}. Copied for performance reasons.
|
||||
*/
|
||||
const AFTER_CLOSE = 0x00010;
|
||||
|
@ -14,27 +14,19 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Admin settings class for the choices for how to display the user's image
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2008 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
namespace mod_quiz\admin;
|
||||
|
||||
/**
|
||||
* Admin settings class for the choices for how to display the user's image.
|
||||
*
|
||||
* Just so we can lazy-load the choices.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category admin
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_admin_setting_user_image extends admin_setting_configselect_with_advanced {
|
||||
class user_image_setting extends \admin_setting_configselect_with_advanced {
|
||||
public function load_choices() {
|
||||
global $CFG;
|
||||
|
@ -26,7 +26,7 @@ use core_adminpresets\local\setting\adminpresets_admin_setting_configselect_with
|
||||
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class adminpresets_mod_quiz_admin_setting_browsersecurity extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
class adminpresets_browser_security_setting extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
|
||||
public function set_behaviors() {
|
||||
$this->behaviors['loadchoices'] = &$this->settingdata;
|
@ -26,7 +26,7 @@ use core_adminpresets\local\setting\adminpresets_admin_setting_configselect_with
|
||||
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class adminpresets_mod_quiz_admin_setting_grademethod extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
class adminpresets_grade_method_setting extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
|
||||
public function set_behaviors() {
|
||||
$this->behaviors['loadchoices'] = &$this->settingdata;
|
@ -26,7 +26,7 @@ use core_adminpresets\local\setting\adminpresets_admin_setting_configselect_with
|
||||
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class adminpresets_mod_quiz_admin_setting_overduehandling extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
class adminpresets_overdue_handling_setting extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
|
||||
public function set_behaviors() {
|
||||
$this->behaviors['loadchoices'] = &$this->settingdata;
|
@ -27,15 +27,15 @@ use core_adminpresets\local\setting\adminpresets_setting;
|
||||
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class adminpresets_mod_quiz_admin_review_setting extends adminpresets_setting {
|
||||
class adminpresets_review_setting extends adminpresets_setting {
|
||||
|
||||
/**
|
||||
* The setting value is a sum of 'mod_quiz_admin_review_setting::times'
|
||||
* The setting value is a sum of 'review_setting::times'
|
||||
*/
|
||||
protected function set_visiblevalue() {
|
||||
|
||||
// Getting the masks descriptions (mod_quiz_admin_review_setting protected method).
|
||||
$reflectiontimes = new ReflectionMethod('mod_quiz_admin_review_setting', 'times');
|
||||
// Getting the masks descriptions (review_setting protected method).
|
||||
$reflectiontimes = new ReflectionMethod('mod_quiz\admin\review_setting', 'times');
|
||||
$reflectiontimes->setAccessible(true);
|
||||
$times = $reflectiontimes->invoke(null);
|
||||
|
@ -26,7 +26,7 @@ use core_adminpresets\local\setting\adminpresets_admin_setting_configselect_with
|
||||
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class adminpresets_mod_quiz_admin_setting_user_image extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
class adminpresets_user_image_setting extends adminpresets_admin_setting_configselect_with_advanced {
|
||||
|
||||
public function set_behaviors() {
|
||||
$this->behaviors['loadchoices'] = &$this->settingdata;
|
@ -194,8 +194,8 @@ class mod_quiz_external extends external_api {
|
||||
(-1 means use decimalpoints.)', VALUE_OPTIONAL),
|
||||
'reviewattempt' => new external_value(PARAM_INT, 'Whether users are allowed to review their quiz
|
||||
attempts at various times. This is a bit field, decoded by the
|
||||
mod_quiz_display_options class. It is formed by ORing together
|
||||
the constants defined there.', VALUE_OPTIONAL),
|
||||
\mod_quiz\question\display_options class. It is formed by ORing
|
||||
together the constants defined there.', VALUE_OPTIONAL),
|
||||
'reviewcorrectness' => new external_value(PARAM_INT, 'Whether users are allowed to review their quiz
|
||||
attempts at various times.
|
||||
A bit field, like reviewattempt.', VALUE_OPTIONAL),
|
||||
|
384
mod/quiz/classes/local/reports/attempts_report.php
Normal file
384
mod/quiz/classes/local/reports/attempts_report.php
Normal file
@ -0,0 +1,384 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\local\reports;
|
||||
|
||||
use coding_exception;
|
||||
use context_module;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
use table_sql;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Base class for quiz reports that are basically a table with one row for each attempt.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class attempts_report extends report_base {
|
||||
/** @var int default page size for reports. */
|
||||
const DEFAULT_PAGE_SIZE = 30;
|
||||
|
||||
/** @var string constant used for the options, means all users with attempts. */
|
||||
const ALL_WITH = 'all_with';
|
||||
/** @var string constant used for the options, means only enrolled users with attempts. */
|
||||
const ENROLLED_WITH = 'enrolled_with';
|
||||
/** @var string constant used for the options, means only enrolled users without attempts. */
|
||||
const ENROLLED_WITHOUT = 'enrolled_without';
|
||||
/** @var string constant used for the options, means all enrolled users. */
|
||||
const ENROLLED_ALL = 'enrolled_any';
|
||||
|
||||
/** @var string the mode this report is. */
|
||||
protected $mode;
|
||||
|
||||
/** @var context_module the quiz context. */
|
||||
protected $context;
|
||||
|
||||
/** @var attempts_report_options_form The settings form to use. */
|
||||
protected $form;
|
||||
|
||||
/** @var string SQL fragment for selecting the attempt that gave the final grade,
|
||||
* if applicable. */
|
||||
protected $qmsubselect;
|
||||
|
||||
/** @var boolean caches the results of {@see should_show_grades()}. */
|
||||
protected $showgrades = null;
|
||||
|
||||
/**
|
||||
* Initialise various aspects of this report.
|
||||
*
|
||||
* @param string $mode
|
||||
* @param string $formclass
|
||||
* @param object $quiz
|
||||
* @param object $cm
|
||||
* @param object $course
|
||||
* @return array with four elements:
|
||||
* 0 => integer the current group id (0 for none).
|
||||
* 1 => \core\dml\sql_join Contains joins, wheres, params for all the students in this course.
|
||||
* 2 => \core\dml\sql_join Contains joins, wheres, params for all the students in the current group.
|
||||
* 3 => \core\dml\sql_join Contains joins, wheres, params for all the students to show in the report.
|
||||
* Will be the same as either element 1 or 2.
|
||||
*/
|
||||
public function init($mode, $formclass, $quiz, $cm, $course): array {
|
||||
$this->mode = $mode;
|
||||
|
||||
$this->context = context_module::instance($cm->id);
|
||||
|
||||
[$currentgroup, $studentsjoins, $groupstudentsjoins, $allowedjoins] = $this->get_students_joins(
|
||||
$cm, $course);
|
||||
|
||||
$this->qmsubselect = quiz_report_qm_filter_select($quiz);
|
||||
|
||||
$this->form = new $formclass($this->get_base_url(),
|
||||
['quiz' => $quiz, 'currentgroup' => $currentgroup, 'context' => $this->context]);
|
||||
|
||||
return [$currentgroup, $studentsjoins, $groupstudentsjoins, $allowedjoins];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL for this report.
|
||||
* @return moodle_url the URL.
|
||||
*/
|
||||
protected function get_base_url() {
|
||||
return new moodle_url('/mod/quiz/report.php',
|
||||
['id' => $this->context->instanceid, 'mode' => $this->mode]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sql fragments (joins) which can be used to build queries that
|
||||
* will select an appropriate set of students to show in the reports.
|
||||
*
|
||||
* @param object $cm the course module.
|
||||
* @param object $course the course settings.
|
||||
* @return array with four elements:
|
||||
* 0 => integer the current group id (0 for none).
|
||||
* 1 => \core\dml\sql_join Contains joins, wheres, params for all the students in this course.
|
||||
* 2 => \core\dml\sql_join Contains joins, wheres, params for all the students in the current group.
|
||||
* 3 => \core\dml\sql_join Contains joins, wheres, params for all the students to show in the report.
|
||||
* Will be the same as either element 1 or 2.
|
||||
*/
|
||||
protected function get_students_joins($cm, $course = null) {
|
||||
$currentgroup = $this->get_current_group($cm, $course, $this->context);
|
||||
|
||||
$empty = new \core\dml\sql_join();
|
||||
if ($currentgroup == self::NO_GROUPS_ALLOWED) {
|
||||
return [$currentgroup, $empty, $empty, $empty];
|
||||
}
|
||||
|
||||
$studentsjoins = get_enrolled_with_capabilities_join($this->context, '',
|
||||
['mod/quiz:attempt', 'mod/quiz:reviewmyattempts']);
|
||||
|
||||
if (empty($currentgroup)) {
|
||||
return [$currentgroup, $studentsjoins, $empty, $studentsjoins];
|
||||
}
|
||||
|
||||
// We have a currently selected group.
|
||||
$groupstudentsjoins = get_enrolled_with_capabilities_join($this->context, '',
|
||||
['mod/quiz:attempt', 'mod/quiz:reviewmyattempts'], $currentgroup);
|
||||
|
||||
return [$currentgroup, $studentsjoins, $groupstudentsjoins, $groupstudentsjoins];
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the things you commonly want at the top of a quiz report.
|
||||
*
|
||||
* Calls through to {@see print_header_and_tabs()} and then
|
||||
* outputs the standard group selector, number of attempts summary,
|
||||
* and messages to cover common cases when the report can't be shown.
|
||||
*
|
||||
* @param stdClass $cm the course_module information.
|
||||
* @param stdClass $course the course settings.
|
||||
* @param stdClass $quiz the quiz settings.
|
||||
* @param attempts_report_options $options the current report settings.
|
||||
* @param int $currentgroup the current group.
|
||||
* @param bool $hasquestions whether there are any questions in the quiz.
|
||||
* @param bool $hasstudents whether there are any relevant students.
|
||||
*/
|
||||
protected function print_standard_header_and_messages($cm, $course, $quiz,
|
||||
$options, $currentgroup, $hasquestions, $hasstudents) {
|
||||
global $OUTPUT;
|
||||
|
||||
$this->print_header_and_tabs($cm, $course, $quiz, $this->mode);
|
||||
|
||||
if (groups_get_activity_groupmode($cm)) {
|
||||
// Groups are being used, so output the group selector if we are not downloading.
|
||||
groups_print_activity_menu($cm, $options->get_url());
|
||||
}
|
||||
|
||||
// Print information on the number of existing attempts.
|
||||
if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) {
|
||||
echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>';
|
||||
}
|
||||
|
||||
if (!$hasquestions) {
|
||||
echo quiz_no_questions_message($quiz, $cm, $this->context);
|
||||
} else if ($currentgroup == self::NO_GROUPS_ALLOWED) {
|
||||
echo $OUTPUT->notification(get_string('notingroup'));
|
||||
} else if (!$hasstudents) {
|
||||
echo $OUTPUT->notification(get_string('nostudentsyet'));
|
||||
} else if ($currentgroup && !$this->hasgroupstudents) {
|
||||
echo $OUTPUT->notification(get_string('nostudentsingroup'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the user-related columns to the $columns and $headers arrays.
|
||||
* @param table_sql $table the table being constructed.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
*/
|
||||
protected function add_user_columns($table, &$columns, &$headers) {
|
||||
global $CFG;
|
||||
if (!$table->is_downloading() && $CFG->grade_report_showuserimage) {
|
||||
$columns[] = 'picture';
|
||||
$headers[] = '';
|
||||
}
|
||||
if (!$table->is_downloading()) {
|
||||
$columns[] = 'fullname';
|
||||
$headers[] = get_string('name');
|
||||
} else {
|
||||
$columns[] = 'lastname';
|
||||
$headers[] = get_string('lastname');
|
||||
$columns[] = 'firstname';
|
||||
$headers[] = get_string('firstname');
|
||||
}
|
||||
|
||||
$extrafields = \core_user\fields::get_identity_fields($this->context);
|
||||
foreach ($extrafields as $field) {
|
||||
$columns[] = $field;
|
||||
$headers[] = \core_user\fields::get_display_name($field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the display options for the user-related columns in the table.
|
||||
* @param table_sql $table the table being constructed.
|
||||
*/
|
||||
protected function configure_user_columns($table) {
|
||||
$table->column_suppress('picture');
|
||||
$table->column_suppress('fullname');
|
||||
|
||||
$extrafields = \core_user\fields::get_identity_fields($this->context);
|
||||
foreach ($extrafields as $field) {
|
||||
$table->column_suppress($field);
|
||||
}
|
||||
|
||||
$table->column_class('picture', 'picture');
|
||||
$table->column_class('lastname', 'bold');
|
||||
$table->column_class('firstname', 'bold');
|
||||
$table->column_class('fullname', 'bold');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the state column to the $columns and $headers arrays.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
*/
|
||||
protected function add_state_column(&$columns, &$headers) {
|
||||
$columns[] = 'state';
|
||||
$headers[] = get_string('attemptstate', 'quiz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the time-related columns to the $columns and $headers arrays.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
*/
|
||||
protected function add_time_columns(&$columns, &$headers) {
|
||||
$columns[] = 'timestart';
|
||||
$headers[] = get_string('startedon', 'quiz');
|
||||
|
||||
$columns[] = 'timefinish';
|
||||
$headers[] = get_string('timecompleted', 'quiz');
|
||||
|
||||
$columns[] = 'duration';
|
||||
$headers[] = get_string('attemptduration', 'quiz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the grade and feedback columns, if applicable, to the $columns
|
||||
* and $headers arrays.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param bool $usercanseegrades whether the user is allowed to see grades for this quiz.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
* @param bool $includefeedback whether to include the feedbacktext columns
|
||||
*/
|
||||
protected function add_grade_columns($quiz, $usercanseegrades, &$columns, &$headers, $includefeedback = true) {
|
||||
if ($usercanseegrades) {
|
||||
$columns[] = 'sumgrades';
|
||||
$headers[] = get_string('grade', 'quiz') . '/' .
|
||||
quiz_format_grade($quiz, $quiz->grade);
|
||||
}
|
||||
|
||||
if ($includefeedback && quiz_has_feedback($quiz)) {
|
||||
$columns[] = 'feedbacktext';
|
||||
$headers[] = get_string('feedback', 'quiz');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the table.
|
||||
* @param table_sql $table the table being constructed.
|
||||
* @param array $columns the list of columns.
|
||||
* @param array $headers the columns headings.
|
||||
* @param moodle_url $reporturl the URL of this report.
|
||||
* @param attempts_report_options $options the display options.
|
||||
* @param bool $collapsible whether to allow columns in the report to be collapsed.
|
||||
*/
|
||||
protected function set_up_table_columns($table, $columns, $headers, $reporturl,
|
||||
attempts_report_options $options, $collapsible) {
|
||||
$table->define_columns($columns);
|
||||
$table->define_headers($headers);
|
||||
$table->sortable(true, 'uniqueid');
|
||||
|
||||
$table->define_baseurl($options->get_url());
|
||||
|
||||
$this->configure_user_columns($table);
|
||||
|
||||
$table->no_sorting('feedbacktext');
|
||||
$table->column_class('sumgrades', 'bold');
|
||||
|
||||
$table->set_attribute('id', 'attempts');
|
||||
|
||||
$table->collapsible($collapsible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any submitted actions.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param object $cm the cm object for the quiz.
|
||||
* @param int $currentgroup the currently selected group.
|
||||
* @param \core\dml\sql_join $groupstudentsjoins (joins, wheres, params) the students in the current group.
|
||||
* @param \core\dml\sql_join $allowedjoins (joins, wheres, params) the users whose attempt this user is allowed to modify.
|
||||
* @param moodle_url $redirecturl where to redircet to after a successful action.
|
||||
*/
|
||||
protected function process_actions($quiz, $cm, $currentgroup, \core\dml\sql_join $groupstudentsjoins,
|
||||
\core\dml\sql_join $allowedjoins, $redirecturl) {
|
||||
if (empty($currentgroup) || $this->hasgroupstudents) {
|
||||
if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) {
|
||||
if ($attemptids = optional_param_array('attemptid', [], PARAM_INT)) {
|
||||
require_capability('mod/quiz:deleteattempts', $this->context);
|
||||
$this->delete_selected_attempts($quiz, $cm, $attemptids, $allowedjoins);
|
||||
redirect($redirecturl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the quiz attempts
|
||||
* @param object $quiz the quiz settings. Attempts that don't belong to
|
||||
* this quiz are not deleted.
|
||||
* @param object $cm the course_module object.
|
||||
* @param array $attemptids the list of attempt ids to delete.
|
||||
* @param \core\dml\sql_join $allowedjoins (joins, wheres, params) This list of userids that are visible in the report.
|
||||
* Users can only delete attempts that they are allowed to see in the report.
|
||||
* Empty means all users.
|
||||
*/
|
||||
protected function delete_selected_attempts($quiz, $cm, $attemptids, \core\dml\sql_join $allowedjoins) {
|
||||
global $DB;
|
||||
|
||||
foreach ($attemptids as $attemptid) {
|
||||
if (empty($allowedjoins->joins)) {
|
||||
$sql = "SELECT quiza.*
|
||||
FROM {quiz_attempts} quiza
|
||||
JOIN {user} u ON u.id = quiza.userid
|
||||
WHERE quiza.id = :attemptid";
|
||||
} else {
|
||||
$sql = "SELECT quiza.*
|
||||
FROM {quiz_attempts} quiza
|
||||
JOIN {user} u ON u.id = quiza.userid
|
||||
{$allowedjoins->joins}
|
||||
WHERE {$allowedjoins->wheres} AND quiza.id = :attemptid";
|
||||
}
|
||||
$params = $allowedjoins->params + ['attemptid' => $attemptid];
|
||||
$attempt = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE);
|
||||
if (!$attempt || $attempt->quiz != $quiz->id || $attempt->preview != 0) {
|
||||
// Ensure the attempt exists, belongs to this quiz and belongs to
|
||||
// a student included in the report. If not skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the course module id before calling quiz_delete_attempt().
|
||||
$quiz->cmid = $cm->id;
|
||||
quiz_delete_attempt($attempt, $quiz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about which students to show in the report.
|
||||
* @param object $cm the coures module.
|
||||
* @param object $course the course settings.
|
||||
* @return array with four elements:
|
||||
* 0 => integer the current group id (0 for none).
|
||||
* 1 => array ids of all the students in this course.
|
||||
* 2 => array ids of all the students in the current group.
|
||||
* 3 => array ids of all the students to show in the report. Will be the
|
||||
* same as either element 1 or 2.
|
||||
* @deprecated since Moodle 3.2 Please use get_students_joins() instead.
|
||||
*/
|
||||
protected function load_relevant_students($cm, $course = null) {
|
||||
$msg = 'The function load_relevant_students() is deprecated. Please use get_students_joins() instead.';
|
||||
throw new coding_exception($msg);
|
||||
}
|
||||
}
|
286
mod/quiz/classes/local/reports/attempts_report_options.php
Normal file
286
mod/quiz/classes/local/reports/attempts_report_options.php
Normal file
@ -0,0 +1,286 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_quiz\local\reports;
|
||||
|
||||
use context_module;
|
||||
use moodle_url;
|
||||
use quiz_attempt;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Base class for the options that control what is visible in an {@see attempts_report}.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class attempts_report_options {
|
||||
|
||||
/** @var string the report mode. */
|
||||
public $mode;
|
||||
|
||||
/** @var stdClass the settings for the quiz being reported on. */
|
||||
public $quiz;
|
||||
|
||||
/** @var stdClass the course module objects for the quiz being reported on. */
|
||||
public $cm;
|
||||
|
||||
/** @var stdClass the course settings for the course the quiz is in. */
|
||||
public $course;
|
||||
|
||||
/**
|
||||
* @var array form field name => corresponding quiz_attempt:: state constant.
|
||||
*/
|
||||
protected static $statefields = [
|
||||
'stateinprogress' => quiz_attempt::IN_PROGRESS,
|
||||
'stateoverdue' => quiz_attempt::OVERDUE,
|
||||
'statefinished' => quiz_attempt::FINISHED,
|
||||
'stateabandoned' => quiz_attempt::ABANDONED,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string attempts_report::ALL_WITH, attempts_report::ENROLLED_WITH,
|
||||
* attempts_report::ENROLLED_WITHOUT or attempts_report::ENROLLED_ALL
|
||||
*/
|
||||
public $attempts = attempts_report::ENROLLED_WITH;
|
||||
|
||||
/** @var int the currently selected group. 0 if no group is selected. */
|
||||
public $group = 0;
|
||||
|
||||
/**
|
||||
* @var array|null of quiz_attempt::IN_PROGRESS, etc. constants. null means
|
||||
* no restriction.
|
||||
*/
|
||||
public $states = [quiz_attempt::IN_PROGRESS, quiz_attempt::OVERDUE,
|
||||
quiz_attempt::FINISHED, quiz_attempt::ABANDONED];
|
||||
|
||||
/**
|
||||
* @var bool whether to show all finished attmepts, or just the one that gave
|
||||
* the final grade for the user.
|
||||
*/
|
||||
public $onlygraded = false;
|
||||
|
||||
/** @var int Number of attempts to show per page. */
|
||||
public $pagesize = attempts_report::DEFAULT_PAGE_SIZE;
|
||||
|
||||
/** @var string whether the data should be downloaded in some format, or '' to display it. */
|
||||
public $download = '';
|
||||
|
||||
/** @var bool whether the current user has permission to see grades. */
|
||||
public $usercanseegrades;
|
||||
|
||||
/** @var bool whether the report table should have a column of checkboxes. */
|
||||
public $checkboxcolumn = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $mode which report these options are for.
|
||||
* @param object $quiz the settings for the quiz being reported on.
|
||||
* @param object $cm the course module objects for the quiz being reported on.
|
||||
* @param object $course the course settings for the coures this quiz is in.
|
||||
*/
|
||||
public function __construct($mode, $quiz, $cm, $course) {
|
||||
$this->mode = $mode;
|
||||
$this->quiz = $quiz;
|
||||
$this->cm = $cm;
|
||||
$this->course = $course;
|
||||
|
||||
$this->usercanseegrades = quiz_report_should_show_grades($quiz, context_module::instance($cm->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL parameters required to show the report with these options.
|
||||
* @return array URL parameter name => value.
|
||||
*/
|
||||
protected function get_url_params() {
|
||||
$params = [
|
||||
'id' => $this->cm->id,
|
||||
'mode' => $this->mode,
|
||||
'attempts' => $this->attempts,
|
||||
'onlygraded' => $this->onlygraded,
|
||||
];
|
||||
|
||||
if ($this->states) {
|
||||
$params['states'] = implode('-', $this->states);
|
||||
}
|
||||
|
||||
if (groups_get_activity_groupmode($this->cm, $this->course)) {
|
||||
$params['group'] = $this->group;
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to show the report with these options.
|
||||
* @return moodle_url the URL.
|
||||
*/
|
||||
public function get_url() {
|
||||
return new moodle_url('/mod/quiz/report.php', $this->get_url_params());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the data we get when the settings form is submitted. This includes
|
||||
* updating the fields of this class, and updating the user preferences
|
||||
* where appropriate.
|
||||
* @param object $fromform The data from $mform->get_data() from the settings form.
|
||||
*/
|
||||
public function process_settings_from_form($fromform) {
|
||||
$this->setup_from_form_data($fromform);
|
||||
$this->resolve_dependencies();
|
||||
$this->update_user_preferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up this preferences object using optional_param (using user_preferences
|
||||
* to set anything not specified by the params.
|
||||
*/
|
||||
public function process_settings_from_params() {
|
||||
$this->setup_from_user_preferences();
|
||||
$this->setup_from_params();
|
||||
$this->resolve_dependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value of the settings to pass to the settings form.
|
||||
*/
|
||||
public function get_initial_form_data() {
|
||||
$toform = new stdClass();
|
||||
$toform->attempts = $this->attempts;
|
||||
$toform->onlygraded = $this->onlygraded;
|
||||
$toform->pagesize = $this->pagesize;
|
||||
|
||||
if ($this->states) {
|
||||
foreach (self::$statefields as $field => $state) {
|
||||
$toform->$field = in_array($state, $this->states);
|
||||
}
|
||||
}
|
||||
|
||||
return $toform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields of this object from the form data.
|
||||
* @param object $fromform The data from $mform->get_data() from the settings form.
|
||||
*/
|
||||
public function setup_from_form_data($fromform) {
|
||||
$this->attempts = $fromform->attempts;
|
||||
$this->group = groups_get_activity_group($this->cm, true);
|
||||
$this->onlygraded = !empty($fromform->onlygraded);
|
||||
$this->pagesize = $fromform->pagesize;
|
||||
|
||||
$this->states = [];
|
||||
foreach (self::$statefields as $field => $state) {
|
||||
if (!empty($fromform->$field)) {
|
||||
$this->states[] = $state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields of this object from the URL parameters.
|
||||
*/
|
||||
public function setup_from_params() {
|
||||
$this->attempts = optional_param('attempts', $this->attempts, PARAM_ALPHAEXT);
|
||||
$this->group = groups_get_activity_group($this->cm, true);
|
||||
$this->onlygraded = optional_param('onlygraded', $this->onlygraded, PARAM_BOOL);
|
||||
$this->pagesize = optional_param('pagesize', $this->pagesize, PARAM_INT);
|
||||
|
||||
$states = optional_param('states', '', PARAM_ALPHAEXT);
|
||||
if (!empty($states)) {
|
||||
$this->states = explode('-', $states);
|
||||
}
|
||||
|
||||
$this->download = optional_param('download', $this->download, PARAM_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields of this object from the user's preferences.
|
||||
* (For those settings that are backed by user-preferences).
|
||||
*/
|
||||
public function setup_from_user_preferences() {
|
||||
$this->pagesize = get_user_preferences('quiz_report_pagesize', $this->pagesize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user preferences so they match the settings in this object.
|
||||
* (For those settings that are backed by user-preferences).
|
||||
*/
|
||||
public function update_user_preferences() {
|
||||
set_user_preference('quiz_report_pagesize', $this->pagesize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the settings, and remove any 'impossible' combinations.
|
||||
*/
|
||||
public function resolve_dependencies() {
|
||||
if ($this->group) {
|
||||
// Default for when a group is selected.
|
||||
if ($this->attempts === null || $this->attempts == attempts_report::ALL_WITH) {
|
||||
$this->attempts = attempts_report::ENROLLED_WITH;
|
||||
}
|
||||
|
||||
} else if (!$this->group && $this->course->id == SITEID) {
|
||||
// Force report on front page to show all, unless a group is selected.
|
||||
$this->attempts = attempts_report::ALL_WITH;
|
||||
|
||||
} else if (!in_array($this->attempts, [attempts_report::ALL_WITH, attempts_report::ENROLLED_WITH,
|
||||
attempts_report::ENROLLED_WITHOUT, attempts_report::ENROLLED_ALL])) {
|
||||
$this->attempts = attempts_report::ENROLLED_WITH;
|
||||
}
|
||||
|
||||
$cleanstates = [];
|
||||
foreach (self::$statefields as $state) {
|
||||
if (in_array($state, $this->states)) {
|
||||
$cleanstates[] = $state;
|
||||
}
|
||||
}
|
||||
$this->states = $cleanstates;
|
||||
if (count($this->states) == count(self::$statefields)) {
|
||||
// If all states have been selected, then there is no constraint
|
||||
// required in the SQL, so clear the array.
|
||||
$this->states = null;
|
||||
}
|
||||
|
||||
if (!quiz_report_can_filter_only_graded($this->quiz)) {
|
||||
// A grading mode like 'average' has been selected, so we cannot do
|
||||
// the show the attempt that gave the final grade thing.
|
||||
$this->onlygraded = false;
|
||||
}
|
||||
|
||||
if ($this->attempts == attempts_report::ENROLLED_WITHOUT) {
|
||||
$this->states = null;
|
||||
$this->onlygraded = false;
|
||||
}
|
||||
|
||||
if (!$this->is_showing_finished_attempts()) {
|
||||
$this->onlygraded = false;
|
||||
}
|
||||
|
||||
if ($this->pagesize < 1) {
|
||||
$this->pagesize = attempts_report::DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the options are such that finished attempts are being shown.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_showing_finished_attempts() {
|
||||
return $this->states === null || in_array(quiz_attempt::FINISHED, $this->states);
|
||||
}
|
||||
}
|
136
mod/quiz/classes/local/reports/attempts_report_options_form.php
Normal file
136
mod/quiz/classes/local/reports/attempts_report_options_form.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\local\reports;
|
||||
|
||||
use html_writer;
|
||||
use MoodleQuickForm;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
|
||||
/**
|
||||
* Base class for the settings form for {@see attempts_report}s.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class attempts_report_options_form extends \moodleform {
|
||||
|
||||
protected function definition() {
|
||||
$mform = $this->_form;
|
||||
|
||||
$mform->addElement('header', 'preferencespage',
|
||||
get_string('reportwhattoinclude', 'quiz'));
|
||||
|
||||
$this->standard_attempt_fields($mform);
|
||||
$this->other_attempt_fields($mform);
|
||||
|
||||
$mform->addElement('header', 'preferencesuser',
|
||||
get_string('reportdisplayoptions', 'quiz'));
|
||||
|
||||
$this->standard_preference_fields($mform);
|
||||
$this->other_preference_fields($mform);
|
||||
|
||||
$mform->addElement('submit', 'submitbutton',
|
||||
get_string('showreport', 'quiz'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the standard form fields for selecting which attempts to include in the report.
|
||||
*
|
||||
* @param MoodleQuickForm $mform the form we are building.
|
||||
*/
|
||||
protected function standard_attempt_fields(MoodleQuickForm $mform) {
|
||||
|
||||
$mform->addElement('select', 'attempts', get_string('reportattemptsfrom', 'quiz'), [
|
||||
attempts_report::ENROLLED_WITH => get_string('reportuserswith', 'quiz'),
|
||||
attempts_report::ENROLLED_WITHOUT => get_string('reportuserswithout', 'quiz'),
|
||||
attempts_report::ENROLLED_ALL => get_string('reportuserswithorwithout', 'quiz'),
|
||||
attempts_report::ALL_WITH => get_string('reportusersall', 'quiz'),
|
||||
]);
|
||||
|
||||
$stategroup = [
|
||||
$mform->createElement('advcheckbox', 'stateinprogress', '',
|
||||
get_string('stateinprogress', 'quiz')),
|
||||
$mform->createElement('advcheckbox', 'stateoverdue', '',
|
||||
get_string('stateoverdue', 'quiz')),
|
||||
$mform->createElement('advcheckbox', 'statefinished', '',
|
||||
get_string('statefinished', 'quiz')),
|
||||
$mform->createElement('advcheckbox', 'stateabandoned', '',
|
||||
get_string('stateabandoned', 'quiz')),
|
||||
];
|
||||
$mform->addGroup($stategroup, 'stateoptions',
|
||||
get_string('reportattemptsthatare', 'quiz'), [' '], false);
|
||||
$mform->setDefault('stateinprogress', 1);
|
||||
$mform->setDefault('stateoverdue', 1);
|
||||
$mform->setDefault('statefinished', 1);
|
||||
$mform->setDefault('stateabandoned', 1);
|
||||
$mform->disabledIf('stateinprogress', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('stateoverdue', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('statefinished', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('stateabandoned', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
|
||||
if (quiz_report_can_filter_only_graded($this->_customdata['quiz'])) {
|
||||
$gm = html_writer::tag('span',
|
||||
quiz_get_grading_option_name($this->_customdata['quiz']->grademethod),
|
||||
['class' => 'highlight']);
|
||||
$mform->addElement('advcheckbox', 'onlygraded', '',
|
||||
get_string('reportshowonlyfinished', 'quiz', $gm));
|
||||
$mform->disabledIf('onlygraded', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('onlygraded', 'statefinished', 'notchecked');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point to allow subclasses to add their own fields in the attempts section.
|
||||
*
|
||||
* @param MoodleQuickForm $mform the form we are building.
|
||||
*/
|
||||
protected function other_attempt_fields(MoodleQuickForm $mform) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the standard options fields to the form.
|
||||
*
|
||||
* @param MoodleQuickForm $mform the form we are building.
|
||||
*/
|
||||
protected function standard_preference_fields(MoodleQuickForm $mform) {
|
||||
$mform->addElement('text', 'pagesize', get_string('pagesize', 'quiz'));
|
||||
$mform->setType('pagesize', PARAM_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point to allow subclasses to add their own fields in the options section.
|
||||
*
|
||||
* @param MoodleQuickForm $mform the form we are building.
|
||||
*/
|
||||
protected function other_preference_fields(MoodleQuickForm $mform) {
|
||||
}
|
||||
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
if ($data['attempts'] != attempts_report::ENROLLED_WITHOUT && !(
|
||||
$data['stateinprogress'] || $data['stateoverdue'] || $data['statefinished'] || $data['stateabandoned'])) {
|
||||
$errors['stateoptions'] = get_string('reportmustselectstate', 'quiz');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
726
mod/quiz/classes/local/reports/attempts_report_table.php
Normal file
726
mod/quiz/classes/local/reports/attempts_report_table.php
Normal file
@ -0,0 +1,726 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\local\reports;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
use coding_exception;
|
||||
use context_module;
|
||||
use html_writer;
|
||||
use moodle_url;
|
||||
use popup_action;
|
||||
use question_state;
|
||||
use qubaid_condition;
|
||||
use qubaid_join;
|
||||
use qubaid_list;
|
||||
use question_engine_data_mapper;
|
||||
use quiz_attempt;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Base class for the table used by a {@see attempts_report}.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class attempts_report_table extends \table_sql {
|
||||
public $useridfield = 'userid';
|
||||
|
||||
/** @var moodle_url the URL of this report. */
|
||||
protected $reporturl;
|
||||
|
||||
/** @var array the display options. */
|
||||
protected $displayoptions;
|
||||
|
||||
/**
|
||||
* @var array information about the latest step of each question.
|
||||
* Loaded by {@see load_question_latest_steps()}, if applicable.
|
||||
*/
|
||||
protected $lateststeps = null;
|
||||
|
||||
/** @var stdClass the quiz settings for the quiz we are reporting on. */
|
||||
protected $quiz;
|
||||
|
||||
/** @var context_module the quiz context. */
|
||||
protected $context;
|
||||
|
||||
/** @var string HTML fragment to select the first/best/last attempt, if appropriate. */
|
||||
protected $qmsubselect;
|
||||
|
||||
/** @var stdClass attempts_report_options the options affecting this report. */
|
||||
protected $options;
|
||||
|
||||
/** @var \core\dml\sql_join Contains joins, wheres, params to find students
|
||||
* in the currently selected group, if applicable.
|
||||
*/
|
||||
protected $groupstudentsjoins;
|
||||
|
||||
/** @var \core\dml\sql_join Contains joins, wheres, params to find the students in the course. */
|
||||
protected $studentsjoins;
|
||||
|
||||
/** @var array the questions that comprise this quiz. */
|
||||
protected $questions;
|
||||
|
||||
/** @var bool whether to include the column with checkboxes to select each attempt. */
|
||||
protected $includecheckboxes;
|
||||
|
||||
/** @var string The toggle group name for the checkboxes in the checkbox column. */
|
||||
protected $togglegroup = 'quiz-attempts';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $uniqueid
|
||||
* @param stdClass $quiz
|
||||
* @param context_module $context
|
||||
* @param string $qmsubselect
|
||||
* @param attempts_report_options $options
|
||||
* @param \core\dml\sql_join $groupstudentsjoins Contains joins, wheres, params
|
||||
* @param \core\dml\sql_join $studentsjoins Contains joins, wheres, params
|
||||
* @param array $questions
|
||||
* @param moodle_url $reporturl
|
||||
*/
|
||||
public function __construct($uniqueid, $quiz, $context, $qmsubselect,
|
||||
attempts_report_options $options, \core\dml\sql_join $groupstudentsjoins, \core\dml\sql_join $studentsjoins,
|
||||
$questions, $reporturl) {
|
||||
parent::__construct($uniqueid);
|
||||
$this->quiz = $quiz;
|
||||
$this->context = $context;
|
||||
$this->qmsubselect = $qmsubselect;
|
||||
$this->groupstudentsjoins = $groupstudentsjoins;
|
||||
$this->studentsjoins = $studentsjoins;
|
||||
$this->questions = $questions;
|
||||
$this->includecheckboxes = $options->checkboxcolumn;
|
||||
$this->reporturl = $reporturl;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the checkbox column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_checkbox($attempt) {
|
||||
global $OUTPUT;
|
||||
|
||||
if ($attempt->attempt) {
|
||||
$checkbox = new \core\output\checkbox_toggleall($this->togglegroup, false, [
|
||||
'id' => "attemptid_{$attempt->attempt}",
|
||||
'name' => 'attemptid[]',
|
||||
'value' => $attempt->attempt,
|
||||
'label' => get_string('selectattempt', 'quiz'),
|
||||
'labelclasses' => 'accesshide',
|
||||
]);
|
||||
return $OUTPUT->render($checkbox);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the user's picture column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_picture($attempt) {
|
||||
global $OUTPUT;
|
||||
$user = new stdClass();
|
||||
$additionalfields = explode(',', implode(',', \core_user\fields::get_picture_fields()));
|
||||
$user = username_load_fields_from_object($user, $attempt, null, $additionalfields);
|
||||
$user->id = $attempt->userid;
|
||||
return $OUTPUT->user_picture($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the user's full name column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_fullname($attempt) {
|
||||
$html = parent::col_fullname($attempt);
|
||||
if ($this->is_downloading() || empty($attempt->attempt)) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
return $html . html_writer::empty_tag('br') . html_writer::link(
|
||||
new moodle_url('/mod/quiz/review.php', ['attempt' => $attempt->attempt]),
|
||||
get_string('reviewattempt', 'quiz'), ['class' => 'reviewlink']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the attempt state column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_state($attempt) {
|
||||
if (!is_null($attempt->attempt)) {
|
||||
return quiz_attempt::state_name($attempt->state);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the start time column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_timestart($attempt) {
|
||||
if ($attempt->attempt) {
|
||||
return userdate($attempt->timestart, $this->strtimeformat);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the finish time column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_timefinish($attempt) {
|
||||
if ($attempt->attempt && $attempt->timefinish) {
|
||||
return userdate($attempt->timefinish, $this->strtimeformat);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the time taken column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_duration($attempt) {
|
||||
if ($attempt->timefinish) {
|
||||
return format_time($attempt->timefinish - $attempt->timestart);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the feedback column.
|
||||
*
|
||||
* @param stdClass $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_feedbacktext($attempt) {
|
||||
if ($attempt->state != quiz_attempt::FINISHED) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
$feedback = quiz_report_feedback_for_grade(
|
||||
quiz_rescale_grade($attempt->sumgrades, $this->quiz, false),
|
||||
$this->quiz->id, $this->context);
|
||||
|
||||
if ($this->is_downloading()) {
|
||||
$feedback = strip_tags($feedback);
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
}
|
||||
|
||||
public function get_row_class($attempt) {
|
||||
if ($this->qmsubselect && $attempt->gradedattempt) {
|
||||
return 'gradedattempt';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a link to review an individual question in a popup window.
|
||||
*
|
||||
* @param string $data HTML fragment. The text to make into the link.
|
||||
* @param stdClass $attempt data for the row of the table being output.
|
||||
* @param int $slot the number used to identify this question within this usage.
|
||||
*/
|
||||
public function make_review_link($data, $attempt, $slot) {
|
||||
global $OUTPUT, $CFG;
|
||||
|
||||
$flag = '';
|
||||
if ($this->is_flagged($attempt->usageid, $slot)) {
|
||||
$flag = $OUTPUT->pix_icon('i/flagged', get_string('flagged', 'question'),
|
||||
'moodle', ['class' => 'questionflag']);
|
||||
}
|
||||
|
||||
$feedbackimg = '';
|
||||
$state = $this->slot_state($attempt, $slot);
|
||||
if ($state && $state->is_finished() && $state != question_state::$needsgrading) {
|
||||
$feedbackimg = $this->icon_for_fraction($this->slot_fraction($attempt, $slot));
|
||||
}
|
||||
|
||||
$output = html_writer::tag('span', $feedbackimg . html_writer::tag('span',
|
||||
$data, ['class' => $state->get_state_class(true)]) . $flag, ['class' => 'que']);
|
||||
|
||||
$reviewparams = ['attempt' => $attempt->attempt, 'slot' => $slot];
|
||||
if (isset($attempt->try)) {
|
||||
$reviewparams['step'] = $this->step_no_for_try($attempt->usageid, $slot, $attempt->try);
|
||||
}
|
||||
$url = new moodle_url('/mod/quiz/reviewquestion.php', $reviewparams);
|
||||
$output = $OUTPUT->action_link($url, $output,
|
||||
new popup_action('click', $url, 'reviewquestion',
|
||||
['height' => 450, 'width' => 650]),
|
||||
['title' => get_string('reviewresponse', 'quiz')]);
|
||||
|
||||
if (!empty($CFG->enableplagiarism)) {
|
||||
require_once($CFG->libdir . '/plagiarismlib.php');
|
||||
$output .= plagiarism_get_links([
|
||||
'context' => $this->context->id,
|
||||
'component' => 'qtype_'.$this->questions[$slot]->qtype,
|
||||
'cmid' => $this->context->instanceid,
|
||||
'area' => $attempt->usageid,
|
||||
'itemid' => $slot,
|
||||
'userid' => $attempt->userid]);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the question attempt state for a particular question in a particular quiz attempt.
|
||||
*
|
||||
* @param stdClass $attempt the row data.
|
||||
* @param int $slot indicates which question.
|
||||
* @return question_state the state of that question.
|
||||
*/
|
||||
protected function slot_state($attempt, $slot) {
|
||||
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
|
||||
return question_state::get($stepdata->state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Work out if a particular question in a particular attempt has been flagged.
|
||||
*
|
||||
* @param int $questionusageid used to identify the attempt of interest.
|
||||
* @param int $slot identifies which question in the attempt to check.
|
||||
* @return bool true if the question is flagged in the attempt.
|
||||
*/
|
||||
protected function is_flagged($questionusageid, $slot) {
|
||||
$stepdata = $this->lateststeps[$questionusageid][$slot];
|
||||
return $stepdata->flagged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mark (out of 1) for the question in a particular slot.
|
||||
*
|
||||
* @param stdClass $attempt the row data
|
||||
* @param int $slot which slot to check.
|
||||
* @return float the score for this question on a scale of 0 - 1.
|
||||
*/
|
||||
protected function slot_fraction($attempt, $slot) {
|
||||
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
|
||||
return $stepdata->fraction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an appropriate icon (green tick, red cross, etc.) for a grade.
|
||||
*
|
||||
* @param float $fraction grade on a scale 0..1.
|
||||
* @return string html fragment.
|
||||
*/
|
||||
protected function icon_for_fraction($fraction) {
|
||||
global $OUTPUT;
|
||||
|
||||
$feedbackclass = question_state::graded_state_for_fraction($fraction)->get_feedback_class();
|
||||
return $OUTPUT->pix_icon('i/grade_' . $feedbackclass, get_string($feedbackclass, 'question'),
|
||||
'moodle', ['class' => 'icon']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load any extra data after main query.
|
||||
*
|
||||
* At this point you can call {@see get_qubaids_condition} to get the condition
|
||||
* that limits the query to just the question usages shown in this report page or
|
||||
* alternatively for all attempts if downloading a full report.
|
||||
*/
|
||||
protected function load_extra_data() {
|
||||
$this->lateststeps = $this->load_question_latest_steps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load information about the latest state of selected questions in selected attempts.
|
||||
*
|
||||
* The results are returned as a two-dimensional array $qubaid => $slot => $dataobject.
|
||||
*
|
||||
* @param qubaid_condition|null $qubaids used to restrict which usages are included
|
||||
* in the query. See {@see qubaid_condition}.
|
||||
* @return array of records. See the SQL in this function to see the fields available.
|
||||
*/
|
||||
protected function load_question_latest_steps(qubaid_condition $qubaids = null) {
|
||||
if ($qubaids === null) {
|
||||
$qubaids = $this->get_qubaids_condition();
|
||||
}
|
||||
$dm = new question_engine_data_mapper();
|
||||
$latesstepdata = $dm->load_questions_usages_latest_steps(
|
||||
$qubaids, array_keys($this->questions));
|
||||
|
||||
$lateststeps = [];
|
||||
foreach ($latesstepdata as $step) {
|
||||
$lateststeps[$step->questionusageid][$step->slot] = $step;
|
||||
}
|
||||
|
||||
return $lateststeps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this report require loading any more data after the main query.
|
||||
*
|
||||
* @return bool should {@see query_db()} call {@see load_extra_data}?
|
||||
*/
|
||||
protected function requires_extra_data() {
|
||||
return $this->requires_latest_steps_loaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this report require the detailed information for each question from the question_attempts_steps table?
|
||||
*
|
||||
* @return bool should {@see load_extra_data} call {@see load_question_latest_steps}?
|
||||
*/
|
||||
protected function requires_latest_steps_loaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a column that depends on joining to the latest state information?
|
||||
*
|
||||
* If so, return the corresponding slot. If not, return false.
|
||||
*
|
||||
* @param string $column a column name
|
||||
* @return int|false false if no, else a slot.
|
||||
*/
|
||||
protected function is_latest_step_column($column) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any fields that might be needed when sorting on date for a particular slot.
|
||||
*
|
||||
* Note: these values are only used for sorting. The values displayed are taken
|
||||
* from $this->lateststeps loaded in load_extra_data().
|
||||
*
|
||||
* @param int $slot the slot for the column we want.
|
||||
* @param string $alias the table alias for latest state information relating to that slot.
|
||||
* @return string definitions of extra fields to add to the SELECT list of the query.
|
||||
*/
|
||||
protected function get_required_latest_state_fields($slot, $alias) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Contruct all the parts of the main database query.
|
||||
*
|
||||
* @param \core\dml\sql_join $allowedstudentsjoins (joins, wheres, params) defines allowed users for the report.
|
||||
* @return array with 4 elements [$fields, $from, $where, $params] that can be used to
|
||||
* build the actual database query.
|
||||
*/
|
||||
public function base_sql(\core\dml\sql_join $allowedstudentsjoins) {
|
||||
global $DB;
|
||||
|
||||
// Please note this uniqueid column is not the same as quiza.uniqueid.
|
||||
$fields = 'DISTINCT ' . $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid,';
|
||||
|
||||
if ($this->qmsubselect) {
|
||||
$fields .= "\n(CASE WHEN $this->qmsubselect THEN 1 ELSE 0 END) AS gradedattempt,";
|
||||
}
|
||||
|
||||
$userfieldsapi = \core_user\fields::for_identity($this->context)->with_name()
|
||||
->excluding('id', 'idnumber', 'picture', 'imagealt', 'institution', 'department', 'email');
|
||||
$userfields = $userfieldsapi->get_sql('u', true, '', '', false);
|
||||
|
||||
$fields .= '
|
||||
quiza.uniqueid AS usageid,
|
||||
quiza.id AS attempt,
|
||||
u.id AS userid,
|
||||
u.idnumber,
|
||||
u.picture,
|
||||
u.imagealt,
|
||||
u.institution,
|
||||
u.department,
|
||||
u.email,' . $userfields->selects . ',
|
||||
quiza.state,
|
||||
quiza.sumgrades,
|
||||
quiza.timefinish,
|
||||
quiza.timestart,
|
||||
CASE WHEN quiza.timefinish = 0 THEN null
|
||||
WHEN quiza.timefinish > quiza.timestart THEN quiza.timefinish - quiza.timestart
|
||||
ELSE 0 END AS duration';
|
||||
// To explain that last bit, timefinish can be non-zero and less
|
||||
// than timestart when you have two load-balanced servers with very
|
||||
// badly synchronised clocks, and a student does a really quick attempt.
|
||||
|
||||
// This part is the same for all cases. Join the users and quiz_attempts tables.
|
||||
$from = " {user} u";
|
||||
$from .= "\n{$userfields->joins}";
|
||||
$from .= "\nLEFT JOIN {quiz_attempts} quiza ON
|
||||
quiza.userid = u.id AND quiza.quiz = :quizid";
|
||||
$params = array_merge($userfields->params, ['quizid' => $this->quiz->id]);
|
||||
|
||||
if ($this->qmsubselect && $this->options->onlygraded) {
|
||||
$from .= " AND (quiza.state <> :finishedstate OR $this->qmsubselect)";
|
||||
$params['finishedstate'] = quiz_attempt::FINISHED;
|
||||
}
|
||||
|
||||
switch ($this->options->attempts) {
|
||||
case attempts_report::ALL_WITH:
|
||||
// Show all attempts, including students who are no longer in the course.
|
||||
$where = 'quiza.id IS NOT NULL AND quiza.preview = 0';
|
||||
break;
|
||||
case attempts_report::ENROLLED_WITH:
|
||||
// Show only students with attempts.
|
||||
$from .= "\n" . $allowedstudentsjoins->joins;
|
||||
$where = "quiza.preview = 0 AND quiza.id IS NOT NULL AND " . $allowedstudentsjoins->wheres;
|
||||
$params = array_merge($params, $allowedstudentsjoins->params);
|
||||
break;
|
||||
case attempts_report::ENROLLED_WITHOUT:
|
||||
// Show only students without attempts.
|
||||
$from .= "\n" . $allowedstudentsjoins->joins;
|
||||
$where = "quiza.id IS NULL AND " . $allowedstudentsjoins->wheres;
|
||||
$params = array_merge($params, $allowedstudentsjoins->params);
|
||||
break;
|
||||
case attempts_report::ENROLLED_ALL:
|
||||
// Show all students with or without attempts.
|
||||
$from .= "\n" . $allowedstudentsjoins->joins;
|
||||
$where = "(quiza.preview = 0 OR quiza.preview IS NULL) AND " . $allowedstudentsjoins->wheres;
|
||||
$params = array_merge($params, $allowedstudentsjoins->params);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->options->states) {
|
||||
[$statesql, $stateparams] = $DB->get_in_or_equal($this->options->states,
|
||||
SQL_PARAMS_NAMED, 'state');
|
||||
$params += $stateparams;
|
||||
$where .= " AND (quiza.state $statesql OR quiza.state IS NULL)";
|
||||
}
|
||||
|
||||
return [$fields, $from, $where, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets subclasses modify the SQL after the count query has been created and before the full query is.
|
||||
*
|
||||
* @param string $fields SELECT list.
|
||||
* @param string $from JOINs part of the SQL.
|
||||
* @param string $where WHERE clauses.
|
||||
* @param array $params Query params.
|
||||
* @return array with 4 elements ($fields, $from, $where, $params) as from base_sql.
|
||||
*/
|
||||
protected function update_sql_after_count($fields, $from, $where, $params) {
|
||||
return [$fields, $from, $where, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the SQL queries (count rows, and get data).
|
||||
*
|
||||
* @param \core\dml\sql_join $allowedjoins (joins, wheres, params) defines allowed users for the report.
|
||||
*/
|
||||
public function setup_sql_queries($allowedjoins) {
|
||||
[$fields, $from, $where, $params] = $this->base_sql($allowedjoins);
|
||||
|
||||
// The WHERE clause is vital here, because some parts of tablelib.php will expect to
|
||||
// add bits like ' AND x = 1' on the end, and that needs to leave to valid SQL.
|
||||
$this->set_count_sql("SELECT COUNT(1) FROM (SELECT $fields FROM $from WHERE $where) temp WHERE 1 = 1", $params);
|
||||
|
||||
[$fields, $from, $where, $params] = $this->update_sql_after_count($fields, $from, $where, $params);
|
||||
$this->set_sql($fields, $from, $where, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the information about the latest state of the question with slot
|
||||
* $slot to the query.
|
||||
*
|
||||
* The extra information is added as a join to a
|
||||
* 'table' with alias qa$slot, with columns that are a union of
|
||||
* the columns of the question_attempts and question_attempts_states tables.
|
||||
*
|
||||
* @param int $slot the question to add information for.
|
||||
*/
|
||||
protected function add_latest_state_join($slot) {
|
||||
$alias = 'qa' . $slot;
|
||||
|
||||
$fields = $this->get_required_latest_state_fields($slot, $alias);
|
||||
if (!$fields) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This condition roughly filters the list of attempts to be considered.
|
||||
// It is only used in a sub-select to help crappy databases (see MDL-30122)
|
||||
// therefore, it is better to use a very simple join, which may include
|
||||
// too many records, than to do a super-accurate join.
|
||||
$qubaids = new qubaid_join("{quiz_attempts} {$alias}quiza", "{$alias}quiza.uniqueid",
|
||||
"{$alias}quiza.quiz = :{$alias}quizid", ["{$alias}quizid" => $this->sql->params['quizid']]);
|
||||
|
||||
$dm = new question_engine_data_mapper();
|
||||
[$inlineview, $viewparams] = $dm->question_attempt_latest_state_view($alias, $qubaids);
|
||||
|
||||
$this->sql->fields .= ",\n$fields";
|
||||
$this->sql->from .= "\nLEFT JOIN $inlineview ON " .
|
||||
"$alias.questionusageid = quiza.uniqueid AND $alias.slot = :{$alias}slot";
|
||||
$this->sql->params[$alias . 'slot'] = $slot;
|
||||
$this->sql->params = array_merge($this->sql->params, $viewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an appropriate qubaid_condition for loading more data about the attempts we are displaying.
|
||||
*
|
||||
* @return qubaid_condition
|
||||
*/
|
||||
protected function get_qubaids_condition() {
|
||||
if (is_null($this->rawdata)) {
|
||||
throw new coding_exception(
|
||||
'Cannot call get_qubaids_condition until the main data has been loaded.');
|
||||
}
|
||||
|
||||
if ($this->is_downloading()) {
|
||||
// We want usages for all attempts.
|
||||
return new qubaid_join("(
|
||||
SELECT DISTINCT quiza.uniqueid
|
||||
FROM " . $this->sql->from . "
|
||||
WHERE " . $this->sql->where . "
|
||||
) quizasubquery", 'quizasubquery.uniqueid',
|
||||
"1 = 1", $this->sql->params);
|
||||
}
|
||||
|
||||
$qubaids = [];
|
||||
foreach ($this->rawdata as $attempt) {
|
||||
if ($attempt->usageid > 0) {
|
||||
$qubaids[] = $attempt->usageid;
|
||||
}
|
||||
}
|
||||
|
||||
return new qubaid_list($qubaids);
|
||||
}
|
||||
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
$doneslots = [];
|
||||
foreach ($this->get_sort_columns() as $column => $notused) {
|
||||
$slot = $this->is_latest_step_column($column);
|
||||
if ($slot && !in_array($slot, $doneslots)) {
|
||||
$this->add_latest_state_join($slot);
|
||||
$doneslots[] = $slot;
|
||||
}
|
||||
}
|
||||
|
||||
parent::query_db($pagesize, $useinitialsbar);
|
||||
|
||||
if ($this->requires_extra_data()) {
|
||||
$this->load_extra_data();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_sort_columns() {
|
||||
// Add attemptid as a final tie-break to the sort. This ensures that
|
||||
// Attempts by the same student appear in order when just sorting by name.
|
||||
$sortcolumns = parent::get_sort_columns();
|
||||
$sortcolumns['quiza.id'] = SORT_ASC;
|
||||
return $sortcolumns;
|
||||
}
|
||||
|
||||
public function wrap_html_start() {
|
||||
if ($this->is_downloading() || !$this->includecheckboxes) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $this->options->get_url();
|
||||
$url->param('sesskey', sesskey());
|
||||
|
||||
echo '<div id="tablecontainer">';
|
||||
echo '<form id="attemptsform" method="post" action="' . $url->out_omit_querystring() . '">';
|
||||
|
||||
echo html_writer::input_hidden_params($url);
|
||||
echo '<div>';
|
||||
}
|
||||
|
||||
public function wrap_html_finish() {
|
||||
global $PAGE;
|
||||
if ($this->is_downloading() || !$this->includecheckboxes) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div id="commands">';
|
||||
$this->submit_buttons();
|
||||
echo '</div>';
|
||||
|
||||
// Close the form.
|
||||
echo '</div>';
|
||||
echo '</form></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output any submit buttons required by the $this->includecheckboxes form.
|
||||
*/
|
||||
protected function submit_buttons() {
|
||||
global $PAGE;
|
||||
if (has_capability('mod/quiz:deleteattempts', $this->context)) {
|
||||
$deletebuttonparams = [
|
||||
'type' => 'submit',
|
||||
'class' => 'btn btn-secondary mr-1',
|
||||
'id' => 'deleteattemptsbutton',
|
||||
'name' => 'delete',
|
||||
'value' => get_string('deleteselected', 'quiz_overview'),
|
||||
'data-action' => 'toggle',
|
||||
'data-togglegroup' => $this->togglegroup,
|
||||
'data-toggle' => 'action',
|
||||
'disabled' => true
|
||||
];
|
||||
echo html_writer::empty_tag('input', $deletebuttonparams);
|
||||
$PAGE->requires->event_handler('#deleteattemptsbutton', 'click', 'M.util.show_confirm_dialog',
|
||||
['message' => get_string('deleteattemptcheck', 'quiz')]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the contents for the checkbox column header.
|
||||
*
|
||||
* It returns the HTML for a master \core\output\checkbox_toggleall component that selects/deselects all quiz attempts.
|
||||
*
|
||||
* @param string $columnname The name of the checkbox column.
|
||||
* @return string
|
||||
*/
|
||||
public function checkbox_col_header(string $columnname) {
|
||||
global $OUTPUT;
|
||||
|
||||
// Make sure to disable sorting on this column.
|
||||
$this->no_sorting($columnname);
|
||||
|
||||
// Build the select/deselect all control.
|
||||
$selectallid = $this->uniqueid . '-selectall-attempts';
|
||||
$selectalltext = get_string('selectall', 'quiz');
|
||||
$deselectalltext = get_string('selectnone', 'quiz');
|
||||
$mastercheckbox = new \core\output\checkbox_toggleall($this->togglegroup, true, [
|
||||
'id' => $selectallid,
|
||||
'name' => $selectallid,
|
||||
'value' => 1,
|
||||
'label' => $selectalltext,
|
||||
'labelclasses' => 'accesshide',
|
||||
'selectall' => $selectalltext,
|
||||
'deselectall' => $deselectalltext,
|
||||
]);
|
||||
|
||||
return $OUTPUT->render($mastercheckbox);
|
||||
}
|
||||
}
|
96
mod/quiz/classes/local/reports/report_base.php
Normal file
96
mod/quiz/classes/local/reports/report_base.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\local\reports;
|
||||
|
||||
use context;
|
||||
use context_module;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Base class for quiz report plugins.
|
||||
*
|
||||
* Doesn't do anything on its own -- it needs to be extended.
|
||||
* This class displays quiz reports. Because it is called from
|
||||
* within /mod/quiz/report.php you can assume that the page header
|
||||
* and footer are taken care of.
|
||||
*
|
||||
* This file can refer to itself as report.php to pass variables
|
||||
* to itself - all these will also be globally available. You must
|
||||
* pass "id=$cm->id" or q=$quiz->id", and "mode=reportname".
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class report_base {
|
||||
/** @var int special value used in place of groupid, to mean the use cannot access any groups. */
|
||||
const NO_GROUPS_ALLOWED = -2;
|
||||
|
||||
/**
|
||||
* Override this function to display the report.
|
||||
*
|
||||
* @param stdClass $quiz this quiz.
|
||||
* @param stdClass $cm the course-module for this quiz.
|
||||
* @param stdClass $course the coures we are in.
|
||||
*/
|
||||
abstract public function display($quiz, $cm, $course);
|
||||
|
||||
/**
|
||||
* Initialise some parts of $PAGE and start output.
|
||||
*
|
||||
* @param stdClass $cm the course_module information.
|
||||
* @param stdClass $course the course settings.
|
||||
* @param stdClass $quiz the quiz settings.
|
||||
* @param string $reportmode the report name.
|
||||
*/
|
||||
public function print_header_and_tabs($cm, $course, $quiz, $reportmode = 'overview') {
|
||||
global $PAGE, $OUTPUT, $CFG;
|
||||
|
||||
// Print the page header.
|
||||
$PAGE->set_title($quiz->name);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
echo $OUTPUT->header();
|
||||
$context = context_module::instance($cm->id);
|
||||
if (!$PAGE->has_secondary_navigation()) {
|
||||
echo $OUTPUT->heading(format_string($quiz->name, true, ['context' => $context]));
|
||||
}
|
||||
if (!empty($CFG->enableplagiarism)) {
|
||||
require_once($CFG->libdir . '/plagiarismlib.php');
|
||||
echo plagiarism_update_status($course, $cm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current group for the user user looking at the report.
|
||||
*
|
||||
* @param object $cm the course_module information.
|
||||
* @param object $course the course settings.
|
||||
* @param context $context the quiz context.
|
||||
* @return int the current group id, if applicable. 0 for all users,
|
||||
* NO_GROUPS_ALLOWED if the user cannot see any group.
|
||||
*/
|
||||
public function get_current_group($cm, $course, $context) {
|
||||
$groupmode = groups_get_activity_groupmode($cm, $course);
|
||||
$currentgroup = groups_get_activity_group($cm, true);
|
||||
|
||||
if ($groupmode == SEPARATEGROUPS && !$currentgroup && !has_capability('moodle/site:accessallgroups', $context)) {
|
||||
$currentgroup = self::NO_GROUPS_ALLOWED;
|
||||
}
|
||||
|
||||
return $currentgroup;
|
||||
}
|
||||
}
|
112
mod/quiz/classes/question/display_options.php
Normal file
112
mod/quiz/classes/question/display_options.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\question;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->dirroot . '/question/engine/lib.php');
|
||||
|
||||
/**
|
||||
* An extension of question_display_options that includes the extra options used by the quiz.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category question
|
||||
* @copyright 2022 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class display_options extends \question_display_options {
|
||||
/**
|
||||
* The bitmask patterns use in the review option settings.
|
||||
*
|
||||
* In the quiz settings, the review... (e.g. reviewmarks) values are
|
||||
* bit patterns that allow what is visible to be change at different times.
|
||||
* These constants define which bit is for which time.
|
||||
*
|
||||
* @var int bit used to indicate 'during the attempt'.
|
||||
*/
|
||||
const DURING = 0x10000;
|
||||
|
||||
/** @var int as above, bit used to indicate 'immediately after the attempt'. */
|
||||
const IMMEDIATELY_AFTER = 0x01000;
|
||||
|
||||
/** @var int as above, bit used to indicate 'later while the quiz is still open'. */
|
||||
const LATER_WHILE_OPEN = 0x00100;
|
||||
|
||||
/** @var int as above, bit used to indicate 'after the quiz is closed'. */
|
||||
const AFTER_CLOSE = 0x00010;
|
||||
|
||||
/**
|
||||
* @var bool if this is false, then the student is not allowed to review
|
||||
* anything about the attempt.
|
||||
*/
|
||||
public $attempt = true;
|
||||
|
||||
/**
|
||||
* @var int whether the attempt overall feedback is visible.
|
||||
*/
|
||||
public $overallfeedback = self::VISIBLE;
|
||||
|
||||
/**
|
||||
* Set up the various options from the quiz settings, and a time constant.
|
||||
*
|
||||
* @param \stdClass $quiz the quiz settings from the database.
|
||||
* @param int $when of the constants {@see DURING}, {@see IMMEDIATELY_AFTER},
|
||||
* {@see LATER_WHILE_OPEN} or {@see AFTER_CLOSE}.
|
||||
* @return display_options instance of this class set up appropriately.
|
||||
*/
|
||||
public static function make_from_quiz(\stdClass $quiz, int $when): self {
|
||||
$options = new self();
|
||||
|
||||
$options->attempt = self::extract($quiz->reviewattempt, $when, true, false);
|
||||
$options->correctness = self::extract($quiz->reviewcorrectness, $when);
|
||||
$options->marks = self::extract($quiz->reviewmarks, $when,
|
||||
self::MARK_AND_MAX, self::MAX_ONLY);
|
||||
$options->feedback = self::extract($quiz->reviewspecificfeedback, $when);
|
||||
$options->generalfeedback = self::extract($quiz->reviewgeneralfeedback, $when);
|
||||
$options->rightanswer = self::extract($quiz->reviewrightanswer, $when);
|
||||
$options->overallfeedback = self::extract($quiz->reviewoverallfeedback, $when);
|
||||
|
||||
$options->numpartscorrect = $options->feedback;
|
||||
$options->manualcomment = $options->feedback;
|
||||
|
||||
if ($quiz->questiondecimalpoints != -1) {
|
||||
$options->markdp = $quiz->questiondecimalpoints;
|
||||
} else {
|
||||
$options->markdp = $quiz->decimalpoints;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return one value or another depending on whether one bit is set.
|
||||
*
|
||||
* @param int $setting the setting to unpack (e.g. $quiz->reviewmarks).
|
||||
* @param int $when of the constants {@see DURING}, {@see IMMEDIATELY_AFTER},
|
||||
* {@see LATER_WHILE_OPEN} or {@see AFTER_CLOSE}.
|
||||
* @param bool|int $whenset value to return when the bit is set.
|
||||
* @param bool|int $whennotset value to return when the bit is set.
|
||||
* @return bool|int $whenset or $whennotset, depending.
|
||||
*/
|
||||
protected static function extract(int $setting, int $when,
|
||||
$whenset = self::VISIBLE, $whennotset = self::HIDDEN) {
|
||||
if ($setting & $when) {
|
||||
return $whenset;
|
||||
} else {
|
||||
return $whennotset;
|
||||
}
|
||||
}
|
||||
}
|
55
mod/quiz/classes/question/qubaids_for_quiz.php
Normal file
55
mod/quiz/classes/question/qubaids_for_quiz.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\question;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/engine/datalib.php');
|
||||
|
||||
/**
|
||||
* A {@see qubaid_condition} for finding all the question usages belonging to a particular quiz.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category question
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qubaids_for_quiz extends \qubaid_join {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $quizid The quiz to search.
|
||||
* @param bool $includepreviews Whether to include preview attempts
|
||||
* @param bool $onlyfinished Whether to only include finished attempts or not
|
||||
*/
|
||||
public function __construct(int $quizid, bool $includepreviews = true, bool $onlyfinished = false) {
|
||||
$where = 'quiza.quiz = :quizaquiz';
|
||||
$params = array('quizaquiz' => $quizid);
|
||||
|
||||
if (!$includepreviews) {
|
||||
$where .= ' AND preview = 0';
|
||||
}
|
||||
|
||||
if ($onlyfinished) {
|
||||
$where .= ' AND state = :statefinished';
|
||||
$params['statefinished'] = \quiz_attempt::FINISHED;
|
||||
}
|
||||
|
||||
parent::__construct('{quiz_attempts} quiza', 'quiza.uniqueid', $where, $params);
|
||||
}
|
||||
}
|
43
mod/quiz/classes/question/qubaids_for_quiz_user.php
Normal file
43
mod/quiz/classes/question/qubaids_for_quiz_user.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\question;
|
||||
|
||||
/**
|
||||
* A {@see qubaid_condition} for finding all the question usages belonging to a particular user and quiz combination.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category question
|
||||
* @copyright 2018 Andrew Nicols <andrwe@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated This class was never needed because qubaids_for_users_attempts already existed and is more flexible.
|
||||
*/
|
||||
class qubaids_for_quiz_user extends qubaids_for_users_attempts {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $quizid The quiz to search.
|
||||
* @param int $userid The user to filter on
|
||||
* @param bool $includepreviews Whether to include preview attempts
|
||||
* @param bool $onlyfinished Whether to only include finished attempts or not
|
||||
*/
|
||||
public function __construct(int $quizid, int $userid,
|
||||
bool $includepreviews = true, bool $onlyfinished = false) {
|
||||
debugging('qubaids_for_quiz_user is deprecated. Please use qubaids_for_users_attempts instead.');
|
||||
parent::__construct($quizid, $userid,
|
||||
$onlyfinished ? 'finished' : 'all', $includepreviews);
|
||||
}
|
||||
}
|
@ -14,24 +14,18 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A {@link qubaid_condition} representing all the attempts by one user at a given quiz.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category question
|
||||
* @copyright 2015 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_quiz\question;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/engine/datalib.php');
|
||||
require_once($CFG->dirroot.'/mod/quiz/attemptlib.php');
|
||||
|
||||
/**
|
||||
* A {@link qubaid_condition} representing all the attempts by one user at a given quiz.
|
||||
* A {@see qubaid_condition} representing all the attempts by one user at a given quiz.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category question
|
||||
* @copyright 2015 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@ -48,7 +42,7 @@ class qubaids_for_users_attempts extends \qubaid_join {
|
||||
*/
|
||||
public function __construct($quizid, $userid, $status = 'finished', $includepreviews = false) {
|
||||
$where = 'quiza.quiz = :quizaquiz AND quiza.userid = :userid';
|
||||
$params = array('quizaquiz' => $quizid, 'userid' => $userid);
|
||||
$params = ['quizaquiz' => $quizid, 'userid' => $userid];
|
||||
|
||||
if (!$includepreviews) {
|
||||
$where .= ' AND preview = 0';
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
namespace mod_quiz;
|
||||
use mod_quiz\question\bank\qbank_helper;
|
||||
use mod_quiz\question\qubaids_for_quiz;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@ -1043,7 +1044,7 @@ class structure {
|
||||
$previousmaxmark = $slot->maxmark;
|
||||
$slot->maxmark = $maxmark;
|
||||
$DB->update_record('quiz_slots', $slot);
|
||||
\question_engine::set_max_mark_in_attempts(new \qubaids_for_quiz($slot->quizid),
|
||||
\question_engine::set_max_mark_in_attempts(new qubaids_for_quiz($slot->quizid),
|
||||
$slot->slot, $maxmark);
|
||||
$trans->allow_commit();
|
||||
|
||||
|
@ -22,7 +22,7 @@ use context_course;
|
||||
use core_user;
|
||||
use moodle_recordset;
|
||||
use question_display_options;
|
||||
use mod_quiz_display_options;
|
||||
use mod_quiz\question\display_options;
|
||||
use quiz_attempt;
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
@ -103,7 +103,7 @@ class quiz_notify_attempt_manual_grading_completed extends \core\task\scheduled_
|
||||
|
||||
$quiz = quiz_update_effective_access($quiz, $attempt->userid);
|
||||
$attemptobj = new quiz_attempt($attempt, $quiz, $cm, $course, false);
|
||||
$options = mod_quiz_display_options::make_from_quiz($quiz, quiz_attempt_state($quiz, $attempt));
|
||||
$options = display_options::make_from_quiz($quiz, quiz_attempt_state($quiz, $attempt));
|
||||
|
||||
if ($options->manualcomment == question_display_options::HIDDEN) {
|
||||
// User cannot currently see the feedback, so don't message them.
|
||||
|
@ -23,7 +23,7 @@
|
||||
<FIELD NAME="grademethod" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="One of the values QUIZ_GRADEHIGHEST, QUIZ_GRADEAVERAGE, QUIZ_ATTEMPTFIRST or QUIZ_ATTEMPTLAST."/>
|
||||
<FIELD NAME="decimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="2" SEQUENCE="false" COMMENT="Number of decimal points to use when displaying grades."/>
|
||||
<FIELD NAME="questiondecimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="-1" SEQUENCE="false" COMMENT="Number of decimal points to use when displaying question grades. (-1 means use decimalpoints.)"/>
|
||||
<FIELD NAME="reviewattempt" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. This is a bit field, decoded by the mod_quiz_display_options class. It is formed by ORing together the constants defined there."/>
|
||||
<FIELD NAME="reviewattempt" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. This is a bit field, decoded by the \mod_quiz\question\display_options class. It is formed by ORing together the constants defined there."/>
|
||||
<FIELD NAME="reviewcorrectness" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt."/>
|
||||
<FIELD NAME="reviewmarks" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt."/>
|
||||
<FIELD NAME="reviewspecificfeedback" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt."/>
|
||||
|
@ -27,4 +27,28 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$renamedclasses = [
|
||||
// Since Moodle 4.1.
|
||||
'mod_quiz\local\views\secondary' => 'mod_quiz\navigation\views\secondary',
|
||||
// Since Moodle 4.2.
|
||||
'mod_quiz_display_options' => 'mod_quiz\question\display_options',
|
||||
'qubaids_for_quiz' => 'mod_quiz\question\qubaids_for_quiz',
|
||||
'qubaids_for_quiz_user' => 'mod_quiz\question\qubaids_for_quiz_user',
|
||||
'mod_quiz_admin_setting_browsersecurity' => 'mod_quiz\admin\browser_security_setting',
|
||||
'mod_quiz_admin_setting_grademethod' => 'mod_quiz\admin\grade_method_setting',
|
||||
'mod_quiz_admin_setting_overduehandling' => 'mod_quiz\admin\overdue_handling_setting',
|
||||
'mod_quiz_admin_review_setting' => 'mod_quiz\admin\review_setting',
|
||||
'mod_quiz_admin_setting_user_image' => 'mod_quiz\admin\user_image_setting',
|
||||
'mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_browsersecurity' =>
|
||||
'mod_quiz\adminpresets\adminpresets_browser_security_setting',
|
||||
'mod_quiz\adminpresets/adminpresets_mod_quiz_admin_setting_grademethod' =>
|
||||
'mod_quiz\adminpresets\adminpresets_grade_method_setting',
|
||||
'mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_overduehandling' =>
|
||||
'mod_quiz\adminpresets\adminpresets_overdue_handling_setting',
|
||||
'mod_quiz\adminpresets\adminpresets_mod_quiz_admin_review_setting' =>
|
||||
'mod_quiz\adminpresets\adminpresets_review_setting',
|
||||
'mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_user_image' =>
|
||||
'mod_quiz\adminpresets\adminpresets_user_image_setting',
|
||||
'quiz_default_report' => 'mod_quiz\local\reports\report_base',
|
||||
'quiz_attempts_report' => 'mod_quiz\local\reports\attempts_report',
|
||||
'mod_quiz_attempts_report_form' => 'mod_quiz\local\reports\attempts_report_options_form',
|
||||
'mod_quiz_attempts_report_options' => 'mod_quiz\local\reports\attempts_report_options',
|
||||
'quiz_attempts_report_table' => 'mod_quiz\local\reports\attempts_report_table',
|
||||
];
|
||||
|
@ -29,6 +29,9 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_quiz\question\bank\custom_view;
|
||||
use mod_quiz\question\display_options;
|
||||
use mod_quiz\question\qubaids_for_quiz;
|
||||
use mod_quiz\question\qubaids_for_users_attempts;
|
||||
use core_question\statistics\questions\all_calculated_for_qubaid_condition;
|
||||
|
||||
require_once($CFG->dirroot . '/calendar/lib.php');
|
||||
@ -414,7 +417,8 @@ function quiz_delete_all_attempts($quiz) {
|
||||
function quiz_delete_user_attempts($quiz, $user) {
|
||||
global $CFG, $DB;
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
question_engine::delete_questions_usage_by_activities(new qubaids_for_quiz_user($quiz->get_quizid(), $user->id));
|
||||
question_engine::delete_questions_usage_by_activities(new qubaids_for_users_attempts(
|
||||
$quiz->get_quizid(), $user->id, 'all'));
|
||||
$params = [
|
||||
'quiz' => $quiz->get_quizid(),
|
||||
'userid' => $user->id,
|
||||
@ -759,10 +763,10 @@ function quiz_grade_item_update($quiz, $grades = null) {
|
||||
// 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.
|
||||
$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);
|
||||
$openreviewoptions = display_options::make_from_quiz($quiz,
|
||||
display_options::LATER_WHILE_OPEN);
|
||||
$closedreviewoptions = display_options::make_from_quiz($quiz,
|
||||
display_options::AFTER_CLOSE);
|
||||
if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
|
||||
$closedreviewoptions->marks < question_display_options::MARK_AND_MAX) {
|
||||
$params['hidden'] = 1;
|
||||
@ -1115,8 +1119,8 @@ function quiz_process_options($quiz) {
|
||||
$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;
|
||||
$quiz->reviewattempt |= display_options::DURING;
|
||||
$quiz->reviewoverallfeedback &= ~display_options::DURING;
|
||||
|
||||
// Ensure that disabled checkboxes in completion settings are set to 0.
|
||||
// But only if the completion settinsg are unlocked.
|
||||
@ -1140,10 +1144,10 @@ function quiz_process_options($quiz) {
|
||||
*/
|
||||
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,
|
||||
'during' => display_options::DURING,
|
||||
'immediately' => display_options::IMMEDIATELY_AFTER,
|
||||
'open' => display_options::LATER_WHILE_OPEN,
|
||||
'closed' => display_options::AFTER_CLOSE,
|
||||
);
|
||||
|
||||
$review = 0;
|
||||
|
@ -40,6 +40,7 @@ require_once($CFG->libdir . '/filelib.php');
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
|
||||
use mod_quiz\question\bank\qbank_helper;
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
/**
|
||||
* @var int We show the countdown timer if there is less than this amount of time left before the
|
||||
@ -1450,8 +1451,8 @@ function quiz_question_edit_button($cmid, $question, $returnurl, $contentafteric
|
||||
*/
|
||||
function quiz_question_preview_url($quiz, $question, $variant = null) {
|
||||
// Get the appropriate display options.
|
||||
$displayoptions = mod_quiz_display_options::make_from_quiz($quiz,
|
||||
mod_quiz_display_options::DURING);
|
||||
$displayoptions = display_options::make_from_quiz($quiz,
|
||||
display_options::DURING);
|
||||
|
||||
$maxmark = null;
|
||||
if (isset($question->maxmark)) {
|
||||
@ -1500,33 +1501,32 @@ function quiz_get_flag_option($attempt, $context) {
|
||||
* quiz_get_review_options, not in the sense of $attempt->state.
|
||||
* @param object $quiz the quiz settings
|
||||
* @param object $attempt the quiz_attempt database row.
|
||||
* @return int one of the mod_quiz_display_options::DURING,
|
||||
* @return int one of the display_options::DURING,
|
||||
* IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants.
|
||||
*/
|
||||
function quiz_attempt_state($quiz, $attempt) {
|
||||
if ($attempt->state == quiz_attempt::IN_PROGRESS) {
|
||||
return mod_quiz_display_options::DURING;
|
||||
return display_options::DURING;
|
||||
} else if ($quiz->timeclose && time() >= $quiz->timeclose) {
|
||||
return mod_quiz_display_options::AFTER_CLOSE;
|
||||
return display_options::AFTER_CLOSE;
|
||||
} else if (time() < $attempt->timefinish + 120) {
|
||||
return mod_quiz_display_options::IMMEDIATELY_AFTER;
|
||||
return display_options::IMMEDIATELY_AFTER;
|
||||
} else {
|
||||
return mod_quiz_display_options::LATER_WHILE_OPEN;
|
||||
return display_options::LATER_WHILE_OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The the appropraite mod_quiz_display_options object for this attempt at this
|
||||
* quiz right now.
|
||||
* The appropriate display_options object for this attempt at this quiz right now.
|
||||
*
|
||||
* @param stdClass $quiz the quiz instance.
|
||||
* @param stdClass $attempt the attempt in question.
|
||||
* @param context $context the quiz context.
|
||||
*
|
||||
* @return mod_quiz_display_options
|
||||
* @return display_options
|
||||
*/
|
||||
function quiz_get_review_options($quiz, $attempt, $context) {
|
||||
$options = mod_quiz_display_options::make_from_quiz($quiz, quiz_attempt_state($quiz, $attempt));
|
||||
$options = display_options::make_from_quiz($quiz, quiz_attempt_state($quiz, $attempt));
|
||||
|
||||
$options->readonly = true;
|
||||
$options->flags = quiz_get_flag_option($attempt, $context);
|
||||
@ -1595,7 +1595,7 @@ function quiz_get_combined_reviewoptions($quiz, $attempts) {
|
||||
}
|
||||
|
||||
foreach ($attempts as $attempt) {
|
||||
$attemptoptions = mod_quiz_display_options::make_from_quiz($quiz,
|
||||
$attemptoptions = display_options::make_from_quiz($quiz,
|
||||
quiz_attempt_state($quiz, $attempt));
|
||||
foreach ($fields as $field) {
|
||||
$someoptions->$field = $someoptions->$field || $attemptoptions->$field;
|
||||
@ -2066,137 +2066,6 @@ function quiz_get_js_module() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An extension of question_display_options that includes the extra options used
|
||||
* by the quiz.
|
||||
*
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_display_options extends question_display_options {
|
||||
/**#@+
|
||||
* @var integer bits used to indicate various times in relation to a
|
||||
* quiz attempt.
|
||||
*/
|
||||
const DURING = 0x10000;
|
||||
const IMMEDIATELY_AFTER = 0x01000;
|
||||
const LATER_WHILE_OPEN = 0x00100;
|
||||
const AFTER_CLOSE = 0x00010;
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* @var boolean if this is false, then the student is not allowed to review
|
||||
* anything about the attempt.
|
||||
*/
|
||||
public $attempt = true;
|
||||
|
||||
/**
|
||||
* @var boolean if this is false, then the student is not allowed to review
|
||||
* anything about the attempt.
|
||||
*/
|
||||
public $overallfeedback = self::VISIBLE;
|
||||
|
||||
/**
|
||||
* Set up the various options from the quiz settings, and a time constant.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $one of the {@link DURING}, {@link IMMEDIATELY_AFTER},
|
||||
* {@link LATER_WHILE_OPEN} or {@link AFTER_CLOSE} constants.
|
||||
* @return mod_quiz_display_options set up appropriately.
|
||||
*/
|
||||
public static function make_from_quiz($quiz, $when) {
|
||||
$options = new self();
|
||||
|
||||
$options->attempt = self::extract($quiz->reviewattempt, $when, true, false);
|
||||
$options->correctness = self::extract($quiz->reviewcorrectness, $when);
|
||||
$options->marks = self::extract($quiz->reviewmarks, $when,
|
||||
self::MARK_AND_MAX, self::MAX_ONLY);
|
||||
$options->feedback = self::extract($quiz->reviewspecificfeedback, $when);
|
||||
$options->generalfeedback = self::extract($quiz->reviewgeneralfeedback, $when);
|
||||
$options->rightanswer = self::extract($quiz->reviewrightanswer, $when);
|
||||
$options->overallfeedback = self::extract($quiz->reviewoverallfeedback, $when);
|
||||
|
||||
$options->numpartscorrect = $options->feedback;
|
||||
$options->manualcomment = $options->feedback;
|
||||
|
||||
if ($quiz->questiondecimalpoints != -1) {
|
||||
$options->markdp = $quiz->questiondecimalpoints;
|
||||
} else {
|
||||
$options->markdp = $quiz->decimalpoints;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected static function extract($bitmask, $bit,
|
||||
$whenset = self::VISIBLE, $whennotset = self::HIDDEN) {
|
||||
if ($bitmask & $bit) {
|
||||
return $whenset;
|
||||
} else {
|
||||
return $whennotset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link qubaid_condition} for finding all the question usages belonging to
|
||||
* a particular quiz.
|
||||
*
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qubaids_for_quiz extends qubaid_join {
|
||||
public function __construct($quizid, $includepreviews = true, $onlyfinished = false) {
|
||||
$where = 'quiza.quiz = :quizaquiz';
|
||||
$params = array('quizaquiz' => $quizid);
|
||||
|
||||
if (!$includepreviews) {
|
||||
$where .= ' AND preview = 0';
|
||||
}
|
||||
|
||||
if ($onlyfinished) {
|
||||
$where .= ' AND state = :statefinished';
|
||||
$params['statefinished'] = quiz_attempt::FINISHED;
|
||||
}
|
||||
|
||||
parent::__construct('{quiz_attempts} quiza', 'quiza.uniqueid', $where, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link qubaid_condition} for finding all the question usages belonging to a particular user and quiz combination.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrwe@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qubaids_for_quiz_user extends qubaid_join {
|
||||
/**
|
||||
* Constructor for this qubaid.
|
||||
*
|
||||
* @param int $quizid The quiz to search.
|
||||
* @param int $userid The user to filter on
|
||||
* @param bool $includepreviews Whether to include preview attempts
|
||||
* @param bool $onlyfinished Whether to only include finished attempts or not
|
||||
*/
|
||||
public function __construct($quizid, $userid, $includepreviews = true, $onlyfinished = false) {
|
||||
$where = 'quiza.quiz = :quizaquiz AND quiza.userid = :quizauserid';
|
||||
$params = [
|
||||
'quizaquiz' => $quizid,
|
||||
'quizauserid' => $userid,
|
||||
];
|
||||
|
||||
if (!$includepreviews) {
|
||||
$where .= ' AND preview = 0';
|
||||
}
|
||||
|
||||
if ($onlyfinished) {
|
||||
$where .= ' AND state = :statefinished';
|
||||
$params['statefinished'] = quiz_attempt::FINISHED;
|
||||
}
|
||||
|
||||
parent::__construct('{quiz_attempts} quiza', 'quiza.uniqueid', $where, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a textual representation of a question for display.
|
||||
*
|
||||
|
@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->dirroot . '/course/moodleform_mod.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
/**
|
||||
* Settings form for the quiz module.
|
||||
@ -204,13 +205,13 @@ class mod_quiz_mod_form extends moodleform_mod {
|
||||
|
||||
// Review options.
|
||||
$this->add_review_options_group($mform, $quizconfig, 'during',
|
||||
mod_quiz_display_options::DURING, true);
|
||||
display_options::DURING, true);
|
||||
$this->add_review_options_group($mform, $quizconfig, 'immediately',
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER);
|
||||
display_options::IMMEDIATELY_AFTER);
|
||||
$this->add_review_options_group($mform, $quizconfig, 'open',
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN);
|
||||
display_options::LATER_WHILE_OPEN);
|
||||
$this->add_review_options_group($mform, $quizconfig, 'closed',
|
||||
mod_quiz_display_options::AFTER_CLOSE);
|
||||
display_options::AFTER_CLOSE);
|
||||
|
||||
foreach ($behaviours as $behaviour => $notused) {
|
||||
$unusedoptions = question_engine::get_behaviour_unused_display_options($behaviour);
|
||||
@ -459,13 +460,13 @@ class mod_quiz_mod_form extends moodleform_mod {
|
||||
}
|
||||
|
||||
$this->preprocessing_review_settings($toform, 'during',
|
||||
mod_quiz_display_options::DURING);
|
||||
display_options::DURING);
|
||||
$this->preprocessing_review_settings($toform, 'immediately',
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER);
|
||||
display_options::IMMEDIATELY_AFTER);
|
||||
$this->preprocessing_review_settings($toform, 'open',
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN);
|
||||
display_options::LATER_WHILE_OPEN);
|
||||
$this->preprocessing_review_settings($toform, 'closed',
|
||||
mod_quiz_display_options::AFTER_CLOSE);
|
||||
display_options::AFTER_CLOSE);
|
||||
$toform['attemptduring'] = true;
|
||||
$toform['overallfeedbackduring'] = false;
|
||||
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
|
||||
/**
|
||||
* The renderer for the quiz module.
|
||||
@ -41,12 +43,12 @@ class mod_quiz_renderer extends plugin_renderer_base {
|
||||
* @param int $page the current page number
|
||||
* @param bool $showall whether to show entire attempt on one page.
|
||||
* @param bool $lastpage if true the current page is the last page.
|
||||
* @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options.
|
||||
* @param display_options $displayoptions instance of display_options.
|
||||
* @param array $summarydata contains all table data
|
||||
* @return $output containing html data.
|
||||
*/
|
||||
public function review_page(quiz_attempt $attemptobj, $slots, $page, $showall,
|
||||
$lastpage, mod_quiz_display_options $displayoptions,
|
||||
$lastpage, display_options $displayoptions,
|
||||
$summarydata) {
|
||||
|
||||
$output = '';
|
||||
@ -67,12 +69,12 @@ class mod_quiz_renderer extends plugin_renderer_base {
|
||||
* @param quiz_attempt $attemptobj an instance of quiz_attempt.
|
||||
* @param int $slot which question to display.
|
||||
* @param int $seq which step of the question attempt to show. null = latest.
|
||||
* @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options.
|
||||
* @param display_options $displayoptions instance of display_options.
|
||||
* @param array $summarydata contains all table data
|
||||
* @return $output containing html data.
|
||||
*/
|
||||
public function review_question_page(quiz_attempt $attemptobj, $slot, $seq,
|
||||
mod_quiz_display_options $displayoptions, $summarydata) {
|
||||
display_options $displayoptions, $summarydata) {
|
||||
|
||||
$output = '';
|
||||
$output .= $this->header();
|
||||
@ -177,10 +179,10 @@ class mod_quiz_renderer extends plugin_renderer_base {
|
||||
* @param array $slots array of intgers relating to questions
|
||||
* @param int $page current page number
|
||||
* @param bool $showall if true shows attempt on single page
|
||||
* @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options
|
||||
* @param display_options $displayoptions instance of display_options
|
||||
*/
|
||||
public function questions(quiz_attempt $attemptobj, $reviewing, $slots, $page, $showall,
|
||||
mod_quiz_display_options $displayoptions) {
|
||||
display_options $displayoptions) {
|
||||
$output = '';
|
||||
foreach ($slots as $slot) {
|
||||
$output .= $attemptobj->render_question($slot, $reviewing, $this,
|
||||
@ -194,7 +196,7 @@ class mod_quiz_renderer extends plugin_renderer_base {
|
||||
*
|
||||
* @param array $summarydata contain row data for table
|
||||
* @param int $page current page number
|
||||
* @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options
|
||||
* @param display_options $displayoptions instance of display_options
|
||||
* @param $content contains each question
|
||||
* @param quiz_attempt $attemptobj instance of quiz_attempt
|
||||
* @param bool $showall if true display attempt on one page
|
||||
@ -662,7 +664,7 @@ class mod_quiz_renderer extends plugin_renderer_base {
|
||||
* Create the summary page
|
||||
*
|
||||
* @param quiz_attempt $attemptobj
|
||||
* @param mod_quiz_display_options $displayoptions
|
||||
* @param display_options $displayoptions
|
||||
*/
|
||||
public function summary_page($attemptobj, $displayoptions) {
|
||||
$output = '';
|
||||
@ -680,7 +682,7 @@ class mod_quiz_renderer extends plugin_renderer_base {
|
||||
* Generates the table of summarydata
|
||||
*
|
||||
* @param quiz_attempt $attemptobj
|
||||
* @param mod_quiz_display_options $displayoptions
|
||||
* @param display_options $displayoptions
|
||||
*/
|
||||
public function summary_table($attemptobj, $displayoptions) {
|
||||
// Prepare the summary table header.
|
||||
|
@ -27,7 +27,6 @@ define('NO_OUTPUT_BUFFERING', true);
|
||||
require_once(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/default.php');
|
||||
|
||||
$id = optional_param('id', 0, PARAM_INT);
|
||||
$q = optional_param('q', 0, PARAM_INT);
|
||||
|
@ -15,371 +15,11 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The file defines a base class that can be used to build a report like the
|
||||
* overview or responses report, that has one row per attempt.
|
||||
* File only retained to prevent fatal errors in code that tries to require/include this.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-76612 delete this file as part of Moodle 4.6 development.
|
||||
* @deprecated This file is no longer required in Moodle 4.2+.
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Base class for quiz reports that are basically a table with one row for each attempt.
|
||||
*
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class quiz_attempts_report extends quiz_default_report {
|
||||
/** @var int default page size for reports. */
|
||||
const DEFAULT_PAGE_SIZE = 30;
|
||||
|
||||
/** @var string constant used for the options, means all users with attempts. */
|
||||
const ALL_WITH = 'all_with';
|
||||
/** @var string constant used for the options, means only enrolled users with attempts. */
|
||||
const ENROLLED_WITH = 'enrolled_with';
|
||||
/** @var string constant used for the options, means only enrolled users without attempts. */
|
||||
const ENROLLED_WITHOUT = 'enrolled_without';
|
||||
/** @var string constant used for the options, means all enrolled users. */
|
||||
const ENROLLED_ALL = 'enrolled_any';
|
||||
|
||||
/** @var string the mode this report is. */
|
||||
protected $mode;
|
||||
|
||||
/** @var context_module the quiz context. */
|
||||
protected $context;
|
||||
|
||||
/** @var mod_quiz_attempts_report_form The settings form to use. */
|
||||
protected $form;
|
||||
|
||||
/** @var string SQL fragment for selecting the attempt that gave the final grade,
|
||||
* if applicable. */
|
||||
protected $qmsubselect;
|
||||
|
||||
/** @var boolean caches the results of {@link should_show_grades()}. */
|
||||
protected $showgrades = null;
|
||||
|
||||
/**
|
||||
* Initialise various aspects of this report.
|
||||
*
|
||||
* @param string $mode
|
||||
* @param string $formclass
|
||||
* @param object $quiz
|
||||
* @param object $cm
|
||||
* @param object $course
|
||||
* @return array with four elements:
|
||||
* 0 => integer the current group id (0 for none).
|
||||
* 1 => \core\dml\sql_join Contains joins, wheres, params for all the students in this course.
|
||||
* 2 => \core\dml\sql_join Contains joins, wheres, params for all the students in the current group.
|
||||
* 3 => \core\dml\sql_join Contains joins, wheres, params for all the students to show in the report.
|
||||
* Will be the same as either element 1 or 2.
|
||||
*/
|
||||
public function init($mode, $formclass, $quiz, $cm, $course) {
|
||||
$this->mode = $mode;
|
||||
|
||||
$this->context = context_module::instance($cm->id);
|
||||
|
||||
list($currentgroup, $studentsjoins, $groupstudentsjoins, $allowedjoins) = $this->get_students_joins(
|
||||
$cm, $course);
|
||||
|
||||
$this->qmsubselect = quiz_report_qm_filter_select($quiz);
|
||||
|
||||
$this->form = new $formclass($this->get_base_url(),
|
||||
array('quiz' => $quiz, 'currentgroup' => $currentgroup, 'context' => $this->context));
|
||||
|
||||
return array($currentgroup, $studentsjoins, $groupstudentsjoins, $allowedjoins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL for this report.
|
||||
* @return moodle_url the URL.
|
||||
*/
|
||||
protected function get_base_url() {
|
||||
return new moodle_url('/mod/quiz/report.php',
|
||||
array('id' => $this->context->instanceid, 'mode' => $this->mode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sql fragments (joins) which can be used to build queries that
|
||||
* will select an appropriate set of students to show in the reports.
|
||||
*
|
||||
* @param object $cm the course module.
|
||||
* @param object $course the course settings.
|
||||
* @return array with four elements:
|
||||
* 0 => integer the current group id (0 for none).
|
||||
* 1 => \core\dml\sql_join Contains joins, wheres, params for all the students in this course.
|
||||
* 2 => \core\dml\sql_join Contains joins, wheres, params for all the students in the current group.
|
||||
* 3 => \core\dml\sql_join Contains joins, wheres, params for all the students to show in the report.
|
||||
* Will be the same as either element 1 or 2.
|
||||
*/
|
||||
protected function get_students_joins($cm, $course = null) {
|
||||
$currentgroup = $this->get_current_group($cm, $course, $this->context);
|
||||
|
||||
$empty = new \core\dml\sql_join();
|
||||
if ($currentgroup == self::NO_GROUPS_ALLOWED) {
|
||||
return array($currentgroup, $empty, $empty, $empty);
|
||||
}
|
||||
|
||||
$studentsjoins = get_enrolled_with_capabilities_join($this->context, '',
|
||||
array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'));
|
||||
|
||||
if (empty($currentgroup)) {
|
||||
return array($currentgroup, $studentsjoins, $empty, $studentsjoins);
|
||||
}
|
||||
|
||||
// We have a currently selected group.
|
||||
$groupstudentsjoins = get_enrolled_with_capabilities_join($this->context, '',
|
||||
array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'), $currentgroup);
|
||||
|
||||
return array($currentgroup, $studentsjoins, $groupstudentsjoins, $groupstudentsjoins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the things you commonly want at the top of a quiz report.
|
||||
*
|
||||
* Calls through to {@link print_header_and_tabs()} and then
|
||||
* outputs the standard group selector, number of attempts summary,
|
||||
* and messages to cover common cases when the report can't be shown.
|
||||
*
|
||||
* @param stdClass $cm the course_module information.
|
||||
* @param stdClass $course the course settings.
|
||||
* @param stdClass $quiz the quiz settings.
|
||||
* @param mod_quiz_attempts_report_options $options the current report settings.
|
||||
* @param int $currentgroup the current group.
|
||||
* @param bool $hasquestions whether there are any questions in the quiz.
|
||||
* @param bool $hasstudents whether there are any relevant students.
|
||||
*/
|
||||
protected function print_standard_header_and_messages($cm, $course, $quiz,
|
||||
$options, $currentgroup, $hasquestions, $hasstudents) {
|
||||
global $OUTPUT;
|
||||
|
||||
$this->print_header_and_tabs($cm, $course, $quiz, $this->mode);
|
||||
|
||||
if (groups_get_activity_groupmode($cm)) {
|
||||
// Groups are being used, so output the group selector if we are not downloading.
|
||||
groups_print_activity_menu($cm, $options->get_url());
|
||||
}
|
||||
|
||||
// Print information on the number of existing attempts.
|
||||
if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) {
|
||||
echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>';
|
||||
}
|
||||
|
||||
if (!$hasquestions) {
|
||||
echo quiz_no_questions_message($quiz, $cm, $this->context);
|
||||
} else if ($currentgroup == self::NO_GROUPS_ALLOWED) {
|
||||
echo $OUTPUT->notification(get_string('notingroup'));
|
||||
} else if (!$hasstudents) {
|
||||
echo $OUTPUT->notification(get_string('nostudentsyet'));
|
||||
} else if ($currentgroup && !$this->hasgroupstudents) {
|
||||
echo $OUTPUT->notification(get_string('nostudentsingroup'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the user-related columns to the $columns and $headers arrays.
|
||||
* @param table_sql $table the table being constructed.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
*/
|
||||
protected function add_user_columns($table, &$columns, &$headers) {
|
||||
global $CFG;
|
||||
if (!$table->is_downloading() && $CFG->grade_report_showuserimage) {
|
||||
$columns[] = 'picture';
|
||||
$headers[] = '';
|
||||
}
|
||||
if (!$table->is_downloading()) {
|
||||
$columns[] = 'fullname';
|
||||
$headers[] = get_string('name');
|
||||
} else {
|
||||
$columns[] = 'lastname';
|
||||
$headers[] = get_string('lastname');
|
||||
$columns[] = 'firstname';
|
||||
$headers[] = get_string('firstname');
|
||||
}
|
||||
|
||||
$extrafields = \core_user\fields::get_identity_fields($this->context);
|
||||
foreach ($extrafields as $field) {
|
||||
$columns[] = $field;
|
||||
$headers[] = \core_user\fields::get_display_name($field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the display options for the user-related columns in the table.
|
||||
* @param table_sql $table the table being constructed.
|
||||
*/
|
||||
protected function configure_user_columns($table) {
|
||||
$table->column_suppress('picture');
|
||||
$table->column_suppress('fullname');
|
||||
|
||||
$extrafields = \core_user\fields::get_identity_fields($this->context);
|
||||
foreach ($extrafields as $field) {
|
||||
$table->column_suppress($field);
|
||||
}
|
||||
|
||||
$table->column_class('picture', 'picture');
|
||||
$table->column_class('lastname', 'bold');
|
||||
$table->column_class('firstname', 'bold');
|
||||
$table->column_class('fullname', 'bold');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the state column to the $columns and $headers arrays.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
*/
|
||||
protected function add_state_column(&$columns, &$headers) {
|
||||
$columns[] = 'state';
|
||||
$headers[] = get_string('attemptstate', 'quiz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the time-related columns to the $columns and $headers arrays.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
*/
|
||||
protected function add_time_columns(&$columns, &$headers) {
|
||||
$columns[] = 'timestart';
|
||||
$headers[] = get_string('startedon', 'quiz');
|
||||
|
||||
$columns[] = 'timefinish';
|
||||
$headers[] = get_string('timecompleted', 'quiz');
|
||||
|
||||
$columns[] = 'duration';
|
||||
$headers[] = get_string('attemptduration', 'quiz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the grade and feedback columns, if applicable, to the $columns
|
||||
* and $headers arrays.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param bool $usercanseegrades whether the user is allowed to see grades for this quiz.
|
||||
* @param array $columns the list of columns. Added to.
|
||||
* @param array $headers the columns headings. Added to.
|
||||
* @param bool $includefeedback whether to include the feedbacktext columns
|
||||
*/
|
||||
protected function add_grade_columns($quiz, $usercanseegrades, &$columns, &$headers, $includefeedback = true) {
|
||||
if ($usercanseegrades) {
|
||||
$columns[] = 'sumgrades';
|
||||
$headers[] = get_string('grade', 'quiz') . '/' .
|
||||
quiz_format_grade($quiz, $quiz->grade);
|
||||
}
|
||||
|
||||
if ($includefeedback && quiz_has_feedback($quiz)) {
|
||||
$columns[] = 'feedbacktext';
|
||||
$headers[] = get_string('feedback', 'quiz');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the table.
|
||||
* @param table_sql $table the table being constructed.
|
||||
* @param array $columns the list of columns.
|
||||
* @param array $headers the columns headings.
|
||||
* @param moodle_url $reporturl the URL of this report.
|
||||
* @param mod_quiz_attempts_report_options $options the display options.
|
||||
* @param bool $collapsible whether to allow columns in the report to be collapsed.
|
||||
*/
|
||||
protected function set_up_table_columns($table, $columns, $headers, $reporturl,
|
||||
mod_quiz_attempts_report_options $options, $collapsible) {
|
||||
$table->define_columns($columns);
|
||||
$table->define_headers($headers);
|
||||
$table->sortable(true, 'uniqueid');
|
||||
|
||||
$table->define_baseurl($options->get_url());
|
||||
|
||||
$this->configure_user_columns($table);
|
||||
|
||||
$table->no_sorting('feedbacktext');
|
||||
$table->column_class('sumgrades', 'bold');
|
||||
|
||||
$table->set_attribute('id', 'attempts');
|
||||
|
||||
$table->collapsible($collapsible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any submitted actions.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param object $cm the cm object for the quiz.
|
||||
* @param int $currentgroup the currently selected group.
|
||||
* @param \core\dml\sql_join $groupstudentsjoins (joins, wheres, params) the students in the current group.
|
||||
* @param \core\dml\sql_join $allowedjoins (joins, wheres, params) the users whose attempt this user is allowed to modify.
|
||||
* @param moodle_url $redirecturl where to redircet to after a successful action.
|
||||
*/
|
||||
protected function process_actions($quiz, $cm, $currentgroup, \core\dml\sql_join $groupstudentsjoins,
|
||||
\core\dml\sql_join $allowedjoins, $redirecturl) {
|
||||
if (empty($currentgroup) || $this->hasgroupstudents) {
|
||||
if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) {
|
||||
if ($attemptids = optional_param_array('attemptid', array(), PARAM_INT)) {
|
||||
require_capability('mod/quiz:deleteattempts', $this->context);
|
||||
$this->delete_selected_attempts($quiz, $cm, $attemptids, $allowedjoins);
|
||||
redirect($redirecturl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the quiz attempts
|
||||
* @param object $quiz the quiz settings. Attempts that don't belong to
|
||||
* this quiz are not deleted.
|
||||
* @param object $cm the course_module object.
|
||||
* @param array $attemptids the list of attempt ids to delete.
|
||||
* @param \core\dml\sql_join $allowedjoins (joins, wheres, params) This list of userids that are visible in the report.
|
||||
* Users can only delete attempts that they are allowed to see in the report.
|
||||
* Empty means all users.
|
||||
*/
|
||||
protected function delete_selected_attempts($quiz, $cm, $attemptids, \core\dml\sql_join $allowedjoins) {
|
||||
global $DB;
|
||||
|
||||
foreach ($attemptids as $attemptid) {
|
||||
if (empty($allowedjoins->joins)) {
|
||||
$sql = "SELECT quiza.*
|
||||
FROM {quiz_attempts} quiza
|
||||
JOIN {user} u ON u.id = quiza.userid
|
||||
WHERE quiza.id = :attemptid";
|
||||
} else {
|
||||
$sql = "SELECT quiza.*
|
||||
FROM {quiz_attempts} quiza
|
||||
JOIN {user} u ON u.id = quiza.userid
|
||||
{$allowedjoins->joins}
|
||||
WHERE {$allowedjoins->wheres} AND quiza.id = :attemptid";
|
||||
}
|
||||
$params = $allowedjoins->params + array('attemptid' => $attemptid);
|
||||
$attempt = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE);
|
||||
if (!$attempt || $attempt->quiz != $quiz->id || $attempt->preview != 0) {
|
||||
// Ensure the attempt exists, belongs to this quiz and belongs to
|
||||
// a student included in the report. If not skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the course module id before calling quiz_delete_attempt().
|
||||
$quiz->cmid = $cm->id;
|
||||
quiz_delete_attempt($attempt, $quiz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about which students to show in the report.
|
||||
* @param object $cm the coures module.
|
||||
* @param object $course the course settings.
|
||||
* @return array with four elements:
|
||||
* 0 => integer the current group id (0 for none).
|
||||
* 1 => array ids of all the students in this course.
|
||||
* 2 => array ids of all the students in the current group.
|
||||
* 3 => array ids of all the students to show in the report. Will be the
|
||||
* same as either element 1 or 2.
|
||||
* @deprecated since Moodle 3.2 Please use get_students_joins() instead.
|
||||
*/
|
||||
protected function load_relevant_students($cm, $course = null) {
|
||||
$msg = 'The function load_relevant_students() is deprecated. Please use get_students_joins() instead.';
|
||||
throw new coding_exception($msg);
|
||||
}
|
||||
}
|
||||
debugging('This file is no longer required in Moodle 4.2+. Please do not include/require it.', DEBUG_DEVELOPER);
|
||||
|
@ -15,106 +15,11 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Base class for the settings form for {@link quiz_attempts_report}s.
|
||||
* File only retained to prevent fatal errors in code that tries to require/include this.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-76612 delete this file as part of Moodle 4.6 development.
|
||||
* @deprecated This file is no longer required in Moodle 4.2+.
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Base class for the settings form for {@link quiz_attempts_report}s.
|
||||
*
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class mod_quiz_attempts_report_form extends moodleform {
|
||||
|
||||
protected function definition() {
|
||||
$mform = $this->_form;
|
||||
|
||||
$mform->addElement('header', 'preferencespage',
|
||||
get_string('reportwhattoinclude', 'quiz'));
|
||||
|
||||
$this->standard_attempt_fields($mform);
|
||||
$this->other_attempt_fields($mform);
|
||||
|
||||
$mform->addElement('header', 'preferencesuser',
|
||||
get_string('reportdisplayoptions', 'quiz'));
|
||||
|
||||
$this->standard_preference_fields($mform);
|
||||
$this->other_preference_fields($mform);
|
||||
|
||||
$mform->addElement('submit', 'submitbutton',
|
||||
get_string('showreport', 'quiz'));
|
||||
}
|
||||
|
||||
protected function standard_attempt_fields(MoodleQuickForm $mform) {
|
||||
|
||||
$mform->addElement('select', 'attempts', get_string('reportattemptsfrom', 'quiz'), array(
|
||||
quiz_attempts_report::ENROLLED_WITH => get_string('reportuserswith', 'quiz'),
|
||||
quiz_attempts_report::ENROLLED_WITHOUT => get_string('reportuserswithout', 'quiz'),
|
||||
quiz_attempts_report::ENROLLED_ALL => get_string('reportuserswithorwithout', 'quiz'),
|
||||
quiz_attempts_report::ALL_WITH => get_string('reportusersall', 'quiz'),
|
||||
));
|
||||
|
||||
$stategroup = array(
|
||||
$mform->createElement('advcheckbox', 'stateinprogress', '',
|
||||
get_string('stateinprogress', 'quiz')),
|
||||
$mform->createElement('advcheckbox', 'stateoverdue', '',
|
||||
get_string('stateoverdue', 'quiz')),
|
||||
$mform->createElement('advcheckbox', 'statefinished', '',
|
||||
get_string('statefinished', 'quiz')),
|
||||
$mform->createElement('advcheckbox', 'stateabandoned', '',
|
||||
get_string('stateabandoned', 'quiz')),
|
||||
);
|
||||
$mform->addGroup($stategroup, 'stateoptions',
|
||||
get_string('reportattemptsthatare', 'quiz'), array(' '), false);
|
||||
$mform->setDefault('stateinprogress', 1);
|
||||
$mform->setDefault('stateoverdue', 1);
|
||||
$mform->setDefault('statefinished', 1);
|
||||
$mform->setDefault('stateabandoned', 1);
|
||||
$mform->disabledIf('stateinprogress', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('stateoverdue', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('statefinished', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('stateabandoned', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
|
||||
if (quiz_report_can_filter_only_graded($this->_customdata['quiz'])) {
|
||||
$gm = html_writer::tag('span',
|
||||
quiz_get_grading_option_name($this->_customdata['quiz']->grademethod),
|
||||
array('class' => 'highlight'));
|
||||
$mform->addElement('advcheckbox', 'onlygraded', '',
|
||||
get_string('reportshowonlyfinished', 'quiz', $gm));
|
||||
$mform->disabledIf('onlygraded', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('onlygraded', 'statefinished', 'notchecked');
|
||||
}
|
||||
}
|
||||
|
||||
protected function other_attempt_fields(MoodleQuickForm $mform) {
|
||||
}
|
||||
|
||||
protected function standard_preference_fields(MoodleQuickForm $mform) {
|
||||
$mform->addElement('text', 'pagesize', get_string('pagesize', 'quiz'));
|
||||
$mform->setType('pagesize', PARAM_INT);
|
||||
}
|
||||
|
||||
protected function other_preference_fields(MoodleQuickForm $mform) {
|
||||
}
|
||||
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
if ($data['attempts'] != quiz_attempts_report::ENROLLED_WITHOUT && !(
|
||||
$data['stateinprogress'] || $data['stateoverdue'] || $data['statefinished'] || $data['stateabandoned'])) {
|
||||
$errors['stateoptions'] = get_string('reportmustselectstate', 'quiz');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
debugging('This file is no longer required in Moodle 4.2+. Please do not include/require it.', DEBUG_DEVELOPER);
|
||||
|
@ -15,277 +15,11 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Base class for the options that control what is visible in an {@link quiz_attempts_report}.
|
||||
* File only retained to prevent fatal errors in code that tries to require/include this.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-76612 delete this file as part of Moodle 4.6 development.
|
||||
* @deprecated This file is no longer required in Moodle 4.2+.
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Base class for the options that control what is visible in an {@link quiz_attempts_report}.
|
||||
*
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_attempts_report_options {
|
||||
|
||||
/** @var string the report mode. */
|
||||
public $mode;
|
||||
|
||||
/** @var object the settings for the quiz being reported on. */
|
||||
public $quiz;
|
||||
|
||||
/** @var object the course module objects for the quiz being reported on. */
|
||||
public $cm;
|
||||
|
||||
/** @var object the course settings for the course the quiz is in. */
|
||||
public $course;
|
||||
|
||||
/**
|
||||
* @var array form field name => corresponding quiz_attempt:: state constant.
|
||||
*/
|
||||
protected static $statefields = array(
|
||||
'stateinprogress' => quiz_attempt::IN_PROGRESS,
|
||||
'stateoverdue' => quiz_attempt::OVERDUE,
|
||||
'statefinished' => quiz_attempt::FINISHED,
|
||||
'stateabandoned' => quiz_attempt::ABANDONED,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string quiz_attempts_report::ALL_WITH or quiz_attempts_report::ENROLLED_WITH
|
||||
* quiz_attempts_report::ENROLLED_WITHOUT or quiz_attempts_report::ENROLLED_ALL
|
||||
*/
|
||||
public $attempts = quiz_attempts_report::ENROLLED_WITH;
|
||||
|
||||
/** @var int the currently selected group. 0 if no group is selected. */
|
||||
public $group = 0;
|
||||
|
||||
/**
|
||||
* @var array|null of quiz_attempt::IN_PROGRESS, etc. constants. null means
|
||||
* no restriction.
|
||||
*/
|
||||
public $states = array(quiz_attempt::IN_PROGRESS, quiz_attempt::OVERDUE,
|
||||
quiz_attempt::FINISHED, quiz_attempt::ABANDONED);
|
||||
|
||||
/**
|
||||
* @var bool whether to show all finished attmepts, or just the one that gave
|
||||
* the final grade for the user.
|
||||
*/
|
||||
public $onlygraded = false;
|
||||
|
||||
/** @var int Number of attempts to show per page. */
|
||||
public $pagesize = quiz_attempts_report::DEFAULT_PAGE_SIZE;
|
||||
|
||||
/** @var string whether the data should be downloaded in some format, or '' to display it. */
|
||||
public $download = '';
|
||||
|
||||
/** @var bool whether the current user has permission to see grades. */
|
||||
public $usercanseegrades;
|
||||
|
||||
/** @var bool whether the report table should have a column of checkboxes. */
|
||||
public $checkboxcolumn = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $mode which report these options are for.
|
||||
* @param object $quiz the settings for the quiz being reported on.
|
||||
* @param object $cm the course module objects for the quiz being reported on.
|
||||
* @param object $coures the course settings for the coures this quiz is in.
|
||||
*/
|
||||
public function __construct($mode, $quiz, $cm, $course) {
|
||||
$this->mode = $mode;
|
||||
$this->quiz = $quiz;
|
||||
$this->cm = $cm;
|
||||
$this->course = $course;
|
||||
|
||||
$this->usercanseegrades = quiz_report_should_show_grades($quiz, context_module::instance($cm->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL parameters required to show the report with these options.
|
||||
* @return array URL parameter name => value.
|
||||
*/
|
||||
protected function get_url_params() {
|
||||
$params = array(
|
||||
'id' => $this->cm->id,
|
||||
'mode' => $this->mode,
|
||||
'attempts' => $this->attempts,
|
||||
'onlygraded' => $this->onlygraded,
|
||||
);
|
||||
|
||||
if ($this->states) {
|
||||
$params['states'] = implode('-', $this->states);
|
||||
}
|
||||
|
||||
if (groups_get_activity_groupmode($this->cm, $this->course)) {
|
||||
$params['group'] = $this->group;
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to show the report with these options.
|
||||
* @return moodle_url the URL.
|
||||
*/
|
||||
public function get_url() {
|
||||
return new moodle_url('/mod/quiz/report.php', $this->get_url_params());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the data we get when the settings form is submitted. This includes
|
||||
* updating the fields of this class, and updating the user preferences
|
||||
* where appropriate.
|
||||
* @param object $fromform The data from $mform->get_data() from the settings form.
|
||||
*/
|
||||
public function process_settings_from_form($fromform) {
|
||||
$this->setup_from_form_data($fromform);
|
||||
$this->resolve_dependencies();
|
||||
$this->update_user_preferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up this preferences object using optional_param (using user_preferences
|
||||
* to set anything not specified by the params.
|
||||
*/
|
||||
public function process_settings_from_params() {
|
||||
$this->setup_from_user_preferences();
|
||||
$this->setup_from_params();
|
||||
$this->resolve_dependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value of the settings to pass to the settings form.
|
||||
*/
|
||||
public function get_initial_form_data() {
|
||||
$toform = new stdClass();
|
||||
$toform->attempts = $this->attempts;
|
||||
$toform->onlygraded = $this->onlygraded;
|
||||
$toform->pagesize = $this->pagesize;
|
||||
|
||||
if ($this->states) {
|
||||
foreach (self::$statefields as $field => $state) {
|
||||
$toform->$field = in_array($state, $this->states);
|
||||
}
|
||||
}
|
||||
|
||||
return $toform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields of this object from the form data.
|
||||
* @param object $fromform The data from $mform->get_data() from the settings form.
|
||||
*/
|
||||
public function setup_from_form_data($fromform) {
|
||||
$this->attempts = $fromform->attempts;
|
||||
$this->group = groups_get_activity_group($this->cm, true);
|
||||
$this->onlygraded = !empty($fromform->onlygraded);
|
||||
$this->pagesize = $fromform->pagesize;
|
||||
|
||||
$this->states = array();
|
||||
foreach (self::$statefields as $field => $state) {
|
||||
if (!empty($fromform->$field)) {
|
||||
$this->states[] = $state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields of this object from the URL parameters.
|
||||
*/
|
||||
public function setup_from_params() {
|
||||
$this->attempts = optional_param('attempts', $this->attempts, PARAM_ALPHAEXT);
|
||||
$this->group = groups_get_activity_group($this->cm, true);
|
||||
$this->onlygraded = optional_param('onlygraded', $this->onlygraded, PARAM_BOOL);
|
||||
$this->pagesize = optional_param('pagesize', $this->pagesize, PARAM_INT);
|
||||
|
||||
$states = optional_param('states', '', PARAM_ALPHAEXT);
|
||||
if (!empty($states)) {
|
||||
$this->states = explode('-', $states);
|
||||
}
|
||||
|
||||
$this->download = optional_param('download', $this->download, PARAM_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields of this object from the user's preferences.
|
||||
* (For those settings that are backed by user-preferences).
|
||||
*/
|
||||
public function setup_from_user_preferences() {
|
||||
$this->pagesize = get_user_preferences('quiz_report_pagesize', $this->pagesize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user preferences so they match the settings in this object.
|
||||
* (For those settings that are backed by user-preferences).
|
||||
*/
|
||||
public function update_user_preferences() {
|
||||
set_user_preference('quiz_report_pagesize', $this->pagesize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the settings, and remove any 'impossible' combinations.
|
||||
*/
|
||||
public function resolve_dependencies() {
|
||||
if ($this->group) {
|
||||
// Default for when a group is selected.
|
||||
if ($this->attempts === null || $this->attempts == quiz_attempts_report::ALL_WITH) {
|
||||
$this->attempts = quiz_attempts_report::ENROLLED_WITH;
|
||||
}
|
||||
|
||||
} else if (!$this->group && $this->course->id == SITEID) {
|
||||
// Force report on front page to show all, unless a group is selected.
|
||||
$this->attempts = quiz_attempts_report::ALL_WITH;
|
||||
|
||||
} else if (!in_array($this->attempts, array(quiz_attempts_report::ALL_WITH, quiz_attempts_report::ENROLLED_WITH,
|
||||
quiz_attempts_report::ENROLLED_WITHOUT, quiz_attempts_report::ENROLLED_ALL))) {
|
||||
$this->attempts = quiz_attempts_report::ENROLLED_WITH;
|
||||
}
|
||||
|
||||
$cleanstates = array();
|
||||
foreach (self::$statefields as $state) {
|
||||
if (in_array($state, $this->states)) {
|
||||
$cleanstates[] = $state;
|
||||
}
|
||||
}
|
||||
$this->states = $cleanstates;
|
||||
if (count($this->states) == count(self::$statefields)) {
|
||||
// If all states have been selected, then there is no constraint
|
||||
// required in the SQL, so clear the array.
|
||||
$this->states = null;
|
||||
}
|
||||
|
||||
if (!quiz_report_can_filter_only_graded($this->quiz)) {
|
||||
// A grading mode like 'average' has been selected, so we cannot do
|
||||
// the show the attempt that gave the final grade thing.
|
||||
$this->onlygraded = false;
|
||||
}
|
||||
|
||||
if ($this->attempts == quiz_attempts_report::ENROLLED_WITHOUT) {
|
||||
$this->states = null;
|
||||
$this->onlygraded = false;
|
||||
}
|
||||
|
||||
if (!$this->is_showing_finished_attempts()) {
|
||||
$this->onlygraded = false;
|
||||
}
|
||||
|
||||
if ($this->pagesize < 1) {
|
||||
$this->pagesize = quiz_attempts_report::DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the options are such that finished attempts are being shown.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_showing_finished_attempts() {
|
||||
return $this->states === null || in_array(quiz_attempt::FINISHED, $this->states);
|
||||
}
|
||||
}
|
||||
debugging('This file is no longer required in Moodle 4.2+. Please do not include/require it.', DEBUG_DEVELOPER);
|
||||
|
@ -15,687 +15,11 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Base class for the table used by a {@link quiz_attempts_report}.
|
||||
* File only retained to prevent fatal errors in code that tries to require/include this.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-76612 delete this file as part of Moodle 4.6 development.
|
||||
* @deprecated This file is no longer required in Moodle 4.2+.
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Base class for the table used by a {@link quiz_attempts_report}.
|
||||
*
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class quiz_attempts_report_table extends table_sql {
|
||||
public $useridfield = 'userid';
|
||||
|
||||
/** @var moodle_url the URL of this report. */
|
||||
protected $reporturl;
|
||||
|
||||
/** @var array the display options. */
|
||||
protected $displayoptions;
|
||||
|
||||
/**
|
||||
* @var array information about the latest step of each question.
|
||||
* Loaded by {@link load_question_latest_steps()}, if applicable.
|
||||
*/
|
||||
protected $lateststeps = null;
|
||||
|
||||
/** @var object the quiz settings for the quiz we are reporting on. */
|
||||
protected $quiz;
|
||||
|
||||
/** @var context the quiz context. */
|
||||
protected $context;
|
||||
|
||||
/** @var string HTML fragment to select the first/best/last attempt, if appropriate. */
|
||||
protected $qmsubselect;
|
||||
|
||||
/** @var object mod_quiz_attempts_report_options the options affecting this report. */
|
||||
protected $options;
|
||||
|
||||
/** @var \core\dml\sql_join Contains joins, wheres, params to find students
|
||||
* in the currently selected group, if applicable.
|
||||
*/
|
||||
protected $groupstudentsjoins;
|
||||
|
||||
/** @var \core\dml\sql_join Contains joins, wheres, params to find the students in the course. */
|
||||
protected $studentsjoins;
|
||||
|
||||
/** @var object the questions that comprise this quiz.. */
|
||||
protected $questions;
|
||||
|
||||
/** @var bool whether to include the column with checkboxes to select each attempt. */
|
||||
protected $includecheckboxes;
|
||||
|
||||
/** @var string The toggle group name for the checkboxes in the checkbox column. */
|
||||
protected $togglegroup = 'quiz-attempts';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $uniqueid
|
||||
* @param object $quiz
|
||||
* @param context $context
|
||||
* @param string $qmsubselect
|
||||
* @param mod_quiz_attempts_report_options $options
|
||||
* @param \core\dml\sql_join $groupstudentsjoins Contains joins, wheres, params
|
||||
* @param \core\dml\sql_join $studentsjoins Contains joins, wheres, params
|
||||
* @param array $questions
|
||||
* @param moodle_url $reporturl
|
||||
*/
|
||||
public function __construct($uniqueid, $quiz, $context, $qmsubselect,
|
||||
mod_quiz_attempts_report_options $options, \core\dml\sql_join $groupstudentsjoins, \core\dml\sql_join $studentsjoins,
|
||||
$questions, $reporturl) {
|
||||
parent::__construct($uniqueid);
|
||||
$this->quiz = $quiz;
|
||||
$this->context = $context;
|
||||
$this->qmsubselect = $qmsubselect;
|
||||
$this->groupstudentsjoins = $groupstudentsjoins;
|
||||
$this->studentsjoins = $studentsjoins;
|
||||
$this->questions = $questions;
|
||||
$this->includecheckboxes = $options->checkboxcolumn;
|
||||
$this->reporturl = $reporturl;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the checkbox column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_checkbox($attempt) {
|
||||
global $OUTPUT;
|
||||
|
||||
if ($attempt->attempt) {
|
||||
$checkbox = new \core\output\checkbox_toggleall($this->togglegroup, false, [
|
||||
'id' => "attemptid_{$attempt->attempt}",
|
||||
'name' => 'attemptid[]',
|
||||
'value' => $attempt->attempt,
|
||||
'label' => get_string('selectattempt', 'quiz'),
|
||||
'labelclasses' => 'accesshide',
|
||||
]);
|
||||
return $OUTPUT->render($checkbox);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the user's picture column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_picture($attempt) {
|
||||
global $OUTPUT;
|
||||
$user = new stdClass();
|
||||
$additionalfields = explode(',', implode(',', \core_user\fields::get_picture_fields()));
|
||||
$user = username_load_fields_from_object($user, $attempt, null, $additionalfields);
|
||||
$user->id = $attempt->userid;
|
||||
return $OUTPUT->user_picture($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the user's full name column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_fullname($attempt) {
|
||||
$html = parent::col_fullname($attempt);
|
||||
if ($this->is_downloading() || empty($attempt->attempt)) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
return $html . html_writer::empty_tag('br') . html_writer::link(
|
||||
new moodle_url('/mod/quiz/review.php', array('attempt' => $attempt->attempt)),
|
||||
get_string('reviewattempt', 'quiz'), array('class' => 'reviewlink'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the attempt state column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_state($attempt) {
|
||||
if (!is_null($attempt->attempt)) {
|
||||
return quiz_attempt::state_name($attempt->state);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the start time column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_timestart($attempt) {
|
||||
if ($attempt->attempt) {
|
||||
return userdate($attempt->timestart, $this->strtimeformat);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the finish time column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_timefinish($attempt) {
|
||||
if ($attempt->attempt && $attempt->timefinish) {
|
||||
return userdate($attempt->timefinish, $this->strtimeformat);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the time taken column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_duration($attempt) {
|
||||
if ($attempt->timefinish) {
|
||||
return format_time($attempt->timefinish - $attempt->timestart);
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the display of the feedback column.
|
||||
* @param object $attempt the table row being output.
|
||||
* @return string HTML content to go inside the td.
|
||||
*/
|
||||
public function col_feedbacktext($attempt) {
|
||||
if ($attempt->state != quiz_attempt::FINISHED) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
$feedback = quiz_report_feedback_for_grade(
|
||||
quiz_rescale_grade($attempt->sumgrades, $this->quiz, false),
|
||||
$this->quiz->id, $this->context);
|
||||
|
||||
if ($this->is_downloading()) {
|
||||
$feedback = strip_tags($feedback);
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
}
|
||||
|
||||
public function get_row_class($attempt) {
|
||||
if ($this->qmsubselect && $attempt->gradedattempt) {
|
||||
return 'gradedattempt';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a link to review an individual question in a popup window.
|
||||
*
|
||||
* @param string $data HTML fragment. The text to make into the link.
|
||||
* @param object $attempt data for the row of the table being output.
|
||||
* @param int $slot the number used to identify this question within this usage.
|
||||
*/
|
||||
public function make_review_link($data, $attempt, $slot) {
|
||||
global $OUTPUT, $CFG;
|
||||
|
||||
$flag = '';
|
||||
if ($this->is_flagged($attempt->usageid, $slot)) {
|
||||
$flag = $OUTPUT->pix_icon('i/flagged', get_string('flagged', 'question'),
|
||||
'moodle', array('class' => 'questionflag'));
|
||||
}
|
||||
|
||||
$feedbackimg = '';
|
||||
$state = $this->slot_state($attempt, $slot);
|
||||
if ($state && $state->is_finished() && $state != question_state::$needsgrading) {
|
||||
$feedbackimg = $this->icon_for_fraction($this->slot_fraction($attempt, $slot));
|
||||
}
|
||||
|
||||
$output = html_writer::tag('span', $feedbackimg . html_writer::tag('span',
|
||||
$data, array('class' => $state->get_state_class(true))) . $flag, array('class' => 'que'));
|
||||
|
||||
$reviewparams = array('attempt' => $attempt->attempt, 'slot' => $slot);
|
||||
if (isset($attempt->try)) {
|
||||
$reviewparams['step'] = $this->step_no_for_try($attempt->usageid, $slot, $attempt->try);
|
||||
}
|
||||
$url = new moodle_url('/mod/quiz/reviewquestion.php', $reviewparams);
|
||||
$output = $OUTPUT->action_link($url, $output,
|
||||
new popup_action('click', $url, 'reviewquestion',
|
||||
array('height' => 450, 'width' => 650)),
|
||||
array('title' => get_string('reviewresponse', 'quiz')));
|
||||
|
||||
if (!empty($CFG->enableplagiarism)) {
|
||||
require_once($CFG->libdir . '/plagiarismlib.php');
|
||||
$output .= plagiarism_get_links([
|
||||
'context' => $this->context->id,
|
||||
'component' => 'qtype_'.$this->questions[$slot]->qtype,
|
||||
'cmid' => $this->context->instanceid,
|
||||
'area' => $attempt->usageid,
|
||||
'itemid' => $slot,
|
||||
'userid' => $attempt->userid]);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $attempt the row data
|
||||
* @param int $slot
|
||||
* @return question_state
|
||||
*/
|
||||
protected function slot_state($attempt, $slot) {
|
||||
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
|
||||
return question_state::get($stepdata->state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $questionusageid
|
||||
* @param int $slot
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_flagged($questionusageid, $slot) {
|
||||
$stepdata = $this->lateststeps[$questionusageid][$slot];
|
||||
return $stepdata->flagged;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param object $attempt the row data
|
||||
* @param int $slot
|
||||
* @return float
|
||||
*/
|
||||
protected function slot_fraction($attempt, $slot) {
|
||||
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
|
||||
return $stepdata->fraction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an appropriate icon (green tick, red cross, etc.) for a grade.
|
||||
* @param float $fraction grade on a scale 0..1.
|
||||
* @return string html fragment.
|
||||
*/
|
||||
protected function icon_for_fraction($fraction) {
|
||||
global $OUTPUT;
|
||||
|
||||
$feedbackclass = question_state::graded_state_for_fraction($fraction)->get_feedback_class();
|
||||
return $OUTPUT->pix_icon('i/grade_' . $feedbackclass, get_string($feedbackclass, 'question'),
|
||||
'moodle', array('class' => 'icon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load any extra data after main query. At this point you can call {@link get_qubaids_condition} to get the condition that
|
||||
* limits the query to just the question usages shown in this report page or alternatively for all attempts if downloading a
|
||||
* full report.
|
||||
*/
|
||||
protected function load_extra_data() {
|
||||
$this->lateststeps = $this->load_question_latest_steps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load information about the latest state of selected questions in selected attempts.
|
||||
*
|
||||
* The results are returned as an two dimensional array $qubaid => $slot => $dataobject
|
||||
*
|
||||
* @param qubaid_condition|null $qubaids used to restrict which usages are included
|
||||
* in the query. See {@link qubaid_condition}.
|
||||
* @return array of records. See the SQL in this function to see the fields available.
|
||||
*/
|
||||
protected function load_question_latest_steps(qubaid_condition $qubaids = null) {
|
||||
if ($qubaids === null) {
|
||||
$qubaids = $this->get_qubaids_condition();
|
||||
}
|
||||
$dm = new question_engine_data_mapper();
|
||||
$latesstepdata = $dm->load_questions_usages_latest_steps(
|
||||
$qubaids, array_keys($this->questions));
|
||||
|
||||
$lateststeps = array();
|
||||
foreach ($latesstepdata as $step) {
|
||||
$lateststeps[$step->questionusageid][$step->slot] = $step;
|
||||
}
|
||||
|
||||
return $lateststeps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this report require loading any more data after the main query. After the main query then
|
||||
* you can use $this->get
|
||||
*
|
||||
* @return bool should {@link query_db()} call {@link load_extra_data}?
|
||||
*/
|
||||
protected function requires_extra_data() {
|
||||
return $this->requires_latest_steps_loaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this report require the detailed information for each question from the
|
||||
* question_attempts_steps table?
|
||||
* @return bool should {@link load_extra_data} call {@link load_question_latest_steps}?
|
||||
*/
|
||||
protected function requires_latest_steps_loaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a column that depends on joining to the latest state information?
|
||||
* If so, return the corresponding slot. If not, return false.
|
||||
* @param string $column a column name
|
||||
* @return int false if no, else a slot.
|
||||
*/
|
||||
protected function is_latest_step_column($column) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any fields that might be needed when sorting on date for a particular slot.
|
||||
*
|
||||
* Note: these values are only used for sorting. The values displayed are taken
|
||||
* from $this->lateststeps loaded in load_extra_data().
|
||||
*
|
||||
* @param int $slot the slot for the column we want.
|
||||
* @param string $alias the table alias for latest state information relating to that slot.
|
||||
* @return string definitions of extra fields to add to the SELECT list of the query.
|
||||
*/
|
||||
protected function get_required_latest_state_fields($slot, $alias) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Contruct all the parts of the main database query.
|
||||
* @param \core\dml\sql_join $allowedstudentsjoins (joins, wheres, params) defines allowed users for the report.
|
||||
* @return array with 4 elements ($fields, $from, $where, $params) that can be used to
|
||||
* build the actual database query.
|
||||
*/
|
||||
public function base_sql(\core\dml\sql_join $allowedstudentsjoins) {
|
||||
global $DB;
|
||||
|
||||
// Please note this uniqueid column is not the same as quiza.uniqueid.
|
||||
$fields = 'DISTINCT ' . $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid,';
|
||||
|
||||
if ($this->qmsubselect) {
|
||||
$fields .= "\n(CASE WHEN $this->qmsubselect THEN 1 ELSE 0 END) AS gradedattempt,";
|
||||
}
|
||||
|
||||
$userfieldsapi = \core_user\fields::for_identity($this->context)->with_name()
|
||||
->excluding('id', 'idnumber', 'picture', 'imagealt', 'institution', 'department', 'email');
|
||||
$userfields = $userfieldsapi->get_sql('u', true, '', '', false);
|
||||
|
||||
$fields .= '
|
||||
quiza.uniqueid AS usageid,
|
||||
quiza.id AS attempt,
|
||||
u.id AS userid,
|
||||
u.idnumber,
|
||||
u.picture,
|
||||
u.imagealt,
|
||||
u.institution,
|
||||
u.department,
|
||||
u.email,' . $userfields->selects . ',
|
||||
quiza.state,
|
||||
quiza.sumgrades,
|
||||
quiza.timefinish,
|
||||
quiza.timestart,
|
||||
CASE WHEN quiza.timefinish = 0 THEN null
|
||||
WHEN quiza.timefinish > quiza.timestart THEN quiza.timefinish - quiza.timestart
|
||||
ELSE 0 END AS duration';
|
||||
// To explain that last bit, timefinish can be non-zero and less
|
||||
// than timestart when you have two load-balanced servers with very
|
||||
// badly synchronised clocks, and a student does a really quick attempt.
|
||||
|
||||
// This part is the same for all cases. Join the users and quiz_attempts tables.
|
||||
$from = " {user} u";
|
||||
$from .= "\n{$userfields->joins}";
|
||||
$from .= "\nLEFT JOIN {quiz_attempts} quiza ON
|
||||
quiza.userid = u.id AND quiza.quiz = :quizid";
|
||||
$params = array_merge($userfields->params, ['quizid' => $this->quiz->id]);
|
||||
|
||||
if ($this->qmsubselect && $this->options->onlygraded) {
|
||||
$from .= " AND (quiza.state <> :finishedstate OR $this->qmsubselect)";
|
||||
$params['finishedstate'] = quiz_attempt::FINISHED;
|
||||
}
|
||||
|
||||
switch ($this->options->attempts) {
|
||||
case quiz_attempts_report::ALL_WITH:
|
||||
// Show all attempts, including students who are no longer in the course.
|
||||
$where = 'quiza.id IS NOT NULL AND quiza.preview = 0';
|
||||
break;
|
||||
case quiz_attempts_report::ENROLLED_WITH:
|
||||
// Show only students with attempts.
|
||||
$from .= "\n" . $allowedstudentsjoins->joins;
|
||||
$where = "quiza.preview = 0 AND quiza.id IS NOT NULL AND " . $allowedstudentsjoins->wheres;
|
||||
$params = array_merge($params, $allowedstudentsjoins->params);
|
||||
break;
|
||||
case quiz_attempts_report::ENROLLED_WITHOUT:
|
||||
// Show only students without attempts.
|
||||
$from .= "\n" . $allowedstudentsjoins->joins;
|
||||
$where = "quiza.id IS NULL AND " . $allowedstudentsjoins->wheres;
|
||||
$params = array_merge($params, $allowedstudentsjoins->params);
|
||||
break;
|
||||
case quiz_attempts_report::ENROLLED_ALL:
|
||||
// Show all students with or without attempts.
|
||||
$from .= "\n" . $allowedstudentsjoins->joins;
|
||||
$where = "(quiza.preview = 0 OR quiza.preview IS NULL) AND " . $allowedstudentsjoins->wheres;
|
||||
$params = array_merge($params, $allowedstudentsjoins->params);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->options->states) {
|
||||
list($statesql, $stateparams) = $DB->get_in_or_equal($this->options->states,
|
||||
SQL_PARAMS_NAMED, 'state');
|
||||
$params += $stateparams;
|
||||
$where .= " AND (quiza.state $statesql OR quiza.state IS NULL)";
|
||||
}
|
||||
|
||||
return array($fields, $from, $where, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* A chance for subclasses to modify the SQL after the count query has been generated,
|
||||
* and before the full query is constructed.
|
||||
* @param string $fields SELECT list.
|
||||
* @param string $from JOINs part of the SQL.
|
||||
* @param string $where WHERE clauses.
|
||||
* @param array $params Query params.
|
||||
* @return array with 4 elements ($fields, $from, $where, $params) as from base_sql.
|
||||
*/
|
||||
protected function update_sql_after_count($fields, $from, $where, $params) {
|
||||
return [$fields, $from, $where, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the SQL queries (count rows, and get data).
|
||||
*
|
||||
* @param \core\dml\sql_join $allowedjoins (joins, wheres, params) defines allowed users for the report.
|
||||
*/
|
||||
public function setup_sql_queries($allowedjoins) {
|
||||
list($fields, $from, $where, $params) = $this->base_sql($allowedjoins);
|
||||
|
||||
// The WHERE clause is vital here, because some parts of tablelib.php will expect to
|
||||
// add bits like ' AND x = 1' on the end, and that needs to leave to valid SQL.
|
||||
$this->set_count_sql("SELECT COUNT(1) FROM (SELECT $fields FROM $from WHERE $where) temp WHERE 1 = 1", $params);
|
||||
|
||||
list($fields, $from, $where, $params) = $this->update_sql_after_count($fields, $from, $where, $params);
|
||||
$this->set_sql($fields, $from, $where, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the information about the latest state of the question with slot
|
||||
* $slot to the query.
|
||||
*
|
||||
* The extra information is added as a join to a
|
||||
* 'table' with alias qa$slot, with columns that are a union of
|
||||
* the columns of the question_attempts and question_attempts_states tables.
|
||||
*
|
||||
* @param int $slot the question to add information for.
|
||||
*/
|
||||
protected function add_latest_state_join($slot) {
|
||||
$alias = 'qa' . $slot;
|
||||
|
||||
$fields = $this->get_required_latest_state_fields($slot, $alias);
|
||||
if (!$fields) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This condition roughly filters the list of attempts to be considered.
|
||||
// It is only used in a subselect to help crappy databases (see MDL-30122)
|
||||
// therefore, it is better to use a very simple join, which may include
|
||||
// too many records, than to do a super-accurate join.
|
||||
$qubaids = new qubaid_join("{quiz_attempts} {$alias}quiza", "{$alias}quiza.uniqueid",
|
||||
"{$alias}quiza.quiz = :{$alias}quizid", array("{$alias}quizid" => $this->sql->params['quizid']));
|
||||
|
||||
$dm = new question_engine_data_mapper();
|
||||
list($inlineview, $viewparams) = $dm->question_attempt_latest_state_view($alias, $qubaids);
|
||||
|
||||
$this->sql->fields .= ",\n$fields";
|
||||
$this->sql->from .= "\nLEFT JOIN $inlineview ON " .
|
||||
"$alias.questionusageid = quiza.uniqueid AND $alias.slot = :{$alias}slot";
|
||||
$this->sql->params[$alias . 'slot'] = $slot;
|
||||
$this->sql->params = array_merge($this->sql->params, $viewparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an appropriate qubaid_condition for loading more data about the
|
||||
* attempts we are displaying.
|
||||
* @return qubaid_condition
|
||||
*/
|
||||
protected function get_qubaids_condition() {
|
||||
if (is_null($this->rawdata)) {
|
||||
throw new coding_exception(
|
||||
'Cannot call get_qubaids_condition until the main data has been loaded.');
|
||||
}
|
||||
|
||||
if ($this->is_downloading()) {
|
||||
// We want usages for all attempts.
|
||||
return new qubaid_join("(
|
||||
SELECT DISTINCT quiza.uniqueid
|
||||
FROM " . $this->sql->from . "
|
||||
WHERE " . $this->sql->where . "
|
||||
) quizasubquery", 'quizasubquery.uniqueid',
|
||||
"1 = 1", $this->sql->params);
|
||||
}
|
||||
|
||||
$qubaids = array();
|
||||
foreach ($this->rawdata as $attempt) {
|
||||
if ($attempt->usageid > 0) {
|
||||
$qubaids[] = $attempt->usageid;
|
||||
}
|
||||
}
|
||||
|
||||
return new qubaid_list($qubaids);
|
||||
}
|
||||
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
$doneslots = array();
|
||||
foreach ($this->get_sort_columns() as $column => $notused) {
|
||||
$slot = $this->is_latest_step_column($column);
|
||||
if ($slot && !in_array($slot, $doneslots)) {
|
||||
$this->add_latest_state_join($slot);
|
||||
$doneslots[] = $slot;
|
||||
}
|
||||
}
|
||||
|
||||
parent::query_db($pagesize, $useinitialsbar);
|
||||
|
||||
if ($this->requires_extra_data()) {
|
||||
$this->load_extra_data();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_sort_columns() {
|
||||
// Add attemptid as a final tie-break to the sort. This ensures that
|
||||
// Attempts by the same student appear in order when just sorting by name.
|
||||
$sortcolumns = parent::get_sort_columns();
|
||||
$sortcolumns['quiza.id'] = SORT_ASC;
|
||||
return $sortcolumns;
|
||||
}
|
||||
|
||||
public function wrap_html_start() {
|
||||
if ($this->is_downloading() || !$this->includecheckboxes) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $this->options->get_url();
|
||||
$url->param('sesskey', sesskey());
|
||||
|
||||
echo '<div id="tablecontainer">';
|
||||
echo '<form id="attemptsform" method="post" action="' . $url->out_omit_querystring() . '">';
|
||||
|
||||
echo html_writer::input_hidden_params($url);
|
||||
echo '<div>';
|
||||
}
|
||||
|
||||
public function wrap_html_finish() {
|
||||
global $PAGE;
|
||||
if ($this->is_downloading() || !$this->includecheckboxes) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div id="commands">';
|
||||
$this->submit_buttons();
|
||||
echo '</div>';
|
||||
|
||||
// Close the form.
|
||||
echo '</div>';
|
||||
echo '</form></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output any submit buttons required by the $this->includecheckboxes form.
|
||||
*/
|
||||
protected function submit_buttons() {
|
||||
global $PAGE;
|
||||
if (has_capability('mod/quiz:deleteattempts', $this->context)) {
|
||||
$deletebuttonparams = [
|
||||
'type' => 'submit',
|
||||
'class' => 'btn btn-secondary mr-1',
|
||||
'id' => 'deleteattemptsbutton',
|
||||
'name' => 'delete',
|
||||
'value' => get_string('deleteselected', 'quiz_overview'),
|
||||
'data-action' => 'toggle',
|
||||
'data-togglegroup' => $this->togglegroup,
|
||||
'data-toggle' => 'action',
|
||||
'disabled' => true
|
||||
];
|
||||
echo html_writer::empty_tag('input', $deletebuttonparams);
|
||||
$PAGE->requires->event_handler('#deleteattemptsbutton', 'click', 'M.util.show_confirm_dialog',
|
||||
array('message' => get_string('deleteattemptcheck', 'quiz')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the contents for the checkbox column header.
|
||||
*
|
||||
* It returns the HTML for a master \core\output\checkbox_toggleall component that selects/deselects all quiz attempts.
|
||||
*
|
||||
* @param string $columnname The name of the checkbox column.
|
||||
* @return string
|
||||
*/
|
||||
public function checkbox_col_header(string $columnname) {
|
||||
global $OUTPUT;
|
||||
|
||||
// Make sure to disable sorting on this column.
|
||||
$this->no_sorting($columnname);
|
||||
|
||||
// Build the select/deselect all control.
|
||||
$selectallid = $this->uniqueid . '-selectall-attempts';
|
||||
$selectalltext = get_string('selectall', 'quiz');
|
||||
$deselectalltext = get_string('selectnone', 'quiz');
|
||||
$mastercheckbox = new \core\output\checkbox_toggleall($this->togglegroup, true, [
|
||||
'id' => $selectallid,
|
||||
'name' => $selectallid,
|
||||
'value' => 1,
|
||||
'label' => $selectalltext,
|
||||
'labelclasses' => 'accesshide',
|
||||
'selectall' => $selectalltext,
|
||||
'deselectall' => $deselectalltext,
|
||||
]);
|
||||
|
||||
return $OUTPUT->render($mastercheckbox);
|
||||
}
|
||||
}
|
||||
debugging('This file is no longer required in Moodle 4.2+. Please do not include/require it.', DEBUG_DEVELOPER);
|
||||
|
@ -15,85 +15,11 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Base class for quiz report plugins.
|
||||
* File only retained to prevent fatal errors in code that tries to require/include this.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-76612 delete this file as part of Moodle 4.6 development.
|
||||
* @deprecated This file is no longer required in Moodle 4.2+.
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Base class for quiz report plugins.
|
||||
*
|
||||
* Doesn't do anything on it's own -- it needs to be extended.
|
||||
* This class displays quiz reports. Because it is called from
|
||||
* within /mod/quiz/report.php you can assume that the page header
|
||||
* and footer are taken care of.
|
||||
*
|
||||
* This file can refer to itself as report.php to pass variables
|
||||
* to itself - all these will also be globally available. You must
|
||||
* pass "id=$cm->id" or q=$quiz->id", and "mode=reportname".
|
||||
*
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class quiz_default_report {
|
||||
const NO_GROUPS_ALLOWED = -2;
|
||||
|
||||
/**
|
||||
* Override this function to displays the report.
|
||||
* @param $cm the course-module for this quiz.
|
||||
* @param $course the coures we are in.
|
||||
* @param $quiz this quiz.
|
||||
*/
|
||||
public abstract function display($cm, $course, $quiz);
|
||||
|
||||
/**
|
||||
* Initialise some parts of $PAGE and start output.
|
||||
*
|
||||
* @param object $cm the course_module information.
|
||||
* @param object $coures the course settings.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param string $reportmode the report name.
|
||||
*/
|
||||
public function print_header_and_tabs($cm, $course, $quiz, $reportmode = 'overview') {
|
||||
global $PAGE, $OUTPUT, $CFG;
|
||||
|
||||
// Print the page header.
|
||||
$PAGE->set_title($quiz->name);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
echo $OUTPUT->header();
|
||||
$context = context_module::instance($cm->id);
|
||||
if (!$PAGE->has_secondary_navigation()) {
|
||||
echo $OUTPUT->heading(format_string($quiz->name, true, array('context' => $context)));
|
||||
}
|
||||
if (!empty($CFG->enableplagiarism)) {
|
||||
require_once($CFG->libdir . '/plagiarismlib.php');
|
||||
echo plagiarism_update_status($course, $cm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current group for the user user looking at the report.
|
||||
*
|
||||
* @param object $cm the course_module information.
|
||||
* @param object $coures the course settings.
|
||||
* @param context $context the quiz context.
|
||||
* @return int the current group id, if applicable. 0 for all users,
|
||||
* NO_GROUPS_ALLOWED if the user cannot see any group.
|
||||
*/
|
||||
public function get_current_group($cm, $course, $context) {
|
||||
$groupmode = groups_get_activity_groupmode($cm, $course);
|
||||
$currentgroup = groups_get_activity_group($cm, true);
|
||||
|
||||
if ($groupmode == SEPARATEGROUPS && !$currentgroup && !has_capability('moodle/site:accessallgroups', $context)) {
|
||||
$currentgroup = self::NO_GROUPS_ALLOWED;
|
||||
}
|
||||
|
||||
return $currentgroup;
|
||||
}
|
||||
}
|
||||
debugging('This file is no longer required in Moodle 4.2+. Please do not include/require it.', DEBUG_DEVELOPER);
|
||||
|
@ -14,20 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file defines the quiz manual grading report class.
|
||||
*
|
||||
* @package quiz_grading
|
||||
* @copyright 2006 Gustav Delius
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use mod_quiz\local\reports\report_base;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/grading/gradingsettings_form.php');
|
||||
|
||||
|
||||
/**
|
||||
* Quiz report to help teachers manually grade questions that need it.
|
||||
*
|
||||
@ -38,7 +30,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/grading/gradingsettings_form.php'
|
||||
* @copyright 2006 Gustav Delius
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_grading_report extends quiz_default_report {
|
||||
class quiz_grading_report extends report_base {
|
||||
const DEFAULT_PAGE_SIZE = 5;
|
||||
const DEFAULT_ORDER = 'random';
|
||||
|
||||
|
@ -14,19 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file defines the setting form for the quiz overview report.
|
||||
*
|
||||
* @package quiz_overview
|
||||
* @copyright 2008 Jamie Pratt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_form.php');
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
use mod_quiz\local\reports\attempts_report_options_form;
|
||||
|
||||
/**
|
||||
* Quiz overview report settings form.
|
||||
@ -34,13 +23,13 @@ require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_form.php');
|
||||
* @copyright 2008 Jamie Pratt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_overview_settings_form extends mod_quiz_attempts_report_form {
|
||||
class quiz_overview_settings_form extends attempts_report_options_form {
|
||||
|
||||
protected function other_attempt_fields(MoodleQuickForm $mform) {
|
||||
if (has_capability('mod/quiz:regrade', $this->_customdata['context'])) {
|
||||
$mform->addElement('advcheckbox', 'onlyregraded', get_string('reportshowonly', 'quiz'),
|
||||
get_string('optonlyregradedattempts', 'quiz_overview'));
|
||||
$mform->disabledIf('onlyregraded', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('onlyregraded', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,19 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class to store the options for a {@link quiz_overview_report}.
|
||||
*
|
||||
* @package quiz_overview
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_options.php');
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
use mod_quiz\local\reports\attempts_report_options;
|
||||
|
||||
/**
|
||||
* Class to store the options for a {@link quiz_overview_report}.
|
||||
@ -34,7 +23,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_options.php');
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_overview_options extends mod_quiz_attempts_report_options {
|
||||
class quiz_overview_options extends attempts_report_options {
|
||||
|
||||
/** @var bool whether to show only attempt that need regrading. */
|
||||
public $onlyregraded = false;
|
||||
@ -88,7 +77,7 @@ class quiz_overview_options extends mod_quiz_attempts_report_options {
|
||||
public function resolve_dependencies() {
|
||||
parent::resolve_dependencies();
|
||||
|
||||
if ($this->attempts == quiz_attempts_report::ENROLLED_WITHOUT) {
|
||||
if ($this->attempts == attempts_report::ENROLLED_WITHOUT) {
|
||||
$this->onlyregraded = false;
|
||||
}
|
||||
|
||||
@ -100,6 +89,6 @@ class quiz_overview_options extends mod_quiz_attempts_report_options {
|
||||
// if the user has permissions and if the report mode is showing attempts.
|
||||
$this->checkboxcolumn = has_any_capability(
|
||||
array('mod/quiz:regrade', 'mod/quiz:deleteattempts'), context_module::instance($this->cm->id))
|
||||
&& ($this->attempts != quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
&& ($this->attempts != attempts_report::ENROLLED_WITHOUT);
|
||||
}
|
||||
}
|
||||
|
@ -14,19 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file defines the quiz grades table.
|
||||
*
|
||||
* @package quiz_overview
|
||||
* @copyright 2008 Jamie Pratt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_table.php');
|
||||
|
||||
use mod_quiz\local\reports\attempts_report_table;
|
||||
|
||||
/**
|
||||
* This is a table subclass for displaying the quiz grades report.
|
||||
@ -34,7 +22,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_table.php');
|
||||
* @copyright 2008 Jamie Pratt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_overview_table extends quiz_attempts_report_table {
|
||||
class quiz_overview_table extends attempts_report_table {
|
||||
|
||||
protected $regradedqs = array();
|
||||
|
||||
|
@ -22,11 +22,11 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
use mod_quiz\question\bank\qbank_helper;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/overview/overview_options.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/overview/overview_form.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/overview/overview_table.php');
|
||||
@ -38,7 +38,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/overview/overview_table.php');
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_overview_report extends quiz_attempts_report {
|
||||
class quiz_overview_report extends attempts_report {
|
||||
|
||||
/**
|
||||
* @var bool whether there are actually students to show, given the options.
|
||||
|
@ -14,26 +14,23 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
|
||||
/**
|
||||
* Makes some protected methods of quiz_attempts_report public to facilitate testing.
|
||||
* Makes some protected methods of attempts_report public to facilitate testing.
|
||||
*
|
||||
* @package quiz_overview
|
||||
* @copyright 2020 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport.php');
|
||||
|
||||
/**
|
||||
* Makes some protected methods of quiz_attempts_report public to facilitate testing.
|
||||
* Makes some protected methods of attempts_report public to facilitate testing.
|
||||
*
|
||||
* @copyright 2020 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class testable_quiz_attempts_report extends quiz_attempts_report {
|
||||
class testable_quiz_attempts_report extends attempts_report {
|
||||
|
||||
/**
|
||||
* Override this function to displays the report.
|
||||
|
@ -21,7 +21,7 @@ use mod_quiz\external\submit_question_version;
|
||||
use question_engine;
|
||||
use quiz;
|
||||
use quiz_attempt;
|
||||
use quiz_attempts_report;
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
use quiz_overview_options;
|
||||
use quiz_overview_report;
|
||||
use quiz_overview_table;
|
||||
@ -31,7 +31,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/default.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/overview/report.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/overview/overview_form.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/overview/tests/helpers.php');
|
||||
@ -176,7 +175,7 @@ class report_test extends \advanced_testcase {
|
||||
|
||||
// Set the options.
|
||||
$reportoptions = new quiz_overview_options('overview', $quiz, $cm, null);
|
||||
$reportoptions->attempts = quiz_attempts_report::ENROLLED_ALL;
|
||||
$reportoptions->attempts = attempts_report::ENROLLED_ALL;
|
||||
$reportoptions->onlygraded = true;
|
||||
$reportoptions->states = array(quiz_attempt::IN_PROGRESS, quiz_attempt::OVERDUE, quiz_attempt::FINISHED);
|
||||
|
||||
|
@ -30,6 +30,8 @@ require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/accessmanager.php');
|
||||
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
/**
|
||||
* Takes an array of objects and constructs a multidimensional array keyed by
|
||||
* the keys it finds on the object.
|
||||
@ -427,11 +429,11 @@ function quiz_no_questions_message($quiz, $cm, $context) {
|
||||
*/
|
||||
function quiz_report_should_show_grades($quiz, context $context) {
|
||||
if ($quiz->timeclose && time() > $quiz->timeclose) {
|
||||
$when = mod_quiz_display_options::AFTER_CLOSE;
|
||||
$when = display_options::AFTER_CLOSE;
|
||||
} else {
|
||||
$when = mod_quiz_display_options::LATER_WHILE_OPEN;
|
||||
$when = display_options::LATER_WHILE_OPEN;
|
||||
}
|
||||
$reviewoptions = mod_quiz_display_options::make_from_quiz($quiz, $when);
|
||||
$reviewoptions = display_options::make_from_quiz($quiz, $when);
|
||||
|
||||
return quiz_has_grades($quiz) &&
|
||||
($reviewoptions->marks >= question_display_options::MARK_AND_MAX ||
|
||||
|
@ -14,18 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file defines the quiz responses table for showing last try at question.
|
||||
*
|
||||
* @package quiz_responses
|
||||
* @copyright 2008 Jean-Michel Vedrine
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_table.php');
|
||||
use mod_quiz\local\reports\attempts_report_table;
|
||||
|
||||
|
||||
/**
|
||||
@ -34,7 +23,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_table.php');
|
||||
* @copyright 2008 Jean-Michel Vedrine
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_last_responses_table extends quiz_attempts_report_table {
|
||||
class quiz_last_responses_table extends attempts_report_table {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -22,10 +22,10 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/responses/responses_options.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/responses/responses_form.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/responses/last_responses_table.php');
|
||||
@ -46,7 +46,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/responses/first_or_all_responses_
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_responses_report extends quiz_attempts_report {
|
||||
class quiz_responses_report extends attempts_report {
|
||||
|
||||
public function display($quiz, $cm, $course) {
|
||||
global $OUTPUT, $DB;
|
||||
|
@ -14,19 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file defines the setting form for the quiz responses report.
|
||||
*
|
||||
* @package quiz_responses
|
||||
* @copyright 2008 Jean-Michel Vedrine
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_form.php');
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
use mod_quiz\local\reports\attempts_report_options_form;
|
||||
|
||||
/**
|
||||
* Quiz responses report settings form.
|
||||
@ -34,7 +23,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_form.php');
|
||||
* @copyright 2008 Jean-Michel Vedrine
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_responses_settings_form extends mod_quiz_attempts_report_form {
|
||||
class quiz_responses_settings_form extends attempts_report_options_form {
|
||||
|
||||
protected function other_preference_fields(MoodleQuickForm $mform) {
|
||||
$mform->addGroup(array(
|
||||
@ -45,15 +34,15 @@ class quiz_responses_settings_form extends mod_quiz_attempts_report_form {
|
||||
$mform->createElement('advcheckbox', 'right', '',
|
||||
get_string('rightanswer', 'quiz_responses')),
|
||||
), 'coloptions', get_string('showthe', 'quiz_responses'), array(' '), false);
|
||||
$mform->disabledIf('qtext', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('resp', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('right', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('qtext', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('resp', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('right', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
}
|
||||
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
if ($data['attempts'] != quiz_attempts_report::ENROLLED_WITHOUT && !(
|
||||
if ($data['attempts'] != attempts_report::ENROLLED_WITHOUT && !(
|
||||
$data['qtext'] || $data['resp'] || $data['right'])) {
|
||||
$errors['coloptions'] = get_string('reportmustselectstate', 'quiz');
|
||||
}
|
||||
@ -70,7 +59,7 @@ class quiz_responses_settings_form extends mod_quiz_attempts_report_form {
|
||||
question_attempt::ALL_TRIES => get_string('alltries', 'question'))
|
||||
);
|
||||
$mform->setDefault('whichtries', question_attempt::LAST_TRY);
|
||||
$mform->disabledIf('whichtries', 'attempts', 'eq', quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
$mform->disabledIf('whichtries', 'attempts', 'eq', attempts_report::ENROLLED_WITHOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,19 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class to store the options for a {@link quiz_responses_report}.
|
||||
*
|
||||
* @package quiz_responses
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_options.php');
|
||||
|
||||
use mod_quiz\local\reports\attempts_report;
|
||||
use mod_quiz\local\reports\attempts_report_options;
|
||||
|
||||
/**
|
||||
* Class to store the options for a {@link quiz_responses_report}.
|
||||
@ -34,7 +23,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_options.php');
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
class quiz_responses_options extends attempts_report_options {
|
||||
|
||||
/** @var bool whether to show the question text columns. */
|
||||
public $showqtext = false;
|
||||
@ -126,6 +115,6 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
// We only want to show the checkbox to delete attempts
|
||||
// if the user has permissions and if the report mode is showing attempts.
|
||||
$this->checkboxcolumn = has_capability('mod/quiz:deleteattempts', context_module::instance($this->cm->id))
|
||||
&& ($this->attempts != quiz_attempts_report::ENROLLED_WITHOUT);
|
||||
&& ($this->attempts != attempts_report::ENROLLED_WITHOUT);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/tests/attempt_walkthrough_from_csv_test.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/default.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/statistics/report.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
|
||||
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_quiz\local\reports\report_base;
|
||||
use core_question\statistics\questions\all_calculated_for_qubaid_condition;
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/default.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_form.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_table.php');
|
||||
@ -42,7 +42,7 @@ require_once($CFG->dirroot . '/mod/quiz/report/statistics/statisticslib.php');
|
||||
* @copyright 2008 Jamie Pratt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quiz_statistics_report extends quiz_default_report {
|
||||
class quiz_statistics_report extends report_base {
|
||||
|
||||
/** @var context_module context of this quiz.*/
|
||||
protected $context;
|
||||
|
@ -25,7 +25,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/tests/attempt_walkthrough_from_csv_test.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/default.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/statistics/report.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
|
||||
|
||||
|
@ -4,11 +4,31 @@ Overview of this plugin type at http://docs.moodle.org/dev/Quiz_reports
|
||||
|
||||
=== 4.2 ===
|
||||
|
||||
There was an ill-advised, never documented, API change in quiz_report_get_significant_questions
|
||||
in Moodle 4.0. The API has now been reverted to how it was before 4.0. Hopefully this
|
||||
will not cause anyone a problem. (The API revert did not require any changes in any automated tests
|
||||
or standard quiz reports.)
|
||||
* There was an ill-advised, never documented, API change in quiz_report_get_significant_questions
|
||||
in Moodle 4.0. The API has now been reverted to how it was before 4.0. Hopefully this
|
||||
will not cause anyone a problem. (The API revert did not require any changes in any automated tests
|
||||
or standard quiz reports.)
|
||||
|
||||
* The quiz has a lot of old classes in lib.php files. These have now been moved into the classes folder,
|
||||
and so are now in namespaces. Because of Moodle's class renaming support, your code should continue
|
||||
working, but output deprecated warnings, so you probably want to update. This should mostly be
|
||||
doable by adding use statements, and a search/replace.
|
||||
- quiz_default_report => mod_quiz\local\reports\report_base
|
||||
- quiz_attempts_report => mod_quiz\local\reports\attempts_report
|
||||
- mod_quiz_attempts_report_form => mod_quiz\local\reports\attempts_report_options_form
|
||||
- mod_quiz_attempts_report_options => mod_quiz\local\reports\attempts_report_options
|
||||
- quiz_attempts_report_table => mod_quiz\local\reports\attempts_report_table
|
||||
|
||||
As part of the clean-up, the following files are no longer required, and if you try to
|
||||
include them, you will get a debugging notices telling you not to:
|
||||
- mod/quiz/report/attemptsreport.php
|
||||
- mod/quiz/report/attemptsreport_form.php
|
||||
- mod/quiz/report/attemptsreport_options.php
|
||||
- mod/quiz/report/attemptsreport_table.php
|
||||
- mod/quiz/report/default.php
|
||||
|
||||
|
||||
>>>>>>> 40c14e4712e... MDL-74923 quiz: move report base classes to local folder
|
||||
=== 3.9 ===
|
||||
|
||||
* Quiz report plugins defining capabilities used to require an extra string like
|
||||
|
@ -22,6 +22,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use mod_quiz\admin\review_setting;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@ -72,7 +73,7 @@ if ($ADMIN->fulltree) {
|
||||
get_string('attemptgradeddelay', 'quiz'), get_string('attemptgradeddelay_desc', 'quiz'), 5 * HOURSECS, HOURSECS));
|
||||
|
||||
// What to do with overdue attempts.
|
||||
$setting = new mod_quiz_admin_setting_overduehandling('quiz/overduehandling',
|
||||
$setting = new \mod_quiz\admin\overdue_handling_setting('quiz/overduehandling',
|
||||
get_string('overduehandling', 'quiz'), get_string('overduehandling_desc', 'quiz'),
|
||||
array('value' => 'autosubmit', 'adv' => false), null);
|
||||
$setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
|
||||
@ -104,7 +105,7 @@ if ($ADMIN->fulltree) {
|
||||
$quizsettings->add($setting);
|
||||
|
||||
// Grading method.
|
||||
$setting = new mod_quiz_admin_setting_grademethod('quiz/grademethod',
|
||||
$setting = new \mod_quiz\admin\grade_method_setting('quiz/grademethod',
|
||||
get_string('grademethod', 'quiz'), get_string('configgrademethod', 'quiz'),
|
||||
array('value' => QUIZ_GRADEHIGHEST, 'adv' => false), null);
|
||||
$setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
|
||||
@ -174,21 +175,21 @@ if ($ADMIN->fulltree) {
|
||||
// Review options.
|
||||
$quizsettings->add(new admin_setting_heading('reviewheading',
|
||||
get_string('reviewoptionsheading', 'quiz'), ''));
|
||||
foreach (mod_quiz_admin_review_setting::fields() as $field => $name) {
|
||||
$default = mod_quiz_admin_review_setting::all_on();
|
||||
foreach (review_setting::fields() as $field => $name) {
|
||||
$default = review_setting::all_on();
|
||||
$forceduring = null;
|
||||
if ($field == 'attempt') {
|
||||
$forceduring = true;
|
||||
} else if ($field == 'overallfeedback') {
|
||||
$default = $default ^ mod_quiz_admin_review_setting::DURING;
|
||||
$default = $default ^ review_setting::DURING;
|
||||
$forceduring = false;
|
||||
}
|
||||
$quizsettings->add(new mod_quiz_admin_review_setting('quiz/review' . $field,
|
||||
$quizsettings->add(new review_setting('quiz/review' . $field,
|
||||
$name, '', $default, $forceduring));
|
||||
}
|
||||
|
||||
// Show the user's picture.
|
||||
$setting = new mod_quiz_admin_setting_user_image('quiz/showuserpicture',
|
||||
$setting = new \mod_quiz\admin\user_image_setting('quiz/showuserpicture',
|
||||
get_string('showuserpicture', 'quiz'), get_string('configshowuserpicture', 'quiz'),
|
||||
array('value' => 0, 'adv' => false), null);
|
||||
$setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
|
||||
@ -259,7 +260,7 @@ if ($ADMIN->fulltree) {
|
||||
$quizsettings->add($setting);
|
||||
|
||||
// Browser security.
|
||||
$setting = new mod_quiz_admin_setting_browsersecurity('quiz/browsersecurity',
|
||||
$setting = new \mod_quiz\admin\browser_security_setting('quiz/browsersecurity',
|
||||
get_string('showinsecurepopup', 'quiz'), get_string('configpopup', 'quiz'),
|
||||
array('value' => '-', 'adv' => true), null);
|
||||
$setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
|
||||
|
6
mod/quiz/tests/external/external_test.php
vendored
6
mod/quiz/tests/external/external_test.php
vendored
@ -28,7 +28,7 @@ namespace mod_quiz\external;
|
||||
|
||||
use externallib_advanced_testcase;
|
||||
use mod_quiz_external;
|
||||
use mod_quiz_display_options;
|
||||
use mod_quiz\question\display_options;
|
||||
use quiz;
|
||||
use quiz_attempt;
|
||||
|
||||
@ -681,7 +681,7 @@ class external_test extends externallib_advanced_testcase {
|
||||
}
|
||||
/**
|
||||
* Test get_combined_review_options.
|
||||
* This is a basic test, this is already tested in mod_quiz_display_options_testcase.
|
||||
* This is a basic test, this is already tested in display_options_testcase.
|
||||
*/
|
||||
public function test_get_combined_review_options() {
|
||||
global $DB;
|
||||
@ -1027,7 +1027,7 @@ class external_test extends externallib_advanced_testcase {
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(true);
|
||||
|
||||
// Set correctness mask so questions state can be fetched only after finishing the attempt.
|
||||
$DB->set_field('quiz', 'reviewcorrectness', mod_quiz_display_options::IMMEDIATELY_AFTER, array('id' => $quiz->id));
|
||||
$DB->set_field('quiz', 'reviewcorrectness', display_options::IMMEDIATELY_AFTER, array('id' => $quiz->id));
|
||||
|
||||
$quizobj = $attemptobj->get_quizobj();
|
||||
$quizobj->preload_questions();
|
||||
|
@ -25,7 +25,7 @@
|
||||
namespace mod_quiz;
|
||||
|
||||
use quiz_attempt;
|
||||
use mod_quiz_display_options;
|
||||
use mod_quiz\question\display_options;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@ -60,30 +60,31 @@ class locallib_test extends \advanced_testcase {
|
||||
|
||||
public function quiz_attempt_state_data_provider() {
|
||||
return [
|
||||
[quiz_attempt::IN_PROGRESS, null, null, mod_quiz_display_options::DURING],
|
||||
[quiz_attempt::FINISHED, -90, null, mod_quiz_display_options::IMMEDIATELY_AFTER],
|
||||
[quiz_attempt::FINISHED, -7200, null, mod_quiz_display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::FINISHED, -7200, 3600, mod_quiz_display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::FINISHED, -30, 30, mod_quiz_display_options::IMMEDIATELY_AFTER],
|
||||
[quiz_attempt::FINISHED, -90, -30, mod_quiz_display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::FINISHED, -7200, -3600, mod_quiz_display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::FINISHED, -90, -3600, mod_quiz_display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::ABANDONED, -10000000, null, mod_quiz_display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::ABANDONED, -7200, 3600, mod_quiz_display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::ABANDONED, -7200, -3600, mod_quiz_display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::IN_PROGRESS, null, null, display_options::DURING],
|
||||
[quiz_attempt::FINISHED, -90, null, display_options::IMMEDIATELY_AFTER],
|
||||
[quiz_attempt::FINISHED, -7200, null, display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::FINISHED, -7200, 3600, display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::FINISHED, -30, 30, display_options::IMMEDIATELY_AFTER],
|
||||
[quiz_attempt::FINISHED, -90, -30, display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::FINISHED, -7200, -3600, display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::FINISHED, -90, -3600, display_options::AFTER_CLOSE],
|
||||
[quiz_attempt::ABANDONED, -10000000, null, display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::ABANDONED, -7200, 3600, display_options::LATER_WHILE_OPEN],
|
||||
[quiz_attempt::ABANDONED, -7200, -3600, display_options::AFTER_CLOSE],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider quiz_attempt_state_data_provider
|
||||
*
|
||||
* @param unknown $attemptstate as in the quiz_attempts.state DB column.
|
||||
* @param unknown $relativetimefinish time relative to now when the attempt finished, or null for 0.
|
||||
* @param unknown $relativetimeclose time relative to now when the quiz closes, or null for 0.
|
||||
* @param unknown $expectedstate expected result. One of the mod_quiz_display_options constants/
|
||||
* @param string $attemptstate as in the quiz_attempts.state DB column.
|
||||
* @param int|null $relativetimefinish time relative to now when the attempt finished, or null for 0.
|
||||
* @param int|null $relativetimeclose time relative to now when the quiz closes, or null for 0.
|
||||
* @param int $expectedstate expected result. One of the display_options constants.
|
||||
* @covers ::quiz_attempt_state
|
||||
*/
|
||||
public function test_quiz_attempt_state($attemptstate,
|
||||
$relativetimefinish, $relativetimeclose, $expectedstate) {
|
||||
public function test_quiz_attempt_state(string $attemptstate,
|
||||
?int $relativetimefinish, ?int $relativetimeclose, int $expectedstate) {
|
||||
|
||||
$attempt = new \stdClass();
|
||||
$attempt->state = $attemptstate;
|
||||
|
82
mod/quiz/tests/question/display_options_test.php
Normal file
82
mod/quiz/tests/question/display_options_test.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz\question;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
/**
|
||||
* Unit tests for {@see display_options}.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category test
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \mod_quiz\question\display_options
|
||||
*/
|
||||
class display_options_test extends \basic_testcase {
|
||||
public function test_num_attempts_access_rule() {
|
||||
$quiz = new \stdClass();
|
||||
$quiz->decimalpoints = 2;
|
||||
$quiz->questiondecimalpoints = -1;
|
||||
$quiz->reviewattempt = 0x11110;
|
||||
$quiz->reviewcorrectness = 0x10000;
|
||||
$quiz->reviewmarks = 0x01110;
|
||||
$quiz->reviewspecificfeedback = 0x10000;
|
||||
$quiz->reviewgeneralfeedback = 0x01000;
|
||||
$quiz->reviewrightanswer = 0x00100;
|
||||
$quiz->reviewoverallfeedback = 0x00010;
|
||||
|
||||
$options = display_options::make_from_quiz($quiz,
|
||||
display_options::DURING);
|
||||
|
||||
$this->assertEquals(true, $options->attempt);
|
||||
$this->assertEquals(display_options::VISIBLE, $options->correctness);
|
||||
$this->assertEquals(display_options::MAX_ONLY, $options->marks);
|
||||
$this->assertEquals(display_options::VISIBLE, $options->feedback);
|
||||
// The next two should be controlled by the same settings as ->feedback.
|
||||
$this->assertEquals(display_options::VISIBLE, $options->numpartscorrect);
|
||||
$this->assertEquals(display_options::VISIBLE, $options->manualcomment);
|
||||
$this->assertEquals(2, $options->markdp);
|
||||
|
||||
$quiz->questiondecimalpoints = 5;
|
||||
$options = display_options::make_from_quiz($quiz,
|
||||
display_options::IMMEDIATELY_AFTER);
|
||||
|
||||
$this->assertEquals(display_options::MARK_AND_MAX, $options->marks);
|
||||
$this->assertEquals(display_options::VISIBLE, $options->generalfeedback);
|
||||
$this->assertEquals(display_options::HIDDEN, $options->feedback);
|
||||
// The next two should be controlled by the same settings as ->feedback.
|
||||
$this->assertEquals(display_options::HIDDEN, $options->numpartscorrect);
|
||||
$this->assertEquals(display_options::HIDDEN, $options->manualcomment);
|
||||
$this->assertEquals(5, $options->markdp);
|
||||
|
||||
$options = display_options::make_from_quiz($quiz,
|
||||
display_options::LATER_WHILE_OPEN);
|
||||
|
||||
$this->assertEquals(display_options::VISIBLE, $options->rightanswer);
|
||||
$this->assertEquals(display_options::HIDDEN, $options->generalfeedback);
|
||||
|
||||
$options = display_options::make_from_quiz($quiz,
|
||||
display_options::AFTER_CLOSE);
|
||||
|
||||
$this->assertEquals(display_options::VISIBLE, $options->overallfeedback);
|
||||
$this->assertEquals(display_options::HIDDEN, $options->rightanswer);
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
namespace mod_quiz;
|
||||
|
||||
use mod_quiz_display_options;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
/**
|
||||
* Unit tests for {@link mod_quiz_display_options}.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category test
|
||||
* @copyright 2010 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class quizdisplayoptions_test extends \basic_testcase {
|
||||
public function test_num_attempts_access_rule() {
|
||||
$quiz = new \stdClass();
|
||||
$quiz->decimalpoints = 2;
|
||||
$quiz->questiondecimalpoints = -1;
|
||||
$quiz->reviewattempt = 0x11110;
|
||||
$quiz->reviewcorrectness = 0x10000;
|
||||
$quiz->reviewmarks = 0x01110;
|
||||
$quiz->reviewspecificfeedback = 0x10000;
|
||||
$quiz->reviewgeneralfeedback = 0x01000;
|
||||
$quiz->reviewrightanswer = 0x00100;
|
||||
$quiz->reviewoverallfeedback = 0x00010;
|
||||
|
||||
$options = mod_quiz_display_options::make_from_quiz($quiz,
|
||||
mod_quiz_display_options::DURING);
|
||||
|
||||
$this->assertEquals(true, $options->attempt);
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->correctness);
|
||||
$this->assertEquals(mod_quiz_display_options::MAX_ONLY, $options->marks);
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->feedback);
|
||||
// The next two should be controlled by the same settings as ->feedback.
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->numpartscorrect);
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->manualcomment);
|
||||
$this->assertEquals(2, $options->markdp);
|
||||
|
||||
$quiz->questiondecimalpoints = 5;
|
||||
$options = mod_quiz_display_options::make_from_quiz($quiz,
|
||||
mod_quiz_display_options::IMMEDIATELY_AFTER);
|
||||
|
||||
$this->assertEquals(mod_quiz_display_options::MARK_AND_MAX, $options->marks);
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->generalfeedback);
|
||||
$this->assertEquals(mod_quiz_display_options::HIDDEN, $options->feedback);
|
||||
// The next two should be controlled by the same settings as ->feedback.
|
||||
$this->assertEquals(mod_quiz_display_options::HIDDEN, $options->numpartscorrect);
|
||||
$this->assertEquals(mod_quiz_display_options::HIDDEN, $options->manualcomment);
|
||||
$this->assertEquals(5, $options->markdp);
|
||||
|
||||
$options = mod_quiz_display_options::make_from_quiz($quiz,
|
||||
mod_quiz_display_options::LATER_WHILE_OPEN);
|
||||
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->rightanswer);
|
||||
$this->assertEquals(mod_quiz_display_options::HIDDEN, $options->generalfeedback);
|
||||
|
||||
$options = mod_quiz_display_options::make_from_quiz($quiz,
|
||||
mod_quiz_display_options::AFTER_CLOSE);
|
||||
|
||||
$this->assertEquals(mod_quiz_display_options::VISIBLE, $options->overallfeedback);
|
||||
$this->assertEquals(mod_quiz_display_options::HIDDEN, $options->rightanswer);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
|
||||
namespace mod_quiz;
|
||||
|
||||
use mod_quiz_display_options;
|
||||
use mod_quiz\question\display_options;
|
||||
use quiz;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
@ -44,19 +44,19 @@ class quizobj_test extends \basic_testcase {
|
||||
$quizobj = new quiz($quiz, $cm, new \stdClass(), false);
|
||||
|
||||
$this->assertEquals('',
|
||||
$quizobj->cannot_review_message(mod_quiz_display_options::DURING));
|
||||
$quizobj->cannot_review_message(display_options::DURING));
|
||||
$this->assertEquals('',
|
||||
$quizobj->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER));
|
||||
$quizobj->cannot_review_message(display_options::IMMEDIATELY_AFTER));
|
||||
$this->assertEquals(get_string('noreview', 'quiz'),
|
||||
$quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
|
||||
$quizobj->cannot_review_message(display_options::LATER_WHILE_OPEN));
|
||||
$this->assertEquals(get_string('noreview', 'quiz'),
|
||||
$quizobj->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE));
|
||||
$quizobj->cannot_review_message(display_options::AFTER_CLOSE));
|
||||
|
||||
$closetime = time() + 10000;
|
||||
$quiz->timeclose = $closetime;
|
||||
$quizobj = new quiz($quiz, $cm, new \stdClass(), false);
|
||||
|
||||
$this->assertEquals(get_string('noreviewuntil', 'quiz', userdate($closetime)),
|
||||
$quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
|
||||
$quizobj->cannot_review_message(display_options::LATER_WHILE_OPEN));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,50 @@
|
||||
This files describes API changes in the quiz code.
|
||||
|
||||
=== 4.2 ===
|
||||
|
||||
* The quiz has a lot of old classes in lib.php files. These have now been moved into the classes folder,
|
||||
and so are now in namespaces. Because of Moodle's class renaming support, your code should continue
|
||||
working, but output deprecated warnings, so you probably want to update. This should mostly be
|
||||
doable by adding use statements, and a search/replace.
|
||||
- mod_quiz_display_options => mod_quiz\question\display_options
|
||||
- qubaids_for_quiz => mod_quiz\question\qubaids_for_quiz
|
||||
- qubaids_for_quiz_user => mod_quiz\question\qubaids_for_quiz_user - which is deprecated, because
|
||||
it is almost the same as mod_quiz\question\qubaids_for_users_attempts.
|
||||
- mod_quiz_admin_setting_browsersecurity => mod_quiz\admin\browser_security_setting
|
||||
- mod_quiz_admin_setting_grademethod => mod_quiz\admin\grade_method_setting
|
||||
- mod_quiz_admin_setting_overduehandling => mod_quiz\admin\overdue_handling_setting
|
||||
- mod_quiz_admin_review_setting => mod_quiz\admin\review_setting
|
||||
- mod_quiz_admin_setting_user_image => mod_quiz\admin\user_image_setting
|
||||
- mod_quiz_admin_setting_browsersecurity => mod_quiz\admin\browser_security_setting
|
||||
- mod_quiz_admin_setting_grademethod => mod_quiz\admin\grade_method_setting
|
||||
- mod_quiz_admin_setting_overduehandling => mod_quiz\admin\overdue_handling_setting
|
||||
- mod_quiz_admin_review_setting => mod_quiz\admin\review_setting
|
||||
- mod_quiz_admin_setting_user_image => mod_quiz\admin\user_image_setting
|
||||
- mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_browsersecurity =>
|
||||
mod_quiz\adminpresets\adminpresets_browser_security_setting
|
||||
- mod_quiz\adminpresets/adminpresets_mod_quiz_admin_setting_grademethod =>
|
||||
mod_quiz\adminpresets\adminpresets_grade_method_setting
|
||||
- mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_overduehandling =>
|
||||
mod_quiz\adminpresets\adminpresets_overdue_handling_setting
|
||||
- mod_quiz\adminpresets\adminpresets_mod_quiz_admin_review_setting =>
|
||||
mod_quiz\adminpresets\adminpresets_review_setting
|
||||
- mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_user_image =>
|
||||
mod_quiz\adminpresets\adminpresets_user_image_setting
|
||||
- quiz_default_report => mod_quiz\local\reports\report_base
|
||||
- quiz_attempts_report => mod_quiz\local\reports\attempts_report
|
||||
- mod_quiz_attempts_report_form => mod_quiz\local\reports\attempts_report_options_form
|
||||
- mod_quiz_attempts_report_options => mod_quiz\local\reports\attempts_report_options
|
||||
- quiz_attempts_report_table => mod_quiz\local\reports\attempts_report_table
|
||||
|
||||
As part of the clean-up, the following files are no longer required, and if you try to
|
||||
include them, you will get a debugging notices telling you not to:
|
||||
- mod/quiz/report/attemptsreport.php
|
||||
- mod/quiz/report/attemptsreport_form.php
|
||||
- mod/quiz/report/attemptsreport_options.php
|
||||
- mod/quiz/report/attemptsreport_table.php
|
||||
- mod/quiz/report/default.php
|
||||
|
||||
|
||||
=== 4.1 ===
|
||||
|
||||
* quiz_has_question_use is now deprecated. Use mod_quiz\structure::has_use_capability istead.
|
||||
|
@ -1210,7 +1210,7 @@ ORDER BY
|
||||
/**
|
||||
* Get a subquery that returns the latest step of every qa in some qubas.
|
||||
* Currently, this is only used by the quiz reports. See
|
||||
* {@link quiz_attempts_report_table::add_latest_state_join()}.
|
||||
* {@see \mod_quiz\local\reports\attempts_report_table::add_latest_state_join()}.
|
||||
*
|
||||
* This method may be called publicly.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user