diff --git a/admin/tool/brickfield/classes/local/areas/core_question/base.php b/admin/tool/brickfield/classes/local/areas/core_question/base.php
index 8887675f059..0dbf58be1b6 100644
--- a/admin/tool/brickfield/classes/local/areas/core_question/base.php
+++ b/admin/tool/brickfield/classes/local/areas/core_question/base.php
@@ -137,7 +137,7 @@ abstract class base extends area_base {
}
// Default to SITEID if courseid is null, i.e. system or category level questions.
$thiscourseid = ($componentinfo->courseid !== null) ? $componentinfo->courseid : SITEID;
- return new \moodle_url('/question/question.php', ['courseid' => $thiscourseid, 'id' => $questionid]);
+ return new \moodle_url('/question/bank/editquestion/question.php', ['courseid' => $thiscourseid, 'id' => $questionid]);
}
/**
diff --git a/config-dist.php b/config-dist.php
index 317fd9543ca..31b7dced261 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -692,23 +692,6 @@ $CFG->admin = 'admin';
// $CFG->uninstallclionly = true;
//
//
-// Customise question bank display
-//
-// The display of Moodle's question bank is made up of a number of columns.
-// You can customise this display by giving a comma-separated list of column class
-// names here. Each class must be a subclass of \core_question\bank\column_base.
-// For example you might define a class like
-// class \local_qbank_extensions\my_column extends \core_question\bank\column_base
-// in a local plugin, then add it to the list here. At the time of writing,
-// the default question bank display is equivalent to the following, but you might like
-// to check the latest default in question/classes/bank/view.php before setting this.
-//
-// $CFG->questionbankcolumns = 'checkbox_column,question_type_column,'
-// . 'question_name_idnumber_tags_column,edit_menu_column,'
-// . 'tags_action_column,edit_action_column,copy_action_column,'
-// . 'preview_action_column,delete_action_column,export_xml_action_column,'
-// . 'creator_name_column,modifier_name_column';
-//
// Forum summary report
//
// In order for the forum summary report to calculate word count and character count data, those details are now stored
diff --git a/lib/classes/event/question_created.php b/lib/classes/event/question_created.php
index b05e1fb151d..feeb4edc747 100644
--- a/lib/classes/event/question_created.php
+++ b/lib/classes/event/question_created.php
@@ -77,11 +77,11 @@ class question_created extends question_base {
public function get_url() {
if ($this->courseid) {
if ($this->contextlevel == CONTEXT_MODULE) {
- return new \moodle_url('/question/preview.php', ['cmid' => $this->contextinstanceid, 'id' => $this->objectid]);
+ return new \moodle_url('/question/bank/previewquestion/preview.php', ['cmid' => $this->contextinstanceid, 'id' => $this->objectid]);
}
- return new \moodle_url('/question/preview.php', ['courseid' => $this->courseid, 'id' => $this->objectid]);
+ return new \moodle_url('/question/bank/previewquestion/preview.php', ['courseid' => $this->courseid, 'id' => $this->objectid]);
}
// Lets try editing from the frontpage for contexts above course.
- return new \moodle_url('/question/preview.php', ['courseid' => SITEID, 'id' => $this->objectid]);
+ return new \moodle_url('/question/bank/previewquestion/preview.php', ['courseid' => SITEID, 'id' => $this->objectid]);
}
}
diff --git a/lib/classes/plugininfo/qbank.php b/lib/classes/plugininfo/qbank.php
index d2e8540e446..8978dd90423 100644
--- a/lib/classes/plugininfo/qbank.php
+++ b/lib/classes/plugininfo/qbank.php
@@ -25,8 +25,6 @@
namespace core\plugininfo;
-defined('MOODLE_INTERNAL') || die();
-
/**
* Base class for qbank plugins.
*
@@ -38,6 +36,9 @@ defined('MOODLE_INTERNAL') || die();
class qbank extends base {
public function is_uninstall_allowed(): bool {
+ if (in_array($this->name, \core_plugin_manager::standard_plugins_list('qbank'))) {
+ return false;
+ }
return true;
}
diff --git a/lib/db/renamedclasses.php b/lib/db/renamedclasses.php
index 8e58d6d9812..f5eedc44266 100644
--- a/lib/db/renamedclasses.php
+++ b/lib/db/renamedclasses.php
@@ -35,17 +35,51 @@ defined('MOODLE_INTERNAL') || die();
// Like other files in the db directory this file uses an array.
// The old class name is the key, the new class name is the value.
// The array must be called $renamedclasses.
-$renamedclasses = array(
+$renamedclasses = [
// Since Moodle 3.7.
'core\\analytics\\target\\course_dropout' => 'core_course\\analytics\\target\\course_dropout',
'core\\analytics\\target\\course_competencies' => 'core_course\\analytics\\target\\course_competencies',
'core\\analytics\\target\\course_completion' => 'core_course\\analytics\\target\\course_completion',
'core\\analytics\\target\\course_gradetopass' => 'core_course\\analytics\\target\\course_gradetopass',
'core\\analytics\\target\\no_teaching' => 'core_course\\analytics\\target\\no_teaching',
+ // Since Moodle 4.0.
'format_base' => 'core_courseformat\\base',
'format_topics_renderer' => 'format_topics\\output\\renderer',
'format_section_renderer_base' => 'core_courseformat\\output\\section_renderer',
'format_singleactivity_renderer' => 'format_singleactivity\\output\\renderer',
'format_site_renderer' => 'core_courseformat\\output\\site_renderer',
'format_weeks_renderer' => 'format_weeks\\output\\renderer',
-);
+ 'core_question\\bank\\action_column_base' => 'core_question\\local\\bank\\action_column_base',
+ 'core_question\\bank\\checkbox_column' => 'core_question\\local\\bank\\checkbox_column',
+ 'core_question\\bank\\column_base' => 'core_question\\local\\bank\\column_base',
+ 'core_question\\bank\\edit_menu_column' => 'core_question\\local\\bank\\edit_menu_column',
+ 'core_question\\bank\\menu_action_column_base' => 'core_question\\local\\bank\\menu_action_column_base',
+ 'core_question\\bank\\menuable_action' => 'core_question\\local\\bank\\menuable_action',
+ 'core_question\\bank\\random_question_loader' => 'core_question\\local\\bank\\random_question_loader',
+ 'core_question\\bank\\row_base' => 'core_question\\local\\bank\\row_base',
+ 'core_question\\bank\\view' => 'core_question\\local\\bank\\view',
+ 'core_question\\bank\\copy_action_column' => 'qbank_editquestion\\copy_action_column',
+ 'core_question\\bank\\edit_action_column' => 'qbank_editquestion\\edit_action_column',
+ 'core_question\\bank\\creator_name_column' => 'qbank_viewcreator\\creator_name_column',
+ 'core_question\\bank\\modifier_name_column' => 'qbank_viewcreator\\modifier_name_column',
+ 'core_question\\bank\\question_name_column' => 'qbank_viewquestionname\\viewquestionname_column_helper',
+ 'core_question\\bank\\question_name_idnumber_tags_column' => 'qbank_viewquestionname\\question_name_idnumber_tags_column',
+ 'core_question\\bank\\delete_action_column' => 'qbank_deletequestion\\delete_action_column',
+ 'core_question\\bank\\export_xml_action_column' => 'qbank_exporttoxml\\export_xml_action_column',
+ 'core_question\\bank\\preview_action_column' => 'qbank_previewquestion\\preview_action_column',
+ 'core_question\\bank\\question_text_row' => 'qbank_viewquestiontext\\question_text_row',
+ 'core_question\\bank\\question_type_column' => 'qbank_viewquestiontype\\question_type_column',
+ 'core_question\\bank\\tags_action_column' => 'qbank_tagquestion\\tags_action_column',
+ 'core_question\\output\\qbank_chooser' => 'qbank_editquestion\\qbank_chooser',
+ 'core_question\\output\\qbank_chooser_item' => 'qbank_editquestion\\qbank_chooser_item',
+ 'question_move_form' => 'qbank_managecategories\\form\\question_move_form',
+ 'question_import_form' => 'qbank_importquestions\\form\\question_import_form',
+ 'question_category_list' => 'qbank_managecategories\\question_category_list',
+ 'question_category_list_item' => 'qbank_managecategories\\question_category_list_item',
+ 'question_category_object' => 'qbank_managecategories\\question_category_object',
+ 'category_form' => 'qbank_managecategories\\form\\category_form',
+ 'export_form' => 'qbank_exportquestions\\form\\export_form',
+ 'preview_options_form' => 'qbank_previewquestion\\form\\preview_options_form',
+ 'question_preview_options' => 'qbank_previewquestion\\output\\question_preview_options',
+ 'core_question\\form\\tags' => '\qbank_tagquestion\\form\\tags_form'
+];
diff --git a/lib/questionlib.php b/lib/questionlib.php
index e8eaa55b603..1fdf92f5add 100644
--- a/lib/questionlib.php
+++ b/lib/questionlib.php
@@ -759,14 +759,12 @@ function question_move_category_to_context($categoryid, $oldcontextid, $newconte
* @return moodle_url the URL.
* @deprecated since Moodle 4.0
* @see qbank_previewquestion\helper::question_preview_url()
- * @todo MDL-71679 uncomment the debug messages after implementing the changes in mod_quiz.
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
function question_preview_url($questionid, $preferredbehaviour = null,
$maxmark = null, $displayoptions = null, $variant = null, $context = null) {
- // Debugging message will be re-added after implementing the changes in mod_quiz.
- // ...debugging('Function question_preview_url() has been deprecated and moved to qbank_previewquestion plugin,
- // Please use qbank_previewquestion\helper::question_preview_url() instead.', DEBUG_DEVELOPER);.
+ debugging('Function question_preview_url() has been deprecated and moved to qbank_previewquestion plugin,
+ Please use qbank_previewquestion\helper::question_preview_url() instead.', DEBUG_DEVELOPER);
return \qbank_previewquestion\helper::question_preview_url($questionid, $preferredbehaviour = null,
$maxmark = null, $displayoptions = null, $variant = null, $context = null);
@@ -776,13 +774,11 @@ function question_preview_url($questionid, $preferredbehaviour = null,
* @return array that can be passed as $params to the {@link popup_action} constructor.
* @deprecated since Moodle 4.0
* @see qbank_previewquestion\helper::question_preview_popup_params()
- * @todo MDL-71679 uncomment the debug messages after implementing the changes to mod_quiz.
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
function question_preview_popup_params() {
- // Debugging message will be re-added after implementing the changes in mod_quiz.
- // ...debugging('Function question_preview_popup_params() has been deprecated and moved to qbank_previewquestion plugin,
- // Please use qbank_previewquestion\helper::question_preview_popup_params() instead.', DEBUG_DEVELOPER);.
+ debugging('Function question_preview_popup_params() has been deprecated and moved to qbank_previewquestion plugin,
+ Please use qbank_previewquestion\helper::question_preview_popup_params() instead.', DEBUG_DEVELOPER);
return \qbank_previewquestion\helper::question_preview_popup_params();
}
@@ -1656,18 +1652,9 @@ function question_extend_settings_navigation(navigation_node $navigationnode, $c
'title' => get_string('questions', 'question'),
'url' => new moodle_url($baseurl)
],
- 'categories' => [
- 'title' => get_string('categories', 'question'),
- 'url' => new moodle_url('/question/category.php')
- ],
- 'import' => [
- 'title' => get_string('import', 'question'),
- 'url' => new moodle_url('/question/import.php')
- ],
- 'export' => [
- 'title' => get_string('export', 'question'),
- 'url' => new moodle_url('/question/export.php')
- ]
+ 'categories' => [],
+ 'import' => [],
+ 'export' => []
];
$plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
@@ -1692,16 +1679,23 @@ function question_extend_settings_navigation(navigation_node $navigationnode, $c
];
}
}
+ }
+ // Mitigate the risk of regression.
+ foreach ($corenavigations as $node => $corenavigation) {
+ if (empty($corenavigation)) {
+ unset($corenavigations[$node]);
+ }
}
// Community/additional plugins have navigation node.
$pluginnavigations = [];
foreach ($plugins as $componentname => $plugin) {
- $pluginentrypoin = new $plugin();
- $pluginentrypointobject = $pluginentrypoin->get_navigation_node();
- if (!\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
- unset($corenavigations[$key]);
+ $pluginentrypoint = new $plugin();
+ $pluginentrypointobject = $pluginentrypoint->get_navigation_node();
+ // Don't need the plugins without navigation node.
+ if ($pluginentrypointobject === null || !\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
+ unset($plugins[$componentname]);
continue;
}
$pluginnavigations[$pluginentrypointobject->get_navigation_key()] = [
diff --git a/lib/upgrade.txt b/lib/upgrade.txt
index 5f054c1316c..9f5db3e4c91 100644
--- a/lib/upgrade.txt
+++ b/lib/upgrade.txt
@@ -71,6 +71,11 @@ information provided here is intended especially for developers.
that wants to listen for the events. Backwards compatibility with existing code using jQuery is preserved.
* The function message_send() in messagelib.php now returns false if there is an error sending the message to the
message processor (MDL-70046).
+* Following methods are now deprecated in questionlib and moved to the new qbank_previewquestion plugin:
+ - question_preview_url() is moved to qbank_previewquestion\helper::question_preview_url()
+ - question_preview_popup_params() is moved to \qbank_previewquestion\helper::question_preview_popup_params()
+ Calling these functions in the question will point to the plugin.
+ The deprecated codes are removed from the questionlib for those two methods.
* The following functions are deprecated in questionlib.php and moved to the new location.
These are marked for final deprecation on 4.4:
- question_remove_stale_questions_from_category() =>
diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php
index 261b4a19afc..91a60482da2 100644
--- a/mod/quiz/attemptlib.php
+++ b/mod/quiz/attemptlib.php
@@ -2107,7 +2107,7 @@ class quiz_attempt {
} else {
$tagids = quiz_retrieve_slot_tag_ids($this->slots[$slot]->id);
- $randomloader = new \core_question\bank\random_question_loader($qubaids, array());
+ $randomloader = new \core_question\local\bank\random_question_loader($qubaids, array());
$newqusetionid = $randomloader->get_next_question_id($questiondata->category,
(bool) $questiondata->questiontext, $tagids);
if ($newqusetionid === null) {
diff --git a/mod/quiz/classes/output/edit_renderer.php b/mod/quiz/classes/output/edit_renderer.php
index f7d15b75a30..6f0cdda398b 100644
--- a/mod/quiz/classes/output/edit_renderer.php
+++ b/mod/quiz/classes/output/edit_renderer.php
@@ -651,7 +651,7 @@ class edit_renderer extends \plugin_renderer_base {
'addonpage' => $page, 'appendqnumstring' => 'addquestion');
$actions['addaquestion'] = new \action_menu_link_secondary(
- new \moodle_url('/question/addquestion.php', $params),
+ new \moodle_url('/question/bank/editquestion/addquestion.php', $params),
new \pix_icon('t/add', $str->addaquestion, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->addaquestion, array('class' => 'cm-edit-action addquestion', 'data-action' => 'addquestion')
);
@@ -722,7 +722,7 @@ class edit_renderer extends \plugin_renderer_base {
return html_writer::tag('form', html_writer::div($output),
array('class' => 'addnewquestion', 'method' => 'post',
- 'action' => new \moodle_url('/question/addquestion.php')));
+ 'action' => new \moodle_url('/question/bank/editquestion/addquestion.php')));
}
/**
@@ -836,7 +836,7 @@ class edit_renderer extends \plugin_renderer_base {
$image = $this->pix_icon('t/preview', $strpreviewquestion);
$action = new \popup_action('click', $url, 'questionpreview',
- question_preview_popup_params());
+ \qbank_previewquestion\helper::question_preview_popup_params());
return $this->action_link($url, $image . $strpreviewlabel, $action,
array('title' => $strpreviewquestion, 'class' => 'preview'));
@@ -946,7 +946,7 @@ class edit_renderer extends \plugin_renderer_base {
$output = '';
$question = $structure->get_question_in_slot($slot);
- $editurl = new \moodle_url('/question/question.php', array(
+ $editurl = new \moodle_url('/question/bank/editquestion/question.php', array(
'returnurl' => $pageurl->out_as_local_url(),
'cmid' => $structure->get_cmid(), 'id' => $question->id));
@@ -1252,9 +1252,7 @@ class edit_renderer extends \plugin_renderer_base {
*/
public function question_bank_contents(\mod_quiz\question\bank\custom_view $questionbank, array $pagevars) {
- $qbank = $questionbank->render('editq', $pagevars['qpage'], $pagevars['qperpage'],
- $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['qbshowtext'],
- $pagevars['qtagids']);
+ $qbank = $questionbank->render($pagevars, 'editq');
return html_writer::div(html_writer::div($qbank, 'bd'), 'questionbankformforpopup');
}
}
diff --git a/mod/quiz/classes/output/question_chooser.php b/mod/quiz/classes/output/question_chooser.php
index ee16e1d0e66..f58ffe114e2 100644
--- a/mod/quiz/classes/output/question_chooser.php
+++ b/mod/quiz/classes/output/question_chooser.php
@@ -32,5 +32,5 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class question_chooser extends \core_question\output\qbank_chooser {
+class question_chooser extends \qbank_editquestion\qbank_chooser {
}
diff --git a/mod/quiz/classes/question/bank/add_action_column.php b/mod/quiz/classes/question/bank/add_action_column.php
index cf314b55f9b..d47367488f9 100644
--- a/mod/quiz/classes/question/bank/add_action_column.php
+++ b/mod/quiz/classes/question/bank/add_action_column.php
@@ -14,30 +14,23 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * A column type for the add this question to the quiz action.
- *
- * @package mod_quiz
- * @category question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
namespace mod_quiz\question\bank;
-defined('MOODLE_INTERNAL') || die();
-
/**
* A column type for the add this question to the quiz action.
*
+ * @package mod_quiz
+ * @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\bank\action_column_base {
+class add_action_column extends \core_question\local\bank\action_column_base {
+
/** @var string caches a lang string used repeatedly. */
protected $stradd;
- public function init() {
+ public function init(): void {
parent::init();
$this->stradd = get_string('addtoquiz', 'quiz');
}
diff --git a/mod/quiz/classes/question/bank/custom_view.php b/mod/quiz/classes/question/bank/custom_view.php
index 0e15338176a..4f4f4036e3a 100644
--- a/mod/quiz/classes/question/bank/custom_view.php
+++ b/mod/quiz/classes/question/bank/custom_view.php
@@ -31,18 +31,22 @@ use coding_exception;
* Subclass to customise the view of the question bank for the quiz editing screen.
*
* @copyright 2009 Tim Hunt
+ * @author 2021 Safat Shahin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class custom_view extends \core_question\bank\view {
- /** @var bool whether the quiz this is used by has been attemptd. */
+class custom_view extends \core_question\local\bank\view {
+
+ /** @var bool $quizhasattempts whether the quiz this is used by has been attemptd. */
protected $quizhasattempts = false;
- /** @var \stdClass the quiz settings. */
+
+ /** @var \stdClass $quiz the quiz settings. */
protected $quiz = false;
+
/** @var int The maximum displayed length of the category info. */
const MAX_TEXT_LENGTH = 200;
/**
- * Constructor
+ * Constructor for custom_view.
* @param \question_edit_contexts $contexts
* @param \moodle_url $pageurl
* @param \stdClass $course course settings
@@ -54,76 +58,66 @@ class custom_view extends \core_question\bank\view {
$this->quiz = $quiz;
}
- protected function wanted_columns() {
- global $CFG;
-
- if (empty($CFG->quizquestionbankcolumns)) {
- $quizquestionbankcolumns = array(
- 'add_action_column',
- 'checkbox_column',
- 'question_type_column',
- 'question_name_text_column',
- 'preview_action_column',
- );
- } else {
- $quizquestionbankcolumns = explode(',', $CFG->quizquestionbankcolumns);
- }
+ protected function wanted_columns(): array {
+ $quizquestionbankcolumns = [
+ 'mod_quiz\\question\\bank\\add_action_column',
+ 'core_question\\local\\bank\\checkbox_column',
+ 'qbank_viewquestiontype\\question_type_column',
+ 'mod_quiz\\question\\bank\\question_name_text_column',
+ 'qbank_previewquestion\\preview_action_column',
+ ];
foreach ($quizquestionbankcolumns as $fullname) {
if (!class_exists($fullname)) {
- if (class_exists('mod_quiz\\question\\bank\\' . $fullname)) {
- $fullname = 'mod_quiz\\question\\bank\\' . $fullname;
- } else if (class_exists('core_question\\bank\\' . $fullname)) {
- $fullname = 'core_question\\bank\\' . $fullname;
- } else if (class_exists('question_bank_' . $fullname)) {
- debugging('Legacy question bank column class question_bank_' .
- $fullname . ' should be renamed to mod_quiz\\question\\bank\\' .
- $fullname, DEBUG_DEVELOPER);
- $fullname = 'question_bank_' . $fullname;
- } else {
- throw new coding_exception('Invalid quiz question bank column', $fullname);
- }
+ throw new coding_exception('Invalid quiz question bank column', $fullname);
}
$this->requiredcolumns[$fullname] = new $fullname($this);
}
return $this->requiredcolumns;
}
- /**
- * Specify the column heading
- *
- * @return string Column name for the heading
- */
- protected function heading_column() {
+ protected function heading_column(): string {
return 'mod_quiz\\question\\bank\\question_name_text_column';
}
- protected function default_sort() {
- return array(
- 'core_question\\bank\\question_type_column' => 1,
+ protected function default_sort(): array {
+ return [
+ 'qbank_viewquestiontype\\question_type_column' => 1,
'mod_quiz\\question\\bank\\question_name_text_column' => 1,
- );
+ ];
}
/**
* Let the question bank display know whether the quiz has been attempted,
* hence whether some bits of UI, like the add this question to the quiz icon,
* should be displayed.
+ *
* @param bool $quizhasattempts whether the quiz has attempts.
*/
- public function set_quiz_has_attempts($quizhasattempts) {
+ public function set_quiz_has_attempts($quizhasattempts): void {
$this->quizhasattempts = $quizhasattempts;
if ($quizhasattempts && isset($this->visiblecolumns['addtoquizaction'])) {
unset($this->visiblecolumns['addtoquizaction']);
}
}
+ /**
+ * Question preview url.
+ *
+ * @param \stdClass $question
+ * @return \moodle_url
+ */
public function preview_question_url($question) {
return quiz_question_preview_url($this->quiz, $question);
}
+ /**
+ * URL of add to quiz.
+ *
+ * @param $questionid
+ * @return \moodle_url
+ */
public function add_to_quiz_url($questionid) {
- global $CFG;
$params = $this->baseurl->params();
$params['addquestion'] = $questionid;
$params['sesskey'] = sesskey();
@@ -136,34 +130,26 @@ class custom_view extends \core_question\bank\view {
* Note that you can only output this rendered result once per page, as
* it contains IDs which must be unique.
*
+ * @param array $pagevars
+ * @param string $tabname
* @return string HTML code for the form
*/
- public function render($tabname, $page, $perpage, $cat, $recurse, $showhidden,
- $showquestiontext, $tagids = []) {
+ public function render($pagevars, $tabname): string {
ob_start();
- $this->display($tabname, $page, $perpage, $cat, $recurse, $showhidden, $showquestiontext, $tagids);
+ $this->display($pagevars, $tabname);
$out = ob_get_contents();
ob_end_clean();
return $out;
}
- /**
- * Display the controls at the bottom of the list of questions.
- * @param int $totalnumber Total number of questions that might be shown (if it was not for paging).
- * @param bool $recurse Whether to include subcategories.
- * @param \stdClass $category The question_category row from the database.
- * @param \context $catcontext The context of the category being displayed.
- * @param array $addcontexts contexts where the user is allowed to add new questions.
- */
- protected function display_bottom_controls($totalnumber, $recurse, $category, \context $catcontext, array $addcontexts) {
+ protected function display_bottom_controls($totalnumber, $recurse, $category, \context $catcontext, array $addcontexts): void {
$cmoptions = new \stdClass();
$cmoptions->hasattempts = !empty($this->quizhasattempts);
$canuseall = has_capability('moodle/question:useall', $catcontext);
- echo '
';
- }
-
- protected function display_options($recurse, $showhidden, $showquestiontext) {
- debugging('display_options() is deprecated, see display_options_form() instead.', DEBUG_DEVELOPER);
- echo '';
- }
-
- protected function create_new_question_form($category, $canadd) {
+ protected function create_new_question_form($category, $canadd): void {
// Don't display this.
}
/**
- * Override the base implementation in \core_question\bank\view
+ * Override the base implementation in \core_question\local\bank\view
* because we don't want to print the headers in the fragment
* for the modal.
*/
- protected function display_question_bank_header() {
+ protected function display_question_bank_header(): void {
}
/**
@@ -256,7 +186,7 @@ class custom_view extends \core_question\bank\view {
* Unfortunately the best we can do is to look at the URL for
* those parameters (only marginally better really).
*/
- protected function init_sort_from_params() {
+ protected function init_sort_from_params(): void {
$this->sort = [];
for ($i = 1; $i <= self::MAX_SORTS; $i++) {
if (!$sort = $this->baseurl->param('qbs' . $i)) {
@@ -272,8 +202,8 @@ class custom_view extends \core_question\bank\view {
}
}
// Deal with subsorts.
- list($colname, $subsort) = $this->parse_subsort($sort);
- $this->requiredcolumns[$colname] = $this->get_column_type($colname);
+ list($colname) = $this->parse_subsort($sort);
+ $this->get_column_type($colname);
$this->sort[$sort] = $order;
}
}
diff --git a/mod/quiz/classes/question/bank/question_name_column.php b/mod/quiz/classes/question/bank/question_name_column.php
index 43ff13d4476..4f1462cf816 100644
--- a/mod/quiz/classes/question/bank/question_name_column.php
+++ b/mod/quiz/classes/question/bank/question_name_column.php
@@ -14,39 +14,35 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * A column type for the name of the question name.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
namespace mod_quiz\question\bank;
-defined('MOODLE_INTERNAL') || die();
-
/**
* A column type for the name of the question name.
*
+ * @package mod_quiz
+ * @category question
* @copyright 2009 Tim Hunt
+ * @author 2021 Safat Shahin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
*/
-class question_name_column extends \core_question\bank\column_base {
+class question_name_column extends \core_question\local\bank\column_base {
+
+ /**
+ * @var null $checkboxespresent
+ */
protected $checkboxespresent = null;
- public function get_name() {
+ public function get_name(): string {
return 'questionname';
}
- protected function get_title() {
+ protected function get_title(): string {
return get_string('question');
}
- protected function label_for($question) {
+ protected function label_for($question): string {
if (is_null($this->checkboxespresent)) {
- $this->checkboxespresent = $this->qbank->has_column('core_question\bank\checkbox_column');
+ $this->checkboxespresent = $this->qbank->has_column('core_question\local\bank\checkbox_column');
}
if ($this->checkboxespresent) {
return 'checkq' . $question->id;
@@ -55,19 +51,19 @@ class question_name_column extends \core_question\bank\column_base {
}
}
- protected function display_content($question, $rowclasses) {
+ protected function display_content($question, $rowclasses): void {
$labelfor = $this->label_for($question);
if ($labelfor) {
- echo '';
+ echo \html_writer::end_tag('label');
}
}
- public function get_required_fields() {
- return array('q.id', 'q.name');
+ public function get_required_fields(): array {
+ return ['q.id', 'q.name'];
}
public function is_sortable() {
diff --git a/mod/quiz/classes/question/bank/question_name_text_column.php b/mod/quiz/classes/question/bank/question_name_text_column.php
index 65e8437e62f..2439d315f49 100644
--- a/mod/quiz/classes/question/bank/question_name_text_column.php
+++ b/mod/quiz/classes/question/bank/question_name_text_column.php
@@ -14,44 +14,37 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * A column type for the name followed by the start of the question text.
- *
- * @package mod_quiz
- * @category question
- * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
namespace mod_quiz\question\bank;
-defined('MOODLE_INTERNAL') || die();
-
/**
* A column type for the name followed by the start of the question text.
*
+ * @package mod_quiz
+ * @category question
* @copyright 2009 Tim Hunt
+ * @author 2021 Safat Shahin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_name_text_column extends question_name_column {
- public function get_name() {
+
+ public function get_name(): string {
return 'questionnametext';
}
- protected function display_content($question, $rowclasses) {
- echo '
\n";
- }
-
- /**
- * Title for this column. Not used if is_sortable returns an array.
- */
- protected abstract function get_title();
-
- /**
- * @return string a fuller version of the name. Use this when get_title() returns
- * something very short, and you want a longer version as a tool tip.
- */
- protected function get_title_tip() {
- return '';
- }
-
- /**
- * Get a link that changes the sort order, and indicates the current sort state.
- * @param string $sort the column to sort on.
- * @param string $title the link text.
- * @param string $tip the link tool-tip text. If empty, defaults to title.
- * @param bool $defaultreverse whether the default sort order for this column is descending, rather than ascending.
- * @return string HTML fragment.
- */
- protected function make_sort_link($sort, $title, $tip, $defaultreverse = false) {
- $currentsort = $this->qbank->get_primary_sort_order($sort);
- $newsortreverse = $defaultreverse;
- if ($currentsort) {
- $newsortreverse = $currentsort > 0;
- }
- if (!$tip) {
- $tip = $title;
- }
- if ($newsortreverse) {
- $tip = get_string('sortbyxreverse', '', $tip);
- } else {
- $tip = get_string('sortbyx', '', $tip);
- }
- $link = '';
- $link .= $title;
- if ($currentsort) {
- $link .= $this->get_sort_icon($currentsort < 0);
- }
- $link .= '';
- return $link;
- }
-
- /**
- * Get an icon representing the corrent sort state.
- * @param bool $reverse sort is descending, not ascending.
- * @return string HTML image tag.
- */
- protected function get_sort_icon($reverse) {
- global $OUTPUT;
- if ($reverse) {
- return $OUTPUT->pix_icon('t/sort_desc', get_string('desc'), '', array('class' => 'iconsort'));
- } else {
- return $OUTPUT->pix_icon('t/sort_asc', get_string('asc'), '', array('class' => 'iconsort'));
- }
- }
-
- /**
- * Output 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.
- */
- public function display($question, $rowclasses) {
- $this->display_start($question, $rowclasses);
- $this->display_content($question, $rowclasses);
- $this->display_end($question, $rowclasses);
- }
-
- /**
- * Output the opening column tag. If it is set as heading, it will use
tag instead of
- *
- * @param \stdClass $question
- * @param string $rowclasses
- */
- protected function display_start($question, $rowclasses) {
- $tag = 'td';
- $attr = array('class' => $this->get_classes());
- if ($this->isheading) {
- $tag = 'th';
- $attr['scope'] = 'row';
- }
- echo \html_writer::start_tag($tag, $attr);
- }
-
- /**
- * @return string the CSS classes to apply to every cell in this column.
- */
- protected function get_classes() {
- $classes = $this->get_extra_classes();
- $classes[] = $this->get_name();
- return implode(' ', $classes);
- }
-
- /**
- * 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 abstract function get_name();
-
- /**
- * @return array any extra class names you would like applied to every cell in this column.
- */
- public function get_extra_classes() {
- return array();
- }
-
- /**
- * 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 abstract function display_content($question, $rowclasses);
-
- /**
- * Output the closing column tag
- *
- * @param object $question
- * @param string $rowclasses
- */
- protected function display_end($question, $rowclasses) {
- $tag = 'td';
- if ($this->isheading) {
- $tag = 'th';
- }
- echo \html_writer::end_tag($tag);
- }
-
- /**
- * Return an array 'table_alias' => 'JOIN clause' to bring in any data that
- * this column required.
- *
- * The return values for all the columns will be checked. It is OK if two
- * columns join in the same table with the same alias and identical JOIN clauses.
- * If to columns try to use the same alias with different joins, you get an error.
- * The only table included by default is the question table, which is aliased to 'q'.
- *
- * It is importnat that your join simply adds additional data (or NULLs) to the
- * existing rows of the query. It must not cause additional rows.
- *
- * @return array 'table_alias' => 'JOIN clause'
- */
- public function get_extra_joins() {
- return array();
- }
-
- /**
- * @return array fields required. use table alias 'q' for the question table, or one of the
- * ones from get_extra_joins. Every field requested must specify a table prefix.
- */
- public function get_required_fields() {
- return array();
- }
-
- /**
- * If this column needs extra data (e.g. tags) then load that here.
- *
- * The extra data should be added to the question object in the array.
- * Probably a good idea to check that another column has not already
- * loaded the data you want.
- *
- * @param \stdClass[] $questions the questions that will be displayed.
- */
- public function load_additional_data(array $questions) {
- }
-
- /**
- * Load the tags for each question.
- *
- * Helper that can be used from {@see load_additional_data()};
- *
- * @param array $questions
- */
- public function load_question_tags(array $questions) {
- $firstquestion = reset($questions);
- if (isset($firstquestion->tags)) {
- // Looks like tags are already loaded, so don't do it again.
- return;
- }
-
- // Load the tags.
- $tagdata = \core_tag_tag::get_items_tags('core_question', 'question',
- array_keys($questions));
-
- // Add them to the question objects.
- foreach ($tagdata as $questionid => $tags) {
- $questions[$questionid]->tags = $tags;
- }
- }
-
- /**
- * Can this column be sorted on? You can return either:
- * + false for no (the default),
- * + a field name, if sorting this column corresponds to sorting on that datbase field.
- * + an array of subnames to sort on as follows
- * return array(
- * 'firstname' => array('field' => 'uc.firstname', 'title' => get_string('firstname')),
- * 'lastname' => array('field' => 'uc.lastname', 'title' => get_string('lastname')),
- * );
- * As well as field, and field, you can also add 'revers' => 1 if you want the default sort
- * order to be DESC.
- * @return mixed as above.
- */
- public function is_sortable() {
- return false;
- }
-
- /**
- * Helper method for building sort clauses.
- * @param bool $reverse whether the normal direction should be reversed.
- * @return string 'ASC' or 'DESC'
- */
- protected function sortorder($reverse) {
- if ($reverse) {
- return ' DESC';
- } else {
- return ' ASC';
- }
- }
-
- /**
- * @param bool $reverse Whether to sort in the reverse of the default sort order.
- * @param string $subsort if is_sortable returns an array of subnames, then this will be
- * one of those. Otherwise will be empty.
- * @return string some SQL to go in the order by clause.
- */
- public function sort_expression($reverse, $subsort) {
- $sortable = $this->is_sortable();
- if (is_array($sortable)) {
- if (array_key_exists($subsort, $sortable)) {
- return $sortable[$subsort]['field'] . $this->sortorder($reverse);
- } else {
- throw new \coding_exception('Unexpected $subsort type: ' . $subsort);
- }
- } else if ($sortable) {
- return $sortable . $this->sortorder($reverse);
- } else {
- throw new \coding_exception('sort_expression called on a non-sortable column.');
- }
- }
-}
diff --git a/question/classes/bank/copy_action_column.php b/question/classes/bank/copy_action_column.php
deleted file mode 100644
index 769b81ac26a..00000000000
--- a/question/classes/bank/copy_action_column.php
+++ /dev/null
@@ -1,66 +0,0 @@
-.
-
-/**
- * Question bank column for the duplicate action icon.
- *
- * @package core_question
- * @copyright 2013 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Question bank column for the duplicate action icon.
- *
- * @copyright 2013 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class copy_action_column extends menu_action_column_base {
- /** @var string avoids repeated calls to get_string('duplicate'). */
- protected $strcopy;
-
- public function init() {
- parent::init();
- $this->strcopy = get_string('duplicate');
- }
-
- public function get_name() {
- return 'copyaction';
- }
-
- protected function get_url_icon_and_label(\stdClass $question): array {
- if (!\question_bank::is_qtype_installed($question->qtype)) {
- // It sometimes happens that people end up with junk questions
- // in their question bank of a type that is no longer installed.
- // We cannot do most actions on them, because that leads to errors.
- return [null, null, null];
- }
-
- // To copy a question, you need permission to add a question in the same
- // category as the existing question, and ability to access the details of
- // the question being copied.
- if (question_has_capability_on($question, 'add') &&
- (question_has_capability_on($question, 'edit') || question_has_capability_on($question, 'view'))) {
- return [$this->qbank->copy_question_moodle_url($question->id), 't/copy', $this->strcopy];
- }
- return [null, null, null];
- }
-}
diff --git a/question/classes/bank/creator_name_column.php b/question/classes/bank/creator_name_column.php
deleted file mode 100644
index f190e15cc4b..00000000000
--- a/question/classes/bank/creator_name_column.php
+++ /dev/null
@@ -1,75 +0,0 @@
-.
-
-/**
- * A column type for the name of the question creator.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A column type for the name of the question creator.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class creator_name_column extends column_base {
- public function get_name() {
- return 'creatorname';
- }
-
- protected function get_title() {
- return get_string('createdby', 'question');
- }
-
- protected function display_content($question, $rowclasses) {
- if (!empty($question->creatorfirstname) && !empty($question->creatorlastname)) {
- $u = new \stdClass();
- $u = username_load_fields_from_object($u, $question, 'creator');
- $date = userdate($question->timecreated, get_string('strftimedatetime', 'langconfig'));
- echo fullname($u) . ' ' . \html_writer::tag('span', $date, array('class' => 'date'));
- }
- }
-
- public function get_extra_joins() {
- return array('uc' => 'LEFT JOIN {user} uc ON uc.id = q.createdby');
- }
-
- public function get_required_fields() {
- $allnames = \core_user\fields::get_name_fields();
- $requiredfields = array();
- foreach ($allnames as $allname) {
- $requiredfields[] = 'uc.' . $allname . ' AS creator' . $allname;
- }
- $requiredfields[] = 'q.timecreated';
- return $requiredfields;
- }
-
- public function is_sortable() {
- return array(
- 'firstname' => array('field' => 'uc.firstname', 'title' => get_string('firstname')),
- 'lastname' => array('field' => 'uc.lastname', 'title' => get_string('lastname')),
- 'timecreated' => array('field' => 'q.timecreated', 'title' => get_string('date'))
- );
- }
-}
diff --git a/question/classes/bank/delete_action_column.php b/question/classes/bank/delete_action_column.php
deleted file mode 100644
index f969dcc84f9..00000000000
--- a/question/classes/bank/delete_action_column.php
+++ /dev/null
@@ -1,82 +0,0 @@
-.
-
-/**
- * action to delete (or hide) a question, or restore a previously hidden question.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * action to delete (or hide) a question, or restore a previously hidden question.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class delete_action_column extends menu_action_column_base {
- protected $strdelete;
- protected $strrestore;
-
- public function init() {
- parent::init();
- $this->strdelete = get_string('delete');
- $this->strrestore = get_string('restore');
- }
-
- public function get_name() {
- return 'deleteaction';
- }
-
- /**
- * Work out the info required to display this action, if appropriate.
- *
- * If the action is not appropriate to this question, return [null, null, null].
- *
- * Otherwise return an array with three elements:
- * moodel_url $url the URL to perform the action.
- * string $icon the icon name. E.g. 't/delete'.
- * string $label the label to display.
- *
- * @param object $question the row from the $question table, augmented with extra information.
- * @return array [$url, $label, $icon] as above.
- */
- protected function get_url_icon_and_label(\stdClass $question): array {
- if (!question_has_capability_on($question, 'edit')) {
- return [null, null, null];
- }
- if ($question->hidden) {
- $url = new \moodle_url($this->qbank->base_url(), array('unhide' => $question->id, 'sesskey' => sesskey()));
- return [$url, 't/restore', $this->strrestore];
- } else {
- $url = new \moodle_url($this->qbank->base_url(), array('deleteselected' => $question->id, 'q' . $question->id => 1,
- 'sesskey' => sesskey()));
- return [$url, 't/delete', $this->strdelete];
- }
- }
-
- public function get_required_fields() {
- $required = parent::get_required_fields();
- $required[] = 'q.hidden';
- return $required;
- }
-}
diff --git a/question/classes/bank/edit_action_column.php b/question/classes/bank/edit_action_column.php
deleted file mode 100644
index 582ce80ae49..00000000000
--- a/question/classes/bank/edit_action_column.php
+++ /dev/null
@@ -1,66 +0,0 @@
-.
-
-/**
- * Base class for question bank columns that just contain an action icon.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Base class for question bank columns that just contain an action icon.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class edit_action_column extends menu_action_column_base {
- protected $stredit;
- protected $strview;
-
- public function init() {
- parent::init();
- $this->stredit = get_string('editquestion', 'question');
- $this->strview = get_string('view');
- }
-
- public function get_name() {
- return 'editaction';
- }
-
- protected function get_url_icon_and_label(\stdClass $question): array {
- if (!\question_bank::is_qtype_installed($question->qtype)) {
- // It sometimes happens that people end up with junk questions
- // in their question bank of a type that is no longer installed.
- // We cannot do most actions on them, because that leads to errors.
- return [null, null, null];
- }
-
- if (question_has_capability_on($question, 'edit')) {
- return [$this->qbank->edit_question_moodle_url($question->id), 't/edit', $this->stredit];
- } else if (question_has_capability_on($question, 'view')) {
- return [$this->qbank->edit_question_moodle_url($question->id), 'i/info', $this->strview];
- } else {
- return [null, null, null];
- }
- }
-}
diff --git a/question/classes/bank/edit_menu_column.php b/question/classes/bank/edit_menu_column.php
deleted file mode 100644
index 687d0b8546e..00000000000
--- a/question/classes/bank/edit_menu_column.php
+++ /dev/null
@@ -1,102 +0,0 @@
-.
-
-/**
- * A question bank column which gathers together all the actions into a menu.
- *
- * @package core_question
- * @copyright 2019 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A question bank column which gathers together all the actions into a menu.
- *
- * This question bank column, if added to the question bank, will
- * replace all of the other columns which implement the
- * {@link menuable_action} interface and replace them with a single
- * column containing an Edit menu.
- *
- * @copyright 2019 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
- */
-class edit_menu_column extends column_base {
- /**
- * @var menuable_action[]
- */
- protected $actions;
-
- /**
- * Set up the list of actions that should be shown in the menu.
- *
- * This takes a list of column object (the list from a question
- * bank view). It extracts all the ones that should go in the menu
- * and stores them for later use. Then it returns the remaining columns.
- *
- * @param column_base[] $allcolumns a set of columns.
- * @return column_base[] the non-action columns from the set.
- */
- public function claim_menuable_columns($allcolumns) {
- $remainingcolumns = [];
- foreach ($allcolumns as $key => $column) {
- if ($column instanceof menuable_action) {
- $this->actions[$key] = $column;
- } else {
- $remainingcolumns[$key] = $column;
- }
- }
- return $remainingcolumns;
- }
-
- protected function get_title() {
- return get_string('actions');
- }
-
- public function get_name() {
- return 'editmenu';
- }
-
- protected function display_content($question, $rowclasses) {
- global $OUTPUT;
-
- $menu = new \action_menu();
- $menu->set_menu_trigger(get_string('edit'));
- $menu->set_alignment(\action_menu::TL, \action_menu::BL);
- foreach ($this->actions as $actioncolumn) {
- $action = $actioncolumn->get_action_menu_link($question);
- if ($action) {
- $menu->add($action);
- }
- }
-
- $qtypeactions = \question_bank::get_qtype($question->qtype, false)
- ->get_extra_question_bank_actions($question);
- foreach ($qtypeactions as $action) {
- $menu->add($action);
- }
-
- echo $OUTPUT->render($menu);
- }
-
- public function get_required_fields() {
- return ['q.qtype'];
- }
-}
diff --git a/question/classes/bank/export_xml_action_column.php b/question/classes/bank/export_xml_action_column.php
deleted file mode 100644
index 55cbd250483..00000000000
--- a/question/classes/bank/export_xml_action_column.php
+++ /dev/null
@@ -1,64 +0,0 @@
-.
-
-/**
- * Question bank column export the question in Moodle XML format.
- *
- * @package core_question
- * @copyright 2019 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Question bank column export the question in Moodle XML format.
- *
- * @copyright 2019 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class export_xml_action_column extends menu_action_column_base {
- /** @var string avoids repeated calls to get_string('duplicate'). */
- protected $strexportasxml;
-
- public function init() {
- parent::init();
- $this->strexportasxml = get_string('exportasxml', 'question');
- }
-
- public function get_name() {
- return 'exportasxmlaction';
- }
-
- protected function get_url_icon_and_label(\stdClass $question): array {
- if (!\question_bank::is_qtype_installed($question->qtype)) {
- // It sometimes happens that people end up with junk questions
- // in their question bank of a type that is no longer installed.
- // We cannot do most actions on them, because that leads to errors.
- return [null, null, null];
- }
-
- if (!question_has_capability_on($question, 'view')) {
- return [null, null, null];
- }
-
- return [\qbank_exporttoxml\helper::question_get_export_single_question_url($question),
- 't/download', $this->strexportasxml];
- }
-}
diff --git a/question/classes/bank/menu_action_column_base.php b/question/classes/bank/menu_action_column_base.php
deleted file mode 100644
index c730bd7dfdc..00000000000
--- a/question/classes/bank/menu_action_column_base.php
+++ /dev/null
@@ -1,71 +0,0 @@
-.
-
-/**
- * Base class to make it easier to implement actions that are menuable_actions.
- *
- * @package core_question
- * @copyright 2019 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Base class to make it easier to implement actions that are menuable_actions.
- *
- * Use this class if your action is simple (defined by just a URL, label and icon).
- * If your action is not simple enough to fit into the pattern that this
- * class implements, then you will have to implement the menuable_action
- * interface yourself.
- *
- * @copyright 2019 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
- */
-abstract class menu_action_column_base extends action_column_base implements menuable_action {
-
- /**
- * Get the information required to display this action either as a menu item or a separate action column.
- *
- * If this action cannot apply to this question (e.g. because the user does not have
- * permission, then return [null, null, null].
- *
- * @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)
- */
- abstract protected function get_url_icon_and_label(\stdClass $question): array;
-
- protected function display_content($question, $rowclasses) {
- [$url, $icon, $label] = $this->get_url_icon_and_label($question);
- if ($url) {
- $this->print_icon($icon, $label, $url);
- }
- }
-
- public function get_action_menu_link(\stdClass $question): ?\action_menu_link {
- [$url, $icon, $label] = $this->get_url_icon_and_label($question);
- if (!$url) {
- return null;
- }
- return new \action_menu_link_secondary($url, new \pix_icon($icon, ''), $label);
- }
-}
diff --git a/question/classes/bank/menuable_action.php b/question/classes/bank/menuable_action.php
deleted file mode 100644
index 2a4278cc157..00000000000
--- a/question/classes/bank/menuable_action.php
+++ /dev/null
@@ -1,57 +0,0 @@
-.
-
-/**
- * Interface to indicate that a question bank column can go in the action menu.
- *
- * @package core_question
- * @copyright 2019 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Interface to indicate that a question bank column can go in the action menu.
- *
- * If a question bank column implements this interface, and if the {@link edit_menu_column}
- * is present in the question bank view, then the 'column' will be shown as an entry in the
- * edit menu instead of as a separate column.
- *
- * Probably most columns that want to implement this will be subclasses of
- * {@link action_column_base}, and most such columns should probably implement
- * this interface.
- *
- * If your column is a simple action, you can probably save duplicated code by
- * using the base class action_column_menuable as an easy way to implement both
- * action_column_base and this interface.
- *
- * @copyright 2019 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
- */
-interface menuable_action {
-
- /**
- * Return the appropriate action menu link, or null if it does not apply to this question.
- *
- * @param \stdClass $question data about the question being displayed in this row.
- * @return \action_menu_link|null the action, if applicable to this question.
- */
- public function get_action_menu_link(\stdClass $question): ?\action_menu_link;
-}
diff --git a/question/classes/bank/modifier_name_column.php b/question/classes/bank/modifier_name_column.php
deleted file mode 100644
index 3b7621ff287..00000000000
--- a/question/classes/bank/modifier_name_column.php
+++ /dev/null
@@ -1,75 +0,0 @@
-.
-
-/**
- * A column type for the name of the question last modifier.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A column type for the name of the question last modifier.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class modifier_name_column extends column_base {
- public function get_name() {
- return 'modifiername';
- }
-
- protected function get_title() {
- return get_string('lastmodifiedby', 'question');
- }
-
- protected function display_content($question, $rowclasses) {
- if (!empty($question->modifierfirstname) && !empty($question->modifierlastname)) {
- $u = new \stdClass();
- $u = username_load_fields_from_object($u, $question, 'modifier');
- $date = userdate($question->timemodified, get_string('strftimedatetime', 'langconfig'));
- echo fullname($u) . ' ' . \html_writer::tag('span', $date, array('class' => 'date'));
- }
- }
-
- public function get_extra_joins() {
- return array('um' => 'LEFT JOIN {user} um ON um.id = q.modifiedby');
- }
-
- public function get_required_fields() {
- $allnames = \core_user\fields::get_name_fields();
- $requiredfields = array();
- foreach ($allnames as $allname) {
- $requiredfields[] = 'um.' . $allname . ' AS modifier' . $allname;
- }
- $requiredfields[] = 'q.timemodified';
- return $requiredfields;
- }
-
- public function is_sortable() {
- return array(
- 'firstname' => array('field' => 'um.firstname', 'title' => get_string('firstname')),
- 'lastname' => array('field' => 'um.lastname', 'title' => get_string('lastname')),
- 'timemodified' => array('field' => 'q.timemodified', 'title' => get_string('date'))
- );
- }
-}
diff --git a/question/classes/bank/preview_action_column.php b/question/classes/bank/preview_action_column.php
deleted file mode 100644
index 54469e0e2ed..00000000000
--- a/question/classes/bank/preview_action_column.php
+++ /dev/null
@@ -1,84 +0,0 @@
-.
-
-/**
- * Question bank columns for the preview action icon.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Question bank columns for the preview action icon.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class preview_action_column extends action_column_base implements menuable_action {
- /**
- * @var string store this lang string for performance.
- */
- protected $strpreview;
-
- public function init() {
- parent::init();
- $this->strpreview = get_string('preview');
- }
-
- public function get_name() {
- return 'previewaction';
- }
-
- protected function display_content($question, $rowclasses) {
- global $PAGE;
-
- if (!\question_bank::is_qtype_installed($question->qtype)) {
- // It sometimes happens that people end up with junk questions
- // in their question bank of a type that is no longer installed.
- // We cannot do most actions on them, because that leads to errors.
- return;
- }
-
- if (question_has_capability_on($question, 'use')) {
- echo $PAGE->get_renderer('core_question')->question_preview_link(
- $question->id, $this->qbank->get_most_specific_context(), false);
- }
- }
-
- public function get_action_menu_link(\stdClass $question): ?\action_menu_link {
- if (!\question_bank::is_qtype_installed($question->qtype)) {
- // It sometimes happens that people end up with junk questions
- // in their question bank of a type that is no longer installed.
- // We cannot do most actions on them, because that leads to errors.
- return null;
- }
-
- if (!question_has_capability_on($question, 'use')) {
- return null;
- }
-
- $context = $this->qbank->get_most_specific_context();
- $url = question_preview_url($question->id, null, null, null, null, $context);
- return new \action_menu_link_secondary($url, new \pix_icon('t/preview', ''),
- $this->strpreview, ['target' => 'questionpreview']);
- }
-}
diff --git a/question/classes/bank/question_name_column.php b/question/classes/bank/question_name_column.php
deleted file mode 100644
index 2fd578b2449..00000000000
--- a/question/classes/bank/question_name_column.php
+++ /dev/null
@@ -1,76 +0,0 @@
-.
-
-/**
- * A column type for the name of the question name.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A column type for the name of the question name.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class question_name_column extends column_base {
- protected $checkboxespresent = null;
-
- public function get_name() {
- return 'questionname';
- }
-
- protected function get_title() {
- return get_string('question');
- }
-
- protected function label_for($question) {
- if (is_null($this->checkboxespresent)) {
- $this->checkboxespresent = $this->qbank->has_column('core_question\local\bank\checkbox_column');
- }
- if ($this->checkboxespresent) {
- return 'checkq' . $question->id;
- } else {
- return '';
- }
- }
-
- protected function display_content($question, $rowclasses) {
- $labelfor = $this->label_for($question);
- if ($labelfor) {
- echo '';
- }
- }
-
- public function get_required_fields() {
- return array('q.id', 'q.name');
- }
-
- public function is_sortable() {
- return 'q.name';
- }
-}
diff --git a/question/classes/bank/question_name_idnumber_tags_column.php b/question/classes/bank/question_name_idnumber_tags_column.php
deleted file mode 100644
index d026a0dc40b..00000000000
--- a/question/classes/bank/question_name_idnumber_tags_column.php
+++ /dev/null
@@ -1,90 +0,0 @@
-.
-
-/**
- * A question bank column showing the question name with idnumber and tags.
- *
- * @package core_question
- * @copyright 2019 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A question bank column showing the question name with idnumber and tags.
- *
- * @copyright 2019 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class question_name_idnumber_tags_column extends question_name_column {
- public function get_name() {
- return 'qnameidnumbertags';
- }
-
- protected function display_content($question, $rowclasses) {
- global $OUTPUT;
-
- $layoutclasses = 'd-inline-flex flex-nowrap overflow-hidden w-100';
- $labelfor = $this->label_for($question);
- if ($labelfor) {
- echo '';
- } else {
- echo '';
- $closetag = '';
- }
-
- // Question name.
- echo \html_writer::span(format_string($question->name), 'questionname flex-grow-1 flex-shrink-1 text-truncate');
-
- // Question idnumber.
- if ($question->idnumber !== null && $question->idnumber !== '') {
- echo ' ' . \html_writer::span(
- \html_writer::span(get_string('idnumber', 'question'), 'accesshide') . ' ' .
- \html_writer::span(s($question->idnumber), 'badge badge-primary'), 'ml-1');
- }
-
- // Question tags.
- if (!empty($question->tags)) {
- $tags = \core_tag_tag::get_item_tags('core_question', 'question', $question->id);
- echo $OUTPUT->tag_list($tags, null, 'd-inline flex-shrink-1 text-truncate ml-1', 0, null, true);
- }
-
- echo $closetag; // Computed above to ensure it matches.
- }
-
- public function get_required_fields() {
- $fields = parent::get_required_fields();
- $fields[] = 'q.idnumber';
- return $fields;
- }
-
- public function is_sortable() {
- return [
- 'name' => ['field' => 'q.name', 'title' => get_string('questionname', 'question')],
- 'idnumber' => ['field' => 'q.idnumber', 'title' => get_string('idnumber', 'question')],
- ];
- }
-
- public function load_additional_data(array $questions) {
- parent::load_additional_data($questions);
- parent::load_question_tags($questions);
- }
-}
diff --git a/question/classes/bank/question_text_row.php b/question/classes/bank/question_text_row.php
deleted file mode 100644
index 626083999a0..00000000000
--- a/question/classes/bank/question_text_row.php
+++ /dev/null
@@ -1,72 +0,0 @@
-.
-
-/**
- * A column type for the name of the question name.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A column type for the name of the question name.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class question_text_row extends row_base {
- protected $formatoptions;
-
- protected function init() {
- $this->formatoptions = new \stdClass();
- $this->formatoptions->noclean = true;
- $this->formatoptions->para = false;
- }
-
- public function get_name() {
- return 'questiontext';
- }
-
- protected function get_title() {
- return get_string('questiontext', 'question');
- }
-
- protected function display_content($question, $rowclasses) {
- $text = question_rewrite_question_preview_urls($question->questiontext, $question->id,
- $question->contextid, 'question', 'questiontext', $question->id,
- $question->contextid, 'core_question');
- $text = format_text($text, $question->questiontextformat,
- $this->formatoptions);
- if ($text == '') {
- $text = ' ';
- }
- echo $text;
- }
-
- public function get_extra_joins() {
- return array('qc' => 'JOIN {question_categories} qc ON qc.id = q.category');
- }
-
- public function get_required_fields() {
- return array('q.id', 'q.questiontext', 'q.questiontextformat', 'qc.contextid');
- }
-}
diff --git a/question/classes/bank/question_type_column.php b/question/classes/bank/question_type_column.php
deleted file mode 100644
index cf2f18ecf43..00000000000
--- a/question/classes/bank/question_type_column.php
+++ /dev/null
@@ -1,60 +0,0 @@
-.
-
-/**
- * A column type for the name of the question type.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * A column type for the name of the question type.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
- */
-class question_type_column extends column_base {
- public function get_name() {
- return 'qtype';
- }
-
- protected function get_title() {
- return get_string('qtypeveryshort', 'question');
- }
-
- protected function get_title_tip() {
- return get_string('questiontype', 'question');
- }
-
- protected function display_content($question, $rowclasses) {
- echo print_question_icon($question);
- }
-
- public function get_required_fields() {
- return array('q.qtype');
- }
-
- public function is_sortable() {
- return 'q.qtype';
- }
-}
diff --git a/question/classes/bank/random_question_loader.php b/question/classes/bank/random_question_loader.php
deleted file mode 100644
index 9dac3ac9128..00000000000
--- a/question/classes/bank/random_question_loader.php
+++ /dev/null
@@ -1,323 +0,0 @@
-.
-
-/**
- * A class for efficiently finds questions at random from the question bank.
- *
- * @package core_question
- * @copyright 2015 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-
-
-/**
- * This class efficiently finds questions at random from the question bank.
- *
- * You can ask for questions at random one at a time. Each time you ask, you
- * pass a category id, and whether to pick from that category and all subcategories
- * or just that category.
- *
- * The number of teams each question has been used is tracked, and we will always
- * return a question from among those elegible that has been used the fewest times.
- * So, if there are questions that have not been used yet in the category asked for,
- * one of those will be returned. However, within one instantiation of this class,
- * we will never return a given question more than once, and we will never return
- * questions passed into the constructor as $usedquestions.
- *
- * @copyright 2015 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
- */
-class random_question_loader {
- /** @var \qubaid_condition which usages to consider previous attempts from. */
- protected $qubaids;
-
- /** @var array qtypes that cannot be used by random questions. */
- protected $excludedqtypes;
-
- /** @var array categoryid & include subcategories => num previous uses => questionid => 1. */
- protected $availablequestionscache = array();
-
- /**
- * @var array questionid => num recent uses. Questions that have been used,
- * but that is not yet recorded in the DB.
- */
- protected $recentlyusedquestions;
-
- /**
- * Constructor.
- * @param \qubaid_condition $qubaids the usages to consider when counting previous uses of each question.
- * @param array $usedquestions questionid => number of times used count. If we should allow for
- * further existing uses of a question in addition to the ones in $qubaids.
- */
- public function __construct(\qubaid_condition $qubaids, array $usedquestions = array()) {
- $this->qubaids = $qubaids;
- $this->recentlyusedquestions = $usedquestions;
-
- foreach (\question_bank::get_all_qtypes() as $qtype) {
- if (!$qtype->is_usable_by_random()) {
- $this->excludedqtypes[] = $qtype->name();
- }
- }
- }
-
- /**
- * Pick a question at random from the given category, from among those with the fewest uses.
- * If an array of tag ids are specified, then only the questions that are tagged with ALL those tags will be selected.
- *
- * It is up the the caller to verify that the cateogry exists. An unknown category
- * behaves like an empty one.
- *
- * @param int $categoryid the id of a category in the question bank.
- * @param bool $includesubcategories wether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param array $tagids An array of tag ids. A question has to be tagged with all the provided tagids (if any)
- * in order to be eligible for being picked.
- * @return int|null the id of the question picked, or null if there aren't any.
- */
- public function get_next_question_id($categoryid, $includesubcategories, $tagids = []) {
- $this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
-
- $categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
- if (empty($this->availablequestionscache[$categorykey])) {
- return null;
- }
-
- reset($this->availablequestionscache[$categorykey]);
- $lowestcount = key($this->availablequestionscache[$categorykey]);
- reset($this->availablequestionscache[$categorykey][$lowestcount]);
- $questionid = key($this->availablequestionscache[$categorykey][$lowestcount]);
- $this->use_question($questionid);
- return $questionid;
- }
-
- /**
- * Get the key into {@link $availablequestionscache} for this combination of options.
- * @param int $categoryid the id of a category in the question bank.
- * @param bool $includesubcategories wether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param array $tagids an array of tag ids.
- * @return string the cache key.
- */
- protected function get_category_key($categoryid, $includesubcategories, $tagids = []) {
- if ($includesubcategories) {
- $key = $categoryid . '|1';
- } else {
- $key = $categoryid . '|0';
- }
-
- if (!empty($tagids)) {
- $key .= '|' . implode('|', $tagids);
- }
-
- return $key;
- }
-
- /**
- * Populate {@link $availablequestionscache} for this combination of options.
- * @param int $categoryid The id of a category in the question bank.
- * @param bool $includesubcategories Whether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param array $tagids An array of tag ids. If an array is provided, then
- * only the questions that are tagged with ALL the provided tagids will be loaded.
- */
- protected function ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids = []) {
- global $DB;
-
- $categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
-
- if (isset($this->availablequestionscache[$categorykey])) {
- // Data is already in the cache, nothing to do.
- return;
- }
-
- // Load the available questions from the question bank.
- if ($includesubcategories) {
- $categoryids = question_categorylist($categoryid);
- } else {
- $categoryids = array($categoryid);
- }
-
- list($extraconditions, $extraparams) = $DB->get_in_or_equal($this->excludedqtypes,
- SQL_PARAMS_NAMED, 'excludedqtype', false);
-
- $questionidsandcounts = \question_bank::get_finder()->get_questions_from_categories_and_tags_with_usage_counts(
- $categoryids, $this->qubaids, 'q.qtype ' . $extraconditions, $extraparams, $tagids);
- if (!$questionidsandcounts) {
- // No questions in this category.
- $this->availablequestionscache[$categorykey] = array();
- return;
- }
-
- // Put all the questions with each value of $prevusecount in separate arrays.
- $idsbyusecount = array();
- foreach ($questionidsandcounts as $questionid => $prevusecount) {
- if (isset($this->recentlyusedquestions[$questionid])) {
- // Recently used questions are never returned.
- continue;
- }
- $idsbyusecount[$prevusecount][] = $questionid;
- }
-
- // Now put that data into our cache. For each count, we need to shuffle
- // questionids, and make those the keys of an array.
- $this->availablequestionscache[$categorykey] = array();
- foreach ($idsbyusecount as $prevusecount => $questionids) {
- shuffle($questionids);
- $this->availablequestionscache[$categorykey][$prevusecount] = array_combine(
- $questionids, array_fill(0, count($questionids), 1));
- }
- ksort($this->availablequestionscache[$categorykey]);
- }
-
- /**
- * Update the internal data structures to indicate that a given question has
- * been used one more time.
- *
- * @param int $questionid the question that is being used.
- */
- protected function use_question($questionid) {
- if (isset($this->recentlyusedquestions[$questionid])) {
- $this->recentlyusedquestions[$questionid] += 1;
- } else {
- $this->recentlyusedquestions[$questionid] = 1;
- }
-
- foreach ($this->availablequestionscache as $categorykey => $questionsforcategory) {
- foreach ($questionsforcategory as $numuses => $questionids) {
- if (!isset($questionids[$questionid])) {
- continue;
- }
- unset($this->availablequestionscache[$categorykey][$numuses][$questionid]);
- if (empty($this->availablequestionscache[$categorykey][$numuses])) {
- unset($this->availablequestionscache[$categorykey][$numuses]);
- }
- }
- }
- }
-
- /**
- * Get the list of available question ids for the given criteria.
- *
- * @param int $categoryid The id of a category in the question bank.
- * @param bool $includesubcategories Whether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param array $tagids An array of tag ids. If an array is provided, then
- * only the questions that are tagged with ALL the provided tagids will be loaded.
- * @return int[] The list of question ids
- */
- protected function get_question_ids($categoryid, $includesubcategories, $tagids = []) {
- $this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
- $categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
- $cachedvalues = $this->availablequestionscache[$categorykey];
- $questionids = [];
-
- foreach ($cachedvalues as $usecount => $ids) {
- $questionids = array_merge($questionids, array_keys($ids));
- }
-
- return $questionids;
- }
-
- /**
- * Check whether a given question is available in a given category. If so, mark it used.
- * If an optional list of tag ids are provided, then the question must be tagged with
- * ALL of the provided tags to be considered as available.
- *
- * @param int $categoryid the id of a category in the question bank.
- * @param bool $includesubcategories wether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param int $questionid the question that is being used.
- * @param array $tagids An array of tag ids. Only the questions that are tagged with all the provided tagids can be available.
- * @return bool whether the question is available in the requested category.
- */
- public function is_question_available($categoryid, $includesubcategories, $questionid, $tagids = []) {
- $this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
- $categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
-
- foreach ($this->availablequestionscache[$categorykey] as $questionids) {
- if (isset($questionids[$questionid])) {
- $this->use_question($questionid);
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Get the list of available questions for the given criteria.
- *
- * @param int $categoryid The id of a category in the question bank.
- * @param bool $includesubcategories Whether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param array $tagids An array of tag ids. If an array is provided, then
- * only the questions that are tagged with ALL the provided tagids will be loaded.
- * @param int $limit Maximum number of results to return.
- * @param int $offset Number of items to skip from the begging of the result set.
- * @param string[] $fields The fields to return for each question.
- * @return \stdClass[] The list of question records
- */
- public function get_questions(
- $categoryid,
- $includesubcategories,
- $tagids = [],
- $limit = 100,
- $offset = 0,
- $fields = []
- ) {
- global $DB;
-
- $questionids = $this->get_question_ids($categoryid, $includesubcategories, $tagids);
- if (empty($questionids)) {
- return [];
- }
-
- if (empty($fields)) {
- // Return all fields.
- $fieldsstring = '*';
- } else {
- $fieldsstring = implode(',', $fields);
- }
-
- return $DB->get_records_list(
- 'question',
- 'id',
- $questionids,
- 'id',
- $fieldsstring,
- $offset,
- $limit
- );
- }
-
- /**
- * Count the number of available questions for the given criteria.
- *
- * @param int $categoryid The id of a category in the question bank.
- * @param bool $includesubcategories Whether to pick a question from exactly
- * that category, or that category and subcategories.
- * @param array $tagids An array of tag ids. If an array is provided, then
- * only the questions that are tagged with ALL the provided tagids will be loaded.
- * @return int The number of questions matching the criteria.
- */
- public function count_questions($categoryid, $includesubcategories, $tagids = []) {
- $questionids = $this->get_question_ids($categoryid, $includesubcategories, $tagids);
- return count($questionids);
- }
-}
diff --git a/question/classes/bank/row_base.php b/question/classes/bank/row_base.php
deleted file mode 100644
index 7f9363ca40b..00000000000
--- a/question/classes/bank/row_base.php
+++ /dev/null
@@ -1,53 +0,0 @@
-.
-
-/**
- * Base class for 'columns' that are actually displayed as a row following the main question row.
- *
- * @package core_question
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_question\bank;
-defined('MOODLE_INTERNAL') || die();
-
-
-/**
- * Base class for 'columns' that are actually displayed as a row following the main question row.
- *
- * @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
- */
-abstract class row_base extends column_base {
- public function is_extra_row() {
- return true;
- }
-
- protected function display_start($question, $rowclasses) {
- if ($rowclasses) {
- echo '
\n";
- }
-
- /**
- * Prints a form to choose categories
- * @deprecated since Moodle 2.7 MDL-40313.
- * @see \core_question\bank\search\condition
- * @todo MDL-41978 This will be deleted in Moodle 2.8
- */
- protected function display_category_form($contexts, $pageurl, $current) {
- global $OUTPUT;
-
- debugging('display_category_form() is deprecated, please use ' .
- '\core_question\bank\search\condition instead.', DEBUG_DEVELOPER);
- // Get all the existing categories now.
- echo '
\n";
- }
-
- /**
- * Display the options form.
- * @param bool $recurse no longer used.
- * @param bool $showhidden no longer used.
- * @param bool $showquestiontext whether to show the question text.
- * @deprecated since Moodle 2.7 MDL-40313.
- * @see display_options_form
- * @todo MDL-41978 This will be deleted in Moodle 2.8
- * @see \core_question\bank\search\condition
- */
- protected function display_options($recurse, $showhidden, $showquestiontext) {
- debugging('display_options() is deprecated, please use display_options_form instead.', DEBUG_DEVELOPER);
- $this->display_options_form($showquestiontext);
- }
-
- /**
- * Print a single option checkbox.
- * @deprecated since Moodle 2.7 MDL-40313.
- * @see \core_question\bank\search\condition
- * @see html_writer::checkbox
- * @todo MDL-41978 This will be deleted in Moodle 2.8
- */
- protected function display_category_form_checkbox($name, $value, $label) {
- debugging('display_category_form_checkbox() is deprecated, ' .
- 'please use \core_question\bank\search\condition instead.', DEBUG_DEVELOPER);
- echo '
';
- echo '';
- echo '';
- echo "
\n";
- }
-
- /**
- * Display the form with options for which questions are displayed and how they are displayed.
- * @param bool $showquestiontext Display the text of the question within the list.
- * @param string $scriptpath path to the script displaying this page.
- * @param bool $showtextoption whether to include the 'Show question text' checkbox.
- */
- protected function display_options_form($showquestiontext, $scriptpath = '/question/edit.php',
- $showtextoption = true) {
- global $PAGE;
-
- echo \html_writer::start_tag('form', array('method' => 'get',
- 'action' => new \moodle_url($scriptpath), 'id' => 'displayoptions'));
- echo \html_writer::start_div();
-
- $excludes = array('recurse', 'showhidden', 'qbshowtext');
- // If the URL contains any tags then we need to prevent them
- // being added to the form as hidden elements because the tags
- // are managed separately.
- if ($this->baseurl->param('qtagids[0]')) {
- $index = 0;
- while ($this->baseurl->param("qtagids[{$index}]")) {
- $excludes[] = "qtagids[{$index}]";
- $index++;
- }
- }
- echo \html_writer::input_hidden_params($this->baseurl, $excludes);
-
- foreach ($this->searchconditions as $searchcondition) {
- echo $searchcondition->display_options();
- }
- if ($showtextoption) {
- $this->display_showtext_checkbox($showquestiontext);
- }
- $this->display_advanced_search_form();
- $go = \html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go')));
- echo \html_writer::tag('noscript', \html_writer::div($go), array('class' => 'inline'));
- echo \html_writer::end_div();
- echo \html_writer::end_tag('form');
- $PAGE->requires->yui_module('moodle-question-searchform', 'M.question.searchform.init');
- }
-
- /**
- * Print the "advanced" UI elements for the form to select which questions. Hidden by default.
- */
- protected function display_advanced_search_form() {
- print_collapsible_region_start('', 'advancedsearch', get_string('advancedsearchoptions', 'question'),
- 'question_bank_advanced_search');
- foreach ($this->searchconditions as $searchcondition) {
- echo $searchcondition->display_options_adv();
- }
- print_collapsible_region_end();
- }
-
- /**
- * Display the checkbox UI for toggling the display of the question text in the list.
- * @param bool $showquestiontext the current or default value for whether to display the text.
- */
- protected function display_showtext_checkbox($showquestiontext) {
- echo '
';
- }
-
- /**
- * Prints the table of questions in a category with interactions
- *
- * @param array $contexts Not used!
- * @param \moodle_url $pageurl The URL to reload this page.
- * @param string $categoryandcontext 'categoryID,contextID'.
- * @param \stdClass $cm Not used!
- * @param int $recurse Whether to include subcategories.
- * @param int $page The number of the page to be displayed
- * @param int $perpage Number of questions to show per page
- * @param bool $showhidden Not used! This is now controlled in a different way.
- * @param bool $showquestiontext Not used! This is now controlled in a different way.
- * @param array $addcontexts contexts where the user is allowed to add new questions.
- */
- protected function display_question_list($contexts, $pageurl, $categoryandcontext,
- $cm = null, $recurse=1, $page=0, $perpage=100, $showhidden=false,
- $showquestiontext = false, $addcontexts = array()) {
- global $OUTPUT;
-
- // This function can be moderately slow with large question counts and may time out.
- // We probably do not want to raise it to unlimited, so randomly picking 5 minutes.
- // Note: We do not call this in the loop because quiz ob_ captures this function (see raise() PHP doc).
- \core_php_time_limit::raise(300);
-
- $category = $this->get_current_category($categoryandcontext);
-
- list($categoryid, $contextid) = explode(',', $categoryandcontext);
- $catcontext = \context::instance_by_id($contextid);
-
- $canadd = has_capability('moodle/question:add', $catcontext);
-
- $this->create_new_question_form($category, $canadd);
-
- $this->build_query();
- $totalnumber = $this->get_question_count();
- if ($totalnumber == 0) {
- return;
- }
- $questionsrs = $this->load_page_questions($page, $perpage);
- $questions = [];
- foreach ($questionsrs as $question) {
- $questions[$question->id] = $question;
- }
- $questionsrs->close();
- foreach ($this->requiredcolumns as $name => $column) {
- $column->load_additional_data($questions);
- }
-
- echo '
';
-
- echo '\n";
- }
-
- /**
- * Display the controls at the bottom of the list of questions.
- * @param int $totalnumber Total number of questions that might be shown (if it was not for paging).
- * @param bool $recurse Whether to include subcategories.
- * @param \stdClass $category The question_category row from the database.
- * @param \context $catcontext The context of the category being displayed.
- * @param array $addcontexts contexts where the user is allowed to add new questions.
- */
- protected function display_bottom_controls($totalnumber, $recurse, $category, \context $catcontext, array $addcontexts) {
- $caneditall = has_capability('moodle/question:editall', $catcontext);
- $canuseall = has_capability('moodle/question:useall', $catcontext);
- $canmoveall = has_capability('moodle/question:moveall', $catcontext);
-
- echo '