qtype admin: MDL-18425 also related to MDL-18355.

Allow question types  to be displayed in order that is better than random or alphabetical. Since we don't know all the qtypes there may be:
1. Store the order in the DB (config plugins).
2. Set up a good default order for the standard types. (Unknown types go at the end by default.)
3. Allow admins to edit the order on the qtype admin screen.
This commit is contained in:
tjhunt
2009-03-03 07:47:32 +00:00
parent a5bc73aa74
commit af52eceef4
6 changed files with 198 additions and 28 deletions
+55 -17
View File
@@ -14,7 +14,7 @@
admin_externalpage_setup('manageqtypes');
/// Get some data we will need.
/// Get some data we will need - question counts and which types are needed.
$counts = $DB->get_records_sql("
SELECT qtype, COUNT(1) as numquestions, SUM(hidden) as numhidden
FROM {question} GROUP BY qtype", array());
@@ -41,7 +41,15 @@
}
}
// Process actions =========================================================
/// Work of the correct sort order.
$config = get_config('question');
$sortedqtypes = array();
foreach ($QTYPES as $qtypename => $qtype) {
$sortedqtypes[$qtypename] = $qtype->local_name();
}
$sortedqtypes = question_sort_qtype_array($sortedqtypes, $config);
/// Process actions ============================================================
// Disable.
if (($disable = optional_param('disable', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
@@ -67,6 +75,28 @@
redirect(admin_url('qtypes.php'));
}
// Move up in order.
if (($up = optional_param('up', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
if (!isset($QTYPES[$up])) {
print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $up);
}
$neworder = question_reorder_qtypes($sortedqtypes, $up, -1);
question_save_qtype_order($neworder, $config);
redirect(admin_url('qtypes.php'));
}
// Move down in order.
if (($down = optional_param('down', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
if (!isset($QTYPES[$down])) {
print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $down);
}
$neworder = question_reorder_qtypes($sortedqtypes, $down, +1);
question_save_qtype_order($neworder, $config);
redirect(admin_url('qtypes.php'));
}
// Delete.
if ($delete = optional_param('delete', '', PARAM_SAFEDIR) && confirm_sesskey()) {
// Check it is OK to delete this question type.
@@ -107,6 +137,8 @@
if (!unset_all_config_for_plugin('qtype_' . $delete)) {
notify(get_string('errordeletingconfig', 'admin', 'qtype_' . $delete));
}
unset_config($delete . '_disabled', 'question');
unset_config($delete . '_sortorder', 'question');
// Then the tables themselves
drop_plugin_tables($delete, $QTYPES[$delete]->plugin_dir() . '/db/install.xml', false);
@@ -141,14 +173,15 @@
/// Add a row for each question type.
$createabletypes = question_type_menu();
foreach ($QTYPES as $qtypename => $qtype) {
foreach ($sortedqtypes as $qtypename => $localname) {
$qtype = $QTYPES[$qtypename];
$row = array();
// Question icon and name.
$fakequestion = new stdClass;
$fakequestion->qtype = $qtypename;
$icon = print_question_icon($fakequestion, true);
$row[] = $icon . ' ' . $qtype->local_name();
$row[] = $icon . ' ' . $localname;
// Number of questions of this type.
if ($counts[$qtypename]->numquestions + $counts[$qtypename]->numhidden > 0) {
@@ -191,14 +224,19 @@
$rowclass = '';
if ($qtype->menu_name()) {
$createable = isset($createabletypes[$qtypename]);
$row[] = enable_disable_button($qtypename, $createable);
$icons = enable_disable_button($qtypename, $createable);
if (!$createable) {
$rowclass = 'dimmed_text';
}
} else {
$row[] = '';
$icons = '<img src="' . $CFG->pixpath . '/spacer.gif" alt="" class="spacer" />';
}
// Move icons.
$icons .= icon_html('up', $qtypename, 't/up.gif', get_string('up'), '');
$icons .= icon_html('down', $qtypename, 't/down.gif', get_string('down'), '');
$row[] = $icons;
// Delete link, if available.
if ($needed[$qtypename]) {
$row[] = '';
@@ -229,22 +267,22 @@ function admin_url($endbit) {
}
function enable_disable_button($qtypename, $createable) {
global $CFG;
if ($createable) {
$action = 'disable';
$tip = get_string('disable');
$alt = get_string('enabled', 'question');
$icon = 'hide';
return icon_html('disable', $qtypename, 'i/hide.gif', get_string('enabled', 'question'), get_string('disable'));
} else {
$action = 'enable';
$tip = get_string('enable');
$alt = get_string('disabled', 'question');
$icon = 'show';
return icon_html('enable', $qtypename, 'i/show.gif', get_string('disabled', 'question'), get_string('enable'));
}
$html = '<form action="' . admin_url('qtypes.php') . '" method="post"><div>';
}
function icon_html($action, $qtypename, $icon, $alt, $tip) {
global $CFG;
if ($tip) {
$tip = 'title="' . $tip . '" ';
}
$html = ' <form action="' . admin_url('qtypes.php') . '" method="post"><div>';
$html .= '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
$html .= '<input type="image" name="' . $action . '" value="' . $qtypename .
'" src="' . $CFG->pixpath . '/i/' . $icon . '.gif" alt="' . $alt . '" title="' . $tip . '" />';
'" src="' . $CFG->pixpath . '/' . $icon . '" alt="' . $alt . '" ' . $tip . '/>';
$html .= '</div></form>';
return $html;
}
+19
View File
@@ -1472,6 +1472,25 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
/// Main savepoint reached
upgrade_main_savepoint($result, 2009021801);
}
/// Add default sort order for question types.
if ($result && $oldversion < 2009030300) {
set_config('multichoice_sortorder', 1, 'question');
set_config('truefalse_sortorder', 2, 'question');
set_config('shortanswer_sortorder', 3, 'question');
set_config('numerical_sortorder', 4, 'question');
set_config('calculated_sortorder', 5, 'question');
set_config('essay_sortorder', 6, 'question');
set_config('match_sortorder', 7, 'question');
set_config('randomsamatch_sortorder', 8, 'question');
set_config('multianswer_sortorder', 9, 'question');
set_config('description_sortorder', 10, 'question');
set_config('random_sortorder', 11, 'question');
set_config('missingtype_sortorder', 12, 'question');
upgrade_main_savepoint($result, 2009030300);
}
return $result;
}
+87 -4
View File
@@ -169,20 +169,103 @@ function question_type_menu() {
global $QTYPES;
static $menuoptions = null;
if (is_null($menuoptions)) {
$disbled = get_config('question');
$config = get_config('question');
$menuoptions = array();
foreach ($QTYPES as $name => $qtype) {
// Get the name if this qtype is enabled.
$menuname = $qtype->menu_name();
$configname = $name . '_disabled';
if ($menuname && !isset($disbled->$configname)) {
$enabledvar = $name . '_disabled';
if ($menuname && !isset($config->$enabledvar)) {
$menuoptions[$name] = $menuname;
}
}
asort($menuoptions, SORT_LOCALE_STRING);
$menuoptions = question_sort_qtype_array($menuoptions, $config);
}
return $menuoptions;
}
/**
* Sort an array of question type names according to the question type sort order stored in
* config_plugins. Entries for which there is no xxx_sortorder defined will go
* at the end, sorted according to asort($inarray, SORT_LOCALE_STRING).
* @param $inarray an array $qtype => $QTYPES[$qtype]->local_name().
* @param $config get_config('question'), if you happen to have it around, to save one DB query.
* @return array the sorted version of $inarray.
*/
function question_sort_qtype_array($inarray, $config = null) {
if (is_null($config)) {
$config = get_config('question');
}
$sortorder = array();
foreach ($inarray as $name => $notused) {
$sortvar = $name . '_sortorder';
if (isset($config->$sortvar)) {
$sortorder[$config->$sortvar] = $name;
}
}
ksort($sortorder);
$outarray = array();
foreach ($sortorder as $name) {
$outarray[$name] = $inarray[$name];
unset($inarray[$name]);
}
asort($inarray, SORT_LOCALE_STRING);
return array_merge($outarray, $inarray);
}
/**
* Move one question type in a list of question types. If you try to move one element
* off of the end, nothing will change.
*
* @param array $sortedqtypes An array $qtype => anything.
* @param string $tomove one of the keys from $sortedqtypes
* @param integer $direction +1 or -1
* @return array an array $index => $qtype, with $index from 0 to n in order, and
* the $qtypes in the same order as $sortedqtypes, except that $tomove will
* have been moved one place.
*/
function question_reorder_qtypes($sortedqtypes, $tomove, $direction) {
$neworder = array_keys($sortedqtypes);
// Find the element to move.
$key = array_search($tomove, $neworder);
if ($key === false) {
return $neworder;
}
// Work out the other index.
$otherkey = $key + $direction;
if (!isset($neworder[$otherkey])) {
return $neworder;
}
// Do the swap.
$swap = $neworder[$otherkey];
$neworder[$otherkey] = $neworder[$key];
$neworder[$key] = $swap;
return $neworder;
}
/**
* Save a new question type order to the config_plugins table.
* @param $neworder An arra $index => $qtype. Indices should start at 0 and be in order.
* @param $config get_config('question'), if you happen to have it around, to save one DB query.
*/
function question_save_qtype_order($neworder, $config = null) {
global $DB;
if (is_null($config)) {
$config = get_config('question');
}
foreach ($neworder as $index => $qtype) {
$sortvar = $qtype . '_sortorder';
if (!isset($config->$sortvar) || $config->$sortvar != $index + 1) {
set_config($sortvar, $index + 1, 'question');
}
}
}
/// OTHER CLASSES /////////////////////////////////////////////////////////
/**
+26 -6
View File
@@ -37,13 +37,34 @@ if (!defined('MOODLE_INTERNAL')) {
require_once($CFG->libdir . '/questionlib.php');
class questionlib_test extends FakeDBUnitTestCase {
function setUp() {
class questionlib_test extends UnitTestCase {
function test_question_sort_qtype_array() {
$config = new stdClass();
$config->multichoice_sortorder = '1';
$config->calculated_sortorder = '2';
$qtypes = array(
'frog' => 'toad',
'calculated' => 'newt',
'multichoice' => 'eft',
);
$this->assertEqual(question_sort_qtype_array($qtypes), array(
'multichoice' => 'eft',
'calculated' => 'newt',
'frog' => 'toad',
));
}
function tearDown() {
function test_question_reorder_qtypes() {
$this->assertEqual(question_reorder_qtypes(array('t1' => '', 't2' => '', 't3' => ''), 't1', +1),
array(0 => 't2', 1 => 't1', 2 => 't3'));
$this->assertEqual(question_reorder_qtypes(array('t1' => '', 't2' => '', 't3' => ''), 't1', -1),
array(0 => 't1', 1 => 't2', 2 => 't3'));
$this->assertEqual(question_reorder_qtypes(array('t1' => '', 't2' => '', 't3' => ''), 't2', -1),
array(0 => 't2', 1 => 't1', 2 => 't3'));
$this->assertEqual(question_reorder_qtypes(array('t1' => '', 't2' => '', 't3' => ''), 't3', +1),
array(0 => 't1', 1 => 't2', 2 => 't3'));
$this->assertEqual(question_reorder_qtypes(array('t1' => '', 't2' => '', 't3' => ''), 'missing', +1),
array(0 => 't1', 1 => 't2', 2 => 't3'));
}
function test_question_state_is_closed() {
@@ -110,7 +131,6 @@ class questionlib_test extends FakeDBUnitTestCase {
$state->event = QUESTION_EVENTGRADE;
$this->assertTrue(question_state_is_graded($state));
}
}
+10
View File
@@ -1073,6 +1073,9 @@ body#admin-modules table.generaltable td.c0
#admin-report-questioninstances-index #settingsform p {
margin-bottom: 0;
}
#admin-qtypes th {
white-space: normal;
}
#admin-qtypes .cell.c3 {
font-size: 0.7em;
}
@@ -1082,6 +1085,13 @@ body#admin-modules table.generaltable td.c0
#admin-qtypes .cell.c0 {
text-align: left;
}
#admin-qtypes #qtypes div,
#admin-qtypes #qtypes form {
display: inline;
}
#admin-qtypes #qtypes img.spacer {
width: 16px;
}
#admin-roles-allowassign .buttons,
#admin-roles-allowoverride .buttons,
#admin-roles-manage .buttons,
+1 -1
View File
@@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
$version = 2009021801; // YYYYMMDD = date of the last version bump
$version = 2009030300; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0 dev (Build: 20090303)'; // Human-friendly version name