Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 08:02:04 +01:00
parent 4413528994
commit 1d90fbf296
6865 changed files with 1091082 additions and 0 deletions

View file

@ -0,0 +1,392 @@
<?php
/**
* Set of functions used to build NHibernate dumps of tables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Plugins\Export\Helpers\TableProperty;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use function __;
use function implode;
use function preg_match;
use function preg_replace;
use function sprintf;
use function ucfirst;
/**
* Handles the export for the CodeGen class
*/
class ExportCodegen extends ExportPlugin
{
/**
* CodeGen Formats
*
* @var array
*/
private $cgFormats;
private const HANDLER_NHIBERNATE_CS = 0;
private const HANDLER_NHIBERNATE_XML = 1;
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'codegen';
}
/**
* Initialize the local variables that are used for export CodeGen.
*/
protected function init(): void
{
$this->setCgFormats([
self::HANDLER_NHIBERNATE_CS => 'NHibernate C# DO',
self::HANDLER_NHIBERNATE_XML => 'NHibernate XML',
]);
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
$format = (int) $GLOBALS['codegen_format'];
if ($format === self::HANDLER_NHIBERNATE_CS) {
return $this->export->outputHandler($this->handleNHibernateCSBody($db, $table, $crlf, $aliases));
}
if ($format === self::HANDLER_NHIBERNATE_XML) {
return $this->export->outputHandler($this->handleNHibernateXMLBody($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 = (string) 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 = [])
{
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$result = $dbi->query(
sprintf(
'DESC %s.%s',
Util::backquote($db),
Util::backquote($table)
)
);
/** @var TableProperty[] $tableProperties */
$tableProperties = [];
while ($row = $result->fetchRow()) {
$col_as = $this->getAlias($aliases, $row[0], 'col', $db, $table);
if (! empty($col_as)) {
$row[0] = $col_as;
}
$tableProperties[] = new TableProperty($row);
}
unset($result);
$lines = [];
$lines[] = 'using System;';
$lines[] = 'using System.Collections;';
$lines[] = 'using System.Collections.Generic;';
$lines[] = 'using System.Text;';
$lines[] = 'namespace ' . self::cgMakeIdentifier($db_alias);
$lines[] = '{';
$lines[] = ' #region '
. self::cgMakeIdentifier($table_alias);
$lines[] = ' public class '
. self::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 '
. self::cgMakeIdentifier($table_alias) . '() { }';
$temp = [];
foreach ($tableProperties as $tableProperty) {
if ($tableProperty->isPK()) {
continue;
}
$temp[] = $tableProperty->formatCs('#dotNetPrimitiveType# #name#');
}
$lines[] = ' public '
. self::cgMakeIdentifier($table_alias)
. '('
. implode(', ', $temp)
. ')';
$lines[] = ' {';
foreach ($tableProperties as $tableProperty) {
if ($tableProperty->isPK()) {
continue;
}
$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 = []
) {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$lines = [];
$lines[] = '<?xml version="1.0" encoding="utf-8" ?>';
$lines[] = '<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" '
. 'namespace="' . self::cgMakeIdentifier($db_alias) . '" '
. 'assembly="' . self::cgMakeIdentifier($db_alias) . '">';
$lines[] = ' <class '
. 'name="' . self::cgMakeIdentifier($table_alias) . '" '
. 'table="' . self::cgMakeIdentifier($table_alias) . '">';
$result = $dbi->query(
sprintf(
'DESC %s.%s',
Util::backquote($db),
Util::backquote($table)
)
);
while ($row = $result->fetchRow()) {
$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(
' <id name="#ucfirstName#" type="#dotNetObjectType#"'
. ' unsaved-value="0">' . "\n"
. ' <column name="#name#" sql-type="#type#"'
. ' not-null="#notNull#" unique="#unique#"'
. ' index="PRIMARY"/>' . "\n"
. ' <generator class="native" />' . "\n"
. ' </id>'
);
} else {
$lines[] = $tableProperty->formatXml(
' <property name="#ucfirstName#"'
. ' type="#dotNetObjectType#">' . "\n"
. ' <column name="#name#" sql-type="#type#"'
. ' not-null="#notNull#" #indexName#/>' . "\n"
. ' </property>'
);
}
}
$lines[] = ' </class>';
$lines[] = '</hibernate-mapping>';
return implode($crlf, $lines);
}
/**
* Getter for CodeGen formats
*
* @return array
*/
private function getCgFormats()
{
return $this->cgFormats;
}
/**
* Setter for CodeGen formats
*
* @param array $CG_FORMATS contains CodeGen Formats
*/
private function setCgFormats(array $CG_FORMATS): void
{
$this->cgFormats = $CG_FORMATS;
}
}

View file

@ -0,0 +1,334 @@
<?php
/**
* CSV export code
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function __;
use function mb_strtolower;
use function mb_substr;
use function preg_replace;
use function str_replace;
use function stripslashes;
use function trim;
/**
* Handles the export for the CSV format
*/
class ExportCsv extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'csv';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
global $what, $csv_terminated, $csv_separator, $csv_enclosed, $csv_escaped;
// 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'] = true;
}
} 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
);
}
$csv_separator = str_replace('\\t', "\011", $csv_separator);
}
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Alias of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $what, $csv_terminated, $csv_separator, $csv_enclosed, $csv_escaped, $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
// Gets the data from the database
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$fields_cnt = $result->numFields();
// If required, get fields name at the first line
if (isset($GLOBALS['csv_columns']) && $GLOBALS['csv_columns']) {
$schema_insert = '';
foreach ($result->getFieldNames() as $col_as) {
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;
}
$schema_insert = trim(mb_substr($schema_insert, 0, -1));
if (! $this->export->outputHandler($schema_insert . $csv_terminated)) {
return false;
}
}
// Format the data
while ($row = $result->fetchRow()) {
$schema_insert = '';
for ($j = 0; $j < $fields_cnt; $j++) {
if (! isset($row[$j])) {
$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) {
continue;
}
$schema_insert .= $csv_separator;
}
if (! $this->export->outputHandler($schema_insert . $csv_terminated)) {
return false;
}
}
return true;
}
/**
* Outputs result of raw query in CSV format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
}

View file

@ -0,0 +1,86 @@
<?php
/**
* Class for exporting CSV dumps of tables for excel
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function __;
/**
* Handles the export for the CSV-Excel format
*/
class ExportExcel extends ExportCsv
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'excel';
}
protected function setProperties(): ExportPluginProperties
{
$exportPluginProperties = new ExportPluginProperties();
$exportPluginProperties->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);
return $exportPluginProperties;
}
}

View file

