ExportHtmlword.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * HTML-Word export code
  5. *
  6. * @package PhpMyAdmin-Export
  7. * @subpackage HTML-Word
  8. */
  9. declare(strict_types=1);
  10. namespace PhpMyAdmin\Plugins\Export;
  11. use PhpMyAdmin\DatabaseInterface;
  12. use PhpMyAdmin\Export;
  13. use PhpMyAdmin\Plugins\ExportPlugin;
  14. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  15. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  16. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  17. use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
  18. use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
  19. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  20. use PhpMyAdmin\Relation;
  21. use PhpMyAdmin\Transformations;
  22. use PhpMyAdmin\Util;
  23. /**
  24. * Handles the export for the HTML-Word format
  25. *
  26. * @package PhpMyAdmin-Export
  27. * @subpackage HTML-Word
  28. */
  29. class ExportHtmlword extends ExportPlugin
  30. {
  31. /**
  32. * Constructor
  33. */
  34. public function __construct()
  35. {
  36. parent::__construct();
  37. $this->setProperties();
  38. }
  39. /**
  40. * Sets the export HTML-Word properties
  41. *
  42. * @return void
  43. */
  44. protected function setProperties()
  45. {
  46. $exportPluginProperties = new ExportPluginProperties();
  47. $exportPluginProperties->setText('Microsoft Word 2000');
  48. $exportPluginProperties->setExtension('doc');
  49. $exportPluginProperties->setMimeType('application/vnd.ms-word');
  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(
  56. "Format Specific Options"
  57. );
  58. // what to dump (structure/data/both)
  59. $dumpWhat = new OptionsPropertyMainGroup(
  60. "dump_what",
  61. __('Dump table')
  62. );
  63. // create primary items and add them to the group
  64. $leaf = new RadioPropertyItem("structure_or_data");
  65. $leaf->setValues(
  66. [
  67. 'structure' => __('structure'),
  68. 'data' => __('data'),
  69. 'structure_and_data' => __('structure and data'),
  70. ]
  71. );
  72. $dumpWhat->addProperty($leaf);
  73. // add the main group to the root group
  74. $exportSpecificOptions->addProperty($dumpWhat);
  75. // data options main group
  76. $dataOptions = new OptionsPropertyMainGroup(
  77. "dump_what",
  78. __('Data dump options')
  79. );
  80. $dataOptions->setForce('structure');
  81. // create primary items and add them to the group
  82. $leaf = new TextPropertyItem(
  83. "null",
  84. __('Replace NULL with:')
  85. );
  86. $dataOptions->addProperty($leaf);
  87. $leaf = new BoolPropertyItem(
  88. "columns",
  89. __('Put columns names in the first row')
  90. );
  91. $dataOptions->addProperty($leaf);
  92. // add the main group to the root group
  93. $exportSpecificOptions->addProperty($dataOptions);
  94. // set the options for the export plugin property item
  95. $exportPluginProperties->setOptions($exportSpecificOptions);
  96. $this->properties = $exportPluginProperties;
  97. }
  98. /**
  99. * Outputs export header
  100. *
  101. * @return bool Whether it succeeded
  102. */
  103. public function exportHeader()
  104. {
  105. global $charset;
  106. return $this->export->outputHandler(
  107. '<html xmlns:o="urn:schemas-microsoft-com:office:office"
  108. xmlns:x="urn:schemas-microsoft-com:office:word"
  109. xmlns="http://www.w3.org/TR/REC-html40">
  110. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
  111. . ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  112. <html>
  113. <head>
  114. <meta http-equiv="Content-type" content="text/html;charset='
  115. . (isset($charset) ? $charset : 'utf-8') . '" />
  116. </head>
  117. <body>'
  118. );
  119. }
  120. /**
  121. * Outputs export footer
  122. *
  123. * @return bool Whether it succeeded
  124. */
  125. public function exportFooter()
  126. {
  127. return $this->export->outputHandler('</body></html>');
  128. }
  129. /**
  130. * Outputs database header
  131. *
  132. * @param string $db Database name
  133. * @param string $db_alias Aliases of db
  134. *
  135. * @return bool Whether it succeeded
  136. */
  137. public function exportDBHeader($db, $db_alias = '')
  138. {
  139. if (empty($db_alias)) {
  140. $db_alias = $db;
  141. }
  142. return $this->export->outputHandler(
  143. '<h1>' . __('Database') . ' ' . htmlspecialchars($db_alias) . '</h1>'
  144. );
  145. }
  146. /**
  147. * Outputs database footer
  148. *
  149. * @param string $db Database name
  150. *
  151. * @return bool Whether it succeeded
  152. */
  153. public function exportDBFooter($db)
  154. {
  155. return true;
  156. }
  157. /**
  158. * Outputs CREATE DATABASE statement
  159. *
  160. * @param string $db Database name
  161. * @param string $export_type 'server', 'database', 'table'
  162. * @param string $db_alias Aliases of db
  163. *
  164. * @return bool Whether it succeeded
  165. */
  166. public function exportDBCreate($db, $export_type, $db_alias = '')
  167. {
  168. return true;
  169. }
  170. /**
  171. * Outputs the content of a table in HTML-Word format
  172. *
  173. * @param string $db database name
  174. * @param string $table table name
  175. * @param string $crlf the end of line sequence
  176. * @param string $error_url the url to go back in case of error
  177. * @param string $sql_query SQL query for obtaining data
  178. * @param array $aliases Aliases of db/table/columns
  179. *
  180. * @return bool Whether it succeeded
  181. */
  182. public function exportData(
  183. $db,
  184. $table,
  185. $crlf,
  186. $error_url,
  187. $sql_query,
  188. array $aliases = []
  189. ) {
  190. global $what;
  191. $db_alias = $db;
  192. $table_alias = $table;
  193. $this->initAlias($aliases, $db_alias, $table_alias);
  194. if (! $this->export->outputHandler(
  195. '<h2>'
  196. . __('Dumping data for table') . ' ' . htmlspecialchars($table_alias)
  197. . '</h2>'
  198. )
  199. ) {
  200. return false;
  201. }
  202. if (! $this->export->outputHandler(
  203. '<table class="width100" cellspacing="1">'
  204. )
  205. ) {
  206. return false;
  207. }
  208. // Gets the data from the database
  209. $result = $GLOBALS['dbi']->query(
  210. $sql_query,
  211. DatabaseInterface::CONNECT_USER,
  212. DatabaseInterface::QUERY_UNBUFFERED
  213. );
  214. $fields_cnt = $GLOBALS['dbi']->numFields($result);
  215. // If required, get fields name at the first line
  216. if (isset($GLOBALS['htmlword_columns'])) {
  217. $schema_insert = '<tr class="print-category">';
  218. for ($i = 0; $i < $fields_cnt; $i++) {
  219. $col_as = $GLOBALS['dbi']->fieldName($result, $i);
  220. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  221. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  222. }
  223. $col_as = stripslashes($col_as);
  224. $schema_insert .= '<td class="print"><strong>'
  225. . htmlspecialchars($col_as)
  226. . '</strong></td>';
  227. } // end for
  228. $schema_insert .= '</tr>';
  229. if (! $this->export->outputHandler($schema_insert)) {
  230. return false;
  231. }
  232. } // end if
  233. // Format the data
  234. while ($row = $GLOBALS['dbi']->fetchRow($result)) {
  235. $schema_insert = '<tr class="print-category">';
  236. for ($j = 0; $j < $fields_cnt; $j++) {
  237. if (! isset($row[$j]) || $row[$j] === null) {
  238. $value = $GLOBALS[$what . '_null'];
  239. } elseif ($row[$j] == '0' || $row[$j] != '') {
  240. $value = $row[$j];
  241. } else {
  242. $value = '';
  243. }
  244. $schema_insert .= '<td class="print">'
  245. . htmlspecialchars((string) $value)
  246. . '</td>';
  247. } // end for
  248. $schema_insert .= '</tr>';
  249. if (! $this->export->outputHandler($schema_insert)) {
  250. return false;
  251. }
  252. } // end while
  253. $GLOBALS['dbi']->freeResult($result);
  254. return $this->export->outputHandler('</table>');
  255. }
  256. /**
  257. * Returns a stand-in CREATE definition to resolve view dependencies
  258. *
  259. * @param string $db the database name
  260. * @param string $view the view name
  261. * @param string $crlf the end of line sequence
  262. * @param array $aliases Aliases of db/table/columns
  263. *
  264. * @return string resulting definition
  265. */
  266. public function getTableDefStandIn($db, $view, $crlf, $aliases = [])
  267. {
  268. $schema_insert = '<table class="width100" cellspacing="1">'
  269. . '<tr class="print-category">'
  270. . '<th class="print">'
  271. . __('Column')
  272. . '</th>'
  273. . '<td class="print"><strong>'
  274. . __('Type')
  275. . '</strong></td>'
  276. . '<td class="print"><strong>'
  277. . __('Null')
  278. . '</strong></td>'
  279. . '<td class="print"><strong>'
  280. . __('Default')
  281. . '</strong></td>'
  282. . '</tr>';
  283. /**
  284. * Get the unique keys in the view
  285. */
  286. $unique_keys = [];
  287. $keys = $GLOBALS['dbi']->getTableIndexes($db, $view);
  288. foreach ($keys as $key) {
  289. if ($key['Non_unique'] == 0) {
  290. $unique_keys[] = $key['Column_name'];
  291. }
  292. }
  293. $columns = $GLOBALS['dbi']->getColumns($db, $view);
  294. foreach ($columns as $column) {
  295. $col_as = $column['Field'];
  296. if (! empty($aliases[$db]['tables'][$view]['columns'][$col_as])) {
  297. $col_as = $aliases[$db]['tables'][$view]['columns'][$col_as];
  298. }
  299. $schema_insert .= $this->formatOneColumnDefinition(
  300. $column,
  301. $unique_keys,
  302. $col_as
  303. );
  304. $schema_insert .= '</tr>';
  305. }
  306. $schema_insert .= '</table>';
  307. return $schema_insert;
  308. }
  309. /**
  310. * Returns $table's CREATE definition
  311. *
  312. * @param string $db the database name
  313. * @param string $table the table name
  314. * @param bool $do_relation whether to include relation comments
  315. * @param bool $do_comments whether to include the pmadb-style column
  316. * comments as comments in the structure;
  317. * this is deprecated but the parameter is
  318. * left here because export.php calls
  319. * PMA_exportStructure() also for other
  320. * export types which use this parameter
  321. * @param bool $do_mime whether to include mime comments
  322. * at the end
  323. * @param bool $view whether we're handling a view
  324. * @param array $aliases Aliases of db/table/columns
  325. *
  326. * @return string resulting schema
  327. */
  328. public function getTableDef(
  329. $db,
  330. $table,
  331. $do_relation,
  332. $do_comments,
  333. $do_mime,
  334. $view = false,
  335. array $aliases = []
  336. ) {
  337. // set $cfgRelation here, because there is a chance that it's modified
  338. // since the class initialization
  339. global $cfgRelation;
  340. $schema_insert = '';
  341. /**
  342. * Gets fields properties
  343. */
  344. $GLOBALS['dbi']->selectDb($db);
  345. // Check if we can use Relations
  346. list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus(
  347. $do_relation && ! empty($cfgRelation['relation']),
  348. $db,
  349. $table
  350. );
  351. /**
  352. * Displays the table structure
  353. */
  354. $schema_insert .= '<table class="width100" cellspacing="1">';
  355. $schema_insert .= '<tr class="print-category">';
  356. $schema_insert .= '<th class="print">'
  357. . __('Column')
  358. . '</th>';
  359. $schema_insert .= '<td class="print"><strong>'
  360. . __('Type')
  361. . '</strong></td>';
  362. $schema_insert .= '<td class="print"><strong>'
  363. . __('Null')
  364. . '</strong></td>';
  365. $schema_insert .= '<td class="print"><strong>'
  366. . __('Default')
  367. . '</strong></td>';
  368. if ($do_relation && $have_rel) {
  369. $schema_insert .= '<td class="print"><strong>'
  370. . __('Links to')
  371. . '</strong></td>';
  372. }
  373. if ($do_comments) {
  374. $schema_insert .= '<td class="print"><strong>'
  375. . __('Comments')
  376. . '</strong></td>';
  377. $comments = $this->relation->getComments($db, $table);
  378. }
  379. if ($do_mime && $cfgRelation['mimework']) {
  380. $schema_insert .= '<td class="print"><strong>'
  381. . __('Media (MIME) type')
  382. . '</strong></td>';
  383. $mime_map = $this->transformations->getMime($db, $table, true);
  384. }
  385. $schema_insert .= '</tr>';
  386. $columns = $GLOBALS['dbi']->getColumns($db, $table);
  387. /**
  388. * Get the unique keys in the table
  389. */
  390. $unique_keys = [];
  391. $keys = $GLOBALS['dbi']->getTableIndexes($db, $table);
  392. foreach ($keys as $key) {
  393. if ($key['Non_unique'] == 0) {
  394. $unique_keys[] = $key['Column_name'];
  395. }
  396. }
  397. foreach ($columns as $column) {
  398. $col_as = $column['Field'];
  399. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  400. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  401. }
  402. $schema_insert .= $this->formatOneColumnDefinition(
  403. $column,
  404. $unique_keys,
  405. $col_as
  406. );
  407. $field_name = $column['Field'];
  408. if ($do_relation && $have_rel) {
  409. $schema_insert .= '<td class="print">'
  410. . htmlspecialchars(
  411. $this->getRelationString(
  412. $res_rel,
  413. $field_name,
  414. $db,
  415. $aliases
  416. )
  417. )
  418. . '</td>';
  419. }
  420. if ($do_comments && $cfgRelation['commwork']) {
  421. $schema_insert .= '<td class="print">'
  422. . (isset($comments[$field_name])
  423. ? htmlspecialchars($comments[$field_name])
  424. : '') . '</td>';
  425. }
  426. if ($do_mime && $cfgRelation['mimework']) {
  427. $schema_insert .= '<td class="print">'
  428. . (isset($mime_map[$field_name]) ?
  429. htmlspecialchars(
  430. str_replace('_', '/', $mime_map[$field_name]['mimetype'])
  431. )
  432. : '') . '</td>';
  433. }
  434. $schema_insert .= '</tr>';
  435. } // end foreach
  436. $schema_insert .= '</table>';
  437. return $schema_insert;
  438. }
  439. /**
  440. * Outputs triggers
  441. *
  442. * @param string $db database name
  443. * @param string $table table name
  444. *
  445. * @return string Formatted triggers list
  446. */
  447. protected function getTriggers($db, $table)
  448. {
  449. $dump = '<table class="width100" cellspacing="1">';
  450. $dump .= '<tr class="print-category">';
  451. $dump .= '<th class="print">' . __('Name') . '</th>';
  452. $dump .= '<td class="print"><strong>' . __('Time') . '</strong></td>';
  453. $dump .= '<td class="print"><strong>' . __('Event') . '</strong></td>';
  454. $dump .= '<td class="print"><strong>' . __('Definition') . '</strong></td>';
  455. $dump .= '</tr>';
  456. $triggers = $GLOBALS['dbi']->getTriggers($db, $table);
  457. foreach ($triggers as $trigger) {
  458. $dump .= '<tr class="print-category">';
  459. $dump .= '<td class="print">'
  460. . htmlspecialchars($trigger['name'])
  461. . '</td>'
  462. . '<td class="print">'
  463. . htmlspecialchars($trigger['action_timing'])
  464. . '</td>'
  465. . '<td class="print">'
  466. . htmlspecialchars($trigger['event_manipulation'])
  467. . '</td>'
  468. . '<td class="print">'
  469. . htmlspecialchars($trigger['definition'])
  470. . '</td>'
  471. . '</tr>';
  472. }
  473. $dump .= '</table>';
  474. return $dump;
  475. }
  476. /**
  477. * Outputs table's structure
  478. *
  479. * @param string $db database name
  480. * @param string $table table name
  481. * @param string $crlf the end of line sequence
  482. * @param string $error_url the url to go back in case of error
  483. * @param string $export_mode 'create_table', 'triggers', 'create_view',
  484. * 'stand_in'
  485. * @param string $export_type 'server', 'database', 'table'
  486. * @param bool $do_relation whether to include relation comments
  487. * @param bool $do_comments whether to include the pmadb-style column
  488. * comments as comments in the structure;
  489. * this is deprecated but the parameter is
  490. * left here because export.php calls
  491. * PMA_exportStructure() also for other
  492. * export types which use this parameter
  493. * @param bool $do_mime whether to include mime comments
  494. * @param bool $dates whether to include creation/update/check dates
  495. * @param array $aliases Aliases of db/table/columns
  496. *
  497. * @return bool Whether it succeeded
  498. */
  499. public function exportStructure(
  500. $db,
  501. $table,
  502. $crlf,
  503. $error_url,
  504. $export_mode,
  505. $export_type,
  506. $do_relation = false,
  507. $do_comments = false,
  508. $do_mime = false,
  509. $dates = false,
  510. array $aliases = []
  511. ) {
  512. $db_alias = $db;
  513. $table_alias = $table;
  514. $this->initAlias($aliases, $db_alias, $table_alias);
  515. $dump = '';
  516. switch ($export_mode) {
  517. case 'create_table':
  518. $dump .= '<h2>'
  519. . __('Table structure for table') . ' '
  520. . htmlspecialchars($table_alias)
  521. . '</h2>';
  522. $dump .= $this->getTableDef(
  523. $db,
  524. $table,
  525. $do_relation,
  526. $do_comments,
  527. $do_mime,
  528. false,
  529. $aliases
  530. );
  531. break;
  532. case 'triggers':
  533. $dump = '';
  534. $triggers = $GLOBALS['dbi']->getTriggers($db, $table);
  535. if ($triggers) {
  536. $dump .= '<h2>'
  537. . __('Triggers') . ' ' . htmlspecialchars($table_alias)
  538. . '</h2>';
  539. $dump .= $this->getTriggers($db, $table);
  540. }
  541. break;
  542. case 'create_view':
  543. $dump .= '<h2>'
  544. . __('Structure for view') . ' ' . htmlspecialchars($table_alias)
  545. . '</h2>';
  546. $dump .= $this->getTableDef(
  547. $db,
  548. $table,
  549. $do_relation,
  550. $do_comments,
  551. $do_mime,
  552. true,
  553. $aliases
  554. );
  555. break;
  556. case 'stand_in':
  557. $dump .= '<h2>'
  558. . __('Stand-in structure for view') . ' '
  559. . htmlspecialchars($table_alias)
  560. . '</h2>';
  561. // export a stand-in definition to resolve view dependencies
  562. $dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases);
  563. } // end switch
  564. return $this->export->outputHandler($dump);
  565. }
  566. /**
  567. * Formats the definition for one column
  568. *
  569. * @param array $column info about this column
  570. * @param array $unique_keys unique keys of the table
  571. * @param string $col_alias Column Alias
  572. *
  573. * @return string Formatted column definition
  574. */
  575. protected function formatOneColumnDefinition(
  576. array $column,
  577. array $unique_keys,
  578. $col_alias = ''
  579. ) {
  580. if (empty($col_alias)) {
  581. $col_alias = $column['Field'];
  582. }
  583. $definition = '<tr class="print-category">';
  584. $extracted_columnspec = Util::extractColumnSpec($column['Type']);
  585. $type = htmlspecialchars($extracted_columnspec['print_type']);
  586. if (empty($type)) {
  587. $type = '&nbsp;';
  588. }
  589. if (! isset($column['Default'])) {
  590. if ($column['Null'] != 'NO') {
  591. $column['Default'] = 'NULL';
  592. }
  593. }
  594. $fmt_pre = '';
  595. $fmt_post = '';
  596. if (in_array($column['Field'], $unique_keys)) {
  597. $fmt_pre = '<strong>' . $fmt_pre;
  598. $fmt_post .= '</strong>';
  599. }
  600. if ($column['Key'] == 'PRI') {
  601. $fmt_pre = '<em>' . $fmt_pre;
  602. $fmt_post .= '</em>';
  603. }
  604. $definition .= '<td class="print">' . $fmt_pre
  605. . htmlspecialchars($col_alias) . $fmt_post . '</td>';
  606. $definition .= '<td class="print">' . htmlspecialchars($type) . '</td>';
  607. $definition .= '<td class="print">'
  608. . (($column['Null'] == '' || $column['Null'] == 'NO')
  609. ? __('No')
  610. : __('Yes'))
  611. . '</td>';
  612. $definition .= '<td class="print">'
  613. . htmlspecialchars(isset($column['Default']) ? $column['Default'] : '')
  614. . '</td>';
  615. return $definition;
  616. }
  617. }