ExportOdt.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. <?php
  2. /**
  3. * Set of functions used to build OpenDocument Text dumps of tables
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Plugins\Export;
  7. use PhpMyAdmin\DatabaseInterface;
  8. use PhpMyAdmin\FieldMetadata;
  9. use PhpMyAdmin\OpenDocument;
  10. use PhpMyAdmin\Plugins\ExportPlugin;
  11. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  12. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  13. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  14. use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
  15. use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
  16. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  17. use PhpMyAdmin\Util;
  18. use function __;
  19. use function bin2hex;
  20. use function htmlspecialchars;
  21. use function str_replace;
  22. use function stripslashes;
  23. /**
  24. * Handles the export for the ODT class
  25. */
  26. class ExportOdt extends ExportPlugin
  27. {
  28. protected function init(): void
  29. {
  30. $GLOBALS['odt_buffer'] = '';
  31. }
  32. /**
  33. * @psalm-return non-empty-lowercase-string
  34. */
  35. public function getName(): string
  36. {
  37. return 'odt';
  38. }
  39. protected function setProperties(): ExportPluginProperties
  40. {
  41. global $plugin_param;
  42. $hide_structure = false;
  43. if ($plugin_param['export_type'] === 'table' && ! $plugin_param['single_table']) {
  44. $hide_structure = true;
  45. }
  46. $exportPluginProperties = new ExportPluginProperties();
  47. $exportPluginProperties->setText('OpenDocument Text');
  48. $exportPluginProperties->setExtension('odt');
  49. $exportPluginProperties->setMimeType('application/vnd.oasis.opendocument.text');
  50. $exportPluginProperties->setForceFile(true);
  51. $exportPluginProperties->setOptionsText(__('Options'));
  52. // create the root group that will be the options field for
  53. // $exportPluginProperties
  54. // this will be shown as "Format specific options"
  55. $exportSpecificOptions = new OptionsPropertyRootGroup('Format Specific Options');
  56. // what to dump (structure/data/both) main group
  57. $dumpWhat = new OptionsPropertyMainGroup(
  58. 'general_opts',
  59. __('Dump table')
  60. );
  61. // create primary items and add them to the group
  62. $leaf = new RadioPropertyItem('structure_or_data');
  63. $leaf->setValues(
  64. [
  65. 'structure' => __('structure'),
  66. 'data' => __('data'),
  67. 'structure_and_data' => __('structure and data'),
  68. ]
  69. );
  70. $dumpWhat->addProperty($leaf);
  71. // add the main group to the root group
  72. $exportSpecificOptions->addProperty($dumpWhat);
  73. // structure options main group
  74. if (! $hide_structure) {
  75. $structureOptions = new OptionsPropertyMainGroup(
  76. 'structure',
  77. __('Object creation options')
  78. );
  79. $structureOptions->setForce('data');
  80. $relationParameters = $this->relation->getRelationParameters();
  81. // create primary items and add them to the group
  82. if ($relationParameters->relationFeature !== null) {
  83. $leaf = new BoolPropertyItem(
  84. 'relation',
  85. __('Display foreign key relationships')
  86. );
  87. $structureOptions->addProperty($leaf);
  88. }
  89. $leaf = new BoolPropertyItem(
  90. 'comments',
  91. __('Display comments')
  92. );
  93. $structureOptions->addProperty($leaf);
  94. if ($relationParameters->browserTransformationFeature !== null) {
  95. $leaf = new BoolPropertyItem(
  96. 'mime',
  97. __('Display media types')
  98. );
  99. $structureOptions->addProperty($leaf);
  100. }
  101. // add the main group to the root group
  102. $exportSpecificOptions->addProperty($structureOptions);
  103. }
  104. // data options main group
  105. $dataOptions = new OptionsPropertyMainGroup(
  106. 'data',
  107. __('Data dump options')
  108. );
  109. $dataOptions->setForce('structure');
  110. // create primary items and add them to the group
  111. $leaf = new BoolPropertyItem(
  112. 'columns',
  113. __('Put columns names in the first row')
  114. );
  115. $dataOptions->addProperty($leaf);
  116. $leaf = new TextPropertyItem(
  117. 'null',
  118. __('Replace NULL with:')
  119. );
  120. $dataOptions->addProperty($leaf);
  121. // add the main group to the root group
  122. $exportSpecificOptions->addProperty($dataOptions);
  123. // set the options for the export plugin property item
  124. $exportPluginProperties->setOptions($exportSpecificOptions);
  125. return $exportPluginProperties;
  126. }
  127. /**
  128. * Outputs export header
  129. */
  130. public function exportHeader(): bool
  131. {
  132. $GLOBALS['odt_buffer'] .= '<?xml version="1.0" encoding="utf-8"?' . '>'
  133. . '<office:document-content '
  134. . OpenDocument::NS . ' office:version="1.0">'
  135. . '<office:body>'
  136. . '<office:text>';
  137. return true;
  138. }
  139. /**
  140. * Outputs export footer
  141. */
  142. public function exportFooter(): bool
  143. {
  144. $GLOBALS['odt_buffer'] .= '</office:text></office:body></office:document-content>';
  145. return $this->export->outputHandler(OpenDocument::create(
  146. 'application/vnd.oasis.opendocument.text',
  147. $GLOBALS['odt_buffer']
  148. ));
  149. }
  150. /**
  151. * Outputs database header
  152. *
  153. * @param string $db Database name
  154. * @param string $dbAlias Aliases of db
  155. */
  156. public function exportDBHeader($db, $dbAlias = ''): bool
  157. {
  158. if (empty($dbAlias)) {
  159. $dbAlias = $db;
  160. }
  161. $GLOBALS['odt_buffer'] .= '<text:h text:outline-level="1" text:style-name="Heading_1"'
  162. . ' text:is-list-header="true">'
  163. . __('Database') . ' ' . htmlspecialchars($dbAlias)
  164. . '</text:h>';
  165. return true;
  166. }
  167. /**
  168. * Outputs database footer
  169. *
  170. * @param string $db Database name
  171. */
  172. public function exportDBFooter($db): bool
  173. {
  174. return true;
  175. }
  176. /**
  177. * Outputs CREATE DATABASE statement
  178. *
  179. * @param string $db Database name
  180. * @param string $exportType 'server', 'database', 'table'
  181. * @param string $dbAlias Aliases of db
  182. */
  183. public function exportDBCreate($db, $exportType, $dbAlias = ''): bool
  184. {
  185. return true;
  186. }
  187. /**
  188. * Outputs the content of a table in NHibernate format
  189. *
  190. * @param string $db database name
  191. * @param string $table table name
  192. * @param string $crlf the end of line sequence
  193. * @param string $errorUrl the url to go back in case of error
  194. * @param string $sqlQuery SQL query for obtaining data
  195. * @param array $aliases Aliases of db/table/columns
  196. */
  197. public function exportData(
  198. $db,
  199. $table,
  200. $crlf,
  201. $errorUrl,
  202. $sqlQuery,
  203. array $aliases = []
  204. ): bool {
  205. global $what, $dbi;
  206. $db_alias = $db;
  207. $table_alias = $table;
  208. $this->initAlias($aliases, $db_alias, $table_alias);
  209. // Gets the data from the database
  210. $result = $dbi->query($sqlQuery, DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_UNBUFFERED);
  211. $fields_cnt = $result->numFields();
  212. /** @var FieldMetadata[] $fieldsMeta */
  213. $fieldsMeta = $dbi->getFieldsMeta($result);
  214. $GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
  215. . ' text:is-list-header="true">';
  216. $table_alias != ''
  217. ? $GLOBALS['odt_buffer'] .= __('Dumping data for table') . ' ' . htmlspecialchars($table_alias)
  218. : $GLOBALS['odt_buffer'] .= __('Dumping data for query result');
  219. $GLOBALS['odt_buffer'] .= '</text:h>'
  220. . '<table:table'
  221. . ' table:name="' . htmlspecialchars($table_alias) . '_structure">'
  222. . '<table:table-column'
  223. . ' table:number-columns-repeated="' . $fields_cnt . '"/>';
  224. // If required, get fields name at the first line
  225. if (isset($GLOBALS[$what . '_columns'])) {
  226. $GLOBALS['odt_buffer'] .= '<table:table-row>';
  227. foreach ($fieldsMeta as $field) {
  228. $col_as = $field->name;
  229. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  230. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  231. }
  232. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  233. . '<text:p>'
  234. . htmlspecialchars(
  235. stripslashes($col_as)
  236. )
  237. . '</text:p>'
  238. . '</table:table-cell>';
  239. }
  240. $GLOBALS['odt_buffer'] .= '</table:table-row>';
  241. }
  242. // Format the data
  243. while ($row = $result->fetchRow()) {
  244. $GLOBALS['odt_buffer'] .= '<table:table-row>';
  245. for ($j = 0; $j < $fields_cnt; $j++) {
  246. if ($fieldsMeta[$j]->isMappedTypeGeometry) {
  247. // export GIS types as hex
  248. $row[$j] = '0x' . bin2hex($row[$j]);
  249. }
  250. if (! isset($row[$j])) {
  251. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  252. . '<text:p>'
  253. . htmlspecialchars($GLOBALS[$what . '_null'])
  254. . '</text:p>'
  255. . '</table:table-cell>';
  256. } elseif ($fieldsMeta[$j]->isBinary && $fieldsMeta[$j]->isBlob) {
  257. // ignore BLOB
  258. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  259. . '<text:p></text:p>'
  260. . '</table:table-cell>';
  261. } elseif (
  262. $fieldsMeta[$j]->isNumeric
  263. ) {
  264. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="float"'
  265. . ' office:value="' . $row[$j] . '" >'
  266. . '<text:p>'
  267. . htmlspecialchars($row[$j])
  268. . '</text:p>'
  269. . '</table:table-cell>';
  270. } else {
  271. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  272. . '<text:p>'
  273. . htmlspecialchars($row[$j])
  274. . '</text:p>'
  275. . '</table:table-cell>';
  276. }
  277. }
  278. $GLOBALS['odt_buffer'] .= '</table:table-row>';
  279. }
  280. $GLOBALS['odt_buffer'] .= '</table:table>';
  281. return true;
  282. }
  283. /**
  284. * Outputs result raw query in ODT format
  285. *
  286. * @param string $errorUrl the url to go back in case of error
  287. * @param string|null $db the database where the query is executed
  288. * @param string $sqlQuery the rawquery to output
  289. * @param string $crlf the end of line sequence
  290. */
  291. public function exportRawQuery(string $errorUrl, ?string $db, string $sqlQuery, string $crlf): bool
  292. {
  293. global $dbi;
  294. if ($db !== null) {
  295. $dbi->selectDb($db);
  296. }
  297. return $this->exportData($db ?? '', '', $crlf, $errorUrl, $sqlQuery);
  298. }
  299. /**
  300. * Returns a stand-in CREATE definition to resolve view dependencies
  301. *
  302. * @param string $db the database name
  303. * @param string $view the view name
  304. * @param string $crlf the end of line sequence
  305. * @param array $aliases Aliases of db/table/columns
  306. *
  307. * @return string resulting definition
  308. */
  309. public function getTableDefStandIn($db, $view, $crlf, $aliases = [])
  310. {
  311. global $dbi;
  312. $db_alias = $db;
  313. $view_alias = $view;
  314. $this->initAlias($aliases, $db_alias, $view_alias);
  315. /**
  316. * Gets fields properties
  317. */
  318. $dbi->selectDb($db);
  319. /**
  320. * Displays the table structure
  321. */
  322. $GLOBALS['odt_buffer'] .= '<table:table table:name="'
  323. . htmlspecialchars($view_alias) . '_data">';
  324. $columns_cnt = 4;
  325. $GLOBALS['odt_buffer'] .= '<table:table-column'
  326. . ' table:number-columns-repeated="' . $columns_cnt . '"/>';
  327. /* Header */
  328. $GLOBALS['odt_buffer'] .= '<table:table-row>'
  329. . '<table:table-cell office:value-type="string">'
  330. . '<text:p>' . __('Column') . '</text:p>'
  331. . '</table:table-cell>'
  332. . '<table:table-cell office:value-type="string">'
  333. . '<text:p>' . __('Type') . '</text:p>'
  334. . '</table:table-cell>'
  335. . '<table:table-cell office:value-type="string">'
  336. . '<text:p>' . __('Null') . '</text:p>'
  337. . '</table:table-cell>'
  338. . '<table:table-cell office:value-type="string">'
  339. . '<text:p>' . __('Default') . '</text:p>'
  340. . '</table:table-cell>'
  341. . '</table:table-row>';
  342. $columns = $dbi->getColumns($db, $view);
  343. foreach ($columns as $column) {
  344. $col_as = $column['Field'] ?? null;
  345. if (! empty($aliases[$db]['tables'][$view]['columns'][$col_as])) {
  346. $col_as = $aliases[$db]['tables'][$view]['columns'][$col_as];
  347. }
  348. $GLOBALS['odt_buffer'] .= $this->formatOneColumnDefinition($column, $col_as);
  349. $GLOBALS['odt_buffer'] .= '</table:table-row>';
  350. }
  351. $GLOBALS['odt_buffer'] .= '</table:table>';
  352. return '';
  353. }
  354. /**
  355. * Returns $table's CREATE definition
  356. *
  357. * @param string $db the database name
  358. * @param string $table the table name
  359. * @param string $crlf the end of line sequence
  360. * @param string $error_url the url to go back in case of error
  361. * @param bool $do_relation whether to include relation comments
  362. * @param bool $do_comments whether to include the pmadb-style column
  363. * comments as comments in the structure;
  364. * this is deprecated but the parameter is
  365. * left here because /export calls
  366. * PMA_exportStructure() also for other
  367. * @param bool $do_mime whether to include mime comments
  368. * @param bool $show_dates whether to include creation/update/check dates
  369. * @param bool $add_semicolon whether to add semicolon and end-of-line at
  370. * the end
  371. * @param bool $view whether we're handling a view
  372. * @param array $aliases Aliases of db/table/columns
  373. */
  374. public function getTableDef(
  375. $db,
  376. $table,
  377. $crlf,
  378. $error_url,
  379. $do_relation,
  380. $do_comments,
  381. $do_mime,
  382. $show_dates = false,
  383. $add_semicolon = true,
  384. $view = false,
  385. array $aliases = []
  386. ): bool {
  387. global $dbi;
  388. $db_alias = $db;
  389. $table_alias = $table;
  390. $this->initAlias($aliases, $db_alias, $table_alias);
  391. $relationParameters = $this->relation->getRelationParameters();
  392. /**
  393. * Gets fields properties
  394. */
  395. $dbi->selectDb($db);
  396. // Check if we can use Relations
  397. [$res_rel, $have_rel] = $this->relation->getRelationsAndStatus(
  398. $do_relation && $relationParameters->relationFeature !== null,
  399. $db,
  400. $table
  401. );
  402. /**
  403. * Displays the table structure
  404. */
  405. $GLOBALS['odt_buffer'] .= '<table:table table:name="'
  406. . htmlspecialchars($table_alias) . '_structure">';
  407. $columns_cnt = 4;
  408. if ($do_relation && $have_rel) {
  409. $columns_cnt++;
  410. }
  411. if ($do_comments) {
  412. $columns_cnt++;
  413. }
  414. if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
  415. $columns_cnt++;
  416. }
  417. $GLOBALS['odt_buffer'] .= '<table:table-column'
  418. . ' table:number-columns-repeated="' . $columns_cnt . '"/>';
  419. /* Header */
  420. $GLOBALS['odt_buffer'] .= '<table:table-row>'
  421. . '<table:table-cell office:value-type="string">'
  422. . '<text:p>' . __('Column') . '</text:p>'
  423. . '</table:table-cell>'
  424. . '<table:table-cell office:value-type="string">'
  425. . '<text:p>' . __('Type') . '</text:p>'
  426. . '</table:table-cell>'
  427. . '<table:table-cell office:value-type="string">'
  428. . '<text:p>' . __('Null') . '</text:p>'
  429. . '</table:table-cell>'
  430. . '<table:table-cell office:value-type="string">'
  431. . '<text:p>' . __('Default') . '</text:p>'
  432. . '</table:table-cell>';
  433. if ($do_relation && $have_rel) {
  434. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  435. . '<text:p>' . __('Links to') . '</text:p>'
  436. . '</table:table-cell>';
  437. }
  438. if ($do_comments) {
  439. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  440. . '<text:p>' . __('Comments') . '</text:p>'
  441. . '</table:table-cell>';
  442. $comments = $this->relation->getComments($db, $table);
  443. }
  444. if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
  445. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  446. . '<text:p>' . __('Media type') . '</text:p>'
  447. . '</table:table-cell>';
  448. $mime_map = $this->transformations->getMime($db, $table, true);
  449. }
  450. $GLOBALS['odt_buffer'] .= '</table:table-row>';
  451. $columns = $dbi->getColumns($db, $table);
  452. foreach ($columns as $column) {
  453. $col_as = $field_name = $column['Field'];
  454. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  455. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  456. }
  457. $GLOBALS['odt_buffer'] .= $this->formatOneColumnDefinition($column, $col_as);
  458. if ($do_relation && $have_rel) {
  459. $foreigner = $this->relation->searchColumnInForeigners($res_rel, $field_name);
  460. if ($foreigner) {
  461. $rtable = $foreigner['foreign_table'];
  462. $rfield = $foreigner['foreign_field'];
  463. if (! empty($aliases[$db]['tables'][$rtable]['columns'][$rfield])) {
  464. $rfield = $aliases[$db]['tables'][$rtable]['columns'][$rfield];
  465. }
  466. if (! empty($aliases[$db]['tables'][$rtable]['alias'])) {
  467. $rtable = $aliases[$db]['tables'][$rtable]['alias'];
  468. }
  469. $relation = htmlspecialchars($rtable . ' (' . $rfield . ')');
  470. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  471. . '<text:p>'
  472. . htmlspecialchars($relation)
  473. . '</text:p>'
  474. . '</table:table-cell>';
  475. }
  476. }
  477. if ($do_comments) {
  478. if (isset($comments[$field_name])) {
  479. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  480. . '<text:p>'
  481. . htmlspecialchars($comments[$field_name])
  482. . '</text:p>'
  483. . '</table:table-cell>';
  484. } else {
  485. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  486. . '<text:p></text:p>'
  487. . '</table:table-cell>';
  488. }
  489. }
  490. if ($do_mime && $relationParameters->browserTransformationFeature !== null) {
  491. if (isset($mime_map[$field_name])) {
  492. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  493. . '<text:p>'
  494. . htmlspecialchars(
  495. str_replace('_', '/', $mime_map[$field_name]['mimetype'])
  496. )
  497. . '</text:p>'
  498. . '</table:table-cell>';
  499. } else {
  500. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  501. . '<text:p></text:p>'
  502. . '</table:table-cell>';
  503. }
  504. }
  505. $GLOBALS['odt_buffer'] .= '</table:table-row>';
  506. }
  507. $GLOBALS['odt_buffer'] .= '</table:table>';
  508. return true;
  509. }
  510. /**
  511. * Outputs triggers
  512. *
  513. * @param string $db database name
  514. * @param string $table table name
  515. * @param array $aliases Aliases of db/table/columns
  516. *
  517. * @return string
  518. */
  519. protected function getTriggers($db, $table, array $aliases = [])
  520. {
  521. global $dbi;
  522. $db_alias = $db;
  523. $table_alias = $table;
  524. $this->initAlias($aliases, $db_alias, $table_alias);
  525. $GLOBALS['odt_buffer'] .= '<table:table'
  526. . ' table:name="' . htmlspecialchars($table_alias) . '_triggers">'
  527. . '<table:table-column'
  528. . ' table:number-columns-repeated="4"/>'
  529. . '<table:table-row>'
  530. . '<table:table-cell office:value-type="string">'
  531. . '<text:p>' . __('Name') . '</text:p>'
  532. . '</table:table-cell>'
  533. . '<table:table-cell office:value-type="string">'
  534. . '<text:p>' . __('Time') . '</text:p>'
  535. . '</table:table-cell>'
  536. . '<table:table-cell office:value-type="string">'
  537. . '<text:p>' . __('Event') . '</text:p>'
  538. . '</table:table-cell>'
  539. . '<table:table-cell office:value-type="string">'
  540. . '<text:p>' . __('Definition') . '</text:p>'
  541. . '</table:table-cell>'
  542. . '</table:table-row>';
  543. $triggers = $dbi->getTriggers($db, $table);
  544. foreach ($triggers as $trigger) {
  545. $GLOBALS['odt_buffer'] .= '<table:table-row>';
  546. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  547. . '<text:p>'
  548. . htmlspecialchars($trigger['name'])
  549. . '</text:p>'
  550. . '</table:table-cell>';
  551. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  552. . '<text:p>'
  553. . htmlspecialchars($trigger['action_timing'])
  554. . '</text:p>'
  555. . '</table:table-cell>';
  556. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  557. . '<text:p>'
  558. . htmlspecialchars($trigger['event_manipulation'])
  559. . '</text:p>'
  560. . '</table:table-cell>';
  561. $GLOBALS['odt_buffer'] .= '<table:table-cell office:value-type="string">'
  562. . '<text:p>'
  563. . htmlspecialchars($trigger['definition'])
  564. . '</text:p>'
  565. . '</table:table-cell>';
  566. $GLOBALS['odt_buffer'] .= '</table:table-row>';
  567. }
  568. $GLOBALS['odt_buffer'] .= '</table:table>';
  569. return $GLOBALS['odt_buffer'];
  570. }
  571. /**
  572. * Outputs table's structure
  573. *
  574. * @param string $db database name
  575. * @param string $table table name
  576. * @param string $crlf the end of line sequence
  577. * @param string $errorUrl the url to go back in case of error
  578. * @param string $exportMode 'create_table', 'triggers', 'create_view',
  579. * 'stand_in'
  580. * @param string $exportType 'server', 'database', 'table'
  581. * @param bool $do_relation whether to include relation comments
  582. * @param bool $do_comments whether to include the pmadb-style column
  583. * comments as comments in the structure;
  584. * this is deprecated but the parameter is
  585. * left here because /export calls
  586. * PMA_exportStructure() also for other
  587. * @param bool $do_mime whether to include mime comments
  588. * @param bool $dates whether to include creation/update/check dates
  589. * @param array $aliases Aliases of db/table/columns
  590. */
  591. public function exportStructure(
  592. $db,
  593. $table,
  594. $crlf,
  595. $errorUrl,
  596. $exportMode,
  597. $exportType,
  598. $do_relation = false,
  599. $do_comments = false,
  600. $do_mime = false,
  601. $dates = false,
  602. array $aliases = []
  603. ): bool {
  604. global $dbi;
  605. $db_alias = $db;
  606. $table_alias = $table;
  607. $this->initAlias($aliases, $db_alias, $table_alias);
  608. switch ($exportMode) {
  609. case 'create_table':
  610. $GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
  611. . ' text:is-list-header="true">'
  612. . __('Table structure for table') . ' ' .
  613. htmlspecialchars($table_alias)
  614. . '</text:h>';
  615. $this->getTableDef(
  616. $db,
  617. $table,
  618. $crlf,
  619. $errorUrl,
  620. $do_relation,
  621. $do_comments,
  622. $do_mime,
  623. $dates,
  624. true,
  625. false,
  626. $aliases
  627. );
  628. break;
  629. case 'triggers':
  630. $triggers = $dbi->getTriggers($db, $table);
  631. if ($triggers) {
  632. $GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
  633. . ' text:is-list-header="true">'
  634. . __('Triggers') . ' '
  635. . htmlspecialchars($table_alias)
  636. . '</text:h>';
  637. $this->getTriggers($db, $table);
  638. }
  639. break;
  640. case 'create_view':
  641. $GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
  642. . ' text:is-list-header="true">'
  643. . __('Structure for view') . ' '
  644. . htmlspecialchars($table_alias)
  645. . '</text:h>';
  646. $this->getTableDef(
  647. $db,
  648. $table,
  649. $crlf,
  650. $errorUrl,
  651. $do_relation,
  652. $do_comments,
  653. $do_mime,
  654. $dates,
  655. true,
  656. true,
  657. $aliases
  658. );
  659. break;
  660. case 'stand_in':
  661. $GLOBALS['odt_buffer'] .= '<text:h text:outline-level="2" text:style-name="Heading_2"'
  662. . ' text:is-list-header="true">'
  663. . __('Stand-in structure for view') . ' '
  664. . htmlspecialchars($table_alias)
  665. . '</text:h>';
  666. // export a stand-in definition to resolve view dependencies
  667. $this->getTableDefStandIn($db, $table, $crlf, $aliases);
  668. }
  669. return true;
  670. }
  671. /**
  672. * Formats the definition for one column
  673. *
  674. * @param array $column info about this column
  675. * @param string $col_as column alias
  676. *
  677. * @return string Formatted column definition
  678. */
  679. protected function formatOneColumnDefinition($column, $col_as = '')
  680. {
  681. if (empty($col_as)) {
  682. $col_as = $column['Field'];
  683. }
  684. $definition = '<table:table-row>';
  685. $definition .= '<table:table-cell office:value-type="string">'
  686. . '<text:p>' . htmlspecialchars($col_as) . '</text:p>'
  687. . '</table:table-cell>';
  688. $extracted_columnspec = Util::extractColumnSpec($column['Type']);
  689. $type = htmlspecialchars($extracted_columnspec['print_type']);
  690. if (empty($type)) {
  691. $type = '&nbsp;';
  692. }
  693. $definition .= '<table:table-cell office:value-type="string">'
  694. . '<text:p>' . htmlspecialchars($type) . '</text:p>'
  695. . '</table:table-cell>';
  696. if (! isset($column['Default'])) {
  697. if ($column['Null'] !== 'NO') {
  698. $column['Default'] = 'NULL';
  699. } else {
  700. $column['Default'] = '';
  701. }
  702. }
  703. $definition .= '<table:table-cell office:value-type="string">'
  704. . '<text:p>'
  705. . ($column['Null'] == '' || $column['Null'] === 'NO'
  706. ? __('No')
  707. : __('Yes'))
  708. . '</text:p>'
  709. . '</table:table-cell>';
  710. $definition .= '<table:table-cell office:value-type="string">'
  711. . '<text:p>' . htmlspecialchars($column['Default']) . '</text:p>'
  712. . '</table:table-cell>';
  713. return $definition;
  714. }
  715. }