@ -0,0 +1,641 @@
<?php
/**
* HTML-Word export code
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use function __;
use function htmlspecialchars;
use function in_array;
use function str_replace;
use function stripslashes;
/**
* Handles the export for the HTML-Word format
*/
class ExportHtmlword extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'htmlword';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
global $charset;
return $this->export->outputHandler(
'<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:word"
xmlns="http://www.w3.org/TR/REC-html40">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
. ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset='
. ($charset ?? 'utf-8') . '" />
</head>
<body>'
);
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return $this->export->outputHandler('</body></html>');
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
if (empty($dbAlias)) {
$dbAlias = $db;
}
return $this->export->outputHandler(
'<h1>' . __('Database') . ' ' . htmlspecialchars($dbAlias) . '</h1>'
);
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $what, $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
if (
! $this->export->outputHandler(
'<h2>'
. __('Dumping data for table') . ' ' . htmlspecialchars($table_alias)
. '</h2>'
)
) {
return false;
}
if (! $this->export->outputHandler('<table width="100%" cellspacing="1">')) {
return false;
}
// Gets the data from the database
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$fields_cnt = $result->numFields();
// If required, get fields name at the first line
if (isset($GLOBALS['htmlword_columns'])) {
$schema_insert = '<tr class="print-category">';
foreach ($result->getFieldNames() as $col_as) {
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 .= '<td class="print"><strong>'
. htmlspecialchars($col_as)
. '</strong></td>';
}
$schema_insert .= '</tr>';
if (! $this->export->outputHandler($schema_insert)) {
return false;
}
}
// Format the data
while ($row = $result->fetchRow()) {
$schema_insert = '<tr class="print-category">';
for ($j = 0; $j < $fields_cnt; $j++) {
if (! isset($row[$j])) {
$value = $GLOBALS[$what . '_null'];
} elseif ($row[$j] == '0' || $row[$j] != '') {
$value = $row[$j];
} else {
$value = '';
}
$schema_insert .= '<td class="print">'
. htmlspecialchars((string) $value)
. '</td>';
}
$schema_insert .= '</tr>';
if (! $this->export->outputHandler($schema_insert)) {
return false;
}
}
return $this->export->outputHandler('</table>');
}
/**
* 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 = [])
{
global $dbi;
$schema_insert = '<table width="100%" cellspacing="1">'
. '<tr class="print-category">'
. '<th class="print">'
. __('Column')
. '</th>'
. '<td class="print"><strong>'
. __('Type')
. '</strong></td>'
. '<td class="print"><strong>'
. __('Null')
. '</strong></td>'
. '<td class="print"><strong>'
. __('Default')
. '</strong></td>'
. '</tr>';
/**
* Get the unique keys in the view
*/
$unique_keys = [];
$keys = $dbi->getTableIndexes($db, $view);
foreach ($keys as $key) {
if ($key['Non_unique'] != 0) {
continue;
}
$unique_keys[] = $key['Column_name'];
}
$columns = $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 .= '</tr>';
}
$schema_insert .= '</table>';
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 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 = []
) {
global $dbi;
$relationParameters = $this->relation->getRelationParameters();
$schema_insert = '';
/**
* Gets fields properties
*/
$dbi->selectDb($db);
// Check if we can use Relations
[$res_rel, $have_rel] = $this->relation->getRelationsAndStatus(
$do_relation && $relationParameters->relationFeature !== null,
$db,
$table
);
/**
* Displays the table structure
*/
$schema_insert .= '<table width="100%" cellspacing="1">';
$schema_insert .= '<tr class="print-category">';
$schema_insert .= '<th class="print">'
. __('Column')
. '</th>';
$schema_insert .= '<td class="print"><strong>'
. __('Type')
. '</strong></td>';
$schema_insert .= '<td class="print"><strong>'
. __('Null')
. '</strong></td>';
$schema_insert .= '<td class="print"><strong>'
. __('Default')
. '</strong></td>';
if ($do_relation && $have_rel) {
$schema_insert .= '<td class="print"><strong>'
. __('Links to')
. '</strong></td>';
}
if ($do_comments) {
$schema_insert .= '<td class="print"><strong>'
. __('Comments')
. '</strong></td>';
$comments = $this->relation->getComments($db, $table);
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$schema_insert .= '<td class="print"><strong>'
. __('Media type')
. '</strong></td>';
$mime_map = $this->transformations->getMime($db, $table, true);
}
$schema_insert .= '</tr>';
$columns = $dbi->getColumns($db, $table);
/**
* Get the unique keys in the table
*/
$unique_keys = [];
$keys = $dbi->getTableIndexes($db, $table);
foreach ($keys as $key) {
if ($key['Non_unique'] != 0) {
continue;
}
$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 .= '<td class="print">'
. htmlspecialchars(
$this->getRelationString(
$res_rel,
$field_name,
$db,
$aliases
)
)
. '</td>';
}
if ($do_comments && $relationParameters->columnCommentsFeature !== null) {
$schema_insert .= '<td class="print">'
. (isset($comments[$field_name])
? htmlspecialchars($comments[$field_name])
: '') . '</td>';
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$schema_insert .= '<td class="print">'
. (isset($mime_map[$field_name]) ?
htmlspecialchars(
str_replace('_', '/', $mime_map[$field_name]['mimetype'])
)
: '') . '</td>';
}
$schema_insert .= '</tr>';
}
$schema_insert .= '</table>';
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)
{
global $dbi;
$dump = '<table width="100%" cellspacing="1">';
$dump .= '<tr class="print-category">';
$dump .= '<th class="print">' . __('Name') . '</th>';
$dump .= '<td class="print"><strong>' . __('Time') . '</strong></td>';
$dump .= '<td class="print"><strong>' . __('Event') . '</strong></td>';
$dump .= '<td class="print"><strong>' . __('Definition') . '</strong></td>';
$dump .= '</tr>';
$triggers = $dbi->getTriggers($db, $table);
foreach ($triggers as $trigger) {
$dump .= '<tr class="print-category">';
$dump .= '<td class="print">'
. htmlspecialchars($trigger['name'])
. '</td>'
. '<td class="print">'
. htmlspecialchars($trigger['action_timing'])
. '</td>'
. '<td class="print">'
. htmlspecialchars($trigger['event_manipulation'])
. '</td>'
. '<td class="print">'
. htmlspecialchars($trigger['definition'])
. '</td>'
. '</tr>';
}
$dump .= '</table>';
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 $errorUrl the url to go back in case of error
* @param string $exportMode 'create_table', 'triggers', 'create_view',
* 'stand_in'
* @param string $exportType '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 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
*/
public function exportStructure(
$db,
$table,
$crlf,
$errorUrl,
$exportMode,
$exportType,
$do_relation = false,
$do_comments = false,
$do_mime = false,
$dates = false,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$dump = '';
switch ($exportMode) {
case 'create_table':
$dump .= '<h2>'
. __('Table structure for table') . ' '
. htmlspecialchars($table_alias)
. '</h2>';
$dump .= $this->getTableDef($db, $table, $do_relation, $do_comments, $do_mime, false, $aliases);
break;
case 'triggers':
$dump = '';
$triggers = $dbi->getTriggers($db, $table);
if ($triggers) {
$dump .= '<h2>'
. __('Triggers') . ' ' . htmlspecialchars($table_alias)
. '</h2>';
$dump .= $this->getTriggers($db, $table);
}
break;
case 'create_view':
$dump .= '<h2>'
. __('Structure for view') . ' ' . htmlspecialchars($table_alias)
. '</h2>';
$dump .= $this->getTableDef($db, $table, $do_relation, $do_comments, $do_mime, true, $aliases);
break;
case 'stand_in':
$dump .= '<h2>'
. __('Stand-in structure for view') . ' '
. htmlspecialchars($table_alias)
. '</h2>';
// export a stand-in definition to resolve view dependencies
$dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases);
}
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 = '<tr class="print-category">';
$extracted_columnspec = Util::extractColumnSpec($column['Type']);
$type = htmlspecialchars($extracted_columnspec['print_type']);
if (empty($type)) {
$type = '&nbsp;';
}
if (! isset($column['Default'])) {
if ($column['Null'] !== 'NO') {
$column['Default'] = 'NULL';
}
}
$fmt_pre = '';
$fmt_post = '';
if (in_array($column['Field'], $unique_keys)) {
$fmt_pre = '<strong>' . $fmt_pre;
$fmt_post .= '</strong>';
}
if ($column['Key'] === 'PRI') {
$fmt_pre = '<em>' . $fmt_pre;
$fmt_post .= '</em>';
}
$definition .= '<td class="print">' . $fmt_pre
. htmlspecialchars($col_alias) . $fmt_post . '</td>';
$definition .= '<td class="print">' . htmlspecialchars($type) . '</td>';
$definition .= '<td class="print">'
. ($column['Null'] == '' || $column['Null'] === 'NO'
? __('No')
: __('Yes'))
. '</td>';
$definition .= '<td class="print">'
. htmlspecialchars($column['Default'] ?? '')
. '</td>';
return $definition;
}
}

View file

