Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/backup/converter/moodle1/handlerlib.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00030 defined('MOODLE_INTERNAL') || die();
00031 
00032 require_once($CFG->dirroot . '/backup/util/xml/xml_writer.class.php');
00033 require_once($CFG->dirroot . '/backup/util/xml/output/xml_output.class.php');
00034 require_once($CFG->dirroot . '/backup/util/xml/output/file_xml_output.class.php');
00035 
00039 abstract class moodle1_handlers_factory {
00040 
00045     public static function get_handlers(moodle1_converter $converter) {
00046 
00047         $handlers = array(
00048             new moodle1_root_handler($converter),
00049             new moodle1_info_handler($converter),
00050             new moodle1_course_header_handler($converter),
00051             new moodle1_course_outline_handler($converter),
00052             new moodle1_roles_definition_handler($converter),
00053             new moodle1_question_bank_handler($converter),
00054             new moodle1_scales_handler($converter),
00055             new moodle1_outcomes_handler($converter),
00056             new moodle1_gradebook_handler($converter),
00057         );
00058 
00059         $handlers = array_merge($handlers, self::get_plugin_handlers('mod', $converter));
00060         $handlers = array_merge($handlers, self::get_plugin_handlers('block', $converter));
00061 
00062         // make sure that all handlers have expected class
00063         foreach ($handlers as $handler) {
00064             if (!$handler instanceof moodle1_handler) {
00065                 throw new moodle1_convert_exception('wrong_handler_class', get_class($handler));
00066             }
00067         }
00068 
00069         return $handlers;
00070     }
00071 
00073 
00083     protected static function get_plugin_handlers($type, moodle1_converter $converter) {
00084         global $CFG;
00085 
00086         $handlers = array();
00087         $plugins = get_plugin_list($type);
00088         foreach ($plugins as $name => $dir) {
00089             $handlerfile  = $dir . '/backup/moodle1/lib.php';
00090             $handlerclass = "moodle1_{$type}_{$name}_handler";
00091             if (!file_exists($handlerfile)) {
00092                 continue;
00093             }
00094             require_once($handlerfile);
00095 
00096             if (!class_exists($handlerclass)) {
00097                 throw new moodle1_convert_exception('missing_handler_class', $handlerclass);
00098             }
00099             $handlers[] = new $handlerclass($converter, $type, $name);
00100         }
00101         return $handlers;
00102     }
00103 }
00104 
00105 
00109 abstract class moodle1_handler implements loggable {
00110 
00112     protected $converter;
00113 
00117     public function __construct(moodle1_converter $converter) {
00118         $this->converter = $converter;
00119     }
00120 
00124     public function get_converter() {
00125         return $this->converter;
00126     }
00127 
00137     public function log($message, $level, $a = null, $depth = null, $display = false) {
00138         $this->converter->log($message, $level, $a, $depth, $display);
00139     }
00140 }
00141 
00142 
00146 abstract class moodle1_xml_handler extends moodle1_handler {
00147 
00149     protected $xmlfilename;
00150 
00152     protected $xmlwriter;
00153 
00160     protected function open_xml_writer($filename) {
00161 
00162         if (!is_null($this->xmlfilename) and $filename !== $this->xmlfilename) {
00163             throw new moodle1_convert_exception('xml_writer_already_opened_for_other_file', $this->xmlfilename);
00164         }
00165 
00166         if (!$this->xmlwriter instanceof xml_writer) {
00167             $this->xmlfilename = $filename;
00168             $fullpath  = $this->converter->get_workdir_path() . '/' . $this->xmlfilename;
00169             $directory = pathinfo($fullpath, PATHINFO_DIRNAME);
00170 
00171             if (!check_dir_exists($directory)) {
00172                 throw new moodle1_convert_exception('unable_create_target_directory', $directory);
00173             }
00174             $this->xmlwriter = new xml_writer(new file_xml_output($fullpath), new moodle1_xml_transformer());
00175             $this->xmlwriter->start();
00176         }
00177     }
00178 
00186     protected function close_xml_writer() {
00187         if ($this->xmlwriter instanceof xml_writer) {
00188             $this->xmlwriter->stop();
00189         }
00190         unset($this->xmlwriter);
00191         $this->xmlwriter = null;
00192         $this->xmlfilename = null;
00193     }
00194 
00200     protected function has_xml_writer() {
00201 
00202         if ($this->xmlwriter instanceof xml_writer) {
00203             return true;
00204         } else {
00205             return false;
00206         }
00207     }
00208 
00217     protected function write_xml($element, array $data, array $attribs = array(), $parent = '/') {
00218 
00219         if (!$this->has_xml_writer()) {
00220             throw new moodle1_convert_exception('write_xml_without_writer');
00221         }
00222 
00223         $mypath    = $parent . $element;
00224         $myattribs = array();
00225 
00226         // detect properties that should be rendered as element's attributes instead of children
00227         foreach ($data as $name => $value) {
00228             if (!is_array($value)) {
00229                 if (in_array($mypath . '/' . $name, $attribs)) {
00230                     $myattribs[$name] = $value;
00231                     unset($data[$name]);
00232                 }
00233             }
00234         }
00235 
00236         // reorder the $data so that all sub-branches are at the end (needed by our parser)
00237         $leaves   = array();
00238         $branches = array();
00239         foreach ($data as $name => $value) {
00240             if (is_array($value)) {
00241                 $branches[$name] = $value;
00242             } else {
00243                 $leaves[$name] = $value;
00244             }
00245         }
00246         $data = array_merge($leaves, $branches);
00247 
00248         $this->xmlwriter->begin_tag($element, $myattribs);
00249 
00250         foreach ($data as $name => $value) {
00251             if (is_array($value)) {
00252                 // recursively call self
00253                 $this->write_xml($name, $value, $attribs, $mypath.'/');
00254             } else {
00255                 $this->xmlwriter->full_tag($name, $value);
00256             }
00257         }
00258 
00259         $this->xmlwriter->end_tag($element);
00260     }
00261 
00274     protected function make_sure_xml_exists($filename, $rootelement = false, $content = array()) {
00275 
00276         $existed = file_exists($this->converter->get_workdir_path().'/'.$filename);
00277 
00278         if ($existed) {
00279             return true;
00280         }
00281 
00282         if ($rootelement !== false) {
00283             $this->open_xml_writer($filename);
00284             $this->write_xml($rootelement, $content);
00285             $this->close_xml_writer();
00286         }
00287 
00288         return false;
00289     }
00290 }
00291 
00292 
00296 class moodle1_root_handler extends moodle1_xml_handler {
00297 
00298     public function get_paths() {
00299         return array(new convert_path('root_element', '/MOODLE_BACKUP'));
00300     }
00301 
00305     public function on_root_element_start() {
00306 
00307         // convert course files
00308         $fileshandler = new moodle1_files_handler($this->converter);
00309         $fileshandler->process();
00310     }
00311 
00315     public function on_root_element_end() {
00316         global $CFG;
00317 
00318         // restore the stashes prepared by other handlers for us
00319         $backupinfo         = $this->converter->get_stash('backup_info');
00320         $originalcourseinfo = $this->converter->get_stash('original_course_info');
00321 
00323         // write moodle_backup.xml
00325         $this->open_xml_writer('moodle_backup.xml');
00326 
00327         $this->xmlwriter->begin_tag('moodle_backup');
00328         $this->xmlwriter->begin_tag('information');
00329 
00330         // moodle_backup/information
00331         $this->xmlwriter->full_tag('name', $backupinfo['name']);
00332         $this->xmlwriter->full_tag('moodle_version', $backupinfo['moodle_version']);
00333         $this->xmlwriter->full_tag('moodle_release', $backupinfo['moodle_release']);
00334         $this->xmlwriter->full_tag('backup_version', $CFG->backup_version); // {@see restore_prechecks_helper::execute_prechecks}
00335         $this->xmlwriter->full_tag('backup_release', $CFG->backup_release);
00336         $this->xmlwriter->full_tag('backup_date', $backupinfo['date']);
00337         // see the commit c0543b - all backups created in 1.9 and later declare the
00338         // information or it is considered as false
00339         if (isset($backupinfo['mnet_remoteusers'])) {
00340             $this->xmlwriter->full_tag('mnet_remoteusers', $backupinfo['mnet_remoteusers']);
00341         } else {
00342             $this->xmlwriter->full_tag('mnet_remoteusers', false);
00343         }
00344         $this->xmlwriter->full_tag('original_wwwroot', $backupinfo['original_wwwroot']);
00345         // {@see backup_general_helper::backup_is_samesite()}
00346         if (isset($backupinfo['original_site_identifier_hash'])) {
00347             $this->xmlwriter->full_tag('original_site_identifier_hash', $backupinfo['original_site_identifier_hash']);
00348         } else {
00349             $this->xmlwriter->full_tag('original_site_identifier_hash', null);
00350         }
00351         $this->xmlwriter->full_tag('original_course_id', $originalcourseinfo['original_course_id']);
00352         $this->xmlwriter->full_tag('original_course_fullname', $originalcourseinfo['original_course_fullname']);
00353         $this->xmlwriter->full_tag('original_course_shortname', $originalcourseinfo['original_course_shortname']);
00354         $this->xmlwriter->full_tag('original_course_startdate', $originalcourseinfo['original_course_startdate']);
00355         $this->xmlwriter->full_tag('original_system_contextid', $this->converter->get_contextid(CONTEXT_SYSTEM));
00356         // note that even though we have original_course_contextid available, we regenerate the
00357         // original course contextid using our helper method to be sure that the data are consistent
00358         // within the MBZ file
00359         $this->xmlwriter->full_tag('original_course_contextid', $this->converter->get_contextid(CONTEXT_COURSE));
00360 
00361         // moodle_backup/information/details
00362         $this->xmlwriter->begin_tag('details');
00363         $this->write_xml('detail', array(
00364             'backup_id'     => $this->converter->get_id(),
00365             'type'          => backup::TYPE_1COURSE,
00366             'format'        => backup::FORMAT_MOODLE,
00367             'interactive'   => backup::INTERACTIVE_YES,
00368             'mode'          => backup::MODE_CONVERTED,
00369             'execution'     => backup::EXECUTION_INMEDIATE,
00370             'executiontime' => 0,
00371         ), array('/detail/backup_id'));
00372         $this->xmlwriter->end_tag('details');
00373 
00374         // moodle_backup/information/contents
00375         $this->xmlwriter->begin_tag('contents');
00376 
00377         // moodle_backup/information/contents/activities
00378         $this->xmlwriter->begin_tag('activities');
00379         $activitysettings = array();
00380         foreach ($this->converter->get_stash('coursecontents') as $activity) {
00381             $modinfo = $this->converter->get_stash('modinfo_'.$activity['modulename']);
00382             $modinstance = $modinfo['instances'][$activity['instanceid']];
00383             $this->write_xml('activity', array(
00384                 'moduleid'      => $activity['cmid'],
00385                 'sectionid'     => $activity['sectionid'],
00386                 'modulename'    => $activity['modulename'],
00387                 'title'         => $modinstance['name'],
00388                 'directory'     => 'activities/'.$activity['modulename'].'_'.$activity['cmid']
00389             ));
00390             $activitysettings[] = array(
00391                 'level'     => 'activity',
00392                 'activity'  => $activity['modulename'].'_'.$activity['cmid'],
00393                 'name'      => $activity['modulename'].'_'.$activity['cmid'].'_included',
00394                 'value'     => (($modinfo['included'] === 'true' and $modinstance['included'] === 'true') ? 1 : 0));
00395             $activitysettings[] = array(
00396                 'level'     => 'activity',
00397                 'activity'  => $activity['modulename'].'_'.$activity['cmid'],
00398                 'name'      => $activity['modulename'].'_'.$activity['cmid'].'_userinfo',
00399                 //'value'     => (($modinfo['userinfo'] === 'true' and $modinstance['userinfo'] === 'true') ? 1 : 0));
00400                 'value'     => 0); // todo hardcoded non-userinfo for now
00401         }
00402         $this->xmlwriter->end_tag('activities');
00403 
00404         // moodle_backup/information/contents/sections
00405         $this->xmlwriter->begin_tag('sections');
00406         $sectionsettings = array();
00407         foreach ($this->converter->get_stash_itemids('sectioninfo') as $sectionid) {
00408             $sectioninfo = $this->converter->get_stash('sectioninfo', $sectionid);
00409             $sectionsettings[] = array(
00410                 'level'     => 'section',
00411                 'section'   => 'section_'.$sectionid,
00412                 'name'      => 'section_'.$sectionid.'_included',
00413                 'value'     => 1);
00414             $sectionsettings[] = array(
00415                 'level'     => 'section',
00416                 'section'   => 'section_'.$sectionid,
00417                 'name'      => 'section_'.$sectionid.'_userinfo',
00418                 'value'     => 0); // @todo how to detect this from moodle.xml?
00419             $this->write_xml('section', array(
00420                 'sectionid' => $sectionid,
00421                 'title'     => $sectioninfo['number'], // because the title is not available
00422                 'directory' => 'sections/section_'.$sectionid));
00423         }
00424         $this->xmlwriter->end_tag('sections');
00425 
00426         // moodle_backup/information/contents/course
00427         $this->write_xml('course', array(
00428             'courseid'  => $originalcourseinfo['original_course_id'],
00429             'title'     => $originalcourseinfo['original_course_shortname'],
00430             'directory' => 'course'));
00431         unset($originalcourseinfo);
00432 
00433         $this->xmlwriter->end_tag('contents');
00434 
00435         // moodle_backup/information/settings
00436         $this->xmlwriter->begin_tag('settings');
00437 
00438         // fake backup root seetings
00439         $rootsettings = array(
00440             'filename'         => $backupinfo['name'],
00441             'users'            => 0, // @todo how to detect this from moodle.xml?
00442             'anonymize'        => 0,
00443             'role_assignments' => 0,
00444             'user_files'       => 0,
00445             'activities'       => 1,
00446             'blocks'           => 1,
00447             'filters'          => 0,
00448             'comments'         => 0,
00449             'userscompletion'  => 0,
00450             'logs'             => 0,
00451             'grade_histories'  => 0,
00452         );
00453         unset($backupinfo);
00454         foreach ($rootsettings as $name => $value) {
00455             $this->write_xml('setting', array(
00456                 'level' => 'root',
00457                 'name'  => $name,
00458                 'value' => $value));
00459         }
00460         unset($rootsettings);
00461 
00462         // activity settings populated above
00463         foreach ($activitysettings as $activitysetting) {
00464             $this->write_xml('setting', $activitysetting);
00465         }
00466         unset($activitysettings);
00467 
00468         // section settings populated above
00469         foreach ($sectionsettings as $sectionsetting) {
00470             $this->write_xml('setting', $sectionsetting);
00471         }
00472         unset($sectionsettings);
00473 
00474         $this->xmlwriter->end_tag('settings');
00475 
00476         $this->xmlwriter->end_tag('information');
00477         $this->xmlwriter->end_tag('moodle_backup');
00478 
00479         $this->close_xml_writer();
00480 
00482         // write files.xml
00484         $this->open_xml_writer('files.xml');
00485         $this->xmlwriter->begin_tag('files');
00486         foreach ($this->converter->get_stash_itemids('files') as $fileid) {
00487             $this->write_xml('file', $this->converter->get_stash('files', $fileid), array('/file/id'));
00488         }
00489         $this->xmlwriter->end_tag('files');
00490         $this->close_xml_writer('files.xml');
00491 
00493         // write scales.xml
00495         $this->open_xml_writer('scales.xml');
00496         $this->xmlwriter->begin_tag('scales_definition');
00497         foreach ($this->converter->get_stash_itemids('scales') as $scaleid) {
00498             $this->write_xml('scale', $this->converter->get_stash('scales', $scaleid), array('/scale/id'));
00499         }
00500         $this->xmlwriter->end_tag('scales_definition');
00501         $this->close_xml_writer('scales.xml');
00502 
00504         // write course/inforef.xml
00506         $this->open_xml_writer('course/inforef.xml');
00507         $this->xmlwriter->begin_tag('inforef');
00508 
00509         $this->xmlwriter->begin_tag('fileref');
00510         // legacy course files
00511         $fileids = $this->converter->get_stash('course_files_ids');
00512         if (is_array($fileids)) {
00513             foreach ($fileids as $fileid) {
00514                 $this->write_xml('file', array('id' => $fileid));
00515             }
00516         }
00517         // todo site files
00518         // course summary files
00519         $fileids = $this->converter->get_stash('course_summary_files_ids');
00520         if (is_array($fileids)) {
00521             foreach ($fileids as $fileid) {
00522                 $this->write_xml('file', array('id' => $fileid));
00523             }
00524         }
00525         $this->xmlwriter->end_tag('fileref');
00526 
00527         $this->xmlwriter->begin_tag('question_categoryref');
00528         foreach ($this->converter->get_stash_itemids('question_categories') as $questioncategoryid) {
00529             $this->write_xml('question_category', array('id' => $questioncategoryid));
00530         }
00531         $this->xmlwriter->end_tag('question_categoryref');
00532 
00533         $this->xmlwriter->end_tag('inforef');
00534         $this->close_xml_writer();
00535 
00536         // make sure that the files required by the restore process have been generated.
00537         // missing file may happen if the watched tag is not present in moodle.xml (for example
00538         // QUESTION_CATEGORIES is optional in moodle.xml but questions.xml must exist in
00539         // moodle2 format) or the handler has not been implemented yet.
00540         // apparently this must be called after the handler had a chance to create the file.
00541         $this->make_sure_xml_exists('questions.xml', 'question_categories');
00542         $this->make_sure_xml_exists('groups.xml', 'groups');
00543         $this->make_sure_xml_exists('outcomes.xml', 'outcomes_definition');
00544         $this->make_sure_xml_exists('users.xml', 'users');
00545         $this->make_sure_xml_exists('course/roles.xml', 'roles',
00546             array('role_assignments' => array(), 'role_overrides' => array()));
00547         $this->make_sure_xml_exists('course/enrolments.xml', 'enrolments',
00548             array('enrols' => array()));
00549     }
00550 }
00551 
00552 
00558 class moodle1_files_handler extends moodle1_xml_handler {
00559 
00563     public function process() {
00564         $this->migrate_course_files();
00565         // todo $this->migrate_site_files();
00566     }
00567 
00571     protected function migrate_course_files() {
00572         $ids  = array();
00573         $fileman = $this->converter->get_file_manager($this->converter->get_contextid(CONTEXT_COURSE), 'course', 'legacy');
00574         $this->converter->set_stash('course_files_ids', array());
00575         if (file_exists($this->converter->get_tempdir_path().'/course_files')) {
00576             $ids = $fileman->migrate_directory('course_files');
00577             $this->converter->set_stash('course_files_ids', $ids);
00578         }
00579         $this->log('course files migrated', backup::LOG_INFO, count($ids));
00580     }
00581 }
00582 
00583 
00590 class moodle1_info_handler extends moodle1_handler {
00591 
00593     protected $modnames = array();
00594 
00596     protected $currentmod;
00597 
00598     public function get_paths() {
00599         return array(
00600             new convert_path('info', '/MOODLE_BACKUP/INFO'),
00601             new convert_path('info_details', '/MOODLE_BACKUP/INFO/DETAILS'),
00602             new convert_path('info_details_mod', '/MOODLE_BACKUP/INFO/DETAILS/MOD'),
00603             new convert_path('info_details_mod_instance', '/MOODLE_BACKUP/INFO/DETAILS/MOD/INSTANCES/INSTANCE'),
00604         );
00605     }
00606 
00610     public function process_info($data) {
00611         $this->converter->set_stash('backup_info', $data);
00612     }
00613 
00617     public function process_info_details_mod($data) {
00618         $this->currentmod = $data;
00619         $this->currentmod['instances'] = array();
00620     }
00621 
00625     public function process_info_details_mod_instance($data) {
00626         $this->currentmod['instances'][$data['id']] = $data;
00627     }
00628 
00632     public function on_info_details_mod_end($data) {
00633         global $CFG;
00634 
00635         // keep only such modules that seem to have the support for moodle1 implemented
00636         $modname = $this->currentmod['name'];
00637         if (file_exists($CFG->dirroot.'/mod/'.$modname.'/backup/moodle1/lib.php')) {
00638             $this->converter->set_stash('modinfo_'.$modname, $this->currentmod);
00639             $this->modnames[] = $modname;
00640         } else {
00641             $this->log('unsupported activity module', backup::LOG_WARNING, $modname);
00642         }
00643 
00644         $this->currentmod = array();
00645     }
00646 
00650     public function on_info_details_end() {
00651         $this->converter->set_stash('modnameslist', $this->modnames);
00652     }
00653 }
00654 
00655 
00659 class moodle1_course_header_handler extends moodle1_xml_handler {
00660 
00662     protected $course = array();
00663 
00665     protected $courseraw = array();
00666 
00668     protected $category;
00669 
00670     public function get_paths() {
00671         return array(
00672             new convert_path(
00673                 'course_header', '/MOODLE_BACKUP/COURSE/HEADER',
00674                 array(
00675                     'newfields' => array(
00676                         'summaryformat'          => 1,
00677                         'legacyfiles'            => 2,
00678                         'requested'              => 0, // @todo not really new, but maybe never backed up?
00679                         'restrictmodules'        => 0,
00680                         'enablecompletion'       => 0,
00681                         'completionstartonenrol' => 0,
00682                         'completionnotify'       => 0,
00683                         'tags'                   => array(),
00684                         'allowed_modules'        => array(),
00685                     ),
00686                     'dropfields' => array(
00687                         'roles_overrides',
00688                         'roles_assignments',
00689                         'cost',
00690                         'currancy',
00691                         'defaultrole',
00692                         'enrol',
00693                         'enrolenddate',
00694                         'enrollable',
00695                         'enrolperiod',
00696                         'enrolstartdate',
00697                         'expirynotify',
00698                         'expirythreshold',
00699                         'guest',
00700                         'notifystudents',
00701                         'password',
00702                         'student',
00703                         'students',
00704                         'teacher',
00705                         'teachers',
00706                         'metacourse',
00707                     )
00708                 )
00709             ),
00710             new convert_path(
00711                 'course_header_category', '/MOODLE_BACKUP/COURSE/HEADER/CATEGORY',
00712                 array(
00713                     'newfields' => array(
00714                         'description' => null,
00715                     )
00716                 )
00717             ),
00718         );
00719     }
00720 
00726     public function process_course_header($data, $raw) {
00727        $this->course    = array_merge($this->course, $data);
00728        $this->courseraw = array_merge($this->courseraw, $raw);
00729     }
00730 
00731     public function process_course_header_category($data) {
00732         $this->category = $data;
00733     }
00734 
00735     public function on_course_header_end() {
00736 
00737         $contextid = $this->converter->get_contextid(CONTEXT_COURSE);
00738 
00739         // stash the information needed by other handlers
00740         $info = array(
00741             'original_course_id'        => $this->course['id'],
00742             'original_course_fullname'  => $this->course['fullname'],
00743             'original_course_shortname' => $this->course['shortname'],
00744             'original_course_startdate' => $this->course['startdate'],
00745             'original_course_contextid' => $contextid
00746         );
00747         $this->converter->set_stash('original_course_info', $info);
00748 
00749         $this->course['contextid'] = $contextid;
00750         $this->course['category'] = $this->category;
00751 
00752         // migrate files embedded into the course summary and stash their ids
00753         $fileman = $this->converter->get_file_manager($contextid, 'course', 'summary');
00754         $this->course['summary'] = moodle1_converter::migrate_referenced_files($this->course['summary'], $fileman);
00755         $this->converter->set_stash('course_summary_files_ids', $fileman->get_fileids());
00756 
00757         // write course.xml
00758         $this->open_xml_writer('course/course.xml');
00759         $this->write_xml('course', $this->course, array('/course/id', '/course/contextid'));
00760         $this->close_xml_writer();
00761     }
00762 }
00763 
00764 
00768 class moodle1_course_outline_handler extends moodle1_xml_handler {
00769 
00771     protected $coursecontents = array();
00772 
00774     protected $currentsection;
00775 
00779     public function get_paths() {
00780         return array(
00781             new convert_path('course_sections', '/MOODLE_BACKUP/COURSE/SECTIONS'),
00782             new convert_path(
00783                 'course_section', '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION',
00784                 array(
00785                     'newfields' => array(
00786                         'name'          => null,
00787                         'summaryformat' => 1,
00788                         'sequence'      => null,
00789                     ),
00790                 )
00791             ),
00792             new convert_path(
00793                 'course_module', '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD',
00794                 array(
00795                     'newfields' => array(
00796                         'completion'                => 0,
00797                         'completiongradeitemnumber' => null,
00798                         'completionview'            => 0,
00799                         'completionexpected'        => 0,
00800                         'availablefrom'             => 0,
00801                         'availableuntil'            => 0,
00802                         'showavailability'          => 0,
00803                         'availability_info'         => array(),
00804                         'visibleold'                => 1,
00805                         'showdescription'           => 0,
00806                     ),
00807                     'dropfields' => array(
00808                         'instance',
00809                         'roles_overrides',
00810                         'roles_assignments',
00811                     ),
00812                     'renamefields' => array(
00813                         'type' => 'modulename',
00814                     ),
00815                 )
00816             ),
00817             new convert_path('course_modules', '/MOODLE_BACKUP/COURSE/MODULES'),
00818             // todo new convert_path('course_module_roles_overrides', '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES'),
00819             // todo new convert_path('course_module_roles_assignments', '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_ASSIGNMENTS'),
00820         );
00821     }
00822 
00823     public function process_course_section($data) {
00824         $this->currentsection = $data;
00825     }
00826 
00831     public function process_course_module($data, $raw) {
00832         global $CFG;
00833 
00834         // check that this type of module should be included in the mbz
00835         $modinfo = $this->converter->get_stash_itemids('modinfo_'.$data['modulename']);
00836         if (empty($modinfo)) {
00837             return;
00838         }
00839 
00840         // add the course module into the course contents list
00841         $this->coursecontents[$data['id']] = array(
00842             'cmid'       => $data['id'],
00843             'instanceid' => $raw['INSTANCE'],
00844             'sectionid'  => $this->currentsection['id'],
00845             'modulename' => $data['modulename'],
00846             'title'      => null
00847         );
00848 
00849         // add the course module id into the section's sequence
00850         if (is_null($this->currentsection['sequence'])) {
00851             $this->currentsection['sequence'] = $data['id'];
00852         } else {
00853             $this->currentsection['sequence'] .= ',' . $data['id'];
00854         }
00855 
00856         // add the sectionid and sectionnumber
00857         $data['sectionid']      = $this->currentsection['id'];
00858         $data['sectionnumber']  = $this->currentsection['number'];
00859 
00860         // generate the module version - this is a bit tricky as this information
00861         // is not present in 1.9 backups. we will use the currently installed version
00862         // whenever we can but that might not be accurate for some modules.
00863         // also there might be problem with modules that are not present at the target
00864         // host...
00865         $versionfile = $CFG->dirroot.'/mod/'.$data['modulename'].'/version.php';
00866         if (file_exists($versionfile)) {
00867             include($versionfile);
00868             $data['version'] = $module->version;
00869         } else {
00870             $data['version'] = null;
00871         }
00872 
00873         // stash the course module info in stashes like 'cminfo_forum' with
00874         // itemid set to the instance id. this is needed so that module handlers
00875         // can later obtain information about the course module and dump it into
00876         // the module.xml file
00877         $this->converter->set_stash('cminfo_'.$data['modulename'], $data, $raw['INSTANCE']);
00878     }
00879 
00883     public function on_course_section_end() {
00884 
00885         // migrate files embedded into the section summary field
00886         $contextid = $this->converter->get_contextid(CONTEXT_COURSE);
00887         $fileman = $this->converter->get_file_manager($contextid, 'course', 'section', $this->currentsection['id']);
00888         $this->currentsection['summary'] = moodle1_converter::migrate_referenced_files($this->currentsection['summary'], $fileman);
00889 
00890         // write section's inforef.xml with the file references
00891         $this->open_xml_writer('sections/section_' . $this->currentsection['id'] . '/inforef.xml');
00892         $this->xmlwriter->begin_tag('inforef');
00893         $this->xmlwriter->begin_tag('fileref');
00894         $fileids = $fileman->get_fileids();
00895         if (is_array($fileids)) {
00896             foreach ($fileids as $fileid) {
00897                 $this->write_xml('file', array('id' => $fileid));
00898             }
00899         }
00900         $this->xmlwriter->end_tag('fileref');
00901         $this->xmlwriter->end_tag('inforef');
00902         $this->close_xml_writer();
00903 
00904         // stash the section info and write section.xml
00905         $this->converter->set_stash('sectioninfo', $this->currentsection, $this->currentsection['id']);
00906         $this->open_xml_writer('sections/section_' . $this->currentsection['id'] . '/section.xml');
00907         $this->write_xml('section', $this->currentsection);
00908         $this->close_xml_writer();
00909         unset($this->currentsection);
00910     }
00911 
00915     public function on_course_sections_end() {
00916         $this->converter->set_stash('coursecontents', $this->coursecontents);
00917     }
00918 
00922     public function on_course_modules_end() {
00923 
00924         foreach ($this->converter->get_stash('modnameslist') as $modname) {
00925             $modinfo = $this->converter->get_stash('modinfo_'.$modname);
00926             foreach ($modinfo['instances'] as $modinstanceid => $modinstance) {
00927                 $cminfo    = $this->converter->get_stash('cminfo_'.$modname, $modinstanceid);
00928                 $directory = 'activities/'.$modname.'_'.$cminfo['id'];
00929 
00930                 // write module.xml
00931                 $this->open_xml_writer($directory.'/module.xml');
00932                 $this->write_xml('module', $cminfo, array('/module/id', '/module/version'));
00933                 $this->close_xml_writer();
00934 
00935                 // write grades.xml
00936                 $this->open_xml_writer($directory.'/grades.xml');
00937                 $this->xmlwriter->begin_tag('activity_gradebook');
00938                 $gradeitems = $this->converter->get_stash_or_default('gradebook_modgradeitem_'.$modname, $modinstanceid, array());
00939                 if (!empty($gradeitems)) {
00940                     $this->xmlwriter->begin_tag('grade_items');
00941                     foreach ($gradeitems as $gradeitem) {
00942                         $this->write_xml('grade_item', $gradeitem, array('/grade_item/id'));
00943                     }
00944                     $this->xmlwriter->end_tag('grade_items');
00945                 }
00946                 $this->write_xml('grade_letters', array()); // no grade_letters in module context in Moodle 1.9
00947                 $this->xmlwriter->end_tag('activity_gradebook');
00948                 $this->close_xml_writer();
00949 
00950                 // todo: write proper roles.xml, for now we just make sure the file is present
00951                 $this->make_sure_xml_exists($directory.'/roles.xml', 'roles');
00952             }
00953         }
00954     }
00955 }
00956 
00957 
00961 class moodle1_roles_definition_handler extends moodle1_xml_handler {
00962 
00966     public function get_paths() {
00967         return array(
00968             new convert_path('roles', '/MOODLE_BACKUP/ROLES'),
00969             new convert_path(
00970                 'roles_role', '/MOODLE_BACKUP/ROLES/ROLE',
00971                 array(
00972                     'newfields' => array(
00973                         'description'   => '',
00974                         'sortorder'     => 0,
00975                         'archetype'     => ''
00976                     )
00977                 )
00978             )
00979         );
00980     }
00981 
00985     public function process_roles_role($data) {
00986 
00987         if (!$this->has_xml_writer()) {
00988             $this->open_xml_writer('roles.xml');
00989             $this->xmlwriter->begin_tag('roles_definition');
00990         }
00991         if (!isset($data['nameincourse'])) {
00992             $data['nameincourse'] = null;
00993         }
00994         $this->write_xml('role', $data, array('role/id'));
00995     }
00996 
01000     public function on_roles_end() {
01001 
01002         if (!$this->has_xml_writer()) {
01003             // no roles defined in moodle.xml so {link self::process_roles_role()}
01004             // was never executed
01005             $this->open_xml_writer('roles.xml');
01006             $this->write_xml('roles_definition', array());
01007 
01008         } else {
01009             // some roles were dumped into the file, let us close their wrapper now
01010             $this->xmlwriter->end_tag('roles_definition');
01011         }
01012         $this->close_xml_writer();
01013     }
01014 }
01015 
01016 
01020 class moodle1_question_bank_handler extends moodle1_xml_handler {
01021 
01023     protected $currentcategory = null;
01024 
01026     protected $currentcategoryraw = null;
01027 
01029     protected $fileman = null;
01030 
01032     private $currentcategorywritten = false;
01033 
01035     private $questionswrapperwritten = false;
01036 
01038     private $qtypehandlers = null;
01039 
01043     public function get_paths() {
01044 
01045         $paths = array(
01046             new convert_path('question_categories', '/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES'),
01047             new convert_path(
01048                 'question_category', '/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES/QUESTION_CATEGORY',
01049                 array(
01050                     'newfields' => array(
01051                         'infoformat' => 0
01052                     )
01053                 )),
01054             new convert_path('question_category_context', '/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES/QUESTION_CATEGORY/CONTEXT'),
01055             new convert_path('questions', '/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS'),
01056             // the question element must be grouped so we can re-dispatch it to the qtype handler as a whole
01057             new convert_path('question', '/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS/QUESTION', array(), true),
01058         );
01059 
01060         // annotate all question subpaths required by the qtypes subplugins
01061         $subpaths = array();
01062         foreach ($this->get_qtype_handler('*') as $qtypehandler) {
01063             foreach ($qtypehandler->get_question_subpaths() as $subpath) {
01064                 $subpaths[$subpath] = true;
01065             }
01066         }
01067         foreach (array_keys($subpaths) as $subpath) {
01068             $name = 'subquestion_'.strtolower(str_replace('/', '_', $subpath));
01069             $path = '/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS/QUESTION/'.$subpath;
01070             $paths[] = new convert_path($name, $path);
01071         }
01072 
01073         return $paths;
01074     }
01075 
01079     public function on_question_categories_start() {
01080         $this->open_xml_writer('questions.xml');
01081         $this->xmlwriter->begin_tag('question_categories');
01082         if (is_null($this->fileman)) {
01083             $this->fileman = $this->converter->get_file_manager();
01084         }
01085     }
01086 
01090     public function on_question_category_start() {
01091         $this->currentcategory         = array();
01092         $this->currentcategoryraw      = array();
01093         $this->currentcategorywritten  = false;
01094         $this->questionswrapperwritten = false;
01095     }
01096 
01103     public function process_question_category($data, $raw) {
01104         $this->currentcategory    = array_merge($this->currentcategory, $data);
01105         $this->currentcategoryraw = array_merge($this->currentcategoryraw, $raw);
01106     }
01107 
01111     public function process_question_category_context($data) {
01112 
01113         switch ($data['level']) {
01114         case 'module':
01115             $this->currentcategory['contextid'] = $this->converter->get_contextid(CONTEXT_MODULE, $data['instance']);
01116             $this->currentcategory['contextlevel'] = CONTEXT_MODULE;
01117             $this->currentcategory['contextinstanceid'] = $data['instance'];
01118             break;
01119         case 'course':
01120             $originalcourseinfo = $this->converter->get_stash('original_course_info');
01121             $originalcourseid   = $originalcourseinfo['original_course_id'];
01122             $this->currentcategory['contextid'] = $this->converter->get_contextid(CONTEXT_COURSE);
01123             $this->currentcategory['contextlevel'] = CONTEXT_COURSE;
01124             $this->currentcategory['contextinstanceid'] = $originalcourseid;
01125             break;
01126         case 'coursecategory':
01127             // this is a bit hacky. the source moodle.xml defines COURSECATEGORYLEVEL as a distance
01128             // of the course category (1 = parent category, 2 = grand-parent category etc). We pretend
01129             // that this level*10 is the id of that category and create an artifical contextid for it
01130             $this->currentcategory['contextid'] = $this->converter->get_contextid(CONTEXT_COURSECAT, $data['coursecategorylevel'] * 10);
01131             $this->currentcategory['contextlevel'] = CONTEXT_COURSECAT;
01132             $this->currentcategory['contextinstanceid'] = $data['coursecategorylevel'] * 10;
01133             break;
01134         case 'system':
01135             $this->currentcategory['contextid'] = $this->converter->get_contextid(CONTEXT_SYSTEM);
01136             $this->currentcategory['contextlevel'] = CONTEXT_SYSTEM;
01137             $this->currentcategory['contextinstanceid'] = 0;
01138             break;
01139         }
01140     }
01141 
01150     public function process_question(array $data, array $raw) {
01151         global $CFG;
01152 
01153         // firstly make sure that the category data and the <questions> wrapper are written
01154         // note that because of MDL-27693 we can't use {@link self::process_question_category()}
01155         // and {@link self::on_questions_start()} to do so
01156 
01157         if (empty($this->currentcategorywritten)) {
01158             $this->xmlwriter->begin_tag('question_category', array('id' => $this->currentcategory['id']));
01159             foreach ($this->currentcategory as $name => $value) {
01160                 if ($name === 'id') {
01161                     continue;
01162                 }
01163                 $this->xmlwriter->full_tag($name, $value);
01164             }
01165             $this->currentcategorywritten = true;
01166         }
01167 
01168         if (empty($this->questionswrapperwritten)) {
01169             $this->xmlwriter->begin_tag('questions');
01170             $this->questionswrapperwritten = true;
01171         }
01172 
01173         $qtype = $data['qtype'];
01174 
01175         // replay the upgrade step 2008050700 {@see question_fix_random_question_parents()}
01176         if ($qtype == 'random' and $data['parent'] <> $data['id']) {
01177             $data['parent'] = $data['id'];
01178         }
01179 
01180         // replay the upgrade step 2010080900 and part of 2010080901
01181         $data['generalfeedbackformat'] = $data['questiontextformat'];
01182         $data['oldquestiontextformat'] = $data['questiontextformat'];
01183 
01184         if ($CFG->texteditors !== 'textarea') {
01185             $data['questiontext'] = text_to_html($data['questiontext'], false, false, true);
01186             $data['questiontextformat'] = FORMAT_HTML;
01187             $data['generalfeedback'] = text_to_html($data['generalfeedback'], false, false, true);
01188             $data['generalfeedbackformat'] = FORMAT_HTML;
01189         }
01190 
01191         // replay the upgrade step 2010080901 - updating question image
01192         if (!empty($data['image'])) {
01193             $textlib = textlib_get_instance();
01194             if ($textlib->substr($textlib->strtolower($data['image']), 0, 7) == 'http://') {
01195                 // it is a link, appending to existing question text
01196                 $data['questiontext'] .= ' <img src="' . $data['image'] . '" />';
01197 
01198             } else {
01199                 // it is a file in course_files
01200                 $filename = basename($data['image']);
01201                 $filepath = dirname($data['image']);
01202                 if (empty($filepath) or $filepath == '.' or $filepath == '/') {
01203                     $filepath = '/';
01204                 } else {
01205                     // append /
01206                     $filepath = '/'.trim($filepath, './@#$ ').'/';
01207                 }
01208 
01209                 if (file_exists($this->converter->get_tempdir_path().'/course_files'.$filepath.$filename)) {
01210                     $this->fileman->contextid = $this->currentcategory['contextid'];
01211                     $this->fileman->component = 'question';
01212                     $this->fileman->filearea  = 'questiontext';
01213                     $this->fileman->itemid    = $data['id'];
01214                     $this->fileman->migrate_file('course_files'.$filepath.$filename, '/', $filename);
01215                     // note this is slightly different from the upgrade code as we put the file into the
01216                     // root folder here. this makes our life easier as we do not need to create all the
01217                     // directories within the specified filearea/itemid
01218                     $data['questiontext'] .= ' <img src="@@PLUGINFILE@@/' . $filename . '" />';
01219 
01220                 } else {
01221                     $this->log('question file not found', backup::LOG_WARNING, array($data['id'], $filepath.$filename));
01222                 }
01223             }
01224         }
01225         unset($data['image']);
01226 
01227         // replay the upgrade step 2011060301 - Rename field defaultgrade on table question to defaultmark
01228         $data['defaultmark'] = $data['defaultgrade'];
01229 
01230         // write the common question data
01231         $this->xmlwriter->begin_tag('question', array('id' => $data['id']));
01232         foreach (array(
01233             'parent', 'name', 'questiontext', 'questiontextformat',
01234             'generalfeedback', 'generalfeedbackformat', 'defaultmark',
01235             'penalty', 'qtype', 'length', 'stamp', 'version', 'hidden',
01236             'timecreated', 'timemodified', 'createdby', 'modifiedby'
01237         ) as $fieldname) {
01238             if (!array_key_exists($fieldname, $data)) {
01239                 throw new moodle1_convert_exception('missing_common_question_field', $fieldname);
01240             }
01241             $this->xmlwriter->full_tag($fieldname, $data[$fieldname]);
01242         }
01243         // unless we know that the given qtype does not append any own structures,
01244         // give the handler a chance to do so now
01245         if (!in_array($qtype, array('description', 'random'))) {
01246             $handler = $this->get_qtype_handler($qtype);
01247             if ($handler === false) {
01248                 $this->log('question type converter not found', backup::LOG_ERROR, $qtype);
01249 
01250             } else {
01251                 $this->xmlwriter->begin_tag('plugin_qtype_'.$qtype.'_question');
01252                 $handler->use_xml_writer($this->xmlwriter);
01253                 $handler->process_question($data, $raw);
01254                 $this->xmlwriter->end_tag('plugin_qtype_'.$qtype.'_question');
01255             }
01256         }
01257 
01258         $this->xmlwriter->end_tag('question');
01259     }
01260 
01264     public function on_questions_end() {
01265         $this->xmlwriter->end_tag('questions');
01266     }
01267 
01272     public function on_question_category_end() {
01273         // make sure that the category data were written by {@link self::process_question()}
01274         // if not, write it now. this may happen when the current category does not contain any
01275         // questions so the subpaths is missing completely
01276         if (empty($this->currentcategorywritten)) {
01277             $this->write_xml('question_category', $this->currentcategory, array('/question_category/id'));
01278         } else {
01279             $this->xmlwriter->end_tag('question_category');
01280         }
01281         $this->converter->set_stash('question_categories', $this->currentcategory, $this->currentcategory['id']);
01282     }
01283 
01287     public function on_question_categories_end() {
01288         $this->xmlwriter->end_tag('question_categories');
01289         $this->close_xml_writer();
01290     }
01291 
01302     protected function get_qtype_handler($qtype) {
01303 
01304         if (is_null($this->qtypehandlers)) {
01305             // initialize the list of qtype handler instances
01306             $this->qtypehandlers = array();
01307             foreach (get_plugin_list('qtype') as $qtypename => $qtypelocation) {
01308                 $filename = $qtypelocation.'/backup/moodle1/lib.php';
01309                 if (file_exists($filename)) {
01310                     $classname = 'moodle1_qtype_'.$qtypename.'_handler';
01311                     require_once($filename);
01312                     if (!class_exists($classname)) {
01313                         throw new moodle1_convert_exception('missing_handler_class', $classname);
01314                     }
01315                     $this->log('registering handler', backup::LOG_DEBUG, $classname, 2);
01316                     $this->qtypehandlers[$qtypename] = new $classname($this, $qtypename);
01317                 }
01318             }
01319         }
01320 
01321         if ($qtype === '*') {
01322             return $this->qtypehandlers;
01323 
01324         } else if (isset($this->qtypehandlers[$qtype])) {
01325             return $this->qtypehandlers[$qtype];
01326 
01327         } else {
01328             return false;
01329         }
01330     }
01331 }
01332 
01333 
01337 class moodle1_scales_handler extends moodle1_handler {
01338 
01340     protected $fileman = null;
01341 
01345     public function get_paths() {
01346         return array(
01347             new convert_path('scales', '/MOODLE_BACKUP/COURSE/SCALES'),
01348             new convert_path(
01349                 'scale', '/MOODLE_BACKUP/COURSE/SCALES/SCALE',
01350                 array(
01351                     'renamefields' => array(
01352                         'scaletext' => 'scale',
01353                     ),
01354                     'addfields' => array(
01355                         'descriptionformat' => 0,
01356                     )
01357                 )
01358             ),
01359         );
01360     }
01361 
01365     public function on_scales_start() {
01366         $syscontextid  = $this->converter->get_contextid(CONTEXT_SYSTEM);
01367         $this->fileman = $this->converter->get_file_manager($syscontextid, 'grade', 'scale');
01368     }
01369 
01377     public function process_scale(array $data, array $raw) {
01378         global $CFG;
01379 
01380         // replay upgrade step 2009110400
01381         if ($CFG->texteditors !== 'textarea') {
01382             $data['description'] = text_to_html($data['description'], false, false, true);
01383             $data['descriptionformat'] = FORMAT_HTML;
01384         }
01385 
01386         // convert course files embedded into the scale description field
01387         $this->fileman->itemid = $data['id'];
01388         $data['description'] = moodle1_converter::migrate_referenced_files($data['description'], $this->fileman);
01389 
01390         // stash the scale
01391         $this->converter->set_stash('scales', $data, $data['id']);
01392     }
01393 }
01394 
01395 
01399 class moodle1_outcomes_handler extends moodle1_xml_handler {
01400 
01402     protected $fileman = null;
01403 
01407     public function get_paths() {
01408         return array(
01409             new convert_path('gradebook_grade_outcomes', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_OUTCOMES'),
01410             new convert_path(
01411                 'gradebook_grade_outcome', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_OUTCOMES/GRADE_OUTCOME',
01412                 array(
01413                     'addfields' => array(
01414                         'descriptionformat' => FORMAT_MOODLE,
01415                     ),
01416                 )
01417             ),
01418         );
01419     }
01420 
01424     public function on_gradebook_grade_outcomes_start() {
01425 
01426         $syscontextid  = $this->converter->get_contextid(CONTEXT_SYSTEM);
01427         $this->fileman = $this->converter->get_file_manager($syscontextid, 'grade', 'outcome');
01428 
01429         $this->open_xml_writer('outcomes.xml');
01430         $this->xmlwriter->begin_tag('outcomes_definition');
01431     }
01432 
01436     public function process_gradebook_grade_outcome(array $data, array $raw) {
01437         global $CFG;
01438 
01439         // replay the upgrade step 2009110400
01440         if ($CFG->texteditors !== 'textarea') {
01441             $data['description']       = text_to_html($data['description'], false, false, true);
01442             $data['descriptionformat'] = FORMAT_HTML;
01443         }
01444 
01445         // convert course files embedded into the outcome description field
01446         $this->fileman->itemid = $data['id'];
01447         $data['description'] = moodle1_converter::migrate_referenced_files($data['description'], $this->fileman);
01448 
01449         // write the outcome data
01450         $this->write_xml('outcome', $data, array('/outcome/id'));
01451 
01452         return $data;
01453     }
01454 
01458     public function on_gradebook_grade_outcomes_end() {
01459         $this->xmlwriter->end_tag('outcomes_definition');
01460         $this->close_xml_writer();
01461     }
01462 }
01463 
01464 
01468 class moodle1_gradebook_handler extends moodle1_xml_handler {
01469 
01471     protected $categoryparent = array();
01472 
01476     public function get_paths() {
01477         return array(
01478             new convert_path('gradebook', '/MOODLE_BACKUP/COURSE/GRADEBOOK'),
01479             new convert_path('gradebook_grade_letter', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_LETTERS/GRADE_LETTER'),
01480             new convert_path(
01481                 'gradebook_grade_category', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_CATEGORIES/GRADE_CATEGORY',
01482                 array(
01483                     'addfields' => array(
01484                         'hidden' => 0,  // upgrade step 2010011200
01485                     ),
01486                 )
01487             ),
01488             new convert_path('gradebook_grade_item', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_ITEMS/GRADE_ITEM'),
01489             new convert_path('gradebook_grade_item_grades', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_ITEMS/GRADE_ITEM/GRADE_GRADES'),
01490         );
01491     }
01492 
01500     public function on_gradebook_start() {
01501         $this->categoryparent = array();
01502     }
01503 
01510     public function process_gradebook_grade_letter(array $data, array $raw) {
01511         $this->converter->set_stash('gradebook_gradeletter', $data, $data['id']);
01512     }
01513 
01517     public function process_gradebook_grade_category(array $data, array $raw) {
01518         $this->categoryparent[$data['id']] = $data['parent'];
01519         $this->converter->set_stash('gradebook_gradecategory', $data, $data['id']);
01520     }
01521 
01525     public function process_gradebook_grade_item(array $data, array $raw) {
01526 
01527         // here we use get_nextid() to get a nondecreasing sequence
01528         $data['sortorder'] = $this->converter->get_nextid();
01529 
01530         if ($data['itemtype'] === 'mod') {
01531             return $this->process_mod_grade_item($data, $raw);
01532 
01533         } else if (in_array($data['itemtype'], array('manual', 'course', 'category'))) {
01534             return $this->process_nonmod_grade_item($data, $raw);
01535 
01536         } else {
01537             $this->log('unsupported grade_item type', backup::LOG_ERROR, $data['itemtype']);
01538         }
01539     }
01540 
01544     protected function process_mod_grade_item(array $data, array $raw) {
01545 
01546         $stashname   = 'gradebook_modgradeitem_'.$data['itemmodule'];
01547         $stashitemid = $data['iteminstance'];
01548         $gradeitems  = $this->converter->get_stash_or_default($stashname, $stashitemid, array());
01549 
01550         // typically there will be single item with itemnumber 0
01551         $gradeitems[$data['itemnumber']] = $data;
01552 
01553         $this->converter->set_stash($stashname, $gradeitems, $stashitemid);
01554 
01555         return $data;
01556     }
01557 
01561     protected function process_nonmod_grade_item(array $data, array $raw) {
01562 
01563         $stashname   = 'gradebook_nonmodgradeitem';
01564         $stashitemid = $data['id'];
01565         $this->converter->set_stash($stashname, $data, $stashitemid);
01566 
01567         return $data;
01568     }
01569 
01573     public function on_gradebook_grade_item_grades_start() {
01574     }
01575 
01579     public function on_gradebook_end() {
01580 
01581         $this->open_xml_writer('gradebook.xml');
01582         $this->xmlwriter->begin_tag('gradebook');
01583         $this->write_grade_categories();
01584         $this->write_grade_items();
01585         $this->write_grade_letters();
01586         $this->xmlwriter->end_tag('gradebook');
01587         $this->close_xml_writer();
01588     }
01589 
01593     protected function write_grade_categories() {
01594 
01595         $this->xmlwriter->begin_tag('grade_categories');
01596         foreach ($this->converter->get_stash_itemids('gradebook_gradecategory') as $gradecategoryid) {
01597             $gradecategory = $this->converter->get_stash('gradebook_gradecategory', $gradecategoryid);
01598             $path = $this->calculate_category_path($gradecategoryid);
01599             $gradecategory['depth'] = count($path);
01600             $gradecategory['path']  = '/'.implode('/', $path).'/';
01601             $this->write_xml('grade_category', $gradecategory, array('/grade_category/id'));
01602         }
01603         $this->xmlwriter->end_tag('grade_categories');
01604     }
01605 
01615     protected function calculate_category_path($categoryid) {
01616 
01617         if (!array_key_exists($categoryid, $this->categoryparent)) {
01618             throw new moodle1_convert_exception('gradebook_unknown_categoryid', null, $categoryid);
01619         }
01620 
01621         $path = array($categoryid);
01622         $parent = $this->categoryparent[$categoryid];
01623         while (!is_null($parent)) {
01624             array_unshift($path, $parent);
01625             $parent = $this->categoryparent[$parent];
01626             if (in_array($parent, $path)) {
01627                 throw new moodle1_convert_exception('circular_reference_in_categories_tree');
01628             }
01629         }
01630 
01631         return $path;
01632     }
01633 
01637     protected function write_grade_items() {
01638 
01639         $this->xmlwriter->begin_tag('grade_items');
01640         foreach ($this->converter->get_stash_itemids('gradebook_nonmodgradeitem') as $gradeitemid) {
01641             $gradeitem = $this->converter->get_stash('gradebook_nonmodgradeitem', $gradeitemid);
01642             $this->write_xml('grade_item', $gradeitem, array('/grade_item/id'));
01643         }
01644         $this->xmlwriter->end_tag('grade_items');
01645     }
01646 
01650     protected function write_grade_letters() {
01651 
01652         $this->xmlwriter->begin_tag('grade_letters');
01653         foreach ($this->converter->get_stash_itemids('gradebook_gradeletter') as $gradeletterid) {
01654             $gradeletter = $this->converter->get_stash('gradebook_gradeletter', $gradeletterid);
01655             $this->write_xml('grade_letter', $gradeletter, array('/grade_letter/id'));
01656         }
01657         $this->xmlwriter->end_tag('grade_letters');
01658     }
01659 }
01660 
01661 
01665 abstract class moodle1_plugin_handler extends moodle1_xml_handler {
01666 
01668     protected $plugintype;
01669 
01671     protected $pluginname;
01672 
01678     public function __construct(moodle1_converter $converter, $plugintype, $pluginname) {
01679 
01680         parent::__construct($converter);
01681         $this->plugintype = $plugintype;
01682         $this->pluginname = $pluginname;
01683     }
01684 
01690     public function get_component_name() {
01691         return $this->plugintype.'_'.$this->pluginname;
01692     }
01693 }
01694 
01695 
01699 abstract class moodle1_qtype_handler extends moodle1_plugin_handler {
01700 
01702     protected $qbankhandler;
01703 
01710     public function get_question_subpaths() {
01711         return array();
01712     }
01713 
01720     public function process_question(array $data, array $raw) {
01721     }
01722 
01731     protected function write_answers(array $answers, $qtype) {
01732 
01733         $this->xmlwriter->begin_tag('answers');
01734         foreach ($answers as $elementname => $elements) {
01735             foreach ($elements as $element) {
01736                 $answer = $this->convert_answer($element, $qtype);
01737                 $this->write_xml('answer', $answer, array('/answer/id'));
01738             }
01739         }
01740         $this->xmlwriter->end_tag('answers');
01741     }
01742 
01748     protected function write_numerical_units(array $numericalunits) {
01749 
01750         $this->xmlwriter->begin_tag('numerical_units');
01751         foreach ($numericalunits as $elementname => $elements) {
01752             foreach ($elements as $element) {
01753                 $element['id'] = $this->converter->get_nextid();
01754                 $this->write_xml('numerical_unit', $element, array('/numerical_unit/id'));
01755             }
01756         }
01757         $this->xmlwriter->end_tag('numerical_units');
01758     }
01759 
01766     protected function write_numerical_options(array $numericaloption) {
01767 
01768         $this->xmlwriter->begin_tag('numerical_options');
01769         if (!empty($numericaloption)) {
01770             $this->write_xml('numerical_option', $numericaloption, array('/numerical_option/id'));
01771         }
01772         $this->xmlwriter->end_tag('numerical_options');
01773     }
01774 
01784     protected function get_default_numerical_options($oldquestiontextformat, $units) {
01785         global $CFG;
01786 
01787         // replay the upgrade step 2009100100 - new table
01788         $options = array(
01789             'id'                 => $this->converter->get_nextid(),
01790             'instructions'       => null,
01791             'instructionsformat' => 0,
01792             'showunits'          => 0,
01793             'unitsleft'          => 0,
01794             'unitgradingtype'    => 0,
01795             'unitpenalty'        => 0.1
01796         );
01797 
01798         // replay the upgrade step 2009100101
01799         if ($CFG->texteditors !== 'textarea' and $oldquestiontextformat == FORMAT_MOODLE) {
01800             $options['instructionsformat'] = FORMAT_HTML;
01801         } else {
01802             $options['instructionsformat'] = $oldquestiontextformat;
01803         }
01804 
01805         // Set a good default, depending on whether there are any units defined.
01806         if (empty($units)) {
01807             $options['showunits'] = 3;
01808         }
01809 
01810         return $options;
01811     }
01812 
01818     protected function write_dataset_definitions(array $datasetdefinitions) {
01819 
01820         $this->xmlwriter->begin_tag('dataset_definitions');
01821         foreach ($datasetdefinitions as $datasetdefinition) {
01822             $this->xmlwriter->begin_tag('dataset_definition', array('id' => $this->converter->get_nextid()));
01823             foreach (array('category', 'name', 'type', 'options', 'itemcount') as $element) {
01824                 $this->xmlwriter->full_tag($element, $datasetdefinition[$element]);
01825             }
01826             $this->xmlwriter->begin_tag('dataset_items');
01827             if (!empty($datasetdefinition['dataset_items']['dataset_item'])) {
01828                 foreach ($datasetdefinition['dataset_items']['dataset_item'] as $datasetitem) {
01829                     $datasetitem['id'] = $this->converter->get_nextid();
01830                     $this->write_xml('dataset_item', $datasetitem, array('/dataset_item/id'));
01831                 }
01832             }
01833             $this->xmlwriter->end_tag('dataset_items');
01834             $this->xmlwriter->end_tag('dataset_definition');
01835         }
01836         $this->xmlwriter->end_tag('dataset_definitions');
01837     }
01838 
01840 
01841     public function __construct(moodle1_question_bank_handler $qbankhandler, $qtype) {
01842 
01843         parent::__construct($qbankhandler->get_converter(), 'qtype', $qtype);
01844         $this->qbankhandler = $qbankhandler;
01845     }
01846 
01850     final public function get_paths() {
01851         throw new moodle1_convert_exception('qtype_handler_get_paths');
01852     }
01853 
01857     final protected function open_xml_writer() {
01858         throw new moodle1_convert_exception('opening_xml_writer_forbidden');
01859     }
01860 
01864     final protected function close_xml_writer() {
01865         throw new moodle1_convert_exception('opening_xml_writer_forbidden');
01866     }
01867 
01873     public function use_xml_writer(xml_writer $xmlwriter) {
01874         $this->xmlwriter = $xmlwriter;
01875     }
01876 
01886     private function convert_answer(array $old, $qtype) {
01887         global $CFG;
01888 
01889         $new                    = array();
01890         $new['id']              = $old['id'];
01891         $new['answertext']      = $old['answer_text'];
01892         $new['answerformat']    = 0;   // upgrade step 2010080900
01893         $new['fraction']        = $old['fraction'];
01894         $new['feedback']        = $old['feedback'];
01895         $new['feedbackformat']  = 0;   // upgrade step 2010080900
01896 
01897         // replay upgrade step 2010080901
01898         if ($qtype !== 'multichoice') {
01899             $new['answerformat'] = FORMAT_PLAIN;
01900         } else {
01901             $new['answerformat'] = FORMAT_MOODLE;
01902         }
01903 
01904         if ($CFG->texteditors !== 'textarea') {
01905             if ($qtype == 'essay') {
01906                 $new['feedback'] = text_to_html($new['feedback'], false, false, true);
01907             }
01908             $new['feedbackformat'] = FORMAT_HTML;
01909 
01910         } else {
01911             $new['feedbackformat'] = FORMAT_MOODLE;
01912         }
01913 
01914         return $new;
01915     }
01916 }
01917 
01918 
01922 abstract class moodle1_mod_handler extends moodle1_plugin_handler {
01923 
01929     public function get_modname() {
01930         return $this->pluginname;
01931     }
01932 
01943     protected function get_cminfo($instance, $modname = null) {
01944 
01945         if (is_null($modname)) {
01946             $modname = $this->pluginname;
01947         }
01948         return $this->converter->get_stash('cminfo_'.$modname, $instance);
01949     }
01950 }
01951 
01952 
01956 abstract class moodle1_resource_successor_handler extends moodle1_mod_handler {
01957 
01964     final public function get_paths() {
01965         return array();
01966     }
01967 
01976     public function process_legacy_resource(array $data, array $raw) {
01977     }
01978 
01984     public function on_legacy_resource_end(array $data) {
01985     }
01986 }
01987 
01991 abstract class moodle1_block_handler extends moodle1_plugin_handler {
01992 
01993 }
01994 
01995 
01999 abstract class moodle1_submod_handler extends moodle1_plugin_handler {
02000 
02002     protected $parenthandler;
02003 
02009     public function __construct(moodle1_mod_handler $parenthandler, $subplugintype, $subpluginname) {
02010         $this->parenthandler = $parenthandler;
02011         parent::__construct($parenthandler->converter, $subplugintype, $subpluginname);
02012     }
02013 
02022     final public function get_paths() {
02023         return array();
02024     }
02025 }
 All Data Structures Namespaces Files Functions Variables Enumerations