From e85a924fec54a3c4aaea019aedb258a07bdc847c Mon Sep 17 00:00:00 2001 From: skodak Date: Sat, 30 Aug 2008 18:54:57 +0000 Subject: [PATCH] MDL-15635 DTL refactoring, adding release field to xml file --- admin/dbtransfer/exportlib.php | 380 ----------------------- admin/dbtransfer/importlib.php | 351 --------------------- admin/dbtransfer/lib.php | 35 --- lib/dtl/database_exporter.php | 136 ++++++++ lib/dtl/database_importer.php | 165 ++++++++++ lib/dtl/database_mover.php | 73 +++++ {admin/dbtransfer => lib/dtl}/dbdata.xsd | 19 +- lib/dtl/file_xml_database_exporter.php | 51 +++ lib/dtl/file_xml_database_importer.php | 45 +++ lib/dtl/string_xml_database_exporter.php | 37 +++ lib/dtl/string_xml_database_importer.php | 37 +++ lib/dtl/xml_database_exporter.php | 74 +++++ lib/dtl/xml_database_importer.php | 133 ++++++++ lib/dtllib.php | 63 ++++ 14 files changed, 825 insertions(+), 774 deletions(-) delete mode 100644 admin/dbtransfer/exportlib.php delete mode 100644 admin/dbtransfer/importlib.php delete mode 100644 admin/dbtransfer/lib.php create mode 100644 lib/dtl/database_exporter.php create mode 100644 lib/dtl/database_importer.php create mode 100644 lib/dtl/database_mover.php rename {admin/dbtransfer => lib/dtl}/dbdata.xsd (75%) create mode 100644 lib/dtl/file_xml_database_exporter.php create mode 100644 lib/dtl/file_xml_database_importer.php create mode 100644 lib/dtl/string_xml_database_exporter.php create mode 100644 lib/dtl/string_xml_database_importer.php create mode 100644 lib/dtl/xml_database_exporter.php create mode 100644 lib/dtl/xml_database_importer.php create mode 100644 lib/dtllib.php diff --git a/admin/dbtransfer/exportlib.php b/admin/dbtransfer/exportlib.php deleted file mode 100644 index c8bffc0638..0000000000 --- a/admin/dbtransfer/exportlib.php +++ /dev/null @@ -1,380 +0,0 @@ -mdb = $mdb; - $this->manager = $mdb->get_manager(); - $this->schema = $schema; - $this->check_schema = $check_schema; - } - - /** - * Callback function. Should be called only once database per export - * operation, before any other export operations. Subclasses should export - * basic database information (version and timestamp). - * - * @param float $version the version of the system which generating the data - * @param string $timestamp the timestamp of the data (in ISO 8601) format. - * @param string $description a user description of the data. - * @return void - */ - public function begin_database_export($version, $timestamp, $description) { - } - - /** - * Callback function. Should be called only once per table export operation, - * before any other table export operations. Subclasses should export - * basic database information (name and schema's hash). - * - * @param xmldb_table $table - XMLDB object for the exported table - * @return void - */ - public function begin_table_export(xmldb_table $table) { - } - - /** - * Callback function. Should be called only once per table export operation, - * after all other table export operations. - * - * @param xmldb_table $table - XMLDB object for the exported table - */ - public function finish_table_export(xmldb_table $table) { - } - - /** - * Callback function. Should be called only once database per export - * operation, after all database export operations. - */ - public function finish_database_export() { - } - - /** - * Callback function. Should be called only once per record export operation, - * only between @see begin_table_export and @see finish_table_export calls. - * It will insert table data. Subclasses should export basic record - * information (data values). - * - * @param xmldb_table $table - XMLDB object of the table from which data was retrived - * @param object $data - data object (fields and values from record) - * @return void - */ - public abstract function export_table_data(xmldb_table $table, $data); - - /** - * Generic method to export the database. It checks the schema (if - * @see $check_schema is true), queries the database and calls - * appropiate callbacks. - * - * @exception export_exception if any checking (e.g. database schema) fails - * - * @param string $description a user description of the data. - */ - public function export_database($description=null) { - global $CFG; - - if ($this->check_schema && $this->manager->check_database_schema($this->schema)) { - //TODO put message in error lang - throw new export_exception('XMLDB schema does not match database schema.'); - } - $tables = $this->schema->getTables(); - $this->begin_database_export($CFG->version, date('c'), $description); - foreach ($tables as $table) { - $rs = $this->mdb->get_recordset_sql('SELECT * FROM {'.$table->getName().'}'); - //TODO remove this when dml will have exceptions - if (!$rs) { - //TODO put message in error lang - throw new export_exception('An error occured while reading the database.'); - } - $this->begin_table_export($table); - foreach ($rs as $row) { - $this->export_table_data($table, $row); - } - $this->finish_table_export($table); - } - $this->finish_database_export(); - } - -} - -/** - * XML format exporter class. - * Provides logic for writing XML tags and data inside appropiate callbacks. - * Subclasses should define XML data sinks. - */ -abstract class xml_database_exporter extends database_exporter { - /** - * Generic output method. Subclasses should implement it with code specific - * to the target XML sink. - */ - protected abstract function output($text); - - /** - * Callback function. Outputs open XML PI and moodle_database opening tag. - * - * @param float $version the version of the system which generating the data - * @param string $timestamp the timestamp of the data (in ISO 8601) format. - * @param string $description a user description of the data. - * @return void - */ - public function begin_database_export($version, $timestamp, $description) { - $this->output(''); - //TODO add xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" and schema information - $this->output(''); - } - - /** - * Callback function. Outputs table opening tag. - * - * @param xmldb_table $table - XMLDB object for the exported table - * @return void - */ - public function begin_table_export(xmldb_table $table) { - $this->output(''); - } - - /** - * Callback function. Outputs table closing tag. - * - * @param xmldb_table $table - XMLDB object for the exported table - */ - public function finish_table_export(xmldb_table $table) { - $this->output('
'); - } - - /** - * Callback function. Outputs moodle_database closing tag. - */ - public function finish_database_export() { - $this->output('
'); - } - - /** - * Callback function. Outputs record tag with field subtags and data. - * - * @param xmldb_table $table - XMLDB object of the table from which data was retrived - * @param object $data - data object (fields and values from record) - * @return void - */ - public function export_table_data(xmldb_table $table, $data) { - $this->output(''); - foreach ($data as $key => $value) { - if (is_null($value)) { - $this->output(''); - } else { - $this->output(''.htmlspecialchars($value, ENT_NOQUOTES).''); - } - } - $this->output(''); - } -} - -/** - * XML format exporter class to file storage. - */ -class file_xml_database_exporter extends xml_database_exporter { - /** Path to the XML data file. */ - protected $filepath; - /** File descriptor for the output file. */ - protected $file; - - /** - * Object constructor. - * - * @param string $filepath - path to the XML data file. Use null for PHP - * output stream. - * @param moodle_database $mdb Connection to the source database - * @see xml_database_exporter::__construct() - * @param xmldb_structure $schema Source database schema in XMLDB format - * @see xml_database_exporter::__construct() - * @param boolean $check_schema - whether or not to check that XML database - * @see xml_database_exporter::__construct() - */ - function __construct($filepath, moodle_database $mdb, xmldb_structure $schema=null, $check_schema=true) { - parent::__construct($mdb, $schema, $check_schema); - if (is_null($filepath)) { - $filepath = 'php://output'; - } - $this->filepath = $filepath; - } - - /** - * Specific output method for the file XML sink. - */ - protected function output($text) { - fwrite($this->file, $text); - } - - /** - * Specific implementation for file exporting the database: it opens output stream, calls - * superclass @see database_exporter::export_database() and closes output stream. - * - * @exception export_exception if any checking (e.g. database schema) fails - * - * @param string $description a user description of the data. - */ - public function export_database($description=null) { - $this->file = fopen($this->filepath, 'wb'); - parent::export_database($description); - fclose($this->file); - } -} - -/** - * XML format exporter class to memory storage (i.e. a string). - */ -class string_xml_database_exporter extends xml_database_exporter { - /** String with XML data. */ - protected $data; - - /** - * Specific output method for the memory XML sink. - */ - protected function output($text) { - $this->data .= $text; - } - - /** - * Returns the output of the exporters - * @return string XML data from exporter - */ - public function get_output() { - return $this->data; - } - - /** - * Specific implementation for memory exporting the database: it clear the buffer - * and calls superclass @see database_exporter::export_database(). - * - * @exception export_exception if any checking (e.g. database schema) fails - * @param string $description a user description of the data. - * @return void - */ - public function export_database($description=null) { - $this->data = ''; - parent::export_database($description); - } -} - -class database_mover extends database_exporter { - /** Importer object used to transfer data. */ - protected $importer; - - /** - * Object constructor. - * - * @param moodle_database $mdb_target Connection to the target database (a - * @see moodle_database object). - * @param moodle_database $mdb Connection to the source database (a - * @see moodle_database object). - * @param xmldb_structure $schema Source database schema in XMLDB format (a - * @see xmldb_structure object). Use null to load the schema from the - * system's install.xml files. - * @param boolean $check_schema - whether or not to check that XML database - * schema matches the RDBMS database schema before exporting (used by - * @see export_database). - */ - public function __construct(moodle_database $mdb_target, moodle_database $mdb_source, xmldb_structure $schema=null, $check_schema=true) { - parent::__construct($mdb_source, $schema, $check_schema); - $this->importer = new database_importer($mdb_target, $schema, $check_schema); - } - - /** - * Callback function. Calls importer's begin_database_import callback method. - * - * @param float $version the version of the system which generating the data - * @param string $timestamp the timestamp of the data (in ISO 8601) format. - * @param string $description a user description of the data. - * @return void - */ - public function begin_database_export($version, $timestamp, $description) { - $this->importer->init_database(); - $this->importer->begin_database_import($version, $timestamp, $description); - } - - /** - * Callback function. Calls importer's begin_table_import callback method. - * - * @param xmldb_table $table - XMLDB object for the exported table - * @return void - */ - public function begin_table_export(xmldb_table $table) { - $this->importer->begin_table_import($table->getName(), $table->getHash()); - } - - /** - * Callback function. Calls importer's import_table_data callback method. - * - * @param xmldb_table $table - XMLDB object of the table from which data - * was retrived - * @param object $data - data object (fields and values from record) - * @return void - */ - public function export_table_data(xmldb_table $table, $data) { - $this->importer->import_table_data($table->getName(), $data); - } - - /** - * Callback function. Calls importer's finish_table_import callback method. - * @param xmldb_table $table - XMLDB object for the exported table - * @return void - */ - public function finish_table_export(xmldb_table $table) { - $this->importer->finish_table_import($table->getName()); - } - - /** - * Callback function. Calls importer's finish_database_import callback method. - * @return void - */ - public function finish_database_export() { - $this->importer->finish_database_import(); - } -} diff --git a/admin/dbtransfer/importlib.php b/admin/dbtransfer/importlib.php deleted file mode 100644 index 3f12ec4fdc..0000000000 --- a/admin/dbtransfer/importlib.php +++ /dev/null @@ -1,351 +0,0 @@ -mdb = $mdb; - $this->manager = $mdb->get_manager(); - $this->schema = $schema; - $this->check_schema = $check_schema; - } - - /** - * Callback function. Should be called only once database per import - * operation, before any database changes are made. It will check the database - * schema if @see check_schema is true - * - * @exception import_exception if any checking (e.g. database schema, Moodle - * version) fails - * - * @param float $version the version of the system which generated the data - * @param string $timestamp the timestamp of the data (in ISO 8601) format. - * @return void - */ - public function begin_database_import($version, $timestamp) { - global $CFG; - - if (round($version, 2) !== round($CFG->version, 2)) { // version might be in decimal format too - //TODO put message in error lang - throw new import_exception('Current Moodle version does not match exported Moodle version.'); - } - if ($this->check_schema && $this->manager->check_database_schema($this->schema)) { - //TODO put message in error lang - throw new import_exception('XMLDB schema does not match database schema.'); - } - $this->mdb->begin_sql(); - } - - /** - * Callback function. Should be called only once per table import operation, - * before any table changes are made. It will delete all table data. - * - * @exception import_exception an unknown table import is attempted - * @exception ddl_table_missing_exception if the table is missing - * - * @param string $tablename - the name of the table that will be imported - * @param string $schemaHash - the hash of the xmldb_table schema of the table - * @return void - */ - public function begin_table_import($tablename, $schemaHash) { - $table = $this->schema->getTable($tablename); - if (is_null($table)) { - //TODO put message in error lang - throw new import_exception('Unknown table in import data'); - } - if ($schemaHash != $table->getHash()) { - throw new import_exception('XMLDB schema does not match database schema.'); - } - // this should not happen, unless someone drops tables after import started - if (!$this->manager->table_exists($table)) { - // in the future, missing tables will be recreated with - //$this->manager->create_table($table); - throw new ddl_table_missing_exception($tablename); - } - $this->mdb->delete_records($tablename); - } - - /** - * Callback function. Should be called only once per table import operation, - * after all table changes are made. It will reset table sequences if any. - * @param string $tablename - * @return void - */ - public function finish_table_import($tablename) { - $table = $this->schema->getTable($tablename); - $fields = $table->getFields(); - foreach ($fields as $field) { - if ($field->getSequence()) { - //TODO make sure that there aren't tables using two sequences (probably there is none) - $this->mdb->reset_sequence($tablename); - return; - } - } - } - - /** - * Callback function. Should be called only once database per import - * operation, after all database changes are made. It will commit changes. - * @return void - */ - public function finish_database_import() { - $this->mdb->commit_sql(); - } - - /** - * Callback function. Should be called only once per record import operation, only - * between @see begin_table_import and @see finish_table_import calls. - * It will insert table data. - * - * @exception dml_exception if data insert operation failed - * - * @param string $tablename - the name of the table in which data will be - * imported - * @param object $data - data object (fields and values will be inserted - * into table) - * @return void - */ - public function import_table_data($tablename, $data) { - $this->mdb->import_record($tablename, $data); - } - - /** - * All subclases must call this - * @return void - */ - public function init_database() { - if (!$this->mdb->get_tables()) { - // not tables yet, time to create all tables - $this->manager->install_from_xmldb_structure($this->schema); - } - } -} - -/** - * XML format importer class (uses SAX for speed and low memory footprint). - * Provides logic for parsing XML data and calling appropiate callbacks. - * Subclasses should define XML data sources. - */ -abstract class xml_database_importer extends database_importer { - /** - * Creates and setups a SAX parser. Subclasses should use this method to - * create the XML parser. - * - * @return resource XML parser resource. - */ - protected function get_parser() { - $parser = xml_parser_create(); - xml_set_object($parser, $this); - xml_set_element_handler($parser, 'tag_open', 'tag_close'); - xml_set_character_data_handler($parser, 'cdata'); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); - return $parser; - } - - /** - * Callback function. Called by the XML parser for opening tags processing. - * - * @param resource $parser XML parser resource. - * @param string $tag name of opening tag - * @param array $attributes set of opening tag XML attributes - * @return void - */ - protected function tag_open($parser, $tag, $attributes) { - switch ($tag) { - case 'moodle_database' : - $this->begin_database_import($attributes['version'], $attributes['timestamp']); - break; - case 'table' : - $this->current_table = $attributes['name']; - $this->begin_table_import($this->current_table, $attributes['schemaHash']); - break; - case 'record' : - $this->current_row = new object(); - break; - case 'field' : - $this->current_field = $attributes['name']; - $this->current_data = @$attributes['value'] == 'null' ? null : ''; - break; - default : - //TODO put message in error lang - throw new import_exception('XML content not valid for import operation.'); - } - } - - /** - * Callback function. Called by the XML parser for closing tags processing. - * - * @param resource $parser XML parser resource. - * @param string $tag name of opening tag - * @return void - */ - protected function tag_close($parser, $tag) { - switch ($tag) { - case 'moodle_database' : - $this->finish_database_import(); - break; - case 'table' : - $this->finish_table_import($this->current_table); - unset ($this->current_table); - break; - case 'record' : - $this->import_table_data($this->current_table, $this->current_row); - unset ($this->current_row); - break; - case 'field' : - $field = $this->current_field; - unset ($this->current_field); - $this->current_row-> $field = $this->current_data; - unset ($this->current_data); - break; - default : - //TODO put message in error lang - throw new import_exception('XML content not valid for import operation.'); - } - } - - /** - * Callback function. Called by the XML parser for character data processing. - * - * @param resource $parser XML parser resource. - * @param string $data character data to be processed - * @return void - */ - protected function cdata($parser, $cdata) { - if (isset($this->current_field)) { - $this->current_data .= $cdata; - } - } - - /** - * Common import method - * @return void - */ - public abstract function import_database(); -} - -/** - * XML format importer class from file storage. - */ -class file_xml_database_importer extends xml_database_importer { - /** Path to the XML data file. */ - protected $filepath; - - /** - * Object constructor. - * - * @param string $filepath - path to the XML data file. Use null for PHP - * input stream. - * @param moodle_database $mdb Connection to the target database - * @see xml_database_importer::__construct() - * @param xmldb_structure $schema Target database schema in XMLDB format - * @see xml_database_importer::__construct() - * @param boolean $check_schema - whether or not to check that XML database - * @see xml_database_importer::__construct() - */ - public function __construct($filepath, moodle_database $mdb, xmldb_structure $schema=null) { - parent::__construct($mdb, $schema); - if (is_null($filepath)) { - $filepath = 'php://input'; - } - $this->filepath = $filepath; - } - - /** - * Common import method: it opens the file storage, creates the parser, feeds - * the XML parser with data, releases the parser and closes the file storage. - * @return void - */ - public function import_database() { - $this->init_database(); - $file = fopen($this->filepath, 'r'); - $parser = $this->get_parser(); - while (($data = fread($file, 65536)) && xml_parse($parser, $data, feof($file))); - xml_parser_free($parser); - fclose($file); - } -} - -/** - * XML format importer class from memory storage (i.e. string). - */ -class string_xml_database_importer extends xml_database_importer { - /** String with XML data. */ - protected $data; - - /** - * Object constructor. - * - * @param string data - string with XML data - * @param moodle_database $mdb Connection to the target database - * @see xml_database_importer::__construct() - * @param xmldb_structure $schema Target database schema in XMLDB format - * @see xml_database_importer::__construct() - * @param boolean $check_schema - whether or not to check that XML database - * @see xml_database_importer::__construct() - */ - public function __construct($data, moodle_database $mdb, xmldb_structure $schema=null) { - parent::__construct($mdb, $schema); - $this->data = $data; - } - - /** - * Common import method: it creates the parser, feeds the XML parser with - * data, releases the parser. - * @return void - */ - public function import_database() { - $this->init_database(); - $parser = $this->get_parser(); - xml_parse($parser, $this->data, true); - xml_parser_free($parser); - } -} diff --git a/admin/dbtransfer/lib.php b/admin/dbtransfer/lib.php deleted file mode 100644 index daf000ee15..0000000000 --- a/admin/dbtransfer/lib.php +++ /dev/null @@ -1,35 +0,0 @@ -libdir.'/xmldb/xmldb_structure.php'; -require_once $CFG->libdir.'/ddl/database_manager.php'; -require_once $CFG->dirroot.'/'.$CFG->admin.'/dbtransfer/exportlib.php'; -require_once $CFG->dirroot.'/'.$CFG->admin.'/dbtransfer/importlib.php'; - -/** - * Exception class for export operations. - * @see moodle_exception - * TODO subclass for specific purposes - */ -class export_exception extends moodle_exception { - function __construct($errorcode, $a=null, $debuginfo=null) { - parent::__construct($errorcode, '', '', $a, $debuginfo); - } -} - -/** - * Exception class for import operations. - * @see moodle_exception - * TODO subclass for specific purposes - */ -class import_exception extends moodle_exception { - function __construct($errorcode, $a=null, $debuginfo=null) { - parent::__construct($errorcode, '', '', $a, $debuginfo); - } -} diff --git a/lib/dtl/database_exporter.php b/lib/dtl/database_exporter.php new file mode 100644 index 0000000000..2c6b573c28 --- /dev/null +++ b/lib/dtl/database_exporter.php @@ -0,0 +1,136 @@ +mdb = $mdb; + $this->manager = $mdb->get_manager(); + $this->schema = $this->manager->get_install_xml_schema(); + $this->check_schema = $check_schema; + } + + /** + * Callback function. Should be called only once database per export + * operation, before any other export operations. Subclasses should export + * basic database information (version and timestamp). + * + * @param float $version the version of the system which generating the data + * @param string $release moodle release info + * @param string $timestamp the timestamp of the data (in ISO 8601) format. + * @param string $description a user description of the data. + * @return void + */ + public abstract function begin_database_export($version, $release, $timestamp, $description); + + /** + * Callback function. Should be called only once per table export operation, + * before any other table export operations. Subclasses should export + * basic database information (name and schema's hash). + * + * @param xmldb_table $table - XMLDB object for the exported table + * @return void + */ + public abstract function begin_table_export(xmldb_table $table); + + /** + * Callback function. Should be called only once per table export operation, + * after all other table export operations. + * + * @param xmldb_table $table - XMLDB object for the exported table + */ + public abstract function finish_table_export(xmldb_table $table); + + /** + * Callback function. Should be called only once database per export + * operation, after all database export operations. + */ + public abstract function finish_database_export(); + + /** + * Callback function. Should be called only once per record export operation, + * only between @see begin_table_export and @see finish_table_export calls. + * It will insert table data. Subclasses should export basic record + * information (data values). + * + * @param xmldb_table $table - XMLDB object of the table from which data was retrived + * @param object $data - data object (fields and values from record) + * @return void + */ + public abstract function export_table_data(xmldb_table $table, $data); + + /** + * Generic method to export the database. It checks the schema (if + * @see $check_schema is true), queries the database and calls + * appropiate callbacks. + * + * @exception export_exception if any checking (e.g. database schema) fails + * + * @param string $description a user description of the data. + */ + public function export_database($description=null) { + global $CFG; + + if ($this->check_schema and $this->manager->check_database_schema($this->schema)) { + //TODO put message in error lang + throw new export_exception('XMLDB schema does not match database schema.'); + } + $tables = $this->schema->getTables(); + $this->begin_database_export($CFG->version, $CFG->release, date('c'), $description); + foreach ($tables as $table) { + $rs = $this->mdb->get_recordset_sql('SELECT * FROM {'.$table->getName().'}'); + //TODO remove this when dml will have exceptions + if (!$rs) { + //TODO put message in error lang + throw new export_exception('An error occured while reading the database.'); + } + $this->begin_table_export($table); + foreach ($rs as $row) { + $this->export_table_data($table, $row); + } + $this->finish_table_export($table); + } + $this->finish_database_export(); + } + +} diff --git a/lib/dtl/database_importer.php b/lib/dtl/database_importer.php new file mode 100644 index 0000000000..24cf79d7d9 --- /dev/null +++ b/lib/dtl/database_importer.php @@ -0,0 +1,165 @@ +mdb = $mdb; + $this->manager = $mdb->get_manager(); + $this->schema = $this->manager->get_install_xml_schema(); + $this->check_schema = $check_schema; + } + + /** + * Callback function. Should be called only once database per import + * operation, before any database changes are made. It will check the database + * schema if @see check_schema is true + * + * @exception import_exception if any checking (e.g. database schema, Moodle + * version) fails + * + * @param float $version the version of the system which generated the data + * @param string $timestamp the timestamp of the data (in ISO 8601) format. + * @return void + */ + public function begin_database_import($version, $timestamp) { + global $CFG; + + if (!$this->mdb->get_tables()) { + // not tables yet, time to create all tables + $this->manager->install_from_xmldb_structure($this->schema); + } + + if (round($version, 2) !== round($CFG->version, 2)) { // version might be in decimal format too + //TODO put message in error lang + throw new import_exception('Current Moodle version does not match exported Moodle version.'); + } + + if ($this->check_schema && $this->manager->check_database_schema($this->schema)) { + //TODO put message in error lang + throw new import_exception('XMLDB schema does not match database schema.'); + } + $this->mdb->begin_sql(); + } + + /** + * Callback function. Should be called only once per table import operation, + * before any table changes are made. It will delete all table data. + * + * @exception import_exception an unknown table import is attempted + * @exception ddl_table_missing_exception if the table is missing + * + * @param string $tablename - the name of the table that will be imported + * @param string $schemaHash - the hash of the xmldb_table schema of the table + * @return void + */ + public function begin_table_import($tablename, $schemaHash) { + if (!$table = $this->schema->getTable($tablename)) { + //TODO put message in error lang + throw new import_exception('Unknown table in import data'); + } + if ($schemaHash != $table->getHash()) { + throw new import_exception('XMLDB schema does not match database schema.'); + } + // this should not happen, unless someone drops tables after import started + if (!$this->manager->table_exists($table)) { + // in the future, missing tables will be recreated with + //$this->manager->create_table($table); + throw new ddl_table_missing_exception($tablename); + } + $this->mdb->delete_records($tablename); + } + + /** + * Callback function. Should be called only once per table import operation, + * after all table changes are made. It will reset table sequences if any. + * @param string $tablename + * @return void + */ + public function finish_table_import($tablename) { + $table = $this->schema->getTable($tablename); + $fields = $table->getFields(); + foreach ($fields as $field) { + if ($field->getSequence()) { + $this->mdb->reset_sequence($tablename); + return; + } + } + } + + /** + * Callback function. Should be called only once database per import + * operation, after all database changes are made. It will commit changes. + * @return void + */ + public function finish_database_import() { + $this->mdb->commit_sql(); + } + + /** + * Callback function. Should be called only once per record import operation, only + * between @see begin_table_import and @see finish_table_import calls. + * It will insert table data. + * + * @exception dml_exception if data insert operation failed + * + * @param string $tablename - the name of the table in which data will be + * imported + * @param object $data - data object (fields and values will be inserted + * into table) + * @return void + */ + public function import_table_data($tablename, $data) { + $this->mdb->import_record($tablename, $data); + } + + /** + * Common import method + * @return void + */ + public function import_database() { + // implement in subclasses + } +} diff --git a/lib/dtl/database_mover.php b/lib/dtl/database_mover.php new file mode 100644 index 0000000000..cdadf243ef --- /dev/null +++ b/lib/dtl/database_mover.php @@ -0,0 +1,73 @@ +importer = new database_importer($mdb_target, $check_schema); + } + + /** + * Callback function. Calls importer's begin_database_import callback method. + * + * @param float $version the version of the system which generating the data + * @param string $timestamp the timestamp of the data (in ISO 8601) format. + * @param string $description a user description of the data. + * @return void + */ + public function begin_database_export($version, $release, $timestamp, $description) { + $this->importer->begin_database_import($version, $timestamp, $description); + } + + /** + * Callback function. Calls importer's begin_table_import callback method. + * + * @param xmldb_table $table - XMLDB object for the exported table + * @return void + */ + public function begin_table_export(xmldb_table $table) { + $this->importer->begin_table_import($table->getName(), $table->getHash()); + } + + /** + * Callback function. Calls importer's import_table_data callback method. + * + * @param xmldb_table $table - XMLDB object of the table from which data + * was retrived + * @param object $data - data object (fields and values from record) + * @return void + */ + public function export_table_data(xmldb_table $table, $data) { + $this->importer->import_table_data($table->getName(), $data); + } + + /** + * Callback function. Calls importer's finish_table_import callback method. + * @param xmldb_table $table - XMLDB object for the exported table + * @return void + */ + public function finish_table_export(xmldb_table $table) { + $this->importer->finish_table_import($table->getName()); + } + + /** + * Callback function. Calls importer's finish_database_import callback method. + * @return void + */ + public function finish_database_export() { + $this->importer->finish_database_import(); + } +} diff --git a/admin/dbtransfer/dbdata.xsd b/lib/dtl/dbdata.xsd similarity index 75% rename from admin/dbtransfer/dbdata.xsd rename to lib/dtl/dbdata.xsd index 6abf66c027..174aaba022 100644 --- a/admin/dbtransfer/dbdata.xsd +++ b/lib/dtl/dbdata.xsd @@ -1,14 +1,17 @@ - - - - - - - - + + + + + + + + + diff --git a/lib/dtl/file_xml_database_exporter.php b/lib/dtl/file_xml_database_exporter.php new file mode 100644 index 0000000000..6fec2e4610 --- /dev/null +++ b/lib/dtl/file_xml_database_exporter.php @@ -0,0 +1,51 @@ +filepath = $filepath; + } + + /** + * Specific output method for the file XML sink. + */ + protected function output($text) { + fwrite($this->file, $text); + } + + /** + * Specific implementation for file exporting the database: it opens output stream, calls + * superclass @see database_exporter::export_database() and closes output stream. + * + * @exception export_exception if any checking (e.g. database schema) fails + * + * @param string $description a user description of the data. + */ + public function export_database($description=null) { + // TODO: add exception if file creation fails + $this->file = fopen($this->filepath, 'wb'); + parent::export_database($description); + fclose($this->file); + } +} diff --git a/lib/dtl/file_xml_database_importer.php b/lib/dtl/file_xml_database_importer.php new file mode 100644 index 0000000000..8d6dfc6309 --- /dev/null +++ b/lib/dtl/file_xml_database_importer.php @@ -0,0 +1,45 @@ +filepath = $filepath; + parent::__construct($mdb, $check_schema); + } + + /** + * Common import method: it opens the file storage, creates the parser, feeds + * the XML parser with data, releases the parser and closes the file storage. + * @return void + */ + public function import_database() { + $file = fopen($this->filepath, 'r'); + $parser = $this->get_parser(); + while ($data = fread($file, 65536)) { + if (!xml_parse($parser, $data, feof($file))) { + //TODO localize + throw new import_exception("XML data not well-formed."); + } + } + xml_parser_free($parser); + fclose($file); + } +} diff --git a/lib/dtl/string_xml_database_exporter.php b/lib/dtl/string_xml_database_exporter.php new file mode 100644 index 0000000000..8464992919 --- /dev/null +++ b/lib/dtl/string_xml_database_exporter.php @@ -0,0 +1,37 @@ +data .= $text; + } + + /** + * Returns the output of the exporters + * @return string XML data from exporter + */ + public function get_output() { + return $this->data; + } + + /** + * Specific implementation for memory exporting the database: it clear the buffer + * and calls superclass @see database_exporter::export_database(). + * + * @exception export_exception if any checking (e.g. database schema) fails + * @param string $description a user description of the data. + * @return void + */ + public function export_database($description=null) { + $this->data = ''; + parent::export_database($description); + } +} diff --git a/lib/dtl/string_xml_database_importer.php b/lib/dtl/string_xml_database_importer.php new file mode 100644 index 0000000000..983d566df5 --- /dev/null +++ b/lib/dtl/string_xml_database_importer.php @@ -0,0 +1,37 @@ +data = $data; + } + + /** + * Common import method: it creates the parser, feeds the XML parser with + * data, releases the parser. + * @return void + */ + public function import_database() { + $parser = $this->get_parser(); + if (!xml_parse($parser, $this->data, true)) { + //TODO localize + throw new import_exception("XML data not well-formed."); + } + xml_parser_free($parser); + } +} diff --git a/lib/dtl/xml_database_exporter.php b/lib/dtl/xml_database_exporter.php new file mode 100644 index 0000000000..e58e3391a6 --- /dev/null +++ b/lib/dtl/xml_database_exporter.php @@ -0,0 +1,74 @@ +output(''); + //TODO add xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" and schema information + $this->output(''); + } + + /** + * Callback function. Outputs table opening tag. + * + * @param xmldb_table $table - XMLDB object for the exported table + * @return void + */ + public function begin_table_export(xmldb_table $table) { + $this->output(''); + } + + /** + * Callback function. Outputs table closing tag. + * + * @param xmldb_table $table - XMLDB object for the exported table + */ + public function finish_table_export(xmldb_table $table) { + $this->output('
'); + } + + /** + * Callback function. Outputs moodle_database closing tag. + */ + public function finish_database_export() { + $this->output('
'); + } + + /** + * Callback function. Outputs record tag with field subtags and data. + * + * @param xmldb_table $table - XMLDB object of the table from which data was retrived + * @param object $data - data object (fields and values from record) + * @return void + */ + public function export_table_data(xmldb_table $table, $data) { + $this->output(''); + foreach ($data as $key => $value) { + if (is_null($value)) { + $this->output(''); + } else { + $this->output(''.htmlspecialchars($value, ENT_NOQUOTES).''); + } + } + $this->output(''); + } +} diff --git a/lib/dtl/xml_database_importer.php b/lib/dtl/xml_database_importer.php new file mode 100644 index 0000000000..4badbc2edb --- /dev/null +++ b/lib/dtl/xml_database_importer.php @@ -0,0 +1,133 @@ +begin_database_import($attributes['version'], $attributes['timestamp']); + break; + case 'table' : + if (isset($this->current_table)) { + throw new import_exception('Unexpected tag in data file.'); + } + if (empty($attributes['name']) || empty($attributes['schemaHash'])) { + throw new import_exception('Missing tag attribute in data file.'); + } + $this->current_table = $attributes['name']; + $this->begin_table_import($this->current_table, $attributes['schemaHash']); + break; + case 'record' : + if (isset($this->current_row) || !isset($this->current_table)) { + throw new import_exception('Unexpected tag in data file.'); + } + $this->current_row = new object(); + break; + case 'field' : + if (isset($this->current_field) || !isset($this->current_row)) { + throw new import_exception('Unexpected tag in data file.'); + } + $this->current_field = $attributes['name']; + $this->current_data = ''; + if (isset($attributes['value']) and $attributes['value'] === 'null') { + $this->current_data_is_null = true; + } else { + $this->current_data_is_null = false; + } + break; + default : + //TODO localize + throw new import_exception('XML content not valid for import operation.'); + } + } + + /** + * Callback function. Called by the XML parser for closing tags processing. + * + * @param resource $parser XML parser resource. + * @param string $tag name of opening tag + * @return void + */ + protected function tag_close($parser, $tag) { + switch ($tag) { + case 'moodle_database' : + $this->finish_database_import(); + break; + + case 'table' : + $this->finish_table_import($this->current_table); + $this->current_table = null; + break; + + case 'record' : + $this->import_table_data($this->current_table, $this->current_row); + $this->current_row = null;; + break; + + case 'field' : + $field = $this->current_field; + if ($this->current_data_is_null) { + $this->current_row->$field = null; + } else { + $this->current_row->$field = $this->current_data; + } + $this->current_field = null; + $this->current_data = null; + $this->current_data_is_null = null; + break; + + default : + //TODO put message in error lang + throw new import_exception('XML content not valid for import operation.'); + } + } + + /** + * Callback function. Called by the XML parser for character data processing. + * + * @param resource $parser XML parser resource. + * @param string $data character data to be processed + * @return void + */ + protected function cdata($parser, $cdata) { + if (isset($this->current_field)) { + $this->current_data .= $cdata; + } + } +} diff --git a/lib/dtllib.php b/lib/dtllib.php new file mode 100644 index 0000000000..d5e8874fc1 --- /dev/null +++ b/lib/dtllib.php @@ -0,0 +1,63 @@ +libdir.'/ddllib.php'); + +require_once($CFG->libdir.'/dtl/database_exporter.php'); +require_once($CFG->libdir.'/dtl/xml_database_exporter.php'); +require_once($CFG->libdir.'/dtl/file_xml_database_exporter.php'); +require_once($CFG->libdir.'/dtl/string_xml_database_exporter.php'); +require_once($CFG->libdir.'/dtl/database_mover.php'); +require_once($CFG->libdir.'/dtl/database_importer.php'); +require_once($CFG->libdir.'/dtl/xml_database_importer.php'); +require_once($CFG->libdir.'/dtl/file_xml_database_importer.php'); +require_once($CFG->libdir.'/dtl/string_xml_database_importer.php'); + +/** + * Exception class for export operations. + * @see moodle_exception + * TODO subclass for specific purposes + */ +class export_exception extends moodle_exception { + function __construct($errorcode, $a=null, $debuginfo=null) { + parent::__construct($errorcode, '', '', $a, $debuginfo); + } +} + +/** + * Exception class for import operations. + * @see moodle_exception + * TODO subclass for specific purposes + */ +class import_exception extends moodle_exception { + function __construct($errorcode, $a=null, $debuginfo=null) { + parent::__construct($errorcode, '', '', $a, $debuginfo); + } +} -- 2.39.5