diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml
index 191de69..1360477 100644
--- a/.github/workflows/moodle-ci.yml
+++ b/.github/workflows/moodle-ci.yml
@@ -1,138 +1,113 @@
-name: Moodle plugin CI
+name: Moodle Plugin CI
+
on: [push, pull_request]
jobs:
test:
- runs-on: 'ubuntu-latest'
- strategy:
- fail-fast: false
- matrix:
- include:
- - php: '8.1'
- moodle-branch: 'MOODLE_402_STABLE'
- database: 'mariadb'
- - php: '8.1'
- moodle-branch: 'MOODLE_402_STABLE'
- database: 'mariadb'
- - php: '8.0'
- moodle-branch: 'MOODLE_402_STABLE'
- database: 'mariadb'
- - php: '8.0'
- moodle-branch: 'MOODLE_402_STABLE'
- database: 'mariadb'
- - php: '8.1'
- moodle-branch: 'MOODLE_401_STABLE'
- database: 'mariadb'
- - php: '8.1'
- moodle-branch: 'MOODLE_401_STABLE'
- database: 'mariadb'
- - php: '8.0'
- moodle-branch: 'MOODLE_401_STABLE'
- database: 'mariadb'
- - php: '8.0'
- moodle-branch: 'MOODLE_401_STABLE'
- database: 'mariadb'
- - php: '7.4'
- moodle-branch: 'MOODLE_401_STABLE'
- database: 'mariadb'
- - php: '7.4'
- moodle-branch: 'MOODLE_401_STABLE'
- database: 'mariadb'
- - php: '7.4'
- moodle-branch: 'MOODLE_400_STABLE'
- database: 'mariadb'
- - php: '7.4'
- moodle-branch: 'MOODLE_400_STABLE'
- database: 'mariadb'
+ runs-on: ubuntu-22.04
services:
postgres:
- image: postgres
+ image: postgres:13
env:
POSTGRES_USER: 'postgres'
POSTGRES_HOST_AUTH_METHOD: 'trust'
- options: >-
- --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 3
ports:
- 5432:5432
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
mariadb:
- image: mariadb:10.5
+ image: mariadb:10
env:
MYSQL_USER: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
+ MYSQL_CHARACTER_SET_SERVER: "utf8mb4"
+ MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci"
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3
+ strategy:
+ fail-fast: false
+ matrix:
+ php: ['8.0']
+ moodle-branch: ['MOODLE_403_STABLE', 'MOODLE_404_STABLE']
+ database: [pgsql, mariadb]
+
steps:
- - name: Checkout
- uses: actions/checkout@v3
+ - name: Check out repository code
+ uses: actions/checkout@v4
with:
path: plugin
- - name: Setup PHP
+ - name: Setup PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- extensions: zip, gd, mbstring, pgsql, mysqli
+ extensions: ${{ matrix.extensions }}
+ ini-values: max_input_vars=5000
+ coverage: none
- - name: Deploy moodle-plugin-ci
+ - name: Initialise moodle-plugin-ci
run: |
- composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
- # Add dirs to $PATH
+ composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4
echo $(cd ci/bin; pwd) >> $GITHUB_PATH
echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
- # PHPUnit depends on en_AU.UTF-8 locale
sudo locale-gen en_AU.UTF-8
- - name: Install Moodle
- # Need explicit IP to stop mysql client fail on attempt to use unix socket.
+ echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
+
+ - name: Install moodle-plugin-ci
run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
env:
DB: ${{ matrix.database }}
MOODLE_BRANCH: ${{ matrix.moodle-branch }}
- IGNORE_NAMES: 'mobile_*.mustache'
- IGNORE_PATHS: 'templates/local/mobile'
- - name: phplint
- if: ${{ always() }}
+ - name: PHP Lint
+ if: ${{ !cancelled() }}
run: moodle-plugin-ci phplint
- - name: phpcpd
- if: ${{ always() }}
- run: moodle-plugin-ci phpcpd || true
+ - name: PHP Copy/Paste Detector
+ continue-on-error: true # This step will show errors but will not fail
+ if: ${{ !cancelled() }}
+ run: moodle-plugin-ci phpcpd
- - name: phpmd
- if: ${{ always() }}
+ - name: PHP Mess Detector
+ continue-on-error: true # This step will show errors but will not fail
+ if: ${{ !cancelled() }}
run: moodle-plugin-ci phpmd
- - name: codechecker
- if: ${{ always() }}
- run: moodle-plugin-ci codechecker
+ - name: Moodle Code Checker
+ if: ${{ !cancelled() }}
+ run: moodle-plugin-ci phpcs --max-warnings 0
- - name: validate
- if: ${{ always() }}
+ - name: Moodle PHPDoc Checker
+ if: ${{ !cancelled() }}
+ run: moodle-plugin-ci phpdoc --max-warnings 0
+
+ - name: Validating
+ if: ${{ !cancelled() }}
run: moodle-plugin-ci validate
- - name: savepoints
- if: ${{ always() }}
+ - name: Check upgrade savepoints
+ if: ${{ !cancelled() }}
run: moodle-plugin-ci savepoints
- - name: mustache
- if: ${{ always() }}
+ - name: Mustache Lint
+ if: ${{ !cancelled() }}
run: moodle-plugin-ci mustache
- - name: grunt
- if: ${{ always() }}
- run: moodle-plugin-ci grunt
+ - name: Grunt
+ if: ${{ !cancelled() }}
+ run: moodle-plugin-ci grunt --max-lint-warnings 0
- - name: phpunit
- if: ${{ always() }}
- run: moodle-plugin-ci phpunit
+ - name: PHPUnit tests
+ if: ${{ !cancelled() }}
+ run: moodle-plugin-ci phpunit --fail-on-warning
- - name: behat
- if: ${{ always() }}
+ - name: Behat features
+ if: ${{ !cancelled() }}
run: moodle-plugin-ci behat --profile chrome
+
+ - name: Mark cancelled jobs as failed.
+ if: ${{ cancelled() }}
+ run: exit 1
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100755
index 9879a04..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-language: php
-
-addons:
- postgresql: "9.5"
-
-services:
- - mysql
- - postgresql
- - docker
-
-cache:
- directories:
- - $HOME/.composer/cache
- - $HOME/.npm
-
-php:
- - 7.2
- - 7.3
- - 7.4
-
-env:
- global:
- - MOODLE_BRANCH=MOODLE_39_STABLE
- matrix:
- - DB=pgsql
- - DB=mysqli
-
-before_install:
- - phpenv config-rm xdebug.ini
- - cd ../..
- - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
- - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
-
-install:
- - moodle-plugin-ci install
-
-script:
- - moodle-plugin-ci phplint
- - moodle-plugin-ci phpcpd
- - moodle-plugin-ci phpmd
- - moodle-plugin-ci codechecker
- - moodle-plugin-ci validate
- - moodle-plugin-ci savepoints
- - moodle-plugin-ci mustache
- - moodle-plugin-ci grunt
- - moodle-plugin-ci phpdoc
- - moodle-plugin-ci phpunit --coverage-clover
- - moodle-plugin-ci behat
-
-after_success:
- - moodle-plugin-ci coveralls-upload
diff --git a/action.php b/action.php
index f93300d..191e8eb 100755
--- a/action.php
+++ b/action.php
@@ -33,16 +33,14 @@
$cmid = capquiz_urls::require_course_module_id_param();
$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST);
require_login($cm->course, false, $cm);
+
$context = \context_module::instance($cmid);
require_capability('mod/capquiz:instructor', $context);
$action = required_param('action', PARAM_TEXT);
-$cmid = capquiz_urls::require_course_module_id_param();
$capquiz = new capquiz($cmid);
-if ($capquiz) {
- capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlasync);
- capquiz_action_performer::perform($action, $capquiz);
-}
+capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlasync);
+capquiz_action_performer::perform($action, $capquiz);
capquiz_urls::redirect_to_dashboard();
diff --git a/adminlib.php b/adminlib.php
index 2e09f85..5ce721b 100644
--- a/adminlib.php
+++ b/adminlib.php
@@ -38,37 +38,33 @@
class capquiz_admin_page_manage_capquiz_plugins extends admin_externalpage {
/** @var string the name of plugin subtype */
- private $subtype = '';
+ private string $subtype;
/**
- * The constructor - calls parent constructor
+ * Constructor.
*
* @param string $subtype
*/
- public function __construct($subtype) {
+ public function __construct(string $subtype) {
$this->subtype = $subtype;
- $url = new moodle_url('/mod/capquiz/adminmanageplugins.php', array('subtype' => $subtype));
- parent::__construct('manage' . $subtype . 'plugins',
- get_string('manage' . $subtype . 'plugins', 'capquiz'),
- $url);
+ $url = new moodle_url('/mod/capquiz/adminmanageplugins.php', ['subtype' => $subtype]);
+ parent::__construct('manage' . $subtype . 'plugins', get_string('manage' . $subtype . 'plugins', 'capquiz'), $url);
}
/**
* Search plugins for the specified string
*
* @param string $query The string to search for
- * @return array
*/
- public function search($query) {
- if ($result = parent::search($query)) {
+ public function search($query): array {
+ $result = parent::search($query);
+ if ($result) {
return $result;
}
-
$found = false;
-
foreach (core_component::get_plugin_list($this->subtype) as $name => $notused) {
- if (strpos(core_text::strtolower(get_string('pluginname', $this->subtype . '_' . $name)),
- $query) !== false) {
+ $pluginname = get_string('pluginname', $this->subtype . '_' . $name);
+ if (str_contains(core_text::strtolower($pluginname), $query)) {
$found = true;
break;
}
@@ -76,10 +72,10 @@ public function search($query) {
if ($found) {
$result = new stdClass();
$result->page = $this;
- $result->settings = array();
- return array($this->name => $result);
+ $result->settings = [];
+ return [$this->name => $result];
} else {
- return array();
+ return [];
}
}
}
@@ -95,60 +91,53 @@ public function search($query) {
*/
class capquiz_plugin_manager {
- /** @var object the url of the manage capquiz plugin page */
- private $pageurl;
- /** @var string any error from the current action */
- private $error = '';
+ /** @var moodle_url the url of the manage capquiz plugin page */
+ private moodle_url $pageurl;
+
/** @var string report */
- private $subtype = '';
+ private string $subtype;
+
+ /** @var string any error from the current action */
+ private string $error = '';
/**
- * Constructor for this capquiz plugin manager
+ * Constructor.
+ *
* @param string $subtype
*/
- public function __construct($subtype) {
- $this->pageurl = new moodle_url('/mod/capquiz/adminmanageplugins.php', array('subtype' => $subtype));
+ public function __construct(string $subtype) {
+ $this->pageurl = new moodle_url('/mod/capquiz/adminmanageplugins.php', ['subtype' => $subtype]);
$this->subtype = $subtype;
}
/**
* This is the entry point for this controller class.
*
- * @param string $action - The action to perform
- * @param string $plugin - Optional name of a plugin type to perform the action on
- * @return None
+ * @param string $action Action to perform
+ * @param ?string $plugin Optional name of a plugin type to perform the action on
*/
- public function execute($action, $plugin) {
- if ($action == null) {
- $action = 'view';
- }
-
+ public function execute(string $action = 'view', ?string $plugin = null): void {
$this->check_permissions();
-
- // Process.
- if ($action == 'hide' && $plugin != null) {
- $action = $this->hide_plugin($plugin);
- } else if ($action == 'show' && $plugin != null) {
- $action = $this->show_plugin($plugin);
- } else if ($action == 'moveup' && $plugin != null) {
- $action = $this->move_plugin($plugin, 'up');
- } else if ($action == 'movedown' && $plugin != null) {
- $action = $this->move_plugin($plugin, 'down');
+ if ($plugin !== null) {
+ if ($action === 'hide') {
+ $action = $this->hide_plugin($plugin);
+ } else if ($action === 'show') {
+ $action = $this->show_plugin($plugin);
+ } else if ($action === 'moveup') {
+ $action = $this->move_plugin($plugin, 'up');
+ } else if ($action === 'movedown') {
+ $action = $this->move_plugin($plugin, 'down');
+ }
}
-
- // View.
- if ($action == 'view') {
+ if ($action === 'view') {
$this->view_plugins_table();
}
}
/**
* Check this user has permission to edit the list of installed plugins
- *
- * @return None
*/
- private function check_permissions() {
- // Check permissions.
+ private function check_permissions(): void {
require_login();
$systemcontext = context_system::instance();
require_capability('moodle/site:config', $systemcontext);
@@ -160,7 +149,7 @@ private function check_permissions() {
* @param string $plugin - The plugin to hide
* @return string The next page to display
*/
- public function hide_plugin($plugin) {
+ public function hide_plugin(string $plugin): string {
set_config('disabled', 1, $this->subtype . '_' . $plugin);
core_plugin_manager::reset_caches();
return 'view';
@@ -172,7 +161,7 @@ public function hide_plugin($plugin) {
* @param string $plugin - The plugin to show
* @return string The next page to display
*/
- public function show_plugin($plugin) {
+ public function show_plugin(string $plugin): string {
set_config('disabled', 0, $this->subtype . '_' . $plugin);
core_plugin_manager::reset_caches();
return 'view';
@@ -185,7 +174,7 @@ public function show_plugin($plugin) {
* @param string $dir - up or down
* @return string The next page to display
*/
- public function move_plugin($plugintomove, $dir) {
+ public function move_plugin(string $plugintomove, string $dir): string {
// Get a list of the current plugins.
$plugins = $this->get_sorted_plugins_list();
@@ -229,11 +218,9 @@ public function move_plugin($plugintomove, $dir) {
*
* @return array The list of plugins
*/
- public function get_sorted_plugins_list() {
+ public function get_sorted_plugins_list(): array {
$names = core_component::get_plugin_list($this->subtype);
-
- $result = array();
-
+ $result = [];
foreach ($names as $name => $path) {
$idx = get_config($this->subtype . '_' . $name, 'sortorder');
if (!$idx) {
@@ -245,16 +232,13 @@ public function get_sorted_plugins_list() {
$result[$idx] = $name;
}
ksort($result);
-
return $result;
}
/**
* Write the HTML for the capquiz plugins table.
- *
- * @return None
*/
- private function view_plugins_table() {
+ private function view_plugins_table(): void {
global $OUTPUT, $CFG;
require_once($CFG->libdir . '/tablelib.php');
@@ -262,11 +246,10 @@ private function view_plugins_table() {
$this->view_header();
$table = new flexible_table($this->subtype . 'pluginsadminttable');
$table->define_baseurl($this->pageurl);
- $table->define_columns(array('pluginname', 'version', 'hideshow', 'order',
- 'settings', 'uninstall'));
- $table->define_headers(array(get_string($this->subtype . 'type', 'capquiz'),
+ $table->define_columns(['pluginname', 'version', 'hideshow', 'order', 'settings', 'uninstall']);
+ $table->define_headers([get_string($this->subtype . 'type', 'capquiz'),
get_string('version'), get_string('hideshow', 'capquiz'),
- get_string('order'), get_string('settings'), get_string('uninstallplugin', 'core_admin')));
+ get_string('order'), get_string('settings'), get_string('uninstallplugin', 'core_admin')]);
$table->set_attribute('id', $this->subtype . 'plugins');
$table->set_attribute('class', 'admintable generaltable');
$table->setup();
@@ -275,7 +258,7 @@ private function view_plugins_table() {
$shortsubtype = substr($this->subtype, strlen('capquiz'));
foreach ($plugins as $idx => $plugin) {
- $row = array();
+ $row = [];
$class = '';
$row[] = get_string('pluginname', $this->subtype . '_' . $plugin);
@@ -294,7 +277,7 @@ private function view_plugins_table() {
if (!$idx == 0) {
$movelinks .= $this->format_icon_link('moveup', $plugin, 't/up', get_string('up'));
} else {
- $movelinks .= $OUTPUT->spacer(array('width' => 16));
+ $movelinks .= $OUTPUT->spacer(['width' => 16]);
}
if ($idx != count($plugins) - 1) {
$movelinks .= $this->format_icon_link('movedown', $plugin, 't/down', get_string('down'));
@@ -302,9 +285,9 @@ private function view_plugins_table() {
$row[] = $movelinks;
$exists = file_exists($CFG->dirroot . '/mod/capquiz/' . $shortsubtype . '/' . $plugin . '/settings.php');
- if ($row[1] != '' && $exists) {
- $row[] = html_writer::link(new moodle_url('/admin/settings.php',
- array('section' => $this->subtype . '_' . $plugin)), get_string('settings'));
+ if ($row[1] !== '' && $exists) {
+ $url = new moodle_url('/admin/settings.php', ['section' => $this->subtype . '_' . $plugin]);
+ $row[] = html_writer::link($url, get_string('settings'));
} else {
$row[] = ' ';
}
@@ -320,10 +303,8 @@ private function view_plugins_table() {
/**
* Write the page header
- *
- * @return None
*/
- private function view_header() {
+ private function view_header(): void {
global $OUTPUT;
admin_externalpage_setup('manage' . $this->subtype . 'plugins');
// Print the page heading.
@@ -340,11 +321,8 @@ private function view_header() {
* @param string $alt The string description of the link used as the title and alt text
* @return string The icon/link
*/
- private function format_icon_link($action, $plugin, $icon, $alt) {
+ private function format_icon_link(string $action, string $plugin, string $icon, string $alt): string {
global $OUTPUT;
-
- $url = $this->pageurl;
-
if ($action === 'delete') {
$url = core_plugin_manager::instance()->get_uninstall_url($this->subtype . '_' . $plugin, 'manage');
if (!$url) {
@@ -352,19 +330,15 @@ private function format_icon_link($action, $plugin, $icon, $alt) {
}
return html_writer::link($url, get_string('uninstallplugin', 'core_admin'));
}
-
- return $OUTPUT->action_icon(new moodle_url($url,
- array('action' => $action, 'plugin' => $plugin, 'sesskey' => sesskey())),
- new pix_icon($icon, $alt, 'moodle', array('title' => $alt)),
- null, array('title' => $alt)) . ' ';
+ $url = new moodle_url($this->pageurl, ['action' => $action, 'plugin' => $plugin, 'sesskey' => sesskey()]);
+ $icon = new pix_icon($icon, $alt, 'moodle', ['title' => $alt]);
+ return $OUTPUT->action_icon($url, $icon, null, ['title' => $alt]) . ' ';
}
/**
* Write the page footer
- *
- * @return None
*/
- private function view_footer() {
+ private function view_footer(): void {
global $OUTPUT;
echo $OUTPUT->footer();
}
diff --git a/adminmanageplugins.php b/adminmanageplugins.php
index 3d9d758..ca758fd 100644
--- a/adminmanageplugins.php
+++ b/adminmanageplugins.php
@@ -30,17 +30,14 @@
require_once($CFG->dirroot . '/mod/capquiz/adminlib.php');
$subtype = required_param('subtype', PARAM_PLUGIN);
-$action = optional_param('action', null, PARAM_PLUGIN);
+$action = optional_param('action', 'view', PARAM_PLUGIN);
$plugin = optional_param('plugin', null, PARAM_PLUGIN);
if (!empty($plugin)) {
require_sesskey();
}
-// Create the class for this controller.
-$pluginmanager = new capquiz_plugin_manager($subtype);
-
$PAGE->set_context(context_system::instance());
-// Execute the controller.
+$pluginmanager = new capquiz_plugin_manager($subtype);
$pluginmanager->execute($action, $plugin);
diff --git a/amd/build/edit_questions.min.js b/amd/build/edit_questions.min.js
index ccf188c..e709700 100755
--- a/amd/build/edit_questions.min.js
+++ b/amd/build/edit_questions.min.js
@@ -4,6 +4,6 @@
* @copyright 2019 NTNU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define("mod_capquiz/edit_questions",["jquery"],(function($){var parameters={courseModuleId:0};function sendAction(data,onSuccess,onError){$.ajax({type:"post",url:"action.php",data:data,success:onSuccess,error:onError})}function sendDefaultQuestionRating(data,rating,onSuccess,onError){sendAction({action:"set-default-question-rating",id:parameters.courseModuleId,rating:rating},onSuccess,onError)}function sendQuestionRating(data,rating,onSuccess,onError){sendAction({action:"set-question-rating",id:parameters.courseModuleId,"question-id":data.questionId,rating:rating},onSuccess,onError)}function submitInput($input,sendInput,data){$input.data("saving",!0),$input.data("dirty",!1);var $indicator=$input.next();$indicator.css("color","blue"),sendInput(data,$input.val(),(function(){!0===$input.data("dirty")?submitInput($input,sendInput,data):($indicator.css("color","green"),$input.data("dirty",!1),$input.data("saving",!1))}),(function(){$indicator.css("color","red")}))}function submitQuestionRating($input){submitInput($input,sendQuestionRating,{questionId:$input.data("question-id")})}function submitDefaultQuestionRating($input){submitInput($input,sendDefaultQuestionRating,null)}function registerListener(query,submit){$(document).on("input",query,(function(event){var $input=$(event.target);!0!==$input.data("saving")?submit($input):$input.data("dirty",!0)}))}function sortTable($header){var column=$header.index(),$table=$header.parent().parent(),$rows=$table.find("tr:gt(0)").toArray().sort((function(rowA,rowB){var $colA=$(rowA).children("td").eq(0),$colB=$(rowB).children("td").eq(0);return parseInt($colA.text())-parseInt($colB.text())}));$table.append($rows),$rows=$table.find("tr:gt(0)").toArray().sort((function(rowA,rowB){var $colA=$(rowA).children("td").eq(column),$colB=$(rowB).children("td").eq(column),$itemA=$colA.find(".capquiz-sortable-item"),$itemB=$colB.find(".capquiz-sortable-item"),valA=0===$itemA.length?$colA.html():0===$itemA.val().length?$itemA.html():$itemA.val(),valB=0===$itemB.length?$colB.html():0===$itemB.val().length?$itemB.html():$itemB.val();return $.isNumeric(valA)&&$.isNumeric(valB)?valA-valB:valA.toString().localeCompare(valB)}));var ascending="true"===$table.data("asc");$table.data("asc",ascending?"false":"true");var iconName=ascending?"fa-arrow-up":"fa-arrow-down";$.each($table.find(".capquiz-sortable"),(function(){$(this).find(".fa").remove()})),$header.prepend(''),ascending||($rows=$rows.reverse()),$table.append($rows);var i=1;$table.find("tr:gt(0)").each((function(){$(this).find("td:first-child").html(i),i++}))}return{initialize:function(courseModuleId){parameters.courseModuleId=courseModuleId,registerListener(".capquiz-question-rating input",submitQuestionRating),registerListener(".capquiz-default-question-rating input",submitDefaultQuestionRating),$(".capquiz-question-rating-submit-wrapper button").each((function(index,object){$(object).attr("tabindex",-1)})),$(document).on("click",".capquiz-sortable",(function(){sortTable($(this))})),$(".capquiz-sortable-default-asc").each((function(){sortTable($(this)),sortTable($(this))})),$(".capquiz-sortable-default-desc").each((function(){sortTable($(this))})),$(".capquiz-add-selected-questions").on("click",(function(){var questionIds="";$("#categoryquestions td input[type=checkbox]:checked").each((function(){questionIds+=$(this).attr("name").slice(1)+","})),$.post("action.php",{action:"add-question",id:parameters.courseModuleId,"question-id":questionIds},(function(){location.reload()}))}))}}}));
+define("mod_capquiz/edit_questions",["jquery"],(function($){var parameters={courseModuleId:0};function sendAction(data,onSuccess,onError){$.ajax({type:"post",url:"action.php",data:data,success:onSuccess,error:onError})}function sendDefaultQuestionRating(data,rating,onSuccess,onError){sendAction({action:"set-default-question-rating",id:parameters.courseModuleId,rating:rating},onSuccess,onError)}function sendQuestionRating(data,rating,onSuccess,onError){sendAction({action:"set-question-rating",id:parameters.courseModuleId,"question-id":data.questionId,rating:rating},onSuccess,onError)}function submitInput($input,sendInput,data){$input.data("saving",!0),$input.data("dirty",!1);var $indicator=$input.next();$indicator.css("color","blue"),sendInput(data,$input.val(),(function(){!0===$input.data("dirty")?submitInput($input,sendInput,data):($indicator.css("color","green"),$input.data("dirty",!1),$input.data("saving",!1))}),(function(){$indicator.css("color","red")}))}function submitQuestionRating($input){submitInput($input,sendQuestionRating,{questionId:$input.data("question-id")})}function submitDefaultQuestionRating($input){submitInput($input,sendDefaultQuestionRating,null)}function registerListener(query,submit){$(document).on("input",query,(function(event){var $input=$(event.target);!0!==$input.data("saving")?submit($input):$input.data("dirty",!0)}))}function sortTable($header){const column=$header.index(),$table=$header.parent().parent();let $rows=$table.find("tr:gt(0)").toArray().sort((function(rowA,rowB){const $colA=$(rowA).children("td").eq(0),$colB=$(rowB).children("td").eq(0);return parseInt($colA.text())-parseInt($colB.text())}));$table.append($rows),$rows=$table.find("tr:gt(0)").toArray().sort((function(rowA,rowB){const $colA=$(rowA).children("td").eq(column),$colB=$(rowB).children("td").eq(column),$itemA=$colA.find(".capquiz-sortable-item"),$itemB=$colB.find(".capquiz-sortable-item");let valA,valB;return valA=0===$itemA.length?$colA.html():0===$itemA.val().length?$itemA.html():$itemA.val(),valB=0===$itemB.length?$colB.html():0===$itemB.val().length?$itemB.html():$itemB.val(),$.isNumeric(valA)&&$.isNumeric(valB)?valA-valB:valA.toString().localeCompare(valB)}));const ascending="true"===$table.data("asc");$table.data("asc",ascending?"false":"true");const iconName=ascending?"fa-arrow-up":"fa-arrow-down";$.each($table.find(".capquiz-sortable"),(function(){$(this).find(".fa").remove()})),$header.prepend(''),ascending||($rows=$rows.reverse()),$table.append($rows);let i=1;$table.find("tr:gt(0)").each((function(){$(this).find("td:first-child").html(i),i++}))}return{initialize:function(courseModuleId){parameters.courseModuleId=courseModuleId,registerListener(".capquiz-question-rating input",submitQuestionRating),registerListener(".capquiz-default-question-rating input",submitDefaultQuestionRating),$(".capquiz-question-rating-submit-wrapper button").each((function(index,object){$(object).attr("tabindex",-1)})),$(document).on("click",".capquiz-sortable",(function(){sortTable($(this))})),$(".capquiz-sortable-default-asc").each((function(){sortTable($(this)),sortTable($(this))})),$(".capquiz-sortable-default-desc").each((function(){sortTable($(this))})),$(".capquiz-add-selected-questions").on("click",(function(){var questionIds="";$("#categoryquestions td input[type=checkbox]:checked").each((function(){questionIds+=$(this).attr("name").slice(1)+","})),$.post("action.php",{action:"add-question",id:parameters.courseModuleId,"question-id":questionIds},(function(){location.reload()}))}))}}}));
//# sourceMappingURL=edit_questions.min.js.map
\ No newline at end of file
diff --git a/amd/build/edit_questions.min.js.map b/amd/build/edit_questions.min.js.map
index 1e3d7d1..77d73e9 100644
--- a/amd/build/edit_questions.min.js.map
+++ b/amd/build/edit_questions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"edit_questions.min.js","sources":["../src/edit_questions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module mod_capquiz\n * @author Sebastian S. Gundersen \n * @copyright 2019 NTNU\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n var parameters = {\n courseModuleId: 0,\n };\n\n /**\n * Send an action to the server.\n * @param {Object} data\n * @param {Object} onSuccess\n * @param {Object} onError\n */\n function sendAction(data, onSuccess, onError) {\n $.ajax({\n type: 'post',\n url: 'action.php',\n data: data,\n success: onSuccess,\n error: onError\n });\n }\n\n /**\n * Send the new default rating for the question list to the server.\n * @param {Object} data\n * @param {number} rating\n * @param {Object} onSuccess\n * @param {Object} onError\n */\n function sendDefaultQuestionRating(data, rating, onSuccess, onError) {\n sendAction({\n 'action': 'set-default-question-rating',\n 'id': parameters.courseModuleId,\n 'rating': rating,\n }, onSuccess, onError);\n }\n\n /**\n * Send the new rating for the question to the server.\n * @param {Object} data\n * @param {number} rating\n * @param {Object} onSuccess\n * @param {Object} onError\n */\n function sendQuestionRating(data, rating, onSuccess, onError) {\n sendAction({\n 'action': 'set-question-rating',\n 'id': parameters.courseModuleId,\n 'question-id': data.questionId,\n 'rating': rating,\n }, onSuccess, onError);\n }\n\n /**\n * Send the new value, and avoid race condition.\n * @param {Object} $input\n * @param {Object} sendInput\n * @param {Object} data\n */\n function submitInput($input, sendInput, data) {\n $input.data('saving', true);\n $input.data('dirty', false);\n var $indicator = $input.next();\n $indicator.css('color', 'blue');\n sendInput(data, $input.val(), function() {\n if ($input.data('dirty') === true) {\n submitInput($input, sendInput, data);\n } else {\n $indicator.css('color', 'green');\n $input.data('dirty', false);\n $input.data('saving', false);\n }\n }, function() {\n $indicator.css('color', 'red');\n });\n }\n\n /**\n * Send the new rating for the question, and avoid race condition.\n * @param {Object} $input\n */\n function submitQuestionRating($input) {\n submitInput($input, sendQuestionRating, {questionId: $input.data('question-id')});\n }\n\n /**\n * Send the new default rating for the question list, and avoid race condition.\n * @param {Object} $input\n */\n function submitDefaultQuestionRating($input) {\n submitInput($input, sendDefaultQuestionRating, null);\n }\n\n /**\n * Register an input event listener for submission.\n * @param {string} query\n * @param {Object} submit\n */\n function registerListener(query, submit) {\n $(document).on('input', query, function(event) {\n var $input = $(event.target);\n var isBeingSaved = $input.data('saving');\n if (isBeingSaved === true) {\n $input.data('dirty', true);\n return;\n }\n submit($input);\n });\n }\n\n /**\n * Sorts a table by the respective column based on $header.\n * It searches for an element of class \"capquiz-sortable-item\" inside the
, and if found,\n * the value attribute is used if it exists. Otherwise, the inner html is used to sort by.\n *\n * The
tag may not have the item class, as it has no effect on the sorting.\n * Their children elements are not required to have the class either. The inner html of
will be used then.\n *\n * The first column in the table must be an index of the row.\n *\n * @param {Object} $header The header column for which to sort the table by.\n */\n function sortTable($header) {\n var column = $header.index();\n var $table = $header.parent().parent();\n var $rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {\n var $colA = $(rowA).children('td').eq(0);\n var $colB = $(rowB).children('td').eq(0);\n return parseInt($colA.text()) - parseInt($colB.text());\n });\n $table.append($rows);\n $rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {\n var $colA = $(rowA).children('td').eq(column);\n var $colB = $(rowB).children('td').eq(column);\n var $itemA = $colA.find('.capquiz-sortable-item');\n var $itemB = $colB.find('.capquiz-sortable-item');\n var valA = ($itemA.length === 0 ? $colA.html() : ($itemA.val().length === 0 ? $itemA.html() : $itemA.val()));\n var valB = ($itemB.length === 0 ? $colB.html() : ($itemB.val().length === 0 ? $itemB.html() : $itemB.val()));\n if ($.isNumeric(valA) && $.isNumeric(valB)) {\n return valA - valB;\n } else {\n return valA.toString().localeCompare(valB);\n }\n });\n var ascending = ($table.data('asc') === 'true');\n $table.data('asc', ascending ? 'false' : 'true');\n var iconName = (ascending ? 'fa-arrow-up' : 'fa-arrow-down');\n $.each($table.find('.capquiz-sortable'), function() {\n $(this).find('.fa').remove();\n });\n $header.prepend('');\n if (!ascending) {\n $rows = $rows.reverse();\n }\n $table.append($rows);\n var i = 1;\n $table.find('tr:gt(0)').each(function() {\n $(this).find('td:first-child').html(i);\n i++;\n });\n }\n\n /**\n * Register click event listeners for the sortable table columns.\n */\n function registerSortListener() {\n $(document).on('click', '.capquiz-sortable', function() {\n sortTable($(this));\n });\n $('.capquiz-sortable-default-asc').each(function() {\n sortTable($(this));\n sortTable($(this));\n });\n $('.capquiz-sortable-default-desc').each(function() {\n sortTable($(this));\n });\n }\n\n /**\n * Set the tab indices for the question rating elements to be more user friendly.\n */\n function fixTabIndicesForQuestionRatingInputs() {\n $('.capquiz-question-rating-submit-wrapper button').each(function(index, object) {\n $(object).attr('tabindex', -1);\n });\n }\n\n /**\n * Register click event listener for \"Add to quiz\" button.\n */\n function listenAddToQuiz() {\n $('.capquiz-add-selected-questions').on('click', function() {\n var questionIds = '';\n $('#categoryquestions td input[type=checkbox]:checked').each(function() {\n questionIds += $(this).attr('name').slice(1) + ',';\n });\n $.post('action.php', {\n 'action': 'add-question',\n 'id': parameters.courseModuleId,\n 'question-id': questionIds,\n }, function() {\n location.reload();\n });\n });\n }\n\n return {\n initialize: function(courseModuleId) {\n parameters.courseModuleId = courseModuleId;\n registerListener('.capquiz-question-rating input', submitQuestionRating);\n registerListener('.capquiz-default-question-rating input', submitDefaultQuestionRating);\n fixTabIndicesForQuestionRatingInputs();\n registerSortListener();\n listenAddToQuiz();\n }\n };\n\n});\n"],"names":["define","$","parameters","courseModuleId","sendAction","data","onSuccess","onError","ajax","type","url","success","error","sendDefaultQuestionRating","rating","sendQuestionRating","questionId","submitInput","$input","sendInput","$indicator","next","css","val","submitQuestionRating","submitDefaultQuestionRating","registerListener","query","submit","document","on","event","target","sortTable","$header","column","index","$table","parent","$rows","find","toArray","sort","rowA","rowB","$colA","children","eq","$colB","parseInt","text","append","$itemA","$itemB","valA","length","html","valB","isNumeric","toString","localeCompare","ascending","iconName","each","this","remove","prepend","reverse","i","initialize","object","attr","questionIds","slice","post","location","reload"],"mappings":";;;;;;AAsBAA,oCAAO,CAAC,WAAW,SAASC,OAEpBC,WAAa,CACbC,eAAgB,YASXC,WAAWC,KAAMC,UAAWC,SACjCN,EAAEO,KAAK,CACHC,KAAM,OACNC,IAAK,aACLL,KAAMA,KACNM,QAASL,UACTM,MAAOL,mBAWNM,0BAA0BR,KAAMS,OAAQR,UAAWC,SACxDH,WAAW,QACG,iCACJF,WAAWC,sBACPW,QACXR,UAAWC,kBAUTQ,mBAAmBV,KAAMS,OAAQR,UAAWC,SACjDH,WAAW,QACG,yBACJF,WAAWC,6BACFE,KAAKW,kBACVF,QACXR,UAAWC,kBASTU,YAAYC,OAAQC,UAAWd,MACpCa,OAAOb,KAAK,UAAU,GACtBa,OAAOb,KAAK,SAAS,OACjBe,WAAaF,OAAOG,OACxBD,WAAWE,IAAI,QAAS,QACxBH,UAAUd,KAAMa,OAAOK,OAAO,YACG,IAAzBL,OAAOb,KAAK,SACZY,YAAYC,OAAQC,UAAWd,OAE/Be,WAAWE,IAAI,QAAS,SACxBJ,OAAOb,KAAK,SAAS,GACrBa,OAAOb,KAAK,UAAU,OAE3B,WACCe,WAAWE,IAAI,QAAS,mBAQvBE,qBAAqBN,QAC1BD,YAAYC,OAAQH,mBAAoB,CAACC,WAAYE,OAAOb,KAAK,0BAO5DoB,4BAA4BP,QACjCD,YAAYC,OAAQL,0BAA2B,eAQ1Ca,iBAAiBC,MAAOC,QAC7B3B,EAAE4B,UAAUC,GAAG,QAASH,OAAO,SAASI,WAChCb,OAASjB,EAAE8B,MAAMC,SAEA,IADFd,OAAOb,KAAK,UAK/BuB,OAAOV,QAHHA,OAAOb,KAAK,SAAS,eAmBxB4B,UAAUC,aACXC,OAASD,QAAQE,QACjBC,OAASH,QAAQI,SAASA,SAC1BC,MAAQF,OAAOG,KAAK,YAAYC,UAAUC,MAAK,SAASC,KAAMC,UAC1DC,MAAQ5C,EAAE0C,MAAMG,SAAS,MAAMC,GAAG,GAClCC,MAAQ/C,EAAE2C,MAAME,SAAS,MAAMC,GAAG,UAC/BE,SAASJ,MAAMK,QAAUD,SAASD,MAAME,WAEnDb,OAAOc,OAAOZ,OACdA,MAAQF,OAAOG,KAAK,YAAYC,UAAUC,MAAK,SAASC,KAAMC,UACtDC,MAAQ5C,EAAE0C,MAAMG,SAAS,MAAMC,GAAGZ,QAClCa,MAAQ/C,EAAE2C,MAAME,SAAS,MAAMC,GAAGZ,QAClCiB,OAASP,MAAML,KAAK,0BACpBa,OAASL,MAAMR,KAAK,0BACpBc,KAA0B,IAAlBF,OAAOG,OAAeV,MAAMW,OAAkC,IAAxBJ,OAAO7B,MAAMgC,OAAeH,OAAOI,OAASJ,OAAO7B,MACjGkC,KAA0B,IAAlBJ,OAAOE,OAAeP,MAAMQ,OAAkC,IAAxBH,OAAO9B,MAAMgC,OAAeF,OAAOG,OAASH,OAAO9B,aACjGtB,EAAEyD,UAAUJ,OAASrD,EAAEyD,UAAUD,MAC1BH,KAAOG,KAEPH,KAAKK,WAAWC,cAAcH,aAGzCI,UAAoC,SAAvBxB,OAAOhC,KAAK,OAC7BgC,OAAOhC,KAAK,MAAOwD,UAAY,QAAU,YACrCC,SAAYD,UAAY,cAAgB,gBAC5C5D,EAAE8D,KAAK1B,OAAOG,KAAK,sBAAsB,WACrCvC,EAAE+D,MAAMxB,KAAK,OAAOyB,YAExB/B,QAAQgC,QAAQ,gBAAkBJ,SAAW,UACxCD,YACDtB,MAAQA,MAAM4B,WAElB9B,OAAOc,OAAOZ,WACV6B,EAAI,EACR/B,OAAOG,KAAK,YAAYuB,MAAK,WACzB9D,EAAE+D,MAAMxB,KAAK,kBAAkBgB,KAAKY,GACpCA,aAgDD,CACHC,WAAY,SAASlE,gBACjBD,WAAWC,eAAiBA,eAC5BuB,iBAAiB,iCAAkCF,sBACnDE,iBAAiB,yCAA0CD,6BA5B/DxB,EAAE,kDAAkD8D,MAAK,SAAS3B,MAAOkC,QACrErE,EAAEqE,QAAQC,KAAK,YAAa,MAjBhCtE,EAAE4B,UAAUC,GAAG,QAAS,qBAAqB,WACzCG,UAAUhC,EAAE+D,UAEhB/D,EAAE,iCAAiC8D,MAAK,WACpC9B,UAAUhC,EAAE+D,OACZ/B,UAAUhC,EAAE+D,UAEhB/D,EAAE,kCAAkC8D,MAAK,WACrC9B,UAAUhC,EAAE+D,UAiBhB/D,EAAE,mCAAmC6B,GAAG,SAAS,eACzC0C,YAAc,GAClBvE,EAAE,sDAAsD8D,MAAK,WACzDS,aAAevE,EAAE+D,MAAMO,KAAK,QAAQE,MAAM,GAAK,OAEnDxE,EAAEyE,KAAK,aAAc,QACP,kBACJxE,WAAWC,6BACFqE,cAChB,WACCG,SAASC"}
\ No newline at end of file
+{"version":3,"file":"edit_questions.min.js","sources":["../src/edit_questions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module mod_capquiz\n * @author Sebastian S. Gundersen \n * @copyright 2019 NTNU\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n var parameters = {\n courseModuleId: 0,\n };\n\n /**\n * Send an action to the server.\n * @param {Object} data\n * @param {Object} onSuccess\n * @param {Object} onError\n */\n function sendAction(data, onSuccess, onError) {\n $.ajax({\n type: 'post',\n url: 'action.php',\n data: data,\n success: onSuccess,\n error: onError\n });\n }\n\n /**\n * Send the new default rating for the question list to the server.\n * @param {Object} data\n * @param {number} rating\n * @param {Object} onSuccess\n * @param {Object} onError\n */\n function sendDefaultQuestionRating(data, rating, onSuccess, onError) {\n sendAction({\n 'action': 'set-default-question-rating',\n 'id': parameters.courseModuleId,\n 'rating': rating,\n }, onSuccess, onError);\n }\n\n /**\n * Send the new rating for the question to the server.\n * @param {Object} data\n * @param {number} rating\n * @param {Object} onSuccess\n * @param {Object} onError\n */\n function sendQuestionRating(data, rating, onSuccess, onError) {\n sendAction({\n 'action': 'set-question-rating',\n 'id': parameters.courseModuleId,\n 'question-id': data.questionId,\n 'rating': rating,\n }, onSuccess, onError);\n }\n\n /**\n * Send the new value, and avoid race condition.\n * @param {Object} $input\n * @param {Object} sendInput\n * @param {Object} data\n */\n function submitInput($input, sendInput, data) {\n $input.data('saving', true);\n $input.data('dirty', false);\n var $indicator = $input.next();\n $indicator.css('color', 'blue');\n sendInput(data, $input.val(), function() {\n if ($input.data('dirty') === true) {\n submitInput($input, sendInput, data);\n } else {\n $indicator.css('color', 'green');\n $input.data('dirty', false);\n $input.data('saving', false);\n }\n }, function() {\n $indicator.css('color', 'red');\n });\n }\n\n /**\n * Send the new rating for the question, and avoid race condition.\n * @param {Object} $input\n */\n function submitQuestionRating($input) {\n submitInput($input, sendQuestionRating, {questionId: $input.data('question-id')});\n }\n\n /**\n * Send the new default rating for the question list, and avoid race condition.\n * @param {Object} $input\n */\n function submitDefaultQuestionRating($input) {\n submitInput($input, sendDefaultQuestionRating, null);\n }\n\n /**\n * Register an input event listener for submission.\n * @param {string} query\n * @param {Object} submit\n */\n function registerListener(query, submit) {\n $(document).on('input', query, function(event) {\n var $input = $(event.target);\n var isBeingSaved = $input.data('saving');\n if (isBeingSaved === true) {\n $input.data('dirty', true);\n return;\n }\n submit($input);\n });\n }\n\n /**\n * Sorts a table by the respective column based on $header.\n * It searches for an element of class \"capquiz-sortable-item\" inside the
, and if found,\n * the value attribute is used if it exists. Otherwise, the inner html is used to sort by.\n *\n * The
tag may not have the item class, as it has no effect on the sorting.\n * Their children elements are not required to have the class either. The inner html of
will be used then.\n *\n * The first column in the table must be an index of the row.\n *\n * @param {Object} $header The header column for which to sort the table by.\n */\n function sortTable($header) {\n const column = $header.index();\n const $table = $header.parent().parent();\n let $rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {\n const $colA = $(rowA).children('td').eq(0);\n const $colB = $(rowB).children('td').eq(0);\n return parseInt($colA.text()) - parseInt($colB.text());\n });\n $table.append($rows);\n $rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {\n const $colA = $(rowA).children('td').eq(column);\n const $colB = $(rowB).children('td').eq(column);\n const $itemA = $colA.find('.capquiz-sortable-item');\n const $itemB = $colB.find('.capquiz-sortable-item');\n let valA;\n if ($itemA.length === 0) {\n valA = $colA.html();\n } else{\n valA = $itemA.val().length === 0 ? $itemA.html() : $itemA.val();\n }\n let valB;\n if ($itemB.length === 0) {\n valB = $colB.html();\n } else{\n valB = $itemB.val().length === 0 ? $itemB.html() : $itemB.val();\n }\n if ($.isNumeric(valA) && $.isNumeric(valB)) {\n return valA - valB;\n } else {\n return valA.toString().localeCompare(valB);\n }\n });\n const ascending = ($table.data('asc') === 'true');\n $table.data('asc', ascending ? 'false' : 'true');\n const iconName = (ascending ? 'fa-arrow-up' : 'fa-arrow-down');\n $.each($table.find('.capquiz-sortable'), function() {\n $(this).find('.fa').remove();\n });\n $header.prepend('');\n if (!ascending) {\n $rows = $rows.reverse();\n }\n $table.append($rows);\n let i = 1;\n $table.find('tr:gt(0)').each(function() {\n $(this).find('td:first-child').html(i);\n i++;\n });\n }\n\n /**\n * Register click event listeners for the sortable table columns.\n */\n function registerSortListener() {\n $(document).on('click', '.capquiz-sortable', function() {\n sortTable($(this));\n });\n $('.capquiz-sortable-default-asc').each(function() {\n sortTable($(this));\n sortTable($(this));\n });\n $('.capquiz-sortable-default-desc').each(function() {\n sortTable($(this));\n });\n }\n\n /**\n * Set the tab indices for the question rating elements to be more user friendly.\n */\n function fixTabIndicesForQuestionRatingInputs() {\n $('.capquiz-question-rating-submit-wrapper button').each(function(index, object) {\n $(object).attr('tabindex', -1);\n });\n }\n\n /**\n * Register click event listener for \"Add to quiz\" button.\n */\n function listenAddToQuiz() {\n $('.capquiz-add-selected-questions').on('click', function() {\n var questionIds = '';\n $('#categoryquestions td input[type=checkbox]:checked').each(function() {\n questionIds += $(this).attr('name').slice(1) + ',';\n });\n $.post('action.php', {\n 'action': 'add-question',\n 'id': parameters.courseModuleId,\n 'question-id': questionIds,\n }, function() {\n location.reload();\n });\n });\n }\n\n return {\n initialize: function(courseModuleId) {\n parameters.courseModuleId = courseModuleId;\n registerListener('.capquiz-question-rating input', submitQuestionRating);\n registerListener('.capquiz-default-question-rating input', submitDefaultQuestionRating);\n fixTabIndicesForQuestionRatingInputs();\n registerSortListener();\n listenAddToQuiz();\n }\n };\n\n});\n"],"names":["define","$","parameters","courseModuleId","sendAction","data","onSuccess","onError","ajax","type","url","success","error","sendDefaultQuestionRating","rating","sendQuestionRating","questionId","submitInput","$input","sendInput","$indicator","next","css","val","submitQuestionRating","submitDefaultQuestionRating","registerListener","query","submit","document","on","event","target","sortTable","$header","column","index","$table","parent","$rows","find","toArray","sort","rowA","rowB","$colA","children","eq","$colB","parseInt","text","append","$itemA","$itemB","valA","valB","length","html","isNumeric","toString","localeCompare","ascending","iconName","each","this","remove","prepend","reverse","i","initialize","object","attr","questionIds","slice","post","location","reload"],"mappings":";;;;;;AAsBAA,oCAAO,CAAC,WAAW,SAASC,OAEpBC,WAAa,CACbC,eAAgB,YASXC,WAAWC,KAAMC,UAAWC,SACjCN,EAAEO,KAAK,CACHC,KAAM,OACNC,IAAK,aACLL,KAAMA,KACNM,QAASL,UACTM,MAAOL,mBAWNM,0BAA0BR,KAAMS,OAAQR,UAAWC,SACxDH,WAAW,QACG,iCACJF,WAAWC,sBACPW,QACXR,UAAWC,kBAUTQ,mBAAmBV,KAAMS,OAAQR,UAAWC,SACjDH,WAAW,QACG,yBACJF,WAAWC,6BACFE,KAAKW,kBACVF,QACXR,UAAWC,kBASTU,YAAYC,OAAQC,UAAWd,MACpCa,OAAOb,KAAK,UAAU,GACtBa,OAAOb,KAAK,SAAS,OACjBe,WAAaF,OAAOG,OACxBD,WAAWE,IAAI,QAAS,QACxBH,UAAUd,KAAMa,OAAOK,OAAO,YACG,IAAzBL,OAAOb,KAAK,SACZY,YAAYC,OAAQC,UAAWd,OAE/Be,WAAWE,IAAI,QAAS,SACxBJ,OAAOb,KAAK,SAAS,GACrBa,OAAOb,KAAK,UAAU,OAE3B,WACCe,WAAWE,IAAI,QAAS,mBAQvBE,qBAAqBN,QAC1BD,YAAYC,OAAQH,mBAAoB,CAACC,WAAYE,OAAOb,KAAK,0BAO5DoB,4BAA4BP,QACjCD,YAAYC,OAAQL,0BAA2B,eAQ1Ca,iBAAiBC,MAAOC,QAC7B3B,EAAE4B,UAAUC,GAAG,QAASH,OAAO,SAASI,WAChCb,OAASjB,EAAE8B,MAAMC,SAEA,IADFd,OAAOb,KAAK,UAK/BuB,OAAOV,QAHHA,OAAOb,KAAK,SAAS,eAmBxB4B,UAAUC,eACTC,OAASD,QAAQE,QACjBC,OAASH,QAAQI,SAASA,aAC5BC,MAAQF,OAAOG,KAAK,YAAYC,UAAUC,MAAK,SAASC,KAAMC,YACxDC,MAAQ5C,EAAE0C,MAAMG,SAAS,MAAMC,GAAG,GAClCC,MAAQ/C,EAAE2C,MAAME,SAAS,MAAMC,GAAG,UACjCE,SAASJ,MAAMK,QAAUD,SAASD,MAAME,WAEnDb,OAAOc,OAAOZ,OACdA,MAAQF,OAAOG,KAAK,YAAYC,UAAUC,MAAK,SAASC,KAAMC,YACpDC,MAAQ5C,EAAE0C,MAAMG,SAAS,MAAMC,GAAGZ,QAClCa,MAAQ/C,EAAE2C,MAAME,SAAS,MAAMC,GAAGZ,QAClCiB,OAASP,MAAML,KAAK,0BACpBa,OAASL,MAAMR,KAAK,8BACtBc,KAMAC,YAJAD,KADkB,IAAlBF,OAAOI,OACAX,MAAMY,OAEkB,IAAxBL,OAAO7B,MAAMiC,OAAeJ,OAAOK,OAASL,OAAO7B,MAI1DgC,KADkB,IAAlBF,OAAOG,OACAR,MAAMS,OAEkB,IAAxBJ,OAAO9B,MAAMiC,OAAeH,OAAOI,OAASJ,OAAO9B,MAE1DtB,EAAEyD,UAAUJ,OAASrD,EAAEyD,UAAUH,MAC1BD,KAAOC,KAEPD,KAAKK,WAAWC,cAAcL,eAGvCM,UAAoC,SAAvBxB,OAAOhC,KAAK,OAC/BgC,OAAOhC,KAAK,MAAOwD,UAAY,QAAU,cACnCC,SAAYD,UAAY,cAAgB,gBAC9C5D,EAAE8D,KAAK1B,OAAOG,KAAK,sBAAsB,WACrCvC,EAAE+D,MAAMxB,KAAK,OAAOyB,YAExB/B,QAAQgC,QAAQ,gBAAkBJ,SAAW,UACxCD,YACDtB,MAAQA,MAAM4B,WAElB9B,OAAOc,OAAOZ,WACV6B,EAAI,EACR/B,OAAOG,KAAK,YAAYuB,MAAK,WACzB9D,EAAE+D,MAAMxB,KAAK,kBAAkBiB,KAAKW,GACpCA,aAgDD,CACHC,WAAY,SAASlE,gBACjBD,WAAWC,eAAiBA,eAC5BuB,iBAAiB,iCAAkCF,sBACnDE,iBAAiB,yCAA0CD,6BA5B/DxB,EAAE,kDAAkD8D,MAAK,SAAS3B,MAAOkC,QACrErE,EAAEqE,QAAQC,KAAK,YAAa,MAjBhCtE,EAAE4B,UAAUC,GAAG,QAAS,qBAAqB,WACzCG,UAAUhC,EAAE+D,UAEhB/D,EAAE,iCAAiC8D,MAAK,WACpC9B,UAAUhC,EAAE+D,OACZ/B,UAAUhC,EAAE+D,UAEhB/D,EAAE,kCAAkC8D,MAAK,WACrC9B,UAAUhC,EAAE+D,UAiBhB/D,EAAE,mCAAmC6B,GAAG,SAAS,eACzC0C,YAAc,GAClBvE,EAAE,sDAAsD8D,MAAK,WACzDS,aAAevE,EAAE+D,MAAMO,KAAK,QAAQE,MAAM,GAAK,OAEnDxE,EAAEyE,KAAK,aAAc,QACP,kBACJxE,WAAWC,6BACFqE,cAChB,WACCG,SAASC"}
\ No newline at end of file
diff --git a/amd/src/edit_questions.js b/amd/src/edit_questions.js
index d8971ca..a18bdd9 100755
--- a/amd/src/edit_questions.js
+++ b/amd/src/edit_questions.js
@@ -143,30 +143,40 @@ define(['jquery'], function($) {
* @param {Object} $header The header column for which to sort the table by.
*/
function sortTable($header) {
- var column = $header.index();
- var $table = $header.parent().parent();
- var $rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {
- var $colA = $(rowA).children('td').eq(0);
- var $colB = $(rowB).children('td').eq(0);
+ const column = $header.index();
+ const $table = $header.parent().parent();
+ let $rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {
+ const $colA = $(rowA).children('td').eq(0);
+ const $colB = $(rowB).children('td').eq(0);
return parseInt($colA.text()) - parseInt($colB.text());
});
$table.append($rows);
$rows = $table.find('tr:gt(0)').toArray().sort(function(rowA, rowB) {
- var $colA = $(rowA).children('td').eq(column);
- var $colB = $(rowB).children('td').eq(column);
- var $itemA = $colA.find('.capquiz-sortable-item');
- var $itemB = $colB.find('.capquiz-sortable-item');
- var valA = ($itemA.length === 0 ? $colA.html() : ($itemA.val().length === 0 ? $itemA.html() : $itemA.val()));
- var valB = ($itemB.length === 0 ? $colB.html() : ($itemB.val().length === 0 ? $itemB.html() : $itemB.val()));
+ const $colA = $(rowA).children('td').eq(column);
+ const $colB = $(rowB).children('td').eq(column);
+ const $itemA = $colA.find('.capquiz-sortable-item');
+ const $itemB = $colB.find('.capquiz-sortable-item');
+ let valA;
+ if ($itemA.length === 0) {
+ valA = $colA.html();
+ } else{
+ valA = $itemA.val().length === 0 ? $itemA.html() : $itemA.val();
+ }
+ let valB;
+ if ($itemB.length === 0) {
+ valB = $colB.html();
+ } else{
+ valB = $itemB.val().length === 0 ? $itemB.html() : $itemB.val();
+ }
if ($.isNumeric(valA) && $.isNumeric(valB)) {
return valA - valB;
} else {
return valA.toString().localeCompare(valB);
}
});
- var ascending = ($table.data('asc') === 'true');
+ const ascending = ($table.data('asc') === 'true');
$table.data('asc', ascending ? 'false' : 'true');
- var iconName = (ascending ? 'fa-arrow-up' : 'fa-arrow-down');
+ const iconName = (ascending ? 'fa-arrow-up' : 'fa-arrow-down');
$.each($table.find('.capquiz-sortable'), function() {
$(this).find('.fa').remove();
});
@@ -175,7 +185,7 @@ define(['jquery'], function($) {
$rows = $rows.reverse();
}
$table.append($rows);
- var i = 1;
+ let i = 1;
$table.find('tr:gt(0)').each(function() {
$(this).find('td:first-child').html(i);
i++;
diff --git a/backup/moodle2/backup_capquiz_activity_task.class.php b/backup/moodle2/backup_capquiz_activity_task.class.php
index 2d1bbba..7319225 100644
--- a/backup/moodle2/backup_capquiz_activity_task.class.php
+++ b/backup/moodle2/backup_capquiz_activity_task.class.php
@@ -53,10 +53,11 @@ protected function define_my_steps() {
/**
* Code the transformations to perform in the activity in order to get transportable (encoded) links.
+ *
* @param string $content
* @return string of content with the URLs encoded
*/
- public static function encode_content_links($content) {
+ public static function encode_content_links($content): string {
global $CFG;
$base = preg_quote($CFG->wwwroot, '/');
// Link to the list of CAPQuizzes.
diff --git a/backup/moodle2/backup_capquiz_stepslib.php b/backup/moodle2/backup_capquiz_stepslib.php
index d45cf1f..0bd25a8 100644
--- a/backup/moodle2/backup_capquiz_stepslib.php
+++ b/backup/moodle2/backup_capquiz_stepslib.php
@@ -35,43 +35,83 @@ class backup_capquiz_activity_structure_step extends backup_questions_activity_s
*/
protected function define_structure() {
$capquiz = new backup_nested_element('capquiz', ['id'], [
- 'name', 'intro', 'introformat', 'timecreated', 'timemodified', 'published', 'default_user_rating'
+ 'name',
+ 'intro',
+ 'introformat',
+ 'timecreated',
+ 'timemodified',
+ 'published',
+ 'default_user_rating',
]);
$questionlist = new backup_nested_element('questionlist', null, [
- 'id', 'capquiz_id', 'title', 'author', 'description',
- 'star_ratings', 'is_template', 'time_created', 'time_modified', 'default_question_rating'
+ 'id',
+ 'capquiz_id',
+ 'title',
+ 'author',
+ 'description',
+ 'star_ratings',
+ 'is_template',
+ 'time_created',
+ 'time_modified',
+ 'default_question_rating',
]);
$questions = new backup_nested_element('questions');
$question = new backup_nested_element('question', ['id'], [
- 'question_id', 'question_list_id', 'rating'
+ 'question_id',
+ 'question_list_id',
+ 'rating',
]);
$questionratings = new backup_nested_element('questionratings');
$questionrating = new backup_nested_element('question_rating', ['id'], [
- 'capquiz_question_id', 'rating', 'manual', 'timecreated'
+ 'capquiz_question_id',
+ 'rating',
+ 'manual',
+ 'timecreated',
]);
$questionselections = new backup_nested_element('questionselections');
$questionselection = new backup_nested_element('questionselection', ['id'], [
- 'capquiz_id', 'strategy', 'configuration'
+ 'capquiz_id',
+ 'strategy',
+ 'configuration',
]);
$ratingsystems = new backup_nested_element('ratingsystems');
$ratingsystem = new backup_nested_element('ratingsystem', ['id'], [
- 'capquiz_id', 'rating_system', 'configuration'
+ 'capquiz_id',
+ 'rating_system',
+ 'configuration',
]);
$users = new backup_nested_element('users');
$user = new backup_nested_element('user', ['id'], [
- 'user_id', 'capquiz_id', 'question_usage_id', 'rating', 'highest_level'
+ 'user_id',
+ 'capquiz_id',
+ 'question_usage_id',
+ 'rating',
+ 'highest_level',
]);
$this->add_question_usages($user, 'question_usage_id');
$userratings = new backup_nested_element('userratings');
$userrating = new backup_nested_element('user_rating', ['id'], [
- 'capquiz_user_id', 'rating', 'manual', 'timecreated'
+ 'capquiz_user_id',
+ 'rating',
+ 'manual',
+ 'timecreated',
]);
$attempts = new backup_nested_element('attempts');
$attempt = new backup_nested_element('attempt', ['id'], [
- 'slot', 'user_id', 'question_id', 'reviewed', 'answered', 'time_answered', 'time_reviewed',
- 'question_rating_id', 'question_prev_rating_id', 'prev_question_rating_id',
- 'prev_question_prev_rating_id', 'user_rating_id', 'user_prev_rating_id'
+ 'slot',
+ 'user_id',
+ 'question_id',
+ 'reviewed',
+ 'answered',
+ 'time_answered',
+ 'time_reviewed',
+ 'question_rating_id',
+ 'question_prev_rating_id',
+ 'prev_question_rating_id',
+ 'prev_question_prev_rating_id',
+ 'user_rating_id',
+ 'user_prev_rating_id',
]);
// Build the tree.
diff --git a/backup/moodle2/restore_capquiz_activity_task.class.php b/backup/moodle2/restore_capquiz_activity_task.class.php
index 6b65421..686145b 100644
--- a/backup/moodle2/restore_capquiz_activity_task.class.php
+++ b/backup/moodle2/restore_capquiz_activity_task.class.php
@@ -49,35 +49,31 @@ protected function define_my_steps() {
}
/**
- * Define the contents in the activity that must be
- * processed by the link decoder
+ * Define the contents in the activity that must be processed by the link decoder
*
* @return restore_decode_content[]
*/
public static function define_decode_contents() {
return [
- new restore_decode_content('capquiz', ['intro'])
+ new restore_decode_content('capquiz', ['intro']),
];
}
/**
- * Define the decoding rules for links belonging
- * to the activity to be executed by the link decoder
+ * Define the decoding rules for links belonging to the activity to be executed by the link decoder
*
* @return restore_decode_rule[]
*/
public static function define_decode_rules() {
return [
new restore_decode_rule('CAPQUIZVIEWBYID', '/mod/capquiz/view.php?id=$1', 'course_module'),
- new restore_decode_rule('CAPQUIZINDEX', '/mod/capquiz/index.php?id=$1', 'course')
+ new restore_decode_rule('CAPQUIZINDEX', '/mod/capquiz/index.php?id=$1', 'course'),
];
}
/**
- * Define the restore log rules that will be applied
- * by the {@see restore_logs_processor} when restoring
- * course logs. It must return one array
- * of {@see restore_log_rule} objects
+ * Define the restore log rules that will be applied by the {@see restore_logs_processor} when restoring
+ * course logs. It must return one array of {@see restore_log_rule} objects
*
* @return restore_log_rule[]
*/
@@ -86,14 +82,10 @@ public static function define_restore_log_rules() {
}
/**
- * Define the restore log rules that will be applied
- * by the {@see restore_logs_processor}
- * when restoring
- * course logs. It must return one array
- * of {@see restore_log_rule} objects
+ * Define the restore log rules that will be applied by the {@see restore_logs_processor} when restoring
+ * course logs. It must return one array of {@see restore_log_rule} objects
*
- * Note this rules are applied when restoring course logs
- * by the restore final task, but are defined here at
+ * Note this rules are applied when restoring course logs by the restore final task, but are defined here at
* activity level. All them are rules not linked to any module instance (cmid = 0)
*
* @return restore_log_rule[]
diff --git a/backup/moodle2/restore_capquiz_stepslib.php b/backup/moodle2/restore_capquiz_stepslib.php
index 2ba2d4a..ff80470 100644
--- a/backup/moodle2/restore_capquiz_stepslib.php
+++ b/backup/moodle2/restore_capquiz_stepslib.php
@@ -62,9 +62,6 @@ protected function define_structure() {
* Processes and backs up capquiz
*
* @param object $data
- * @throws base_step_exception
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz($data) {
global $DB;
@@ -82,9 +79,6 @@ protected function process_capquiz($data) {
* Processes and backs up capquiz question list
*
* @param object $data
- * @throws base_step_exception
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_question_list($data) {
global $DB;
@@ -102,8 +96,6 @@ protected function process_capquiz_question_list($data) {
* Processes and backs up capquiz questions
*
* @param object $data
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_question($data) {
global $DB;
@@ -122,8 +114,6 @@ protected function process_capquiz_question($data) {
* Processes and backs up capquiz question rating
*
* @param object $data
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_question_rating($data) {
global $DB;
@@ -138,8 +128,6 @@ protected function process_capquiz_question_rating($data) {
* Processes and backs up capquiz question selection
*
* @param object $data
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_question_selection($data) {
global $DB;
@@ -154,8 +142,6 @@ protected function process_capquiz_question_selection($data) {
* Processes and backs up capquiz question rating system
*
* @param object $data
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_rating_system($data) {
global $DB;
@@ -182,8 +168,6 @@ protected function process_capquiz_user($data) {
* Processes and backs up capquiz user rating
*
* @param object $data
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_user_rating($data) {
global $DB;
@@ -198,8 +182,6 @@ protected function process_capquiz_user_rating($data) {
* Processes and backs up capquiz question attempt
*
* @param object $data
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function process_capquiz_attempt($data) {
global $DB;
@@ -221,8 +203,6 @@ protected function process_capquiz_attempt($data) {
* Updates a users usageid and maps the users old and new ids
*
* @param int $newusageid
- * @throws dml_exception
- * @throws restore_step_exception
*/
protected function inform_new_usage_id($newusageid) {
global $DB;
diff --git a/classes/bank/add_action_column.php b/classes/bank/add_action_column.php
deleted file mode 100644
index 028bfe5..0000000
--- a/classes/bank/add_action_column.php
+++ /dev/null
@@ -1,66 +0,0 @@
-.
-
-namespace mod_capquiz\bank;
-
-/**
- * A column type for the add this question to the quiz action.
- *
- * This class is copied to CAPQuiz from the Core Quiz, without
- * modification (as of Fri 9 Sep 08:29:48 UTC 2022 ).
- *
- * @package mod_capquiz
- * @category question
- * @copyright 2009 Tim Hunt
- * @author 2021 Safat Shahin
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class add_action_column extends \core_question\local\bank\action_column_base {
-
- /** @var string caches a lang string used repeatedly. */
- protected $stradd;
-
- /**
- * A chance for subclasses to initialise themselves, for example to load lang strings,
- * without having to override the constructor.
- */
- public function init(): void {
- parent::init();
- $this->stradd = get_string('addtoquiz', 'quiz');
- }
-
- /**
- * Get the internal name for this column. Used as a CSS class name,
- * and to store information about the current sort. Must match PARAM_ALPHA.
- *
- * @return string column name.
- */
- public function get_name() {
- return 'addtoquizaction';
- }
-
- /**
- * Output the contents of this column.
- * @param object $question the row from the $question table, augmented with extra information.
- * @param string $rowclasses CSS class names that should be applied to this row of output.
- */
- protected function display_content($question, $rowclasses) {
- if (!question_has_capability_on($question, 'use')) {
- return;
- }
- $this->print_icon('t/add', $this->stradd, $this->qbank->add_to_quiz_url($question->id));
- }
-}
diff --git a/classes/bank/add_to_quiz_action.php b/classes/bank/add_to_quiz_action.php
new file mode 100644
index 0000000..6cb9496
--- /dev/null
+++ b/classes/bank/add_to_quiz_action.php
@@ -0,0 +1,46 @@
+.
+
+namespace mod_capquiz\bank;
+
+use mod_capquiz\capquiz_urls;
+use stdClass;
+
+/**
+ * Question bank action to add question to quiz.
+ *
+ * @package mod_capquiz
+ * @copyright 2024 NTNU
+ * @author 2024 Sebastian Gundersen
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class add_to_quiz_action extends \core_question\local\bank\question_action_base {
+ /**
+ * Get the information required to display this action either as a menu item or a separate action column.
+ *
+ * @param stdClass $question the row from the $question table, augmented with extra information.
+ * @return array with three elements.
+ * $url - the URL to perform the action.
+ * $icon - the icon for this action. E.g. 't/delete'.
+ * $label - text label to display in the UI (either in the menu, or as a tool-tip on the icon)
+ */
+ protected function get_url_icon_and_label(stdClass $question): array {
+ if (!question_has_capability_on($question, 'use')) {
+ return [null, null, null];
+ }
+ return [capquiz_urls::add_question_to_list_url($question->id), 't/add', get_string('addtoquiz', 'quiz')];
+ }
+}
diff --git a/classes/bank/question_bank_view.php b/classes/bank/question_bank_view.php
index 19a4601..8fc8212 100755
--- a/classes/bank/question_bank_view.php
+++ b/classes/bank/question_bank_view.php
@@ -30,18 +30,7 @@
namespace mod_capquiz\bank;
-use \core_question\local\bank\checkbox_column;
-use \qbank_viewcreator\creator_name_column;
-use \qbank_editquestion\edit_action_column;
-use \qbank_deletequestion\delete_action_column;
-use \qbank_previewquestion\preview_action_column;
-use \qbank_viewquestionname\viewquestionname_column_helper;
-use \qbank_viewquestiontype\question_type_column;
-use \core_question\bank\search\tag_condition as tag_condition;
-use \core_question\bank\search\hidden_condition as hidden_condition;
-use \core_question\bank\search\category_condition;
-use mod_capquiz\local\capquiz_urls;
-use mod_capquiz\bank\add_action_column;
+use context;
/**
* Class question_bank_view
@@ -53,122 +42,26 @@
*/
class question_bank_view extends \core_question\local\bank\view {
- /**
- * URL of add to quiz.
- *
- * @param int $questionid the question id
- * @return \moodle_url
- * @throws \coding_exception
- * @throws \moodle_exception
- */
- public function add_to_quiz_url($questionid) {
- return \mod_capquiz\capquiz_urls::add_question_to_list_url($questionid);
- }
-
- /**
- * Get the list of qbank plugins with available objects for features.
- *
- * @return array
- */
- protected function get_question_bank_plugins(): array {
- $questionbankclasscolumns = [];
- $corequestionbankcolumns = [
- 'add_action_column',
- 'checkbox_column',
- 'question_type_column',
- 'question_name_text_column',
- 'preview_action_column',
- 'edit_action_column'
- ];
-
- if (question_get_display_preference('qbshowtext', 0, PARAM_BOOL, new \moodle_url(''))) {
- $corequestionbankcolumns[] = 'question_text_row';
- }
-
- foreach ($corequestionbankcolumns as $fullname) {
- $shortname = $fullname;
- if (class_exists('mod_capquiz\\bank\\' . $fullname)) {
- $fullname = 'mod_capquiz\\bank\\' . $fullname;
- $questionbankclasscolumns[$shortname] = new $fullname($this);
- } else if (class_exists('core_question\\local\\bank\\' . $fullname)) {
- $fullname = 'core_question\\local\\bank\\' . $fullname;
- $questionbankclasscolumns[$shortname] = new $fullname($this);
- } else {
- $questionbankclasscolumns[$shortname] = '';
- }
- }
- $plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
- foreach ($plugins as $componentname => $plugin) {
- $pluginentrypointobject = new $plugin();
- $plugincolumnobjects = $pluginentrypointobject->get_question_columns($this);
- // Don't need the plugins without column objects.
- if (empty($plugincolumnobjects)) {
- unset($plugins[$componentname]);
- continue;
- }
- foreach ($plugincolumnobjects as $columnobject) {
- $columnname = $columnobject->get_column_name();
- foreach ($corequestionbankcolumns as $key => $corequestionbankcolumn) {
- if (!\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
- unset($questionbankclasscolumns[$columnname]);
- continue;
- }
- // Check if it has custom preference selector to view/hide.
- if ($columnobject->has_preference() && !$columnobject->get_preference()) {
- continue;
- }
- if ($corequestionbankcolumn === $columnname) {
- $questionbankclasscolumns[$columnname] = $columnobject;
- }
- }
- }
- }
-
- // Mitigate the error in case of any regression.
- foreach ($questionbankclasscolumns as $shortname => $questionbankclasscolumn) {
- if (empty($questionbankclasscolumn)) {
- unset($questionbankclasscolumns[$shortname]);
- }
- }
-
- return $questionbankclasscolumns;
- }
-
/**
* Specify the column heading
- *
- * @return string Column name for the heading
*/
protected function heading_column(): string {
- return 'mod_capquiz\\bank\\question_name_text_column';
+ return question_name_text_column::class;
}
/**
- * Renders the html question bank (same as display, but returns the result).
- *
- * Note that you can only output this rendered result once per page, as
- * it contains IDs which must be unique.
+ * Display button to add selected questions to the quiz.
*
- * @param array $pagevars
- * @param string $tabname
- * @return string HTML code for the form
+ * @param context $catcontext
*/
- public function render($pagevars, $tabname): string {
- ob_start();
- $this->display($pagevars, $tabname);
- $out = ob_get_contents();
- ob_end_clean();
- return $out;
- }
-
- /**
- * Displays the add selected questions button
- *
- * @throws \coding_exception
- */
- private function display_add_selected_questions_button() {
- $straddtoquiz = get_string('add_to_quiz', 'capquiz');
- echo '';
+ protected function display_bottom_controls(context $catcontext): void {
+ echo '
The question ratings can be edited below. Changes are saved automatically.
';
-$string['question_list_no_questions'] = 'This capquiz has no questions. Add some questions from the list to the right';
+$string['question_list_no_questions'] = 'This capquiz has no questions. Add some questions from the question bank below.';
$string['n_closest'] = 'N-closest';
$string['chronological'] = 'Chronological';
$string['no_strategy_specified'] = 'No strategy specified';
@@ -178,7 +177,6 @@
$string['publish_no_questions_in_list'] = '
There doesn\'t seem to be any questions in the question list for this CAPQuiz instance. You must have at least one question before you can publish
';
$string['publish_already_published'] = '
This CAPQuiz is already published
';
-$string['no_question_list_assigned'] = 'No question list has been assigned';
$string['published'] = 'Published';
$string['not_published'] = 'Not published';
$string['question_list_not_published'] = 'The question list is not yet published';
diff --git a/lib.php b/lib.php
index 02271ec..a89e2f6 100755
--- a/lib.php
+++ b/lib.php
@@ -25,6 +25,7 @@
*/
use mod_capquiz\capquiz;
+use mod_capquiz\output\question_attempt_renderer;
defined('MOODLE_INTERNAL') || die();
@@ -34,9 +35,8 @@
* Add this capquiz instance to the database
*
* @param stdClass $modformdata The data submitted from the form
- * @return bool|int
*/
-function capquiz_add_instance(stdClass $modformdata) {
+function capquiz_add_instance(stdClass $modformdata): bool|int {
global $DB;
$modformdata->time_modified = time();
$modformdata->time_created = time();
@@ -48,9 +48,8 @@ function capquiz_add_instance(stdClass $modformdata) {
* Update this instance in the database
*
* @param stdClass $capquiz database record
- * @return bool
*/
-function capquiz_update_instance(stdClass $capquiz) {
+function capquiz_update_instance(stdClass $capquiz): bool {
global $DB;
$capquiz->id = $capquiz->instance;
$DB->update_record('capquiz', $capquiz);
@@ -63,26 +62,22 @@ function capquiz_update_instance(stdClass $capquiz) {
*
* @param int $cmid Course module id for the instance to be deleted
*/
-function capquiz_delete_instance(int $cmid) {
+function capquiz_delete_instance(int $cmid): void {
$capquiz = new capquiz($cmid);
- if ($capquiz) {
- $user = $capquiz->user();
- if ($user) {
- $quba = $user->question_usage();
- \question_engine::delete_questions_usage_by_activity($quba->get_id());
- }
+ $user = $capquiz->user();
+ if ($user !== null) {
+ $quba = $user->question_usage();
+ question_engine::delete_questions_usage_by_activity($quba->get_id());
}
}
/**
* Implementation of the reset course functionality, delete all the assignment submissions for course $data->courseid.
*
- * @param object $data
+ * @param stdClass $data
* @return array containing the statusreport from execution
- * @throws coding_exception
- * @throws dml_exception
*/
-function capquiz_reset_userdata($data) {
+function capquiz_reset_userdata(stdClass $data): array {
global $DB;
$status = [];
$strmodname = get_string('modulenameplural', 'capquiz');
@@ -99,7 +94,7 @@ function capquiz_reset_userdata($data) {
continue;
}
foreach ($users as $user) {
- \question_engine::delete_questions_usage_by_activity($user->question_usage_id);
+ question_engine::delete_questions_usage_by_activity($user->question_usage_id);
$DB->delete_records('capquiz_attempt', ['user_id' => $user->id]);
}
$DB->delete_records('capquiz_user', ['capquiz_id' => $instance->id]);
@@ -112,9 +107,8 @@ function capquiz_reset_userdata($data) {
* Finds all assignment notifications that have yet to be mailed out, and mails them.
*
* Cron function to be run periodically according to the moodle cron.
- * @return bool
*/
-function capquiz_cron() {
+function capquiz_cron(): bool {
return true;
}
@@ -123,9 +117,8 @@ function capquiz_cron() {
*
* @param settings_navigation $settings
* @param navigation_node $capquiznode
- * @return void
*/
-function capquiz_extend_settings_navigation($settings, $capquiznode) {
+function capquiz_extend_settings_navigation(settings_navigation $settings, navigation_node $capquiznode): void {
global $PAGE, $CFG;
// Require {@link https://github.com/moodle/moodle/blob/master/lib/questionlib.php}
@@ -140,9 +133,8 @@ function capquiz_extend_settings_navigation($settings, $capquiznode) {
*
* @param stdClass $capquiz database record
* @param int $userid int or 0 for all users
- * @return array
*/
-function capquiz_get_user_grades(stdClass $capquiz, int $userid = 0) {
+function capquiz_get_user_grades(stdClass $capquiz, int $userid = 0): array {
global $DB;
$params = ['capquiz_id' => $capquiz->id];
if ($userid > 0) {
@@ -168,15 +160,15 @@ function capquiz_get_user_grades(stdClass $capquiz, int $userid = 0) {
* Create grade item for given assignment.
*
* @param stdClass $capquiz record with extra cmidnumber
- * @param array $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
+ * @param array|string|null $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
* @return int 0 if ok, error code otherwise
*/
-function capquiz_grade_item_update(stdClass $capquiz, $grades = null) {
+function capquiz_grade_item_update(stdClass $capquiz, $grades = null): int {
global $DB;
$capquiz->cmidnumber = get_coursemodule_from_instance('capquiz', $capquiz->id)->id;
$params = [
'itemname' => $capquiz->name,
- 'idnumber' => $capquiz->cmidnumber
+ 'idnumber' => $capquiz->cmidnumber,
];
$params['gradetype'] = GRADE_TYPE_VALUE;
$params['grademax'] = 5;
@@ -191,7 +183,7 @@ function capquiz_grade_item_update(stdClass $capquiz, $grades = null) {
'itemtype' => 'mod',
'itemmodule' => 'capquiz',
'iteminstance' => $capquiz->id,
- 'outcomeid' => null
+ 'outcomeid' => null,
]);
$item->gradepass = $capquiz->stars_to_pass;
$item->update();
@@ -210,7 +202,7 @@ function capquiz_grade_item_update(stdClass $capquiz, $grades = null) {
* @param int $userid specific user only, 0 means all
* @param bool $nullifnone
*/
-function capquiz_update_grades(stdClass $capquiz, int $userid = 0, $nullifnone = true) {
+function capquiz_update_grades(stdClass $capquiz, int $userid = 0, $nullifnone = true): void {
$grades = capquiz_get_user_grades($capquiz, $userid);
if ($grades) {
capquiz_grade_item_update($capquiz, $grades);
@@ -230,7 +222,7 @@ function capquiz_update_grades(stdClass $capquiz, int $userid = 0, $nullifnone =
* @param int $courseid id of the course to be reset
* @param string $type Optional type of assignment to limit the reset to a particular assignment type
*/
-function capquiz_reset_gradebook($courseid, $type = '') {
+function capquiz_reset_gradebook($courseid, $type = ''): void {
global $DB;
$instances = $DB->get_records('capquiz', ['course' => $courseid]);
foreach ($instances as $instance) {
@@ -238,6 +230,40 @@ function capquiz_reset_gradebook($courseid, $type = '') {
}
}
+/**
+ * Serve question files.
+ *
+ * @param stdClass $course
+ * @param stdClass $context
+ * @param string $component
+ * @param string $filearea
+ * @param int $qubaid
+ * @param int $slot
+ * @param array $args
+ * @param bool $forcedownload
+ * @param array $options
+ * @see quiz_question_pluginfile
+ */
+function capquiz_question_pluginfile(stdClass $course, stdClass $context, string $component, string $filearea,
+ int $qubaid, int $slot, array $args, bool $forcedownload, array $options = []): void {
+ global $DB;
+ $user = $DB->get_record('capquiz_user', ['question_usage_id' => $qubaid]);
+ $cm = get_coursemodule_from_instance('capquiz', $user->capquiz_id, $course->id, false, MUST_EXIST);
+ require_login($course, false, $cm);
+ $quba = question_engine::load_questions_usage_by_activity($qubaid);
+ $displayoptions = question_attempt_renderer::attempt_display_options(context_module::instance($cm->id));
+ if (!$quba->check_file_access($slot, $displayoptions, $component, $filearea, $args, $forcedownload)) {
+ send_file_not_found();
+ }
+ $fs = get_file_storage();
+ $relativepath = implode('/', $args);
+ $fullpath = "/$context->id/$component/$filearea/$relativepath";
+ $file = $fs->get_file_by_hash(sha1($fullpath));
+ if (!$file || $file->is_directory()) {
+ send_file_not_found();
+ }
+ send_stored_file($file, 0, 0, $forcedownload, $options);
+}
// Ugly hack to make 3.11 and 4.0 work seamlessly.
if (!defined('FEATURE_MOD_PURPOSE')) {
@@ -251,9 +277,8 @@ function capquiz_reset_gradebook($courseid, $type = '') {
* Checks if $feature is supported
*
* @param string $feature
- * @return mixed
*/
-function capquiz_supports($feature) {
+function capquiz_supports(string $feature) {
switch ($feature) {
case FEATURE_MOD_INTRO:
case FEATURE_BACKUP_MOODLE2:
diff --git a/mod_form.php b/mod_form.php
index 31e0f80..bbf9629 100755
--- a/mod_form.php
+++ b/mod_form.php
@@ -38,10 +38,8 @@ class mod_capquiz_mod_form extends moodleform_mod {
/**
* Defines the corm
- *
- * @throws coding_exception
*/
- public function definition() {
+ public function definition(): void {
$form = $this->_form;
$form->addElement('text', 'name', get_string('name'), ['size' => '64']);
$form->setType('name', PARAM_TEXT);
diff --git a/report/attempts/attempts_form.php b/report/attempts/attempts_form.php
index 4f65453..6b86efc 100644
--- a/report/attempts/attempts_form.php
+++ b/report/attempts/attempts_form.php
@@ -49,9 +49,8 @@ class capquizreport_attempts_settings_form extends capquiz_attempts_report_form
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
- public function validation($data, $files) {
+ public function validation($data, $files): array {
$errors = parent::validation($data, $files);
-
if (!($data['urating']
|| $data['uprevrating']
|| $data['qprevrating']
@@ -61,7 +60,6 @@ public function validation($data, $files) {
|| $data['right'])) {
$errors['coloptions'] = get_string('reportmustselectstate', 'quiz');
}
-
return $errors;
}
@@ -70,22 +68,15 @@ public function validation($data, $files) {
*
* @param MoodleQuickForm $mform the form to add preference fields to
*/
- protected function other_preference_fields(MoodleQuickForm $mform) {
- $mform->addGroup(array(
- $mform->createElement('advcheckbox', 'ansstate', '',
- get_string('ansstate', 'capquizreport_attempts')),
- $mform->createElement('advcheckbox', 'urating', '',
- get_string('urating', 'capquizreport_attempts')),
- $mform->createElement('advcheckbox', 'uprevrating', '',
- get_string('uprevrating', 'capquizreport_attempts')),
- $mform->createElement('advcheckbox', 'qprevrating', '',
- get_string('qprevrating', 'capquizreport_attempts')),
- $mform->createElement('advcheckbox', 'qtext', '',
- get_string('questiontext', 'quiz_responses')),
- $mform->createElement('advcheckbox', 'resp', '',
- get_string('response', 'quiz_responses')),
- $mform->createElement('advcheckbox', 'right', '',
- get_string('rightanswer', 'quiz_responses')),
- ), 'coloptions', get_string('showthe', 'quiz_responses'), array(' '), false);
+ protected function other_preference_fields(MoodleQuickForm $mform): void {
+ $mform->addGroup([
+ $mform->createElement('advcheckbox', 'ansstate', '', get_string('ansstate', 'capquizreport_attempts')),
+ $mform->createElement('advcheckbox', 'urating', '', get_string('urating', 'capquizreport_attempts')),
+ $mform->createElement('advcheckbox', 'uprevrating', '', get_string('uprevrating', 'capquizreport_attempts')),
+ $mform->createElement('advcheckbox', 'qprevrating', '', get_string('qprevrating', 'capquizreport_attempts')),
+ $mform->createElement('advcheckbox', 'qtext', '', get_string('questiontext', 'quiz_responses')),
+ $mform->createElement('advcheckbox', 'resp', '', get_string('response', 'quiz_responses')),
+ $mform->createElement('advcheckbox', 'right', '', get_string('rightanswer', 'quiz_responses')),
+ ], 'coloptions', get_string('showthe', 'quiz_responses'), [' '], false);
}
}
diff --git a/report/attempts/attempts_options.php b/report/attempts/attempts_options.php
index 9bd18c8..175f967 100644
--- a/report/attempts/attempts_options.php
+++ b/report/attempts/attempts_options.php
@@ -28,12 +28,12 @@
use context_module;
use mod_capquiz\report\capquiz_attempts_report;
use mod_capquiz\report\capquiz_attempts_report_options;
+use stdClass;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/capquiz/report/attemptsreport_options.php');
-
/**
* Class to store the options for a {@see capquiz_attempts_report}.
*
@@ -67,7 +67,7 @@ class capquizreport_attempts_options extends capquiz_attempts_report_options {
/**
* Get the current value of the settings to pass to the settings form.
*/
- public function get_initial_form_data() {
+ public function get_initial_form_data(): stdClass {
$toform = parent::get_initial_form_data();
$toform->ansstate = $this->showansstate;
$toform->urating = $this->showurating;
@@ -84,7 +84,7 @@ public function get_initial_form_data() {
* 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) {
+ public function setup_from_form_data($fromform): void {
parent::setup_from_form_data($fromform);
$this->showansstate = $fromform->ansstate;
@@ -99,7 +99,7 @@ public function setup_from_form_data($fromform) {
/**
* Set the fields of this object from the URL parameters.
*/
- public function setup_from_params() {
+ public function setup_from_params(): void {
parent::setup_from_params();
$this->showansstate = optional_param('ansstate', $this->showansstate, PARAM_BOOL);
@@ -115,7 +115,7 @@ public function setup_from_params() {
* 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() {
+ public function setup_from_user_preferences(): void {
parent::setup_from_user_preferences();
$this->showansstate = get_user_preferences('capquizreport_attempts_ansstate', $this->showansstate);
@@ -131,7 +131,7 @@ public function setup_from_user_preferences() {
* 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() {
+ public function update_user_preferences(): void {
parent::update_user_preferences();
set_user_preference('capquizreport_attempts_ansstate', $this->showansstate);
@@ -146,7 +146,7 @@ public function update_user_preferences() {
/**
* Check the settings, and remove any 'impossible' combinations.
*/
- public function resolve_dependencies() {
+ public function resolve_dependencies(): void {
parent::resolve_dependencies();
if (!$this->showansstate
@@ -170,9 +170,10 @@ public function resolve_dependencies() {
/**
* Get the URL parameters required to show the report with these options.
+ *
* @return array URL parameter name => value.
*/
- protected function get_url_params() {
+ protected function get_url_params(): array {
$params = parent::get_url_params();
$params['ansstate'] = $this->showansstate;
$params['urating'] = $this->showurating;
diff --git a/report/attempts/attempts_table.php b/report/attempts/attempts_table.php
index ae3f0c7..16e974c 100644
--- a/report/attempts/attempts_table.php
+++ b/report/attempts/attempts_table.php
@@ -25,18 +25,18 @@
namespace capquizreport_attempts;
+use core\context;
use core\dml\sql_join;
use mod_capquiz\report\capquiz_attempts_report_options;
use mod_capquiz\report\capquiz_attempts_report_table;
use moodle_url;
-use quiz_responses_options;
+use stdClass;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/capquiz/report/attemptsreport_table.php');
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
-
/**
* This is a table subclass for displaying the capquiz attempts report.
*
@@ -55,7 +55,7 @@ class capquizreport_attempts_table extends capquiz_attempts_report_table {
* @param array $questions
* @param moodle_url $reporturl
*/
- public function __construct($capquiz, $context, capquiz_attempts_report_options $options,
+ public function __construct($capquiz, context $context, capquiz_attempts_report_options $options,
sql_join $studentsjoins, $questions, $reporturl) {
parent::__construct('mod-capquiz-report-attempts-report', $capquiz, $context,
$options, $studentsjoins, $questions, $reporturl);
@@ -73,7 +73,6 @@ public function build_table() {
if (!$this->rawdata) {
return;
}
-
$this->strtimeformat = str_replace(',', ' ', get_string('strftimedatetimeseconds', 'capquiz'));
parent::build_table();
}
@@ -83,75 +82,58 @@ public function build_table() {
*
* @param string $colname The column name
* @param stdClass $attempt The attempt
- * @return mixed string or NULL
+ * @return ?string
*/
- public function other_cols($colname, $attempt) {
- switch ($colname) {
- case 'question':
- return $this->data_col($attempt->slot, 'questionsummary', $attempt);
- case 'response':
- return $this->data_col($attempt->slot, 'responsesummary', $attempt);
- case 'right':
- return $this->data_col($attempt->slot, 'rightanswer', $attempt);
- default:
- return null;
- }
+ public function other_cols($colname, $attempt): ?string {
+ return match ($colname) {
+ 'question' => $this->data_col($attempt->slot, 'questionsummary', $attempt),
+ 'response' => $this->data_col($attempt->slot, 'responsesummary', $attempt),
+ 'right' => $this->data_col($attempt->slot, 'rightanswer', $attempt),
+ default => null,
+ };
}
/**
* Format a single column, used in other_cols
*
- * @param integer $slot attempts slot
+ * @param int $slot
* @param string $field
- * @param object $attempt
- * @return string
+ * @param stdClass $attempt
*/
- public function data_col($slot, $field, $attempt) {
+ public function data_col(int $slot, string $field, stdClass $attempt): string {
if ($attempt->usageid == 0) {
return '-';
}
$value = $this->field_from_extra_data($attempt, $slot, $field);
-
- if (is_null($value)) {
- $summary = '-';
- } else {
- $summary = trim($value);
- }
-
+ $summary = $value !== null ? trim($value) : '-';
if ($this->is_downloading() && $this->is_downloading() != 'html') {
return $summary;
}
$summary = s($summary);
-
if ($this->is_downloading()) {
return $summary;
}
-
if ($field === 'responsesummary') {
return $this->make_review_link($summary, $attempt, $slot);
-
- } else if ($field === 'questionsummary') {
+ }
+ if ($field === 'questionsummary') {
return $this->make_preview_link($summary, $attempt, $slot);
-
- } else {
- return $summary;
}
+ return $summary;
}
/**
* Column text from the extra data loaded in load_extra_data(), before html formatting etc.
*
- * @param object $attempt
+ * @param stdClass $attempt
* @param int $slot
* @param string $field
- * @return string
*/
- protected function field_from_extra_data($attempt, $slot, $field) {
+ protected function field_from_extra_data(stdClass $attempt, int $slot, string $field): string {
if (!isset($this->lateststeps[$attempt->usageid][$slot])) {
return '-';
}
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
-
if (property_exists($stepdata, $field . 'full')) {
$value = $stepdata->{$field . 'full'};
} else {
@@ -162,14 +144,12 @@ protected function field_from_extra_data($attempt, $slot, $field) {
/**
* Generate the display of the answer state column.
- * @param object $attempt the table row being output.
+ *
+ * @param stdClass $attempt the table row being output.
* @return string HTML content to go inside the td.
*/
- public function col_answerstate($attempt) {
- if (is_null($attempt->attempt)) {
- return '-';
- }
- if ($attempt->usageid == 0) {
+ public function col_answerstate(stdClass $attempt): string {
+ if ($attempt->attempt === null || $attempt->usageid === 0) {
return '-';
}
$state = $this->slot_state($attempt, $attempt->slot);
@@ -182,54 +162,41 @@ public function col_answerstate($attempt) {
/**
* Generate the display of the user rating column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_userrating($attempt) {
- if ($attempt->userrating) {
- return $attempt->userrating;
- } else {
- return '-';
- }
+ public function col_userrating(stdClass $attempt): string {
+ return $attempt->userrating ?: '-';
}
/**
* Generate the display of the question rating column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_questionrating($attempt) {
- if ($attempt->questionrating) {
- return $attempt->questionrating;
- } else {
- return '-';
- }
+ public function col_questionrating(stdClass $attempt): string {
+ return $attempt->questionrating ?: '-';
}
/**
* Generate the display of the users's previous rating column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_userprevrating($attempt) {
- if ($attempt->userrating) {
- return $attempt->prevuserrating;
- } else {
- return '-';
- }
+ public function col_userprevrating(stdClass $attempt): string {
+ return $attempt->userrating ? $attempt->prevuserrating : '-';
}
/**
* Generate the display of the question's previous rating column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+
+ * @param stdClass $attempt the table row being output.
*/
- public function col_questionprevrating($attempt) {
+ public function col_questionprevrating(stdClass $attempt): string {
global $OUTPUT;
if ($attempt->prevquestionrating) {
- $warningicon = $OUTPUT->pix_icon('i/warning', get_string('rating_manually_updated', 'capquizreport_attempts'),
- 'moodle', array('class' => 'icon'));
-
+ $warningalt = get_string('rating_manually_updated', 'capquizreport_attempts');
+ $warningicon = $OUTPUT->pix_icon('i/warning', $warningalt, 'moodle', ['class' => 'icon']);
if (!$this->is_downloading() && $attempt->manualprevqrating) {
return $warningicon . $attempt->prevquestionrating;
} else {
@@ -242,25 +209,20 @@ public function col_questionprevrating($attempt) {
/**
* Generate the display of the question's previous rating manual column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_questionprevratingmanual($attempt) {
- if (is_null($attempt->manualprevqrating)) {
+ public function col_questionprevratingmanual(stdClass $attempt): string {
+ if ($attempt->manualprevqrating === null) {
return '-';
}
- $ismanual = ($attempt->manualprevqrating) ? 'true' : 'false';
- $manualprevqrating = get_string($ismanual, 'capquiz');
- return $manualprevqrating;
+ return get_string($attempt->manualprevqrating ? 'true' : 'false', 'capquiz');
}
/**
- * Does this report require the detailed information for each question from the
- * question_attempts_steps table?
- *
- * @return bool
+ * Does this report require the detailed information for each question from the question_attempts_steps table?
*/
- protected function requires_latest_steps_loaded() {
+ protected function requires_latest_steps_loaded(): bool {
if ($this->options->showansstate
|| $this->options->showqtext
|| $this->options->showresponses
@@ -288,13 +250,14 @@ protected function is_latest_step_column($column) {
/**
* 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) {
+ protected function update_sql_after_count($fields, $from, $where, $params): array {
$fields .= ',
cq.question_id AS moodlequestionid,
cqr.rating AS questionrating,
diff --git a/report/attempts/report.php b/report/attempts/report.php
index e7baa25..8e504ba 100644
--- a/report/attempts/report.php
+++ b/report/attempts/report.php
@@ -26,7 +26,9 @@
namespace capquizreport_attempts;
use context_course;
+use mod_capquiz\capquiz;
use mod_capquiz\report\capquiz_attempts_report;
+use stdClass;
defined('MOODLE_INTERNAL') || die();
@@ -51,17 +53,16 @@ class capquizreport_attempts_report extends capquiz_attempts_report {
* @param stdClass $course - course object
* @param string $download - type of download being requested
*/
- public function display($capquiz, $cm, $course, $download) {
- global $OUTPUT, $DB;
+ public function display($capquiz, $cm, $course, $download): bool {
+ global $DB;
- list($studentsjoins) = $this->init(
- 'attempts', 'capquizreport_attempts\capquizreport_attempts_settings_form', $capquiz, $cm, $course);
+ list($studentsjoins) = $this->init('attempts', 'capquizreport_attempts\capquizreport_attempts_settings_form',
+ $capquiz, $cm, $course);
$this->options = new capquizreport_attempts_options('attempts', $capquiz, $cm, $course);
if ($fromform = $this->form->get_data()) {
$this->options->process_settings_from_form($fromform);
-
} else {
$this->options->process_settings_from_params();
}
@@ -72,15 +73,16 @@ public function display($capquiz, $cm, $course, $download) {
$questions = capquiz_report_get_questions($capquiz);
// Prepare for downloading, if applicable.
- $courseshortname = format_string($course->shortname, true,
- array('context' => context_course::instance($course->id)));
+ $courseshortname = format_string($course->shortname, true, ['context' => context_course::instance($course->id)]);
$table = new capquizreport_attempts_table($capquiz, $this->context,
$this->options, $studentsjoins, $questions, $this->options->get_url());
+
$filename = capquiz_report_download_filename(get_string('attemptsfilename', 'capquizreport_attempts'),
$courseshortname, $capquiz->name());
- $table->is_downloading($this->options->download, $filename,
- $courseshortname . ' ' . format_string($capquiz->name(), true));
+
+ $table->is_downloading($this->options->download, $filename, $courseshortname . ' ' . format_string($capquiz->name()));
+
if ($table->is_downloading()) {
raise_memory_limit(MEMORY_EXTRA);
}
@@ -103,8 +105,7 @@ public function display($capquiz, $cm, $course, $download) {
// Start output.
if (!$table->is_downloading()) {
// Only print headers if not asked to download data.
- $this->print_standard_header_and_messages($cm, $course, $capquiz,
- $this->options, $hasquestions, $hasstudents);
+ $this->print_standard_header_and_messages($cm, $course, $capquiz, $this->options, $hasquestions, $hasstudents);
// Print the display options.
$this->form->display();
@@ -115,8 +116,8 @@ public function display($capquiz, $cm, $course, $download) {
$table->setup_sql_queries($studentsjoins);
// Define table columns.
- $columns = array();
- $headers = array();
+ $columns = [];
+ $headers = [];
if (!$table->is_downloading() && $this->options->checkboxcolumn) {
$columns[] = 'checkbox';
diff --git a/report/attemptsreport.php b/report/attemptsreport.php
index 3e4bc10..6e4182e 100644
--- a/report/attemptsreport.php
+++ b/report/attemptsreport.php
@@ -26,9 +26,11 @@
namespace mod_capquiz\report;
use context_module;
+use core\context\module;
use core\dml\sql_join;
-use mod_quiz_attempts_report_form;
-use mod_quiz_attempts_report_options;
+use mod_capquiz\capquiz;
+use mod_quiz\local\reports\attempts_report_options;
+use mod_quiz\local\reports\attempts_report_options_form;
use moodle_url;
use stdClass;
use table_sql;
@@ -38,7 +40,6 @@
require_once($CFG->libdir . '/tablelib.php');
require_once($CFG->dirroot . '/mod/capquiz/report/report.php');
-
/**
* Base class for capquiz reports that are basically a table with one row for each attempt.
*
@@ -62,13 +63,13 @@ abstract class capquiz_attempts_report extends report {
/** @var string the mode this report is. */
protected $mode;
- /** @var object the capquiz context. */
- protected $context;
+ /** @var module the capquiz context. */
+ protected module $context;
- /** @var mod_quiz_attempts_report_form The settings form to use. */
+ /** @var attempts_report_options_form The settings form to use. */
protected $form;
- /** @var object mod_quiz_attempts_report_options the options affecting this report. */
+ /** @var attempts_report_options the options affecting this report. */
protected $options = null;
/**
@@ -86,27 +87,20 @@ abstract class capquiz_attempts_report extends report {
* 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 init($mode, $formclass, $capquiz, $cm, $course) {
+ protected function init(string $mode, string $formclass, $capquiz, $cm, $course) {
$this->mode = $mode;
-
$this->context = context_module::instance($cm->id);
-
$studentsjoins = get_enrolled_with_capabilities_join($this->context);
-
- $this->form = new $formclass($this->get_base_url(),
- array('capquiz' => $capquiz, 'context' => $this->context));
-
- return array($studentsjoins);
+ $this->form = new $formclass($this->get_base_url(), ['capquiz' => $capquiz, 'context' => $this->context]);
+ return [$studentsjoins];
}
/**
* Get the base URL for this report.
- * @return moodle_url the URL.
*/
- protected function get_base_url() {
- return new moodle_url('/mod/capquiz/view_report.php',
- array('id' => $this->context->instanceid, 'mode' => $this->mode));
+ protected function get_base_url(): moodle_url {
+ return new moodle_url('/mod/capquiz/view_report.php', ['id' => $this->context->instanceid, 'mode' => $this->mode]);
}
/**
@@ -118,19 +112,21 @@ protected function get_base_url() {
*
* @param stdClass $cm the course_module information.
* @param stdClass $course the course settings.
- * @param stdClass $capquiz the capquiz settings.
- * @param mod_quiz_attempts_report_options $options the current report settings.
+ * @param capquiz $capquiz the capquiz settings.
+ * @param attempts_report_options $options the current report settings.
* @param bool $hasquestions whether there are any questions in the capquiz.
* @param bool $hasstudents whether there are any relevant students.
*/
- protected function print_standard_header_and_messages($cm, $course, $capquiz,
- $options, $hasquestions, $hasstudents) {
+ protected function print_standard_header_and_messages(stdClass $cm, stdClass $course, capquiz $capquiz,
+ attempts_report_options $options, bool $hasquestions,
+ bool $hasstudents): void {
global $OUTPUT;
- echo $this->print_header_and_tabs($cm, $course, $capquiz, $this->mode);
+ $this->print_header_and_tabs($cm, $course, $capquiz, $this->mode);
// Print information on the number of existing attempts.
- if ($strattemptnum = capquiz_num_attempt_summary($capquiz, true)) {
+ $strattemptnum = capquiz_num_attempt_summary($capquiz, true);
+ if (!empty($strattemptnum)) {
echo '
' . $strattemptnum . '
';
}
@@ -141,16 +137,16 @@ protected function print_standard_header_and_messages($cm, $course, $capquiz,
} else if (!$hasstudents) {
echo $OUTPUT->notification(get_string('nostudentsyet'));
}
-
}
/**
* 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) {
+ protected function add_user_columns(table_sql $table, array &$columns, array &$headers): void {
global $CFG;
if (!$table->is_downloading() && $CFG->grade_report_showuserimage) {
$columns[] = 'picture';
@@ -168,11 +164,11 @@ protected function add_user_columns($table, &$columns, &$headers) {
// When downloading, some extra fields are always displayed (because
// there's no space constraint) so do not include in extra-field list.
- $extrafields =
- $table->is_downloading() ?
- \core_user\fields::for_identity($this->context)->including(
- 'institution', 'department', 'email')->get_required_fields() :
- \core_user\fields::for_identity($this->context)->get_required_fields();
+ $fields = \core_user\fields::for_identity($this->context);
+ if ($table->is_downloading()) {
+ $fields = $fields->including('institution', 'department', 'email')->get_required_fields();
+ }
+ $extrafields = $fields->get_required_fields();
foreach ($extrafields as $field) {
$columns[] = $field;
@@ -193,60 +189,63 @@ protected function add_user_columns($table, &$columns, &$headers) {
/**
* 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_questionid_column(&$columns, &$headers) {
+ protected function add_questionid_column(array &$columns, array &$headers): void {
$columns[] = 'questionid';
$headers[] = get_string('questionid', 'capquiz');
}
/**
* 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_moodlequestionid_column(&$columns, &$headers) {
+ protected function add_moodlequestionid_column(array &$columns, array &$headers): void {
$columns[] = 'moodlequestionid';
$headers[] = get_string('moodlequestionid', 'capquiz');
}
/**
* 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_uesrid_column(&$columns, &$headers) {
+ protected function add_uesrid_column(array &$columns, array &$headers): void {
$columns[] = 'userid';
$headers[] = get_string('userid', 'capquiz');
}
/**
* 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) {
+ protected function add_time_columns(array &$columns, array &$headers): void {
$columns[] = 'timeanswered';
$headers[] = get_string('timeanswered', 'capquiz');
$columns[] = 'timereviewed';
$headers[] = get_string('timereviewed', 'capquiz');
-
}
/**
* 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) {
+ protected function configure_user_columns(table_sql $table): void {
$table->column_suppress('picture');
$table->column_suppress('fullname');
$extrafields = \core_user\fields::for_identity($this->context)->get_required_fields();
foreach ($extrafields as $field) {
$table->column_suppress($field);
}
-
$table->column_class('picture', 'picture');
$table->column_class('lastname', 'bold');
$table->column_class('firstname', 'bold');
@@ -255,34 +254,33 @@ protected function configure_user_columns($table) {
/**
* Process any submitted actions.
- * @param object $quiz the capquiz settings.
- * @param object $cm the cm object for the capquiz.
+ *
+ * @param stdClass $capquiz
+ * @param stdClass $cm the cm object for the capquiz.
* @param 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, sql_join $allowedjoins, $redirecturl) {
+ protected function process_actions(stdClass $capquiz, stdClass $cm, sql_join $allowedjoins, moodle_url $redirecturl): void {
if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) {
- if ($attemptids = optional_param_array('attemptid', array(), PARAM_INT)) {
+ if ($attemptids = optional_param_array('attemptid', [], PARAM_INT)) {
require_capability('mod/capquiz:deleteattempts', $this->context);
- $this->delete_selected_attempts($quiz, $cm, $attemptids, $allowedjoins);
+ $this->delete_selected_attempts($capquiz, $cm, $attemptids, $allowedjoins);
redirect($redirecturl);
}
}
}
/**
- * Delete the capquiz attempts
- * @param object $capquiz the capquiz settings. Attempts that don't belong to
- * this capquiz are not deleted.
- * @param object $cm the course_module object.
+ * Delete the capquiz attempts.
+ *
+ * @param stdClass $capquiz the capquiz settings. Attempts that don't belong to this capquiz are not deleted.
+ * @param stdClass $cm the course_module object.
* @param array $attemptids the list of attempt ids to delete.
* @param 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($capquiz, $cm, $attemptids, sql_join $allowedjoins) {
- global $DB;
+ protected function delete_selected_attempts(stdClass $capquiz, stdClass $cm, array $attemptids, sql_join $allowedjoins) {
// TODO implement to add support for attempt deletion.
-
}
}
diff --git a/report/attemptsreport_form.php b/report/attemptsreport_form.php
index 6a490aa..623607c 100644
--- a/report/attemptsreport_form.php
+++ b/report/attemptsreport_form.php
@@ -32,7 +32,6 @@
require_once($CFG->libdir . '/formslib.php');
-
/**
* Base class for the settings form for {@see capquiz_attempts_report}s.
*
@@ -58,23 +57,18 @@ public function validation($data, $files) {
/**
* Defines the form
*/
- protected function definition() {
+ protected function definition(): void {
$mform = $this->_form;
- $mform->addElement('header', 'preferencespage',
- get_string('reportwhattoinclude', 'quiz'));
-
+ $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'));
-
+ $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'));
+ $mform->addElement('submit', 'submitbutton', get_string('showreport', 'quiz'));
}
/**
@@ -82,8 +76,7 @@ protected function definition() {
*
* @param MoodleQuickForm $mform the form to add attempt fields to
*/
- protected function standard_attempt_fields(MoodleQuickForm $mform) {
-
+ protected function standard_attempt_fields(MoodleQuickForm $mform): void {
$mform->addElement('select', 'attempts', get_string('reportattemptsfrom', 'quiz'), array(
capquiz_attempts_report::ENROLLED_WITH => get_string('reportuserswith', 'quiz'),
// phpcs:disable
@@ -93,8 +86,7 @@ protected function standard_attempt_fields(MoodleQuickForm $mform) {
capquiz_attempts_report::ALL_WITH => get_string('reportusersall', 'quiz'),
));
- $mform->addElement('advcheckbox', 'onlyanswered', '',
- get_string('reportshowonlyanswered', 'capquiz'));
+ $mform->addElement('advcheckbox', 'onlyanswered', '', get_string('reportshowonlyanswered', 'capquiz'));
$mform->addHelpButton('onlyanswered', 'reportshowonlyanswered', 'capquiz');
}
diff --git a/report/attemptsreport_options.php b/report/attemptsreport_options.php
index fe36227..5451f1f 100644
--- a/report/attemptsreport_options.php
+++ b/report/attemptsreport_options.php
@@ -33,7 +33,6 @@
require_once($CFG->libdir . '/formslib.php');
-
/**
* Base class for the options that control what is visible in an {@see quiz_attempts_report}.
*
@@ -83,42 +82,40 @@ class capquiz_attempts_report_options {
* @param object $course the course settings for the coures this capquiz is in.
*/
public function __construct(string $mode, capquiz $capquiz, object $cm, object $course) {
- $this->mode = $mode;
- $this->capquiz = $capquiz;
- $this->cm = $cm;
- $this->course = $course;
+ $this->mode = $mode;
+ $this->capquiz = $capquiz;
+ $this->cm = $cm;
+ $this->course = $course;
}
/**
* 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,
+ protected function get_url_params(): array {
+ return [
+ 'id' => $this->cm->id,
+ 'mode' => $this->mode,
+ 'attempts' => $this->attempts,
'onlyanswered' => $this->onlyanswered,
- );
-
- return $params;
+ ];
}
/**
* Get the URL to show the report with these options.
- * @return moodle_url the URL.
*/
- public function get_url() {
+ public function get_url(): moodle_url {
return new moodle_url('/mod/capquiz/view_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.
+ * updating the fields of this class, and updating the user preferences where appropriate.
+ *
+ * @param stdClass $fromform The data from $mform->get_data() from the settings form.
*/
- public function process_settings_from_form($fromform) {
+ public function process_settings_from_form(stdClass $fromform): void {
$this->setup_from_form_data($fromform);
$this->resolve_dependencies();
$this->update_user_preferences();
@@ -128,7 +125,7 @@ public function process_settings_from_form($fromform) {
* 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() {
+ public function process_settings_from_params(): void {
$this->setup_from_user_preferences();
$this->setup_from_params();
$this->resolve_dependencies();
@@ -137,59 +134,57 @@ public function process_settings_from_params() {
/**
* Get the current value of the settings to pass to the settings form.
*/
- public function get_initial_form_data() {
+ public function get_initial_form_data(): stdClass {
$toform = new stdClass();
- $toform->attempts = $this->attempts;
+ $toform->attempts = $this->attempts;
$toform->onlyanswered = $this->onlyanswered;
- $toform->pagesize = $this->pagesize;
-
+ $toform->pagesize = $this->pagesize;
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.
+ * @param stdClass $fromform The data from $mform->get_data() from the settings form.
*/
- public function setup_from_form_data($fromform) {
- $this->attempts = $fromform->attempts;
+ public function setup_from_form_data(stdClass $fromform): void {
+ $this->attempts = $fromform->attempts;
$this->onlyanswered = !empty($fromform->onlyanswered);
- $this->pagesize = $fromform->pagesize;
+ $this->pagesize = $fromform->pagesize;
}
/**
* Set the fields of this object from the URL parameters.
*/
- public function setup_from_params() {
- $this->attempts = optional_param('attempts', $this->attempts, PARAM_ALPHAEXT);
+ public function setup_from_params(): void {
+ $this->attempts = optional_param('attempts', $this->attempts, PARAM_ALPHAEXT);
$this->onlyanswered = optional_param('onlyanswered', $this->onlyanswered, PARAM_BOOL);
- $this->pagesize = optional_param('pagesize', $this->pagesize, PARAM_INT);
- $this->download = optional_param('download', $this->download, PARAM_ALPHA);
+ $this->pagesize = optional_param('pagesize', $this->pagesize, PARAM_INT);
+ $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() {
+ public function setup_from_user_preferences(): void {
$this->pagesize = get_user_preferences('capquiz_report_pagesize', $this->pagesize);
}
/**
- * Update the user preferences so they match the settings in this object.
+ * 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() {
+ public function update_user_preferences(): void {
set_user_preference('capquiz_report_pagesize', $this->pagesize);
}
/**
* Check the settings, and remove any 'impossible' combinations.
*/
- public function resolve_dependencies() {
+ public function resolve_dependencies(): void {
if ($this->pagesize < 1) {
$this->pagesize = capquiz_attempts_report::DEFAULT_PAGE_SIZE;
}
}
-
}
diff --git a/report/attemptsreport_table.php b/report/attemptsreport_table.php
index 1b6c8c6..680608c 100644
--- a/report/attemptsreport_table.php
+++ b/report/attemptsreport_table.php
@@ -26,25 +26,22 @@
namespace mod_capquiz\report;
use coding_exception;
+use core\context;
+use core\context\module;
use core\dml\sql_join;
use html_writer;
-use mod_capquiz\capquiz_question_attempt;
-use mod_quiz_attempts_report_options;
use moodle_url;
use qubaid_condition;
use qubaid_list;
use question_engine_data_mapper;
use question_state;
-use quiz_attempt;
use stdClass;
use table_sql;
-use user_picture;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/tablelib.php');
-
/**
* Base class for the table used by a {@see capquiz_attempts_report}.
*
@@ -89,7 +86,8 @@ abstract class capquiz_attempts_report_table extends table_sql {
protected $includecheckboxes;
/**
- * Constructor
+ * Constructor.
+ *
* @param string $uniqueid
* @param object $quiz
* @param context $context
@@ -98,9 +96,8 @@ abstract class capquiz_attempts_report_table extends table_sql {
* @param array $questions
* @param moodle_url $reporturl
*/
- public function __construct($uniqueid, $quiz, $context,
- capquiz_attempts_report_options $options, sql_join $studentsjoins,
- $questions, $reporturl) {
+ public function __construct($uniqueid, $quiz, context $context, capquiz_attempts_report_options $options,
+ sql_join $studentsjoins, $questions, $reporturl) {
parent::__construct($uniqueid);
$this->capquiz = $quiz;
$this->context = $context;
@@ -113,11 +110,11 @@ public function __construct($uniqueid, $quiz, $context,
/**
* Generate the display of the checkbox column.
- * @param object $attempt the table row being output.
+ * @param stdClass $attempt the table row being output.
* @return string HTML content to go inside the td.
*/
- public function col_checkbox($attempt) {
- if ($attempt->attempt) {
+ public function col_checkbox(stdClass $attempt): string {
+ if (property_exists($attempt, 'attempt')) {
return '';
} else {
return '';
@@ -126,10 +123,10 @@ public function col_checkbox($attempt) {
/**
* 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) {
+ public function col_fullname($attempt): string {
$html = parent::col_fullname($attempt);
if ($this->is_downloading() || empty($attempt->attempt)) {
return $html;
@@ -144,68 +141,48 @@ public function col_fullname($attempt) {
/**
* Generate the display of the time answered column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_timeanswered($attempt) {
- if ($attempt->attempt) {
- return userdate($attempt->timeanswered, $this->strtimeformat);
- } else {
- return '-';
- }
+ public function col_timeanswered(stdClass $attempt): string {
+ return property_exists($attempt, 'attempt') ? userdate($attempt->timeanswered, $this->strtimeformat) : '-';
}
/**
* Generate the display of the time answered column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_timereviewed($attempt) {
- if ($attempt->attempt) {
- return userdate($attempt->timereviewed, $this->strtimeformat);
- } else {
- return '-';
- }
+ public function col_timereviewed(stdClass $attempt) {
+ return property_exists($attempt, 'attempt') ? userdate($attempt->timereviewed, $this->strtimeformat) : '-';
}
/**
* Generate the display of the question id column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_questionid($attempt) {
- if ($attempt->questionid) {
- return $attempt->questionid;
- } else {
- return '-';
- }
+ public function col_questionid(stdClass $attempt): string {
+ return $attempt->questionid ?: '-';
}
/**
* Generate the display of the moodle question rating column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_moodlequestionid($attempt) {
- if ($attempt->moodlequestionid) {
- return $attempt->moodlequestionid;
- } else {
- return '-';
- }
+ public function col_moodlequestionid(stdClass $attempt): string {
+ return $attempt->moodlequestionid ?: '-';
}
/**
* Generate the display of the user id column.
- * @param object $attempt the table row being output.
- * @return string HTML content to go inside the td.
+ *
+ * @param stdClass $attempt the table row being output.
*/
- public function col_userid($attempt) {
- if ($attempt->userid) {
- return $attempt->userid;
- } else {
- return '-';
- }
+ public function col_userid(stdClass $attempt): string {
+ return $attempt->userid ?: '-';
}
/**
@@ -215,9 +192,7 @@ public function col_userid($attempt) {
* @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;
-
+ public function make_review_link($data, $attempt, $slot): string {
$feedbackimg = '';
$state = $this->slot_state($attempt, $slot);
if ($state->is_finished() && $state != question_state::$needsgrading) {
@@ -225,9 +200,9 @@ public function make_review_link($data, $attempt, $slot) {
}
$output = html_writer::tag('span', $feedbackimg . html_writer::tag('span',
- $data, array('class' => $state->get_state_class(true))), array('class' => 'que'));
+ $data, ['class' => $state->get_state_class(true)]), ['class' => 'que']);
- $reviewparams = array('attempt' => $attempt->attempt, 'slot' => $slot);
+ $reviewparams = ['attempt' => $attempt->attempt, 'slot' => $slot];
if (isset($attempt->try)) {
$reviewparams['step'] = $this->step_no_for_try($attempt->usageid, $slot, $attempt->try);
}
@@ -236,8 +211,7 @@ public function make_review_link($data, $attempt, $slot) {
// phpcs:disable
/*$url = new moodle_url('/mod/capquiz/reviewquestion.php', $reviewparams);
$output = $OUTPUT->action_link($url, $output,
- new popup_action('click', $url, 'reviewquestion',
- array('height' => 450, 'width' => 650)),
+ new popup_action('click', $url, 'reviewquestion', array('height' => 450, 'width' => 650)),
array('title' => get_string('reviewresponse', 'quiz')));*/
// phpcs:enable
return $output;
@@ -252,30 +226,21 @@ public function make_review_link($data, $attempt, $slot) {
*/
public function make_preview_link($data, $attempt, $slot) {
global $OUTPUT;
-
$questionid = $this->slot_questionid($attempt, $slot);
-
- $output = html_writer::tag('span', html_writer::tag('span', $data),
- array('class' => 'que'));
-
+ $output = html_writer::tag('span', html_writer::tag('span', $data), ['class' => 'que']);
$url = \qbank_previewquestion\helper::question_preview_url($questionid)->out(false);
-
- $output = $OUTPUT->action_link($url, $output,
- new \popup_action('click', $url, 'previewquestion',
- array('height' => 450, 'width' => 650)),
- array('title' => get_string('previewquestion', 'quiz')));
-
- return $output;
+ return $OUTPUT->action_link($url, $output, new \popup_action('click', $url, 'previewquestion', [
+ 'height' => 450, 'width' => 650]), ['title' => get_string('previewquestion', 'quiz')]);
}
/**
* Find the state for $slot given after this try.
*
- * @param object $attempt the row data
+ * @param stdClass $attempt
* @param int $slot
* @return question_state
*/
- protected function slot_state($attempt, $slot) {
+ protected function slot_state(stdClass $attempt, int $slot): question_state {
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
return question_state::get($stepdata->state);
}
@@ -283,36 +248,33 @@ protected function slot_state($attempt, $slot) {
/**
* Returns the id of the question
*
- * @param object $attempt the row data
+ * @param stdClass $attempt
* @param int $slot
- * @return question_id
*/
- protected function slot_questionid($attempt, $slot) {
+ protected function slot_questionid(stdClass $attempt, int $slot): int {
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
return $stepdata->questionid;
}
/**
* 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) {
+ protected function icon_for_fraction(float $fraction): string {
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'));
+ $feedbackalt = get_string($feedbackclass, 'question');
+ return $OUTPUT->pix_icon('i/grade_' . $feedbackclass, $feedbackalt, 'moodle', ['class' => 'icon']);
}
/**
* The grade for this slot after this try.
*
- * @param object $attempt the row data
+ * @param stdClass $attempt
* @param int $slot
- * @return float
*/
- protected function slot_fraction($attempt, $slot) {
+ protected function slot_fraction(stdClass $attempt, int $slot): float {
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
return $stepdata->fraction;
}
@@ -322,7 +284,7 @@ protected function slot_fraction($attempt, $slot) {
*
* @param sql_join $allowedjoins (joins, wheres, params) defines allowed users for the report.
*/
- public function setup_sql_queries($allowedjoins) {
+ public function setup_sql_queries(sql_join $allowedjoins): void {
list($fields, $from, $where, $params) = $this->base_sql($allowedjoins);
// The WHERE clause is vital here, because some parts of tablelib.php will expect to
@@ -344,11 +306,11 @@ public function base_sql(sql_join $allowedstudentsjoins) {
$fields = 'DISTINCT ' . $DB->sql_concat('u.id', "'#'", 'COALESCE(ca.id, 0)') . ' AS uniqueid,';
- $extrafields =
- \core_user\fields::for_identity($this->context)->including(
- 'id', 'idnumber', 'firstname', 'lastname', 'picture',
- 'imagealt', 'institution', 'department', 'email'
- )->get_sql('u')->selects;
+ $extrafields = \core_user\fields::for_identity($this->context)
+ ->including(
+ 'id', 'idnumber', 'firstname', 'lastname', 'picture', 'imagealt', 'institution', 'department', 'email'
+ )
+ ->get_sql('u')->selects;
// phpcs:disable
// $allnames = get_all_user_name_fields(true, 'u');
// phpcs:enable
@@ -415,7 +377,7 @@ public function base_sql(sql_join $allowedstudentsjoins) {
$where .= " AND ca.answered = 1";
}
- return array($fields, $from, $where, $params);
+ return [$fields, $from, $where, $params];
}
/**
@@ -438,9 +400,8 @@ protected function update_sql_after_count($fields, $from, $where, $params) {
* @param bool $useinitialsbar do you want to use the initials bar. Bar
* will only be used if there is a fullname column defined for the table.
*/
- public function query_db($pagesize, $useinitialsbar = true) {
+ public function query_db($pagesize, $useinitialsbar = true): void {
parent::query_db($pagesize, $useinitialsbar);
-
if ($this->requires_extra_data()) {
$this->load_extra_data();
}
@@ -457,8 +418,8 @@ protected function requires_extra_data() {
}
/**
- * Does this report require the detailed information for each question from the
- * question_attempts_steps table?
+ * 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() {
@@ -470,49 +431,40 @@ protected function requires_latest_steps_loaded() {
* 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() {
+ protected function load_extra_data(): void {
$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 {@see qubaid_condition}.
+ * @param ?qubaid_condition $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) {
+ protected function load_question_latest_steps(?qubaid_condition $qubaids = null): array {
if ($qubaids === null) {
$qubaids = $this->get_qubaids_condition();
}
-
$dm = new question_engine_data_mapper();
- $latesstepdata = $dm->load_questions_usages_latest_steps(
- $qubaids, array_map(function($o) {
- return $o->slot;
- }, $this->questions));
-
- $lateststeps = array();
+ $latesstepdata = $dm->load_questions_usages_latest_steps($qubaids, array_map(fn($o) => $o->slot, $this->questions));
+ $lateststeps = [];
foreach ($latesstepdata as $step) {
$lateststeps[$step->questionusageid][$step->slot] = $step;
}
-
return $lateststeps;
}
/**
- * Get an appropriate qubaid_condition for loading more data about the
- * attempts we are displaying.
- * @return qubaid_condition
+ * Get an appropriate qubaid_condition for loading more data about the attempts we are displaying.
+ *
+ * @return qubaid_list
*/
- 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.');
+ protected function get_qubaids_condition(): qubaid_list {
+ if ($this->rawdata === null) {
+ throw new coding_exception('Cannot call get_qubaids_condition until the main data has been loaded.');
}
- $qubaids = array();
+ $qubaids = [];
foreach ($this->rawdata as $attempt) {
if ($attempt->usageid > 0) {
$qubaids[] = $attempt->usageid;
@@ -523,9 +475,10 @@ protected function get_qubaids_condition() {
/**
* Get the columns to sort by, in the form required by {@see construct_order_by()}.
+ *
* @return array column name => SORT_... constant.
*/
- public function get_sort_columns() {
+ public function get_sort_columns(): array {
// 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();
@@ -540,13 +493,10 @@ public function wrap_html_start() {
if ($this->is_downloading() || !$this->includecheckboxes) {
return;
}
-
$url = $this->options->get_url();
$url->param('sesskey', sesskey());
-
echo '