@ -0,0 +1,346 @@
<?php
/**
* Set of methods used to build dumps of tables as JSON
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Version;
use function __;
use function bin2hex;
use function explode;
use function json_encode;
use function stripslashes;
use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_UNICODE;
/**
* Handles the export for the JSON format
*/
class ExportJson extends ExportPlugin
{
/** @var bool */
private $first = true;
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'json';
}
/**
* Encodes the data into JSON
*
* @param mixed $data Data to encode
*
* @return string|false
*/
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);
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
global $crlf;
$data = $this->encode([
'type' => 'header',
'version' => Version::VERSION,
'comment' => 'Export to JSON plugin for PHPMyAdmin',
]);
if ($data === false) {
return false;
}
return $this->export->outputHandler('[' . $crlf . $data . ',' . $crlf);
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
global $crlf;
return $this->export->outputHandler(']' . $crlf);
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
global $crlf;
if (empty($dbAlias)) {
$dbAlias = $db;
}
$data = $this->encode(['type' => 'database', 'name' => $dbAlias]);
if ($data === false) {
return false;
}
return $this->export->outputHandler($data . ',' . $crlf);
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $dbi;
$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@@',
]);
if ($buffer === false) {
return false;
}
return $this->doExportForQuery($dbi, $sqlQuery, $buffer, $crlf, $aliases, $db, $table);
}
/**
* Export to JSON
*
* @phpstan-param array{
* string: array{
* 'tables': array{
* string: array{
* 'columns': array{string: string}
* }
* }
* }
* }|array|null $aliases
*/
protected function doExportForQuery(
DatabaseInterface $dbi,
string $sqlQuery,
string $buffer,
string $crlf,
?array $aliases,
?string $db,
?string $table
): bool {
[$header, $footer] = explode('"@@DATA@@"', $buffer);
if (! $this->export->outputHandler($header . $crlf . '[' . $crlf)) {
return false;
}
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$columns_cnt = $result->numFields();
$fieldsMeta = $dbi->getFieldsMeta($result);
$columns = [];
foreach ($fieldsMeta as $i => $field) {
$col_as = $field->name;
if (
$db !== null && $table !== null && $aliases !== null
&& ! 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 = $result->fetchRow()) {
$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++) {
// 63 is the binary charset, see: https://dev.mysql.com/doc/internals/en/charsets.html
$isBlobAndIsBinaryCharset = isset($fieldsMeta[$i])
&& $fieldsMeta[$i]->isType(FieldMetadata::TYPE_BLOB)
&& $fieldsMeta[$i]->charsetnr === 63;
// This can occur for binary fields
$isBinaryString = isset($fieldsMeta[$i])
&& $fieldsMeta[$i]->isType(FieldMetadata::TYPE_STRING)
&& $fieldsMeta[$i]->charsetnr === 63;
if (
isset($fieldsMeta[$i]) &&
(
$fieldsMeta[$i]->isMappedTypeGeometry ||
$isBlobAndIsBinaryCharset ||
$isBinaryString
) &&
$record[$i] !== null
) {
// export GIS and blob 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;
}
}
return $this->export->outputHandler($crlf . ']' . $crlf . $footer . $crlf);
}
/**
* Outputs result raw query in JSON format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
$buffer = $this->encode([
'type' => 'raw',
'data' => '@@DATA@@',
]);
if ($buffer === false) {
return false;
}
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->doExportForQuery($dbi, $sqlQuery, $buffer, $crlf, null, $db, null);
}
}

View file

@ -0,0 +1,712 @@
<?php
/**
* Set of methods used to build dumps of tables as Latex
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use PhpMyAdmin\Version;
use function __;
use function count;
use function in_array;
use function mb_strpos;
use function mb_substr;
use function str_replace;
use function stripslashes;
use const PHP_VERSION;
/**
* Handles the export for the Latex format
*/
class ExportLatex extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'latex';
}
/**
* Initialize the local variables that are used for export Latex.
*/
protected function init(): void
{
/* Messages used in default captions */
$GLOBALS['strLatexContent'] = __('Content of table @TABLE@');
$GLOBALS['strLatexContinued'] = __('(continued)');
$GLOBALS['strLatexStructure'] = __('Structure of table @TABLE@');
}
protected function setProperties(): ExportPluginProperties
{
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);
$relationParameters = $this->relation->getRelationParameters();
if ($relationParameters->relationFeature !== null) {
$leaf = new BoolPropertyItem(
'relation',
__('Display foreign key relationships')
);
$structureOptions->addProperty($leaf);
}
$leaf = new BoolPropertyItem(
'comments',
__('Display comments')
);
$structureOptions->addProperty($leaf);
if ($relationParameters->browserTransformationFeature !== null) {
$leaf = new BoolPropertyItem(
'mime',
__('Display media 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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
global $crlf, $cfg, $dbi;
$head = '% phpMyAdmin LaTeX Dump' . $crlf
. '% version ' . Version::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:') . ' ' . $dbi->getVersionString() . $crlf
. '% ' . __('PHP Version:') . ' ' . PHP_VERSION . $crlf;
return $this->export->outputHandler($head);
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
if (empty($dbAlias)) {
$dbAlias = $db;
}
global $crlf;
$head = '% ' . $crlf
. '% ' . __('Database:') . ' \'' . $dbAlias . '\'' . $crlf
. '% ' . $crlf;
return $this->export->outputHandler($head);
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$result = $dbi->tryQuery($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$columns_cnt = $result->numFields();
$columns = [];
$columns_alias = [];
foreach ($result->getFieldNames() as $i => $col_as) {
$columns[$i] = $col_as;
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 = $result->fetchAssoc()) {
$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;
return $this->export->outputHandler($buffer);
}
/**
* Outputs result raw query
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
/**
* Outputs table's structure
*
* @param string $db database name
* @param string $table table name
* @param string $crlf the end of line sequence
* @param string $errorUrl the url to go back in case of error
* @param string $exportMode 'create_table', 'triggers', 'create_view',
* 'stand_in'
* @param string $exportType '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 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
*/
public function exportStructure(
$db,
$table,
$crlf,
$errorUrl,
$exportMode,
$exportType,
$do_relation = false,
$do_comments = false,
$do_mime = false,
$dates = false,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$relationParameters = $this->relation->getRelationParameters();
/* We do not export triggers */
if ($exportMode === 'triggers') {
return true;
}
/**
* Get the unique keys in the table
*/
$unique_keys = [];
$keys = $dbi->getTableIndexes($db, $table);
foreach ($keys as $key) {
if ($key['Non_unique'] != 0) {
continue;
}
$unique_keys[] = $key['Column_name'];
}
/**
* Gets fields properties
*/
$dbi->selectDb($db);
// Check if we can use Relations
[$res_rel, $have_rel] = $this->relation->getRelationsAndStatus(
$do_relation && $relationParameters->relationFeature !== null,
$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 && $relationParameters->browserTransformationFeature !== null) {
$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 && $relationParameters->browserTransformationFeature !== null) {
$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 = $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" . ($row['Default'] ?? '');
if ($do_relation && $have_rel) {
$local_buffer .= "\000";
$local_buffer .= $this->getRelationString($res_rel, $field_name, $db, $aliases);
}
if ($do_comments && $relationParameters->columnCommentsFeature !== null) {
$local_buffer .= "\000";
if (isset($comments[$field_name])) {
$local_buffer .= $comments[$field_name];
}
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$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 = (int) 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 = (int) 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;
}
}
$buffer = ' \\end{longtable}' . $crlf;
return $this->export->outputHandler($buffer);
}
/**
* 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;
}
}

View file

@ -0,0 +1,383 @@
<?php
/**
* Set of functions used to build MediaWiki dumps of tables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use function __;
use function array_values;
use function count;
use function htmlspecialchars;
use function str_repeat;
/**
* Handles the export for the MediaWiki class
*/
class ExportMediawiki extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'mediawiki';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Alias of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $exportMode 'create_table','triggers','create_view',
* 'stand_in'
* @param string $exportType '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 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
*/
public function exportStructure(
$db,
$table,
$crlf,
$errorUrl,
$exportMode,
$exportType,
$do_relation = false,
$do_comments = false,
$do_mime = false,
$dates = false,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$output = '';
switch ($exportMode) {
case 'create_table':
$columns = $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;
}
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
// Print data comment
$output = $this->exportComment(
$table_alias != ''
? 'Table data for ' . Util::backquote($table_alias)
: 'Query results'
);
// 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 = $dbi->getColumnNames($db, $table);
// Add column names as table headers
if ($column_names !== []) {
// 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 = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$fields_cnt = $result->numFields();
while ($row = $result->fetchRow()) {
$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 result raw query in MediaWiki format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
/**
* 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 .= '<!--' . $this->exportCRLF();
$comment .= htmlspecialchars($text) . $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";
}
}

View file

@ -0,0 +1,329 @@
<?php
/**
* Set of functions used to build OpenDocument Spreadsheet dumps of tables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\OpenDocument;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function __;
use function bin2hex;
use function date;
use function htmlspecialchars;
use function stripslashes;
use function strtotime;
/**
* Handles the export for the ODS class
*/
class ExportOds extends ExportPlugin
{
protected function init(): void
{
$GLOBALS['ods_buffer'] = '';
}
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'ods';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
$GLOBALS['ods_buffer'] .= '<?xml version="1.0" encoding="utf-8"?' . '>'
. '<office:document-content '
. OpenDocument::NS . ' office:version="1.0">'
. '<office:automatic-styles>'
. '<number:date-style style:name="N37"'
. ' number:automatic-order="true">'
. '<number:month number:style="long"/>'
. '<number:text>/</number:text>'
. '<number:day number:style="long"/>'
. '<number:text>/</number:text>'
. '<number:year/>'
. '</number:date-style>'
. '<number:time-style style:name="N43">'
. '<number:hours number:style="long"/>'
. '<number:text>:</number:text>'
. '<number:minutes number:style="long"/>'
. '<number:text>:</number:text>'
. '<number:seconds number:style="long"/>'
. '<number:text> </number:text>'
. '<number:am-pm/>'
. '</number:time-style>'
. '<number:date-style style:name="N50"'
. ' number:automatic-order="true"'
. ' number:format-source="language">'
. '<number:month/>'
. '<number:text>/</number:text>'
. '<number:day/>'
. '<number:text>/</number:text>'
. '<number:year/>'
. '<number:text> </number:text>'
. '<number:hours number:style="long"/>'
. '<number:text>:</number:text>'
. '<number:minutes number:style="long"/>'
. '<number:text> </number:text>'
. '<number:am-pm/>'
. '</number:date-style>'
. '<style:style style:name="DateCell" style:family="table-cell"'
. ' style:parent-style-name="Default" style:data-style-name="N37"/>'
. '<style:style style:name="TimeCell" style:family="table-cell"'
. ' style:parent-style-name="Default" style:data-style-name="N43"/>'
. '<style:style style:name="DateTimeCell" style:family="table-cell"'
. ' style:parent-style-name="Default" style:data-style-name="N50"/>'
. '</office:automatic-styles>'
. '<office:body>'
. '<office:spreadsheet>';
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
$GLOBALS['ods_buffer'] .= '</office:spreadsheet></office:body></office:document-content>';
return $this->export->outputHandler(
OpenDocument::create(
'application/vnd.oasis.opendocument.spreadsheet',
$GLOBALS['ods_buffer']
)
);
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $what, $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
// Gets the data from the database
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$fields_cnt = $result->numFields();
/** @var FieldMetadata[] $fieldsMeta */
$fieldsMeta = $dbi->getFieldsMeta($result);
$GLOBALS['ods_buffer'] .= '<table:table table:name="' . htmlspecialchars($table_alias) . '">';
// If required, get fields name at the first line
if (isset($GLOBALS[$what . '_columns'])) {
$GLOBALS['ods_buffer'] .= '<table:table-row>';
foreach ($fieldsMeta as $field) {
$col_as = $field->name;
if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
$col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
}
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars(
stripslashes($col_as)
)
. '</text:p>'
. '</table:table-cell>';
}
$GLOBALS['ods_buffer'] .= '</table:table-row>';
}
// Format the data
while ($row = $result->fetchRow()) {
$GLOBALS['ods_buffer'] .= '<table:table-row>';
for ($j = 0; $j < $fields_cnt; $j++) {
if ($fieldsMeta[$j]->isMappedTypeGeometry) {
// export GIS types as hex
$row[$j] = '0x' . bin2hex($row[$j]);
}
if (! isset($row[$j])) {
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($GLOBALS[$what . '_null'])
. '</text:p>'
. '</table:table-cell>';
} elseif ($fieldsMeta[$j]->isBinary && $fieldsMeta[$j]->isBlob) {
// ignore BLOB
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p></text:p>'
. '</table:table-cell>';
} elseif ($fieldsMeta[$j]->isType(FieldMetadata::TYPE_DATE)) {
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="date"'
. ' office:date-value="'
. date('Y-m-d', strtotime($row[$j]))
. '" table:style-name="DateCell">'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
} elseif ($fieldsMeta[$j]->isType(FieldMetadata::TYPE_TIME)) {
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="time"'
. ' office:time-value="'
. date('\P\TH\Hi\Ms\S', strtotime($row[$j]))
. '" table:style-name="TimeCell">'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
} elseif ($fieldsMeta[$j]->isType(FieldMetadata::TYPE_DATETIME)) {
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="date"'
. ' office:date-value="'
. date('Y-m-d\TH:i:s', strtotime($row[$j]))
. '" table:style-name="DateTimeCell">'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
} elseif (
$fieldsMeta[$j]->isNumeric
) {
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="float"'
. ' office:value="' . $row[$j] . '" >'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
} else {
$GLOBALS['ods_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
}
}
$GLOBALS['ods_buffer'] .= '</table:table-row>';
}
$GLOBALS['ods_buffer'] .= '</table:table>';
return true;
}
/**
* Outputs result raw query in ODS format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
}

View file

@ -0,0 +1,797 @@
<?php
/**
* Set of functions used to build OpenDocument Text dumps of tables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\OpenDocument;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use function __;
use function bin2hex;
use function htmlspecialchars;
use function str_replace;
use function stripslashes;
/**
* Handles the export for the ODT class
*/
class ExportOdt extends ExportPlugin
{
protected function init(): void
{
$GLOBALS['odt_buffer'] = '';
}
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'odt';
}
protected function setProperties(): ExportPluginProperties
{
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');
$relationParameters = $this->relation->getRelationParameters();
// create primary items and add them to the group
if ($relationParameters->relationFeature !== null) {
$leaf = new BoolPropertyItem(
'relation',
__('Display foreign key relationships')
);
$structureOptions->addProperty($leaf);
}
$leaf = new BoolPropertyItem(
'comments',
__('Display comments')
);
$structureOptions->addProperty($leaf);
if ($relationParameters->browserTransformationFeature !== null) {
$leaf = new BoolPropertyItem(
'mime',
__('Display media 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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
$GLOBALS['odt_buffer'] .= '<?xml version="1.0" encoding="utf-8"?' . '>'
. '<office:document-content '
. OpenDocument::NS . ' office:version="1.0">'
. '<office:body>'
. '<office:text>';
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
$GLOBALS['odt_buffer'] .= '</office:text></office:body></office:document-content>';
return $this->export->outputHandler(OpenDocument::create(
'application/vnd.oasis.opendocument.text',
$GLOBALS['odt_buffer']
));
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
if (empty($dbAlias)) {
$dbAlias = $db;
}
$GLOBALS['odt_buffer'] .= '<text:h text:outline-level="1" text:style-name="Heading_1"'
. ' text:is-list-header="true">'
. __('Database') . ' ' . htmlspecialchars($dbAlias)
. '</text:h>';
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $what, $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
// Gets the data from the database
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$fields_cnt = $result->numFields();
/** @var FieldMetadata[] $fieldsMeta */
$fieldsMeta = $dbi->getFieldsMeta($result);
$GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
. ' text:is-list-header="true">';
$table_alias != ''
? $GLOBALS['odt_buffer'] .= __('Dumping data for table') . ' ' . htmlspecialchars($table_alias)
: $GLOBALS['odt_buffer'] .= __('Dumping data for query result');
$GLOBALS['odt_buffer'] .= '</text:h>'
. '<table:table'
. ' table:name="' . htmlspecialchars($table_alias) . '_structure">'
. '<table:table-column'
. ' table:number-columns-repeated="' . $fields_cnt . '"/>';
// If required, get fields name at the first line
if (isset($GLOBALS[$what . '_columns'])) {
$GLOBALS['odt_buffer'] .= '<table:table-row>';
foreach ($fieldsMeta as $field) {
$col_as = $field->name;
if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
$col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
}
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars(
stripslashes($col_as)
)
. '</text:p>'
. '</table:table-cell>';
}
$GLOBALS['odt_buffer'] .= '</table:table-row>';
}
// Format the data
while ($row = $result->fetchRow()) {
$GLOBALS['odt_buffer'] .= '<table:table-row>';
for ($j = 0; $j < $fields_cnt; $j++) {
if ($fieldsMeta[$j]->isMappedTypeGeometry) {
// export GIS types as hex
$row[$j] = '0x' . bin2hex($row[$j]);
}
if (! isset($row[$j])) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($GLOBALS[$what . '_null'])
. '</text:p>'
. '</table:table-cell>';
} elseif ($fieldsMeta[$j]->isBinary && $fieldsMeta[$j]->isBlob) {
// ignore BLOB
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p></text:p>'
. '</table:table-cell>';
} elseif (
$fieldsMeta[$j]->isNumeric
) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="float"'
. ' office:value="' . $row[$j] . '" >'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
} else {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($row[$j])
. '</text:p>'
. '</table:table-cell>';
}
}
$GLOBALS['odt_buffer'] .= '</table:table-row>';
}
$GLOBALS['odt_buffer'] .= '</table:table>';
return true;
}
/**
* Outputs result raw query in ODT format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
/**
* 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 = [])
{
global $dbi;
$db_alias = $db;
$view_alias = $view;
$this->initAlias($aliases, $db_alias, $view_alias);
/**
* Gets fields properties
*/
$dbi->selectDb($db);
/**
* Displays the table structure
*/
$GLOBALS['odt_buffer'] .= '<table:table table:name="'
. htmlspecialchars($view_alias) . '_data">';
$columns_cnt = 4;
$GLOBALS['odt_buffer'] .= '<table:table-column'
. ' table:number-columns-repeated="' . $columns_cnt . '"/>';
/* Header */
$GLOBALS['odt_buffer'] .= '<table:table-row>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Column') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Type') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Null') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Default') . '</text:p>'
. '</table:table-cell>'
. '</table:table-row>';
$columns = $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'] .= '</table:table-row>';
}
$GLOBALS['odt_buffer'] .= '</table:table>';
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 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
*/
public function getTableDef(
$db,
$table,
$crlf,
$error_url,
$do_relation,
$do_comments,
$do_mime,
$show_dates = false,
$add_semicolon = true,
$view = false,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$relationParameters = $this->relation->getRelationParameters();
/**
* Gets fields properties
*/
$dbi->selectDb($db);
// Check if we can use Relations
[$res_rel, $have_rel] = $this->relation->getRelationsAndStatus(
$do_relation && $relationParameters->relationFeature !== null,
$db,
$table
);
/**
* Displays the table structure
*/
$GLOBALS['odt_buffer'] .= '<table:table table:name="'
. htmlspecialchars($table_alias) . '_structure">';
$columns_cnt = 4;
if ($do_relation && $have_rel) {
$columns_cnt++;
}
if ($do_comments) {
$columns_cnt++;
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$columns_cnt++;
}
$GLOBALS['odt_buffer'] .= '<table:table-column'
. ' table:number-columns-repeated="' . $columns_cnt . '"/>';
/* Header */
$GLOBALS['odt_buffer'] .= '<table:table-row>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Column') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Type') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Null') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Default') . '</text:p>'
. '</table:table-cell>';
if ($do_relation && $have_rel) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Links to') . '</text:p>'
. '</table:table-cell>';
}
if ($do_comments) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Comments') . '</text:p>'
. '</table:table-cell>';
$comments = $this->relation->getComments($db, $table);
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Media type') . '</text:p>'
. '</table:table-cell>';
$mime_map = $this->transformations->getMime($db, $table, true);
}
$GLOBALS['odt_buffer'] .= '</table:table-row>';
$columns = $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'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($relation)
. '</text:p>'
. '</table:table-cell>';
}
}
if ($do_comments) {
if (isset($comments[$field_name])) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($comments[$field_name])
. '</text:p>'
. '</table:table-cell>';
} else {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p></text:p>'
. '</table:table-cell>';
}
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
if (isset($mime_map[$field_name])) {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars(
str_replace('_', '/', $mime_map[$field_name]['mimetype'])
)
. '</text:p>'
. '</table:table-cell>';
} else {
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p></text:p>'
. '</table:table-cell>';
}
}
$GLOBALS['odt_buffer'] .= '</table:table-row>';
}
$GLOBALS['odt_buffer'] .= '</table:table>';
return true;
}
/**
* Outputs triggers
*
* @param string $db database name
* @param string $table table name
* @param array $aliases Aliases of db/table/columns
*
* @return string
*/
protected function getTriggers($db, $table, array $aliases = [])
{
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$GLOBALS['odt_buffer'] .= '<table:table'
. ' table:name="' . htmlspecialchars($table_alias) . '_triggers">'
. '<table:table-column'
. ' table:number-columns-repeated="4"/>'
. '<table:table-row>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Name') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Time') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Event') . '</text:p>'
. '</table:table-cell>'
. '<table:table-cell office:value-type="string">'
. '<text:p>' . __('Definition') . '</text:p>'
. '</table:table-cell>'
. '</table:table-row>';
$triggers = $dbi->getTriggers($db, $table);
foreach ($triggers as $trigger) {
$GLOBALS['odt_buffer'] .= '<table:table-row>';
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($trigger['name'])
. '</text:p>'
. '</table:table-cell>';
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($trigger['action_timing'])
. '</text:p>'
. '</table:table-cell>';
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($trigger['event_manipulation'])
. '</text:p>'
. '</table:table-cell>';
$GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. htmlspecialchars($trigger['definition'])
. '</text:p>'
. '</table:table-cell>';
$GLOBALS['odt_buffer'] .= '</table:table-row>';
}
$GLOBALS['odt_buffer'] .= '</table:table>';
return $GLOBALS['odt_buffer'];
}
/**
* Outputs table's structure
*
* @param string $db database name
* @param string $table table name
* @param string $crlf the end of line sequence
* @param string $errorUrl the url to go back in case of error
* @param string $exportMode 'create_table', 'triggers', 'create_view',
* 'stand_in'
* @param string $exportType '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 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
*/
public function exportStructure(
$db,
$table,
$crlf,
$errorUrl,
$exportMode,
$exportType,
$do_relation = false,
$do_comments = false,
$do_mime = false,
$dates = false,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
switch ($exportMode) {
case 'create_table':
$GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
. ' text:is-list-header="true">'
. __('Table structure for table') . ' ' .
htmlspecialchars($table_alias)
. '</text:h>';
$this->getTableDef(
$db,
$table,
$crlf,
$errorUrl,
$do_relation,
$do_comments,
$do_mime,
$dates,
true,
false,
$aliases
);
break;
case 'triggers':
$triggers = $dbi->getTriggers($db, $table);
if ($triggers) {
$GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
. ' text:is-list-header="true">'
. __('Triggers') . ' '
. htmlspecialchars($table_alias)
. '</text:h>';
$this->getTriggers($db, $table);
}
break;
case 'create_view':
$GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
. ' text:is-list-header="true">'
. __('Structure for view') . ' '
. htmlspecialchars($table_alias)
. '</text:h>';
$this->getTableDef(
$db,
$table,
$crlf,
$errorUrl,
$do_relation,
$do_comments,
$do_mime,
$dates,
true,
true,
$aliases
);
break;
case 'stand_in':
$GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
. ' text:is-list-header="true">'
. __('Stand-in structure for view') . ' '
. htmlspecialchars($table_alias)
. '</text:h>';
// export a stand-in definition to resolve view dependencies
$this->getTableDefStandIn($db, $table, $crlf, $aliases);
}
return true;
}
/**
* 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 = '<table:table-row>';
$definition .= '<table:table-cell office:value-type="string">'
. '<text:p>' . htmlspecialchars($col_as) . '</text:p>'
. '</table:table-cell>';
$extracted_columnspec = Util::extractColumnSpec($column['Type']);
$type = htmlspecialchars($extracted_columnspec['print_type']);
if (empty($type)) {
$type = '&nbsp;';
}
$definition .= '<table:table-cell office:value-type="string">'
. '<text:p>' . htmlspecialchars($type) . '</text:p>'
. '</table:table-cell>';
if (! isset($column['Default'])) {
if ($column['Null'] !== 'NO') {
$column['Default'] = 'NULL';
} else {
$column['Default'] = '';
}
}
$definition .= '<table:table-cell office:value-type="string">'
. '<text:p>'
. ($column['Null'] == '' || $column['Null'] === 'NO'
? __('No')
: __('Yes'))
. '</text:p>'
. '</table:table-cell>';
$definition .= '<table:table-cell office:value-type="string">'
. '<text:p>' . htmlspecialchars($column['Default']) . '</text:p>'
. '</table:table-cell>';
return $definition;
}
}

View file

@ -0,0 +1,337 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Plugins\Export\Helpers\Pdf;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use TCPDF;
use function __;
use function class_exists;
/**
* Produce a PDF report (export) from a query
*/
class ExportPdf extends ExportPlugin
{
/**
* PhpMyAdmin\Plugins\Export\Helpers\Pdf instance
*
* @var Pdf
*/
private $pdf;
/**
* PDF Report Title
*
* @var string
*/
private $pdfReportTitle = '';
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'pdf';
}
/**
* Initialize the local variables that are used for export PDF.
*/
protected function init(): void
{
if (! empty($_POST['pdf_report_title'])) {
$this->pdfReportTitle = $_POST['pdf_report_title'];
}
$this->setPdf(new Pdf('L', 'pt', 'A3'));
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
$pdf = $this->getPdf();
$pdf->Open();
$pdf->setTitleFontSize(18);
$pdf->setTitleText($this->pdfReportTitle);
$pdf->setTopMargin(30);
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
$pdf = $this->getPdf();
// instead of $pdf->Output():
return $this->export->outputHandler($pdf->getPDFData());
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$pdf = $this->getPdf();
$pdf->setCurrentDb($db);
$pdf->setCurrentTable($table);
$pdf->setDbAlias($db_alias);
$pdf->setTableAlias($table_alias);
$pdf->setAliases($aliases);
$pdf->setPurpose(__('Dumping data'));
$pdf->mysqlReport($sqlQuery);
return true;
}
/**
* Outputs result of raw query in PDF format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
$pdf = $this->getPdf();
$pdf->setDbAlias('----');
$pdf->setTableAlias('----');
$pdf->setPurpose(__('Query result data'));
if ($db !== null) {
$pdf->setCurrentDb($db);
$dbi->selectDb($db);
}
$pdf->mysqlReport($sqlQuery);
return true;
}
/**
* Outputs table structure
*
* @param string $db database name
* @param string $table table name
* @param string $crlf the end of line sequence
* @param string $errorUrl the url to go back in case of error
* @param string $exportMode 'create_table', 'triggers', 'create_view',
* 'stand_in'
* @param string $exportType '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 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
*/
public function exportStructure(
$db,
$table,
$crlf,
$errorUrl,
$exportMode,
$exportType,
$do_relation = false,
$do_comments = false,
$do_mime = false,
$dates = false,
array $aliases = []
): bool {
$db_alias = $db;
$table_alias = $table;
$purpose = '';
$this->initAlias($aliases, $db_alias, $table_alias);
$pdf = $this->getPdf();
// getting purpose to show at top
switch ($exportMode) {
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');
}
$pdf->setCurrentDb($db);
$pdf->setCurrentTable($table);
$pdf->setDbAlias($db_alias);
$pdf->setTableAlias($table_alias);
$pdf->setAliases($aliases);
$pdf->setPurpose($purpose);
/**
* comment display set true as presently in pdf
* format, no option is present to take user input.
*/
$do_comments = true;
switch ($exportMode) {
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);
*/
}
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
*/
private function setPdf($pdf): void
{
$this->pdf = $pdf;
}
public static function isAvailable(): bool
{
return class_exists(TCPDF::class);
}
}

