From 4b9ef9a757b5fa3b05cb8e5fa35799ce9d43876b Mon Sep 17 00:00:00 2001 From: Aayush Anand Date: Sat, 28 Mar 2015 20:12:35 +0530 Subject: [PATCH] feature 844 structure in PDF export squashed Signed-off-by: Aayush Anand --- libraries/plugins/export/ExportPdf.class.php | 123 +++++- .../plugins/export/PMA_ExportPdf.class.php | 371 +++++++++++++++++- .../plugin/export/PMA_ExportPdf_test.php | 52 ++- 3 files changed, 517 insertions(+), 29 deletions(-) diff --git a/libraries/plugins/export/ExportPdf.class.php b/libraries/plugins/export/ExportPdf.class.php index 121c8d1587..d87bb4314a 100644 --- a/libraries/plugins/export/ExportPdf.class.php +++ b/libraries/plugins/export/ExportPdf.class.php @@ -22,6 +22,7 @@ if (! file_exists(TCPDF_INC)) { require_once 'libraries/plugins/ExportPlugin.class.php'; /* Get the PMA_ExportPdf class */ require_once 'libraries/plugins/export/PMA_ExportPdf.class.php'; +require_once 'libraries/transformations.lib.php'; /** * Handles the export for the PDF class @@ -80,9 +81,8 @@ class ExportPdf extends ExportPlugin include_once "$props/plugins/ExportPluginProperties.class.php"; include_once "$props/options/groups/OptionsPropertyRootGroup.class.php"; include_once "$props/options/groups/OptionsPropertyMainGroup.class.php"; - include_once "$props/options/items/MessageOnlyPropertyItem.class.php"; + include_once "$props/options/items/RadioPropertyItem.class.php"; include_once "$props/options/items/TextPropertyItem.class.php"; - include_once "$props/options/items/HiddenPropertyItem.class.php"; $exportPluginProperties = new ExportPluginProperties(); $exportPluginProperties->setText('PDF'); @@ -101,22 +101,30 @@ class ExportPdf extends ExportPlugin $generalOptions = new OptionsPropertyMainGroup(); $generalOptions->setName("general_opts"); // create primary items and add them to the group - $leaf = new MessageOnlyPropertyItem(); - $leaf->setName("explanation"); - $leaf->setText( - __('(Generates a report containing the data of a single table)') - ); - $generalOptions->addProperty($leaf); $leaf = new TextPropertyItem(); $leaf->setName("report_title"); $leaf->setText(__('Report title:')); $generalOptions->addProperty($leaf); - $leaf = new HiddenPropertyItem(); - $leaf->setName("structure_or_data"); - $generalOptions->addProperty($leaf); - // add the main group to the root group + // add the group to the root group $exportSpecificOptions->addProperty($generalOptions); + // what to dump (structure/data/both) main group + $dumpWhat = new OptionsPropertyMainGroup(); + $dumpWhat->setName("dump_what"); + $dumpWhat->setText(__('Dump table')); + $leaf = new RadioPropertyItem(); + $leaf->setName("structure_or_data"); + $leaf->setValues( + array( + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data') + ) + ); + $dumpWhat->addProperty($leaf); + // add the group to the root group + $exportSpecificOptions->addProperty($dumpWhat); + // set the options for the export plugin property item $exportPluginProperties->setOptions($exportSpecificOptions); $this->properties = $exportPluginProperties; @@ -213,18 +221,107 @@ class ExportPdf extends ExportPlugin $table_alias = $table; $this->initAlias($aliases, $db_alias, $table_alias); $pdf = $this->_getPdf(); - $attr = array( 'currentDb' => $db, 'currentTable' => $table, 'dbAlias' => $db_alias, 'tableAlias' => $table_alias, 'aliases' => $aliases ); $pdf->setAttributes($attr); + $pdf->purpose = __('Dumping Data'); $pdf->mysqlReport($sql_query); return true; } // end of the 'PMA_exportData()' function + /** + * Outputs table structure + * + * @param string $db database name + * @param string $table table name + * @param string $crlf the end of line sequence + * @param string $error_url the url to go back in case of error + * @param string $export_mode 'create_table', 'triggers', 'create_view', + * 'stand_in' + * @param string $export_type 'server', 'database', 'table' + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $dates whether to include creation/update/check dates + * + * @return bool Whether it succeeded + */ + public function exportStructure( + $db, + $table, + $crlf, + $error_url, + $export_mode, + $export_type, + $do_relation = false, + $do_comments = false, + $do_mime = false, + $dates = false, + $aliases = array() + ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $pdf = $this->_getPdf(); + // getting purpose to show at top + switch($export_mode) { + case 'create_table': + $purpose = __('Table Structure'); + break; + case 'triggers': + $purpose = __('Triggers'); + break; + case 'create_view': + $purpose = __('View Structure'); + break; + case 'stand_in': + $purpose = __('Stand In'); + } // end switch + + $attr = array( + 'currentDb' => $db, 'currentTable' => $table, + 'dbAlias' => $db_alias, 'tableAlias' => $table_alias, + 'aliases' => $aliases, 'purpose' => $purpose + ); + $pdf->setAttributes($attr); + /** + * comment display set true as presently in pdf + * format, no option is present to take user input. + */ + $do_comments = true; + switch($export_mode) { + case 'create_table': + $pdf->getTableDef( + $db, $table, $do_relation, $do_comments, $do_mime, false, $aliases + ); + break; + case 'triggers': + $pdf->getTriggers($db, $table); + break; + case 'create_view': + $pdf->getTableDef( + $db, $table, $do_relation, $do_comments, $do_mime, false, $aliases + ); + break; + case 'stand_in': + /* export a stand-in definition to resolve view dependencies + * Yet to develop this function + * $pdf->getTableDefStandIn($db, $table, $crlf); + */ + } // end switch + + return true; + } + /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ diff --git a/libraries/plugins/export/PMA_ExportPdf.class.php b/libraries/plugins/export/PMA_ExportPdf.class.php index 39eb59cd74..fb606940a3 100644 --- a/libraries/plugins/export/PMA_ExportPdf.class.php +++ b/libraries/plugins/export/PMA_ExportPdf.class.php @@ -114,7 +114,8 @@ class PMA_ExportPdf extends PMA_PDF 0, $this->FontSizePt, __('Database:') . ' ' . $this->dbAlias . ', ' - . __('Table:') . ' ' . $this->tableAlias, + . __('Table:') . ' ' . $this->tableAlias . ', ' + . __('Purpose:') . ' ' . $this->purpose, 0, 1, 'L' ); $l = ($this->lMargin); @@ -273,6 +274,374 @@ class PMA_ExportPdf extends PMA_PDF $this->tMargin = $topMargin; } + /** + * Prints triggers + * + * @param string $db database name + * @param string $table table name + */ + public function getTriggers($db, $table) + { + $i=0; + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + foreach ($triggers as $trigger) { + $i++; break; + } + if ($i==0) return; //prevents printing blank trigger list for any table + + unset($this->tablewidths); + unset($this->colTitles); + unset($this->titleWidth); + unset($this->colFits); + unset($this->display_column); + unset($this->colAlign); + + /** + * Making table heading + * Keeping column width constant + */ + $this->colTitles[0] = __('Name'); + $this->tablewidths[0] = 90; + $this->colTitles[1] = __('Time'); + $this->tablewidths[1] = 80; + $this->colTitles[2] = __('Event'); + $this->tablewidths[2] = 40; + $this->colTitles[3] = __('Definition'); + $this->tablewidths[3] = 240; + + for ($columns_cnt = 0; $columns_cnt < 4; $columns_cnt++) { + $this->colAlign[$columns_cnt] = 'L'; + $this->display_column[$columns_cnt] = true; + } + + // Starting to fill table with required info + + $this->setY($this->tMargin); + $this->AddPage(); + $this->SetFont(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 = array(); + $maxpage = $this->page; + + $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + + foreach ($triggers as $trigger) { + $data [] = $trigger['name']; + $data [] = $trigger['action_timing']; + $data [] = $trigger['event_manipulation']; + $data [] = $trigger['definition']; + $this->page = $currpage; + // write the horizontal borders + $this->Line($l, $h, $fullwidth+$l, $h); + // write the content and remember the height of the highest col + foreach ($data as $col => $txt) { + $this->page = $currpage; + $this->SetXY($l, $h); + if ($this->tablewidths[$col] > 0) { + $this->MultiCell( + $this->tablewidths[$col], + $this->FontSizePt, + $txt, + 0, + $this->colAlign[$col] + ); + $l += $this->tablewidths[$col]; + } + + if (! isset($tmpheight[$row . '-' . $this->page])) { + $tmpheight[$row . '-' . $this->page] = 0; + } + if ($tmpheight[$row . '-' . $this->page] < $this->GetY()) { + $tmpheight[$row . '-' . $this->page] = $this->GetY(); + } + if ($this->page > $maxpage) { + $maxpage = $this->page; + } + } + // get the height we were in the last used page + $h = $tmpheight[$row . '-' . $maxpage]; + // set the "pointer" to the left margin + $l = $this->lMargin; + // set the $currpage to the last page + $currpage = $maxpage; + unset($data); + $row++; + } + // draw the borders + // we start adding a horizontal line on the last page + $this->page = $maxpage; + $this->Line($l, $h, $fullwidth+$l, $h); + // now we start at the top of the document and walk down + for ($i = $startpage; $i <= $maxpage; $i++) { + $this->page = $i; + $l = $this->lMargin; + $t = ($i == $startpage) ? $startheight : $this->tMargin; + $lh = ($i == $maxpage) ? $h : $this->h-$this->bMargin; + $this->Line($l, $t, $l, $lh); + foreach ($this->tablewidths as $width) { + $l += $width; + $this->Line($l, $t, $l, $lh); + } + } + // set it to the last page, if not it'll cause some problems + $this->page = $maxpage; + } + + /** + * Print $table's CREATE definition + * + * @param string $db the database name + * @param string $table the table name + * @param bool $do_relation whether to include relation comments + * @param bool $do_comments whether to include the pmadb-style column + * comments as comments in the structure; + * this is deprecated but the parameter is + * left here because export.php calls + * PMA_exportStructure() also for other + * export types which use this parameter + * @param bool $do_mime whether to include mime comments + * @param bool $view whether we're handling a view + * @param bool $aliases Aliases of db/table/columns + */ + public function getTableDef( + $db, + $table, + $do_relation, + $do_comments, + $do_mime, + $view = false, + $aliases = array() + ) { + // set $cfgRelation here, because there is a chance that it's modified + // since the class initialization + global $cfgRelation; + + unset($this->tablewidths); + unset($this->colTitles); + unset($this->titleWidth); + unset($this->colFits); + unset($this->display_column); + unset($this->colAlign); + + /** + * Gets fields properties + */ + $GLOBALS['dbi']->selectDb($db); + + /** + * All these three checks do_relation, do_comment and do_mime is + * not required. As presently all are set true by default. + * But when, methods to take user input will be developed, + * it will be of use + */ + // Check if we can use Relations + if ($do_relation) { + // Find which tables are related with the current one and write it in + // an array + $res_rel = PMA_getForeigners($db, $table); + + if ($res_rel && count($res_rel) > 0) { + $have_rel = true; + } else { + $have_rel = false; + } + } else { + $have_rel = false; + } // end if + + //column count and table heading + + $this->colTitles[0] = __('Column'); + $this->tablewidths[0] = 90; + $this->colTitles[1] = __('Type'); + $this->tablewidths[1] = 80; + $this->colTitles[2] = __('Null'); + $this->tablewidths[2] = 40; + $this->colTitles[3] = __('Default'); + $this->tablewidths[3] = 120; + + for ($columns_cnt = 0; $columns_cnt < 4; $columns_cnt++) { + $this->colAlign[$columns_cnt] = 'L'; + $this->display_column[$columns_cnt] = true; + } + + if ($do_relation && $have_rel) { + $this->colTitles[$columns_cnt] = __('Links to'); + $this->display_column[$columns_cnt] = true; + $this->colAlign[$columns_cnt] = 'L'; + $this->tablewidths[$columns_cnt] = 120; + $columns_cnt++; + } + if ($do_comments /*&& $cfgRelation['commwork']*/) { + $this->colTitles[$columns_cnt] = __('Comments'); + $this->display_column[$columns_cnt] = true; + $this->colAlign[$columns_cnt] = 'L'; + $this->tablewidths[$columns_cnt] = 120; + $columns_cnt++; + } + if ($do_mime && $cfgRelation['mimework']) { + $this->colTitles[$columns_cnt] = __('MIME'); + $this->display_column[$columns_cnt] = true; + $this->colAlign[$columns_cnt] = 'L'; + $this->tablewidths[$columns_cnt] = 120; + $columns_cnt++; + } + + // Starting to fill table with required info + + $this->setY($this->tMargin); + $this->AddPage(); + $this->SetFont(PMA_PDF_FONT, '', 9); + + // Now let's start to write the table structure + + if ($do_comments) { + $comments = PMA_getComments($db, $table); + } + if ($do_mime && $cfgRelation['mimework']) { + $mime_map = PMA_getMIME($db, $table, true); + } + + $columns = $GLOBALS['dbi']->getColumns($db, $table); + /** + * Get the unique keys in the table. + * Presently, this information is not used. We will have to find out + * way of displaying it. + */ + $unique_keys = array(); + $keys = $GLOBALS['dbi']->getTableIndexes($db, $table); + foreach ($keys as $key) { + if ($key['Non_unique'] == 0) { + $unique_keys[] = $key['Column_name']; + } + } + + // 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 = array(); + $maxpage = $this->page; + + // fun begin + foreach ($columns as $column) { + $extracted_columnspec + = PMA_Util::extractColumnSpec($column['Type']); + + $type = $extracted_columnspec['print_type']; + if (empty($type)) { + $type = ' '; + } + + if (! isset($column['Default'])) { + if ($column['Null'] != 'NO') { + $column['Default'] = 'NULL'; + } + } + $data [] = $column['Field']; + $data [] = $type; + $data [] = ($column['Null'] == '' || $column['Null'] == 'NO') + ? 'No' + : 'Yes'; + $data [] = isset($column['Default']) ? $column['Default'] : ''; + + $field_name = $column['Field']; + + if ($do_relation && $have_rel) { + $data [] = isset($res_rel[$field_name]) + ? $res_rel[$field_name]['foreign_table'] + . ' (' . $res_rel[$field_name]['foreign_field'] + . ')' + : ''; + } + if ($do_comments) { + $data [] = isset($comments[$field_name]) + ? $comments[$field_name] + : ''; + } + if ($do_mime) { + $data [] = isset($mime_map[$field_name]) + ? $mime_map[$field_name]['mimetype'] + : ''; + } + + $this->page = $currpage; + // write the horizontal borders + $this->Line($l, $h, $fullwidth+$l, $h); + // write the content and remember the height of the highest col + foreach ($data as $col => $txt) { + $this->page = $currpage; + $this->SetXY($l, $h); + if ($this->tablewidths[$col] > 0) { + $this->MultiCell( + $this->tablewidths[$col], + $this->FontSizePt, + $txt, + 0, + $this->colAlign[$col] + ); + $l += $this->tablewidths[$col]; + } + + if (! isset($tmpheight[$row . '-' . $this->page])) { + $tmpheight[$row . '-' . $this->page] = 0; + } + if ($tmpheight[$row . '-' . $this->page] < $this->GetY()) { + $tmpheight[$row . '-' . $this->page] = $this->GetY(); + } + if ($this->page > $maxpage) { + $maxpage = $this->page; + } + } + + // get the height we were in the last used page + $h = $tmpheight[$row . '-' . $maxpage]; + // set the "pointer" to the left margin + $l = $this->lMargin; + // set the $currpage to the last page + $currpage = $maxpage; + unset($data); + $row++; + + } + // draw the borders + // we start adding a horizontal line on the last page + $this->page = $maxpage; + $this->Line($l, $h, $fullwidth+$l, $h); + // now we start at the top of the document and walk down + for ($i = $startpage; $i <= $maxpage; $i++) { + $this->page = $i; + $l = $this->lMargin; + $t = ($i == $startpage) ? $startheight : $this->tMargin; + $lh = ($i == $maxpage) ? $h : $this->h-$this->bMargin; + $this->Line($l, $t, $l, $lh); + foreach ($this->tablewidths as $width) { + $l += $width; + $this->Line($l, $t, $l, $lh); + } + } + // set it to the last page, if not it'll cause some problems + $this->page = $maxpage; + } + /** * MySQL report * diff --git a/test/classes/plugin/export/PMA_ExportPdf_test.php b/test/classes/plugin/export/PMA_ExportPdf_test.php index 99ae1eb789..2e6025f172 100644 --- a/test/classes/plugin/export/PMA_ExportPdf_test.php +++ b/test/classes/plugin/export/PMA_ExportPdf_test.php @@ -13,7 +13,6 @@ require_once 'libraries/Theme.class.php'; require_once 'libraries/Config.class.php'; require_once 'libraries/php-gettext/gettext.inc'; require_once 'libraries/config.default.php'; -require_once 'libraries/properties/options/items/MessageOnlyPropertyItem.class.php'; require_once 'export.php'; /** * tests for ExportPdf class @@ -108,7 +107,8 @@ class PMA_ExportPdf_Test extends PHPUnit_Framework_TestCase ); $generalOptionsArray = $options->getProperties(); - $generalOptions = $generalOptionsArray[0]; + + $generalOptions = array_shift($generalOptionsArray); $this->assertInstanceOf( 'OptionsPropertyMainGroup', @@ -124,18 +124,6 @@ class PMA_ExportPdf_Test extends PHPUnit_Framework_TestCase $property = array_shift($generalProperties); - $this->assertInstanceOf( - 'MessageOnlyPropertyItem', - $property - ); - - $this->assertEquals( - 'explanation', - $property->getName() - ); - - $property = array_shift($generalProperties); - $this->assertInstanceOf( 'TextPropertyItem', $property @@ -146,12 +134,46 @@ class PMA_ExportPdf_Test extends PHPUnit_Framework_TestCase $property->getName() ); + $generalOptions = array_shift($generalOptionsArray); + + $this->assertInstanceOf( + 'OptionsPropertyMainGroup', + $generalOptions + ); + + $this->assertEquals( + 'dump_what', + $generalOptions->getName() + ); + + $this->assertEquals( + 'Dump table', + $generalOptions->getText() + ); + + $generalProperties = $generalOptions->getProperties(); + $property = array_shift($generalProperties); $this->assertInstanceOf( - 'HiddenPropertyItem', + 'RadioPropertyItem', $property ); + + $this->assertEquals( + 'structure_or_data', + $property->getName() + ); + + $this->assertEquals( + array( + 'structure' => __('structure'), + 'data' => __('data'), + 'structure_and_data' => __('structure and data') + ), + $property->getValues() + ); + } /**