MDL-52879 quiz editing: allow removal of the last question in a quiz
authorTim Hunt <T.J.Hunt@open.ac.uk>
Sat, 6 Feb 2016 10:13:28 +0000 (10:13 +0000)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Tue, 9 Feb 2016 21:45:13 +0000 (21:45 +0000)
Even though you are not normally allowed to remove the last question
in a section.

mod/quiz/classes/output/edit_renderer.php
mod/quiz/classes/structure.php
mod/quiz/styles.css
mod/quiz/tests/behat/editing_remove_question.feature
mod/quiz/tests/structure_test.php
mod/quiz/upgrade.txt

index c1e175e..00afad9 100644 (file)
@@ -64,7 +64,7 @@ class edit_renderer extends \plugin_renderer_base {
         $output .= $this->total_marks($quizobj->get_quiz());
 
         // Show the questions organised into sections and pages.
-        $output .= $this->start_section_list();
+        $output .= $this->start_section_list($structure);
 
         foreach ($structure->get_sections() as $section) {
             $output .= $this->start_section($structure, $section);
@@ -254,10 +254,15 @@ class edit_renderer extends \plugin_renderer_base {
 
     /**
      * Generate the starting container html for the start of a list of sections
+     * @param structure $structure the structure of the quiz being edited.
      * @return string HTML to output.
      */
-    protected function start_section_list() {
-        return html_writer::start_tag('ul', array('class' => 'slots'));
+    protected function start_section_list(structure $structure) {
+        $class = 'slots';
+        if ($structure->get_section_count() == 1) {
+            $class .= ' only-one-section';
+        }
+        return html_writer::start_tag('ul', array('class' => $class));
     }
 
     /**
index 36c3499..a9c4fe2 100644 (file)
@@ -470,6 +470,14 @@ class structure {
         return $this->sections[$sectionid];
     }
 
+    /**
+     * Get the number of questions in the quiz.
+     * @return int the number of questions in the quiz.
+     */
+    public function get_section_count() {
+        return count($this->sections);
+    }
+
     /**
      * Get the overall quiz grade formatted for display.
      * @return string the maximum grade for this quiz.
@@ -876,7 +884,7 @@ class structure {
 
         $this->check_can_be_edited();
 
-        if ($this->is_only_slot_in_section($slotnumber)) {
+        if ($this->is_only_slot_in_section($slotnumber) && $this->get_section_count() > 1) {
             throw new \coding_exception('You cannot remove the last slot in a section.');
         }
 
index f98ea29..a017909 100644 (file)
@@ -773,6 +773,9 @@ table.quizreviewsummary td.cell {
 #page-mod-quiz-edit ul.slots li.section.only-has-one-slot li.activity .editing_delete {
     visibility: hidden;
 }
+#page-mod-quiz-edit ul.slots.only-one-section li.section.only-has-one-slot li.activity .editing_delete {
+    visibility: visible;
+}
 
 #page-mod-quiz-edit ul.slots li.section li.activity .question_dependency_wrapper {
     position: absolute;
index 21d2b24..cc02d51 100644 (file)
@@ -64,3 +64,36 @@ Feature: Edit quiz page - remove questions
     And I should not see "Question A" on quiz page "2"
     And the "Remove" page break icon after question "Question B" should not exist
     And I should see "Total of marks: 1.00"
+
+  @javascript
+  Scenario: Cannot delete the last question in a section.
+    Given the following "questions" exist:
+      | questioncategory | qtype     | name       | questiontext        |
+      | Test questions   | truefalse | Question A | This is question 01 |
+      | Test questions   | truefalse | Question B | This is question 02 |
+      | Test questions   | truefalse | Question C | This is question 03 |
+    And quiz "Quiz 1" contains the following questions:
+      | question   | page |
+      | Question A | 1    |
+      | Question B | 1    |
+      | Question C | 2    |
+    And quiz "Quiz 1" contains the following sections:
+      | heading   | firstslot | shuffle |
+      | Heading 1 | 1         | 1       |
+      | Heading 2 | 2         | 1       |
+    When I follow "Edit quiz"
+    Then "Delete" "link" in the "Question A" "list_item" should not be visible
+    Then "Delete" "link" in the "Question B" "list_item" should be visible
+    Then "Delete" "link" in the "Question C" "list_item" should be visible
+
+  @javascript
+  Scenario: Can delete the last question in a quiz.
+    Given the following "questions" exist:
+      | questioncategory | qtype     | name       | questiontext        |
+      | Test questions   | truefalse | Question A | This is question 01 |
+    And quiz "Quiz 1" contains the following questions:
+      | question   | page |
+      | Question A | 1    |
+    When I follow "Edit quiz"
+    And I delete "Question A" in the quiz by clicking the delete icon
+    Then I should see "Questions: 0"
index 7dfeb95..6210f81 100644 (file)
@@ -153,10 +153,10 @@ class mod_quiz_structure_testcase extends advanced_testcase {
             } else {
                 list($name, $page, $qtype) = $item;
                 $question = $structure->get_question_in_slot($slot);
-                $this->assertEquals($slot,  $question->slot);
                 $this->assertEquals($name,  $question->name);
-                $this->assertEquals($qtype, $question->qtype);
-                $this->assertEquals($page,  $question->page);
+                $this->assertEquals($slot,  $question->slot,  'Slot number wrong for question ' . $name);
+                $this->assertEquals($qtype, $question->qtype, 'Question type wrong for question ' . $name);
+                $this->assertEquals($page,  $question->page,  'Page number wrong for question ' . $name);
 
                 $slot += 1;
             }
@@ -624,6 +624,29 @@ class mod_quiz_structure_testcase extends advanced_testcase {
         $structure->remove_slot(3);
     }
 
+    public function test_can_remove_last_question_in_a_quiz() {
+        $quizobj = $this->create_test_quiz(array(
+                'Heading 1',
+                array('TF1', 1, 'truefalse'),
+            ));
+        $structure = \mod_quiz\structure::create_for_quiz($quizobj);
+
+        $structure->remove_slot(1);
+
+        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
+        $cat = $questiongenerator->create_question_category();
+        $q = $questiongenerator->create_question('truefalse', null,
+                array('name' => 'TF2', 'category' => $cat->id));
+
+        quiz_add_quiz_question($q->id, $quizobj->get_quiz(), 0);
+        $structure = \mod_quiz\structure::create_for_quiz($quizobj);
+
+        $this->assert_quiz_layout(array(
+                'Heading 1',
+                array('TF2', 1, 'truefalse'),
+        ), $structure);
+    }
+
     public function test_add_question_updates_headings() {
         $quizobj = $this->create_test_quiz(array(
                 array('TF1', 1, 'truefalse'),
@@ -648,7 +671,7 @@ class mod_quiz_structure_testcase extends advanced_testcase {
         ), $structure);
     }
 
-    public function test_add_question_and_end_does_not_update_headings() {
+    public function test_add_question_at_end_does_not_update_headings() {
         $quizobj = $this->create_test_quiz(array(
                 array('TF1', 1, 'truefalse'),
                 'Heading 2',
index c006429..f317e4c 100644 (file)
@@ -15,6 +15,9 @@ This files describes API changes in the quiz code.
 * mod_quiz_renderer::review_next_navigation has a new optional argument. If you
   have overridden that method, consider updating your code to match.
 
+* mod_quiz\output\edit_renderer::start_section_list now takes $structure as an
+  argument. If you have overridden this method (it's hard to believe anyone ever
+  would) you will need to update your renderer.
 
 === 2.9 ===