View file

@ -0,0 +1,256 @@
<?php
/**
* Set of functions used to build dumps of tables as PHP Arrays
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use PhpMyAdmin\Version;
use function __;
use function preg_match;
use function preg_replace;
use function stripslashes;
use function strtr;
use function var_export;
/**
* Handles the export for the PHP Array class
*/
class ExportPhparray extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'phparray';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $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
*/
public function exportHeader(): bool
{
$this->export->outputHandler(
'<?php' . $GLOBALS['crlf']
. '/**' . $GLOBALS['crlf']
. ' * Export to PHP Array plugin for PHPMyAdmin' . $GLOBALS['crlf']
. ' * @version ' . Version::VERSION . $GLOBALS['crlf']
. ' */' . $GLOBALS['crlf'] . $GLOBALS['crlf']
);
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
if (empty($dbAlias)) {
$dbAlias = $db;
}
$this->export->outputHandler(
'/**' . $GLOBALS['crlf']
. ' * Database ' . $this->commentString(Util::backquote($dbAlias))
. $GLOBALS['crlf'] . ' */' . $GLOBALS['crlf']
);
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$columns_cnt = $result->numFields();
$columns = [];
foreach ($result->getFieldNames() as $i => $col_as) {
if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
$col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
}
$columns[$i] = stripslashes($col_as);
}
$tablefixed = $table;
// 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;
}
}
$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(';
if (! $this->export->outputHandler($buffer)) {
return false;
}
// Reset the buffer
$buffer = '';
while ($record = $result->fetchRow()) {
$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 .= ')';
if (! $this->export->outputHandler($buffer)) {
return false;
}
// Reset the buffer
$buffer = '';
}
$buffer .= $crlf . ');' . $crlf;
return $this->export->outputHandler($buffer);
}
/**
* Outputs result of raw query as PHP array
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,632 @@
<?php
/**
* Export to Texy! text.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use function __;
use function htmlspecialchars;
use function in_array;
use function str_replace;
use function stripslashes;
/**
* Handles the export for the Texy! text class
*/
class ExportTexytext extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'texytext';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Alias of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
if (empty($dbAlias)) {
$dbAlias = $db;
}
return $this->export->outputHandler(
'===' . __('Database') . ' ' . $dbAlias . "\n\n"
);
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $what, $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
if (
! $this->export->outputHandler(
$table_alias != ''
? '== ' . __('Dumping data for table') . ' ' . $table_alias . "\n\n"
: '==' . __('Dumping data for query result') . "\n\n"
)
) {
return false;
}
// Gets the data from the database
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$fields_cnt = $result->numFields();
// If required, get fields name at the first line
if (isset($GLOBALS[$what . '_columns'])) {
$text_output = "|------\n";
foreach ($result->getFieldNames() as $col_as) {
if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
$col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
}
$text_output .= '|'
. htmlspecialchars(stripslashes($col_as));
}
$text_output .= "\n|------\n";
if (! $this->export->outputHandler($text_output)) {
return false;
}
}
// Format the data
while ($row = $result->fetchRow()) {
$text_output = '';
for ($j = 0; $j < $fields_cnt; $j++) {
if (! isset($row[$j])) {
$value = $GLOBALS[$what . '_null'];
} elseif ($row[$j] == '0' || $row[$j] != '') {
$value = $row[$j];
} else {
$value = ' ';
}
$text_output .= '|'
. str_replace(
'|',
'&#124;',
htmlspecialchars($value)
);
}
$text_output .= "\n";
if (! $this->export->outputHandler($text_output)) {
return false;
}
}
return true;
}
/**
* Outputs result raw query in TexyText format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
/**
* 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 = [])
{
global $dbi;
$text_output = '';
/**
* Get the unique keys in the table
*/
$unique_keys = [];
$keys = $dbi->getTableIndexes($db, $view);
foreach ($keys as $key) {
if ($key['Non_unique'] != 0) {
continue;
}
$unique_keys[] = $key['Column_name'];
}
/**
* Gets fields properties
*/
$dbi->selectDb($db);
/**
* Displays the table structure
*/
$text_output .= "|------\n"
. '|' . __('Column')
. '|' . __('Type')
. '|' . __('Null')
. '|' . __('Default')
. "\n|------\n";
$columns = $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";
}
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 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 $dbi;
$relationParameters = $this->relation->getRelationParameters();
$text_output = '';
/**
* Get the unique keys in the table
*/
$unique_keys = [];
$keys = $dbi->getTableIndexes($db, $table);
foreach ($keys as $key) {
if ($key['Non_unique'] != 0) {
continue;
}
$unique_keys[] = $key['Column_name'];
}
/**
* Gets fields properties
*/
$dbi->selectDb($db);
// Check if we can use Relations
[$res_rel, $have_rel] = $this->relation->getRelationsAndStatus(
$do_relation && $relationParameters->relationFeature !== null,
$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 && $relationParameters->browserTransformationFeature !== null) {
$text_output .= '|' . __('Media type');
$mime_map = $this->transformations->getMime($db, $table, true);
}
$text_output .= "\n|------\n";
$columns = $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 && $relationParameters->columnCommentsFeature !== null) {
$text_output .= '|'
. (isset($comments[$field_name])
? htmlspecialchars($comments[$field_name])
: '');
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$text_output .= '|'
. (isset($mime_map[$field_name])
? htmlspecialchars(
str_replace('_', '/', $mime_map[$field_name]['mimetype'])
)
: '');
}
$text_output .= "\n";
}
return $text_output;
}
/**
* Outputs triggers
*
* @param string $db database name
* @param string $table table name
*
* @return string Formatted triggers list
*/
public function getTriggers($db, $table)
{
global $dbi;
$dump = "|------\n";
$dump .= '|' . __('Name');
$dump .= '|' . __('Time');
$dump .= '|' . __('Event');
$dump .= '|' . __('Definition');
$dump .= "\n|------\n";
$triggers = $dbi->getTriggers($db, $table);
foreach ($triggers as $trigger) {
$dump .= '|' . $trigger['name'];
$dump .= '|' . $trigger['action_timing'];
$dump .= '|' . $trigger['event_manipulation'];
$dump .= '|' .
str_replace(
'|',
'&#124;',
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 $errorUrl the url to go back in case of error
* @param string $exportMode 'create_table', 'triggers', 'create_view',
* 'stand_in'
* @param string $exportType '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 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
*/
public function exportStructure(
$db,
$table,
$crlf,
$errorUrl,
$exportMode,
$exportType,
$do_relation = false,
$do_comments = false,
$do_mime = false,
$dates = false,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$dump = '';
switch ($exportMode) {
case 'create_table':
$dump .= '== ' . __('Table structure for table') . ' '
. $table_alias . "\n\n";
$dump .= $this->getTableDef(
$db,
$table,
$crlf,
$errorUrl,
$do_relation,
$do_comments,
$do_mime,
$dates,
true,
false,
$aliases
);
break;
case 'triggers':
$dump = '';
$triggers = $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,
$errorUrl,
$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);
}
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 = '&nbsp;';
}
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($column['Default'] ?? '');
return $definition;
}
}

