Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.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   * Provides support for the conversion of moodle1 backup to the moodle2 format
  20   *
  21   * @package    mod_resource
  22   * @copyright  2011 Andrew Davis <andrew@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Resource conversion handler
  30   */
  31  class moodle1_mod_resource_handler extends moodle1_mod_handler {
  32  
  33      /** @var moodle1_file_manager instance */
  34      protected $fileman = null;
  35  
  36      /** @var array of resource successors handlers */
  37      private $successors = array();
  38  
  39      /**
  40       * Declare the paths in moodle.xml we are able to convert
  41       *
  42       * The method returns list of {@link convert_path} instances.
  43       * For each path returned, the corresponding conversion method must be
  44       * defined.
  45       *
  46       * Note that the paths /MOODLE_BACKUP/COURSE/MODULES/MOD/RESOURCE do not
  47       * actually exist in the file. The last element with the module name was
  48       * appended by the moodle1_converter class.
  49       *
  50       * @return array of {@link convert_path} instances
  51       */
  52      public function get_paths() {
  53          return array(
  54              new convert_path(
  55                  'resource', '/MOODLE_BACKUP/COURSE/MODULES/MOD/RESOURCE',
  56                  array(
  57                      'renamefields' => array(
  58                          'summary' => 'intro',
  59                      ),
  60                      'newfields' => array(
  61                          'introformat' => 0,
  62                      ),
  63                      'dropfields' => array(
  64                          'modtype',
  65                      ),
  66                  )
  67              )
  68          );
  69      }
  70  
  71      /**
  72       * Converts /MOODLE_BACKUP/COURSE/MODULES/MOD/RESOURCE data
  73       *
  74       * This methods detects the resource type and eventually re-dispatches it to the
  75       * corresponding resource successor (url, forum, page, imscp).
  76       */
  77      public function process_resource(array $data, array $raw) {
  78          global $CFG;
  79          require_once("$CFG->libdir/resourcelib.php");
  80  
  81          // replay the upgrade step 2009042001
  82          if ($CFG->texteditors !== 'textarea') {
  83              $data['intro']       = text_to_html($data['intro'], false, false, true);
  84              $data['introformat'] = FORMAT_HTML;
  85          }
  86  
  87          // fix invalid null popup and options data
  88          if (!array_key_exists('popup', $data) or is_null($data['popup'])) {
  89              $data['popup'] = '';
  90          }
  91          if (!array_key_exists ('options', $data) or is_null($data['options'])) {
  92              $data['options'] = '';
  93          }
  94  
  95          // decide if the legacy resource should be handled by a successor module
  96          if ($successor = $this->get_successor($data['type'], $data['reference'])) {
  97              // the instance id will be kept
  98              $instanceid = $data['id'];
  99  
 100              // move the instance from the resource's modinfo stash to the successor's
 101              // modinfo stash
 102              $resourcemodinfo  = $this->converter->get_stash('modinfo_resource');
 103              $successormodinfo = $this->converter->get_stash('modinfo_'.$successor->get_modname());
 104              $successormodinfo['instances'][$instanceid] = $resourcemodinfo['instances'][$instanceid];
 105              unset($resourcemodinfo['instances'][$instanceid]);
 106              $this->converter->set_stash('modinfo_resource', $resourcemodinfo);
 107              $this->converter->set_stash('modinfo_'.$successor->get_modname(), $successormodinfo);
 108  
 109              // get the course module information for the legacy resource module
 110              $cminfo = $this->get_cminfo($instanceid);
 111  
 112              // use the version of the successor instead of the current mod/resource
 113              // beware - the version.php declares info via $module object, do not use
 114              // a variable of such name here
 115              $plugin = new stdClass();
 116              $plugin->version = null;
 117              $module = $plugin;
 118              include $CFG->dirroot.'/mod/'.$successor->get_modname().'/version.php';
 119              $cminfo['version'] = $plugin->version;
 120  
 121              // stash the new course module information for this successor
 122              $cminfo['modulename'] = $successor->get_modname();
 123              $this->converter->set_stash('cminfo_'.$cminfo['modulename'], $cminfo, $instanceid);
 124  
 125              // rewrite the coursecontents stash
 126              $coursecontents = $this->converter->get_stash('coursecontents');
 127              $coursecontents[$cminfo['id']]['modulename'] = $successor->get_modname();
 128              $this->converter->set_stash('coursecontents', $coursecontents);
 129  
 130              // delegate the processing to the successor handler
 131              return $successor->process_legacy_resource($data, $raw);
 132          }
 133  
 134          // no successor is interested in this record, convert it to the new mod_resource (aka File module)
 135  
 136          $resource = array();
 137          $resource['id']              = $data['id'];
 138          $resource['name']            = $data['name'];
 139          $resource['intro']           = $data['intro'];
 140          $resource['introformat']     = $data['introformat'];
 141          $resource['tobemigrated']    = 0;
 142          $resource['legacyfiles']     = RESOURCELIB_LEGACYFILES_ACTIVE;
 143          $resource['legacyfileslast'] = null;
 144          $resource['filterfiles']     = 0;
 145          $resource['revision']        = 1;
 146          $resource['timemodified']    = $data['timemodified'];
 147  
 148          // populate display and displayoptions fields
 149          $options = array('printintro' => 1);
 150          if ($data['options'] == 'frame') {
 151              $resource['display'] = RESOURCELIB_DISPLAY_FRAME;
 152  
 153          } else if ($data['options'] == 'objectframe') {
 154              $resource['display'] = RESOURCELIB_DISPLAY_EMBED;
 155  
 156          } else if ($data['options'] == 'forcedownload') {
 157              $resource['display'] = RESOURCELIB_DISPLAY_DOWNLOAD;
 158  
 159          } else if ($data['popup']) {
 160              $resource['display'] = RESOURCELIB_DISPLAY_POPUP;
 161              $rawoptions = explode(',', $data['popup']);
 162              foreach ($rawoptions as $rawoption) {
 163                  list($name, $value) = explode('=', trim($rawoption), 2);
 164                  if ($value > 0 and ($name == 'width' or $name == 'height')) {
 165                      $options['popup'.$name] = $value;
 166                      continue;
 167                  }
 168              }
 169  
 170          } else {
 171              $resource['display'] = RESOURCELIB_DISPLAY_AUTO;
 172          }
 173          $resource['displayoptions'] = serialize($options);
 174  
 175          // get the course module id and context id
 176          $instanceid     = $resource['id'];
 177          $currentcminfo  = $this->get_cminfo($instanceid);
 178          $moduleid       = $currentcminfo['id'];
 179          $contextid      = $this->converter->get_contextid(CONTEXT_MODULE, $moduleid);
 180  
 181          // get a fresh new file manager for this instance
 182          $this->fileman = $this->converter->get_file_manager($contextid, 'mod_resource');
 183  
 184          // convert course files embedded into the intro
 185          $this->fileman->filearea = 'intro';
 186          $this->fileman->itemid   = 0;
 187          $resource['intro'] = moodle1_converter::migrate_referenced_files($resource['intro'], $this->fileman);
 188  
 189          // convert the referenced file itself as a main file in the content area
 190          $reference = $data['reference'];
 191          if (strpos($reference, '$@FILEPHP@$') === 0) {
 192              $reference = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $reference);
 193          }
 194          $this->fileman->filearea = 'content';
 195          $this->fileman->itemid   = 0;
 196  
 197          // Rebuild the file path.
 198          $curfilepath = '/';
 199          if ($reference) {
 200              $curfilepath = pathinfo('/'.$reference, PATHINFO_DIRNAME);
 201              if ($curfilepath != '/') {
 202                  $curfilepath .= '/';
 203              }
 204          }
 205          try {
 206              $this->fileman->migrate_file('course_files/'.$reference, $curfilepath, null, 1);
 207          } catch (moodle1_convert_exception $e) {
 208              // the file probably does not exist
 209              $this->log('error migrating the resource main file', backup::LOG_WARNING, 'course_files/'.$reference);
 210          }
 211  
 212          // write resource.xml
 213          $this->open_xml_writer("activities/resource_{$moduleid}/resource.xml");
 214          $this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $moduleid,
 215              'modulename' => 'resource', 'contextid' => $contextid));
 216          $this->write_xml('resource', $resource, array('/resource/id'));
 217          $this->xmlwriter->end_tag('activity');
 218          $this->close_xml_writer();
 219  
 220          // write inforef.xml
 221          $this->open_xml_writer("activities/resource_{$currentcminfo['id']}/inforef.xml");
 222          $this->xmlwriter->begin_tag('inforef');
 223          $this->xmlwriter->begin_tag('fileref');
 224          foreach ($this->fileman->get_fileids() as $fileid) {
 225              $this->write_xml('file', array('id' => $fileid));
 226          }
 227          $this->xmlwriter->end_tag('fileref');
 228          $this->xmlwriter->end_tag('inforef');
 229          $this->close_xml_writer();
 230      }
 231  
 232      /**
 233       * Give succesors a chance to finish their job
 234       */
 235      public function on_resource_end(array $data) {
 236          if ($successor = $this->get_successor($data['type'], $data['reference'])) {
 237              $successor->on_legacy_resource_end($data);
 238          }
 239      }
 240  
 241      /// internal implementation details follow /////////////////////////////////
 242  
 243      /**
 244       * Returns the handler of the new 2.0 mod type according the given type of the legacy 1.9 resource
 245       *
 246       * @param string $type the value of the 'type' field in 1.9 resource
 247       * @param string $reference a file path. Necessary to differentiate files from web URLs
 248       * @throws moodle1_convert_exception for the unknown types
 249       * @return null|moodle1_mod_handler the instance of the handler, or null if the type does not have a successor
 250       */
 251      protected function get_successor($type, $reference) {
 252  
 253          switch ($type) {
 254              case 'text':
 255              case 'html':
 256                  $name = 'page';
 257                  break;
 258              case 'directory':
 259                  $name = 'folder';
 260                  break;
 261              case 'ims':
 262                  $name = 'imscp';
 263                  break;
 264              case 'file':
 265                  // if starts with $@FILEPHP@$ then it is URL link to a local course file
 266                  // to be migrated to the new resource module
 267                  if (strpos($reference, '$@FILEPHP@$') === 0) {
 268                      $name = null;
 269                      break;
 270                  }
 271                  // if http:// https:// ftp:// OR starts with slash need to be converted to URL
 272                  if (strpos($reference, '://') or strpos($reference, '/') === 0) {
 273                      $name = 'url';
 274                  } else {
 275                      $name = null;
 276                  }
 277                  break;
 278              default:
 279                  throw new moodle1_convert_exception('unknown_resource_successor', $type);
 280          }
 281  
 282          if (is_null($name)) {
 283              return null;
 284          }
 285  
 286          if (!isset($this->successors[$name])) {
 287              $this->log('preparing resource successor handler', backup::LOG_DEBUG, $name);
 288              $class = 'moodle1_mod_'.$name.'_handler';
 289              $this->successors[$name] = new $class($this->converter, 'mod', $name);
 290  
 291              // add the successor into the modlist stash
 292              $modnames = $this->converter->get_stash('modnameslist');
 293              $modnames[] = $name;
 294              $modnames = array_unique($modnames); // should not be needed but just in case
 295              $this->converter->set_stash('modnameslist', $modnames);
 296  
 297              // add the successor's modinfo stash
 298              $modinfo = $this->converter->get_stash('modinfo_resource');
 299              $modinfo['name'] = $name;
 300              $modinfo['instances'] = array();
 301              $this->converter->set_stash('modinfo_'.$name, $modinfo);
 302          }
 303  
 304          return $this->successors[$name];
 305       }
 306  }