From 04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa Mon Sep 17 00:00:00 2001 From: Charles Date: Thu, 9 Jan 2020 10:55:03 +0100 Subject: phpmyadmin working --- .../classes/Plugins/Export/ExportCodegen.php | 447 +++ .../libraries/classes/Plugins/Export/ExportCsv.php | 347 +++ .../classes/Plugins/Export/ExportExcel.php | 90 + .../classes/Plugins/Export/ExportHtmlword.php | 670 +++++ .../classes/Plugins/Export/ExportJson.php | 295 ++ .../classes/Plugins/Export/ExportLatex.php | 709 +++++ .../classes/Plugins/Export/ExportMediawiki.php | 386 +++ .../libraries/classes/Plugins/Export/ExportOds.php | 345 +++ .../libraries/classes/Plugins/Export/ExportOdt.php | 813 ++++++ .../libraries/classes/Plugins/Export/ExportPdf.php | 395 +++ .../classes/Plugins/Export/ExportPhparray.php | 259 ++ .../libraries/classes/Plugins/Export/ExportSql.php | 2915 ++++++++++++++++++++ .../classes/Plugins/Export/ExportTexytext.php | 624 +++++ .../libraries/classes/Plugins/Export/ExportXml.php | 593 ++++ .../classes/Plugins/Export/ExportYaml.php | 230 ++ .../classes/Plugins/Export/Helpers/Pdf.php | 855 ++++++ .../Plugins/Export/Helpers/TableProperty.php | 277 ++ .../libraries/classes/Plugins/Export/README | 255 ++ 18 files changed, 10505 insertions(+) create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCodegen.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCsv.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportExcel.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportHtmlword.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportJson.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportLatex.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportMediawiki.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOds.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOdt.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPdf.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPhparray.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportSql.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportTexytext.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportXml.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportYaml.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/Pdf.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/TableProperty.php create mode 100644 srcs/phpmyadmin/libraries/classes/Plugins/Export/README (limited to 'srcs/phpmyadmin/libraries/classes/Plugins/Export') diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCodegen.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCodegen.php new file mode 100644 index 0000000..8a29538 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCodegen.php @@ -0,0 +1,447 @@ +initSpecificVariables(); + $this->setProperties(); + } + + /** + * Initialize the local variables that are used for export CodeGen + * + * @return void + */ + protected function initSpecificVariables() + { + $this->_setCgFormats( + [ + "NHibernate C# DO", + "NHibernate XML", + ] + ); + + $this->_setCgHandlers( + [ + "_handleNHibernateCSBody", + "_handleNHibernateXMLBody", + ] + ); + } + + /** + * Sets the export CodeGen properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('CodeGen'); + $exportPluginProperties->setExtension('cs'); + $exportPluginProperties->setMimeType('text/cs'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new HiddenPropertyItem("structure_or_data"); + $generalOptions->addProperty($leaf); + $leaf = new SelectPropertyItem( + "format", + __('Format:') + ); + $leaf->setValues($this->_getCgFormats()); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in NHibernate format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $CG_FORMATS = $this->_getCgFormats(); + $CG_HANDLERS = $this->_getCgHandlers(); + + $format = $GLOBALS['codegen_format']; + if (isset($CG_FORMATS[$format])) { + $method = $CG_HANDLERS[$format]; + + return $this->export->outputHandler( + $this->$method($db, $table, $crlf, $aliases) + ); + } + + return $this->export->outputHandler(sprintf("%s is not supported.", $format)); + } + + /** + * Used to make identifiers (from table or database names) + * + * @param string $str name to be converted + * @param bool $ucfirst whether to make the first character uppercase + * + * @return string identifier + */ + public static function cgMakeIdentifier($str, $ucfirst = true) + { + // remove unsafe characters + $str = preg_replace('/[^\p{L}\p{Nl}_]/u', '', $str); + // make sure first character is a letter or _ + if (! preg_match('/^\pL/u', $str)) { + $str = '_' . $str; + } + if ($ucfirst) { + $str = ucfirst($str); + } + + return $str; + } + + /** + * C# Handler + * + * @param string $db database name + * @param string $table table name + * @param string $crlf line separator + * @param array $aliases Aliases of db/table/columns + * + * @return string containing C# code lines, separated by "\n" + */ + private function _handleNHibernateCSBody($db, $table, $crlf, array $aliases = []) + { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $lines = []; + + $result = $GLOBALS['dbi']->query( + sprintf( + 'DESC %s.%s', + Util::backquote($db), + Util::backquote($table) + ) + ); + if ($result) { + /** @var TableProperty[] $tableProperties */ + $tableProperties = []; + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $col_as = $this->getAlias($aliases, $row[0], 'col', $db, $table); + if (! empty($col_as)) { + $row[0] = $col_as; + } + $tableProperties[] = new TableProperty($row); + } + $GLOBALS['dbi']->freeResult($result); + $lines[] = 'using System;'; + $lines[] = 'using System.Collections;'; + $lines[] = 'using System.Collections.Generic;'; + $lines[] = 'using System.Text;'; + $lines[] = 'namespace ' . ExportCodegen::cgMakeIdentifier($db_alias); + $lines[] = '{'; + $lines[] = ' #region ' + . ExportCodegen::cgMakeIdentifier($table_alias); + $lines[] = ' public class ' + . ExportCodegen::cgMakeIdentifier($table_alias); + $lines[] = ' {'; + $lines[] = ' #region Member Variables'; + foreach ($tableProperties as $tableProperty) { + $lines[] = $tableProperty->formatCs( + ' protected #dotNetPrimitiveType# _#name#;' + ); + } + $lines[] = ' #endregion'; + $lines[] = ' #region Constructors'; + $lines[] = ' public ' + . ExportCodegen::cgMakeIdentifier($table_alias) . '() { }'; + $temp = []; + foreach ($tableProperties as $tableProperty) { + if (! $tableProperty->isPK()) { + $temp[] = $tableProperty->formatCs( + '#dotNetPrimitiveType# #name#' + ); + } + } + $lines[] = ' public ' + . ExportCodegen::cgMakeIdentifier($table_alias) + . '(' + . implode(', ', $temp) + . ')'; + $lines[] = ' {'; + foreach ($tableProperties as $tableProperty) { + if (! $tableProperty->isPK()) { + $lines[] = $tableProperty->formatCs( + ' this._#name#=#name#;' + ); + } + } + $lines[] = ' }'; + $lines[] = ' #endregion'; + $lines[] = ' #region Public Properties'; + foreach ($tableProperties as $tableProperty) { + $lines[] = $tableProperty->formatCs( + ' public virtual #dotNetPrimitiveType# #ucfirstName#' + . "\n" + . ' {' . "\n" + . ' get {return _#name#;}' . "\n" + . ' set {_#name#=value;}' . "\n" + . ' }' + ); + } + $lines[] = ' #endregion'; + $lines[] = ' }'; + $lines[] = ' #endregion'; + $lines[] = '}'; + } + + return implode($crlf, $lines); + } + + /** + * XML Handler + * + * @param string $db database name + * @param string $table table name + * @param string $crlf line separator + * @param array $aliases Aliases of db/table/columns + * + * @return string containing XML code lines, separated by "\n" + */ + private function _handleNHibernateXMLBody( + $db, + $table, + $crlf, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $lines = []; + $lines[] = ''; + $lines[] = ''; + $lines[] = ' '; + $result = $GLOBALS['dbi']->query( + sprintf( + "DESC %s.%s", + Util::backquote($db), + Util::backquote($table) + ) + ); + if ($result) { + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $col_as = $this->getAlias($aliases, $row[0], 'col', $db, $table); + if (! empty($col_as)) { + $row[0] = $col_as; + } + $tableProperty = new TableProperty($row); + if ($tableProperty->isPK()) { + $lines[] = $tableProperty->formatXml( + ' ' . "\n" + . ' ' . "\n" + . ' ' . "\n" + . ' ' + ); + } else { + $lines[] = $tableProperty->formatXml( + ' ' . "\n" + . ' ' . "\n" + . ' ' + ); + } + } + $GLOBALS['dbi']->freeResult($result); + } + $lines[] = ' '; + $lines[] = ''; + + return implode($crlf, $lines); + } + + + /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ + + /** + * Getter for CodeGen formats + * + * @return array + */ + private function _getCgFormats() + { + return $this->_cgFormats; + } + + /** + * Setter for CodeGen formats + * + * @param array $CG_FORMATS contains CodeGen Formats + * + * @return void + */ + private function _setCgFormats(array $CG_FORMATS) + { + $this->_cgFormats = $CG_FORMATS; + } + + /** + * Getter for CodeGen handlers + * + * @return array + */ + private function _getCgHandlers() + { + return $this->_cgHandlers; + } + + /** + * Setter for CodeGen handlers + * + * @param array $CG_HANDLERS contains CodeGen handler methods + * + * @return void + */ + private function _setCgHandlers(array $CG_HANDLERS) + { + $this->_cgHandlers = $CG_HANDLERS; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCsv.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCsv.php new file mode 100644 index 0000000..8e5a319 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportCsv.php @@ -0,0 +1,347 @@ +setProperties(); + } + + /** + * Sets the export CSV properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('CSV'); + $exportPluginProperties->setExtension('csv'); + $exportPluginProperties->setMimeType('text/comma-separated-values'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create leaf items and add them to the group + $leaf = new TextPropertyItem( + "separator", + __('Columns separated with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "enclosed", + __('Columns enclosed with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "escaped", + __('Columns escaped with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "terminated", + __('Lines terminated with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + 'null', + __('Replace NULL with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new BoolPropertyItem( + 'removeCRLF', + __('Remove carriage return/line feed characters within columns') + ); + $generalOptions->addProperty($leaf); + $leaf = new BoolPropertyItem( + 'columns', + __('Put columns names in the first row') + ); + $generalOptions->addProperty($leaf); + $leaf = new HiddenPropertyItem( + 'structure_or_data' + ); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + global $what, $csv_terminated, $csv_separator, $csv_enclosed, $csv_escaped; + //Enable columns names by default for CSV + if ($what == 'csv') { + $GLOBALS['csv_columns'] = 'yes'; + } + // Here we just prepare some values for export + if ($what == 'excel') { + $csv_terminated = "\015\012"; + switch ($GLOBALS['excel_edition']) { + case 'win': + // as tested on Windows with Excel 2002 and Excel 2007 + $csv_separator = ';'; + break; + case 'mac_excel2003': + $csv_separator = ';'; + break; + case 'mac_excel2008': + $csv_separator = ','; + break; + } + $csv_enclosed = '"'; + $csv_escaped = '"'; + if (isset($GLOBALS['excel_columns'])) { + $GLOBALS['csv_columns'] = 'yes'; + } + } else { + if (empty($csv_terminated) + || mb_strtolower($csv_terminated) == 'auto' + ) { + $csv_terminated = $GLOBALS['crlf']; + } else { + $csv_terminated = str_replace( + [ + '\\r', + '\\n', + '\\t', + ], + [ + "\015", + "\012", + "\011", + ], + $csv_terminated + ); + } // end if + $csv_separator = str_replace('\\t', "\011", $csv_separator); + } + + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Alias of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in CSV format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + global $what, $csv_terminated, $csv_separator, $csv_enclosed, $csv_escaped; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + // Gets the data from the database + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $fields_cnt = $GLOBALS['dbi']->numFields($result); + + // If required, get fields name at the first line + if (isset($GLOBALS['csv_columns'])) { + $schema_insert = ''; + for ($i = 0; $i < $fields_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $col_as = stripslashes($col_as); + if ($csv_enclosed == '') { + $schema_insert .= $col_as; + } else { + $schema_insert .= $csv_enclosed + . str_replace( + $csv_enclosed, + $csv_escaped . $csv_enclosed, + $col_as + ) + . $csv_enclosed; + } + $schema_insert .= $csv_separator; + } // end for + $schema_insert = trim(mb_substr($schema_insert, 0, -1)); + if (! $this->export->outputHandler($schema_insert . $csv_terminated)) { + return false; + } + } // end if + + // Format the data + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $schema_insert = ''; + for ($j = 0; $j < $fields_cnt; $j++) { + if (! isset($row[$j]) || $row[$j] === null) { + $schema_insert .= $GLOBALS[$what . '_null']; + } elseif ($row[$j] == '0' || $row[$j] != '') { + // always enclose fields + if ($what == 'excel') { + $row[$j] = preg_replace("/\015(\012)?/", "\012", $row[$j]); + } + // remove CRLF characters within field + if (isset($GLOBALS[$what . '_removeCRLF']) + && $GLOBALS[$what . '_removeCRLF'] + ) { + $row[$j] = str_replace( + [ + "\r", + "\n", + ], + "", + $row[$j] + ); + } + if ($csv_enclosed == '') { + $schema_insert .= $row[$j]; + } else { + // also double the escape string if found in the data + if ($csv_escaped != $csv_enclosed) { + $schema_insert .= $csv_enclosed + . str_replace( + $csv_enclosed, + $csv_escaped . $csv_enclosed, + str_replace( + $csv_escaped, + $csv_escaped . $csv_escaped, + $row[$j] + ) + ) + . $csv_enclosed; + } else { + // avoid a problem when escape string equals enclose + $schema_insert .= $csv_enclosed + . str_replace( + $csv_enclosed, + $csv_escaped . $csv_enclosed, + $row[$j] + ) + . $csv_enclosed; + } + } + } else { + $schema_insert .= ''; + } + if ($j < $fields_cnt - 1) { + $schema_insert .= $csv_separator; + } + } // end for + + if (! $this->export->outputHandler($schema_insert . $csv_terminated)) { + return false; + } + } // end while + $GLOBALS['dbi']->freeResult($result); + + return true; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportExcel.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportExcel.php new file mode 100644 index 0000000..e778d20 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportExcel.php @@ -0,0 +1,90 @@ +setText('CSV for MS Excel'); + $exportPluginProperties->setExtension('csv'); + $exportPluginProperties->setMimeType('text/comma-separated-values'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new TextPropertyItem( + 'null', + __('Replace NULL with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new BoolPropertyItem( + 'removeCRLF', + __('Remove carriage return/line feed characters within columns') + ); + $generalOptions->addProperty($leaf); + $leaf = new BoolPropertyItem( + 'columns', + __('Put columns names in the first row') + ); + $generalOptions->addProperty($leaf); + $leaf = new SelectPropertyItem( + 'edition', + __('Excel edition:') + ); + $leaf->setValues( + [ + 'win' => 'Windows', + 'mac_excel2003' => 'Excel 2003 / Macintosh', + 'mac_excel2008' => 'Excel 2008 / Macintosh', + ] + ); + $generalOptions->addProperty($leaf); + $leaf = new HiddenPropertyItem( + 'structure_or_data' + ); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportHtmlword.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportHtmlword.php new file mode 100644 index 0000000..cbf4fce --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportHtmlword.php @@ -0,0 +1,670 @@ +setProperties(); + } + + /** + * Sets the export HTML-Word properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('Microsoft Word 2000'); + $exportPluginProperties->setExtension('doc'); + $exportPluginProperties->setMimeType('application/vnd.ms-word'); + $exportPluginProperties->setForceFile(true); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // what to dump (structure/data/both) + $dumpWhat = new OptionsPropertyMainGroup( + "dump_what", + __('Dump table') + ); + // create primary items and add them to the group + $leaf = new RadioPropertyItem("structure_or_data"); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $dumpWhat->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dumpWhat); + + // data options main group + $dataOptions = new OptionsPropertyMainGroup( + "dump_what", + __('Data dump options') + ); + $dataOptions->setForce('structure'); + // create primary items and add them to the group + $leaf = new TextPropertyItem( + "null", + __('Replace NULL with:') + ); + $dataOptions->addProperty($leaf); + $leaf = new BoolPropertyItem( + "columns", + __('Put columns names in the first row') + ); + $dataOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dataOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + global $charset; + + return $this->export->outputHandler( + ' + + + + + + + ' + ); + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + return $this->export->outputHandler(''); + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + if (empty($db_alias)) { + $db_alias = $db; + } + + return $this->export->outputHandler( + '

' . __('Database') . ' ' . htmlspecialchars($db_alias) . '

' + ); + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in HTML-Word format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + global $what; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + if (! $this->export->outputHandler( + '

' + . __('Dumping data for table') . ' ' . htmlspecialchars($table_alias) + . '

' + ) + ) { + return false; + } + if (! $this->export->outputHandler( + '' + ) + ) { + return false; + } + + // Gets the data from the database + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $fields_cnt = $GLOBALS['dbi']->numFields($result); + + // If required, get fields name at the first line + if (isset($GLOBALS['htmlword_columns'])) { + $schema_insert = ''; + for ($i = 0; $i < $fields_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $col_as = stripslashes($col_as); + $schema_insert .= ''; + } // end for + $schema_insert .= ''; + if (! $this->export->outputHandler($schema_insert)) { + return false; + } + } // end if + + // Format the data + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $schema_insert = ''; + for ($j = 0; $j < $fields_cnt; $j++) { + if (! isset($row[$j]) || $row[$j] === null) { + $value = $GLOBALS[$what . '_null']; + } elseif ($row[$j] == '0' || $row[$j] != '') { + $value = $row[$j]; + } else { + $value = ''; + } + $schema_insert .= ''; + } // end for + $schema_insert .= ''; + if (! $this->export->outputHandler($schema_insert)) { + return false; + } + } // end while + $GLOBALS['dbi']->freeResult($result); + + return $this->export->outputHandler('
'); + } + + /** + * Returns a stand-in CREATE definition to resolve view dependencies + * + * @param string $db the database name + * @param string $view the view name + * @param string $crlf the end of line sequence + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting definition + */ + public function getTableDefStandIn($db, $view, $crlf, $aliases = []) + { + $schema_insert = '' + . '' + . '' + . '' + . '' + . '' + . ''; + + /** + * Get the unique keys in the view + */ + $unique_keys = []; + $keys = $GLOBALS['dbi']->getTableIndexes($db, $view); + foreach ($keys as $key) { + if ($key['Non_unique'] == 0) { + $unique_keys[] = $key['Column_name']; + } + } + + $columns = $GLOBALS['dbi']->getColumns($db, $view); + foreach ($columns as $column) { + $col_as = $column['Field']; + if (! empty($aliases[$db]['tables'][$view]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$view]['columns'][$col_as]; + } + $schema_insert .= $this->formatOneColumnDefinition( + $column, + $unique_keys, + $col_as + ); + $schema_insert .= ''; + } + + $schema_insert .= '
'; + + return $schema_insert; + } + + /** + * Returns $table's CREATE definition + * + * @param string $db the database name + * @param string $table the table name + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * at the end + * @param bool $view whether we're handling a view + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting schema + */ + public function getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + $view = false, + array $aliases = [] + ) { + // set $cfgRelation here, because there is a chance that it's modified + // since the class initialization + global $cfgRelation; + + $schema_insert = ''; + + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + // Check if we can use Relations + list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus( + $do_relation && ! empty($cfgRelation['relation']), + $db, + $table + ); + + /** + * Displays the table structure + */ + $schema_insert .= ''; + + $schema_insert .= ''; + $schema_insert .= ''; + $schema_insert .= ''; + $schema_insert .= ''; + $schema_insert .= ''; + if ($do_relation && $have_rel) { + $schema_insert .= ''; + } + if ($do_comments) { + $schema_insert .= ''; + $comments = $this->relation->getComments($db, $table); + } + if ($do_mime && $cfgRelation['mimework']) { + $schema_insert .= ''; + $mime_map = $this->transformations->getMime($db, $table, true); + } + $schema_insert .= ''; + + $columns = $GLOBALS['dbi']->getColumns($db, $table); + /** + * Get the unique keys in the table + */ + $unique_keys = []; + $keys = $GLOBALS['dbi']->getTableIndexes($db, $table); + foreach ($keys as $key) { + if ($key['Non_unique'] == 0) { + $unique_keys[] = $key['Column_name']; + } + } + foreach ($columns as $column) { + $col_as = $column['Field']; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $schema_insert .= $this->formatOneColumnDefinition( + $column, + $unique_keys, + $col_as + ); + $field_name = $column['Field']; + if ($do_relation && $have_rel) { + $schema_insert .= ''; + } + if ($do_comments && $cfgRelation['commwork']) { + $schema_insert .= ''; + } + if ($do_mime && $cfgRelation['mimework']) { + $schema_insert .= ''; + } + + $schema_insert .= ''; + } // end foreach + + $schema_insert .= '
' + . htmlspecialchars( + $this->getRelationString( + $res_rel, + $field_name, + $db, + $aliases + ) + ) + . '' + . (isset($comments[$field_name]) + ? htmlspecialchars($comments[$field_name]) + : '') . '' + . (isset($mime_map[$field_name]) ? + htmlspecialchars( + str_replace('_', '/', $mime_map[$field_name]['mimetype']) + ) + : '') . '
'; + + return $schema_insert; + } + + /** + * Outputs triggers + * + * @param string $db database name + * @param string $table table name + * + * @return string Formatted triggers list + */ + protected function getTriggers($db, $table) + { + $dump = ''; + $dump .= ''; + $dump .= ''; + $dump .= ''; + $dump .= ''; + $dump .= ''; + $dump .= ''; + + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + + foreach ($triggers as $trigger) { + $dump .= ''; + $dump .= '' + . '' + . '' + . '' + . ''; + } + + $dump .= '
'; + + return $dump; + } + + /** + * Outputs table's structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table', 'triggers', 'create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + $dump = ''; + + switch ($export_mode) { + case 'create_table': + $dump .= '

' + . __('Table structure for table') . ' ' + . htmlspecialchars($table_alias) + . '

'; + $dump .= $this->getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + false, + $aliases + ); + break; + case 'triggers': + $dump = ''; + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + if ($triggers) { + $dump .= '

' + . __('Triggers') . ' ' . htmlspecialchars($table_alias) + . '

'; + $dump .= $this->getTriggers($db, $table); + } + break; + case 'create_view': + $dump .= '

' + . __('Structure for view') . ' ' . htmlspecialchars($table_alias) + . '

'; + $dump .= $this->getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + true, + $aliases + ); + break; + case 'stand_in': + $dump .= '

' + . __('Stand-in structure for view') . ' ' + . htmlspecialchars($table_alias) + . '

'; + // export a stand-in definition to resolve view dependencies + $dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases); + } // end switch + + return $this->export->outputHandler($dump); + } + + /** + * Formats the definition for one column + * + * @param array $column info about this column + * @param array $unique_keys unique keys of the table + * @param string $col_alias Column Alias + * + * @return string Formatted column definition + */ + protected function formatOneColumnDefinition( + array $column, + array $unique_keys, + $col_alias = '' + ) { + if (empty($col_alias)) { + $col_alias = $column['Field']; + } + $definition = ''; + + $extracted_columnspec = Util::extractColumnSpec($column['Type']); + + $type = htmlspecialchars($extracted_columnspec['print_type']); + if (empty($type)) { + $type = ' '; + } + + if (! isset($column['Default'])) { + if ($column['Null'] != 'NO') { + $column['Default'] = 'NULL'; + } + } + + $fmt_pre = ''; + $fmt_post = ''; + if (in_array($column['Field'], $unique_keys)) { + $fmt_pre = '' . $fmt_pre; + $fmt_post .= ''; + } + if ($column['Key'] == 'PRI') { + $fmt_pre = '' . $fmt_pre; + $fmt_post .= ''; + } + $definition .= '' . $fmt_pre + . htmlspecialchars($col_alias) . $fmt_post . ''; + $definition .= '' . htmlspecialchars($type) . ''; + $definition .= '' + . (($column['Null'] == '' || $column['Null'] == 'NO') + ? __('No') + : __('Yes')) + . ''; + $definition .= '' + . htmlspecialchars(isset($column['Default']) ? $column['Default'] : '') + . ''; + + return $definition; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportJson.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportJson.php new file mode 100644 index 0000000..4ebfbbe --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportJson.php @@ -0,0 +1,295 @@ +setProperties(); + } + + /** + * Encodes the data into JSON + * + * @param mixed $data Data to encode + * + * @return string + */ + public function encode($data) + { + $options = 0; + if (isset($GLOBALS['json_pretty_print']) + && $GLOBALS['json_pretty_print'] + ) { + $options |= JSON_PRETTY_PRINT; + } + if (isset($GLOBALS['json_unicode']) + && $GLOBALS['json_unicode'] + ) { + $options |= JSON_UNESCAPED_UNICODE; + } + return json_encode($data, $options); + } + + /** + * Sets the export JSON properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('JSON'); + $exportPluginProperties->setExtension('json'); + $exportPluginProperties->setMimeType('text/plain'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new HiddenPropertyItem("structure_or_data"); + $generalOptions->addProperty($leaf); + + $leaf = new BoolPropertyItem( + 'pretty_print', + __('Output pretty-printed JSON (Use human-readable formatting)') + ); + $generalOptions->addProperty($leaf); + + $leaf = new BoolPropertyItem( + 'unicode', + __('Output unicode characters unescaped') + ); + $generalOptions->addProperty($leaf); + + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + global $crlf; + + $meta = [ + 'type' => 'header', + 'version' => PMA_VERSION, + 'comment' => 'Export to JSON plugin for PHPMyAdmin', + ]; + + return $this->export->outputHandler( + '[' . $crlf . $this->encode($meta) . ',' . $crlf + ); + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + global $crlf; + + return $this->export->outputHandler(']' . $crlf); + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + global $crlf; + + if (empty($db_alias)) { + $db_alias = $db; + } + + $meta = [ + 'type' => 'database', + 'name' => $db_alias, + ]; + + return $this->export->outputHandler( + $this->encode($meta) . ',' . $crlf + ); + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in JSON format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + if (! $this->first) { + if (! $this->export->outputHandler(',')) { + return false; + } + } else { + $this->first = false; + } + + $buffer = $this->encode( + [ + 'type' => 'table', + 'name' => $table_alias, + 'database' => $db_alias, + 'data' => "@@DATA@@", + ] + ); + list($header, $footer) = explode('"@@DATA@@"', $buffer); + + if (! $this->export->outputHandler($header . $crlf . '[' . $crlf)) { + return false; + } + + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $columns_cnt = $GLOBALS['dbi']->numFields($result); + $fieldsMeta = $GLOBALS['dbi']->getFieldsMeta($result); + + $columns = []; + for ($i = 0; $i < $columns_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $columns[$i] = stripslashes($col_as); + } + + $record_cnt = 0; + while ($record = $GLOBALS['dbi']->fetchRow($result)) { + $record_cnt++; + + // Output table name as comment if this is the first record of the table + if ($record_cnt > 1) { + if (! $this->export->outputHandler(',' . $crlf)) { + return false; + } + } + + $data = []; + + for ($i = 0; $i < $columns_cnt; $i++) { + if ($fieldsMeta[$i]->type === 'geometry') { + // export GIS types as hex + $record[$i] = '0x' . bin2hex($record[$i]); + } + $data[$columns[$i]] = $record[$i]; + } + + $encodedData = $this->encode($data); + if (! $encodedData) { + return false; + } + if (! $this->export->outputHandler($encodedData)) { + return false; + } + } + + if (! $this->export->outputHandler($crlf . ']' . $crlf . $footer . $crlf)) { + return false; + } + + $GLOBALS['dbi']->freeResult($result); + + return true; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportLatex.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportLatex.php new file mode 100644 index 0000000..e215300 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportLatex.php @@ -0,0 +1,709 @@ +initSpecificVariables(); + $this->setProperties(); + } + + /** + * Initialize the local variables that are used for export Latex + * + * @return void + */ + protected function initSpecificVariables() + { + /* Messages used in default captions */ + $GLOBALS['strLatexContent'] = __('Content of table @TABLE@'); + $GLOBALS['strLatexContinued'] = __('(continued)'); + $GLOBALS['strLatexStructure'] = __('Structure of table @TABLE@'); + } + + /** + * Sets the export Latex properties + * + * @return void + */ + protected function setProperties() + { + global $plugin_param; + $hide_structure = false; + if ($plugin_param['export_type'] == 'table' + && ! $plugin_param['single_table'] + ) { + $hide_structure = true; + } + + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('LaTeX'); + $exportPluginProperties->setExtension('tex'); + $exportPluginProperties->setMimeType('application/x-tex'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new BoolPropertyItem( + "caption", + __('Include table caption') + ); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // what to dump (structure/data/both) main group + $dumpWhat = new OptionsPropertyMainGroup( + "dump_what", + __('Dump table') + ); + // create primary items and add them to the group + $leaf = new RadioPropertyItem("structure_or_data"); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $dumpWhat->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dumpWhat); + + // structure options main group + if (! $hide_structure) { + $structureOptions = new OptionsPropertyMainGroup( + "structure", + __('Object creation options') + ); + $structureOptions->setForce('data'); + // create primary items and add them to the group + $leaf = new TextPropertyItem( + "structure_caption", + __('Table caption:') + ); + $leaf->setDoc('faq6-27'); + $structureOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "structure_continued_caption", + __('Table caption (continued):') + ); + $leaf->setDoc('faq6-27'); + $structureOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "structure_label", + __('Label key:') + ); + $leaf->setDoc('faq6-27'); + $structureOptions->addProperty($leaf); + if (! empty($GLOBALS['cfgRelation']['relation'])) { + $leaf = new BoolPropertyItem( + "relation", + __('Display foreign key relationships') + ); + $structureOptions->addProperty($leaf); + } + $leaf = new BoolPropertyItem( + "comments", + __('Display comments') + ); + $structureOptions->addProperty($leaf); + if (! empty($GLOBALS['cfgRelation']['mimework'])) { + $leaf = new BoolPropertyItem( + "mime", + __('Display media (MIME) types') + ); + $structureOptions->addProperty($leaf); + } + // add the main group to the root group + $exportSpecificOptions->addProperty($structureOptions); + } + + // data options main group + $dataOptions = new OptionsPropertyMainGroup( + "data", + __('Data dump options') + ); + $dataOptions->setForce('structure'); + // create primary items and add them to the group + $leaf = new BoolPropertyItem( + "columns", + __('Put columns names in the first row:') + ); + $dataOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "data_caption", + __('Table caption:') + ); + $leaf->setDoc('faq6-27'); + $dataOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "data_continued_caption", + __('Table caption (continued):') + ); + $leaf->setDoc('faq6-27'); + $dataOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + "data_label", + __('Label key:') + ); + $leaf->setDoc('faq6-27'); + $dataOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + 'null', + __('Replace NULL with:') + ); + $dataOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dataOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + global $crlf; + global $cfg; + + $head = '% phpMyAdmin LaTeX Dump' . $crlf + . '% version ' . PMA_VERSION . $crlf + . '% https://www.phpmyadmin.net/' . $crlf + . '%' . $crlf + . '% ' . __('Host:') . ' ' . $cfg['Server']['host']; + if (! empty($cfg['Server']['port'])) { + $head .= ':' . $cfg['Server']['port']; + } + $head .= $crlf + . '% ' . __('Generation Time:') . ' ' + . Util::localisedDate() . $crlf + . '% ' . __('Server version:') . ' ' . $GLOBALS['dbi']->getVersionString() . $crlf + . '% ' . __('PHP Version:') . ' ' . PHP_VERSION . $crlf; + + return $this->export->outputHandler($head); + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + if (empty($db_alias)) { + $db_alias = $db; + } + global $crlf; + $head = '% ' . $crlf + . '% ' . __('Database:') . ' \'' . $db_alias . '\'' . $crlf + . '% ' . $crlf; + + return $this->export->outputHandler($head); + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in JSON format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + $result = $GLOBALS['dbi']->tryQuery( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + + $columns_cnt = $GLOBALS['dbi']->numFields($result); + $columns = []; + $columns_alias = []; + for ($i = 0; $i < $columns_cnt; $i++) { + $columns[$i] = $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $columns_alias[$i] = $col_as; + } + + $buffer = $crlf . '%' . $crlf . '% ' . __('Data:') . ' ' . $table_alias + . $crlf . '%' . $crlf . ' \\begin{longtable}{|'; + + for ($index = 0; $index < $columns_cnt; $index++) { + $buffer .= 'l|'; + } + $buffer .= '} ' . $crlf; + + $buffer .= ' \\hline \\endhead \\hline \\endfoot \\hline ' . $crlf; + if (isset($GLOBALS['latex_caption'])) { + $buffer .= ' \\caption{' + . Util::expandUserString( + $GLOBALS['latex_data_caption'], + [ + 'texEscape', + static::class, + ], + [ + 'table' => $table_alias, + 'database' => $db_alias, + ] + ) + . '} \\label{' + . Util::expandUserString( + $GLOBALS['latex_data_label'], + null, + [ + 'table' => $table_alias, + 'database' => $db_alias, + ] + ) + . '} \\\\'; + } + if (! $this->export->outputHandler($buffer)) { + return false; + } + + // show column names + if (isset($GLOBALS['latex_columns'])) { + $buffer = '\\hline '; + for ($i = 0; $i < $columns_cnt; $i++) { + $buffer .= '\\multicolumn{1}{|c|}{\\textbf{' + . self::texEscape(stripslashes($columns_alias[$i])) . '}} & '; + } + + $buffer = mb_substr($buffer, 0, -2) . '\\\\ \\hline \hline '; + if (! $this->export->outputHandler($buffer . ' \\endfirsthead ' . $crlf)) { + return false; + } + if (isset($GLOBALS['latex_caption'])) { + if (! $this->export->outputHandler( + '\\caption{' + . Util::expandUserString( + $GLOBALS['latex_data_continued_caption'], + [ + 'texEscape', + static::class, + ], + [ + 'table' => $table_alias, + 'database' => $db_alias, + ] + ) + . '} \\\\ ' + ) + ) { + return false; + } + } + if (! $this->export->outputHandler($buffer . '\\endhead \\endfoot' . $crlf)) { + return false; + } + } else { + if (! $this->export->outputHandler('\\\\ \hline')) { + return false; + } + } + + // print the whole table + while ($record = $GLOBALS['dbi']->fetchAssoc($result)) { + $buffer = ''; + // print each row + for ($i = 0; $i < $columns_cnt; $i++) { + if ($record[$columns[$i]] !== null + && isset($record[$columns[$i]]) + ) { + $column_value = self::texEscape( + stripslashes($record[$columns[$i]]) + ); + } else { + $column_value = $GLOBALS['latex_null']; + } + + // last column ... no need for & character + if ($i == ($columns_cnt - 1)) { + $buffer .= $column_value; + } else { + $buffer .= $column_value . " & "; + } + } + $buffer .= ' \\\\ \\hline ' . $crlf; + if (! $this->export->outputHandler($buffer)) { + return false; + } + } + + $buffer = ' \\end{longtable}' . $crlf; + if (! $this->export->outputHandler($buffer)) { + return false; + } + + $GLOBALS['dbi']->freeResult($result); + + return true; + } // end getTableLaTeX + + /** + * Outputs table's structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table', 'triggers', 'create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + global $cfgRelation; + + /* We do not export triggers */ + if ($export_mode == 'triggers') { + return true; + } + + /** + * Get the unique keys in the table + */ + $unique_keys = []; + $keys = $GLOBALS['dbi']->getTableIndexes($db, $table); + foreach ($keys as $key) { + if ($key['Non_unique'] == 0) { + $unique_keys[] = $key['Column_name']; + } + } + + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + // Check if we can use Relations + list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus( + $do_relation && ! empty($cfgRelation['relation']), + $db, + $table + ); + /** + * Displays the table structure + */ + $buffer = $crlf . '%' . $crlf . '% ' . __('Structure:') . ' ' + . $table_alias . $crlf . '%' . $crlf . ' \\begin{longtable}{'; + if (! $this->export->outputHandler($buffer)) { + return false; + } + + $alignment = '|l|c|c|c|'; + if ($do_relation && $have_rel) { + $alignment .= 'l|'; + } + if ($do_comments) { + $alignment .= 'l|'; + } + if ($do_mime && $cfgRelation['mimework']) { + $alignment .= 'l|'; + } + $buffer = $alignment . '} ' . $crlf; + + $header = ' \\hline '; + $header .= '\\multicolumn{1}{|c|}{\\textbf{' . __('Column') + . '}} & \\multicolumn{1}{|c|}{\\textbf{' . __('Type') + . '}} & \\multicolumn{1}{|c|}{\\textbf{' . __('Null') + . '}} & \\multicolumn{1}{|c|}{\\textbf{' . __('Default') . '}}'; + if ($do_relation && $have_rel) { + $header .= ' & \\multicolumn{1}{|c|}{\\textbf{' . __('Links to') . '}}'; + } + if ($do_comments) { + $header .= ' & \\multicolumn{1}{|c|}{\\textbf{' . __('Comments') . '}}'; + $comments = $this->relation->getComments($db, $table); + } + if ($do_mime && $cfgRelation['mimework']) { + $header .= ' & \\multicolumn{1}{|c|}{\\textbf{MIME}}'; + $mime_map = $this->transformations->getMime($db, $table, true); + } + + // Table caption for first page and label + if (isset($GLOBALS['latex_caption'])) { + $buffer .= ' \\caption{' + . Util::expandUserString( + $GLOBALS['latex_structure_caption'], + [ + 'texEscape', + static::class, + ], + [ + 'table' => $table_alias, + 'database' => $db_alias, + ] + ) + . '} \\label{' + . Util::expandUserString( + $GLOBALS['latex_structure_label'], + null, + [ + 'table' => $table_alias, + 'database' => $db_alias, + ] + ) + . '} \\\\' . $crlf; + } + $buffer .= $header . ' \\\\ \\hline \\hline' . $crlf + . '\\endfirsthead' . $crlf; + // Table caption on next pages + if (isset($GLOBALS['latex_caption'])) { + $buffer .= ' \\caption{' + . Util::expandUserString( + $GLOBALS['latex_structure_continued_caption'], + [ + 'texEscape', + static::class, + ], + [ + 'table' => $table_alias, + 'database' => $db_alias, + ] + ) + . '} \\\\ ' . $crlf; + } + $buffer .= $header . ' \\\\ \\hline \\hline \\endhead \\endfoot ' . $crlf; + + if (! $this->export->outputHandler($buffer)) { + return false; + } + + $fields = $GLOBALS['dbi']->getColumns($db, $table); + foreach ($fields as $row) { + $extracted_columnspec = Util::extractColumnSpec($row['Type']); + $type = $extracted_columnspec['print_type']; + if (empty($type)) { + $type = ' '; + } + + if (! isset($row['Default'])) { + if ($row['Null'] != 'NO') { + $row['Default'] = 'NULL'; + } + } + + $field_name = $col_as = $row['Field']; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + + $local_buffer = $col_as . "\000" . $type . "\000" + . (($row['Null'] == '' || $row['Null'] == 'NO') + ? __('No') : __('Yes')) + . "\000" . (isset($row['Default']) ? $row['Default'] : ''); + + if ($do_relation && $have_rel) { + $local_buffer .= "\000"; + $local_buffer .= $this->getRelationString( + $res_rel, + $field_name, + $db, + $aliases + ); + } + if ($do_comments && $cfgRelation['commwork']) { + $local_buffer .= "\000"; + if (isset($comments[$field_name])) { + $local_buffer .= $comments[$field_name]; + } + } + if ($do_mime && $cfgRelation['mimework']) { + $local_buffer .= "\000"; + if (isset($mime_map[$field_name])) { + $local_buffer .= str_replace( + '_', + '/', + $mime_map[$field_name]['mimetype'] + ); + } + } + $local_buffer = self::texEscape($local_buffer); + if ($row['Key'] == 'PRI') { + $pos = mb_strpos($local_buffer, "\000"); + $local_buffer = '\\textit{' + . + mb_substr($local_buffer, 0, $pos) + . '}' . + mb_substr($local_buffer, $pos); + } + if (in_array($field_name, $unique_keys)) { + $pos = mb_strpos($local_buffer, "\000"); + $local_buffer = '\\textbf{' + . + mb_substr($local_buffer, 0, $pos) + . '}' . + mb_substr($local_buffer, $pos); + } + $buffer = str_replace("\000", ' & ', $local_buffer); + $buffer .= ' \\\\ \\hline ' . $crlf; + + if (! $this->export->outputHandler($buffer)) { + return false; + } + } // end while + + $buffer = ' \\end{longtable}' . $crlf; + + return $this->export->outputHandler($buffer); + } // end of the 'exportStructure' method + + /** + * Escapes some special characters for use in TeX/LaTeX + * + * @param string $string the string to convert + * + * @return string the converted string with escape codes + */ + public static function texEscape($string) + { + $escape = [ + '$', + '%', + '{', + '}', + '&', + '#', + '_', + '^', + ]; + $cnt_escape = count($escape); + for ($k = 0; $k < $cnt_escape; $k++) { + $string = str_replace($escape[$k], '\\' . $escape[$k], $string); + } + + return $string; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportMediawiki.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportMediawiki.php new file mode 100644 index 0000000..9d971cb --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportMediawiki.php @@ -0,0 +1,386 @@ +setProperties(); + } + + /** + * Sets the export MediaWiki properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('MediaWiki Table'); + $exportPluginProperties->setExtension('mediawiki'); + $exportPluginProperties->setMimeType('text/plain'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup( + "general_opts", + __('Dump table') + ); + + // what to dump (structure/data/both) + $subgroup = new OptionsPropertySubgroup( + "dump_table", + __("Dump table") + ); + $leaf = new RadioPropertyItem('structure_or_data'); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $subgroup->setSubgroupHeader($leaf); + $generalOptions->addProperty($subgroup); + + // export table name + $leaf = new BoolPropertyItem( + "caption", + __('Export table names') + ); + $generalOptions->addProperty($leaf); + + // export table headers + $leaf = new BoolPropertyItem( + "headers", + __('Export table headers') + ); + $generalOptions->addProperty($leaf); + //add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Alias of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs table's structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table','triggers','create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; this is + * deprecated but the parameter is left here + * because export.php calls exportStructure() + * also for other export types which use this + * parameter + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + $output = ''; + switch ($export_mode) { + case 'create_table': + $columns = $GLOBALS['dbi']->getColumns($db, $table); + $columns = array_values($columns); + $row_cnt = count($columns); + + // Print structure comment + $output = $this->_exportComment( + "Table structure for " + . Util::backquote($table_alias) + ); + + // Begin the table construction + $output .= "{| class=\"wikitable\" style=\"text-align:center;\"" + . $this->_exportCRLF(); + + // Add the table name + if (isset($GLOBALS['mediawiki_caption'])) { + $output .= "|+'''" . $table_alias . "'''" . $this->_exportCRLF(); + } + + // Add the table headers + if (isset($GLOBALS['mediawiki_headers'])) { + $output .= "|- style=\"background:#ffdead;\"" . $this->_exportCRLF(); + $output .= "! style=\"background:#ffffff\" | " + . $this->_exportCRLF(); + for ($i = 0; $i < $row_cnt; ++$i) { + $col_as = $columns[$i]['Field']; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as]) + ) { + $col_as + = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $output .= " | " . $col_as . $this->_exportCRLF(); + } + } + + // Add the table structure + $output .= "|-" . $this->_exportCRLF(); + $output .= "! Type" . $this->_exportCRLF(); + for ($i = 0; $i < $row_cnt; ++$i) { + $output .= " | " . $columns[$i]['Type'] . $this->_exportCRLF(); + } + + $output .= "|-" . $this->_exportCRLF(); + $output .= "! Null" . $this->_exportCRLF(); + for ($i = 0; $i < $row_cnt; ++$i) { + $output .= " | " . $columns[$i]['Null'] . $this->_exportCRLF(); + } + + $output .= "|-" . $this->_exportCRLF(); + $output .= "! Default" . $this->_exportCRLF(); + for ($i = 0; $i < $row_cnt; ++$i) { + $output .= " | " . $columns[$i]['Default'] . $this->_exportCRLF(); + } + + $output .= "|-" . $this->_exportCRLF(); + $output .= "! Extra" . $this->_exportCRLF(); + for ($i = 0; $i < $row_cnt; ++$i) { + $output .= " | " . $columns[$i]['Extra'] . $this->_exportCRLF(); + } + + $output .= "|}" . str_repeat($this->_exportCRLF(), 2); + break; + } // end switch + + return $this->export->outputHandler($output); + } + + /** + * Outputs the content of a table in MediaWiki format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + // Print data comment + $output = $this->_exportComment( + "Table data for " . Util::backquote($table_alias) + ); + + // Begin the table construction + // Use the "wikitable" class for style + // Use the "sortable" class for allowing tables to be sorted by column + $output .= "{| class=\"wikitable sortable\" style=\"text-align:center;\"" + . $this->_exportCRLF(); + + // Add the table name + if (isset($GLOBALS['mediawiki_caption'])) { + $output .= "|+'''" . $table_alias . "'''" . $this->_exportCRLF(); + } + + // Add the table headers + if (isset($GLOBALS['mediawiki_headers'])) { + // Get column names + $column_names = $GLOBALS['dbi']->getColumnNames($db, $table); + + // Add column names as table headers + if ($column_names !== null) { + // Use '|-' for separating rows + $output .= "|-" . $this->_exportCRLF(); + + // Use '!' for separating table headers + foreach ($column_names as $column) { + if (! empty($aliases[$db]['tables'][$table]['columns'][$column]) + ) { + $column + = $aliases[$db]['tables'][$table]['columns'][$column]; + } + $output .= " ! " . $column . "" . $this->_exportCRLF(); + } + } + } + + // Get the table data from the database + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $fields_cnt = $GLOBALS['dbi']->numFields($result); + + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $output .= "|-" . $this->_exportCRLF(); + + // Use '|' for separating table columns + for ($i = 0; $i < $fields_cnt; ++$i) { + $output .= " | " . $row[$i] . "" . $this->_exportCRLF(); + } + } + + // End table construction + $output .= "|}" . str_repeat($this->_exportCRLF(), 2); + + return $this->export->outputHandler($output); + } + + /** + * Outputs comments containing info about the exported tables + * + * @param string $text Text of comment + * + * @return string The formatted comment + */ + private function _exportComment($text = '') + { + // see https://www.mediawiki.org/wiki/Help:Formatting + $comment = $this->_exportCRLF(); + $comment .= '' . str_repeat($this->_exportCRLF(), 2); + + return $comment; + } + + /** + * Outputs CRLF + * + * @return string CRLF + */ + private function _exportCRLF() + { + // The CRLF expected by the mediawiki format is "\n" + return "\n"; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOds.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOds.php new file mode 100644 index 0000000..bc062da --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOds.php @@ -0,0 +1,345 @@ +setProperties(); + } + + /** + * Sets the export ODS properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('OpenDocument Spreadsheet'); + $exportPluginProperties->setExtension('ods'); + $exportPluginProperties->setMimeType( + 'application/vnd.oasis.opendocument.spreadsheet' + ); + $exportPluginProperties->setForceFile(true); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new TextPropertyItem( + "null", + __('Replace NULL with:') + ); + $generalOptions->addProperty($leaf); + $leaf = new BoolPropertyItem( + "columns", + __('Put columns names in the first row') + ); + $generalOptions->addProperty($leaf); + $leaf = new HiddenPropertyItem("structure_or_data"); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + $GLOBALS['ods_buffer'] .= '' + . '' + . '' + . '' + . '' + . '/' + . '' + . '/' + . '' + . '' + . '' + . '' + . ':' + . '' + . ':' + . '' + . ' ' + . '' + . '' + . '' + . '' + . '/' + . '' + . '/' + . '' + . ' ' + . '' + . ':' + . '' + . ' ' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . ''; + + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + $GLOBALS['ods_buffer'] .= '' + . '' + . ''; + + return $this->export->outputHandler( + OpenDocument::create( + 'application/vnd.oasis.opendocument.spreadsheet', + $GLOBALS['ods_buffer'] + ) + ); + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in NHibernate format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + global $what; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + // Gets the data from the database + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $fields_cnt = $GLOBALS['dbi']->numFields($result); + $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result); + $field_flags = []; + for ($j = 0; $j < $fields_cnt; $j++) { + $field_flags[$j] = $GLOBALS['dbi']->fieldFlags($result, $j); + } + + $GLOBALS['ods_buffer'] + .= ''; + + // If required, get fields name at the first line + if (isset($GLOBALS[$what . '_columns'])) { + $GLOBALS['ods_buffer'] .= ''; + for ($i = 0; $i < $fields_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars( + stripslashes($col_as) + ) + . '' + . ''; + } // end for + $GLOBALS['ods_buffer'] .= ''; + } // end if + + // Format the data + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $GLOBALS['ods_buffer'] .= ''; + for ($j = 0; $j < $fields_cnt; $j++) { + if ($fields_meta[$j]->type === 'geometry') { + // export GIS types as hex + $row[$j] = '0x' . bin2hex($row[$j]); + } + if (! isset($row[$j]) || $row[$j] === null) { + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars($GLOBALS[$what . '_null']) + . '' + . ''; + } elseif (false !== stripos($field_flags[$j], 'BINARY') + && $fields_meta[$j]->blob + ) { + // ignore BLOB + $GLOBALS['ods_buffer'] + .= '' + . '' + . ''; + } elseif ($fields_meta[$j]->type == "date") { + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } elseif ($fields_meta[$j]->type == "time") { + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } elseif ($fields_meta[$j]->type == "datetime") { + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } elseif (($fields_meta[$j]->numeric + && $fields_meta[$j]->type != 'timestamp' + && ! $fields_meta[$j]->blob) + || $fields_meta[$j]->type == 'real' + ) { + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } else { + $GLOBALS['ods_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } + } // end for + $GLOBALS['ods_buffer'] .= ''; + } // end while + $GLOBALS['dbi']->freeResult($result); + + $GLOBALS['ods_buffer'] .= ''; + + return true; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOdt.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOdt.php new file mode 100644 index 0000000..8173afb --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportOdt.php @@ -0,0 +1,813 @@ +setProperties(); + } + + /** + * Sets the export ODT properties + * + * @return void + */ + protected function setProperties() + { + global $plugin_param; + $hide_structure = false; + if ($plugin_param['export_type'] == 'table' + && ! $plugin_param['single_table'] + ) { + $hide_structure = true; + } + + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('OpenDocument Text'); + $exportPluginProperties->setExtension('odt'); + $exportPluginProperties->setMimeType( + 'application/vnd.oasis.opendocument.text' + ); + $exportPluginProperties->setForceFile(true); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // what to dump (structure/data/both) main group + $dumpWhat = new OptionsPropertyMainGroup( + "general_opts", + __('Dump table') + ); + // create primary items and add them to the group + $leaf = new RadioPropertyItem("structure_or_data"); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $dumpWhat->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dumpWhat); + + // structure options main group + if (! $hide_structure) { + $structureOptions = new OptionsPropertyMainGroup( + "structure", + __('Object creation options') + ); + $structureOptions->setForce('data'); + // create primary items and add them to the group + if (! empty($GLOBALS['cfgRelation']['relation'])) { + $leaf = new BoolPropertyItem( + "relation", + __('Display foreign key relationships') + ); + $structureOptions->addProperty($leaf); + } + $leaf = new BoolPropertyItem( + "comments", + __('Display comments') + ); + $structureOptions->addProperty($leaf); + if (! empty($GLOBALS['cfgRelation']['mimework'])) { + $leaf = new BoolPropertyItem( + "mime", + __('Display media (MIME) types') + ); + $structureOptions->addProperty($leaf); + } + // add the main group to the root group + $exportSpecificOptions->addProperty($structureOptions); + } + + // data options main group + $dataOptions = new OptionsPropertyMainGroup( + "data", + __('Data dump options') + ); + $dataOptions->setForce('structure'); + // create primary items and add them to the group + $leaf = new BoolPropertyItem( + "columns", + __('Put columns names in the first row') + ); + $dataOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + 'null', + __('Replace NULL with:') + ); + $dataOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dataOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + $GLOBALS['odt_buffer'] .= '' + . '' + . '' + . ''; + + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + $GLOBALS['odt_buffer'] .= '' + . '' + . ''; + if (! $this->export->outputHandler( + OpenDocument::create( + 'application/vnd.oasis.opendocument.text', + $GLOBALS['odt_buffer'] + ) + ) + ) { + return false; + } + + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + if (empty($db_alias)) { + $db_alias = $db; + } + $GLOBALS['odt_buffer'] + .= '' + . __('Database') . ' ' . htmlspecialchars($db_alias) + . ''; + + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in NHibernate format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + global $what; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + // Gets the data from the database + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $fields_cnt = $GLOBALS['dbi']->numFields($result); + $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result); + $field_flags = []; + for ($j = 0; $j < $fields_cnt; $j++) { + $field_flags[$j] = $GLOBALS['dbi']->fieldFlags($result, $j); + } + + $GLOBALS['odt_buffer'] + .= '' + . __('Dumping data for table') . ' ' . htmlspecialchars($table_alias) + . '' + . '' + . ''; + + // If required, get fields name at the first line + if (isset($GLOBALS[$what . '_columns'])) { + $GLOBALS['odt_buffer'] .= ''; + for ($i = 0; $i < $fields_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars( + stripslashes($col_as) + ) + . '' + . ''; + } // end for + $GLOBALS['odt_buffer'] .= ''; + } // end if + + // Format the data + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $GLOBALS['odt_buffer'] .= ''; + for ($j = 0; $j < $fields_cnt; $j++) { + if ($fields_meta[$j]->type === 'geometry') { + // export GIS types as hex + $row[$j] = '0x' . bin2hex($row[$j]); + } + if (! isset($row[$j]) || $row[$j] === null) { + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars($GLOBALS[$what . '_null']) + . '' + . ''; + } elseif (false !== stripos($field_flags[$j], 'BINARY') + && $fields_meta[$j]->blob + ) { + // ignore BLOB + $GLOBALS['odt_buffer'] + .= '' + . '' + . ''; + } elseif ($fields_meta[$j]->numeric + && $fields_meta[$j]->type != 'timestamp' + && ! $fields_meta[$j]->blob + ) { + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } else { + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars($row[$j]) + . '' + . ''; + } + } // end for + $GLOBALS['odt_buffer'] .= ''; + } // end while + $GLOBALS['dbi']->freeResult($result); + + $GLOBALS['odt_buffer'] .= ''; + + return true; + } + + /** + * Returns a stand-in CREATE definition to resolve view dependencies + * + * @param string $db the database name + * @param string $view the view name + * @param string $crlf the end of line sequence + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting definition + */ + public function getTableDefStandIn($db, $view, $crlf, $aliases = []) + { + $db_alias = $db; + $view_alias = $view; + $this->initAlias($aliases, $db_alias, $view_alias); + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + /** + * Displays the table structure + */ + $GLOBALS['odt_buffer'] + .= ''; + $columns_cnt = 4; + $GLOBALS['odt_buffer'] + .= ''; + /* Header */ + $GLOBALS['odt_buffer'] .= '' + . '' + . '' . __('Column') . '' + . '' + . '' + . '' . __('Type') . '' + . '' + . '' + . '' . __('Null') . '' + . '' + . '' + . '' . __('Default') . '' + . '' + . ''; + + $columns = $GLOBALS['dbi']->getColumns($db, $view); + foreach ($columns as $column) { + $col_as = $column['Field'] ?? null; + if (! empty($aliases[$db]['tables'][$view]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$view]['columns'][$col_as]; + } + $GLOBALS['odt_buffer'] .= $this->formatOneColumnDefinition( + $column, + $col_as + ); + $GLOBALS['odt_buffer'] .= ''; + } // end foreach + + $GLOBALS['odt_buffer'] .= ''; + + return ''; + } + + /** + * Returns $table's CREATE definition + * + * @param string $db the database name + * @param string $table the table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * @param bool $do_mime whether to include mime comments + * @param bool $show_dates whether to include creation/update/check dates + * @param bool $add_semicolon whether to add semicolon and end-of-line at + * the end + * @param bool $view whether we're handling a view + * @param array $aliases Aliases of db/table/columns + * + * @return bool true + */ + public function getTableDef( + $db, + $table, + $crlf, + $error_url, + $do_relation, + $do_comments, + $do_mime, + $show_dates = false, + $add_semicolon = true, + $view = false, + array $aliases = [] + ) { + global $cfgRelation; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + // Check if we can use Relations + list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus( + $do_relation && ! empty($cfgRelation['relation']), + $db, + $table + ); + /** + * Displays the table structure + */ + $GLOBALS['odt_buffer'] .= ''; + $columns_cnt = 4; + if ($do_relation && $have_rel) { + $columns_cnt++; + } + if ($do_comments) { + $columns_cnt++; + } + if ($do_mime && $cfgRelation['mimework']) { + $columns_cnt++; + } + $GLOBALS['odt_buffer'] .= ''; + /* Header */ + $GLOBALS['odt_buffer'] .= '' + . '' + . '' . __('Column') . '' + . '' + . '' + . '' . __('Type') . '' + . '' + . '' + . '' . __('Null') . '' + . '' + . '' + . '' . __('Default') . '' + . ''; + if ($do_relation && $have_rel) { + $GLOBALS['odt_buffer'] .= '' + . '' . __('Links to') . '' + . ''; + } + if ($do_comments) { + $GLOBALS['odt_buffer'] .= '' + . '' . __('Comments') . '' + . ''; + $comments = $this->relation->getComments($db, $table); + } + if ($do_mime && $cfgRelation['mimework']) { + $GLOBALS['odt_buffer'] .= '' + . '' . __('Media (MIME) type') . '' + . ''; + $mime_map = $this->transformations->getMime($db, $table, true); + } + $GLOBALS['odt_buffer'] .= ''; + + $columns = $GLOBALS['dbi']->getColumns($db, $table); + foreach ($columns as $column) { + $col_as = $field_name = $column['Field']; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $GLOBALS['odt_buffer'] .= $this->formatOneColumnDefinition( + $column, + $col_as + ); + if ($do_relation && $have_rel) { + $foreigner = $this->relation->searchColumnInForeigners($res_rel, $field_name); + if ($foreigner) { + $rtable = $foreigner['foreign_table']; + $rfield = $foreigner['foreign_field']; + if (! empty($aliases[$db]['tables'][$rtable]['columns'][$rfield]) + ) { + $rfield + = $aliases[$db]['tables'][$rtable]['columns'][$rfield]; + } + if (! empty($aliases[$db]['tables'][$rtable]['alias'])) { + $rtable = $aliases[$db]['tables'][$rtable]['alias']; + } + $relation = htmlspecialchars($rtable . ' (' . $rfield . ')'); + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars($relation) + . '' + . ''; + } + } + if ($do_comments) { + if (isset($comments[$field_name])) { + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars($comments[$field_name]) + . '' + . ''; + } else { + $GLOBALS['odt_buffer'] + .= '' + . '' + . ''; + } + } + if ($do_mime && $cfgRelation['mimework']) { + if (isset($mime_map[$field_name])) { + $GLOBALS['odt_buffer'] + .= '' + . '' + . htmlspecialchars( + str_replace('_', '/', $mime_map[$field_name]['mimetype']) + ) + . '' + . ''; + } else { + $GLOBALS['odt_buffer'] + .= '' + . '' + . ''; + } + } + $GLOBALS['odt_buffer'] .= ''; + } // end foreach + + $GLOBALS['odt_buffer'] .= ''; + + return true; + } // end of the '$this->getTableDef()' function + + /** + * Outputs triggers + * + * @param string $db database name + * @param string $table table name + * @param array $aliases Aliases of db/table/columns + * + * @return bool true + */ + protected function getTriggers($db, $table, array $aliases = []) + { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $GLOBALS['odt_buffer'] .= '' + . '' + . '' + . '' + . '' . __('Name') . '' + . '' + . '' + . '' . __('Time') . '' + . '' + . '' + . '' . __('Event') . '' + . '' + . '' + . '' . __('Definition') . '' + . '' + . ''; + + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + + foreach ($triggers as $trigger) { + $GLOBALS['odt_buffer'] .= ''; + $GLOBALS['odt_buffer'] .= '' + . '' + . htmlspecialchars($trigger['name']) + . '' + . ''; + $GLOBALS['odt_buffer'] .= '' + . '' + . htmlspecialchars($trigger['action_timing']) + . '' + . ''; + $GLOBALS['odt_buffer'] .= '' + . '' + . htmlspecialchars($trigger['event_manipulation']) + . '' + . ''; + $GLOBALS['odt_buffer'] .= '' + . '' + . htmlspecialchars($trigger['definition']) + . '' + . ''; + $GLOBALS['odt_buffer'] .= ''; + } + + $GLOBALS['odt_buffer'] .= ''; + + return true; + } + + /** + * Outputs table's structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table', 'triggers', 'create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + switch ($export_mode) { + case 'create_table': + $GLOBALS['odt_buffer'] + .= '' + . __('Table structure for table') . ' ' . + htmlspecialchars($table_alias) + . ''; + $this->getTableDef( + $db, + $table, + $crlf, + $error_url, + $do_relation, + $do_comments, + $do_mime, + $dates, + true, + false, + $aliases + ); + break; + case 'triggers': + $triggers = $GLOBALS['dbi']->getTriggers($db, $table, $aliases); + if ($triggers) { + $GLOBALS['odt_buffer'] + .= '' + . __('Triggers') . ' ' + . htmlspecialchars($table_alias) + . ''; + $this->getTriggers($db, $table); + } + break; + case 'create_view': + $GLOBALS['odt_buffer'] + .= '' + . __('Structure for view') . ' ' + . htmlspecialchars($table_alias) + . ''; + $this->getTableDef( + $db, + $table, + $crlf, + $error_url, + $do_relation, + $do_comments, + $do_mime, + $dates, + true, + true, + $aliases + ); + break; + case 'stand_in': + $GLOBALS['odt_buffer'] + .= '' + . __('Stand-in structure for view') . ' ' + . htmlspecialchars($table_alias) + . ''; + // export a stand-in definition to resolve view dependencies + $this->getTableDefStandIn($db, $table, $crlf, $aliases); + } // end switch + + return true; + } // end of the '$this->exportStructure' function + + /** + * Formats the definition for one column + * + * @param array $column info about this column + * @param string $col_as column alias + * + * @return string Formatted column definition + */ + protected function formatOneColumnDefinition($column, $col_as = '') + { + if (empty($col_as)) { + $col_as = $column['Field']; + } + $definition = ''; + $definition .= '' + . '' . htmlspecialchars($col_as) . '' + . ''; + + $extracted_columnspec + = Util::extractColumnSpec($column['Type']); + $type = htmlspecialchars($extracted_columnspec['print_type']); + if (empty($type)) { + $type = ' '; + } + + $definition .= '' + . '' . htmlspecialchars($type) . '' + . ''; + if (! isset($column['Default'])) { + if ($column['Null'] != 'NO') { + $column['Default'] = 'NULL'; + } else { + $column['Default'] = ''; + } + } + $definition .= '' + . '' + . (($column['Null'] == '' || $column['Null'] == 'NO') + ? __('No') + : __('Yes')) + . '' + . ''; + $definition .= '' + . '' . htmlspecialchars($column['Default']) . '' + . ''; + + return $definition; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPdf.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPdf.php new file mode 100644 index 0000000..49e74b3 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPdf.php @@ -0,0 +1,395 @@ +initSpecificVariables(); + + $this->setProperties(); + } + + /** + * Initialize the local variables that are used for export PDF + * + * @return void + */ + protected function initSpecificVariables() + { + if (! empty($_POST['pdf_report_title'])) { + $this->_setPdfReportTitle($_POST['pdf_report_title']); + } + $this->_setPdf(new Pdf('L', 'pt', 'A3')); + } + + /** + * Sets the export PDF properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('PDF'); + $exportPluginProperties->setExtension('pdf'); + $exportPluginProperties->setMimeType('application/pdf'); + $exportPluginProperties->setForceFile(true); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new TextPropertyItem( + "report_title", + __('Report title:') + ); + $generalOptions->addProperty($leaf); + // add the group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // what to dump (structure/data/both) main group + $dumpWhat = new OptionsPropertyMainGroup( + "dump_what", + __('Dump table') + ); + $leaf = new RadioPropertyItem("structure_or_data"); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $dumpWhat->addProperty($leaf); + // add the group to the root group + $exportSpecificOptions->addProperty($dumpWhat); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + $pdf_report_title = $this->_getPdfReportTitle(); + $pdf = $this->_getPdf(); + $pdf->Open(); + + $attr = [ + 'titleFontSize' => 18, + 'titleText' => $pdf_report_title, + ]; + $pdf->setAttributes($attr); + $pdf->setTopMargin(30); + + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + $pdf = $this->_getPdf(); + + // instead of $pdf->Output(): + return $this->export->outputHandler($pdf->getPDFData()); + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in NHibernate format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $pdf = $this->_getPdf(); + $attr = [ + 'currentDb' => $db, + 'currentTable' => $table, + 'dbAlias' => $db_alias, + 'tableAlias' => $table_alias, + 'aliases' => $aliases, + 'purpose' => __('Dumping data'), + ]; + $pdf->setAttributes($attr); + $pdf->mysqlReport($sql_query); + + return true; + } // end of the 'PMA_exportData()' function + + /** + * Outputs table structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table', 'triggers', 'create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases aliases for db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $purpose = null; + $this->initAlias($aliases, $db_alias, $table_alias); + $pdf = $this->_getPdf(); + // getting purpose to show at top + switch ($export_mode) { + case 'create_table': + $purpose = __('Table structure'); + break; + case 'triggers': + $purpose = __('Triggers'); + break; + case 'create_view': + $purpose = __('View structure'); + break; + case 'stand_in': + $purpose = __('Stand in'); + } // end switch + + $attr = [ + 'currentDb' => $db, + 'currentTable' => $table, + 'dbAlias' => $db_alias, + 'tableAlias' => $table_alias, + 'aliases' => $aliases, + 'purpose' => $purpose, + ]; + $pdf->setAttributes($attr); + /** + * comment display set true as presently in pdf + * format, no option is present to take user input. + */ + $do_comments = true; + switch ($export_mode) { + case 'create_table': + $pdf->getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + false, + $aliases + ); + break; + case 'triggers': + $pdf->getTriggers($db, $table); + break; + case 'create_view': + $pdf->getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + false, + $aliases + ); + break; + case 'stand_in': + /* export a stand-in definition to resolve view dependencies + * Yet to develop this function + * $pdf->getTableDefStandIn($db, $table, $crlf); + */ + } // end switch + + return true; + } + + + /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ + + /** + * Gets the PhpMyAdmin\Plugins\Export\Helpers\Pdf instance + * + * @return Pdf + */ + private function _getPdf() + { + return $this->_pdf; + } + + /** + * Instantiates the PhpMyAdmin\Plugins\Export\Helpers\Pdf class + * + * @param Pdf $pdf The instance + * + * @return void + */ + private function _setPdf($pdf) + { + $this->_pdf = $pdf; + } + + /** + * Gets the PDF report title + * + * @return string + */ + private function _getPdfReportTitle() + { + return $this->_pdfReportTitle; + } + + /** + * Sets the PDF report title + * + * @param string $pdfReportTitle PDF report title + * + * @return void + */ + private function _setPdfReportTitle($pdfReportTitle) + { + $this->_pdfReportTitle = $pdfReportTitle; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPhparray.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPhparray.php new file mode 100644 index 0000000..3fe9bd1 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportPhparray.php @@ -0,0 +1,259 @@ +setProperties(); + } + + /** + * Sets the export PHP Array properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('PHP array'); + $exportPluginProperties->setExtension('php'); + $exportPluginProperties->setMimeType('text/plain'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new HiddenPropertyItem("structure_or_data"); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Removes end of comment from a string + * + * @param string $string String to replace + * + * @return string + */ + public function commentString($string) + { + return strtr($string, '*/', '-'); + } + + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + $this->export->outputHandler( + 'export->outputHandler( + '/**' . $GLOBALS['crlf'] + . ' * Database ' . $this->commentString(Util::backquote($db_alias)) + . $GLOBALS['crlf'] . ' */' . $GLOBALS['crlf'] + ); + + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in PHP array format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + + $columns_cnt = $GLOBALS['dbi']->numFields($result); + $columns = []; + for ($i = 0; $i < $columns_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $columns[$i] = stripslashes($col_as); + } + + // fix variable names (based on + // https://www.php.net/manual/en/language.variables.basics.php) + if (! preg_match( + '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', + $table_alias + ) + ) { + // fix invalid characters in variable names by replacing them with + // underscores + $tablefixed = preg_replace( + '/[^a-zA-Z0-9_\x7f-\xff]/', + '_', + $table_alias + ); + + // variable name must not start with a number or dash... + if (preg_match('/^[a-zA-Z_\x7f-\xff]/', $tablefixed) === 0) { + $tablefixed = '_' . $tablefixed; + } + } else { + $tablefixed = $table; + } + + $buffer = ''; + $record_cnt = 0; + // Output table name as comment + $buffer .= $crlf . '/* ' + . $this->commentString(Util::backquote($db_alias)) . '.' + . $this->commentString(Util::backquote($table_alias)) . ' */' . $crlf; + $buffer .= '$' . $tablefixed . ' = array('; + + while ($record = $GLOBALS['dbi']->fetchRow($result)) { + $record_cnt++; + + if ($record_cnt == 1) { + $buffer .= $crlf . ' array('; + } else { + $buffer .= ',' . $crlf . ' array('; + } + + for ($i = 0; $i < $columns_cnt; $i++) { + $buffer .= var_export($columns[$i], true) + . " => " . var_export($record[$i], true) + . (($i + 1 >= $columns_cnt) ? '' : ','); + } + + $buffer .= ')'; + } + + $buffer .= $crlf . ');' . $crlf; + if (! $this->export->outputHandler($buffer)) { + return false; + } + + $GLOBALS['dbi']->freeResult($result); + + return true; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportSql.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportSql.php new file mode 100644 index 0000000..9c5b1a2 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportSql.php @@ -0,0 +1,2915 @@ +setProperties(); + + // Avoids undefined variables, use NULL so isset() returns false + if (! isset($GLOBALS['sql_backquotes'])) { + $GLOBALS['sql_backquotes'] = null; + } + } + + /** + * Sets the export SQL properties + * + * @return void + */ + protected function setProperties() + { + global $plugin_param; + + $hide_sql = false; + $hide_structure = false; + if ($plugin_param['export_type'] == 'table' + && ! $plugin_param['single_table'] + ) { + $hide_structure = true; + $hide_sql = true; + } + + if (! $hide_sql) { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('SQL'); + $exportPluginProperties->setExtension('sql'); + $exportPluginProperties->setMimeType('text/x-sql'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + + // comments + $subgroup = new OptionsPropertySubgroup("include_comments"); + $leaf = new BoolPropertyItem( + 'include_comments', + __( + 'Display comments (includes info such as export' + . ' timestamp, PHP version, and server version)' + ) + ); + $subgroup->setSubgroupHeader($leaf); + + $leaf = new TextPropertyItem( + 'header_comment', + __('Additional custom header comment (\n splits lines):') + ); + $subgroup->addProperty($leaf); + $leaf = new BoolPropertyItem( + 'dates', + __( + 'Include a timestamp of when databases were created, last' + . ' updated, and last checked' + ) + ); + $subgroup->addProperty($leaf); + if (! empty($GLOBALS['cfgRelation']['relation'])) { + $leaf = new BoolPropertyItem( + 'relation', + __('Display foreign key relationships') + ); + $subgroup->addProperty($leaf); + } + if (! empty($GLOBALS['cfgRelation']['mimework'])) { + $leaf = new BoolPropertyItem( + 'mime', + __('Display media (MIME) types') + ); + $subgroup->addProperty($leaf); + } + $generalOptions->addProperty($subgroup); + + // enclose in a transaction + $leaf = new BoolPropertyItem( + "use_transaction", + __('Enclose export in a transaction') + ); + $leaf->setDoc( + [ + 'programs', + 'mysqldump', + 'option_mysqldump_single-transaction', + ] + ); + $generalOptions->addProperty($leaf); + + // disable foreign key checks + $leaf = new BoolPropertyItem( + "disable_fk", + __('Disable foreign key checks') + ); + $leaf->setDoc( + [ + 'manual_MySQL_Database_Administration', + 'server-system-variables', + 'sysvar_foreign_key_checks', + ] + ); + $generalOptions->addProperty($leaf); + + // export views as tables + $leaf = new BoolPropertyItem( + "views_as_tables", + __('Export views as tables') + ); + $generalOptions->addProperty($leaf); + + // export metadata + $leaf = new BoolPropertyItem( + "metadata", + __('Export metadata') + ); + $generalOptions->addProperty($leaf); + + // compatibility maximization + $compats = $GLOBALS['dbi']->getCompatibilities(); + if (count($compats) > 0) { + $values = []; + foreach ($compats as $val) { + $values[$val] = $val; + } + + $leaf = new SelectPropertyItem( + "compatibility", + __( + 'Database system or older MySQL server to maximize output' + . ' compatibility with:' + ) + ); + $leaf->setValues($values); + $leaf->setDoc( + [ + 'manual_MySQL_Database_Administration', + 'Server_SQL_mode', + ] + ); + $generalOptions->addProperty($leaf); + + unset($values); + } + + // what to dump (structure/data/both) + $subgroup = new OptionsPropertySubgroup( + "dump_table", + __("Dump table") + ); + $leaf = new RadioPropertyItem('structure_or_data'); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $subgroup->setSubgroupHeader($leaf); + $generalOptions->addProperty($subgroup); + + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // structure options main group + if (! $hide_structure) { + $structureOptions = new OptionsPropertyMainGroup( + "structure", + __('Object creation options') + ); + $structureOptions->setForce('data'); + + // begin SQL Statements + $subgroup = new OptionsPropertySubgroup(); + $leaf = new MessageOnlyPropertyItem( + 'add_statements', + __('Add statements:') + ); + $subgroup->setSubgroupHeader($leaf); + + // server export options + if ($plugin_param['export_type'] == 'server') { + $leaf = new BoolPropertyItem( + "drop_database", + sprintf(__('Add %s statement'), 'DROP DATABASE IF EXISTS') + ); + $subgroup->addProperty($leaf); + } + + if ($plugin_param['export_type'] == 'database') { + $create_clause = 'CREATE DATABASE / USE'; + $leaf = new BoolPropertyItem( + 'create_database', + sprintf(__('Add %s statement'), $create_clause) + ); + $subgroup->addProperty($leaf); + } + + if ($plugin_param['export_type'] == 'table') { + $drop_clause = $GLOBALS['dbi']->getTable( + $GLOBALS['db'], + $GLOBALS['table'] + )->isView() + ? 'DROP VIEW' + : 'DROP TABLE'; + } else { + $drop_clause = 'DROP TABLE / VIEW / PROCEDURE' + . ' / FUNCTION / EVENT'; + } + + $drop_clause .= ' / TRIGGER'; + + $leaf = new BoolPropertyItem( + 'drop_table', + sprintf(__('Add %s statement'), $drop_clause) + ); + $subgroup->addProperty($leaf); + + $subgroup_create_table = new OptionsPropertySubgroup(); + + // Add table structure option + $leaf = new BoolPropertyItem( + 'create_table', + sprintf(__('Add %s statement'), 'CREATE TABLE') + ); + $subgroup_create_table->setSubgroupHeader($leaf); + + $leaf = new BoolPropertyItem( + 'if_not_exists', + 'IF NOT EXISTS ' . __( + '(less efficient as indexes will be generated during table ' + . 'creation)' + ) + ); + $subgroup_create_table->addProperty($leaf); + + $leaf = new BoolPropertyItem( + 'auto_increment', + sprintf(__('%s value'), 'AUTO_INCREMENT') + ); + $subgroup_create_table->addProperty($leaf); + + $subgroup->addProperty($subgroup_create_table); + + // Add view option + $subgroup_create_view = new OptionsPropertySubgroup(); + $leaf = new BoolPropertyItem( + 'create_view', + sprintf(__('Add %s statement'), 'CREATE VIEW') + ); + $subgroup_create_view->setSubgroupHeader($leaf); + + $leaf = new BoolPropertyItem( + 'view_current_user', + __('Exclude definition of current user') + ); + $subgroup_create_view->addProperty($leaf); + + $leaf = new BoolPropertyItem( + 'or_replace_view', + sprintf(__('%s view'), 'OR REPLACE') + ); + $subgroup_create_view->addProperty($leaf); + + $subgroup->addProperty($subgroup_create_view); + + $leaf = new BoolPropertyItem( + 'procedure_function', + sprintf( + __('Add %s statement'), + 'CREATE PROCEDURE / FUNCTION / EVENT' + ) + ); + $subgroup->addProperty($leaf); + + // Add triggers option + $leaf = new BoolPropertyItem( + 'create_trigger', + sprintf(__('Add %s statement'), 'CREATE TRIGGER') + ); + $subgroup->addProperty($leaf); + + $structureOptions->addProperty($subgroup); + + $leaf = new BoolPropertyItem( + "backquotes", + __( + 'Enclose table and column names with backquotes ' + . '(Protects column and table names formed with' + . ' special characters or keywords)' + ) + ); + + $structureOptions->addProperty($leaf); + + // add the main group to the root group + $exportSpecificOptions->addProperty($structureOptions); + } + + // begin Data options + $dataOptions = new OptionsPropertyMainGroup( + "data", + __('Data creation options') + ); + $dataOptions->setForce('structure'); + $leaf = new BoolPropertyItem( + "truncate", + __('Truncate table before insert') + ); + $dataOptions->addProperty($leaf); + + // begin SQL Statements + $subgroup = new OptionsPropertySubgroup(); + $leaf = new MessageOnlyPropertyItem( + __('Instead of INSERT statements, use:') + ); + $subgroup->setSubgroupHeader($leaf); + + $leaf = new BoolPropertyItem( + "delayed", + __('INSERT DELAYED statements') + ); + $leaf->setDoc( + [ + 'manual_MySQL_Database_Administration', + 'insert_delayed', + ] + ); + $subgroup->addProperty($leaf); + + $leaf = new BoolPropertyItem( + "ignore", + __('INSERT IGNORE statements') + ); + $leaf->setDoc( + [ + 'manual_MySQL_Database_Administration', + 'insert', + ] + ); + $subgroup->addProperty($leaf); + $dataOptions->addProperty($subgroup); + + // Function to use when dumping dat + $leaf = new SelectPropertyItem( + "type", + __('Function to use when dumping data:') + ); + $leaf->setValues( + [ + 'INSERT' => 'INSERT', + 'UPDATE' => 'UPDATE', + 'REPLACE' => 'REPLACE', + ] + ); + $dataOptions->addProperty($leaf); + + /* Syntax to use when inserting data */ + $subgroup = new OptionsPropertySubgroup(); + $leaf = new MessageOnlyPropertyItem( + null, + __('Syntax to use when inserting data:') + ); + $subgroup->setSubgroupHeader($leaf); + $leaf = new RadioPropertyItem( + "insert_syntax", + __('INSERT IGNORE statements') + ); + $leaf->setValues( + [ + 'complete' => __( + 'include column names in every INSERT statement' + . '
      Example: INSERT INTO' + . ' tbl_name (col_A,col_B,col_C) VALUES (1,2,3)' + ), + 'extended' => __( + 'insert multiple rows in every INSERT statement' + . '
      Example: INSERT INTO' + . ' tbl_name VALUES (1,2,3), (4,5,6), (7,8,9)' + ), + 'both' => __( + 'both of the above
      Example:' + . ' INSERT INTO tbl_name (col_A,col_B,col_C) VALUES' + . ' (1,2,3), (4,5,6), (7,8,9)' + ), + 'none' => __( + 'neither of the above
      Example:' + . ' INSERT INTO tbl_name VALUES (1,2,3)' + ), + ] + ); + $subgroup->addProperty($leaf); + $dataOptions->addProperty($subgroup); + + // Max length of query + $leaf = new NumberPropertyItem( + "max_query_size", + __('Maximal length of created query') + ); + $dataOptions->addProperty($leaf); + + // Dump binary columns in hexadecimal + $leaf = new BoolPropertyItem( + "hex_for_binary", + __( + 'Dump binary columns in hexadecimal notation' + . ' (for example, "abc" becomes 0x616263)' + ) + ); + $dataOptions->addProperty($leaf); + + // Dump time in UTC + $leaf = new BoolPropertyItem( + "utc_time", + __( + 'Dump TIMESTAMP columns in UTC (enables TIMESTAMP columns' + . ' to be dumped and reloaded between servers in different' + . ' time zones)' + ) + ); + $dataOptions->addProperty($leaf); + + // add the main group to the root group + $exportSpecificOptions->addProperty($dataOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + } + + /** + * Generates SQL for routines export + * + * @param string $db Database + * @param array $aliases Aliases of db/table/columns + * @param string $type Type of exported routine + * @param string $name Verbose name of exported routine + * @param array $routines List of routines to export + * @param string $delimiter Delimiter to use in SQL + * + * @return string SQL query + */ + protected function _exportRoutineSQL( + $db, + array $aliases, + $type, + $name, + array $routines, + $delimiter + ) { + global $crlf; + + $text = $this->_exportComment() + . $this->_exportComment($name) + . $this->_exportComment(); + + $used_alias = false; + $proc_query = ''; + + foreach ($routines as $routine) { + if (! empty($GLOBALS['sql_drop_table'])) { + $proc_query .= 'DROP ' . $type . ' IF EXISTS ' + . Util::backquote($routine) + . $delimiter . $crlf; + } + $create_query = $this->replaceWithAliases( + $GLOBALS['dbi']->getDefinition($db, $type, $routine), + $aliases, + $db, + '', + $flag + ); + // One warning per database + if ($flag) { + $used_alias = true; + } + $proc_query .= $create_query . $delimiter . $crlf . $crlf; + } + if ($used_alias) { + $text .= $this->_exportComment( + __('It appears your database uses routines;') + ) + . $this->_exportComment( + __('alias export may not work reliably in all cases.') + ) + . $this->_exportComment(); + } + $text .= $proc_query; + + return $text; + } + + /** + * Exports routines (procedures and functions) + * + * @param string $db Database + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportRoutines($db, array $aliases = []) + { + global $crlf; + + $db_alias = $db; + $this->initAlias($aliases, $db_alias); + + $text = ''; + $delimiter = '$$'; + + $procedure_names = $GLOBALS['dbi'] + ->getProceduresOrFunctions($db, 'PROCEDURE'); + $function_names = $GLOBALS['dbi']->getProceduresOrFunctions($db, 'FUNCTION'); + + if ($procedure_names || $function_names) { + $text .= $crlf + . 'DELIMITER ' . $delimiter . $crlf; + + if ($procedure_names) { + $text .= $this->_exportRoutineSQL( + $db, + $aliases, + 'PROCEDURE', + __('Procedures'), + $procedure_names, + $delimiter + ); + } + + if ($function_names) { + $text .= $this->_exportRoutineSQL( + $db, + $aliases, + 'FUNCTION', + __('Functions'), + $function_names, + $delimiter + ); + } + + $text .= 'DELIMITER ;' . $crlf; + } + + if (! empty($text)) { + return $this->export->outputHandler($text); + } + + return false; + } + + /** + * Possibly outputs comment + * + * @param string $text Text of comment + * + * @return string The formatted comment + */ + private function _exportComment($text = '') + { + if (isset($GLOBALS['sql_include_comments']) + && $GLOBALS['sql_include_comments'] + ) { + // see https://dev.mysql.com/doc/refman/5.0/en/ansi-diff-comments.html + if (empty($text)) { + return '--' . $GLOBALS['crlf']; + } + + $lines = preg_split("/\\r\\n|\\r|\\n/", $text); + $result = []; + foreach ($lines as $line) { + $result[] = '-- ' . $line . $GLOBALS['crlf']; + } + return implode('', $result); + } + + return ''; + } + + /** + * Possibly outputs CRLF + * + * @return string crlf or nothing + */ + private function _possibleCRLF() + { + if (isset($GLOBALS['sql_include_comments']) + && $GLOBALS['sql_include_comments'] + ) { + return $GLOBALS['crlf']; + } + + return ''; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + global $crlf; + + $foot = ''; + + if (isset($GLOBALS['sql_disable_fk'])) { + $foot .= 'SET FOREIGN_KEY_CHECKS=1;' . $crlf; + } + + if (isset($GLOBALS['sql_use_transaction'])) { + $foot .= 'COMMIT;' . $crlf; + } + + // restore connection settings + if ($this->_sent_charset) { + $foot .= $crlf + . '/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;' + . $crlf + . '/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;' + . $crlf + . '/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;' + . $crlf; + $this->_sent_charset = false; + } + + /* Restore timezone */ + if (isset($GLOBALS['sql_utc_time']) && $GLOBALS['sql_utc_time']) { + $GLOBALS['dbi']->query('SET time_zone = "' . $GLOBALS['old_tz'] . '"'); + } + + return $this->export->outputHandler($foot); + } + + /** + * Outputs export header. It is the first method to be called, so all + * the required variables are initialized here. + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + global $crlf, $cfg; + + if (isset($GLOBALS['sql_compatibility'])) { + $tmp_compat = $GLOBALS['sql_compatibility']; + if ($tmp_compat == 'NONE') { + $tmp_compat = ''; + } + $GLOBALS['dbi']->tryQuery('SET SQL_MODE="' . $tmp_compat . '"'); + unset($tmp_compat); + } + $head = $this->_exportComment('phpMyAdmin SQL Dump') + . $this->_exportComment('version ' . PMA_VERSION) + . $this->_exportComment('https://www.phpmyadmin.net/') + . $this->_exportComment(); + $host_string = __('Host:') . ' ' . $cfg['Server']['host']; + if (! empty($cfg['Server']['port'])) { + $host_string .= ':' . $cfg['Server']['port']; + } + $head .= $this->_exportComment($host_string); + $head .= $this->_exportComment( + __('Generation Time:') . ' ' + . Util::localisedDate() + ) + . $this->_exportComment( + __('Server version:') . ' ' . $GLOBALS['dbi']->getVersionString() + ) + . $this->_exportComment(__('PHP Version:') . ' ' . PHP_VERSION) + . $this->_possibleCRLF(); + + if (isset($GLOBALS['sql_header_comment']) + && ! empty($GLOBALS['sql_header_comment']) + ) { + // '\n' is not a newline (like "\n" would be), it's the characters + // backslash and n, as explained on the export interface + $lines = explode('\n', $GLOBALS['sql_header_comment']); + $head .= $this->_exportComment(); + foreach ($lines as $one_line) { + $head .= $this->_exportComment($one_line); + } + $head .= $this->_exportComment(); + } + + if (isset($GLOBALS['sql_disable_fk'])) { + $head .= 'SET FOREIGN_KEY_CHECKS=0;' . $crlf; + } + + // We want exported AUTO_INCREMENT columns to have still same value, + // do this only for recent MySQL exports + if (! isset($GLOBALS['sql_compatibility']) + || $GLOBALS['sql_compatibility'] == 'NONE' + ) { + $head .= 'SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";' . $crlf; + } + + if (isset($GLOBALS['sql_use_transaction'])) { + $head .= 'SET AUTOCOMMIT = 0;' . $crlf + . 'START TRANSACTION;' . $crlf; + } + + /* Change timezone if we should export timestamps in UTC */ + if (isset($GLOBALS['sql_utc_time']) && $GLOBALS['sql_utc_time']) { + $head .= 'SET time_zone = "+00:00";' . $crlf; + $GLOBALS['old_tz'] = $GLOBALS['dbi'] + ->fetchValue('SELECT @@session.time_zone'); + $GLOBALS['dbi']->query('SET time_zone = "+00:00"'); + } + + $head .= $this->_possibleCRLF(); + + if (! empty($GLOBALS['asfile'])) { + // we are saving as file, therefore we provide charset information + // so that a utility like the mysql client can interpret + // the file correctly + if (isset($GLOBALS['charset']) + && isset(Charsets::$mysqlCharsetMap[$GLOBALS['charset']]) + ) { + // we got a charset from the export dialog + $set_names = Charsets::$mysqlCharsetMap[$GLOBALS['charset']]; + } else { + // by default we use the connection charset + $set_names = Charsets::$mysqlCharsetMap['utf-8']; + } + if ($set_names == 'utf8' && $GLOBALS['dbi']->getVersion() > 50503) { + $set_names = 'utf8mb4'; + } + $head .= $crlf + . '/*!40101 SET @OLD_CHARACTER_SET_CLIENT=' + . '@@CHARACTER_SET_CLIENT */;' . $crlf + . '/*!40101 SET @OLD_CHARACTER_SET_RESULTS=' + . '@@CHARACTER_SET_RESULTS */;' . $crlf + . '/*!40101 SET @OLD_COLLATION_CONNECTION=' + . '@@COLLATION_CONNECTION */;' . $crlf + . '/*!40101 SET NAMES ' . $set_names . ' */;' . $crlf . $crlf; + $this->_sent_charset = true; + } + + return $this->export->outputHandler($head); + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + global $crlf; + + if (empty($db_alias)) { + $db_alias = $db; + } + if (isset($GLOBALS['sql_compatibility'])) { + $compat = $GLOBALS['sql_compatibility']; + } else { + $compat = 'NONE'; + } + if (isset($GLOBALS['sql_drop_database'])) { + if (! $this->export->outputHandler( + 'DROP DATABASE IF EXISTS ' + . Util::backquoteCompat( + $db_alias, + $compat, + isset($GLOBALS['sql_backquotes']) + ) + . ';' . $crlf + ) + ) { + return false; + } + } + if ($export_type == 'database' && ! isset($GLOBALS['sql_create_database'])) { + return true; + } + + $create_query = 'CREATE DATABASE IF NOT EXISTS ' + . Util::backquoteCompat( + $db_alias, + $compat, + isset($GLOBALS['sql_backquotes']) + ); + $collation = $GLOBALS['dbi']->getDbCollation($db); + if (mb_strpos($collation, '_')) { + $create_query .= ' DEFAULT CHARACTER SET ' + . mb_substr( + $collation, + 0, + mb_strpos($collation, '_') + ) + . ' COLLATE ' . $collation; + } else { + $create_query .= ' DEFAULT CHARACTER SET ' . $collation; + } + $create_query .= ';' . $crlf; + if (! $this->export->outputHandler($create_query)) { + return false; + } + + return $this->_exportUseStatement($db_alias, $compat); + } + + /** + * Outputs USE statement + * + * @param string $db db to use + * @param string $compat sql compatibility + * + * @return bool Whether it succeeded + */ + private function _exportUseStatement($db, $compat) + { + global $crlf; + + if (isset($GLOBALS['sql_compatibility']) + && $GLOBALS['sql_compatibility'] == 'NONE' + ) { + $result = $this->export->outputHandler( + 'USE ' + . Util::backquoteCompat( + $db, + $compat, + isset($GLOBALS['sql_backquotes']) + ) + . ';' . $crlf + ); + } else { + $result = $this->export->outputHandler('USE ' . $db . ';' . $crlf); + } + + return $result; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Alias of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + if (empty($db_alias)) { + $db_alias = $db; + } + if (isset($GLOBALS['sql_compatibility'])) { + $compat = $GLOBALS['sql_compatibility']; + } else { + $compat = 'NONE'; + } + $head = $this->_exportComment() + . $this->_exportComment( + __('Database:') . ' ' + . Util::backquoteCompat( + $db_alias, + $compat, + isset($GLOBALS['sql_backquotes']) + ) + ) + . $this->_exportComment(); + + return $this->export->outputHandler($head); + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + global $crlf; + + $result = true; + + //add indexes to the sql dump file + if (isset($GLOBALS['sql_indexes'])) { + $result = $this->export->outputHandler($GLOBALS['sql_indexes']); + unset($GLOBALS['sql_indexes']); + } + //add auto increments to the sql dump file + if (isset($GLOBALS['sql_auto_increments'])) { + $result = $this->export->outputHandler($GLOBALS['sql_auto_increments']); + unset($GLOBALS['sql_auto_increments']); + } + //add constraints to the sql dump file + if (isset($GLOBALS['sql_constraints'])) { + $result = $this->export->outputHandler($GLOBALS['sql_constraints']); + unset($GLOBALS['sql_constraints']); + } + + return $result; + } + + /** + * Exports events + * + * @param string $db Database + * + * @return bool Whether it succeeded + */ + public function exportEvents($db) + { + global $crlf; + + $text = ''; + $delimiter = '$$'; + + $event_names = $GLOBALS['dbi']->fetchResult( + "SELECT EVENT_NAME FROM information_schema.EVENTS WHERE" + . " EVENT_SCHEMA= '" . $GLOBALS['dbi']->escapeString($db) + . "';" + ); + + if ($event_names) { + $text .= $crlf + . "DELIMITER " . $delimiter . $crlf; + + $text .= $this->_exportComment() + . $this->_exportComment(__('Events')) + . $this->_exportComment(); + + foreach ($event_names as $event_name) { + if (! empty($GLOBALS['sql_drop_table'])) { + $text .= "DROP EVENT " + . Util::backquote($event_name) + . $delimiter . $crlf; + } + $text .= $GLOBALS['dbi']->getDefinition($db, 'EVENT', $event_name) + . $delimiter . $crlf . $crlf; + } + + $text .= "DELIMITER ;" . $crlf; + } + + if (! empty($text)) { + return $this->export->outputHandler($text); + } + + return false; + } + + /** + * Exports metadata from Configuration Storage + * + * @param string $db database being exported + * @param string|array $tables table(s) being exported + * @param array $metadataTypes types of metadata to export + * + * @return bool Whether it succeeded + */ + public function exportMetadata( + $db, + $tables, + array $metadataTypes + ) { + $cfgRelation = $this->relation->getRelationsParam(); + if (! isset($cfgRelation['db'])) { + return true; + } + + $comment = $this->_possibleCRLF() + . $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment(__('Metadata')) + . $this->_exportComment(); + if (! $this->export->outputHandler($comment)) { + return false; + } + + if (! $this->_exportUseStatement( + $cfgRelation['db'], + $GLOBALS['sql_compatibility'] + ) + ) { + return false; + } + + $r = true; + if (is_array($tables)) { + // export metadata for each table + foreach ($tables as $table) { + $r &= $this->_exportMetadata($db, $table, $metadataTypes); + } + // export metadata for the database + $r &= $this->_exportMetadata($db, null, $metadataTypes); + } else { + // export metadata for single table + $r &= $this->_exportMetadata($db, $tables, $metadataTypes); + } + + return $r; + } + + /** + * Exports metadata from Configuration Storage + * + * @param string $db database being exported + * @param string $table table being exported + * @param array $metadataTypes types of metadata to export + * + * @return bool Whether it succeeded + */ + private function _exportMetadata( + $db, + $table, + array $metadataTypes + ) { + $cfgRelation = $this->relation->getRelationsParam(); + + if (isset($table)) { + $types = [ + 'column_info' => 'db_name', + 'table_uiprefs' => 'db_name', + 'tracking' => 'db_name', + ]; + } else { + $types = [ + 'bookmark' => 'dbase', + 'relation' => 'master_db', + 'pdf_pages' => 'db_name', + 'savedsearches' => 'db_name', + 'central_columns' => 'db_name', + ]; + } + + $aliases = []; + + $comment = $this->_possibleCRLF() + . $this->_exportComment(); + + if (isset($table)) { + $comment .= $this->_exportComment( + sprintf( + __('Metadata for table %s'), + $table + ) + ); + } else { + $comment .= $this->_exportComment( + sprintf( + __('Metadata for database %s'), + $db + ) + ); + } + + $comment .= $this->_exportComment(); + + if (! $this->export->outputHandler($comment)) { + return false; + } + + foreach ($types as $type => $dbNameColumn) { + if (in_array($type, $metadataTypes) && isset($cfgRelation[$type])) { + // special case, designer pages and their coordinates + if ($type == 'pdf_pages') { + $sql_query = "SELECT `page_nr`, `page_descr` FROM " + . Util::backquote($cfgRelation['db']) + . "." . Util::backquote($cfgRelation[$type]) + . " WHERE " . Util::backquote($dbNameColumn) + . " = '" . $GLOBALS['dbi']->escapeString($db) . "'"; + + $result = $GLOBALS['dbi']->fetchResult( + $sql_query, + 'page_nr', + 'page_descr' + ); + + foreach ($result as $page => $name) { + // insert row for pdf_page + $sql_query_row = "SELECT `db_name`, `page_descr` FROM " + . Util::backquote($cfgRelation['db']) + . "." . Util::backquote( + $cfgRelation[$type] + ) + . " WHERE " . Util::backquote( + $dbNameColumn + ) + . " = '" . $GLOBALS['dbi']->escapeString($db) . "'" + . " AND `page_nr` = '" . intval($page) . "'"; + + if (! $this->exportData( + $cfgRelation['db'], + $cfgRelation[$type], + $GLOBALS['crlf'], + '', + $sql_query_row, + $aliases + ) + ) { + return false; + } + + $lastPage = $GLOBALS['crlf'] + . "SET @LAST_PAGE = LAST_INSERT_ID();" + . $GLOBALS['crlf']; + if (! $this->export->outputHandler($lastPage)) { + return false; + } + + $sql_query_coords = "SELECT `db_name`, `table_name`, " + . "'@LAST_PAGE' AS `pdf_page_number`, `x`, `y` FROM " + . Util::backquote($cfgRelation['db']) + . "." . Util::backquote( + $cfgRelation['table_coords'] + ) + . " WHERE `pdf_page_number` = '" . $page . "'"; + + $GLOBALS['exporting_metadata'] = true; + if (! $this->exportData( + $cfgRelation['db'], + $cfgRelation['table_coords'], + $GLOBALS['crlf'], + '', + $sql_query_coords, + $aliases + ) + ) { + $GLOBALS['exporting_metadata'] = false; + + return false; + } + $GLOBALS['exporting_metadata'] = false; + } + continue; + } + + // remove auto_incrementing id field for some tables + if ($type == 'bookmark') { + $sql_query = "SELECT `dbase`, `user`, `label`, `query` FROM "; + } elseif ($type == 'column_info') { + $sql_query = "SELECT `db_name`, `table_name`, `column_name`," + . " `comment`, `mimetype`, `transformation`," + . " `transformation_options`, `input_transformation`," + . " `input_transformation_options` FROM"; + } elseif ($type == 'savedsearches') { + $sql_query = "SELECT `username`, `db_name`, `search_name`," + . " `search_data` FROM"; + } else { + $sql_query = "SELECT * FROM "; + } + $sql_query .= Util::backquote($cfgRelation['db']) + . '.' . Util::backquote($cfgRelation[$type]) + . " WHERE " . Util::backquote($dbNameColumn) + . " = '" . $GLOBALS['dbi']->escapeString($db) . "'"; + if (isset($table)) { + $sql_query .= " AND `table_name` = '" + . $GLOBALS['dbi']->escapeString($table) . "'"; + } + + if (! $this->exportData( + $cfgRelation['db'], + $cfgRelation[$type], + $GLOBALS['crlf'], + '', + $sql_query, + $aliases + ) + ) { + return false; + } + } + } + + return true; + } + + /** + * Returns a stand-in CREATE definition to resolve view dependencies + * + * @param string $db the database name + * @param string $view the view name + * @param string $crlf the end of line sequence + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting definition + */ + public function getTableDefStandIn($db, $view, $crlf, $aliases = []) + { + $db_alias = $db; + $view_alias = $view; + $this->initAlias($aliases, $db_alias, $view_alias); + $create_query = ''; + if (! empty($GLOBALS['sql_drop_table'])) { + $create_query .= 'DROP VIEW IF EXISTS ' + . Util::backquote($view_alias) + . ';' . $crlf; + } + + $create_query .= 'CREATE TABLE '; + + if (isset($GLOBALS['sql_if_not_exists']) + && $GLOBALS['sql_if_not_exists'] + ) { + $create_query .= 'IF NOT EXISTS '; + } + $create_query .= Util::backquote($view_alias) . ' (' . $crlf; + $tmp = []; + $columns = $GLOBALS['dbi']->getColumnsFull($db, $view); + foreach ($columns as $column_name => $definition) { + $col_alias = $column_name; + if (! empty($aliases[$db]['tables'][$view]['columns'][$col_alias])) { + $col_alias = $aliases[$db]['tables'][$view]['columns'][$col_alias]; + } + $tmp[] = Util::backquote($col_alias) . ' ' . + $definition['Type'] . $crlf; + } + $create_query .= implode(',', $tmp) . ');' . $crlf; + + return $create_query; + } + + /** + * Returns CREATE definition that matches $view's structure + * + * @param string $db the database name + * @param string $view the view name + * @param string $crlf the end of line sequence + * @param bool $add_semicolon whether to add semicolon and end-of-line at + * the end + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting schema + */ + private function _getTableDefForView( + $db, + $view, + $crlf, + $add_semicolon = true, + array $aliases = [] + ) { + $db_alias = $db; + $view_alias = $view; + $this->initAlias($aliases, $db_alias, $view_alias); + $create_query = "CREATE TABLE"; + if (isset($GLOBALS['sql_if_not_exists'])) { + $create_query .= " IF NOT EXISTS "; + } + $create_query .= Util::backquote($view_alias) . "(" . $crlf; + + $columns = $GLOBALS['dbi']->getColumns($db, $view, null, true); + + $firstCol = true; + foreach ($columns as $column) { + $col_alias = $column['Field']; + if (! empty($aliases[$db]['tables'][$view]['columns'][$col_alias])) { + $col_alias = $aliases[$db]['tables'][$view]['columns'][$col_alias]; + } + $extracted_columnspec = Util::extractColumnSpec( + $column['Type'] + ); + + if (! $firstCol) { + $create_query .= "," . $crlf; + } + $create_query .= " " . Util::backquote($col_alias); + $create_query .= " " . $column['Type']; + if ($extracted_columnspec['can_contain_collation'] + && ! empty($column['Collation']) + ) { + $create_query .= " COLLATE " . $column['Collation']; + } + if ($column['Null'] == 'NO') { + $create_query .= " NOT NULL"; + } + if (isset($column['Default'])) { + $create_query .= " DEFAULT '" + . $GLOBALS['dbi']->escapeString($column['Default']) . "'"; + } else { + if ($column['Null'] == 'YES') { + $create_query .= " DEFAULT NULL"; + } + } + if (! empty($column['Comment'])) { + $create_query .= " COMMENT '" + . $GLOBALS['dbi']->escapeString($column['Comment']) . "'"; + } + $firstCol = false; + } + $create_query .= $crlf . ")" . ($add_semicolon ? ';' : '') . $crlf; + + if (isset($GLOBALS['sql_compatibility'])) { + $compat = $GLOBALS['sql_compatibility']; + } else { + $compat = 'NONE'; + } + if ($compat == 'MSSQL') { + $create_query = $this->_makeCreateTableMSSQLCompatible( + $create_query + ); + } + + return $create_query; + } + + /** + * Returns $table's CREATE definition + * + * @param string $db the database name + * @param string $table the table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case + * of error + * @param bool $show_dates whether to include creation/ + * update/check dates + * @param bool $add_semicolon whether to add semicolon and + * end-of-line at the end + * @param bool $view whether we're handling a view + * @param bool $update_indexes_increments whether we need to update + * two global variables + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting schema + */ + public function getTableDef( + $db, + $table, + $crlf, + $error_url, + $show_dates = false, + $add_semicolon = true, + $view = false, + $update_indexes_increments = true, + array $aliases = [] + ) { + global $sql_drop_table, $sql_backquotes, $sql_constraints, + $sql_constraints_query, $sql_indexes, $sql_indexes_query, + $sql_auto_increments, $sql_drop_foreign_keys; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + $schema_create = ''; + $auto_increment = ''; + $new_crlf = $crlf; + + if (isset($GLOBALS['sql_compatibility'])) { + $compat = $GLOBALS['sql_compatibility']; + } else { + $compat = 'NONE'; + } + + // need to use PhpMyAdmin\DatabaseInterface::QUERY_STORE + // with $GLOBALS['dbi']->numRows() in mysqli + $result = $GLOBALS['dbi']->tryQuery( + 'SHOW TABLE STATUS FROM ' . Util::backquote($db) + . ' WHERE Name = \'' . $GLOBALS['dbi']->escapeString((string) $table) . '\'', + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_STORE + ); + if ($result != false) { + if ($GLOBALS['dbi']->numRows($result) > 0) { + $tmpres = $GLOBALS['dbi']->fetchAssoc($result); + + // Here we optionally add the AUTO_INCREMENT next value, + // but starting with MySQL 5.0.24, the clause is already included + // in SHOW CREATE TABLE so we'll remove it below + if (isset($GLOBALS['sql_auto_increment']) + && ! empty($tmpres['Auto_increment']) + ) { + $auto_increment .= ' AUTO_INCREMENT=' + . $tmpres['Auto_increment'] . ' '; + } + + if ($show_dates + && isset($tmpres['Create_time']) + && ! empty($tmpres['Create_time']) + ) { + $schema_create .= $this->_exportComment( + __('Creation:') . ' ' + . Util::localisedDate( + strtotime($tmpres['Create_time']) + ) + ); + $new_crlf = $this->_exportComment() . $crlf; + } + + if ($show_dates + && isset($tmpres['Update_time']) + && ! empty($tmpres['Update_time']) + ) { + $schema_create .= $this->_exportComment( + __('Last update:') . ' ' + . Util::localisedDate( + strtotime($tmpres['Update_time']) + ) + ); + $new_crlf = $this->_exportComment() . $crlf; + } + + if ($show_dates + && isset($tmpres['Check_time']) + && ! empty($tmpres['Check_time']) + ) { + $schema_create .= $this->_exportComment( + __('Last check:') . ' ' + . Util::localisedDate( + strtotime($tmpres['Check_time']) + ) + ); + $new_crlf = $this->_exportComment() . $crlf; + } + } + $GLOBALS['dbi']->freeResult($result); + } + + $schema_create .= $new_crlf; + + if (! empty($sql_drop_table) + && $GLOBALS['dbi']->getTable($db, $table)->isView() + ) { + $schema_create .= 'DROP VIEW IF EXISTS ' + . Util::backquote($table_alias, $sql_backquotes) . ';' + . $crlf; + } + + // no need to generate a DROP VIEW here, it was done earlier + if (! empty($sql_drop_table) + && ! $GLOBALS['dbi']->getTable($db, $table)->isView() + ) { + $schema_create .= 'DROP TABLE IF EXISTS ' + . Util::backquote($table_alias, $sql_backquotes) . ';' + . $crlf; + } + + // Complete table dump, + // Whether to quote table and column names or not + if ($sql_backquotes) { + $GLOBALS['dbi']->query('SET SQL_QUOTE_SHOW_CREATE = 1'); + } else { + $GLOBALS['dbi']->query('SET SQL_QUOTE_SHOW_CREATE = 0'); + } + + // I don't see the reason why this unbuffered query could cause problems, + // because SHOW CREATE TABLE returns only one row, and we free the + // results below. Nonetheless, we got 2 user reports about this + // (see bug 1562533) so I removed the unbuffered mode. + // $result = $GLOBALS['dbi']->query('SHOW CREATE TABLE ' . backquote($db) + // . '.' . backquote($table), null, DatabaseInterface::QUERY_UNBUFFERED); + // + // Note: SHOW CREATE TABLE, at least in MySQL 5.1.23, does not + // produce a displayable result for the default value of a BIT + // column, nor does the mysqldump command. See MySQL bug 35796 + $GLOBALS['dbi']->tryQuery('USE ' . Util::backquote($db)); + $result = $GLOBALS['dbi']->tryQuery( + 'SHOW CREATE TABLE ' . Util::backquote($db) . '.' + . Util::backquote($table) + ); + // an error can happen, for example the table is crashed + $tmp_error = $GLOBALS['dbi']->getError(); + if ($tmp_error) { + $message = sprintf(__('Error reading structure for table %s:'), "$db.$table"); + $message .= ' ' . $tmp_error; + if (! defined('TESTSUITE')) { + trigger_error($message, E_USER_ERROR); + } + return $this->_exportComment($message); + } + + // Old mode is stored so it can be restored once exporting is done. + $old_mode = Context::$MODE; + + $warning = ''; + if ($result != false && ($row = $GLOBALS['dbi']->fetchRow($result))) { + $create_query = $row[1]; + unset($row); + + // Convert end of line chars to one that we want (note that MySQL + // doesn't return query it will accept in all cases) + if (mb_strpos($create_query, "(\r\n ")) { + $create_query = str_replace("\r\n", $crlf, $create_query); + } elseif (mb_strpos($create_query, "(\n ")) { + $create_query = str_replace("\n", $crlf, $create_query); + } elseif (mb_strpos($create_query, "(\r ")) { + $create_query = str_replace("\r", $crlf, $create_query); + } + + /* + * Drop database name from VIEW creation. + * + * This is a bit tricky, but we need to issue SHOW CREATE TABLE with + * database name, but we don't want name to show up in CREATE VIEW + * statement. + */ + if ($view) { + $create_query = preg_replace( + '/' . preg_quote(Util::backquote($db), '/') . '\./', + '', + $create_query + ); + + // exclude definition of current user + if (isset($GLOBALS['sql_view_current_user'])) { + $create_query = preg_replace( + '/(^|\s)DEFINER=([\S]+)/', + '', + $create_query + ); + } + + // whether to replace existing view or not + if (isset($GLOBALS['sql_or_replace_view'])) { + $create_query = preg_replace( + '/^CREATE/', + 'CREATE OR REPLACE', + $create_query + ); + } + } + + // Substitute aliases in `CREATE` query. + $create_query = $this->replaceWithAliases( + $create_query, + $aliases, + $db, + $table, + $flag + ); + + // One warning per view. + if ($flag && $view) { + $warning = $this->_exportComment() + . $this->_exportComment( + __('It appears your database uses views;') + ) + . $this->_exportComment( + __('alias export may not work reliably in all cases.') + ) + . $this->_exportComment(); + } + + // Adding IF NOT EXISTS, if required. + if (isset($GLOBALS['sql_if_not_exists'])) { + $create_query = preg_replace( + '/^CREATE TABLE/', + 'CREATE TABLE IF NOT EXISTS', + $create_query + ); + } + + // Making the query MSSQL compatible. + if ($compat == 'MSSQL') { + $create_query = $this->_makeCreateTableMSSQLCompatible( + $create_query + ); + } + + // Views have no constraints, indexes, etc. They do not require any + // analysis. + if (! $view) { + if (empty($sql_backquotes)) { + // Option "Enclose table and column names with backquotes" + // was checked. + Context::$MODE |= Context::SQL_MODE_NO_ENCLOSING_QUOTES; + } + + // Using appropriate quotes. + if (($compat === 'MSSQL') || ($sql_backquotes === '"')) { + Context::$MODE |= Context::SQL_MODE_ANSI_QUOTES; + } + } + + /** + * Parser used for analysis. + * + * @var Parser + */ + $parser = new Parser($create_query); + + /** + * `CREATE TABLE` statement. + * + * @var CreateStatement + */ + $statement = $parser->statements[0]; + + if (! empty($statement->entityOptions)) { + $engine = $statement->entityOptions->has('ENGINE'); + } else { + $engine = ''; + } + + /* Avoid operation on ARCHIVE tables as those can not be altered */ + if (! empty($statement->fields) && (empty($engine) || strtoupper($engine) != 'ARCHIVE')) { + + /** + * Fragments containining definition of each constraint. + * + * @var array + */ + $constraints = []; + + /** + * Fragments containining definition of each index. + * + * @var array + */ + $indexes = []; + + /** + * Fragments containining definition of each FULLTEXT index. + * + * @var array + */ + $indexes_fulltext = []; + + /** + * Fragments containining definition of each foreign key that will + * be dropped. + * + * @var array + */ + $dropped = []; + + /** + * Fragment containining definition of the `AUTO_INCREMENT`. + * + * @var array + */ + $auto_increment = []; + + // Scanning each field of the `CREATE` statement to fill the arrays + // above. + // If the field is used in any of the arrays above, it is removed + // from the original definition. + // Also, AUTO_INCREMENT attribute is removed. + /** @var CreateDefinition $field */ + foreach ($statement->fields as $key => $field) { + if ($field->isConstraint) { + // Creating the parts that add constraints. + $constraints[] = $field::build($field); + unset($statement->fields[$key]); + } elseif (! empty($field->key)) { + // Creating the parts that add indexes (must not be + // constraints). + if ($field->key->type === 'FULLTEXT KEY') { + $indexes_fulltext[] = $field->build($field); + unset($statement->fields[$key]); + } else { + if (empty($GLOBALS['sql_if_not_exists'])) { + $indexes[] = str_replace( + 'COMMENT=\'', + 'COMMENT \'', + $field::build($field) + ); + unset($statement->fields[$key]); + } + } + } + + // Creating the parts that drop foreign keys. + if (! empty($field->key)) { + if ($field->key->type === 'FOREIGN KEY') { + $dropped[] = 'FOREIGN KEY ' . Context::escape( + $field->name + ); + unset($statement->fields[$key]); + } + } + + // Dropping AUTO_INCREMENT. + if (! empty($field->options)) { + if ($field->options->has('AUTO_INCREMENT') + && empty($GLOBALS['sql_if_not_exists']) + ) { + $auto_increment[] = $field::build($field); + $field->options->remove('AUTO_INCREMENT'); + } + } + } + + /** + * The header of the `ALTER` statement (`ALTER TABLE tbl`). + * + * @var string + */ + $alter_header = 'ALTER TABLE ' . + Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ); + + /** + * The footer of the `ALTER` statement (usually ';') + * + * @var string + */ + $alter_footer = ';' . $crlf; + + // Generating constraints-related query. + if (! empty($constraints)) { + $sql_constraints_query = $alter_header . $crlf . ' ADD ' + . implode(',' . $crlf . ' ADD ', $constraints) + . $alter_footer; + + $sql_constraints = $this->generateComment( + $crlf, + $sql_constraints, + __('Constraints for dumped tables'), + __('Constraints for table'), + $table_alias, + $compat + ) . $sql_constraints_query; + } + + // Generating indexes-related query. + $sql_indexes_query = ''; + + if (! empty($indexes)) { + $sql_indexes_query .= $alter_header . $crlf . ' ADD ' + . implode(',' . $crlf . ' ADD ', $indexes) + . $alter_footer; + } + + if (! empty($indexes_fulltext)) { + // InnoDB supports one FULLTEXT index creation at a time. + // So FULLTEXT indexes are created one-by-one after other + // indexes where created. + $sql_indexes_query .= $alter_header . + ' ADD ' . implode( + $alter_footer . $alter_header . ' ADD ', + $indexes_fulltext + ) . $alter_footer; + } + + if (! empty($indexes) || ! empty($indexes_fulltext)) { + $sql_indexes = $this->generateComment( + $crlf, + $sql_indexes, + __('Indexes for dumped tables'), + __('Indexes for table'), + $table_alias, + $compat + ) . $sql_indexes_query; + } + + // Generating drop foreign keys-related query. + if (! empty($dropped)) { + $sql_drop_foreign_keys = $alter_header . $crlf . ' DROP ' + . implode(',' . $crlf . ' DROP ', $dropped) + . $alter_footer; + } + + // Generating auto-increment-related query. + if (! empty($auto_increment) && $update_indexes_increments) { + $sql_auto_increments_query = $alter_header . $crlf . ' MODIFY ' + . implode(',' . $crlf . ' MODIFY ', $auto_increment); + if (isset($GLOBALS['sql_auto_increment']) + && ($statement->entityOptions->has('AUTO_INCREMENT') !== false) + ) { + if (! isset($GLOBALS['table_data']) + || (isset($GLOBALS['table_data']) + && in_array($table, $GLOBALS['table_data'])) + ) { + $sql_auto_increments_query .= ', AUTO_INCREMENT=' + . $statement->entityOptions->has('AUTO_INCREMENT'); + } + } + $sql_auto_increments_query .= ';' . $crlf; + + $sql_auto_increments = $this->generateComment( + $crlf, + $sql_auto_increments, + __('AUTO_INCREMENT for dumped tables'), + __('AUTO_INCREMENT for table'), + $table_alias, + $compat + ) . $sql_auto_increments_query; + } + + // Removing the `AUTO_INCREMENT` attribute from the `CREATE TABLE` + // too. + if (! empty($statement->entityOptions) + && (empty($GLOBALS['sql_if_not_exists']) + || empty($GLOBALS['sql_auto_increment'])) + ) { + $statement->entityOptions->remove('AUTO_INCREMENT'); + } + + // Rebuilding the query. + $create_query = $statement->build(); + } + + $schema_create .= $create_query; + } + + $GLOBALS['dbi']->freeResult($result); + + // Restoring old mode. + Context::$MODE = $old_mode; + + return $warning . $schema_create . ($add_semicolon ? ';' . $crlf : ''); + } // end of the 'getTableDef()' function + + /** + * Returns $table's comments, relations etc. + * + * @param string $db database name + * @param string $table table name + * @param string $crlf end of line sequence + * @param bool $do_relation whether to include relation comments + * @param bool $do_mime whether to include mime comments + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting comments + */ + private function _getTableComments( + $db, + $table, + $crlf, + $do_relation = false, + $do_mime = false, + array $aliases = [] + ) { + global $cfgRelation, $sql_backquotes; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + $schema_create = ''; + + // Check if we can use Relations + list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus( + $do_relation && ! empty($cfgRelation['relation']), + $db, + $table + ); + + if ($do_mime && $cfgRelation['mimework']) { + if (! ($mime_map = $this->transformations->getMime($db, $table, true))) { + unset($mime_map); + } + } + + if (isset($mime_map) && count($mime_map) > 0) { + $schema_create .= $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment( + __('MEDIA (MIME) TYPES FOR TABLE') . ' ' + . Util::backquote($table, $sql_backquotes) . ':' + ); + foreach ($mime_map as $mime_field => $mime) { + $schema_create .= $this->_exportComment( + ' ' + . Util::backquote($mime_field, $sql_backquotes) + ) + . $this->_exportComment( + ' ' + . Util::backquote( + $mime['mimetype'], + $sql_backquotes + ) + ); + } + $schema_create .= $this->_exportComment(); + } + + if ($have_rel) { + $schema_create .= $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment( + __('RELATIONSHIPS FOR TABLE') . ' ' + . Util::backquote($table_alias, $sql_backquotes) + . ':' + ); + + foreach ($res_rel as $rel_field => $rel) { + if ($rel_field != 'foreign_keys_data') { + $rel_field_alias = ! empty( + $aliases[$db]['tables'][$table]['columns'][$rel_field] + ) ? $aliases[$db]['tables'][$table]['columns'][$rel_field] + : $rel_field; + $schema_create .= $this->_exportComment( + ' ' + . Util::backquote( + $rel_field_alias, + $sql_backquotes + ) + ) + . $this->_exportComment( + ' ' + . Util::backquote( + $rel['foreign_table'], + $sql_backquotes + ) + . ' -> ' + . Util::backquote( + $rel['foreign_field'], + $sql_backquotes + ) + ); + } else { + foreach ($rel as $one_key) { + foreach ($one_key['index_list'] as $index => $field) { + $rel_field_alias = ! empty( + $aliases[$db]['tables'][$table]['columns'][$field] + ) ? $aliases[$db]['tables'][$table]['columns'][$field] + : $field; + $schema_create .= $this->_exportComment( + ' ' + . Util::backquote( + $rel_field_alias, + $sql_backquotes + ) + ) + . $this->_exportComment( + ' ' + . Util::backquote( + $one_key['ref_table_name'], + $sql_backquotes + ) + . ' -> ' + . Util::backquote( + $one_key['ref_index_list'][$index], + $sql_backquotes + ) + ); + } + } + } + } + $schema_create .= $this->_exportComment(); + } + + return $schema_create; + } // end of the '_getTableComments()' function + + /** + * Outputs table's structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table','triggers','create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $relation whether to include relation comments + * @param bool $comments whether to include the pmadb-style column + * comments as comments in the structure; this is + * deprecated but the parameter is left here + * because export.php calls exportStructure() + * also for other export types which use this + * parameter + * @param bool $mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $relation = false, + $comments = false, + $mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + if (isset($GLOBALS['sql_compatibility'])) { + $compat = $GLOBALS['sql_compatibility']; + } else { + $compat = 'NONE'; + } + + $formatted_table_name = Util::backquoteCompat( + $table_alias, + $compat, + isset($GLOBALS['sql_backquotes']) + ); + $dump = $this->_possibleCRLF() + . $this->_exportComment(str_repeat('-', 56)) + . $this->_possibleCRLF() + . $this->_exportComment(); + + switch ($export_mode) { + case 'create_table': + $dump .= $this->_exportComment( + __('Table structure for table') . ' ' . $formatted_table_name + ); + $dump .= $this->_exportComment(); + $dump .= $this->getTableDef( + $db, + $table, + $crlf, + $error_url, + $dates, + true, + false, + true, + $aliases + ); + $dump .= $this->_getTableComments( + $db, + $table, + $crlf, + $relation, + $mime, + $aliases + ); + break; + case 'triggers': + $dump = ''; + $delimiter = '$$'; + $triggers = $GLOBALS['dbi']->getTriggers($db, $table, $delimiter); + if ($triggers) { + $dump .= $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment( + __('Triggers') . ' ' . $formatted_table_name + ) + . $this->_exportComment(); + $used_alias = false; + $trigger_query = ''; + foreach ($triggers as $trigger) { + if (! empty($GLOBALS['sql_drop_table'])) { + $trigger_query .= $trigger['drop'] . ';' . $crlf; + } + + $trigger_query .= 'DELIMITER ' . $delimiter . $crlf; + $trigger_query .= $this->replaceWithAliases( + $trigger['create'], + $aliases, + $db, + $table, + $flag + ); + if ($flag) { + $used_alias = true; + } + $trigger_query .= 'DELIMITER ;' . $crlf; + } + // One warning per table. + if ($used_alias) { + $dump .= $this->_exportComment( + __('It appears your table uses triggers;') + ) + . $this->_exportComment( + __('alias export may not work reliably in all cases.') + ) + . $this->_exportComment(); + } + $dump .= $trigger_query; + } + break; + case 'create_view': + if (empty($GLOBALS['sql_views_as_tables'])) { + $dump .= $this->_exportComment( + __('Structure for view') + . ' ' + . $formatted_table_name + ) + . $this->_exportComment(); + // delete the stand-in table previously created (if any) + if ($export_type != 'table') { + $dump .= 'DROP TABLE IF EXISTS ' + . Util::backquote($table_alias) . ';' . $crlf; + } + $dump .= $this->getTableDef( + $db, + $table, + $crlf, + $error_url, + $dates, + true, + true, + true, + $aliases + ); + } else { + $dump .= $this->_exportComment( + sprintf( + __('Structure for view %s exported as a table'), + $formatted_table_name + ) + ) + . $this->_exportComment(); + // delete the stand-in table previously created (if any) + if ($export_type != 'table') { + $dump .= 'DROP TABLE IF EXISTS ' + . Util::backquote($table_alias) . ';' . $crlf; + } + $dump .= $this->_getTableDefForView( + $db, + $table, + $crlf, + true, + $aliases + ); + } + break; + case 'stand_in': + $dump .= $this->_exportComment( + __('Stand-in structure for view') . ' ' . $formatted_table_name + ) + . $this->_exportComment( + __('(See below for the actual view)') + ) + . $this->_exportComment(); + // export a stand-in definition to resolve view dependencies + $dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases); + } // end switch + + // this one is built by getTableDef() to use in table copy/move + // but not in the case of export + unset($GLOBALS['sql_constraints_query']); + + return $this->export->outputHandler($dump); + } + + /** + * Outputs the content of a table in SQL format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + global $current_row, $sql_backquotes; + + // Do not export data for merge tables + if ($GLOBALS['dbi']->getTable($db, $table)->isMerge()) { + return true; + } + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + if (isset($GLOBALS['sql_compatibility'])) { + $compat = $GLOBALS['sql_compatibility']; + } else { + $compat = 'NONE'; + } + + $formatted_table_name = Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ); + + // Do not export data for a VIEW, unless asked to export the view as a table + // (For a VIEW, this is called only when exporting a single VIEW) + if ($GLOBALS['dbi']->getTable($db, $table)->isView() + && empty($GLOBALS['sql_views_as_tables']) + ) { + $head = $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment('VIEW ' . $formatted_table_name) + . $this->_exportComment(__('Data:') . ' ' . __('None')) + . $this->_exportComment() + . $this->_possibleCRLF(); + + return $this->export->outputHandler($head); + } + + $result = $GLOBALS['dbi']->tryQuery( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + // a possible error: the table has crashed + $tmp_error = $GLOBALS['dbi']->getError(); + if ($tmp_error) { + $message = sprintf(__('Error reading data for table %s:'), "$db.$table"); + $message .= ' ' . $tmp_error; + if (! defined('TESTSUITE')) { + trigger_error($message, E_USER_ERROR); + } + return $this->export->outputHandler( + $this->_exportComment($message) + ); + } + + if ($result == false) { + $GLOBALS['dbi']->freeResult($result); + + return true; + } + + $fields_cnt = $GLOBALS['dbi']->numFields($result); + + // Get field information + $fields_meta = $GLOBALS['dbi']->getFieldsMeta($result); + $field_flags = []; + for ($j = 0; $j < $fields_cnt; $j++) { + $field_flags[$j] = $GLOBALS['dbi']->fieldFlags($result, $j); + } + + $field_set = []; + for ($j = 0; $j < $fields_cnt; $j++) { + $col_as = $fields_meta[$j]->name; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $field_set[$j] = Util::backquoteCompat( + $col_as, + $compat, + $sql_backquotes + ); + } + + if (isset($GLOBALS['sql_type']) + && $GLOBALS['sql_type'] == 'UPDATE' + ) { + // update + $schema_insert = 'UPDATE '; + if (isset($GLOBALS['sql_ignore'])) { + $schema_insert .= 'IGNORE '; + } + // avoid EOL blank + $schema_insert .= Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ) . ' SET'; + } else { + // insert or replace + if (isset($GLOBALS['sql_type']) + && $GLOBALS['sql_type'] == 'REPLACE' + ) { + $sql_command = 'REPLACE'; + } else { + $sql_command = 'INSERT'; + } + + // delayed inserts? + if (isset($GLOBALS['sql_delayed'])) { + $insert_delayed = ' DELAYED'; + } else { + $insert_delayed = ''; + } + + // insert ignore? + if (isset($GLOBALS['sql_type']) + && $GLOBALS['sql_type'] == 'INSERT' + && isset($GLOBALS['sql_ignore']) + ) { + $insert_delayed .= ' IGNORE'; + } + //truncate table before insert + if (isset($GLOBALS['sql_truncate']) + && $GLOBALS['sql_truncate'] + && $sql_command == 'INSERT' + ) { + $truncate = 'TRUNCATE TABLE ' + . Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ) . ";"; + $truncatehead = $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment( + __('Truncate table before insert') . ' ' + . $formatted_table_name + ) + . $this->_exportComment() + . $crlf; + $this->export->outputHandler($truncatehead); + $this->export->outputHandler($truncate); + } + + // scheme for inserting fields + if ($GLOBALS['sql_insert_syntax'] == 'complete' + || $GLOBALS['sql_insert_syntax'] == 'both' + ) { + $fields = implode(', ', $field_set); + $schema_insert = $sql_command . $insert_delayed . ' INTO ' + . Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ) + // avoid EOL blank + . ' (' . $fields . ') VALUES'; + } else { + $schema_insert = $sql_command . $insert_delayed . ' INTO ' + . Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ) + . ' VALUES'; + } + } + + //\x08\\x09, not required + $current_row = 0; + $query_size = 0; + if (($GLOBALS['sql_insert_syntax'] == 'extended' + || $GLOBALS['sql_insert_syntax'] == 'both') + && (! isset($GLOBALS['sql_type']) + || $GLOBALS['sql_type'] != 'UPDATE') + ) { + $separator = ','; + $schema_insert .= $crlf; + } else { + $separator = ';'; + } + + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + if ($current_row == 0) { + $head = $this->_possibleCRLF() + . $this->_exportComment() + . $this->_exportComment( + __('Dumping data for table') . ' ' + . $formatted_table_name + ) + . $this->_exportComment() + . $crlf; + if (! $this->export->outputHandler($head)) { + return false; + } + } + // We need to SET IDENTITY_INSERT ON for MSSQL + if (isset($GLOBALS['sql_compatibility']) + && $GLOBALS['sql_compatibility'] == 'MSSQL' + && $current_row == 0 + ) { + if (! $this->export->outputHandler( + 'SET IDENTITY_INSERT ' + . Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ) + . ' ON ;' . $crlf + ) + ) { + return false; + } + } + $current_row++; + $values = []; + for ($j = 0; $j < $fields_cnt; $j++) { + // NULL + if (! isset($row[$j]) || $row[$j] === null) { + $values[] = 'NULL'; + } elseif ($fields_meta[$j]->numeric + && $fields_meta[$j]->type != 'timestamp' + && ! $fields_meta[$j]->blob + ) { + // a number + // timestamp is numeric on some MySQL 4.1, BLOBs are + // sometimes numeric + $values[] = $row[$j]; + } elseif (false !== stripos($field_flags[$j], 'BINARY') + && isset($GLOBALS['sql_hex_for_binary']) + ) { + // a true BLOB + // - mysqldump only generates hex data when the --hex-blob + // option is used, for fields having the binary attribute + // no hex is generated + // - a TEXT field returns type blob but a real blob + // returns also the 'binary' flag + + // empty blobs need to be different, but '0' is also empty + // :-( + if (empty($row[$j]) && $row[$j] != '0') { + $values[] = '\'\''; + } else { + $values[] = '0x' . bin2hex($row[$j]); + } + } elseif ($fields_meta[$j]->type == 'bit') { + // detection of 'bit' works only on mysqli extension + $values[] = "b'" . $GLOBALS['dbi']->escapeString( + Util::printableBitValue( + (int) $row[$j], + (int) $fields_meta[$j]->length + ) + ) + . "'"; + } elseif ($fields_meta[$j]->type === 'geometry') { + // export GIS types as hex + $values[] = '0x' . bin2hex($row[$j]); + } elseif (! empty($GLOBALS['exporting_metadata']) + && $row[$j] == '@LAST_PAGE' + ) { + $values[] = '@LAST_PAGE'; + } else { + // something else -> treat as a string + $values[] = '\'' + . $GLOBALS['dbi']->escapeString($row[$j]) + . '\''; + } // end if + } // end for + + // should we make update? + if (isset($GLOBALS['sql_type']) + && $GLOBALS['sql_type'] == 'UPDATE' + ) { + $insert_line = $schema_insert; + for ($i = 0; $i < $fields_cnt; $i++) { + if (0 == $i) { + $insert_line .= ' '; + } + if ($i > 0) { + // avoid EOL blank + $insert_line .= ','; + } + $insert_line .= $field_set[$i] . ' = ' . $values[$i]; + } + + list($tmp_unique_condition, $tmp_clause_is_unique) + = Util::getUniqueCondition( + $result, // handle + $fields_cnt, // fields_cnt + $fields_meta, // fields_meta + $row, // row + false, // force_unique + false, // restrict_to_table + null // analyzed_sql_results + ); + $insert_line .= ' WHERE ' . $tmp_unique_condition; + unset($tmp_unique_condition, $tmp_clause_is_unique); + } else { + // Extended inserts case + if ($GLOBALS['sql_insert_syntax'] == 'extended' + || $GLOBALS['sql_insert_syntax'] == 'both' + ) { + if ($current_row == 1) { + $insert_line = $schema_insert . '(' + . implode(', ', $values) . ')'; + } else { + $insert_line = '(' . implode(', ', $values) . ')'; + $insertLineSize = mb_strlen($insert_line); + $sql_max_size = $GLOBALS['sql_max_query_size']; + if (isset($sql_max_size) + && $sql_max_size > 0 + && $query_size + $insertLineSize > $sql_max_size + ) { + if (! $this->export->outputHandler(';' . $crlf)) { + return false; + } + $query_size = 0; + $current_row = 1; + $insert_line = $schema_insert . $insert_line; + } + } + $query_size += mb_strlen($insert_line); + // Other inserts case + } else { + $insert_line = $schema_insert + . '(' . implode(', ', $values) . ')'; + } + } + unset($values); + + if (! $this->export->outputHandler( + ($current_row == 1 ? '' : $separator . $crlf) + . $insert_line + ) + ) { + return false; + } + } // end while + + if ($current_row > 0) { + if (! $this->export->outputHandler(';' . $crlf)) { + return false; + } + } + + // We need to SET IDENTITY_INSERT OFF for MSSQL + if (isset($GLOBALS['sql_compatibility']) + && $GLOBALS['sql_compatibility'] == 'MSSQL' + && $current_row > 0 + ) { + $outputSucceeded = $this->export->outputHandler( + $crlf . 'SET IDENTITY_INSERT ' + . Util::backquoteCompat( + $table_alias, + $compat, + $sql_backquotes + ) + . ' OFF;' . $crlf + ); + if (! $outputSucceeded) { + return false; + } + } + + $GLOBALS['dbi']->freeResult($result); + + return true; + } // end of the 'exportData()' function + + /** + * Make a create table statement compatible with MSSQL + * + * @param string $create_query MySQL create table statement + * + * @return string MSSQL compatible create table statement + */ + private function _makeCreateTableMSSQLCompatible($create_query) + { + // In MSSQL + // 1. No 'IF NOT EXISTS' in CREATE TABLE + // 2. DATE field doesn't exists, we will use DATETIME instead + // 3. UNSIGNED attribute doesn't exist + // 4. No length on INT, TINYINT, SMALLINT, BIGINT and no precision on + // FLOAT fields + // 5. No KEY and INDEX inside CREATE TABLE + // 6. DOUBLE field doesn't exists, we will use FLOAT instead + + $create_query = preg_replace( + "/^CREATE TABLE IF NOT EXISTS/", + 'CREATE TABLE', + $create_query + ); + // first we need to replace all lines ended with '" DATE ...,\n' + // last preg_replace preserve us from situation with date text + // inside DEFAULT field value + $create_query = preg_replace( + "/\" date DEFAULT NULL(,)?\n/", + '" datetime DEFAULT NULL$1' . "\n", + $create_query + ); + $create_query = preg_replace( + "/\" date NOT NULL(,)?\n/", + '" datetime NOT NULL$1' . "\n", + $create_query + ); + $create_query = preg_replace( + '/" date NOT NULL DEFAULT \'([^\'])/', + '" datetime NOT NULL DEFAULT \'$1', + $create_query + ); + + // next we need to replace all lines ended with ') UNSIGNED ...,' + // last preg_replace preserve us from situation with unsigned text + // inside DEFAULT field value + $create_query = preg_replace( + "/\) unsigned NOT NULL(,)?\n/", + ') NOT NULL$1' . "\n", + $create_query + ); + $create_query = preg_replace( + "/\) unsigned DEFAULT NULL(,)?\n/", + ') DEFAULT NULL$1' . "\n", + $create_query + ); + $create_query = preg_replace( + '/\) unsigned NOT NULL DEFAULT \'([^\'])/', + ') NOT NULL DEFAULT \'$1', + $create_query + ); + + // we need to replace all lines ended with + // '" INT|TINYINT([0-9]{1,}) ...,' last preg_replace preserve us + // from situation with int([0-9]{1,}) text inside DEFAULT field + // value + $create_query = preg_replace( + '/" (int|tinyint|smallint|bigint)\([0-9]+\) DEFAULT NULL(,)?\n/', + '" $1 DEFAULT NULL$2' . "\n", + $create_query + ); + $create_query = preg_replace( + '/" (int|tinyint|smallint|bigint)\([0-9]+\) NOT NULL(,)?\n/', + '" $1 NOT NULL$2' . "\n", + $create_query + ); + $create_query = preg_replace( + '/" (int|tinyint|smallint|bigint)\([0-9]+\) NOT NULL DEFAULT \'([^\'])/', + '" $1 NOT NULL DEFAULT \'$2', + $create_query + ); + + // we need to replace all lines ended with + // '" FLOAT|DOUBLE([0-9,]{1,}) ...,' + // last preg_replace preserve us from situation with + // float([0-9,]{1,}) text inside DEFAULT field value + $create_query = preg_replace( + '/" (float|double)(\([0-9]+,[0-9,]+\))? DEFAULT NULL(,)?\n/', + '" float DEFAULT NULL$3' . "\n", + $create_query + ); + $create_query = preg_replace( + '/" (float|double)(\([0-9,]+,[0-9,]+\))? NOT NULL(,)?\n/', + '" float NOT NULL$3' . "\n", + $create_query + ); + return preg_replace( + '/" (float|double)(\([0-9,]+,[0-9,]+\))? NOT NULL DEFAULT \'([^\'])/', + '" float NOT NULL DEFAULT \'$3', + $create_query + ); + + // @todo remove indexes from CREATE TABLE + } + + /** + * replaces db/table/column names with their aliases + * + * @param string $sql_query SQL query in which aliases are to be substituted + * @param array $aliases Alias information for db/table/column + * @param string $db the database name + * @param string $table the tablename + * @param string $flag the flag denoting whether any replacement was done + * + * @return string query replaced with aliases + */ + public function replaceWithAliases( + $sql_query, + array $aliases, + $db, + $table = '', + &$flag = null + ) { + $flag = false; + + /** + * The parser of this query. + * + * @var Parser $parser + */ + $parser = new Parser($sql_query); + + if (empty($parser->statements[0])) { + return $sql_query; + } + + /** + * The statement that represents the query. + * + * @var CreateStatement $statement + */ + $statement = $parser->statements[0]; + + /** + * Old database name. + * + * @var string $old_database + */ + $old_database = $db; + + // Replacing aliases in `CREATE TABLE` statement. + if ($statement->options->has('TABLE')) { + // Extracting the name of the old database and table from the + // statement to make sure the parameters are corect. + if (! empty($statement->name->database)) { + $old_database = $statement->name->database; + } + + /** + * Old table name. + * + * @var string $old_table + */ + $old_table = $statement->name->table; + + // Finding the aliased database name. + // The database might be empty so we have to add a few checks. + $new_database = null; + if (! empty($statement->name->database)) { + $new_database = $statement->name->database; + if (! empty($aliases[$old_database]['alias'])) { + $new_database = $aliases[$old_database]['alias']; + } + } + + // Finding the aliases table name. + $new_table = $old_table; + if (! empty($aliases[$old_database]['tables'][$old_table]['alias'])) { + $new_table = $aliases[$old_database]['tables'][$old_table]['alias']; + } + + // Replacing new values. + if (($statement->name->database !== $new_database) + || ($statement->name->table !== $new_table) + ) { + $statement->name->database = $new_database; + $statement->name->table = $new_table; + $statement->name->expr = null; // Force rebuild. + $flag = true; + } + + /** @var CreateDefinition $field */ + foreach ($statement->fields as $field) { + // Column name. + if (! empty($field->type)) { + if (! empty($aliases[$old_database]['tables'][$old_table]['columns'][$field->name])) { + $field->name = $aliases[$old_database]['tables'][$old_table]['columns'][$field->name]; + $flag = true; + } + } + + // Key's columns. + if (! empty($field->key)) { + foreach ($field->key->columns as $key => $column) { + if (! empty($aliases[$old_database]['tables'][$old_table]['columns'][$column['name']])) { + $field->key->columns[$key]['name'] = $aliases[$old_database]['tables'][$old_table]['columns'][$column['name']]; + $flag = true; + } + } + } + + // References. + if (! empty($field->references)) { + $ref_table = $field->references->table->table; + // Replacing table. + if (! empty($aliases[$old_database]['tables'][$ref_table]['alias'])) { + $field->references->table->table + = $aliases[$old_database]['tables'][$ref_table]['alias']; + $field->references->table->expr = null; + $flag = true; + } + // Replacing column names. + foreach ($field->references->columns as $key => $column) { + if (! empty($aliases[$old_database]['tables'][$ref_table]['columns'][$column])) { + $field->references->columns[$key] + = $aliases[$old_database]['tables'][$ref_table]['columns'][$column]; + $flag = true; + } + } + } + } + } elseif ($statement->options->has('TRIGGER')) { + // Extracting the name of the old database and table from the + // statement to make sure the parameters are corect. + if (! empty($statement->table->database)) { + $old_database = $statement->table->database; + } + + /** + * Old table name. + * + * @var string $old_table + */ + $old_table = $statement->table->table; + + if (! empty($aliases[$old_database]['tables'][$old_table]['alias'])) { + $statement->table->table + = $aliases[$old_database]['tables'][$old_table]['alias']; + $statement->table->expr = null; // Force rebuild. + $flag = true; + } + } + + if ($statement->options->has('TRIGGER') + || $statement->options->has('PROCEDURE') + || $statement->options->has('FUNCTION') + || $statement->options->has('VIEW') + ) { + // Repalcing the body. + for ($i = 0, $count = count($statement->body); $i < $count; ++$i) { + + /** + * Token parsed at this moment. + * + * @var Token $token + */ + $token = $statement->body[$i]; + + // Replacing only symbols (that are not variables) and unknown + // identifiers. + if (($token->type === Token::TYPE_SYMBOL) + && (! ($token->flags & Token::FLAG_SYMBOL_VARIABLE)) + || (($token->type === Token::TYPE_KEYWORD) + && (! ($token->flags & Token::FLAG_KEYWORD_RESERVED)) + || ($token->type === Token::TYPE_NONE)) + ) { + $alias = $this->getAlias($aliases, $token->value); + if (! empty($alias)) { + // Replacing the token. + $token->token = Context::escape($alias); + $flag = true; + } + } + } + } + + return $statement->build(); + } + + /** + * Generate comment + * + * @param string $crlf Carriage return character + * @param string|null $sql_statement SQL statement + * @param string $comment1 Comment for dumped table + * @param string $comment2 Comment for current table + * @param string $table_alias Table alias + * @param string $compat Compatibility mode + * + * @return string + */ + protected function generateComment( + $crlf, + ?string $sql_statement, + $comment1, + $comment2, + $table_alias, + $compat + ) { + if (! isset($sql_statement)) { + if (isset($GLOBALS['no_constraints_comments'])) { + $sql_statement = ''; + } else { + $sql_statement = $crlf + . $this->_exportComment() + . $this->_exportComment($comment1) + . $this->_exportComment(); + } + } + + // comments for current table + if (! isset($GLOBALS['no_constraints_comments'])) { + $sql_statement .= $crlf + . $this->_exportComment() + . $this->_exportComment( + $comment2 . ' ' . Util::backquoteCompat( + $table_alias, + $compat, + isset($GLOBALS['sql_backquotes']) + ) + ) + . $this->_exportComment(); + } + + return $sql_statement; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportTexytext.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportTexytext.php new file mode 100644 index 0000000..a2fdec8 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportTexytext.php @@ -0,0 +1,624 @@ +setProperties(); + } + + /** + * Sets the export Texy! text properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('Texy! text'); + $exportPluginProperties->setExtension('txt'); + $exportPluginProperties->setMimeType('text/plain'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // what to dump (structure/data/both) main group + $dumpWhat = new OptionsPropertyMainGroup( + "general_opts", + __('Dump table') + ); + // create primary items and add them to the group + $leaf = new RadioPropertyItem("structure_or_data"); + $leaf->setValues( + [ + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data'), + ] + ); + $dumpWhat->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dumpWhat); + + // data options main group + $dataOptions = new OptionsPropertyMainGroup( + "data", + __('Data dump options') + ); + $dataOptions->setForce('structure'); + // create primary items and add them to the group + $leaf = new BoolPropertyItem( + "columns", + __('Put columns names in the first row') + ); + $dataOptions->addProperty($leaf); + $leaf = new TextPropertyItem( + 'null', + __('Replace NULL with:') + ); + $dataOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($dataOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Alias of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + if (empty($db_alias)) { + $db_alias = $db; + } + + return $this->export->outputHandler( + '===' . __('Database') . ' ' . $db_alias . "\n\n" + ); + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in NHibernate format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + global $what; + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + + if (! $this->export->outputHandler( + '== ' . __('Dumping data for table') . ' ' . $table_alias . "\n\n" + ) + ) { + return false; + } + + // Gets the data from the database + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $fields_cnt = $GLOBALS['dbi']->numFields($result); + + // If required, get fields name at the first line + if (isset($GLOBALS[$what . '_columns'])) { + $text_output = "|------\n"; + for ($i = 0; $i < $fields_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $text_output .= '|' + . htmlspecialchars(stripslashes($col_as)); + } // end for + $text_output .= "\n|------\n"; + if (! $this->export->outputHandler($text_output)) { + return false; + } + } // end if + + // Format the data + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $text_output = ''; + for ($j = 0; $j < $fields_cnt; $j++) { + if (! isset($row[$j]) || $row[$j] === null) { + $value = $GLOBALS[$what . '_null']; + } elseif ($row[$j] == '0' || $row[$j] != '') { + $value = $row[$j]; + } else { + $value = ' '; + } + $text_output .= '|' + . str_replace( + '|', + '|', + htmlspecialchars($value) + ); + } // end for + $text_output .= "\n"; + if (! $this->export->outputHandler($text_output)) { + return false; + } + } // end while + $GLOBALS['dbi']->freeResult($result); + + return true; + } + + /** + * Returns a stand-in CREATE definition to resolve view dependencies + * + * @param string $db the database name + * @param string $view the view name + * @param string $crlf the end of line sequence + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting definition + */ + public function getTableDefStandIn($db, $view, $crlf, $aliases = []) + { + $text_output = ''; + + /** + * Get the unique keys in the table + */ + $unique_keys = []; + $keys = $GLOBALS['dbi']->getTableIndexes($db, $view); + foreach ($keys as $key) { + if ($key['Non_unique'] == 0) { + $unique_keys[] = $key['Column_name']; + } + } + + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + /** + * Displays the table structure + */ + + $text_output .= "|------\n" + . '|' . __('Column') + . '|' . __('Type') + . '|' . __('Null') + . '|' . __('Default') + . "\n|------\n"; + + $columns = $GLOBALS['dbi']->getColumns($db, $view); + foreach ($columns as $column) { + $col_as = $column['Field'] ?? null; + if (! empty($aliases[$db]['tables'][$view]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$view]['columns'][$col_as]; + } + $text_output .= $this->formatOneColumnDefinition( + $column, + $unique_keys, + $col_as + ); + $text_output .= "\n"; + } // end foreach + + return $text_output; + } + + /** + * Returns $table's CREATE definition + * + * @param string $db the database name + * @param string $table the table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * $this->exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $show_dates whether to include creation/update/check dates + * @param bool $add_semicolon whether to add semicolon and end-of-line + * at the end + * @param bool $view whether we're handling a view + * @param array $aliases Aliases of db/table/columns + * + * @return string resulting schema + */ + public function getTableDef( + $db, + $table, + $crlf, + $error_url, + $do_relation, + $do_comments, + $do_mime, + $show_dates = false, + $add_semicolon = true, + $view = false, + array $aliases = [] + ) { + global $cfgRelation; + + $text_output = ''; + + /** + * Get the unique keys in the table + */ + $unique_keys = []; + $keys = $GLOBALS['dbi']->getTableIndexes($db, $table); + foreach ($keys as $key) { + if ($key['Non_unique'] == 0) { + $unique_keys[] = $key['Column_name']; + } + } + + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + // Check if we can use Relations + list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus( + $do_relation && ! empty($cfgRelation['relation']), + $db, + $table + ); + + /** + * Displays the table structure + */ + + $text_output .= "|------\n"; + $text_output .= '|' . __('Column'); + $text_output .= '|' . __('Type'); + $text_output .= '|' . __('Null'); + $text_output .= '|' . __('Default'); + if ($do_relation && $have_rel) { + $text_output .= '|' . __('Links to'); + } + if ($do_comments) { + $text_output .= '|' . __('Comments'); + $comments = $this->relation->getComments($db, $table); + } + if ($do_mime && $cfgRelation['mimework']) { + $text_output .= '|' . __('Media (MIME) type'); + $mime_map = $this->transformations->getMime($db, $table, true); + } + $text_output .= "\n|------\n"; + + $columns = $GLOBALS['dbi']->getColumns($db, $table); + foreach ($columns as $column) { + $col_as = $column['Field']; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $text_output .= $this->formatOneColumnDefinition( + $column, + $unique_keys, + $col_as + ); + $field_name = $column['Field']; + if ($do_relation && $have_rel) { + $text_output .= '|' . htmlspecialchars( + $this->getRelationString( + $res_rel, + $field_name, + $db, + $aliases + ) + ); + } + if ($do_comments && $cfgRelation['commwork']) { + $text_output .= '|' + . (isset($comments[$field_name]) + ? htmlspecialchars($comments[$field_name]) + : ''); + } + if ($do_mime && $cfgRelation['mimework']) { + $text_output .= '|' + . (isset($mime_map[$field_name]) + ? htmlspecialchars( + str_replace('_', '/', $mime_map[$field_name]['mimetype']) + ) + : ''); + } + + $text_output .= "\n"; + } // end foreach + + return $text_output; + } // end of the '$this->getTableDef()' function + + /** + * Outputs triggers + * + * @param string $db database name + * @param string $table table name + * + * @return string Formatted triggers list + */ + public function getTriggers($db, $table) + { + $dump = "|------\n"; + $dump .= '|' . __('Name'); + $dump .= '|' . __('Time'); + $dump .= '|' . __('Event'); + $dump .= '|' . __('Definition'); + $dump .= "\n|------\n"; + + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + + foreach ($triggers as $trigger) { + $dump .= '|' . $trigger['name']; + $dump .= '|' . $trigger['action_timing']; + $dump .= '|' . $trigger['event_manipulation']; + $dump .= '|' . + str_replace( + '|', + '|', + htmlspecialchars($trigger['definition']) + ); + $dump .= "\n"; + } + + return $dump; + } + + /** + * Outputs table's structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table', 'triggers', 'create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * $this->exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $dump = ''; + + switch ($export_mode) { + case 'create_table': + $dump .= '== ' . __('Table structure for table') . ' ' + . $table_alias . "\n\n"; + $dump .= $this->getTableDef( + $db, + $table, + $crlf, + $error_url, + $do_relation, + $do_comments, + $do_mime, + $dates, + true, + false, + $aliases + ); + break; + case 'triggers': + $dump = ''; + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + if ($triggers) { + $dump .= '== ' . __('Triggers') . ' ' . $table_alias . "\n\n"; + $dump .= $this->getTriggers($db, $table); + } + break; + case 'create_view': + $dump .= '== ' . __('Structure for view') . ' ' . $table_alias . "\n\n"; + $dump .= $this->getTableDef( + $db, + $table, + $crlf, + $error_url, + $do_relation, + $do_comments, + $do_mime, + $dates, + true, + true, + $aliases + ); + break; + case 'stand_in': + $dump .= '== ' . __('Stand-in structure for view') + . ' ' . $table . "\n\n"; + // export a stand-in definition to resolve view dependencies + $dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases); + } // end switch + + return $this->export->outputHandler($dump); + } + + /** + * Formats the definition for one column + * + * @param array $column info about this column + * @param array $unique_keys unique keys for this table + * @param string $col_alias Column Alias + * + * @return string Formatted column definition + */ + public function formatOneColumnDefinition( + $column, + $unique_keys, + $col_alias = '' + ) { + if (empty($col_alias)) { + $col_alias = $column['Field']; + } + $extracted_columnspec + = Util::extractColumnSpec($column['Type']); + $type = $extracted_columnspec['print_type']; + if (empty($type)) { + $type = ' '; + } + + if (! isset($column['Default'])) { + if ($column['Null'] != 'NO') { + $column['Default'] = 'NULL'; + } + } + + $fmt_pre = ''; + $fmt_post = ''; + if (in_array($column['Field'], $unique_keys)) { + $fmt_pre = '**' . $fmt_pre; + $fmt_post .= '**'; + } + if ($column['Key'] == 'PRI') { + $fmt_pre = '//' . $fmt_pre; + $fmt_post .= '//'; + } + $definition = '|' + . $fmt_pre . htmlspecialchars($col_alias) . $fmt_post; + $definition .= '|' . htmlspecialchars($type); + $definition .= '|' + . (($column['Null'] == '' || $column['Null'] == 'NO') + ? __('No') : __('Yes')); + $definition .= '|' + . htmlspecialchars( + isset($column['Default']) ? $column['Default'] : '' + ); + + return $definition; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportXml.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportXml.php new file mode 100644 index 0000000..db85add --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportXml.php @@ -0,0 +1,593 @@ +setProperties(); + } + + /** + * Initialize the local variables that are used for export XML + * + * @return void + */ + protected function initSpecificVariables() + { + global $table, $tables; + $this->_setTable($table); + if (is_array($tables)) { + $this->_setTables($tables); + } + } + + /** + * Sets the export XML properties + * + * @return void + */ + protected function setProperties() + { + // create the export plugin property item + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('XML'); + $exportPluginProperties->setExtension('xml'); + $exportPluginProperties->setMimeType('text/xml'); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new HiddenPropertyItem("structure_or_data"); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // export structure main group + $structure = new OptionsPropertyMainGroup( + "structure", + __('Object creation options (all are recommended)') + ); + + // create primary items and add them to the group + $leaf = new BoolPropertyItem( + "export_events", + __('Events') + ); + $structure->addProperty($leaf); + $leaf = new BoolPropertyItem( + "export_functions", + __('Functions') + ); + $structure->addProperty($leaf); + $leaf = new BoolPropertyItem( + "export_procedures", + __('Procedures') + ); + $structure->addProperty($leaf); + $leaf = new BoolPropertyItem( + "export_tables", + __('Tables') + ); + $structure->addProperty($leaf); + $leaf = new BoolPropertyItem( + "export_triggers", + __('Triggers') + ); + $structure->addProperty($leaf); + $leaf = new BoolPropertyItem( + "export_views", + __('Views') + ); + $structure->addProperty($leaf); + $exportSpecificOptions->addProperty($structure); + + // data main group + $data = new OptionsPropertyMainGroup( + "data", + __('Data dump options') + ); + // create primary items and add them to the group + $leaf = new BoolPropertyItem( + "export_contents", + __('Export contents') + ); + $data->addProperty($leaf); + $exportSpecificOptions->addProperty($data); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Generates output for SQL defintions of routines + * + * @param string $db Database name + * @param string $type Item type to be used in XML output + * @param string $dbitype Item type used in DBI qieries + * + * @return string XML with definitions + */ + private function _exportRoutines($db, $type, $dbitype) + { + // Export routines + $routines = $GLOBALS['dbi']->getProceduresOrFunctions( + $db, + $dbitype + ); + return $this->_exportDefinitions($db, $type, $dbitype, $routines); + } + + /** + * Generates output for SQL defintions + * + * @param string $db Database name + * @param string $type Item type to be used in XML output + * @param string $dbitype Item type used in DBI qieries + * @param array $names Names of items to export + * + * @return string XML with definitions + */ + private function _exportDefinitions($db, $type, $dbitype, array $names) + { + global $crlf; + + $head = ''; + + if ($names) { + foreach ($names as $name) { + $head .= ' ' . $crlf; + + // Do some formatting + $sql = $GLOBALS['dbi']->getDefinition($db, $dbitype, $name); + $sql = htmlspecialchars(rtrim($sql)); + $sql = str_replace("\n", "\n ", $sql); + + $head .= " " . $sql . $crlf; + $head .= ' ' . $crlf; + } + } + + return $head; + } + + /** + * Outputs export header. It is the first method to be called, so all + * the required variables are initialized here. + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + $this->initSpecificVariables(); + global $crlf, $cfg, $db; + $table = $this->_getTable(); + $tables = $this->_getTables(); + + $export_struct = isset($GLOBALS['xml_export_functions']) + || isset($GLOBALS['xml_export_procedures']) + || isset($GLOBALS['xml_export_tables']) + || isset($GLOBALS['xml_export_triggers']) + || isset($GLOBALS['xml_export_views']); + $export_data = isset($GLOBALS['xml_export_contents']) ? true : false; + + if ($GLOBALS['output_charset_conversion']) { + $charset = $GLOBALS['charset']; + } else { + $charset = 'utf-8'; + } + + $head = '' . $crlf + . '' . $crlf . $crlf; + + $head .= '' . $crlf; + + if ($export_struct) { + $result = $GLOBALS['dbi']->fetchResult( + 'SELECT `DEFAULT_CHARACTER_SET_NAME`, `DEFAULT_COLLATION_NAME`' + . ' FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME`' + . ' = \'' . $GLOBALS['dbi']->escapeString($db) . '\' LIMIT 1' + ); + $db_collation = $result[0]['DEFAULT_COLLATION_NAME']; + $db_charset = $result[0]['DEFAULT_CHARACTER_SET_NAME']; + + $head .= ' ' . $crlf; + $head .= ' ' . $crlf; + $head .= ' ' . $crlf; + + if ($tables === null) { + $tables = []; + } + + if (count($tables) === 0) { + $tables[] = $table; + } + + foreach ($tables as $table) { + // Export tables and views + $result = $GLOBALS['dbi']->fetchResult( + 'SHOW CREATE TABLE ' . Util::backquote($db) . '.' + . Util::backquote($table), + 0 + ); + $tbl = $result[$table][1]; + + $is_view = $GLOBALS['dbi']->getTable($db, $table) + ->isView(); + + if ($is_view) { + $type = 'view'; + } else { + $type = 'table'; + } + + if ($is_view && ! isset($GLOBALS['xml_export_views'])) { + continue; + } + + if (! $is_view && ! isset($GLOBALS['xml_export_tables'])) { + continue; + } + + $head .= ' ' + . $crlf; + + $tbl = " " . htmlspecialchars($tbl); + $tbl = str_replace("\n", "\n ", $tbl); + + $head .= $tbl . ';' . $crlf; + $head .= ' ' . $crlf; + + if (isset($GLOBALS['xml_export_triggers']) + && $GLOBALS['xml_export_triggers'] + ) { + // Export triggers + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + if ($triggers) { + foreach ($triggers as $trigger) { + $code = $trigger['create']; + $head .= ' ' . $crlf; + + // Do some formatting + $code = mb_substr(rtrim($code), 0, -3); + $code = " " . htmlspecialchars($code); + $code = str_replace("\n", "\n ", $code); + + $head .= $code . $crlf; + $head .= ' ' . $crlf; + } + + unset($trigger); + unset($triggers); + } + } + } + + if (isset($GLOBALS['xml_export_functions']) + && $GLOBALS['xml_export_functions'] + ) { + $head .= $this->_exportRoutines($db, 'function', 'FUNCTION'); + } + + if (isset($GLOBALS['xml_export_procedures']) + && $GLOBALS['xml_export_procedures'] + ) { + $head .= $this->_exportRoutines($db, 'procedure', 'PROCEDURE'); + } + + if (isset($GLOBALS['xml_export_events']) + && $GLOBALS['xml_export_events'] + ) { + // Export events + $events = $GLOBALS['dbi']->fetchResult( + "SELECT EVENT_NAME FROM information_schema.EVENTS " + . "WHERE EVENT_SCHEMA='" . $GLOBALS['dbi']->escapeString($db) + . "'" + ); + $head .= $this->_exportDefinitions( + $db, + 'event', + 'EVENT', + $events + ); + } + + unset($result); + + $head .= ' ' . $crlf; + $head .= ' ' . $crlf; + + if ($export_data) { + $head .= $crlf; + } + } + + return $this->export->outputHandler($head); + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + $foot = ''; + + return $this->export->outputHandler($foot); + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + global $crlf; + + if (empty($db_alias)) { + $db_alias = $db; + } + if (isset($GLOBALS['xml_export_contents']) + && $GLOBALS['xml_export_contents'] + ) { + $head = ' ' . $crlf . ' ' . $crlf; + + return $this->export->outputHandler($head); + } + + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + global $crlf; + + if (isset($GLOBALS['xml_export_contents']) + && $GLOBALS['xml_export_contents'] + ) { + return $this->export->outputHandler(' ' . $crlf); + } + + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in XML format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + // Do not export data for merge tables + if ($GLOBALS['dbi']->getTable($db, $table)->isMerge()) { + return true; + } + + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + if (isset($GLOBALS['xml_export_contents']) + && $GLOBALS['xml_export_contents'] + ) { + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + + $columns_cnt = $GLOBALS['dbi']->numFields($result); + $columns = []; + for ($i = 0; $i < $columns_cnt; $i++) { + $columns[$i] = stripslashes($GLOBALS['dbi']->fieldName($result, $i)); + } + unset($i); + + $buffer = ' ' . $crlf; + if (! $this->export->outputHandler($buffer)) { + return false; + } + + while ($record = $GLOBALS['dbi']->fetchRow($result)) { + $buffer = ' ' . $crlf; + for ($i = 0; $i < $columns_cnt; $i++) { + $col_as = $columns[$i]; + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as]) + ) { + $col_as + = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + // If a cell is NULL, still export it to preserve + // the XML structure + if (! isset($record[$i]) || $record[$i] === null) { + $record[$i] = 'NULL'; + } + $buffer .= ' ' + . htmlspecialchars((string) $record[$i]) + . '' . $crlf; + } + $buffer .= '
' . $crlf; + + if (! $this->export->outputHandler($buffer)) { + return false; + } + } + $GLOBALS['dbi']->freeResult($result); + } + + return true; + } + + + /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ + + /** + * Gets the table name + * + * @return string + */ + private function _getTable() + { + return $this->_table; + } + + /** + * Sets the table name + * + * @param string $table table name + * + * @return void + */ + private function _setTable($table) + { + $this->_table = $table; + } + + /** + * Gets the table names + * + * @return array + */ + private function _getTables() + { + return $this->_tables; + } + + /** + * Sets the table names + * + * @param array $tables table names + * + * @return void + */ + private function _setTables(array $tables) + { + $this->_tables = $tables; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportYaml.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportYaml.php new file mode 100644 index 0000000..971f9c6 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/ExportYaml.php @@ -0,0 +1,230 @@ +setProperties(); + } + + /** + * Sets the export YAML properties + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new ExportPluginProperties(); + $exportPluginProperties->setText('YAML'); + $exportPluginProperties->setExtension('yml'); + $exportPluginProperties->setMimeType('text/yaml'); + $exportPluginProperties->setForceFile(true); + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup("general_opts"); + // create primary items and add them to the group + $leaf = new HiddenPropertyItem("structure_or_data"); + $generalOptions->addProperty($leaf); + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader() + { + $this->export->outputHandler( + '%YAML 1.1' . $GLOBALS['crlf'] . '---' . $GLOBALS['crlf'] + ); + + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter() + { + $this->export->outputHandler('...' . $GLOBALS['crlf']); + + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader($db, $db_alias = '') + { + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter($db) + { + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $export_type 'server', 'database', 'table' + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $export_type, $db_alias = '') + { + return true; + } + + /** + * Outputs the content of a table in JSON format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, + $table, + $crlf, + $error_url, + $sql_query, + array $aliases = [] + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $result = $GLOBALS['dbi']->query( + $sql_query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + + $columns_cnt = $GLOBALS['dbi']->numFields($result); + $columns = []; + for ($i = 0; $i < $columns_cnt; $i++) { + $col_as = $GLOBALS['dbi']->fieldName($result, $i); + if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $columns[$i] = stripslashes($col_as); + } + + $buffer = ''; + $record_cnt = 0; + while ($record = $GLOBALS['dbi']->fetchRow($result)) { + $record_cnt++; + + // Output table name as comment if this is the first record of the table + if ($record_cnt == 1) { + $buffer = '# ' . $db_alias . '.' . $table_alias . $crlf; + $buffer .= '-' . $crlf; + } else { + $buffer = '-' . $crlf; + } + + for ($i = 0; $i < $columns_cnt; $i++) { + if (! isset($record[$i])) { + continue; + } + + if ($record[$i] === null) { + $buffer .= ' ' . $columns[$i] . ': null' . $crlf; + continue; + } + + if (is_numeric($record[$i])) { + $buffer .= ' ' . $columns[$i] . ': ' . $record[$i] . $crlf; + continue; + } + + $record[$i] = str_replace( + [ + '\\', + '"', + "\n", + "\r", + ], + [ + '\\\\', + '\"', + '\n', + '\r', + ], + $record[$i] + ); + $buffer .= ' ' . $columns[$i] . ': "' . $record[$i] . '"' . $crlf; + } + + if (! $this->export->outputHandler($buffer)) { + return false; + } + } + $GLOBALS['dbi']->freeResult($result); + + return true; + } // end getTableYAML +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/Pdf.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/Pdf.php new file mode 100644 index 0000000..58f1b5a --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/Pdf.php @@ -0,0 +1,855 @@ +relation = new Relation($GLOBALS['dbi']); + $this->transformations = new Transformations(); + } + + /** + * Add page if needed. + * + * @param float|int $h cell height. Default value: 0 + * @param mixed $y starting y position, leave empty for current + * position + * @param boolean $addpage if true add a page, otherwise only return + * the true/false state + * + * @return boolean true in case of page break, false otherwise. + */ + public function checkPageBreak($h = 0, $y = '', $addpage = true) + { + if (TCPDF_STATIC::empty_string($y)) { + $y = $this->y; + } + $current_page = $this->page; + if ((($y + $h) > $this->PageBreakTrigger) + && (! $this->InFooter) + && $this->AcceptPageBreak() + ) { + if ($addpage) { + //Automatic page break + $x = $this->x; + $this->AddPage($this->CurOrientation); + $this->y = $this->dataY; + $oldpage = $this->page - 1; + + $this_page_orm = $this->pagedim[$this->page]['orm']; + $old_page_orm = $this->pagedim[$oldpage]['orm']; + $this_page_olm = $this->pagedim[$this->page]['olm']; + $old_page_olm = $this->pagedim[$oldpage]['olm']; + if ($this->rtl) { + if ($this_page_orm != $old_page_orm) { + $this->x = $x - ($this_page_orm - $old_page_orm); + } else { + $this->x = $x; + } + } else { + if ($this_page_olm != $old_page_olm) { + $this->x = $x + ($this_page_olm - $old_page_olm); + } else { + $this->x = $x; + } + } + } + + return true; + } + + // account for columns mode + return $current_page != $this->page; + } + + /** + * This method is used to render the page header. + * + * @return void + */ + // @codingStandardsIgnoreLine + public function Header() + { + global $maxY; + // We don't want automatic page breaks while generating header + // as this can lead to infinite recursion as auto generated page + // will want header as well causing another page break + // FIXME: Better approach might be to try to compact the content + $this->SetAutoPageBreak(false); + // Check if header for this page already exists + if (! isset($this->headerset[$this->page])) { + $this->SetY($this->tMargin - ($this->FontSizePt / $this->k) * 5); + $this->cellFontSize = $this->FontSizePt; + $this->SetFont( + PdfLib::PMA_PDF_FONT, + '', + ($this->titleFontSize + ?: $this->FontSizePt) + ); + $this->Cell(0, $this->FontSizePt, $this->titleText, 0, 1, 'C'); + $this->SetFont(PdfLib::PMA_PDF_FONT, '', $this->cellFontSize); + $this->SetY($this->tMargin - ($this->FontSizePt / $this->k) * 2.5); + $this->Cell( + 0, + $this->FontSizePt, + __('Database:') . ' ' . $this->dbAlias . ', ' + . __('Table:') . ' ' . $this->tableAlias . ', ' + . __('Purpose:') . ' ' . $this->purpose, + 0, + 1, + 'L' + ); + $l = $this->lMargin; + foreach ($this->colTitles as $col => $txt) { + $this->SetXY($l, $this->tMargin); + $this->MultiCell( + $this->tablewidths[$col], + $this->FontSizePt, + $txt + ); + $l += $this->tablewidths[$col]; + $maxY = $maxY < $this->GetY() ? $this->GetY() : $maxY; + } + $this->SetXY($this->lMargin, $this->tMargin); + $this->SetFillColor(200, 200, 200); + $l = $this->lMargin; + foreach ($this->colTitles as $col => $txt) { + $this->SetXY($l, $this->tMargin); + $this->Cell( + $this->tablewidths[$col], + $maxY - $this->tMargin, + '', + 1, + 0, + 'L', + 1 + ); + $this->SetXY($l, $this->tMargin); + $this->MultiCell( + $this->tablewidths[$col], + $this->FontSizePt, + $txt, + 0, + 'C' + ); + $l += $this->tablewidths[$col]; + } + $this->SetFillColor(255, 255, 255); + // set headerset + $this->headerset[$this->page] = 1; + } + + $this->dataY = $maxY; + $this->SetAutoPageBreak(true); + } + + /** + * Generate table + * + * @param int $lineheight Height of line + * + * @return void + */ + public function morepagestable($lineheight = 8) + { + // some things to set and 'remember' + $l = $this->lMargin; + $startheight = $h = $this->dataY; + $startpage = $currpage = $this->page; + + // calculate the whole width + $fullwidth = 0; + foreach ($this->tablewidths as $width) { + $fullwidth += $width; + } + + // Now let's start to write the table + $row = 0; + $tmpheight = []; + $maxpage = $this->page; + + while ($data = $GLOBALS['dbi']->fetchRow($this->results)) { + $this->page = $currpage; + // write the horizontal borders + $this->Line($l, $h, $fullwidth + $l, $h); + // write the content and remember the height of the highest col + foreach ($data as $col => $txt) { + $this->page = $currpage; + $this->SetXY($l, $h); + if ($this->tablewidths[$col] > 0) { + $this->MultiCell( + $this->tablewidths[$col], + $lineheight, + $txt, + 0, + $this->colAlign[$col] + ); + $l += $this->tablewidths[$col]; + } + + if (! isset($tmpheight[$row . '-' . $this->page])) { + $tmpheight[$row . '-' . $this->page] = 0; + } + if ($tmpheight[$row . '-' . $this->page] < $this->GetY()) { + $tmpheight[$row . '-' . $this->page] = $this->GetY(); + } + if ($this->page > $maxpage) { + $maxpage = $this->page; + } + unset($data[$col]); + } + + // get the height we were in the last used page + $h = $tmpheight[$row . '-' . $maxpage]; + // set the "pointer" to the left margin + $l = $this->lMargin; + // set the $currpage to the last page + $currpage = $maxpage; + unset($data[$row]); + $row++; + } + // draw the borders + // we start adding a horizontal line on the last page + $this->page = $maxpage; + $this->Line($l, $h, $fullwidth + $l, $h); + // now we start at the top of the document and walk down + for ($i = $startpage; $i <= $maxpage; $i++) { + $this->page = $i; + $l = $this->lMargin; + $t = $i == $startpage ? $startheight : $this->tMargin; + $lh = $i == $maxpage ? $h : $this->h - $this->bMargin; + $this->Line($l, $t, $l, $lh); + foreach ($this->tablewidths as $width) { + $l += $width; + $this->Line($l, $t, $l, $lh); + } + } + // set it to the last page, if not it'll cause some problems + $this->page = $maxpage; + } + + /** + * Sets a set of attributes. + * + * @param array $attr array containing the attributes + * + * @return void + */ + public function setAttributes(array $attr = []) + { + foreach ($attr as $key => $val) { + $this->$key = $val; + } + } + + /** + * Defines the top margin. + * The method can be called before creating the first page. + * + * @param float $topMargin the margin + * + * @return void + */ + public function setTopMargin($topMargin) + { + $this->tMargin = $topMargin; + } + + /** + * Prints triggers + * + * @param string $db database name + * @param string $table table name + * + * @return void + */ + public function getTriggers($db, $table) + { + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + if ([] === $triggers) { + return; //prevents printing blank trigger list for any table + } + + unset($this->tablewidths); + unset($this->colTitles); + unset($this->titleWidth); + unset($this->colFits); + unset($this->display_column); + unset($this->colAlign); + + /** + * Making table heading + * Keeping column width constant + */ + $this->colTitles[0] = __('Name'); + $this->tablewidths[0] = 90; + $this->colTitles[1] = __('Time'); + $this->tablewidths[1] = 80; + $this->colTitles[2] = __('Event'); + $this->tablewidths[2] = 40; + $this->colTitles[3] = __('Definition'); + $this->tablewidths[3] = 240; + + for ($columns_cnt = 0; $columns_cnt < 4; $columns_cnt++) { + $this->colAlign[$columns_cnt] = 'L'; + $this->display_column[$columns_cnt] = true; + } + + // Starting to fill table with required info + + $this->SetY($this->tMargin); + $this->AddPage(); + $this->SetFont(PdfLib::PMA_PDF_FONT, '', 9); + + $l = $this->lMargin; + $startheight = $h = $this->dataY; + $startpage = $currpage = $this->page; + + // calculate the whole width + $fullwidth = 0; + foreach ($this->tablewidths as $width) { + $fullwidth += $width; + } + + $row = 0; + $tmpheight = []; + $maxpage = $this->page; + $data = []; + + foreach ($triggers as $trigger) { + $data[] = $trigger['name']; + $data[] = $trigger['action_timing']; + $data[] = $trigger['event_manipulation']; + $data[] = $trigger['definition']; + $this->page = $currpage; + // write the horizontal borders + $this->Line($l, $h, $fullwidth + $l, $h); + // write the content and remember the height of the highest col + foreach ($data as $col => $txt) { + $this->page = $currpage; + $this->SetXY($l, $h); + if ($this->tablewidths[$col] > 0) { + $this->MultiCell( + $this->tablewidths[$col], + $this->FontSizePt, + $txt, + 0, + $this->colAlign[$col] + ); + $l += $this->tablewidths[$col]; + } + + if (! isset($tmpheight[$row . '-' . $this->page])) { + $tmpheight[$row . '-' . $this->page] = 0; + } + if ($tmpheight[$row . '-' . $this->page] < $this->GetY()) { + $tmpheight[$row . '-' . $this->page] = $this->GetY(); + } + if ($this->page > $maxpage) { + $maxpage = $this->page; + } + } + // get the height we were in the last used page + $h = $tmpheight[$row . '-' . $maxpage]; + // set the "pointer" to the left margin + $l = $this->lMargin; + // set the $currpage to the last page + $currpage = $maxpage; + unset($data); + $row++; + } + // draw the borders + // we start adding a horizontal line on the last page + $this->page = $maxpage; + $this->Line($l, $h, $fullwidth + $l, $h); + // now we start at the top of the document and walk down + for ($i = $startpage; $i <= $maxpage; $i++) { + $this->page = $i; + $l = $this->lMargin; + $t = $i == $startpage ? $startheight : $this->tMargin; + $lh = $i == $maxpage ? $h : $this->h - $this->bMargin; + $this->Line($l, $t, $l, $lh); + foreach ($this->tablewidths as $width) { + $l += $width; + $this->Line($l, $t, $l, $lh); + } + } + // set it to the last page, if not it'll cause some problems + $this->page = $maxpage; + } + + /** + * Print $table's CREATE definition + * + * @param string $db the database name + * @param string $table the table name + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $view whether we're handling a view + * @param array $aliases aliases of db/table/columns + * + * @return void + */ + public function getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + $view = false, + array $aliases = [] + ) { + // set $cfgRelation here, because there is a chance that it's modified + // since the class initialization + global $cfgRelation; + + unset($this->tablewidths); + unset($this->colTitles); + unset($this->titleWidth); + unset($this->colFits); + unset($this->display_column); + unset($this->colAlign); + + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + /** + * All these three checks do_relation, do_comment and do_mime is + * not required. As presently all are set true by default. + * But when, methods to take user input will be developed, + * it will be of use + */ + // Check if we can use Relations + if ($do_relation) { + // Find which tables are related with the current one and write it in + // an array + $res_rel = $this->relation->getForeigners($db, $table); + $have_rel = ! empty($res_rel); + } else { + $have_rel = false; + } // end if + + //column count and table heading + + $this->colTitles[0] = __('Column'); + $this->tablewidths[0] = 90; + $this->colTitles[1] = __('Type'); + $this->tablewidths[1] = 80; + $this->colTitles[2] = __('Null'); + $this->tablewidths[2] = 40; + $this->colTitles[3] = __('Default'); + $this->tablewidths[3] = 120; + + for ($columns_cnt = 0; $columns_cnt < 4; $columns_cnt++) { + $this->colAlign[$columns_cnt] = 'L'; + $this->display_column[$columns_cnt] = true; + } + + if ($do_relation && $have_rel) { + $this->colTitles[$columns_cnt] = __('Links to'); + $this->display_column[$columns_cnt] = true; + $this->colAlign[$columns_cnt] = 'L'; + $this->tablewidths[$columns_cnt] = 120; + $columns_cnt++; + } + if ($do_comments /*&& $cfgRelation['commwork']*/) { + $this->colTitles[$columns_cnt] = __('Comments'); + $this->display_column[$columns_cnt] = true; + $this->colAlign[$columns_cnt] = 'L'; + $this->tablewidths[$columns_cnt] = 120; + $columns_cnt++; + } + if ($do_mime && $cfgRelation['mimework']) { + $this->colTitles[$columns_cnt] = __('Media (MIME) type'); + $this->display_column[$columns_cnt] = true; + $this->colAlign[$columns_cnt] = 'L'; + $this->tablewidths[$columns_cnt] = 120; + $columns_cnt++; + } + + // Starting to fill table with required info + + $this->SetY($this->tMargin); + $this->AddPage(); + $this->SetFont(PdfLib::PMA_PDF_FONT, '', 9); + + // Now let's start to write the table structure + + if ($do_comments) { + $comments = $this->relation->getComments($db, $table); + } + if ($do_mime && $cfgRelation['mimework']) { + $mime_map = $this->transformations->getMime($db, $table, true); + } + + $columns = $GLOBALS['dbi']->getColumns($db, $table); + + // some things to set and 'remember' + $l = $this->lMargin; + $startheight = $h = $this->dataY; + $startpage = $currpage = $this->page; + // calculate the whole width + $fullwidth = 0; + foreach ($this->tablewidths as $width) { + $fullwidth += $width; + } + + $row = 0; + $tmpheight = []; + $maxpage = $this->page; + $data = []; + + // fun begin + foreach ($columns as $column) { + $extracted_columnspec + = Util::extractColumnSpec($column['Type']); + + $type = $extracted_columnspec['print_type']; + if (empty($type)) { + $type = ' '; + } + + if (! isset($column['Default'])) { + if ($column['Null'] != 'NO') { + $column['Default'] = 'NULL'; + } + } + $data[] = $column['Field']; + $data[] = $type; + $data[] = $column['Null'] == '' || $column['Null'] == 'NO' + ? 'No' + : 'Yes'; + $data[] = isset($column['Default']) ? $column['Default'] : ''; + + $field_name = $column['Field']; + + if ($do_relation && $have_rel) { + $data[] = isset($res_rel[$field_name]) + ? $res_rel[$field_name]['foreign_table'] + . ' (' . $res_rel[$field_name]['foreign_field'] + . ')' + : ''; + } + if ($do_comments) { + $data[] = isset($comments[$field_name]) + ? $comments[$field_name] + : ''; + } + if ($do_mime) { + $data[] = isset($mime_map[$field_name]) + ? $mime_map[$field_name]['mimetype'] + : ''; + } + + $this->page = $currpage; + // write the horizontal borders + $this->Line($l, $h, $fullwidth + $l, $h); + // write the content and remember the height of the highest col + foreach ($data as $col => $txt) { + $this->page = $currpage; + $this->SetXY($l, $h); + if ($this->tablewidths[$col] > 0) { + $this->MultiCell( + $this->tablewidths[$col], + $this->FontSizePt, + $txt, + 0, + $this->colAlign[$col] + ); + $l += $this->tablewidths[$col]; + } + + if (! isset($tmpheight[$row . '-' . $this->page])) { + $tmpheight[$row . '-' . $this->page] = 0; + } + if ($tmpheight[$row . '-' . $this->page] < $this->GetY()) { + $tmpheight[$row . '-' . $this->page] = $this->GetY(); + } + if ($this->page > $maxpage) { + $maxpage = $this->page; + } + } + + // get the height we were in the last used page + $h = $tmpheight[$row . '-' . $maxpage]; + // set the "pointer" to the left margin + $l = $this->lMargin; + // set the $currpage to the last page + $currpage = $maxpage; + unset($data); + $row++; + } + // draw the borders + // we start adding a horizontal line on the last page + $this->page = $maxpage; + $this->Line($l, $h, $fullwidth + $l, $h); + // now we start at the top of the document and walk down + for ($i = $startpage; $i <= $maxpage; $i++) { + $this->page = $i; + $l = $this->lMargin; + $t = $i == $startpage ? $startheight : $this->tMargin; + $lh = $i == $maxpage ? $h : $this->h - $this->bMargin; + $this->Line($l, $t, $l, $lh); + foreach ($this->tablewidths as $width) { + $l += $width; + $this->Line($l, $t, $l, $lh); + } + } + // set it to the last page, if not it'll cause some problems + $this->page = $maxpage; + } + + /** + * MySQL report + * + * @param string $query Query to execute + * + * @return void + */ + public function mysqlReport($query) + { + unset($this->tablewidths); + unset($this->colTitles); + unset($this->titleWidth); + unset($this->colFits); + unset($this->display_column); + unset($this->colAlign); + + /** + * Pass 1 for column widths + */ + $this->results = $GLOBALS['dbi']->query( + $query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $this->numFields = $GLOBALS['dbi']->numFields($this->results); + $this->fields = $GLOBALS['dbi']->getFieldsMeta($this->results); + + // sColWidth = starting col width (an average size width) + $availableWidth = $this->w - $this->lMargin - $this->rMargin; + $this->sColWidth = $availableWidth / $this->numFields; + $totalTitleWidth = 0; + + // loop through results header and set initial + // col widths/ titles/ alignment + // if a col title is less than the starting col width, + // reduce that column size + $colFits = []; + $titleWidth = []; + for ($i = 0; $i < $this->numFields; $i++) { + $col_as = $this->fields[$i]->name; + $db = $this->currentDb; + $table = $this->currentTable; + if (! empty($this->aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $this->aliases[$db]['tables'][$table]['columns'][$col_as]; + } + $stringWidth = $this->GetStringWidth($col_as) + 6; + // save the real title's width + $titleWidth[$i] = $stringWidth; + $totalTitleWidth += $stringWidth; + + // set any column titles less than the start width to + // the column title width + if ($stringWidth < $this->sColWidth) { + $colFits[$i] = $stringWidth; + } + $this->colTitles[$i] = $col_as; + $this->display_column[$i] = true; + + switch ($this->fields[$i]->type) { + case 'int': + $this->colAlign[$i] = 'R'; + break; + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + /** + * @todo do not deactivate completely the display + * but show the field's name and [BLOB] + */ + if (false !== stripos($this->fields[$i]->flags, 'BINARY')) { + $this->display_column[$i] = false; + unset($this->colTitles[$i]); + } + $this->colAlign[$i] = 'L'; + break; + default: + $this->colAlign[$i] = 'L'; + } + } + + // title width verification + if ($totalTitleWidth > $availableWidth) { + $adjustingMode = true; + } else { + $adjustingMode = false; + // we have enough space for all the titles at their + // original width so use the true title's width + foreach ($titleWidth as $key => $val) { + $colFits[$key] = $val; + } + } + + // loop through the data; any column whose contents + // is greater than the column size is resized + /** + * @todo force here a LIMIT to avoid reading all rows + */ + while ($row = $GLOBALS['dbi']->fetchRow($this->results)) { + foreach ($colFits as $key => $val) { + $stringWidth = $this->GetStringWidth($row[$key]) + 6; + if ($adjustingMode && ($stringWidth > $this->sColWidth)) { + // any column whose data's width is bigger than + // the start width is now discarded + unset($colFits[$key]); + } else { + // if data's width is bigger than the current column width, + // enlarge the column (but avoid enlarging it if the + // data's width is very big) + if ($stringWidth > $val + && $stringWidth < ($this->sColWidth * 3) + ) { + $colFits[$key] = $stringWidth; + } + } + } + } + + $totAlreadyFitted = 0; + foreach ($colFits as $key => $val) { + // set fitted columns to smallest size + $this->tablewidths[$key] = $val; + // to work out how much (if any) space has been freed up + $totAlreadyFitted += $val; + } + + if ($adjustingMode) { + $surplus = (count($colFits) * $this->sColWidth) - $totAlreadyFitted; + $surplusToAdd = $surplus / ($this->numFields - count($colFits)); + } else { + $surplusToAdd = 0; + } + + for ($i = 0; $i < $this->numFields; $i++) { + if (! array_key_exists($i, $colFits)) { + $this->tablewidths[$i] = $this->sColWidth + $surplusToAdd; + } + if ($this->display_column[$i] == false) { + $this->tablewidths[$i] = 0; + } + } + + ksort($this->tablewidths); + + $GLOBALS['dbi']->freeResult($this->results); + + // Pass 2 + + $this->results = $GLOBALS['dbi']->query( + $query, + DatabaseInterface::CONNECT_USER, + DatabaseInterface::QUERY_UNBUFFERED + ); + $this->SetY($this->tMargin); + $this->AddPage(); + $this->SetFont(PdfLib::PMA_PDF_FONT, '', 9); + $this->morepagestable($this->FontSizePt); + $GLOBALS['dbi']->freeResult($this->results); + } // end of mysqlReport function +} // end of Pdf class diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/TableProperty.php b/srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/TableProperty.php new file mode 100644 index 0000000..8991251 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/Helpers/TableProperty.php @@ -0,0 +1,277 @@ +name = trim((string) $row[0]); + $this->type = trim((string) $row[1]); + $this->nullable = trim((string) $row[2]); + $this->key = trim((string) $row[3]); + $this->defaultValue = trim((string) $row[4]); + $this->ext = trim((string) $row[5]); + } + + /** + * Gets the pure type + * + * @return string type + */ + public function getPureType() + { + $pos = mb_strpos($this->type, "("); + if ($pos > 0) { + return mb_substr($this->type, 0, $pos); + } + return $this->type; + } + + /** + * Tells whether the key is null or not + * + * @return string true if the key is not null, false otherwise + */ + public function isNotNull() + { + return $this->nullable === "NO" ? "true" : "false"; + } + + /** + * Tells whether the key is unique or not + * + * @return string "true" if the key is unique, "false" otherwise + */ + public function isUnique(): string + { + return ($this->key === "PRI" || $this->key === "UNI") ? "true" : "false"; + } + + /** + * Gets the .NET primitive type + * + * @return string type + */ + public function getDotNetPrimitiveType() + { + if (mb_strpos($this->type, "int") === 0) { + return "int"; + } + if (mb_strpos($this->type, "longtext") === 0) { + return "string"; + } + if (mb_strpos($this->type, "long") === 0) { + return "long"; + } + if (mb_strpos($this->type, "char") === 0) { + return "string"; + } + if (mb_strpos($this->type, "varchar") === 0) { + return "string"; + } + if (mb_strpos($this->type, "text") === 0) { + return "string"; + } + if (mb_strpos($this->type, "tinyint") === 0) { + return "bool"; + } + if (mb_strpos($this->type, "datetime") === 0) { + return "DateTime"; + } + return "unknown"; + } + + /** + * Gets the .NET object type + * + * @return string type + */ + public function getDotNetObjectType() + { + if (mb_strpos($this->type, "int") === 0) { + return "Int32"; + } + if (mb_strpos($this->type, "longtext") === 0) { + return "String"; + } + if (mb_strpos($this->type, "long") === 0) { + return "Long"; + } + if (mb_strpos($this->type, "char") === 0) { + return "String"; + } + if (mb_strpos($this->type, "varchar") === 0) { + return "String"; + } + if (mb_strpos($this->type, "text") === 0) { + return "String"; + } + if (mb_strpos($this->type, "tinyint") === 0) { + return "Boolean"; + } + if (mb_strpos($this->type, "datetime") === 0) { + return "DateTime"; + } + return "Unknown"; + } + + /** + * Gets the index name + * + * @return string containing the name of the index + */ + public function getIndexName() + { + if (strlen($this->key) > 0) { + return "index=\"" + . htmlspecialchars($this->name, ENT_COMPAT, 'UTF-8') + . "\""; + } + return ""; + } + + /** + * Tells whether the key is primary or not + * + * @return bool true if the key is primary, false otherwise + */ + public function isPK(): bool + { + return $this->key === "PRI"; + } + + /** + * Formats a string for C# + * + * @param string $text string to be formatted + * + * @return string formatted text + */ + public function formatCs($text) + { + $text = str_replace( + '#name#', + ExportCodegen::cgMakeIdentifier($this->name, false), + $text + ); + return $this->format($text); + } + + /** + * Formats a string for XML + * + * @param string $text string to be formatted + * + * @return string formatted text + */ + public function formatXml($text) + { + $text = str_replace( + [ + '#name#', + '#indexName#', + ], + [ + htmlspecialchars($this->name, ENT_COMPAT, 'UTF-8'), + $this->getIndexName(), + ], + $text + ); + return $this->format($text); + } + + /** + * Formats a string + * + * @param string $text string to be formatted + * + * @return string formatted text + */ + public function format($text) + { + $text = str_replace( + [ + '#ucfirstName#', + '#dotNetPrimitiveType#', + '#dotNetObjectType#', + '#type#', + '#notNull#', + '#unique#', + ], + [ + ExportCodegen::cgMakeIdentifier($this->name), + $this->getDotNetPrimitiveType(), + $this->getDotNetObjectType(), + $this->getPureType(), + $this->isNotNull(), + $this->isUnique(), + ], + $text + ); + return $text; + } +} diff --git a/srcs/phpmyadmin/libraries/classes/Plugins/Export/README b/srcs/phpmyadmin/libraries/classes/Plugins/Export/README new file mode 100644 index 0000000..5ada85b --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Plugins/Export/README @@ -0,0 +1,255 @@ +This directory holds export plugins for phpMyAdmin. Any new plugin should +basically follow the structure presented here. Official plugins need to +have str* messages with their definition in language files, but if you build +some plugins for your use, you can directly use texts in plugin. + +setProperties(); + } + + // optional - declare global variables and use getters later + /** + * Initialize the local variables that are used specific for export SQL + * + * @global type $global_variable_name + * [..] + * + * @return void + */ + protected function initSpecificVariables() + { + global $global_variable_name; + $this->_setGlobalVariableName($global_variable_name); + } + + /** + * Sets the export plugin properties. + * Called in the constructor. + * + * @return void + */ + protected function setProperties() + { + $exportPluginProperties = new PhpMyAdmin\Properties\Plugins\ExportPluginProperties(); + $exportPluginProperties->setText('[name]'); // the name of your plug-in + $exportPluginProperties->setExtension('[ext]'); // extension this plug-in can handle + $exportPluginProperties->setOptionsText(__('Options')); + + // create the root group that will be the options field for + // $exportPluginProperties + // this will be shown as "Format specific options" + $exportSpecificOptions = new PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup( + "Format Specific Options" + ); + + // general options main group + $generalOptions = new PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup( + "general_opts" + ); + + // optional : + // create primary items and add them to the group + // type - one of the classes listed in libraries/properties/options/items/ + // name - form element name + // text - description in GUI + // size - size of text element + // len - maximal size of input + // values - possible values of the item + $leaf = new PhpMyAdmin\Properties\Options\Items\RadioPropertyItem( + "structure_or_data" + ); + $leaf->setValues( + array( + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data') + ) + ); + $generalOptions->addProperty($leaf); + + // add the main group to the root group + $exportSpecificOptions->addProperty($generalOptions); + + // set the options for the export plugin property item + $exportPluginProperties->setOptions($exportSpecificOptions); + $this->properties = $exportPluginProperties; + } + + /** + * Outputs export header + * + * @return bool Whether it succeeded + */ + public function exportHeader () + { + // implementation + return true; + } + + /** + * Outputs export footer + * + * @return bool Whether it succeeded + */ + public function exportFooter () + { + // implementation + return true; + } + + /** + * Outputs database header + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBHeader ($db, $db_alias = '') + { + // implementation + return true; + } + + /** + * Outputs database footer + * + * @param string $db Database name + * + * @return bool Whether it succeeded + */ + public function exportDBFooter ($db) + { + // implementation + return true; + } + + /** + * Outputs CREATE DATABASE statement + * + * @param string $db Database name + * @param string $db_alias Aliases of db + * + * @return bool Whether it succeeded + */ + public function exportDBCreate($db, $db_alias = '') + { + // implementation + return true; + } + + /** + * Outputs the content of a table in [Name] format + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns + * + * @return bool Whether it succeeded + */ + public function exportData( + $db, $table, $crlf, $error_url, $sql_query, $aliases = array() + ) { + // implementation; + return true; + } + + // optional - implement other methods defined in PhpMyAdmin\Plugins\ExportPlugin.class.php: + // - exportRoutines() + // - exportStructure() + // - getTableDefStandIn() + // - getTriggers() + + // optional - implement other private methods in order to avoid + // having huge methods or avoid duplicate code. Make use of them + // as well as of the getters and setters declared both here + // and in the PhpMyAdmin\Plugins\ExportPlugin class + + + // optional: + /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ + + + /** + * Getter description + * + * @return type + */ + private function _getMyOptionalVariable() + { + return $this->_myOptionalVariable; + } + + /** + * Setter description + * + * @param type $my_optional_variable description + * + * @return void + */ + private function _setMyOptionalVariable($my_optional_variable) + { + $this->_myOptionalVariable = $my_optional_variable; + } + + /** + * Getter description + * + * @return type + */ + private function _getGlobalVariableName() + { + return $this->_globalVariableName; + } + + /** + * Setter description + * + * @param type $global_variable_name description + * + * @return void + */ + private function _setGlobalVariableName($global_variable_name) + { + $this->_globalVariableName = $global_variable_name; + } +} +?> -- cgit