View file

@ -0,0 +1,553 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Util;
use PhpMyAdmin\Version;
use function __;
use function count;
use function htmlspecialchars;
use function is_array;
use function mb_substr;
use function rtrim;
use function str_replace;
use function stripslashes;
use function strlen;
use const PHP_VERSION;
/**
* Used to build XML dumps of tables
*/
class ExportXml extends ExportPlugin
{
/**
* Table name
*
* @var string
*/
private $table;
/**
* Table names
*
* @var array
*/
private $tables = [];
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'xml';
}
/**
* Initialize the local variables that are used for export XML
*/
private function initSpecificVariables(): void
{
global $table, $tables;
$this->setTable($table);
if (! is_array($tables)) {
return;
}
$this->setTables($tables);
}
protected function setProperties(): ExportPluginProperties
{
// 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);
return $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 queries
*
* @return string XML with definitions
*/
private function exportRoutinesDefinition($db, $type, $dbitype)
{
global $dbi;
// Export routines
$routines = $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 queries
* @param array $names Names of items to export
*
* @return string XML with definitions
*/
private function exportDefinitions($db, $type, $dbitype, array $names)
{
global $crlf, $dbi;
$head = '';
if ($names) {
foreach ($names as $name) {
$head .= ' <pma:' . $type . ' name="'
. htmlspecialchars($name) . '">' . $crlf;
// Do some formatting
$sql = $dbi->getDefinition($db, $dbitype, $name);
$sql = htmlspecialchars(rtrim($sql));
$sql = str_replace("\n", "\n ", $sql);
$head .= ' ' . $sql . $crlf;
$head .= ' </pma:' . $type . '>' . $crlf;
}
}
return $head;
}
/**
* Outputs export header. It is the first method to be called, so all
* the required variables are initialized here.
*/
public function exportHeader(): bool
{
$this->initSpecificVariables();
global $crlf, $cfg, $db, $dbi;
$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']);
if ($GLOBALS['output_charset_conversion']) {
$charset = $GLOBALS['charset'];
} else {
$charset = 'utf-8';
}
$head = '<?xml version="1.0" encoding="' . $charset . '"?>' . $crlf
. '<!--' . $crlf
. '- phpMyAdmin XML Dump' . $crlf
. '- version ' . Version::VERSION . $crlf
. '- https://www.phpmyadmin.net' . $crlf
. '-' . $crlf
. '- ' . __('Host:') . ' ' . htmlspecialchars($cfg['Server']['host']);
if (! empty($cfg['Server']['port'])) {
$head .= ':' . $cfg['Server']['port'];
}
$head .= $crlf
. '- ' . __('Generation Time:') . ' '
. Util::localisedDate() . $crlf
. '- ' . __('Server version:') . ' ' . $dbi->getVersionString() . $crlf
. '- ' . __('PHP Version:') . ' ' . PHP_VERSION . $crlf
. '-->' . $crlf . $crlf;
$head .= '<pma_xml_export version="1.0"'
. ($export_struct
? ' xmlns:pma="https://www.phpmyadmin.net/some_doc_url/"'
: '')
. '>' . $crlf;
if ($export_struct) {
$result = $dbi->fetchResult(
'SELECT `DEFAULT_CHARACTER_SET_NAME`, `DEFAULT_COLLATION_NAME`'
. ' FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME`'
. ' = \'' . $dbi->escapeString($db) . '\' LIMIT 1'
);
$db_collation = $result[0]['DEFAULT_COLLATION_NAME'];
$db_charset = $result[0]['DEFAULT_CHARACTER_SET_NAME'];
$head .= ' <!--' . $crlf;
$head .= ' - Structure schemas' . $crlf;
$head .= ' -->' . $crlf;
$head .= ' <pma:structure_schemas>' . $crlf;
$head .= ' <pma:database name="' . htmlspecialchars($db)
. '" collation="' . htmlspecialchars($db_collation) . '" charset="' . htmlspecialchars($db_charset)
. '">' . $crlf;
if (count($tables) === 0) {
$tables[] = $table;
}
foreach ($tables as $table) {
// Export tables and views
$result = $dbi->fetchResult(
'SHOW CREATE TABLE ' . Util::backquote($db) . '.'
. Util::backquote($table),
0
);
$tbl = (string) $result[$table][1];
$is_view = $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 .= ' <pma:' . $type . ' name="' . htmlspecialchars($table) . '">'
. $crlf;
$tbl = ' ' . htmlspecialchars($tbl);
$tbl = str_replace("\n", "\n ", $tbl);
$head .= $tbl . ';' . $crlf;
$head .= ' </pma:' . $type . '>' . $crlf;
if (! isset($GLOBALS['xml_export_triggers']) || ! $GLOBALS['xml_export_triggers']) {
continue;
}
// Export triggers
$triggers = $dbi->getTriggers($db, $table);
if (! $triggers) {
continue;
}
foreach ($triggers as $trigger) {
$code = $trigger['create'];
$head .= ' <pma:trigger name="'
. htmlspecialchars($trigger['name']) . '">' . $crlf;
// Do some formatting
$code = mb_substr(rtrim($code), 0, -3);
$code = ' ' . htmlspecialchars($code);
$code = str_replace("\n", "\n ", $code);
$head .= $code . $crlf;
$head .= ' </pma:trigger>' . $crlf;
}
unset($trigger, $triggers);
}
if (isset($GLOBALS['xml_export_functions']) && $GLOBALS['xml_export_functions']) {
$head .= $this->exportRoutinesDefinition($db, 'function', 'FUNCTION');
}
if (isset($GLOBALS['xml_export_procedures']) && $GLOBALS['xml_export_procedures']) {
$head .= $this->exportRoutinesDefinition($db, 'procedure', 'PROCEDURE');
}
if (isset($GLOBALS['xml_export_events']) && $GLOBALS['xml_export_events']) {
// Export events
$events = $dbi->fetchResult(
'SELECT EVENT_NAME FROM information_schema.EVENTS '
. "WHERE EVENT_SCHEMA='" . $dbi->escapeString($db)
. "'"
);
$head .= $this->exportDefinitions($db, 'event', 'EVENT', $events);
}
unset($result);
$head .= ' </pma:database>' . $crlf;
$head .= ' </pma:structure_schemas>' . $crlf;
if ($export_data) {
$head .= $crlf;
}
}
return $this->export->outputHandler($head);
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
$foot = '</pma_xml_export>';
return $this->export->outputHandler($foot);
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
global $crlf;
if (empty($dbAlias)) {
$dbAlias = $db;
}
if (isset($GLOBALS['xml_export_contents']) && $GLOBALS['xml_export_contents']) {
$head = ' <!--' . $crlf
. ' - ' . __('Database:') . ' \''
. htmlspecialchars($dbAlias) . '\'' . $crlf
. ' -->' . $crlf . ' <database name="'
. htmlspecialchars($dbAlias) . '">' . $crlf;
return $this->export->outputHandler($head);
}
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
global $crlf;
if (isset($GLOBALS['xml_export_contents']) && $GLOBALS['xml_export_contents']) {
return $this->export->outputHandler(' </database>' . $crlf);
}
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $dbi;
// Do not export data for merge tables
if ($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 = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$columns_cnt = $result->numFields();
$columns = [];
foreach ($result->getFieldNames() as $column) {
$columns[] = stripslashes($column);
}
$buffer = ' <!-- ' . __('Table') . ' '
. htmlspecialchars($table_alias) . ' -->' . $crlf;
if (! $this->export->outputHandler($buffer)) {
return false;
}
while ($record = $result->fetchRow()) {
$buffer = ' <table name="'
. htmlspecialchars($table_alias) . '">' . $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';
}
$buffer .= ' <column name="'
. htmlspecialchars($col_as) . '">'
. htmlspecialchars((string) $record[$i])
. '</column>' . $crlf;
}
$buffer .= ' </table>' . $crlf;
if (! $this->export->outputHandler($buffer)) {
return false;
}
}
}
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
*/
private function setTable($table): void
{
$this->table = $table;
}
/**
* Gets the table names
*
* @return array
*/
private function getTables()
{
return $this->tables;
}
/**
* Sets the table names
*
* @param array $tables table names
*/
private function setTables(array $tables): void
{
$this->tables = $tables;
}
public static function isAvailable(): bool
{
global $db;
// Can't do server export.
return isset($db) && strlen($db) > 0;
}
}

