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.
   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Defines backup_qtype_plugin class
  20   *
  21   * @package     core_backup
  22   * @subpackage  moodle2
  23   * @category    backup
  24   * @copyright   2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  25   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  /**
  31   * Class extending standard backup_plugin in order to implement some
  32   * helper methods related with the questions (qtype plugin)
  33   *
  34   * TODO: Finish phpdocs
  35   */
  36  abstract class backup_qtype_plugin extends backup_plugin {
  37  
  38      /**
  39       * Attach to $element (usually questions) the needed backup structures
  40       * for question_answers for a given question
  41       * Used by various qtypes (calculated, essay, multianswer,
  42       * multichoice, numerical, shortanswer, truefalse)
  43       */
  44      protected function add_question_question_answers($element) {
  45          // Check $element is one nested_backup_element
  46          if (! $element instanceof backup_nested_element) {
  47              throw new backup_step_exception('question_answers_bad_parent_element', $element);
  48          }
  49  
  50          // Define the elements
  51          $answers = new backup_nested_element('answers');
  52          $answer = new backup_nested_element('answer', array('id'), array(
  53              'answertext', 'answerformat', 'fraction', 'feedback',
  54              'feedbackformat'));
  55  
  56          // Build the tree
  57          $element->add_child($answers);
  58          $answers->add_child($answer);
  59  
  60          // Set the sources
  61          $answer->set_source_table('question_answers', array('question' => backup::VAR_PARENTID), 'id ASC');
  62  
  63          // Aliases
  64          $answer->set_source_alias('answer', 'answertext');
  65  
  66          // don't need to annotate ids nor files
  67      }
  68  
  69      /**
  70       * Attach to $element (usually questions) the needed backup structures
  71       * for question_numerical_units for a given question
  72       * Used both by calculated and numerical qtypes
  73       */
  74      protected function add_question_numerical_units($element) {
  75          // Check $element is one nested_backup_element
  76          if (! $element instanceof backup_nested_element) {
  77              throw new backup_step_exception('question_numerical_units_bad_parent_element', $element);
  78          }
  79  
  80          // Define the elements
  81          $units = new backup_nested_element('numerical_units');
  82          $unit = new backup_nested_element('numerical_unit', array('id'), array(
  83              'multiplier', 'unit'));
  84  
  85          // Build the tree
  86          $element->add_child($units);
  87          $units->add_child($unit);
  88  
  89          // Set the sources
  90          $unit->set_source_table('question_numerical_units', array('question' => backup::VAR_PARENTID), 'id ASC');
  91  
  92          // don't need to annotate ids nor files
  93      }
  94  
  95      /**
  96       * Attach to $element (usually questions) the needed backup structures
  97       * for question_numerical_options for a given question
  98       * Used both by calculated and numerical qtypes
  99       */
 100      protected function add_question_numerical_options($element) {
 101          // Check $element is one nested_backup_element
 102          if (! $element instanceof backup_nested_element) {
 103              throw new backup_step_exception('question_numerical_options_bad_parent_element', $element);
 104          }
 105  
 106          // Define the elements
 107          $options = new backup_nested_element('numerical_options');
 108          $option = new backup_nested_element('numerical_option', array('id'), array(
 109              'showunits', 'unitsleft', 'unitgradingtype', 'unitpenalty'));
 110  
 111          // Build the tree
 112          $element->add_child($options);
 113          $options->add_child($option);
 114  
 115          // Set the sources
 116          $option->set_source_table('question_numerical_options', array('question' => backup::VAR_PARENTID));
 117  
 118          // don't need to annotate ids nor files
 119      }
 120  
 121      /**
 122       * Attach to $element (usually questions) the needed backup structures
 123       * for question_datasets for a given question
 124       * Used by calculated qtypes
 125       */
 126      protected function add_question_datasets($element) {
 127          // Check $element is one nested_backup_element
 128          if (! $element instanceof backup_nested_element) {
 129              throw new backup_step_exception('question_datasets_bad_parent_element', $element);
 130          }
 131  
 132          // Define the elements
 133          $definitions = new backup_nested_element('dataset_definitions');
 134          $definition = new backup_nested_element('dataset_definition', array('id'), array(
 135              'category', 'name', 'type', 'options',
 136              'itemcount'));
 137  
 138          $items = new backup_nested_element('dataset_items');
 139          $item = new backup_nested_element('dataset_item', array('id'), array(
 140              'number', 'value'));
 141  
 142          // Build the tree
 143          $element->add_child($definitions);
 144          $definitions->add_child($definition);
 145  
 146          $definition->add_child($items);
 147          $items->add_child($item);
 148  
 149          // Set the sources
 150          $definition->set_source_sql('SELECT qdd.*
 151                                         FROM {question_dataset_definitions} qdd
 152                                         JOIN {question_datasets} qd ON qd.datasetdefinition = qdd.id
 153                                        WHERE qd.question = ?', array(backup::VAR_PARENTID));
 154  
 155          $item->set_source_table('question_dataset_items', array('definition' => backup::VAR_PARENTID));
 156  
 157          // Aliases
 158          $item->set_source_alias('itemnumber', 'number');
 159  
 160          // don't need to annotate ids nor files
 161      }
 162  
 163      /**
 164       * Returns all the components and fileareas used by all the installed qtypes
 165       *
 166       * The method introspects each qtype, asking it about fileareas used. Then,
 167       * one 2-level array is returned. 1st level is the component name (qtype_xxxx)
 168       * and 2nd level is one array of filearea => mappings to look
 169       *
 170       * Note that this function is used both in backup and restore, so it is important
 171       * to use the same mapping names (usually, name of the table in singular) always
 172       *
 173       * TODO: Surely this can be promoted to backup_plugin easily and make it to
 174       * work for ANY plugin, not only qtypes (but we don't need it for now)
 175       */
 176      public static function get_components_and_fileareas($filter = null) {
 177          $components = array();
 178          // Get all the plugins of this type
 179          $qtypes = core_component::get_plugin_list('qtype');
 180          foreach ($qtypes as $name => $path) {
 181              // Apply filter if specified
 182              if (!is_null($filter) && $filter != $name) {
 183                  continue;
 184              }
 185              // Calculate the componentname
 186              $componentname = 'qtype_' . $name;
 187              // Get the plugin fileareas (all them MUST belong to the same component)
 188              $classname = 'backup_qtype_' . $name . '_plugin';
 189              if (class_exists($classname)) {
 190                  $elements = call_user_func(array($classname, 'get_qtype_fileareas'));
 191                  if ($elements) {
 192                      // If there are elements, add them to $components
 193                      $components[$componentname] = $elements;
 194                  }
 195              }
 196          }
 197          return $components;
 198      }
 199  
 200      /**
 201       * Returns one array with filearea => mappingname elements for the qtype
 202       *
 203       * Used by {@link get_components_and_fileareas} to know about all the qtype
 204       * files to be processed both in backup and restore.
 205       */
 206      public static function get_qtype_fileareas() {
 207          // By default, return empty array, only qtypes having own fileareas will override this
 208          return array();
 209      }
 210  }