From: tjhunt Date: Tue, 3 Mar 2009 07:47:32 +0000 (+0000) Subject: qtype admin: MDL-18425 also related to MDL-18355. X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=af52eceef40c7bf667d96da7a11b05ea20d52839;p=moodle.git 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. --- diff --git a/admin/qtypes.php b/admin/qtypes.php index de4a3a7e9f..cadab6eab5 100644 --- a/admin/qtypes.php +++ b/admin/qtypes.php @@ -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 = ''; } + // 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')); + } +} + +function icon_html($action, $qtypename, $icon, $alt, $tip) { + global $CFG; + if ($tip) { + $tip = 'title="' . $tip . '" '; } - $html = '
'; + $html = '
'; $html .= ''; $html .= ''; + '" src="' . $CFG->pixpath . '/' . $icon . '" alt="' . $alt . '" ' . $tip . '/>'; $html .= '
'; return $html; } diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 98bcd51973..c661d18f2b 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -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; } diff --git a/lib/questionlib.php b/lib/questionlib.php index fbfbd629fd..138dbfc33b 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -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 ///////////////////////////////////////////////////////// /** diff --git a/lib/simpletest/testquestionlib.php b/lib/simpletest/testquestionlib.php index 65f7e9540b..b3dff8ce8f 100644 --- a/lib/simpletest/testquestionlib.php +++ b/lib/simpletest/testquestionlib.php @@ -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)); - } } diff --git a/theme/standard/styles_layout.css b/theme/standard/styles_layout.css index 08c0cb6f46..6bdcf86f2e 100644 --- a/theme/standard/styles_layout.css +++ b/theme/standard/styles_layout.css @@ -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, diff --git a/version.php b/version.php index a8f59f2a76..e9d3e64479 100644 --- a/version.php +++ b/version.php @@ -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