]> git.mjollnir.org Git - moodle.git/commitdiff
qtype admin: MDL-18425 also related to MDL-18355.
authortjhunt <tjhunt>
Tue, 3 Mar 2009 07:47:32 +0000 (07:47 +0000)
committertjhunt <tjhunt>
Tue, 3 Mar 2009 07:47:32 +0000 (07:47 +0000)
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.

admin/qtypes.php
lib/db/upgrade.php
lib/questionlib.php
lib/simpletest/testquestionlib.php
theme/standard/styles_layout.css
version.php

index de4a3a7e9f50eb127685f2e9fd6b4040894a4bad..cadab6eab5084aa6e9a9d5ecdf392b14fc663721 100644 (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());
         }
     }
 
-    // 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()) {
         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.
         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);
 
 /// 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) {
         $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'));
+    }
+}
+
+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 = ' <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;
 }
index 98bcd519734e4a574cacd8f682fd4b2138f9afb5..c661d18f2b7a4a2c77bc0bab6832ce7c69871168 100644 (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;
 }
 
index fbfbd629fdb20c3d3884af1dcb3eda45c8709328..138dbfc33baf0f65b3b4e0131df9418c0674b6e3 100644 (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 /////////////////////////////////////////////////////////
 
 /**
index 65f7e9540b163522d014a866cb06bba0182f82b1..b3dff8ce8f383f7bd05c1a8610d9f2a11836f113 100644 (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));
-
     }
 }
 
index 08c0cb6f4667d1a7846d17c227ed2764078ce379..6bdcf86f2e09553e62850fe9a065afbaecddb567 100644 (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,
index a8f59f2a762b23509457aed6fb1329f2d3ef4f05..e9d3e6447999820acd2ac6c78c0ec0b8c0e23d36 100644 (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