View file

@ -0,0 +1,228 @@
<?php
/**
* Set of functions used to build YAML dumps of tables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function __;
use function array_key_exists;
use function is_numeric;
use function str_replace;
use function stripslashes;
/**
* Handles the export for the YAML format
*/
class ExportYaml extends ExportPlugin
{
/**
* @psalm-return non-empty-lowercase-string
*/
public function getName(): string
{
return 'yaml';
}
protected function setProperties(): ExportPluginProperties
{
$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);
return $exportPluginProperties;
}
/**
* Outputs export header
*/
public function exportHeader(): bool
{
$this->export->outputHandler('%YAML 1.1' . $GLOBALS['crlf'] . '---' . $GLOBALS['crlf']);
return true;
}
/**
* Outputs export footer
*/
public function exportFooter(): bool
{
$this->export->outputHandler('...' . $GLOBALS['crlf']);
return true;
}
/**
* Outputs database header
*
* @param string $db Database name
* @param string $dbAlias Aliases of db
*/
public function exportDBHeader($db, $dbAlias = ''): bool
{
return true;
}
/**
* Outputs database footer
*
* @param string $db Database name
*/
public function exportDBFooter($db): bool
{
return true;
}
/**
* Outputs CREATE DATABASE statement
*
* @param string $db Database name
* @param string $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*/
public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
{
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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
): bool {
global $dbi;
$db_alias = $db;
$table_alias = $table;
$this->initAlias($aliases, $db_alias, $table_alias);
$result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$columns_cnt = $result->numFields();
$fieldsMeta = $dbi->getFieldsMeta($result);
$columns = [];
foreach ($fieldsMeta as $i => $field) {
$col_as = $field->name;
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 = $result->fetchRow()) {
$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 (! array_key_exists($i, $record)) {
continue;
}
if ($record[$i] === null) {
$buffer .= ' ' . $columns[$i] . ': null' . $crlf;
continue;
}
$isNotString = isset($fieldsMeta[$i]) && $fieldsMeta[$i]->isNotType(FieldMetadata::TYPE_STRING);
if (is_numeric($record[$i]) && $isNotString) {
$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;
}
}
return true;
}
/**
* Outputs result raw query in YAML format
*
* @param string $errorUrl the url to go back in case of error
* @param string|null $db the database where the query is executed
* @param string $sqlQuery the rawquery to output
* @param string $crlf the end of line sequence
*/
public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
{
global $dbi;
if ($db !== null) {
$dbi->selectDb($db);
}
return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
}
}

