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   * Provides support for the conversion of moodle1 backup to the moodle2 format
  20   *
  21   * @package   mod_workshop
  22   * @copyright 2011 David Mudrak <david@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   * Workshop conversion handler
  30   */
  31  class moodle1_mod_workshop_handler extends moodle1_mod_handler {
  32  
  33      /** @var array the temporary in-memory cache for the current <MOD> contents */
  34      protected $currentworkshop = null;
  35  
  36      /** @var array in-memory cache for the course module information for the current workshop  */
  37      protected $currentcminfo = null;
  38  
  39      /** @var array the mapping of legacy elementno => newelementid for the current workshop */
  40      protected $newelementids = array();
  41  
  42      /** @var moodle1_file_manager for the current workshop */
  43      protected $fileman = null;
  44  
  45      /** @var moodle1_inforef_manager */
  46      protected $inforefman = null;
  47  
  48      /** @var array list of moodle1_workshopform_handler instances */
  49      private $strategyhandlers = null;
  50  
  51      /** @var int parent id for the rubric level */
  52      private $currentelementid = null;
  53  
  54      /**
  55       * Declare the paths in moodle.xml we are able to convert
  56       *
  57       * The method returns list of {@link convert_path} instances. For each path returned,
  58       * at least one of on_xxx_start(), process_xxx() and on_xxx_end() methods must be
  59       * defined. The method process_xxx() is not executed if the associated path element is
  60       * empty (i.e. it contains none elements or sub-paths only).
  61       *
  62       * Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/WORKSHOP does not
  63       * actually exist in the file. The last element with the module name was
  64       * appended by the moodle1_converter class.
  65       *
  66       * @return array of {@link convert_path} instances
  67       */
  68      public function get_paths() {
  69          return array(
  70              new convert_path('workshop', '/MOODLE_BACKUP/COURSE/MODULES/MOD/WORKSHOP'),
  71              new convert_path('workshop_elements', '/MOODLE_BACKUP/COURSE/MODULES/MOD/WORKSHOP/ELEMENTS'),
  72              new convert_path(
  73                  'workshop_element', '/MOODLE_BACKUP/COURSE/MODULES/MOD/WORKSHOP/ELEMENTS/ELEMENT',
  74                  array(
  75                      'dropfields' => array(
  76                          'stddev',
  77                          'totalassessments',
  78                      ),
  79                  )
  80              ),
  81              new convert_path('workshop_element_rubric', '/MOODLE_BACKUP/COURSE/MODULES/MOD/WORKSHOP/ELEMENTS/ELEMENT/RUBRICS/RUBRIC'),
  82          );
  83      }
  84  
  85      /**
  86       * This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/WORKSHOP
  87       * data available
  88       */
  89      public function process_workshop($data, $raw) {
  90  
  91          // re-use the upgrade function to convert workshop record
  92          $fakerecord = (object)$data;
  93          $fakerecord->course = 12345678;
  94          $this->currentworkshop = (array)workshop_upgrade_transform_instance($fakerecord);
  95          unset($this->currentworkshop['course']);
  96  
  97          // add the new fields with the default values
  98          $this->currentworkshop['id']                        = $data['id'];
  99          $this->currentworkshop['evaluation']                = 'best';
 100          $this->currentworkshop['examplesmode']              = workshop::EXAMPLES_VOLUNTARY;
 101          $this->currentworkshop['gradedecimals']             = 0;
 102          $this->currentworkshop['instructauthors']           = '';
 103          $this->currentworkshop['instructauthorsformat']     = FORMAT_HTML;
 104          $this->currentworkshop['instructreviewers']         = '';
 105          $this->currentworkshop['instructreviewersformat']   = FORMAT_HTML;
 106          $this->currentworkshop['latesubmissions']           = 0;
 107          $this->currentworkshop['conclusion']                = '';
 108          $this->currentworkshop['conclusionformat']          = FORMAT_HTML;
 109  
 110          foreach (array('submissionend', 'submissionstart', 'assessmentend', 'assessmentstart') as $field) {
 111              if (!array_key_exists($field, $this->currentworkshop)) {
 112                  $this->currentworkshop[$field] = null;
 113              }
 114          }
 115  
 116          // get the course module id and context id
 117          $instanceid          = $data['id'];
 118          $this->currentcminfo = $this->get_cminfo($instanceid);
 119          $moduleid            = $this->currentcminfo['id'];
 120          $contextid           = $this->converter->get_contextid(CONTEXT_MODULE, $moduleid);
 121  
 122          // get a fresh new inforef manager for this instance
 123          $this->inforefman = $this->converter->get_inforef_manager('activity', $moduleid);
 124  
 125          // get a fresh new file manager for this instance
 126          $this->fileman = $this->converter->get_file_manager($contextid, 'mod_workshop');
 127  
 128          // convert course files embedded into the intro
 129          $this->fileman->filearea = 'intro';
 130          $this->fileman->itemid   = 0;
 131          $this->currentworkshop['intro'] = moodle1_converter::migrate_referenced_files($this->currentworkshop['intro'], $this->fileman);
 132  
 133          // write workshop.xml
 134          $this->open_xml_writer("activities/workshop_{$moduleid}/workshop.xml");
 135          $this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $moduleid,
 136              'modulename' => 'workshop', 'contextid' => $contextid));
 137          $this->xmlwriter->begin_tag('workshop', array('id' => $instanceid));
 138  
 139          foreach ($this->currentworkshop as $field => $value) {
 140              if ($field <> 'id') {
 141                  $this->xmlwriter->full_tag($field, $value);
 142              }
 143          }
 144  
 145          return $this->currentworkshop;
 146      }
 147  
 148      /**
 149       * This is executed when the parser reaches <ELEMENTS>
 150       *
 151       * The dimensions definition follows. One of the grading strategy subplugins
 152       * will append dimensions data in {@link self::process_workshop_element()}
 153       */
 154      public function on_workshop_elements_start() {
 155  
 156          $this->xmlwriter->begin_tag('subplugin_workshopform_'.$this->currentworkshop['strategy'].'_workshop');
 157  
 158          // inform the strategy handler that a new workshop instance is being processed
 159          $handler = $this->get_strategy_handler($this->currentworkshop['strategy']);
 160          $handler->use_xml_writer($this->xmlwriter);
 161          $handler->on_elements_start();
 162      }
 163  
 164      /**
 165       * Processes one <ELEMENT> tag from moodle.xml
 166       */
 167      public function process_workshop_element($data, $raw) {
 168  
 169          // generate artificial element id and remember it for later usage
 170          $data['id'] = $this->converter->get_nextid();
 171          $this->currentelementid = $data['id'];
 172          $this->newelementids[$data['elementno']] = $data['id'];
 173  
 174          // let the strategy subplugin do whatever it needs to
 175          $handler = $this->get_strategy_handler($this->currentworkshop['strategy']);
 176          return $handler->process_legacy_element($data, $raw);
 177      }
 178  
 179      /**
 180       * Processes one <RUBRIC> tag from moodle.xml
 181       */
 182      public function process_workshop_element_rubric($data, $raw) {
 183          if ($this->currentworkshop['strategy'] == 'rubric') {
 184              $handler = $this->get_strategy_handler('rubric');
 185              $data['elementid'] = $this->currentelementid;
 186              $handler->process_legacy_rubric($data, $raw);
 187          }
 188      }
 189  
 190      /**
 191       * This is executed when the parser reaches </ELEMENT>
 192       */
 193      public function on_workshop_element_end() {
 194          // give the strategy handlers a chance to write what they need
 195          $handler = $this->get_strategy_handler($this->currentworkshop['strategy']);
 196          $handler->on_legacy_element_end();
 197      }
 198  
 199      /**
 200       * This is executed when the parser reaches </ELEMENTS>
 201       */
 202      public function on_workshop_elements_end() {
 203          // give the strategy hanlders last chance to write what they need
 204          $handler = $this->get_strategy_handler($this->currentworkshop['strategy']);
 205          $handler->on_elements_end();
 206  
 207          // close the dimensions definition
 208          $this->xmlwriter->end_tag('subplugin_workshopform_'.$this->currentworkshop['strategy'].'_workshop');
 209  
 210          // as a temporary hack, we just write empty wrappers for the rest of data
 211          $this->write_xml('examplesubmissions', array());
 212          $this->write_xml('submissions', array());
 213          $this->write_xml('aggregations', array());
 214      }
 215  
 216      /**
 217       * This is executed when the parser reaches </MOD>
 218       */
 219      public function on_workshop_end() {
 220          // close workshop.xml
 221          $this->xmlwriter->end_tag('workshop');
 222          $this->xmlwriter->end_tag('activity');
 223          $this->close_xml_writer();
 224  
 225          // write inforef.xml
 226          $this->inforefman->add_refs('file', $this->fileman->get_fileids());
 227          $moduleid = $this->currentcminfo['id'];
 228          $this->open_xml_writer("activities/workshop_{$moduleid}/inforef.xml");
 229          $this->inforefman->write_refs($this->xmlwriter);
 230          $this->close_xml_writer();
 231  
 232          // get ready for the next instance
 233          $this->currentworkshop = null;
 234          $this->currentcminfo   = null;
 235          $this->newelementids   = array();
 236      }
 237  
 238      /**
 239       * Provides access to the current <workshop> data
 240       *
 241       * @return array|null
 242       */
 243      public function get_current_workshop() {
 244          return $this->currentworkshop;
 245      }
 246  
 247      /**
 248       * Provides access to the instance's inforef manager
 249       *
 250       * @return moodle1_inforef_manager
 251       */
 252      public function get_inforef_manager() {
 253          return $this->inforefman;
 254      }
 255  
 256      /// internal implementation details follow /////////////////////////////////
 257  
 258      /**
 259       * Factory method returning the handler of the given grading strategy subplugin
 260       *
 261       * @param string $strategy the name of the grading strategy
 262       * @throws moodle1_convert_exception
 263       * @return moodle1_workshopform_handler the instance of the handler
 264       */
 265      protected function get_strategy_handler($strategy) {
 266          global $CFG; // we include other files here
 267  
 268          if (is_null($this->strategyhandlers)) {
 269              $this->strategyhandlers = array();
 270              $subplugins = core_component::get_plugin_list('workshopform');
 271              foreach ($subplugins as $name => $dir) {
 272                  $handlerfile  = $dir.'/backup/moodle1/lib.php';
 273                  $handlerclass = "moodle1_workshopform_{$name}_handler";
 274                  if (!file_exists($handlerfile)) {
 275                      continue;
 276                  }
 277                  require_once($handlerfile);
 278  
 279                  if (!class_exists($handlerclass)) {
 280                      throw new moodle1_convert_exception('missing_handler_class', $handlerclass);
 281                  }
 282                  $this->log('preparing workshop grading strategy handler', backup::LOG_DEBUG, $handlerclass);
 283                  $this->strategyhandlers[$name] = new $handlerclass($this, $name);
 284                  if (!$this->strategyhandlers[$name] instanceof moodle1_workshopform_handler) {
 285                      throw new moodle1_convert_exception('wrong_handler_class', get_class($this->strategyhandlers[$name]));
 286                  }
 287              }
 288          }
 289  
 290          if (!isset($this->strategyhandlers[$strategy])) {
 291              throw new moodle1_convert_exception('usupported_subplugin', 'workshopform_'.$strategy);
 292          }
 293  
 294          return $this->strategyhandlers[$strategy];
 295      }
 296  }
 297  
 298  
 299  /**
 300   * Base class for the grading strategy subplugin handler
 301   */
 302  abstract class moodle1_workshopform_handler extends moodle1_submod_handler {
 303  
 304      /**
 305       * @param moodle1_mod_handler $workshophandler the handler of a module we are subplugin of
 306       * @param string $subpluginname the name of the subplugin
 307       */
 308      public function __construct(moodle1_mod_handler $workshophandler, $subpluginname) {
 309          parent::__construct($workshophandler, 'workshopform', $subpluginname);
 310      }
 311  
 312      /**
 313       * Provides a xml_writer instance to this workshopform handler
 314       *
 315       * @param xml_writer $xmlwriter
 316       */
 317      public function use_xml_writer(xml_writer $xmlwriter) {
 318          $this->xmlwriter = $xmlwriter;
 319      }
 320  
 321      /**
 322       * Called when we reach <ELEMENTS>
 323       *
 324       * Gives the handler a chance to prepare for a new workshop instance
 325       */
 326      public function on_elements_start() {
 327          // do nothing by default
 328      }
 329  
 330      /**
 331       * Called everytime when legacy <ELEMENT> data are available
 332       *
 333       * @param array $data legacy element data
 334       * @param array $raw raw element data
 335       *
 336       * @return array converted
 337       */
 338      public function process_legacy_element(array $data, array $raw) {
 339          return $data;
 340      }
 341  
 342      /**
 343       * Called when we reach </ELEMENT>
 344       */
 345      public function on_legacy_element_end() {
 346          // do nothing by default
 347      }
 348  
 349      /**
 350       * Called when we reach </ELEMENTS>
 351       */
 352      public function on_elements_end() {
 353          // do nothing by default
 354      }
 355  }
 356  
 357  /**
 358   * Given a record containing data from 1.9 workshop table, returns object containing data as should be saved in 2.0 workshop table
 359   *
 360   * @param stdClass $old record from 1.9 workshop table
 361   * @return stdClass
 362   */
 363  function workshop_upgrade_transform_instance(stdClass $old) {
 364      global $CFG;
 365      require_once (__DIR__ . '/../../locallib.php');
 366  
 367      $new                = new stdClass();
 368      $new->course        = $old->course;
 369      $new->name          = $old->name;
 370      $new->intro         = $old->description;
 371      $new->introformat   = $old->format;
 372      if ($old->nattachments == 0) {
 373          // Convert to the new method for disabling file submissions.
 374          $new->submissiontypefile = WORKSHOP_SUBMISSION_TYPE_DISABLED;
 375          $new->submissiontypetext = WORKSHOP_SUBMISSION_TYPE_REQUIRED;
 376          $new->nattachments = 1;
 377      } else {
 378          $new->nattachments  = $old->nattachments;
 379      }
 380      $new->maxbytes      = $old->maxbytes;
 381      $new->grade         = $old->grade;
 382      $new->gradinggrade  = $old->gradinggrade;
 383      $new->phase         = workshop::PHASE_CLOSED;
 384      $new->timemodified  = time();
 385      if ($old->ntassessments > 0) {
 386          $new->useexamples = 1;
 387      } else {
 388          $new->useexamples = 0;
 389      }
 390      $new->usepeerassessment = 1;
 391      $new->useselfassessment = $old->includeself;
 392      switch ($old->gradingstrategy) {
 393      case 0: // 'notgraded' - renamed
 394          $new->strategy = 'comments';
 395          break;
 396      case 1: // 'accumulative'
 397          $new->strategy = 'accumulative';
 398          break;
 399      case 2: // 'errorbanded' - renamed
 400          $new->strategy = 'numerrors';
 401          break;
 402      case 3: // 'criterion' - will be migrated into 'rubric'
 403          $new->strategy = 'rubric';
 404          break;
 405      case 4: // 'rubric'
 406          $new->strategy = 'rubric';
 407          break;
 408      }
 409      if ($old->submissionstart < $old->submissionend) {
 410          $new->submissionstart = $old->submissionstart;
 411          $new->submissionend   = $old->submissionend;
 412      }
 413      if ($old->assessmentstart < $old->assessmentend) {
 414          $new->assessmentstart = $old->assessmentstart;
 415          $new->assessmentend   = $old->assessmentend;
 416      }
 417  
 418      return $new;
 419  }