Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are 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   * @package moodlecore
  20   * @subpackage backup-helper
  21   * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * Helper class used to decode links back to their original form
  29   *
  30   * This class allows each restore task to specify the changes that
  31   * will be applied to any encoded (by backup) link to revert it back
  32   * to its original form, recoding any parameter as needed.
  33   *
  34   * TODO: Complete phpdocs
  35   */
  36  class restore_decode_rule {
  37  
  38      protected $linkname;    // How the link has been encoded in backup (CHOICEVIEWBYID, COURSEVIEWBYID...)
  39      protected $urltemplate; // How the original URL looks like, with dollar placeholders
  40      protected $mappings;    // Which backup_ids mappings do we need to apply for replacing the placeholders
  41      protected $restoreid;   // The unique restoreid we are executing
  42      protected $sourcewwwroot; // The original wwwroot of the backup file
  43      protected $targetwwwroot; // The targer wwwroot of the restore operation
  44  
  45      protected $cregexp;     // Calculated regular expresion we'll be looking for matches
  46  
  47      public function __construct($linkname, $urltemplate, $mappings) {
  48          // Validate all the params are ok
  49          $this->mappings = $this->validate_params($linkname, $urltemplate, $mappings);
  50          $this->linkname = $linkname;
  51          $this->urltemplate = $urltemplate;
  52          $this->restoreid = 0;
  53          $this->sourcewwwroot = '';
  54          $this->targetwwwroot = ''; // yes, uses to be $CFG->wwwroot, and? ;-)
  55          $this->cregexp = $this->get_calculated_regexp();
  56      }
  57  
  58      public function set_restoreid($restoreid) {
  59          $this->restoreid = $restoreid;
  60      }
  61  
  62      public function set_wwwroots($sourcewwwroot, $targetwwwroot) {
  63          $this->sourcewwwroot = $sourcewwwroot;
  64          $this->targetwwwroot = $targetwwwroot;
  65      }
  66  
  67      public function decode($content) {
  68          if (preg_match_all($this->cregexp, $content, $matches) === 0) { // 0 matches, nothing to change
  69              return $content;
  70          }
  71          // Have found matches, iterate over them
  72          foreach ($matches[0] as $key => $tosearch) {
  73              $mappingsok = true;             // To detect if any mapping has failed
  74              $placeholdersarr   = array();   // The placeholders to be replaced
  75              $mappingssourcearr = array();   // To store the original mappings values
  76              $mappingstargetarr = array();   // To store the target mappings values
  77              $toreplace = $this->urltemplate;// The template used to build the replacement
  78              foreach ($this->mappings as $mappingkey => $mappingsource) {
  79                  $source = $matches[$mappingkey][$key];          // get source
  80                  $mappingssourcearr[$mappingkey] = $source;      // set source arr
  81                  $mappingstargetarr[$mappingkey] = 0;            // apply default mapping
  82                  $placeholdersarr[$mappingkey] = '$'.$mappingkey;// set the placeholders arr
  83                  if (!$mappingsok) {                             // already missing some mapping, continue
  84                      continue;
  85                  }
  86                  if (!$target = $this->get_mapping($mappingsource, $source)) {// mapping not found, mark and continue
  87                      $mappingsok = false;
  88                      continue;
  89                  }
  90                  $mappingstargetarr[$mappingkey] = $target;       // store found mapping
  91              }
  92              $toreplace = $this->apply_modifications($toreplace, $mappingsok); // Apply other changes before replacement
  93              if (!$mappingsok) { // Some mapping has failed, apply original values to the template
  94                  $toreplace = str_replace($placeholdersarr, $mappingssourcearr, $toreplace);
  95  
  96              } else {            // All mappings found, apply target values to the template
  97                  $toreplace = str_replace($placeholdersarr, $mappingstargetarr, $toreplace);
  98              }
  99              // Finally, perform the replacement in original content
 100              $content = str_replace($tosearch, $toreplace, $content);
 101          }
 102          return $content; // return the decoded content, pointing to original or target values
 103      }
 104  
 105  // Protected API starts here
 106  
 107      /**
 108       * Looks for mapping values in backup_ids table, simple wrapper over get_backup_ids_record
 109       */
 110      protected function get_mapping($itemname, $itemid) {
 111          // Check restoreid is set
 112          if (!$this->restoreid) {
 113              throw new restore_decode_rule_exception('decode_rule_restoreid_not_set');
 114          }
 115          if (!$found = restore_dbops::get_backup_ids_record($this->restoreid, $itemname, $itemid)) {
 116              return false;
 117          }
 118          return $found->newitemid;
 119      }
 120  
 121      /**
 122       * Apply other modifications, based in the result of $mappingsok before placeholder replacements
 123       *
 124       * Right now, simply prefix with the proper wwwroot (source/target)
 125       */
 126      protected function apply_modifications($toreplace, $mappingsok) {
 127          // Check wwwroots are set
 128          if (!$this->targetwwwroot || !$this->sourcewwwroot) {
 129              throw new restore_decode_rule_exception('decode_rule_wwwroots_not_set');
 130          }
 131          return ($mappingsok ? $this->targetwwwroot : $this->sourcewwwroot) . $toreplace;
 132      }
 133  
 134      /**
 135       * Perform all the validations and checks on the rule attributes
 136       */
 137      protected function validate_params($linkname, $urltemplate, $mappings) {
 138          // Check linkname is A-Z0-9
 139          if (empty($linkname) || preg_match('/[^A-Z0-9]/', $linkname)) {
 140              throw new restore_decode_rule_exception('decode_rule_incorrect_name', $linkname);
 141          }
 142          // Look urltemplate starts by /
 143          if (empty($urltemplate) || substr($urltemplate, 0, 1) != '/') {
 144              throw new restore_decode_rule_exception('decode_rule_incorrect_urltemplate', $urltemplate);
 145          }
 146          if (!is_array($mappings)) {
 147              $mappings = array($mappings);
 148          }
 149          // Look for placeholders in template
 150          $countph = preg_match_all('/(\$\d+)/', $urltemplate, $matches);
 151          $countma = count($mappings);
 152          // Check mappings number matches placeholders
 153          if ($countph != $countma) {
 154              $a = new stdClass();
 155              $a->placeholders = $countph;
 156              $a->mappings     = $countma;
 157              throw new restore_decode_rule_exception('decode_rule_mappings_incorrect_count', $a);
 158          }
 159          // Verify they are consecutive (starting on 1)
 160          $smatches = str_replace('$', '', $matches[1]);
 161          sort($smatches, SORT_NUMERIC);
 162          if (reset($smatches) != 1 || end($smatches) != $countma) {
 163              throw new restore_decode_rule_exception('decode_rule_nonconsecutive_placeholders', implode(', ', $smatches));
 164          }
 165          // No dupes in placeholders
 166          if (count($smatches) != count(array_unique($smatches))) {
 167              throw new restore_decode_rule_exception('decode_rule_duplicate_placeholders', implode(', ', $smatches));
 168          }
 169  
 170          // Return one array of placeholders as keys and mappings as values
 171          return array_combine($smatches, $mappings);
 172      }
 173  
 174      /**
 175       * based on rule definition, build the regular expression to execute on decode
 176       */
 177      protected function get_calculated_regexp() {
 178          $regexp = '/\$@' . $this->linkname;
 179          foreach ($this->mappings as $key => $value) {
 180              $regexp .= '\*(\d+)';
 181          }
 182          $regexp .= '@\$/';
 183          return $regexp;
 184      }
 185  }
 186  
 187  /*
 188   * Exception class used by all the @restore_decode_rule stuff
 189   */
 190  class restore_decode_rule_exception extends backup_exception {
 191  
 192      public function __construct($errorcode, $a=NULL, $debuginfo=null) {
 193          return parent::__construct($errorcode, $a, $debuginfo);
 194      }
 195  }