Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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