View file

@ -0,0 +1,902 @@
<?php
/**
* PhpMyAdmin\Plugins\Export\Helpers\Pdf class
*/
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export\Helpers;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\ResultInterface;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\Pdf as PdfLib;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Util;
use TCPDF_STATIC;
use function __;
use function array_key_exists;
use function count;
use function ksort;
/**
* Adapted from a LGPL script by Philip Clarke
*/
class Pdf extends PdfLib
{
/** @var array */
public $tablewidths;
/** @var array */
public $headerset;
/** @var int|float */
private $dataY;
/** @var int|float */
private $cellFontSize;
/** @var int */
private $titleFontSize;
/** @var string */
private $titleText;
/** @var string */
private $dbAlias;
/** @var string */
private $tableAlias;
/** @var string */
private $purpose;
/** @var array */
private $colTitles;
/** @var ResultInterface */
private $results;
/** @var array */
private $colAlign;
/** @var mixed */
private $titleWidth;
/** @var mixed */
private $colFits;
/** @var array */
private $displayColumn;
/** @var int */
private $numFields;
/** @var FieldMetadata[] */
private $fields;
/** @var int|float */
private $sColWidth;
/** @var string */
private $currentDb;
/** @var string */
private $currentTable;
/** @var array */
private $aliases;
/** @var Relation */
private $relation;
/** @var Transformations */
private $transformations;
/**
* Constructs PDF and configures standard parameters.
*
* @param string $orientation page orientation
* @param string $unit unit
* @param string $format the format used for pages
* @param bool $unicode true means that the input text is unicode
* @param string $encoding charset encoding; default is UTF-8.
* @param bool $diskcache DEPRECATED TCPDF FEATURE
* @param false|int $pdfa If not false, set the document to PDF/A mode and the good version (1 or 3)
*/
public function __construct(
$orientation = 'P',
$unit = 'mm',
$format = 'A4',
$unicode = true,
$encoding = 'UTF-8',
$diskcache = false,
$pdfa = false
) {
global $dbi;
parent::__construct($orientation, $unit, $format, $unicode, $encoding, $diskcache, $pdfa);
$this->relation = new Relation($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 bool $addpage if true add a page, otherwise only return
* the true/false state
*/
public function checkPageBreak($h = 0, $y = '', $addpage = true): bool
{
if (TCPDF_STATIC::empty_string($y)) {
$y = $this->y;
}
$current_page = $this->page;
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
if ($y + $h > $this->PageBreakTrigger && ! $this->InFooter && $this->AcceptPageBreak()) {
if ($addpage) {
//Automatic page break
$x = $this->x;
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$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.
*/
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
public function Header(): void
{
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
// phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
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', true);
$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;
}
// phpcs:enable
$this->dataY = $maxY;
$this->setAutoPageBreak(true);
}
/**
* Generate table
*
* @param int $lineheight Height of line
*/
public function morepagestable($lineheight = 8): void
{
// 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 = $this->results->fetchRow()) {
$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;
}
/**
* Defines the top margin.
* The method can be called before creating the first page.
*
* @param float $topMargin the margin
*/
public function setTopMargin($topMargin): void
{
$this->tMargin = $topMargin;
}
/**
* Prints triggers
*
* @param string $db database name
* @param string $table table name
*/
public function getTriggers($db, $table): void
{
global $dbi;
$triggers = $dbi->getTriggers($db, $table);
if ($triggers === []) {
return; //prevents printing blank trigger list for any table
}
unset(
$this->tablewidths,
$this->colTitles,
$this->titleWidth,
$this->colFits,
$this->displayColumn,
$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->displayColumn[$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],
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$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) {
continue;
}
$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 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
*/
public function getTableDef(
$db,
$table,
$do_relation,
$do_comments,
$do_mime,
$view = false,
array $aliases = []
): void {
global $dbi;
$relationParameters = $this->relation->getRelationParameters();
unset(
$this->tablewidths,
$this->colTitles,
$this->titleWidth,
$this->colFits,
$this->displayColumn,
$this->colAlign
);
/**
* Gets fields properties
*/
$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;
}
//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->displayColumn[$columns_cnt] = true;
}
if ($do_relation && $have_rel) {
$this->colTitles[$columns_cnt] = __('Links to');
$this->displayColumn[$columns_cnt] = true;
$this->colAlign[$columns_cnt] = 'L';
$this->tablewidths[$columns_cnt] = 120;
}
if ($do_comments) {
$columns_cnt++;
$this->colTitles[$columns_cnt] = __('Comments');
$this->displayColumn[$columns_cnt] = true;
$this->colAlign[$columns_cnt] = 'L';
$this->tablewidths[$columns_cnt] = 120;
}
if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
$columns_cnt++;
$this->colTitles[$columns_cnt] = __('Media type');
$this->displayColumn[$columns_cnt] = true;
$this->colAlign[$columns_cnt] = 'L';
$this->tablewidths[$columns_cnt] = 120;
}
// 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 && $relationParameters->browserTransformationFeature !== null) {
$mime_map = $this->transformations->getMime($db, $table, true);
}
$columns = $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[] = $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[] = $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],
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$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) {
continue;
}
$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
*/
public function mysqlReport($query): void
{
global $dbi;
unset(
$this->tablewidths,
$this->colTitles,
$this->titleWidth,
$this->colFits,
$this->displayColumn,
$this->colAlign
);
/**
* Pass 1 for column widths
*/
$this->results = $dbi->query($query, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$this->numFields = $this->results->numFields();
$this->fields = $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];
}
/** @var float $stringWidth */
$stringWidth = $this->GetStringWidth($col_as);
$stringWidth += 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->displayColumn[$i] = true;
$this->colAlign[$i] = 'L';
if ($this->fields[$i]->isType(FieldMetadata::TYPE_INT)) {
$this->colAlign[$i] = 'R';
}
if (! $this->fields[$i]->isType(FieldMetadata::TYPE_BLOB)) {
continue;
}
/**
* @todo do not deactivate completely the display
* but show the field's name and [BLOB]
*/
if ($this->fields[$i]->isBinary()) {
$this->displayColumn[$i] = false;
unset($this->colTitles[$i]);
}
$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 = $this->results->fetchRow()) {
foreach ($colFits as $key => $val) {
/** @var float $stringWidth */
$stringWidth = $this->GetStringWidth($row[$key]);
$stringWidth += 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->displayColumn[$i] != false) {
continue;
}
$this->tablewidths[$i] = 0;
}
ksort($this->tablewidths);
// Pass 2
$this->results = $dbi->query($query, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
$this->setY($this->tMargin);
$this->AddPage();
$this->setFont(PdfLib::PMA_PDF_FONT, '', 9);
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$this->morepagestable($this->FontSizePt);
}
public function setTitleFontSize(int $titleFontSize): void
{
$this->titleFontSize = $titleFontSize;
}
public function setTitleText(string $titleText): void
{
$this->titleText = $titleText;
}
public function setCurrentDb(?string $currentDb): void
{
$this->currentDb = $currentDb ?? '';
}
public function setCurrentTable(?string $currentTable): void
{
$this->currentTable = $currentTable ?? '';
}
public function setDbAlias(?string $dbAlias): void
{
$this->dbAlias = $dbAlias ?? '';
}
public function setTableAlias(?string $tableAlias): void
{
$this->tableAlias = $tableAlias ?? '';
}
/**
* @param array $aliases
*/
public function setAliases(array $aliases): void
{
$this->aliases = $aliases;
}
public function setPurpose(string $purpose): void
{
$this->purpose = $purpose;
}
}

