Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Defines the quiz repaginate class.
  19   *
  20   * @package   mod_quiz
  21   * @copyright 2014 The Open University
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace mod_quiz;
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * The repaginate class will rearrange questions in pages.
  30   *
  31   * The quiz setting allows users to write quizzes with one question per page,
  32   * n questions per page, or all questions on one page.
  33   *
  34   * @copyright 2014 The Open University
  35   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class repaginate {
  38  
  39      /** @var int means join pages. */
  40      const LINK = 1;
  41      /** @var int means split pages. */
  42      const UNLINK = 2;
  43  
  44      /** @var int the id of the quiz being manipulated. */
  45      private $quizid;
  46      /** @var array the quiz_slots for that quiz. */
  47      private $slots;
  48  
  49      /**
  50       * Constructor.
  51       * @param int $quizid the id of the quiz being manipulated.
  52       * @param stdClass[] $slots the quiz_slots for that quiz.
  53       */
  54      public function __construct($quizid = 0, $slots = null) {
  55          global $DB;
  56          $this->quizid = $quizid;
  57          if (!$this->quizid) {
  58              $this->slots = array();
  59          }
  60          if (!$slots) {
  61              $this->slots = $DB->get_records('quiz_slots', array('quizid' => $this->quizid), 'slot');
  62          } else {
  63              $this->slots = $slots;
  64          }
  65      }
  66  
  67      /**
  68       * Repaginate a given slot with the given pagenumber.
  69       * @param stdClass $slot
  70       * @param int $newpagenumber
  71       * @return stdClass
  72       */
  73      protected function repaginate_this_slot($slot, $newpagenumber) {
  74          $newslot = clone($slot);
  75          $newslot->page = $newpagenumber;
  76          return $newslot;
  77      }
  78  
  79      /**
  80       * Return current slot object.
  81       * @param array $slots
  82       * @param int $slotnumber
  83       * @return stdClass $slot
  84       */
  85      protected function get_this_slot($slots, $slotnumber) {
  86          foreach ($slots as $key => $slot) {
  87              if ($slot->slot == $slotnumber) {
  88                  return $slot;
  89              }
  90          }
  91          return null;
  92      }
  93  
  94      /**
  95       * Return array of slots with slot number as key
  96       * @param stdClass[] $slots
  97       * @return stdClass[]
  98       */
  99      protected function get_slots_by_slot_number($slots) {
 100          if (!$slots) {
 101              return array();
 102          }
 103          $newslots = array();
 104          foreach ($slots as $slot) {
 105              $newslots[$slot->slot] = $slot;
 106          }
 107          return $newslots;
 108      }
 109  
 110      /**
 111       * Return array of slots with slot id as key
 112       * @param stdClass[] $slots
 113       * @return stdClass[]
 114       */
 115      protected function get_slots_by_slotid($slots) {
 116          if (!$slots) {
 117              return array();
 118          }
 119          $newslots = array();
 120          foreach ($slots as $slot) {
 121              $newslots[$slot->id] = $slot;
 122          }
 123          return $newslots;
 124      }
 125  
 126      /**
 127       * Repaginate, update DB and slots object
 128       * @param int $nextslotnumber
 129       * @param int $type repaginate::LINK or repaginate::UNLINK.
 130       */
 131      public function repaginate_slots($nextslotnumber, $type) {
 132          global $DB;
 133          $this->slots = $DB->get_records('quiz_slots', array('quizid' => $this->quizid), 'slot');
 134          $nextslot = null;
 135          $newslots = array();
 136          foreach ($this->slots as $slot) {
 137              if ($slot->slot < $nextslotnumber) {
 138                  $newslots[$slot->id] = $slot;
 139              } else if ($slot->slot == $nextslotnumber) {
 140                  $nextslot = $this->repaginate_next_slot($nextslotnumber, $type);
 141  
 142                  // Update DB.
 143                  $DB->update_record('quiz_slots', $nextslot, true);
 144  
 145                  // Update returning object.
 146                  $newslots[$slot->id] = $nextslot;
 147              }
 148          }
 149          if ($nextslot) {
 150              $newslots = array_merge($newslots, $this->repaginate_the_rest($this->slots, $nextslotnumber, $type));
 151              $this->slots = $this->get_slots_by_slotid($newslots);
 152          }
 153      }
 154  
 155      /**
 156       * Repaginate next slot and return the modified slot object
 157       * @param int $nextslotnumber
 158       * @param int $type repaginate::LINK or repaginate::UNLINK.
 159       * @return stdClass|null
 160       */
 161      public function repaginate_next_slot($nextslotnumber, $type) {
 162          $currentslotnumber = $nextslotnumber - 1;
 163          if (!($currentslotnumber && $nextslotnumber)) {
 164              return null;
 165          }
 166          $currentslot = $this->get_this_slot($this->slots, $currentslotnumber);
 167          $nextslot = $this->get_this_slot($this->slots, $nextslotnumber);
 168  
 169          if ($type === self::LINK) {
 170              return $this->repaginate_this_slot($nextslot, $currentslot->page);
 171          } else if ($type === self::UNLINK) {
 172              return $this->repaginate_this_slot($nextslot, $nextslot->page + 1);
 173          }
 174          return null;
 175      }
 176  
 177      /**
 178       * Return the slots with the new pagination, regardless of current pagination.
 179       * @param stdClass[] $slots the slots to repaginate.
 180       * @param int $number number of question per page
 181       * @return stdClass[] the updated slots.
 182       */
 183      public function repaginate_n_question_per_page($slots, $number) {
 184          $slots = $this->get_slots_by_slot_number($slots);
 185          $newslots = array();
 186          $count = 0;
 187          $page = 1;
 188          foreach ($slots as $key => $slot) {
 189              for ($page + $count; $page < ($number + $count + 1); $page++) {
 190                  if ($slot->slot >= $page) {
 191                      $slot->page = $page;
 192                      $count++;
 193                  }
 194              }
 195              $newslots[$slot->id] = $slot;
 196          }
 197          return $newslots;
 198      }
 199  
 200      /**
 201       * Repaginate the rest.
 202       * @param stdClass[] $quizslots
 203       * @param int $slotfrom
 204       * @param int $type
 205       * @param bool $dbupdate
 206       * @return stdClass[]
 207       */
 208      public function repaginate_the_rest($quizslots, $slotfrom, $type, $dbupdate = true) {
 209          global $DB;
 210          if (!$quizslots) {
 211              return null;
 212          }
 213          $newslots = array();
 214          foreach ($quizslots as $slot) {
 215              if ($type == self::LINK) {
 216                  if ($slot->slot <= $slotfrom) {
 217                      continue;
 218                  }
 219                  $slot->page = $slot->page - 1;
 220              } else if ($type == self::UNLINK) {
 221                  if ($slot->slot <= $slotfrom - 1) {
 222                      continue;
 223                  }
 224                  $slot->page = $slot->page + 1;
 225              }
 226              // Update DB.
 227              if ($dbupdate) {
 228                  $DB->update_record('quiz_slots', $slot);
 229              }
 230              $newslots[$slot->id] = $slot;
 231          }
 232          return $newslots;
 233      }
 234  }