View file

@ -0,0 +1,294 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export\Helpers;
use PhpMyAdmin\Plugins\Export\ExportCodegen;
use function htmlspecialchars;
use function mb_strpos;
use function mb_substr;
use function str_replace;
use function strlen;
use function trim;
use const ENT_COMPAT;
/**
* PhpMyAdmin\Plugins\Export\Helpers\TableProperty class
*/
class TableProperty
{
/**
* Name
*
* @var string
*/
public $name;
/**
* Type
*
* @var string
*/
public $type;
/**
* Whether the key is nullable or not
*
* @var string
*/
public $nullable;
/**
* The key
*
* @var string
*/
public $key;
/**
* Default value
*
* @var mixed
*/
public $defaultValue;
/**
* Extension
*
* @var string
*/
public $ext;
/**
* @param array $row table row
*/
public function __construct(array $row)
{
$this->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 = (int) 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
*/
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;
}
}

View file

@ -0,0 +1,247 @@
# Export plugin creation
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.
```php
<?php
/**
* [Name] export plugin for phpMyAdmin
*/
declare(strict_types=1);
/**
* Handles the export for the [Name] format
*/
class Export[Name] extends PhpMyAdmin\Plugins\ExportPlugin
{
/**
* optional - declare variables and descriptions
*
* @var VariableType
*/
private $myOptionalVariable;
/**
* optional - declare global variables and descriptions
*
* @var VariableType
*/
private $globalVariableName;
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
// optional - declare global variables and use getters later
/**
* Initialize the local variables that are used specific for export SQL
*
* @return void
*
* @global VariableType $global_variable_name
* [..]
*/
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(
[
'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 $dbAlias Aliases of db
*
* @return bool Whether it succeeded
*/
public function exportDBHeader($db, $dbAlias = '')
{
// 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 $exportType 'server', 'database', 'table'
* @param string $dbAlias Aliases of db
*
* @return bool Whether it succeeded
*/
public function exportDBCreate($db, $exportType, $dbAlias = '')
{
// 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 $errorUrl the url to go back in case of error
* @param string $sqlQuery SQL query for obtaining data
* @param array $aliases Aliases of db/table/columns
*
* @return bool Whether it succeeded
*/
public function exportData(
$db,
$table,
$crlf,
$errorUrl,
$sqlQuery,
array $aliases = []
) {
// implementation;
return true;
}
// optional - implement other methods defined in PhpMyAdmin\Plugins\ExportPlugin.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
*/
private function getMyOptionalVariable(): VariableType
{
return $this->myOptionalVariable;
}
/**
* Setter description
*/
private function setMyOptionalVariable(VariableType $my_optional_variable): void
{
$this->myOptionalVariable = $my_optional_variable;
}
/**
* Getter description
*/
private function getGlobalVariableName(): VariableType
{
return $this->globalVariableName;
}
/**
* Setter description
*/
private function setGlobalVariableName(VariableType $global_variable_name): void
{
$this->globalVariableName = $global_variable_name;
}
}
```