diff --git a/db_operations.php b/db_operations.php index 53a282187..9bba859e2 100644 --- a/db_operations.php +++ b/db_operations.php @@ -520,15 +520,15 @@ if ($cfgRelation['pdfwork'] && $num_tables > 0) { ?> WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; $test_rs = PMA_query_as_controluser($test_query, null, PMA_DBI_QUERY_STORE); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - include('./libraries/display_pdf_schema.lib.php'); - } // end if - echo '
'; + /* + * Export Relational Schema View + */ + echo '
'; if ($cfg['PropertiesIconic']) { echo ''; } - echo __('Edit PDF Pages') . '
'; + echo __('Export Relational Schema View') . '
'; } // end if /** diff --git a/export_relation_schema.php b/export_relation_schema.php new file mode 100644 index 000000000..c2608f826 --- /dev/null +++ b/export_relation_schema.php @@ -0,0 +1,140 @@ +%s table not found or not set in %s'), 'relation', 'config.inc.php') . '
' . "\n" + . PMA_showDocu('relation') . "\n"; + require_once './libraries/footer.inc.php'; +} + +if (!$cfgRelation['displaywork']) { + echo sprintf(__('%s table not found or not set in %s'), 'table_info', 'config.inc.php') . '
' . "\n" + . PMA_showDocu('table_info') . "\n"; + require_once './libraries/footer.inc.php'; +} + +if (!isset($cfgRelation['table_coords'])){ + echo sprintf(__('%s table not found or not set in %s'), 'table_coords', 'config.inc.php') . '
' . "\n" + . PMA_showDocu('table_coords') . "\n"; + require_once './libraries/footer.inc.php'; +} +if (!isset($cfgRelation['pdf_pages'])) { + echo sprintf(__('%s table not found or not set in %s'), 'pdf_page', 'config.inc.php') . '
' . "\n" + . PMA_showDocu('pdf_pages') . "\n"; + require_once './libraries/footer.inc.php'; +} + +if ($cfgRelation['pdfwork']) { + + /** + * User object created for presenting the HTML options + * so, user can interact with it and perform export of relations schema + */ + + require_once './libraries/schema/User_Schema.class.php'; + $user_schema = new PMA_User_Schema(); + + /** + * This function will process the user defined pages + * and tables which will be exported as Relational schema + * you can set the table positions on the paper via scratchboard + * for table positions, put the x,y co-ordinates + * + * @param string $do It tells what the Schema is supposed to do + * create and select a page, generate schema etc + */ + if(isset($_REQUEST['do'])){ + $user_schema->setAction($_REQUEST['do']); + $user_schema->processUserPreferences(); + } + + /** + * Show some possibility to select a page for the export of relation schema + * Lists all pages created before and can select and edit from them + */ + + $user_schema->selectPage(); + + /** + * Create a new page where relations will be drawn + */ + + $user_schema->createPage($db); + + /** + * After selection of page or creating a page + * It will show you the list of tables + * A dashboard will also be shown where you can position the tables + */ + + $user_schema->showTableDashBoard(); + + if (isset($_REQUEST['do']) + && ($_REQUEST['do'] == 'edcoord' + || ($_REQUEST['do']== 'selectpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0) + || ($_REQUEST['do'] == 'createpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0))) { + + /** + * show Export schema generation options + */ + $user_schema->displaySchemaGenerationOptions(); + + if ((isset($showwysiwyg) && $showwysiwyg == '1')) { + ?> + + \ No newline at end of file diff --git a/handle_relation_schema.php b/handle_relation_schema.php new file mode 100644 index 000000000..9f74feaaa --- /dev/null +++ b/handle_relation_schema.php @@ -0,0 +1,49 @@ + -
-
- - '; - } - echo __('Display PDF schema'); - ?>: - - - -
- - - - - -
- -
- -
- -
- -
- -
- - -
- - - -
-
- -
-
- diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php new file mode 100644 index 000000000..26e7b926d --- /dev/null +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -0,0 +1,777 @@ + + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + +class PMA_DIA extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + /** + * The "PMA_DIA" constructor + * + * Upon instantiation This starts writing the Dia XML document + * + * @return void + * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument() + */ + function __construct() + { + $this->openMemory(); + /* + * Set indenting using three spaces, + * so output is formatted + */ + + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* + * Create the XML document + */ + + $this->startDocument('1.0','UTF-8'); + } + + /** + * Starts Dia Document + * + * dia document starts by first initializing dia:diagram tag + * then dia:diagramdata contains all the attributes that needed + * to define the document, then finally a Layer starts which + * holds all the objects. + * + * @param string paper The size of the paper/document + * @param float topMargin top margin of the paper/document in cm + * @param float bottomMargin bottom margin of the paper/document in cm + * @param float leftMargin left margin of the paper/document in cm + * @param float rightMargin right margin of the paper/document in cm + * @param string portrait document will be portrait or landscape + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::writeRaw() + */ + function startDiaDoc($paper,$topMargin,$bottomMargin,$leftMargin,$rightMargin,$portrait) + { + if($portrait == 'P'){ + $isPortrait='true'; + }else{ + $isPortrait='false'; + } + $this->startElement('dia:diagram'); + $this->writeAttribute('xmlns:dia', 'http://www.lysator.liu.se/~alla/dia/'); + $this->startElement('dia:diagramdata'); + $this->writeRaw ( + ' + + + + + + + + + #'.$paper.'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '); + $this->endElement(); + $this->startElement('dia:layer'); + $this->writeAttribute('name', 'Background'); + $this->writeAttribute('visible', 'true'); + $this->writeAttribute('active', 'true'); + + } + + /** + * Ends Dia Document + * + * @return void + * @access public + * @see XMLWriter::endElement(),XMLWriter::endDocument() + */ + function endDiaDoc() + { + $this->endElement(); + $this->endDocument(); + } + + /** + * Output Dia Document for download + * + * @param string fileName name of the dia document + * @return void + * @access public + * @see XMLWriter::flush() + */ + function showOutput($fileName) + { + if(ob_get_clean()){ + ob_end_clean(); + } + header('Content-type: application/x-dia-diagram'); + header('Content-Disposition: attachment; filename="'.$fileName.'.dia"'); + $output = $this->flush(); + print $output; + } +} + +/** + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in dia XML document. + * + * @name Table_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_DIA + */ +class Table_Stats +{ + /** + * Defines properties + */ + public $tableName; + public $fields = array(); + public $x, $y; + public $primary = array(); + public $tableId; + public $tableColor; + + /** + * The "Table_Stats" constructor + * + * @param string table_name The table name + * @param integer pageNumber The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @param boolean showKeys Whether to display ONLY keys or not + * @return void + * @global object The current dia document + * @global array The relations settings + * @global string The current db name + * @see PMA_DIA + */ + function __construct($tableName, $pageNumber, $showKeys = false) + { + global $dia, $cfgRelation, $db; + + $this->tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $dia->dieSchema($pageNumber,"DIA",sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $dia->dieSchema($pageNumber,"DIA",sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + /* + * displayfield + */ + $this->displayfield = PMA_getDisplayField($db, $tableName); + /* + * index + */ + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + /** + * Every object in Dia document needs an ID to identify + * so, we used a static variable to keep the things unique + */ + PMA_Dia_Relation_Schema::$objectId += 1; + $this->tableId = PMA_Dia_Relation_Schema::$objectId; + } + + /** + * Do draw the table + * + * Tables are generated using object type Database - Table + * primary fields are underlined in tables. Dia object + * is used to generate the XML of Dia Document. Database Table + * Object and their attributes are involved in the combination + * of displaing Database - Table on Dia Document. + + * @param boolean changeColor Whether to show color for tables text or not + if changeColor is true then an array of $listOfColors + will be used to choose the random colors for tables text + we can change/add more colors to this array + @return void + * @global object The current Dia document + * @access public + * @see PMA_DIA + */ + public function tableDraw($changeColor) + { + global $dia; + + if ($changeColor) { + $listOfColors = array( + 'FF0000', + '000099', + '00FF00' + ); + shuffle($listOfColors); + $this->tableColor = '#'.$listOfColors[0].''; + } else { + $this->tableColor = '#000000'; + } + + $dia->startElement('dia:object'); + $dia->writeAttribute('type', 'Database - Table'); + $dia->writeAttribute('version', '0'); + $dia->writeAttribute('id', ''.$this->tableId.''); + $dia->writeRaw( + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #'.$this->tableName.'# + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + ); + + $dia->startElement('dia:attribute'); + $dia->writeAttribute('name', 'attributes'); + + foreach ($this->fields as $field) { + $dia->writeRaw( + ' + + #'.$field.'# + + + ## + + + ## + ' + ); + unset($pm); + $pm = 'false'; + if (in_array($field, $this->primary)) { + $pm = 'true'; + } + if ($field == $this->displayfield) { + $pm = 'false'; + } + $dia->writeRaw( + ' + + + + + + + + + ' + ); + } + $dia->endElement(); + $dia->endElement(); + } +} + +/** + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in dia XML document. + * + * @name Relation_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_DIA + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $srcConnPointsRight; + public $srcConnPointsLeft; + public $destConnPointsRight; + public $destConnPointsLeft; + public $masterTableId; + public $foreignTableId; + public $masterTablePos; + public $foreignTablePos; + public $referenceColor; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @return void + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + $this->srcConnPointsLeft = $src_pos[0]; + $this->srcConnPointsRight = $src_pos[1]; + $this->destConnPointsLeft = $dest_pos[0]; + $this->destConnPointsRight = $dest_pos[1]; + $this->masterTablePos = $src_pos[2]; + $this->foreignTablePos = $dest_pos[2]; + $this->masterTableId = $master_table->tableId; + $this->foreignTableId = $foreign_table->tableId; + } + + /** + * Each Table object have connection points + * which is used to connect to other objects in Dia + * we detect the position of key in fields and + * then determines its left and right connection + * points. + * + * @param string table The current table name + * @param string column The relation column name + * @return array Table right,left connection points and key position + * @access private + */ + private function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // left, right, position + $value = 12; + if($pos != 0) + { + return array($pos + $value + $pos, $pos + $value + $pos + 1, $pos); + } + return array($pos + $value , $pos + $value + 1, $pos); + } + + /** + * Draws relation references + * + * connects master table's master field to foreign table's + * forein field using Dia object type Database - Reference + * Dia object is used to generate the XML of Dia Document. + * Database reference Object and their attributes are involved + * in the combination of displaing Database - reference on Dia Document. + * + * @param boolean changeColor Whether to use one color per relation or not + if changeColor is true then an array of $listOfColors + will be used to choose the random colors for references + lines. we can change/add more colors to this array + * @return void + * @global object The current Dia document + * @access public + * @see PMA_PDF + */ + public function relationDraw($changeColor) + { + global $dia; + + PMA_Dia_Relation_Schema::$objectId += 1; + /* + * if source connection points and destination connection + * points are same then return it false and don't draw that + * relation + */ + if ( $this->srcConnPointsRight == $this->destConnPointsRight ){ + if ( $this->srcConnPointsLeft == $this->destConnPointsLeft ){ + return false; + } + } + + if ($changeColor) { + $listOfColors = array( + 'FF0000', + '000099', + '00FF00' + ); + shuffle($listOfColors); + $this->referenceColor = '#'.$listOfColors[0].''; + } else { + $this->referenceColor = '#000000'; + } + + $dia->writeRaw( + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #1# + + + #n# + + + + + + + + + + + + ' + ); + } +} + +/** + * Dia Relation Schema Class + * + * Purpose of this class is to generate the Dia XML Document + * which is used for representing the database diagrams in Dia IDE + * This class uses Database Table and Reference Objects of Dia and with + * the combination of these objects actually helps in preparing Dia XML. + * + * Dia XML is generated by using XMLWriter php extension and this class + * inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Dia_Relation_Schema + * @author Muhammad Adnan + * @copyright + * @license + */ +class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema +{ + /** + * Defines properties + */ + private $_tables = array(); + private $_relations = array(); + private $_topMargin = 2.8222000598907471; + private $_bottomMargin = 2.8222000598907471; + private $_leftMargin = 2.8222000598907471; + private $_rightMargin = 2.8222000598907471; + public static $objectId = 0; + + /** + * The "PMA_Dia_Relation_Schema" constructor + * + * Upon instantiation This outputs the Dia XML document + * that user can download + * + * @return void + * @see PMA_DIA,Table_Stats,Relation_Stats + */ + function __construct() + { + global $dia,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowGrid(isset($_POST['show_grid'])); + $this->setShowColor($_POST['show_color']); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setOrientation(isset($_POST['orientation'])); + $this->setPaper($_POST['paper']); + $this->setExportType($_POST['export_type']); + + $dia = new PMA_DIA(); + $dia->startDiaDoc($this->paper,$this->_topMargin,$this->_bottomMargin,$this->_leftMargin,$this->_rightMargin,$this->orientation); + $alltables = $this->getAllTables($db,$this->pageNumber); + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table, $this->pageNumber, $this->showKeys); + } + } + + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'],$this->showKeys); + } + } + } + } + $this->_drawTables($this->showColor); + + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + $dia->endDiaDoc(); + $dia->showOutput($db.'-'.$this->pageNumber); + exit(); + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @return void + * @access private + * @see Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable, $masterField, $foreignTable, $foreignField, $showKeys) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $this->pageNumber, $showKeys); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable, $this->pageNumber, $showKeys); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation references + * + * connects master table's master field to + * foreign table's forein field using Dia object + * type Database - Reference + * + * @param boolean changeColor Whether to use one color per relation or not + * @return void + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * Tables are generated using Dia object type Database - Table + * primary fields are underlined and bold in tables + * + * @param boolean changeColor Whether to show color for tables text or not + * @return void + * @access private + * @see Table_Stats::tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } +} +?> \ No newline at end of file diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php new file mode 100644 index 000000000..e783cbd33 --- /dev/null +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -0,0 +1,858 @@ + + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + +class PMA_EPS +{ + public $font; + public $fontSize; + public $stringCommands; + + /** + * The "PMA_EPS" constructor + * + * Upon instantiation This starts writing the EPS Document. + * %!PS-Adobe-3.0 EPSF-3.0 This is the MUST first comment to include + * it shows/tells that the Post Script document is purely under + * Document Structuring Convention [DSC] and is Compliant + * Encapsulated Post Script Document + * + * @return void + * @access public + */ + function __construct() + { + $this->stringCommands = ""; + $this->stringCommands .= "%!PS-Adobe-3.0 EPSF-3.0 \n"; + } + + /** + * Set document title + * + * @param string value sets the title text + * @return void + * @access public + */ + function setTitle($value) + { + $this->stringCommands .= '%%Title: ' . $value . "\n"; + } + + /** + * Set document author + * + * @param string value sets the author + * @return void + * @access public + */ + function setAuthor($value) + { + $this->stringCommands .= '%%Creator: ' . $value . "\n"; + } + + /** + * Set document creation date + * + * @param string value sets the date + * @return void + * @access public + */ + function setDate($value) + { + $this->stringCommands .= '%%CreationDate: ' . $value . "\n"; + } + + /** + * Set document orientation + * + * @param string value sets the author + * @return void + * @access public + */ + function setOrientation($value) + { + $this->stringCommands .= "%%PageOrder: Ascend \n"; + if($value == "L"){ + $value = "Landscape"; + $this->stringCommands .= '%%Orientation: ' . $value . "\n"; + }else{ + $value = "Portrait"; + $this->stringCommands .= '%%Orientation: ' . $value . "\n"; + } + $this->stringCommands .= "%%EndComments \n"; + $this->stringCommands .= "%%Pages 1 \n"; + $this->stringCommands .= "%%BoundingBox: 72 150 144 170 \n"; + } + + /** + * Set the font and size + * + * font can be set whenever needed in EPS + * + * @param string value sets the font name e.g Arial + * @param integer value sets the size of the font e.g 10 + * @return void + * @access public + */ + function setFont($value,$size) + { + $this->font = $value; + $this->fontSize = $size; + $this->stringCommands .= "/".$value." findfont % Get the basic font\n"; + $this->stringCommands .= "".$size." scalefont % Scale the font to $size points\n"; + $this->stringCommands .= "setfont % Make it the current font\n"; + } + + /** + * Get the font + * + * @return string return the font name e.g Arial + * @access public + */ + function getFont() + { + return $this->font; + } + + /** + * Get the font Size + * + * @return string return the size of the font e.g 10 + * @access public + */ + function getFontSize() + { + return $this->fontSize; + } + + /** + * Draw the line + * + * drawing the lines from x,y source to x,y destination and set the + * width of the line. lines helps in showing relationships of tables + * + * @param integer x_from The x_from attribute defines the start + left position of the element + * @param integer y_from The y_from attribute defines the start + right position of the element + * @param integer x_to The x_to attribute defines the end + left position of the element + * @param integer y_to The y_to attribute defines the end + right position of the element + * @param integer lineWidth sets the width of the line e.g 2 + * @return void + * @access public + */ + function line($x_from=0, $y_from=0, $x_to=0, $y_to=0, $lineWidth=0) + { + $this->stringCommands .= $lineWidth . " setlinewidth \n"; + $this->stringCommands .= $x_from . ' ' . $y_from . " moveto \n"; + $this->stringCommands .= $x_to . ' ' . $y_to . " lineto \n"; + $this->stringCommands .= "stroke \n"; + } + + /** + * Draw the rectangle + * + * drawing the rectangle from x,y source to x,y destination and set the + * width of the line. rectangles drawn around the text shown of fields + * + * @param integer x_from The x_from attribute defines the start + left position of the element + * @param integer y_from The y_from attribute defines the start + right position of the element + * @param integer x_to The x_to attribute defines the end + left position of the element + * @param integer y_to The y_to attribute defines the end + right position of the element + * @param integer lineWidth sets the width of the line e.g 2 + * @return void + * @access public + */ + function rect($x_from, $y_from, $x_to, $y_to, $lineWidth) + { + $this->stringCommands .= $lineWidth . " setlinewidth \n"; + $this->stringCommands .= "newpath \n"; + $this->stringCommands .= $x_from . " " . $y_from . " moveto \n"; + $this->stringCommands .= "0 " . $y_to . " rlineto \n"; + $this->stringCommands .= $x_to . " 0 rlineto \n"; + $this->stringCommands .= "0 -" . $y_to . " rlineto \n"; + $this->stringCommands .= "closepath \n"; + $this->stringCommands .= "stroke \n"; + } + + /** + * Set the current point + * + * The moveto operator takes two numbers off the stack and treats + * them as x and y coordinates to which to move. The coordinates + * specified become the current point. + * + * @param integer x The x attribute defines the + left position of the element + * @param integer y The y attribute defines the + right position of the element + * @return void + * @access public + */ + function moveTo($x, $y) + { + $this->stringCommands .= $x . ' ' . $y . " moveto \n"; + } + + /** + * Output/Display the text + * + * @param string text The string to be displayed + * @return void + * @access public + */ + function show($text) + { + $this->stringCommands .= '(' . $text . ") show \n"; + } + + /** + * Output the text at specified co-ordinates + * + * @param string text The string to be displayed + * @param integer x The x attribute defines the + left position of the element + * @param integer y The y attribute defines the + right position of the element + * @return void + * @access public + */ + function showXY($text, $x, $y) + { + $this->moveTo($x, $y); + $this->show($text); + } + + /** + * get width of string/text + * + * EPS text width is calcualted depending on font name + * and font size. It is very important to know the width of text + * because rectangle is drawn around it. + * + * This is a bit hardcore method. I didn't found any other better than this. + * if someone found better than this. would love to hear that method + * + * @param string text string that width will be calculated + * @param integer font name of the font like Arial,sans-serif etc + * @param integer fontSize size of font + * @return integer width of the text + * @access public + */ + function getStringWidth($text,$font,$fontSize) + { + /* + * Start by counting the width, giving each character a modifying value + */ + $count = 0; + $count = $count + ((strlen($text) - strlen(str_replace(array("i","j","l"),"",$text)))*0.23);//ijl + $count = $count + ((strlen($text) - strlen(str_replace(array("f"),"",$text)))*0.27);//f + $count = $count + ((strlen($text) - strlen(str_replace(array("t","I"),"",$text)))*0.28);//tI + $count = $count + ((strlen($text) - strlen(str_replace(array("r"),"",$text)))*0.34);//r + $count = $count + ((strlen($text) - strlen(str_replace(array("1"),"",$text)))*0.49);//1 + $count = $count + ((strlen($text) - strlen(str_replace(array("c","k","s","v","x","y","z","J"),"",$text)))*0.5);//cksvxyzJ + $count = $count + ((strlen($text) - strlen(str_replace(array("a","b","d","e","g","h","n","o","p","q","u","L","0","2","3","4","5","6","7","8","9"),"",$text)))*0.56);//abdeghnopquL023456789 + $count = $count + ((strlen($text) - strlen(str_replace(array("F","T","Z"),"",$text)))*0.61);//FTZ + $count = $count + ((strlen($text) - strlen(str_replace(array("A","B","E","K","P","S","V","X","Y"),"",$text)))*0.67);//ABEKPSVXY + $count = $count + ((strlen($text) - strlen(str_replace(array("w","C","D","H","N","R","U"),"",$text)))*0.73);//wCDHNRU + $count = $count + ((strlen($text) - strlen(str_replace(array("G","O","Q"),"",$text)))*0.78);//GOQ + $count = $count + ((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM + $count = $count + ((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W + $count = $count + ((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" " + $text = str_replace(" ","",$text);//remove the " "'s + $count = $count + (strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs + + $modifier = 1; + $font = strtolower($font); + switch($font){ + /* + * no modifier for arial and sans-serif + */ + case 'arial': + case 'sans-serif': + break; + /* + * .92 modifer for time, serif, brushscriptstd, and californian fb + */ + case 'times': + case 'serif': + case 'brushscriptstd': + case 'californian fb': + $modifier = .92; + break; + /* + * 1.23 modifier for broadway + */ + case 'broadway': + $modifier = 1.23; + break; + } + $textWidth = $count*$fontSize; + return ceil($textWidth*$modifier); + } + + /** + * Ends EPS Document + * + * @return void + * @access public + */ + function endEpsDoc() + { + $this->stringCommands .= "showpage \n"; + } + + /** + * Output EPS Document for download + * + * @param string fileName name of the eps document + * @return void + * @access public + */ + function showOutput($fileName) + { + // if(ob_get_clean()){ + //ob_end_clean(); + //} + header('Content-type: image/x-eps'); + header('Content-Disposition: attachment; filename="'.$fileName.'.eps"'); + $output = $this->stringCommands; + print $output; + } +} + +/** + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in EPS. + * + * @name Table_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_EPS + */ +class Table_Stats +{ + /** + * Defines properties + */ + + private $_tableName; + private $_showInfo = false; + + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 0; + public $currentCell = 0; + public $x, $y; + public $primary = array(); + + /** + * The "Table_Stats" constructor + * + * @param string tableName The table name + * @param string font The font name + * @param integer fontSize The font size + * @param integer same_wide_width The max width among tables + * @param boolean showKeys Whether to display keys or not + * @param boolean showInfo Whether to display table position or not + * @global object The current eps document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_EPS, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) + { + global $eps, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $eps->dieSchema($pageNumber,"EPS",sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + + // height and width + $this->_setHeightTable($fontSize); + + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->_setWidthTable($font,$fontSize); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + $eps->dieSchema($pageNumber,"EPS",sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $tableName); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions/co-ordinates of the table + * + * @return string The relation/table name + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param string font The font name + * @param integer fontSize The font size + * @global object The current eps document + * @return void + * @access private + * @see PMA_EPS + */ + private function _setWidthTable($font,$fontSize) + { + global $eps; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $eps->getStringWidth($field,$font,$fontSize)); + } + $this->width += $eps->getStringWidth(' ',$font,$fontSize); + /* + * it is unknown what value must be added, because + * table title is affected by the tabe width value + */ + while ($this->width < $eps->getStringWidth($this->_getTitle(),$font,$fontSize)) { + $this->width += 7; + } + } + + /** + * Sets the height of the table + * + * @param integer fontSize The font size + * @return void + * @access private + */ + private function _setHeightTable($fontSize) + { + $this->heightCell = $fontSize + 4; + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * Draw the table + * + * @param boolean showColor Whether to display color + * @global object The current eps document + * @return void + * @access public + * @see PMA_EPS,PMA_EPS::line,PMA_EPS::rect + */ + public function tableDraw($showColor) + { + global $eps; + //echo $this->_tableName.'
'; + $eps->rect($this->x,$this->y + 12, + $this->width,$this->heightCell, + 1 + ); + $eps->showXY($this->_getTitle(),$this->x + 5,$this->y + 14); + foreach ($this->fields as $field) { + $this->currentCell += $this->heightCell; + $showColor = 'none'; + if ($showColor) { + if (in_array($field, $this->primary)) { + $showColor = '#0c0'; + } + if ($field == $this->displayfield) { + $showColor = 'none'; + } + } + $eps->rect($this->x,$this->y + 12 + $this->currentCell, + $this->width, $this->heightCell,1); + $eps->showXY($field, $this->x + 5, $this->y + 14 + $this->currentCell); + } + } +} + +/** + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in EPS document. + * + * @name Relation_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_EPS + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir ; + public $destDir; + public $xDest, $yDest; + public $wTick = 10; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2] + 10; + $this->yDest = $dest_pos[2] + 10; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + private function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @global object The current EPS document + * @access public + * @see PMA_EPS + */ + public function relationDraw($changeColor) + { + global $eps; + + if ($changeColor) { + $listOfColors = array( + 'red', + 'grey', + 'black', + 'yellow', + 'green', + 'cyan', + ' orange' + ); + shuffle($listOfColors); + $color = $listOfColors[0]; + } else { + $color = 'black'; + } + // draw a line like -- to foreign field + $eps->line($this->xSrc,$this->ySrc, + $this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + 1 + ); + // draw a line like -- to master field + $eps->line($this->xDest + $this->destDir * $this->wTick, $this->yDest, + $this->xDest, $this->yDest, + 1 + ); + // draw a line that connects to master field line and foreign field line + $eps->line($this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + $this->xDest + $this->destDir * $this->wTick, $this->yDest, + 1 + ); + $root2 = 2 * sqrt(2); + $eps->line($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc + $this->wTick / $root2 , + 1 + ); + $eps->line($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc - $this->wTick / $root2 , + 1 + ); + $eps->line($this->xDest + $this->destDir * $this->wTick / 2 , $this->yDest , + $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, + $this->yDest + $this->wTick / $root2 , + 1); + $eps->line($this->xDest + $this->destDir * $this->wTick / 2 , + $this->yDest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick , + $this->yDest - $this->wTick / $root2 , + 1 + ); + } +} +/* +* end of the "Relation_Stats" class +*/ + +/** + * EPS Relation Schema Class + * + * Purpose of this class is to generate the EPS Document + * which is used for representing the database diagrams. + * This class uses post script commands and with + * the combination of these commands actually helps in preparing EPS Document. + * + * This class inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Eps_Relation_Schema + * @author Muhammad Adnan + * @copyright + * @license + */ +class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema +{ + private $tables = array(); + private $_relations = array(); + + /** + * The "PMA_EPS_Relation_Schema" constructor + * + * Upon instantiation This starts writing the EPS document + * user will be prompted for download as .eps extension + * + * @return void + * @see PMA_EPS + */ + function __construct() + { + global $eps,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowColor(isset($_POST['show_color'])); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setTableDimension(isset($_POST['show_table_dimension'])); + $this->setAllTableSameWidth(isset($_POST['all_table_same_wide'])); + $this->setOrientation($_POST['orientation']); + $this->setExportType($_POST['export_type']); + + $eps = new PMA_EPS(); + $eps->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber)); + $eps->setAuthor('phpMyAdmin ' . PMA_VERSION); + $eps->setDate(date("j F Y, g:i a")); + $eps->setOrientation($this->orientation); + $eps->setFont('Verdana','10'); + + + + $alltables = $this->getAllTables($db,$this->pageNumber); + + foreach ($alltables AS $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table,$eps->getFont(),$eps->getFontSize(), $this->pageNumber, $this->_tablewidth, $this->showKeys, $this->tableDimension); + } + + if ($this->sameWide) { + $this->tables[$table]->width = $this->_tablewidth; + } + } + + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table,$eps->getFont(),$eps->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension); + } + } + } + } + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + + $this->_drawTables($this->showColor); + $eps->endEpsDoc(); + $eps->showOutput($db.'-'.$this->pageNumber); + exit(); + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @param boolean showInfo Whether to display table position or not + * @return void + * @access private + * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber, $this->_tablewidth, false, $showInfo); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber, $this->_tablewidth, false, $showInfo); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation arrows and lines + * connects master table's master field to + * foreign table's forein field + * + * @param boolean changeColor Whether to use one color per relation or not + * @return void + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * @param boolean changeColor Whether to show color for primary fields or not + * @return void + * @access private + * @see Table_Stats::Table_Stats_tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } +} +?> \ No newline at end of file diff --git a/libraries/schema/Export_Relation_Schema.class.php b/libraries/schema/Export_Relation_Schema.class.php new file mode 100644 index 000000000..b9115015a --- /dev/null +++ b/libraries/schema/Export_Relation_Schema.class.php @@ -0,0 +1,223 @@ + + * @copyright + * @license + */ + +class PMA_Export_Relation_Schema +{ + private $_pageTitle; + public $showGrid; + public $showColor; + public $tableDimension; + public $sameWide; + public $withDoc; + public $showKeys; + public $orientation; + public $paper; + public $pageNumber; + + /** + * Set Page Number + * + * @param integer value Page Number of the document to be created + * @return void + * @access public + */ + public function setPageNumber($value) + { + $this->pageNumber = isset($value) ? $value : 1; + } + + /** + * Set Show Grid + * + * @param boolean value show grid of the document or not + * @return void + * @access public + */ + public function setShowGrid($value) + { + $this->showGrid = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setShowColor($value) + { + $this->showColor = (isset($value) && $value == 'on') ? 1 : 0; + } + + /** + * Set Table Dimension + * + * @param boolean value show table co-ordinates or not + * @return void + * @access public + */ + public function setTableDimension($value) + { + $this->tableDimension = (isset($value) && $value == 'on') ? 1 : 0; + } + + /** + * Set same width of All Tables + * + * @param boolean value set same width of all tables or not + * @return void + * @access public + */ + public function setAllTableSameWidth($value) + { + $this->sameWide = (isset($value) && $value == 'on') ? 1 : 0; + } + + /** + * Set Data Dictionary + * + * @param boolean value show selected database data dictionary or not + * @return void + * @access public + */ + public function setWithDataDictionary($value) + { + $this->withDoc = (isset($value) && $value == 'on') ? 1 : 0; + } + + /** + * Set Show only keys + * + * @param boolean value show only keys or not + * @return void + * @access public + */ + public function setShowKeys($value) + { + $this->showKeys = (isset($value) && $value == 'on') ? 1 : 0; + } + + /** + * Set Orientation + * + * @param string value Orientation will be portrait or landscape + * @return void + * @access public + */ + public function setOrientation($value) + { + $this->orientation = (isset($value) && $value == 'P') ? 'P' : 'L'; + } + + /** + * Set type of paper + * + * @param string value paper type can be A4 etc + * @return void + * @access public + */ + public function setPaper($value) + { + $this->paper = isset($value) ? $value : 'A4'; + } + + /** + * Set title of the page + * + * @param string value title of the page displayed at top of the document + * @return void + * @access public + */ + public function setPageTitle($title) + { + $this->_pageTitle=$title; + } + + /** + * Set type of export relational schema + * + * @param string value can be pdf,svg,dia,visio,eps etc + * @return void + * @access public + */ + public function setExportType($value) + { + $this->exportType=$value; + } + + /** + * get all tables involved or included in page + * + * @param string db name of the database + * @param integer pageNumber page number whose tables will be fetched in an array + * @return Array an array of tables + * @access public + */ + public function getAllTables($db,$pageNumber) + { + global $cfgRelation; + // Get All tables + $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + + $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); + if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { + $this->_die('',__('No tables')); + } + while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { + $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); + } + return $alltables; + } + + /** + * Displays an error message + * + * @param integer pageNumber ID of the page choosen + * @param string type Schema Type + * @param string error_message the error mesage + * @global array the PMA configuration array + * @global integer the current server id + * @global string the current language + * @global string the charset to convert to + * @global string the current database name + * @global string the current charset + * @global string the current text direction + * @global string a localized string + * @global string an other localized string + * @access public + * @return void + */ + function dieSchema($pageNumber, $type = '', $error_message = '') + { + global $cfg; + global $server, $lang, $convcharset, $db; + global $charset, $text_dir; + + require_once './libraries/header.inc.php'; + echo "

" . __("SCHEMA ERROR: ") . $type ."

" . "\n"; + if (!empty($error_message)) { + $error_message = htmlspecialchars($error_message); + } + echo '

' . "\n"; + echo ' ' . $error_message . "\n"; + echo '

' . "\n"; + echo '' . __('Back') . ''; + echo "\n"; + require_once './libraries/footer.inc.php'; + exit(); + } +} +?> \ No newline at end of file diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php new file mode 100644 index 000000000..78e6387b5 --- /dev/null +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -0,0 +1,1352 @@ + + * @copyright + * @license + * @access public + * @see TCPDF + */ +class PMA_PDF extends TCPDF +{ + /** + * Defines properties + */ + var $_xMin; + var $_yMin; + var $leftMargin = 10; + var $topMargin = 10; + var $scale; + var $PMA_links; + var $Outlines = array(); + var $def_outlines; + var $Alias = array(); + var $widths; + + public function getFh() + { + return $this->fh; + } + + public function getFw() + { + return $this->fw; + } + + public function setCMargin($c_margin) + { + $this->cMargin = $c_margin; + } + + function SetAlias($name, $value) + { + $this->Alias[$name] = $value ; + } + + function _putpages() + { + if (count($this->Alias) > 0) { + $nb = $this->page; + foreach ($this->Alias as $alias => $value) { + for ($n = 1;$n <= $nb;$n++) + $this->pages[$n]=str_replace($alias, $value, $this->pages[$n]); + } + } + parent::_putpages(); + } + + // added because tcpdf for PHP 5 has a protected $buffer + public function getBuffer() + { + return $this->buffer; + } + + public function getState() + { + return $this->state; + } + + /** + * Sets the scaling factor, defines minimum coordinates and margins + * + * @param float scale The scaling factor + * @param float _xMin The minimum X coordinate + * @param float _yMin The minimum Y coordinate + * @param float leftMargin The left margin + * @param float topMargin The top margin + * @access public + */ + function PMA_PDF_setScale($scale = 1, $xMin = 0, $yMin = 0, $leftMargin = -1, $topMargin = -1) + { + $this->scale = $scale; + $this->_xMin = $xMin; + $this->_yMin = $yMin; + if ($this->leftMargin != -1) { + $this->leftMargin = $leftMargin; + } + if ($this->topMargin != -1) { + $this->topMargin = $topMargin; + } + } + + /** + * Outputs a scaled cell + * + * @param float w The cell width + * @param float h The cell height + * @param string txt The text to output + * @param mixed border Whether to add borders or not + * @param integer ln Where to put the cursor once the output is done + * @param string align Align mode + * @param integer fill Whether to fill the cell with a color or not + * @access public + * @see TCPDF::Cell() + */ + function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') + { + $h = $h / $this->scale; + $w = $w / $this->scale; + $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); + } + + /** + * Draws a scaled line + * + * @param float x1 The horizontal position of the starting point + * @param float y1 The vertical position of the starting point + * @param float x2 The horizontal position of the ending point + * @param float y2 The vertical position of the ending point + * @access public + * @see TCPDF::Line() + */ + function PMA_PDF_lineScale($x1, $y1, $x2, $y2) + { + $x1 = ($x1 - $this->_xMin) / $this->scale + $this->leftMargin; + $y1 = ($y1 - $this->_yMin) / $this->scale + $this->topMargin; + $x2 = ($x2 - $this->_xMin) / $this->scale + $this->leftMargin; + $y2 = ($y2 - $this->_yMin) / $this->scale + $this->topMargin; + $this->Line($x1, $y1, $x2, $y2); + } + + /** + * Sets x and y scaled positions + * + * @param float x The x position + * @param float y The y position + * @access public + * @see TCPDF::SetXY() + */ + function PMA_PDF_setXyScale($x, $y) + { + $x = ($x - $this->_xMin) / $this->scale + $this->leftMargin; + $y = ($y - $this->_yMin) / $this->scale + $this->topMargin; + $this->SetXY($x, $y); + } + + /** + * Sets the X scaled positions + * + * @param float x The x position + * @access public + * @see TCPDF::SetX() + */ + function PMA_PDF_setXScale($x) + { + $x = ($x - $this->_xMin) / $this->scale + $this->leftMargin; + $this->SetX($x); + } + + /** + * Sets the scaled font size + * + * @param float size The font size (in points) + * @access public + * @see TCPDF::SetFontSize() + */ + function PMA_PDF_setFontSizeScale($size) + { + // Set font size in points + $size = $size / $this->scale; + $this->SetFontSize($size); + } + + /** + * Sets the scaled line width + * + * @param float width The line width + * @access public + * @see TCPDF::SetLineWidth() + */ + function PMA_PDF_setLineWidthScale($width) + { + $width = $width / $this->scale; + $this->SetLineWidth($width); + } + + /** + * Displays an error message + * + * @param string error_message the error mesage + * @access public + * @see PMA_Export_Relation_Schema::dieSchema + */ + function Error($error_message = '') + { + PMA_Export_Relation_Schema::dieSchema($error_message); + } + + function Header() + { + // We only show this if we find something in the new pdf_pages table + + // This function must be named "Header" to work with the TCPDF library + global $cfgRelation, $db, $pdf_page_number, $with_doc; + if ($with_doc) { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . $pdf_page_number . '\''; + $test_rs = PMA_query_as_controluser($test_query); + $pages = @PMA_DBI_fetch_assoc($test_rs); + $this->SetFont('', 'B', 14); + $this->Cell(0, 6, ucfirst($pages['page_descr']), 'B', 1, 'C'); + $this->SetFont('', ''); + $this->Ln(); + } + } + + function Footer() + { + // This function must be named "Footer" to work with the TCPDF library + global $with_doc; + if ($with_doc) { + $this->SetY(-15); + $this->SetFont('', '', 14); + $this->Cell(0, 6, __('Page number:') . ' ' . $this->PageNo() . '/{nb}', 'T', 0, 'C'); + $this->Cell(0, 6, PMA_localisedDate(), 0, 1, 'R'); + $this->SetY(20); + } + } + + function Bookmark($txt, $level = 0, $y = 0) + { + // Add a bookmark + $this->Outlines[0][] = $level; + $this->Outlines[1][] = $txt; + $this->Outlines[2][] = $this->page; + if ($y == -1) { + $y = $this->GetY(); + } + $this->Outlines[3][] = round($this->hPt - $y * $this->k, 2); + } + + function _putbookmarks() + { + if (count($this->Outlines) > 0) { + // Save object number + $memo_n = $this->n; + // Take the number of sub elements for an outline + $nb_outlines = sizeof($this->Outlines[0]); + $first_level = array(); + $parent = array(); + $parent[0] = 1; + for ($i = 0; $i < $nb_outlines; $i++) { + $level = $this->Outlines[0][$i]; + $kids = 0; + $last = -1; + $prev = -1; + $next = -1; + if ($i > 0) { + $cursor = $i-1; + // Take the previous outline in the same level + while ($this->Outlines[0][$cursor] > $level && $cursor > 0) + $cursor--; + if ($this->Outlines[0][$cursor] == $level) { + $prev = $cursor; + } + } + if ($i < $nb_outlines-1) { + $cursor = $i + 1; + while (isset($this->Outlines[0][$cursor]) && $this->Outlines[0][$cursor] > $level) { + // Take the immediate kid in level + 1 + if ($this->Outlines[0][$cursor] == $level + 1) { + $kids++; + $last = $cursor; + } + $cursor++; + } + $cursor = $i + 1; + // Take the next outline in the same level + while ($this->Outlines[0][$cursor] > $level && ($cursor + 1 < sizeof($this->Outlines[0]))) + $cursor++; + if ($this->Outlines[0][$cursor] == $level) { + $next = $cursor; + } + } + $this->_newobj(); + $parent[$level + 1] = $this->n; + if ($level == 0) { + $first_level[] = $this->n; + } + $this->_out('<<'); + $this->_out('/Title (' . $this->Outlines[1][$i] . ')'); + $this->_out('/Parent ' . $parent[$level] . ' 0 R'); + if ($prev != -1) { + $this->_out('/Prev ' . ($memo_n + $prev + 1) . ' 0 R'); + } + if ($next != -1) { + $this->_out('/Next ' . ($this->n + $next - $i) . ' 0 R'); + } + $this->_out('/Dest [' . (1 + (2 * $this->Outlines[2][$i])) . ' 0 R /XYZ null ' . $this->Outlines[3][$i] . ' null]'); + if ($kids > 0) { + $this->_out('/First ' . ($this->n + 1) . ' 0 R'); + $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); + $this->_out('/Count -' . $kids); + } + $this->_out('>>'); + $this->_out('endobj'); + } + // First page of outlines + $this->_newobj(); + $this->def_outlines = $this->n; + $this->_out('<<'); + $this->_out('/Type'); + $this->_out('/Outlines'); + $this->_out('/First ' . $first_level[0] . ' 0 R'); + $this->_out('/Last ' . $first_level[sizeof($first_level)-1] . ' 0 R'); + $this->_out('/Count ' . sizeof($first_level)); + $this->_out('>>'); + $this->_out('endobj'); + } + } + + function _putresources() + { + parent::_putresources(); + $this->_putbookmarks(); + } + + function _putcatalog() + { + parent::_putcatalog(); + if (count($this->Outlines) > 0) { + $this->_out('/Outlines ' . $this->def_outlines . ' 0 R'); + $this->_out('/PageMode /UseOutlines'); + } + } + + function SetWidths($w) + { + // column widths + $this->widths = $w; + } + + function Row($data, $links) + { + // line height + $nb = 0; + $data_cnt = count($data); + for ($i = 0;$i < $data_cnt;$i++) + $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); + $il = $this->FontSize; + $h = ($il + 1) * $nb; + // page break if necessary + $this->CheckPageBreak($h); + // draw the cells + $data_cnt = count($data); + for ($i = 0;$i < $data_cnt;$i++) { + $w = $this->widths[$i]; + // save current position + $x = $this->GetX(); + $y = $this->GetY(); + // draw the border + $this->Rect($x, $y, $w, $h); + if (isset($links[$i])) { + $this->Link($x, $y, $w, $h, $links[$i]); + } + // print text + $this->MultiCell($w, $il + 1, $data[$i], 0, 'L'); + // go to right side + $this->SetXY($x + $w, $y); + } + // go to line + $this->Ln($h); + } + + function CheckPageBreak($h) + { + // if height h overflows, manual page break + if ($this->GetY() + $h > $this->PageBreakTrigger) { + $this->AddPage($this->CurOrientation); + } + } + + function NbLines($w, $txt) + { + // compute number of lines used by a multicell of width w + $cw = &$this->CurrentFont['cw']; + if ($w == 0) { + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w-2 * $this->cMargin) * 1000 / $this->FontSize; + $s = str_replace("\r", '', $txt); + $nb = strlen($s); + if ($nb > 0 and $s[$nb-1] == "\n") { + $nb--; + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $nl = 1; + while ($i < $nb) { + $c = $s[$i]; + if ($c == "\n") { + $i++; + $sep = -1; + $j = $i; + $l = 0; + $nl++; + continue; + } + if ($c == ' ') { + $sep = $i; + } + $l += isset($cw[ord($c)])?$cw[ord($c)]:0 ; + if ($l > $wmax) { + if ($sep == -1) { + if ($i == $j) { + $i++; + } + } else { + $i = $sep + 1; + } + $sep = -1; + $j = $i; + $l = 0; + $nl++; + } else { + $i++; + } + } + return $nl; + } +} + +/** + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in PDF document. + * + * @name Table_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_PDF + */ +class Table_Stats +{ + /** + * Defines properties + */ + private $_tableName; + private $_showInfo = false; + + public $nb_fiels; + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 6; + public $x, $y; + public $primary = array(); + + /** + * The "Table_Stats" constructor + * + * @param string table_name The table name + * @param integer fontSize The font size + * @param integer pageNumber The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @param integer sameWideWidth The max. with among tables + * @param boolean showKeys Whether to display keys or not + * @param boolean showInfo Whether to display table position or not + * @global object The current PDF document + * @global array The relations settings + * @global string The current db name + * @see PMA_PDF, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $fontSize, $pageNumber, &$sameWideWidth, $showKeys = false, $showInfo = false) + { + global $pdf, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $pdf->Error(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + // load fields + //check to see if it will load all fields or only the foreign keys + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + $this->_setHeight(); + /* + * setWidth must me after setHeight, because title + * can include table height which changes table width + */ + $this->_setWidth($fontSize); + if ($sameWideWidth < $this->width) { + $sameWideWidth = $this->width; + } + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $pdf->Error(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + /* + * displayfield + */ + $this->displayfield = PMA_getDisplayField($db, $tableName); + /* + * index + */ + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions of the table + * + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param integer fontSize The font size + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function _setWidth($fontSize) + { + global $pdf; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $pdf->GetStringWidth($field)); + } + $this->width += $pdf->GetStringWidth(' '); + $pdf->SetFont($fontSize, 'B'); + /* + * it is unknown what value must be added, because + * table title is affected by the tabe width value + */ + while ($this->width < $pdf->GetStringWidth($this->_getTitle())) { + $this->width += 5; + } + $pdf->SetFont($fontSize, ''); + } + + /** + * Sets the height of the table + * + * @access private + */ + private function _setHeight() + { + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * Do draw the table + * + * @param integer fontSize The font size + * @param boolean setColor Whether to display color + * @global object The current PDF document + * @access public + * @see PMA_PDF + */ + public function tableDraw($fontSize, $withDoc, $setColor = 0) + { + global $pdf, $withDoc; + + $pdf->PMA_PDF_setXyScale($this->x, $this->y); + $pdf->SetFont($fontSize, 'B'); + if ($setColor) { + $pdf->SetTextColor(200); + $pdf->SetFillColor(0, 0, 128); + } + if ($withDoc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->_tableName]['-'], -1); + } else { + $pdf->PMA_links['doc'][$this->_tableName]['-'] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->heightCell, $this->_getTitle(), 1, 1, 'C', $setColor, $pdf->PMA_links['doc'][$this->_tableName]['-']); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFont($fontSize, ''); + $pdf->SetTextColor(0); + $pdf->SetFillColor(255); + + foreach ($this->fields as $field) { + if ($setColor) { + if (in_array($field, $this->primary)) { + $pdf->SetFillColor(215, 121, 123); + } + if ($field == $this->displayfield) { + $pdf->SetFillColor(142, 159, 224); + } + } + if ($withDoc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->_tableName][$field], -1); + } else { + $pdf->PMA_links['doc'][$this->_tableName][$field] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->heightCell, ' ' . $field, 1, 1, 'L', $setColor, $pdf->PMA_links['doc'][$this->_tableName][$field]); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFillColor(255); + } + /*if ($pdf->PageNo() > 1) { + $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); + } */ + } +} + +/** + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in PDF document. + * + * @name Relation_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_PDF::SetDrawColor,PMA_PDF::PMA_PDF_setLineWidthScale,PMA_PDF::PMA_PDF_lineScale + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir; + public $destDir; + public $xDest, $yDest; + public $wTick = 5; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->yDest = $dest_pos[2]; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + private function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @param integer i The id of the link to draw + * @global object The current PDF document + * @access public + * @see PMA_PDF + */ + public function relationDraw($changeColor, $i) + { + global $pdf; + + if ($changeColor) { + $d = $i % 6; + $j = ($i - $d) / 6; + $j = $j % 4; + $j++; + $case = array( + array(1, 0, 0), + array(0, 1, 0), + array(0, 0, 1), + array(1, 1, 0), + array(1, 0, 1), + array(0, 1, 1) + ); + list ($a, $b, $c) = $case[$d]; + $e = (1 - ($j - 1) / 6); + $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); + } else { + $pdf->SetDrawColor(0); + } + $pdf->PMA_PDF_setLineWidthScale(0.2); + $pdf->PMA_PDF_lineScale($this->xSrc, $this->ySrc, $this->xSrc + $this->srcDir * $this->wTick, $this->ySrc); + $pdf->PMA_PDF_lineScale($this->xDest + $this->destDir * $this->wTick, $this->yDest, $this->xDest, $this->yDest); + $pdf->PMA_PDF_setLineWidthScale(0.1); + $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick, $this->ySrc, $this->xDest + $this->destDir * $this->wTick, $this->yDest); + /* + * Draws arrows -> + */ + $root2 = 2 * sqrt(2); + $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick, $this->ySrc + $this->wTick / $root2); + $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick, $this->ySrc - $this->wTick / $root2); + + $pdf->PMA_PDF_lineScale($this->xDest + $this->destDir * $this->wTick / 2, $this->yDest, $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, $this->yDest + $this->wTick / $root2); + $pdf->PMA_PDF_lineScale($this->xDest + $this->destDir * $this->wTick / 2, $this->yDest, $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, $this->yDest - $this->wTick / $root2); + $pdf->SetDrawColor(0); + } +} + +/** + * Pdf Relation Schema Class + * + * Purpose of this class is to generate the PDF Document. PDF is widely + * used format for documenting text,fonts,images and 3d vector graphics. + * + * This class inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Pdf_Relation_Schema + * @author Muhammad Adnan + * @copyright + * @license + */ +class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema +{ + /** + * Defines properties + */ + private $_tables = array(); + private $_relations = array(); + private $_ff = PMA_PDF_FONT; + private $_xMax = 0; + private $_yMax = 0; + private $scale; + private $_xMin = 100000; + private $_yMin = 100000; + private $topMargin = 10; + private $bottomMargin = 10; + private $leftMargin = 10; + private $rightMargin = 10; + private $_tablewidth; + + /** + * The "PMA_Pdf_Relation_Schema" constructor + * + * @global object The current PDF Schema document + * @global string The current db name + * @global array The relations settings + * @access private + * @see PMA_PDF + */ + function __construct() + { + global $pdf,$db,$cfgRelation; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowGrid(isset($_POST['show_grid'])); + $this->setShowColor(isset($_POST['show_color'])); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setTableDimension(isset($_POST['show_table_dimension'])); + $this->setAllTableSameWidth(isset($_POST['all_table_same_wide'])); + $this->setWithDataDictionary($_POST['with_doc']); + $this->setOrientation($_POST['orientation']); + $this->setPaper($_POST['paper']); + $this->setExportType($_POST['export_type']); + + // Initializes a new document + $pdf = new PMA_PDF('L', 'mm', $this->paper); + $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $this->pageNumber)); + $pdf->setCMargin(0); + $pdf->Open(); + $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $pdf->AliasNbPages(); + $pdf->AddFont('DejaVuSans', '', 'dejavusans.php'); + $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); + $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); + $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); + $pdf->SetFont($this->_ff, '', 14); + $pdf->SetAutoPageBreak('auto'); + $alltables = $this->getAllTables($db,$this->pageNumber); + + if ($this->withDoc) { + $pdf->SetAutoPageBreak('auto', 15); + $pdf->setCMargin(1); + $this->dataDictionaryDoc($alltables); + $pdf->SetAutoPageBreak('auto'); + $pdf->setCMargin(0); + } + + $pdf->Addpage(); + + if ($this->withDoc) { + $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); + $pdf->Bookmark(__('Relational schema')); + $pdf->SetAlias('{00}', $pdf->PageNo()) ; + $this->topMargin = 28; + $this->bottomMargin = 28; + } + + /* snip */ + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table, $this->_ff, $this->pageNumber, $this->_tablewidth, $this->showKeys, $this->tableDimension); + } + if ($this->sameWide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->_setMinMax($this->tables[$table]); + } + + // Defines the scale factor + $this->scale = ceil( + max( + ($this->_xMax - $this->_xMin) / ($pdf->getFh() - $this->rightMargin - $this->leftMargin), + ($this->_yMax - $this->_yMin) / ($pdf->getFw() - $this->topMargin - $this->bottomMargin)) + * 100) / 100; + + $pdf->PMA_PDF_setScale($this->scale, $this->_xMin, $this->_yMin, $this->leftMargin, $this->topMargin); + // Builds and save the PDF document + $pdf->PMA_PDF_setLineWidthScale(0.1); + + if ($this->showGrid) { + $pdf->SetFontSize(10); + $this->_strokeGrid(); + } + $pdf->PMA_PDF_setFontSizeScale(14); + // previous logic was checking master tables and foreign tables + // but I think that looping on every table of the pdf page as a master + // and finding its foreigns is OK (then we can support innodb) + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + // put the foreign table on the schema only if selected + // by the user + // (do not use array_search() because we would have to + // to do a === FALSE and this is not PHP3 compatible) + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension); + } + } // end while + } // end if + } // end while + + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + $this->_drawTables($this->showColor); + $this->_showOutput($this->pageNumber); + exit(); + } + + /** + * Sets X and Y minimum and maximum for a table cell + * + * @param string table The table name of which sets XY co-ordinates + * @access private + */ + private function _setMinMax($table) + { + $this->_xMax = max($this->_xMax, $table->x + $table->width); + $this->_yMax = max($this->_yMax, $table->y + $table->height); + $this->_xMin = min($this->_xMin, $table->x); + $this->_yMin = min($this->_yMin, $table->y); + } + + /** + * Defines relation objects + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreign_field The relation field in the foreign table + * @param boolean show_info Whether to display table position or not + * @access private + * @see _setMinMax + */ + private function _addRelation($masterTable, $masterField, $foreignTable, $foreignField, $showInfo) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $this->_ff, $this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$masterTable]); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable, $this->_ff, $this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$foreignTable]); + } + $this->relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws the grid + * + * @global object the current PMA_PDF instance + * @access private + * @see PMA_PDF + */ + private function _strokeGrid() + { + global $pdf; + + $pdf->SetMargins(0, 0); + $pdf->SetDrawColor(200, 200, 200); + // Draws horizontal lines + for ($l = 0; $l < 21; $l++) { + $pdf->line(0, $l * 10, $pdf->getFh(), $l * 10); + // Avoid duplicates + if ($l > 0) { + $pdf->SetXY(0, $l * 10); + $label = (string) sprintf('%.0f', ($l * 10 - $this->topMargin) * $this->scale + $this->_yMin); + $pdf->Cell(5, 5, ' ' . $label); + } // end if + } // end for + // Draws vertical lines + for ($j = 0; $j < 30 ;$j++) { + $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); + $pdf->SetXY($j * 10, 0); + $label = (string) sprintf('%.0f', ($j * 10 - $this->leftMargin) * $this->scale + $this->_xMin); + $pdf->Cell(5, 7, $label); + } + } + + /** + * Draws relation arrows + * + * @param boolean changeColor Whether to use one color per relation or not + * @access private + * @see Relation_Stats::relationdraw() + */ + private function _drawRelations($changeColor) + { + $i = 0; + foreach ($this->relations as $relation) { + $relation->relationDraw($changeColor, $i); + $i++; + } + } + + /** + * Draws tables + * + * @param boolean changeColor Whether to display table position or not + * @access private + * @see Table_Stats::tableDraw() + */ + private function _drawTables($changeColor = 0) + { + foreach ($this->tables as $table) { + $table->tableDraw($this->_ff, $this->withDoc, $changeColor); + } + } + + /** + * Ouputs the PDF document to a file + * or sends the output to browser + * + * @global object The current PDF document + * @global string The current database name + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @access private + * @see PMA_PDF + */ + private function _showOutput($pageNumber) + { + global $pdf, $db, $cfgRelation; + + $pdf->SetFontSize(14); + $pdf->SetLineWidth(0.2); + $pdf->SetDisplayMode('fullpage'); + // Get the name of this pdfpage to use as filename (Mike Beck) + $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE page_nr = ' . $pageNumber; + $_name_rs = PMA_query_as_controluser($_name_sql); + if ($_name_rs) { + $_name_row = PMA_DBI_fetch_row($_name_rs); + $filename = $_name_row[0] . '.pdf'; + } + if (empty($filename)) { + $filename = $pageNumber . '.pdf'; + } + // instead of $pdf->Output(): + $pdfData = $pdf->getPDFData(); + header('Content-Type: application/pdf'); + header('Content-Length: '.strlen($pdfData).''); + header('Content-disposition: attachment; filename="'.$filename.'"'); + echo $pdfData; + } + + public function dataDictionaryDoc($alltables) + { + global $db, $pdf, $orientation, $paper; + // TOC + $pdf->addpage($GLOBALS['orientation']); + $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); + $pdf->Ln(15); + $i = 1; + foreach ($alltables as $table) { + $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); + $pdf->SetX(10); + // $pdf->Ln(1); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); + // $pdf->Ln(1); + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); + while ($row = PMA_DBI_fetch_assoc($result)) { + $pdf->SetX(20); + $field_name = $row['Field']; + $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); + // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); + } + $lasttable = $table; + $i++; + } + $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); + $pdf->SetX(10); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); + $z = 0; + foreach ($alltables as $table) { + $z++; + $pdf->addpage($GLOBALS['orientation']); + $pdf->Bookmark($table); + $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; + $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); + $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); + $pdf->SetFont('', 'B', 18); + $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); + $pdf->SetFont('', '', 8); + $pdf->ln(); + + $cfgRelation = PMA_getRelationsParam(); + $comments = PMA_getComments($db, $table); + if ($cfgRelation['mimework']) { + $mime_map = PMA_getMIME($db, $table, true); + } + + /** + * Gets table informations + */ + $showtable = PMA_Table::sGetStatusInfo($db, $table); + $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); + $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); + $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); + $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); + $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); + + /** + * Gets table keys and retains them + */ + $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); + $primary = ''; + $indexes = array(); + $lastIndex = ''; + $indexes_info = array(); + $indexes_data = array(); + $pk_array = array(); // will be use to emphasis prim. keys in the table + // view + while ($row = PMA_DBI_fetch_assoc($result)) { + // Backups the list of primary keys + if ($row['Key_name'] == 'PRIMARY') { + $primary .= $row['Column_name'] . ', '; + $pk_array[$row['Column_name']] = 1; + } + // Retains keys informations + if ($row['Key_name'] != $lastIndex) { + $indexes[] = $row['Key_name']; + $lastIndex = $row['Key_name']; + } + $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; + $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; + if (isset($row['Cardinality'])) { + $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; + } + // I don't know what does following column mean.... + // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; + $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; + + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; + if (isset($row['Sub_part'])) { + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; + } + } // end while + if ($result) { + PMA_DBI_free_result($result); + } + + /** + * Gets fields properties + */ + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); + $fields_cnt = PMA_DBI_num_rows($result); + // Check if we can use Relations (Mike Beck) + if (!empty($cfgRelation['relation'])) { + // Find which tables are related with the current one and write it in + // an array + $res_rel = PMA_getForeigners($db, $table); + + if (count($res_rel) > 0) { + $have_rel = true; + } else { + $have_rel = false; + } + } else { + $have_rel = false; + } // end if + + /** + * Displays the comments of the table if MySQL >= 3.23 + */ + + $break = false; + if (!empty($show_comment)) { + $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); + $break = true; + } + + if (!empty($create_time)) { + $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); + $break = true; + } + + if (!empty($update_time)) { + $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); + $break = true; + } + + if (!empty($check_time)) { + $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); + $break = true; + } + + if ($break == true) { + $pdf->Cell(0, 3, '', 0, 1); + $pdf->Ln(); + } + + $pdf->SetFont('', 'B'); + if (isset($orientation) && $orientation == 'L') { + $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); + + if ($paper == 'A4') { + $comments_width = 67; + } else { + // this is really intended for 'letter' + /** + * @todo find optimal width for all formats + */ + $comments_width = 50; + } + $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); + } else { + $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); + } + $pdf->SetFont('', ''); + + while ($row = PMA_DBI_fetch_assoc($result)) { + $type = $row['Type']; + // reformat mysql query output + // set or enum types: slashes single quotes inside options + if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { + $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); + $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; + $type_nowrap = ''; + + $binary = 0; + $unsigned = 0; + $zerofill = 0; + } else { + $type_nowrap = ' nowrap="nowrap"'; + $type = preg_replace('@BINARY@i', '', $type); + $type = preg_replace('@ZEROFILL@i', '', $type); + $type = preg_replace('@UNSIGNED@i', '', $type); + if (empty($type)) { + $type = ' '; + } + + $binary = stristr($row['Type'], 'BINARY'); + $unsigned = stristr($row['Type'], 'UNSIGNED'); + $zerofill = stristr($row['Type'], 'ZEROFILL'); + } + $attribute = ' '; + if ($binary) { + $attribute = 'BINARY'; + } + if ($unsigned) { + $attribute = 'UNSIGNED'; + } + if ($zerofill) { + $attribute = 'UNSIGNED ZEROFILL'; + } + if (!isset($row['Default'])) { + if ($row['Null'] != '' && $row['Null'] != 'NO') { + $row['Default'] = 'NULL'; + } + } + $field_name = $row['Field']; + // $pdf->Ln(); + $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); + $pdf->Bookmark($field_name, 1, -1); + $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); + $pdf_row = array($field_name, + $type, + $attribute, + ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), + ((isset($row['Default'])) ? $row['Default'] : ''), + $row['Extra'], + ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), + ((isset($comments[$field_name])) ? $comments[$field_name] : ''), + ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') + ); + $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; + if (isset($res_rel[$field_name]['foreign_table']) AND + isset($res_rel[$field_name]['foreign_field']) AND + isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) + ) + { + $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; + } else { + unset($links[6]); + } + $pdf->Row($pdf_row, $links); + } // end while + $pdf->SetFont('', '', 14); + PMA_DBI_free_result($result); + } //end each + } +} +?> \ No newline at end of file diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php new file mode 100644 index 000000000..03422cb8f --- /dev/null +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -0,0 +1,854 @@ + + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + +class PMA_SVG extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + /** + * The "PMA_SVG" constructor + * + * Upon instantiation This starts writing the Svg XML document + * + * @return void + * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument() + */ + function __construct() + { + $this->openMemory(); + /* + * Set indenting using three spaces, + * so output is formatted + */ + + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* + * Create the XML document + */ + + $this->startDocument('1.0','UTF-8'); + $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); + $this->endDtd(); + } + + /** + * Set document title + * + * @param string value sets the title text + * @return void + * @access public + */ + function setTitle($value) + { + $this->title = $value; + } + + /** + * Set document author + * + * @param string value sets the author + * @return void + * @access public + */ + function setAuthor($value) + { + $this->author = $value; + } + + /** + * Set document font + * + * @param string value sets the font e.g Arial, Sans-serif etc + * @return void + * @access public + */ + function setFont($value) + { + $this->font = $value; + } + + /** + * Get document font + * + * @return string returns the font name + * @access public + */ + function getFont() + { + return $this->font; + } + + /** + * Set document font size + * + * @param string value sets the font size in pixels + * @return void + * @access public + */ + function setFontSize($value) + { + $this->fontSize = $value; + } + + /** + * Get document font size + * + * @return string returns the font size + * @access public + */ + function getFontSize() + { + return $this->fontSize; + } + + /** + * Starts Svg Document + * + * svg document starts by first initializing svg tag + * which contains all the attributes and namespace that needed + * to define the svg document + * + * @param integer width total width of the Svg document + * @param integer height total height of the Svg document + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute() + */ + function startSvgDoc($width,$height) + { + $this->startElement('svg'); + $this->writeAttribute('width', $width); + $this->writeAttribute('height', $height); + $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); + $this->writeAttribute('version', '1.1'); + } + + /** + * Ends Svg Document + * + * @return void + * @access public + * @see XMLWriter::endElement(),XMLWriter::endDocument() + */ + function endSvgDoc() + { + $this->endElement(); + $this->endDocument(); + } + + /** + * output Svg Document + * + * svg document prompted to the user for download + * Svg document saved in .svg extension and can be + * easily changeable by using any svg IDE + * + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute() + */ + function showOutput($fileName) + { + //ob_get_clean(); + header('Content-type: image/svg+xml'); + header('Content-Disposition: attachment; filename="'.$fileName.'.svg"'); + $output = $this->flush(); + print $output; + } + + /** + * Draws Svg elements + * + * SVG has some predefined shape elements like rectangle & text + * and other elements who have x,y co-ordinates are drawn. + * specify their width and height and can give styles too. + * + * @param string name Svg element name + * @param integer x The x attribute defines the left position of the element + (e.g. x="0" places the element 0 pixels from the left of + the browser window) + * @param integer y The y attribute defines the top position of the element + (e.g. y="0" places the element 0 pixels from the top of + the browser window) + * @param integer width The width attribute defines the width the element + * @param integer height The height attribute defines the height the element + * @param string text The text attribute defines the text the element + * @param string styles The style attribute defines the style the element + styles can be defined like CSS styles + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::text(),XMLWriter::endElement() + */ + function printElement($name,$x,$y,$width = '',$height = '',$text = '',$styles = '') + { + $this->startElement($name); + $this->writeAttribute('width',$width); + $this->writeAttribute('height',$height); + $this->writeAttribute('x', $x); + $this->writeAttribute('y', $y); + $this->writeAttribute('style', $styles); + if(isset($text)){ + $this->writeAttribute('font-family', $this->font); + $this->writeAttribute('font-size', $this->fontSize); + $this->text($text); + } + $this->endElement(); + } + + /** + * Draws Svg Line element + * + * Svg line element is drawn for connecting the tables. + * arrows are also drawn by specify its start and ending + * co-ordinates + * + * @param string name Svg element name i.e line + * @param integer x1 The x1 attribute defines the start of the line on the x-axis + * @param integer y1 The y1 attribute defines the start of the line on the y-axis + * @param integer x2 The x2 attribute defines the end of the line on the x-axis + * @param integer y2 The y2 attribute defines the end of the line on the y-axis + * @param string styles The style attribute defines the style the element + styles can be defined like CSS styles + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::endElement() + */ + function printElementLine($name,$x1,$y1,$x2,$y2,$styles) + { + $this->startElement($name); + $this->writeAttribute('x1',$x1); + $this->writeAttribute('y1',$y1); + $this->writeAttribute('x2', $x2); + $this->writeAttribute('y2', $y2); + $this->writeAttribute('style', $styles); + $this->endElement(); + } + + /** + * get width of string/text + * + * Svg text element width is calcualted depending on font name + * and font size. It is very important to know the width of text + * because rectangle is drawn around it. + * + * This is a bit hardcore method. I didn't found any other than this. + * + * @param string text string that width will be calculated + * @param integer font name of the font like Arial,sans-serif etc + * @param integer fontSize size of font + * @return integer width of the text + * @access public + */ + function getStringWidth($text,$font,$fontSize) + { + /* + * Start by counting the width, giving each character a modifying value + */ + $count = 0; + $count = $count + ((strlen($text) - strlen(str_replace(array("i","j","l"),"",$text)))*0.23);//ijl + $count = $count + ((strlen($text) - strlen(str_replace(array("f"),"",$text)))*0.27);//f + $count = $count + ((strlen($text) - strlen(str_replace(array("t","I"),"",$text)))*0.28);//tI + $count = $count + ((strlen($text) - strlen(str_replace(array("r"),"",$text)))*0.34);//r + $count = $count + ((strlen($text) - strlen(str_replace(array("1"),"",$text)))*0.49);//1 + $count = $count + ((strlen($text) - strlen(str_replace(array("c","k","s","v","x","y","z","J"),"",$text)))*0.5);//cksvxyzJ + $count = $count + ((strlen($text) - strlen(str_replace(array("a","b","d","e","g","h","n","o","p","q","u","L","0","2","3","4","5","6","7","8","9"),"",$text)))*0.56);//abdeghnopquL023456789 + $count = $count + ((strlen($text) - strlen(str_replace(array("F","T","Z"),"",$text)))*0.61);//FTZ + $count = $count + ((strlen($text) - strlen(str_replace(array("A","B","E","K","P","S","V","X","Y"),"",$text)))*0.67);//ABEKPSVXY + $count = $count + ((strlen($text) - strlen(str_replace(array("w","C","D","H","N","R","U"),"",$text)))*0.73);//wCDHNRU + $count = $count + ((strlen($text) - strlen(str_replace(array("G","O","Q"),"",$text)))*0.78);//GOQ + $count = $count + ((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM + $count = $count + ((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W + $count = $count + ((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" " + $text = str_replace(" ","",$text);//remove the " "'s + $count = $count + (strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs + + $modifier = 1; + $font = strtolower($font); + switch($font){ + /* + * no modifier for arial and sans-serif + */ + case 'arial': + case 'sans-serif': + break; + /* + * .92 modifer for time, serif, brushscriptstd, and californian fb + */ + case 'times': + case 'serif': + case 'brushscriptstd': + case 'californian fb': + $modifier = .92; + break; + /* + * 1.23 modifier for broadway + */ + case 'broadway': + $modifier = 1.23; + break; + } + $textWidth = $count*$fontSize; + return ceil($textWidth*$modifier); + } +} + +/** + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in SVG XML document. + * + * @name Table_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_SVG + */ +class Table_Stats +{ + /** + * Defines properties + */ + + private $_tableName; + private $_showInfo = false; + + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 0; + public $currentCell = 0; + public $x, $y; + public $primary = array(); + + /** + * The "Table_Stats" constructor + * + * @param string table_name The table name + * @param integer ff The font size + * @param integer samewidth The max. with among tables + * @param boolean show_keys Whether to display keys or not + * @param boolean show_info Whether to display table position or not + * @global object The current SVG image document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_SVG, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) + { + global $svg, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $svg->dieSchema($pageNumber,"SVG",sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + + // height and width + $this->_setHeightTable($fontSize); + + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->_setWidthTable($font,$fontSize); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + $svg->dieSchema($pageNumber,"SVG",sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $tableName); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions/co-ordinates of the table + * + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param string font The font size + * @param integer fontSize The font size + * @global object The current SVG image document + * @access private + * @see PMA_SVG + */ + function _setWidthTable($font,$fontSize) + { + global $svg; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $svg->getStringWidth($field,$font,$fontSize)); + } + $this->width += $svg->getStringWidth(' ',$font,$fontSize); + /* + * it is unknown what value must be added, because + * table title is affected by the tabe width value + */ + while ($this->width < $svg->getStringWidth($this->_getTitle(),$font,$fontSize)) { + $this->width += 7; + } + } + + /** + * Sets the height of the table + * + * @access private + */ + function _setHeightTable($fontSize) + { + $this->heightCell = $fontSize + 4; + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * draw the table + * + * @param boolean showColor Whether to display color + * @global object The current SVG image document + * @access public + * @see PMA_SVG,PMA_SVG::printElement + */ + public function tableDraw($showColor) + { + global $svg; + //echo $this->_tableName.'
'; + $svg->printElement('rect',$this->x,$this->y, + $this->width,$this->heightCell, + NULL,'fill:red;stroke:black;' + ); + $svg->printElement('text',$this->x + 5,$this->y+ 14, + $this->width,$this->heightCell, + $this->_getTitle(), + 'fill:none;stroke:black;' + ); + foreach ($this->fields as $field) { + $this->currentCell += $this->heightCell; + $showColor = 'none'; + if ($showColor) { + if (in_array($field, $this->primary)) { + $showColor = '#0c0'; + } + if ($field == $this->displayfield) { + $showColor = 'none'; + } + } + $svg->printElement('rect', $this->x,$this->y + $this->currentCell, + $this->width, $this->heightCell, + NULL, + 'fill:'.$showColor.';stroke:black;' + ); + $svg->printElement('text', $this->x + 5, $this->y + 14 + $this->currentCell, + $this->width, $this->heightCell, + $field, + 'fill:none;stroke:black;' + ); + } + } +} + + +/** + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in SVG XML document. + * + * @name Relation_Stats + * @author Muhammad Adnan + * @copyright + * @license + * @see PMA_SVG::printElementLine + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir ; + public $destDir; + public $xDest, $yDest; + public $wTick = 10; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->yDest = $dest_pos[2]; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @global object The current SVG image document + * @access public + * @see PMA_SVG + */ + public function relationDraw($changeColor) + { + global $svg; + + if ($changeColor) { + $listOfColors = array( + 'red', + 'grey', + 'black', + 'yellow', + 'green', + 'cyan', + ' orange' + ); + shuffle($listOfColors); + $color = $listOfColors[0]; + } else { + $color = 'black'; + } + + $svg->printElementLine('line',$this->xSrc,$this->ySrc, + $this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick, $this->yDest, + $this->xDest, $this->yDest, + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + $this->xDest + $this->destDir * $this->wTick, $this->yDest, + 'fill:'.$color.';stroke:'.$color.';stroke-width:1;' + ); + $root2 = 2 * sqrt(2); + $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc + $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc - $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , $this->yDest , + $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, + $this->yDest + $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;'); + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , + $this->yDest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick , + $this->yDest - $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + } +} +/* +* end of the "Relation_Stats" class +*/ + +/** + * Svg Relation Schema Class + * + * Purpose of this class is to generate the SVG XML Document because + * SVG defines the graphics in XML format which is used for representing + * the database diagrams as vector image. This class actually helps + * in preparing SVG XML format. + * + * SVG XML is generated by using XMLWriter php extension and this class + * inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Svg_Relation_Schema + * @author Muhammad Adnan + * @copyright + * @license + */ +class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema +{ + + private $tables = array(); + private $_relations = array(); + private $_xMax = 0; + private $_yMax = 0; + private $scale; + private $_xMin = 100000; + private $_yMin = 100000; + private $t_marg = 10; + private $b_marg = 10; + private $l_marg = 10; + private $r_marg = 10; + private $_tablewidth; + + /** + * The "PMA_Svg_Relation_Schema" constructor + * + * Upon instantiation This starts writing the SVG XML document + * user will be prompted for download as .svg extension + * + * @return void + * @see PMA_SVG + */ + function __construct() + { + global $svg,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowColor(isset($_POST['show_color'])); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setTableDimension(isset($_POST['show_table_dimension'])); + $this->setAllTableSameWidth(isset($_POST['all_table_same_wide'])); + $this->setExportType($_POST['export_type']); + + $svg = new PMA_SVG(); + $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber)); + $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $svg->setFont('Arial'); + $svg->setFontSize('16px'); + $svg->startSvgDoc('1000px','1000px'); + $alltables = $this->getAllTables($db,$this->pageNumber); + + foreach ($alltables AS $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table,$svg->getFont(),$svg->getFontSize(), $this->pageNumber, $this->_tablewidth, $this->showKeys, $this->tableDimension); + } + + if ($this->sameWide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->_setMinMax($this->tables[$table]); + } + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table,$svg->getFont(),$svg->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension); + } + } + } + } + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + + $this->_drawTables($this->showColor); + $svg->endSvgDoc(); + $svg->showOutput($db.'-'.$this->pageNumber); + exit(); + } + + /** + * Sets X and Y minimum and maximum for a table cell + * + * @param string table The table name + * @access private + */ + private function _setMinMax($table) + { + $this->_xMax = max($this->_xMax, $table->x + $table->width); + $this->_yMax = max($this->_yMax, $table->y + $table->height); + $this->_xMin = min($this->_xMin, $table->x); + $this->_yMin = min($this->_yMin, $table->y); + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @param boolean showInfo Whether to display table position or not + * @access private + * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$masterTable]); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$foreignTable]); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation arrows and lines + * connects master table's master field to + * foreign table's forein field + * + * @param boolean changeColor Whether to use one color per relation or not + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * @param boolean changeColor Whether to show color for primary fields or not + * @access private + * @see Table_Stats::Table_Stats_tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } +} +?> \ No newline at end of file diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php new file mode 100644 index 000000000..7739ce340 --- /dev/null +++ b/libraries/schema/User_Schema.class.php @@ -0,0 +1,821 @@ + + * @copyright + * @license + */ + +class PMA_User_Schema +{ + + public $choosenPage; + public $autoLayoutForeign; + public $autoLayoutInternal; + public $pageNumber; + public $c_table_rows; + public $action; + + public function setAction($value) + { + $this->action = $value; + } + /** + * This function will process the user defined pages + * and tables which will be exported as Relational schema + * you can set the table positions on the paper via scratchboard + * for table positions, put the x,y co-ordinates + * + * @param string $this->action It tells what the Schema is supposed to do + * create and select a page, generate schema etc + * @access public + */ + + public function processUserPreferences() + { + global $action_choose,$db,$cfgRelation,$cfg,$query_default_option; + + if (isset($this->action)) { + switch ($this->action) { + case 'selectpage': + $this->choosenPage = $_REQUEST['chpage']; + if ($action_choose=="1") { + $this->deleteCoordinates($db, $cfgRelation, $this->choosenPage, $query_default_option); + $this->deletePages($db, $cfgRelation, $this->choosenPage, $query_default_option); + $this->choosenPage = 0; + } + break; + case 'createpage': + $this->pageNumber = PMA_REL_create_page($_POST['newpage'], $cfgRelation, $db, $query_default_option); + $this->autoLayoutForeign = isset($_POST['auto_layout_foreign']) ? "1":NULL; + $this->autoLayoutInternal = isset($_POST['auto_layout_internal']) ? "1":NULL; + $this->processRelations($db, $this->pageNumber,$cfgRelation,$query_default_option); + break; + case 'edcoord': + $this->choosenPage = $_POST['chpage']; + $this->c_table_rows = $_POST['c_table_rows']; + $this->_editCoordinates($db, $cfgRelation,$query_default_option); + break; + case 'deleteCrap': + $this->_deleteTableRows($delrow,$cfgRelation,$db,$this->choosenPage); + break; + case 'process_export': + $this->_processExportSchema(); + break; + + } // end switch + } // end if (isset($do)) + + } + + /** + * shows/displays the HTML FORM to create the page + * + * @param string db name of the selected database + * @return void + * @access public + */ + public function createPage($db) + { + ?> +
+
+ + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ +
+
+ 0) { + ?> +
+
+ + + + + + + __('Edit'), + '1' => __('Delete') + ); + PMA_display_html_radio('action_choose', $choices, '0', false); + unset($choices); + ?> +
+
+
+
+
+ choosenPage) && $this->choosenPage > 0) { + echo "\n"; + ?> +

+ choosenPage) . '\''; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + $array_sh_page = array(); + $draginit = ''; + $reset_draginit = ''; + $i = 0; + while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { + $array_sh_page[] = $temp_sh_page; + } + /* + * Display WYSIWYG-PDF parts? + */ + + if ($cfg['WYSIWYG-PDF']) { + if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { + $with_field_names = TRUE; + } + $this->_displayScratchboardTables($array_sh_page,$draginit,$reset_draginit); + } + ?> + +
+ + + + + + + + + + + $sh_page) { + $_mtab = $sh_page['table_name']; + $tabExist[$_mtab] = FALSE; + echo "\n" . ' '; + $odd_row != $odd_row; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + $i++; + } + /* + * Add one more empty row + */ + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . ' '; + echo "\n" . '
XY
' + . "\n" . ' ' + . "\n" . ' ' + . "\n" . ' ' . __('Delete'); + echo "\n" . ' ' + . "\n" . ' '; + echo "\n" . ' ' + . "\n" . ' '; + echo "\n" . '
' + . "\n" . ' ' + . "\n" . ' ' + . "\n" . ' ' . __('Delete'); + echo "\n" . ' ' + . "\n" . ' '; + echo "\n" . ' ' + . "\n" . ' '; + echo "\n" . '
' . "\n"; + + echo "\n" . ' '; + echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' ' : ''); + echo "\n" . ' ' . __('Column names') . '
'; + echo "\n" . ' '; + echo "\n" . '
' . "\n\n"; + } // end if + + $this->_deleteTables($db, $this->choosenPage, isset($tabExist)); + } + + /** + * show Export relational schema generation options + * user can select export type of his own choice + * and the attributes related to it + * + * @return void + * @access public + */ + + public function displaySchemaGenerationOptions() + { + global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; + ?> +
+
+ + '; + } + echo __('Display Relational Schema'); + ?>: + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + +
+
+ +
+
+ $value) { + if (!$value) { + $_strtrans .= '' . "\n"; + $_strname .= '
  • ' . htmlspecialchars($key) . '
  • ' . "\n"; + $shoot = TRUE; + } + } + if ($shoot) { + echo '
    ' . "\n" + . PMA_generate_common_hidden_inputs($db, $table) + . '' . "\n" + . '' . "\n" + . __('The current page has references to tables that no longer exist. Would you like to delete those references?') + . '
      ' . "\n" + . $_strname + . '
    ' . "\n" + . $_strtrans + . '' . "\n" + . '
    '; + } + } + + } + + /** + * Check if there are tables that need to be deleted in dashboard, + * if there are, ask the user for allowance + * + * @return void + * @access private + */ + private function _displayScratchboardTables($array_sh_page,$draginit,$reset_draginit) + { + global $with_field_names,$cfg,$db; + ?> + +
    + + +
    + + + autoLayoutInternal) || isset($this->autoLayoutForeign)) { + $all_tables = array(); + } + + if (isset($this->autoLayoutForeign)) { + /* + * get the tables list + * who support FOREIGN KEY, it's not + * important that we group together InnoDB tables + * and PBXT tables, as this logic is just to put + * the tables on the layout, not to determine relations + */ + $tables = PMA_DBI_get_tables_full($db); + $foreignkey_tables = array(); + foreach($tables as $table_name => $table_properties) { + if (PMA_foreignkey_supported($table_properties['ENGINE'])) { + $foreignkey_tables[] = $table_name; + } + } + $all_tables = $foreignkey_tables; + /* + * could be improved by finding the tables which have the + * most references keys and placing them at the beginning + * of the array (so that they are all center of schema) + */ + unset($tables, $foreignkey_tables); + } + + if (isset($this->autoLayoutInternal)) { + /* + * get the tables list who support Internal Relations; + * This type of relations will be created when + * you setup the PMA tables correctly + */ + $master_tables = 'SELECT COUNT(master_table), master_table' + . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) + . ' WHERE master_db = \'' . $db . '\'' + . ' GROUP BY master_table' + . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; + $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); + if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { + /* first put all the master tables at beginning + * of the list, so they are near the center of + * the schema + */ + while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { + $all_tables[] = $master_table; + } + + /* Now for each master, add its foreigns into an array + * of foreign tables, if not already there + * (a foreign might be foreign for more than + * one table, and might be a master itself) + */ + + $foreign_tables = array(); + foreach ($all_tables as $master_table) { + $foreigners = PMA_getForeigners($db, $master_table); + foreach ($foreigners as $foreigner) { + if (!in_array($foreigner['foreign_table'], $foreign_tables)) { + $foreign_tables[] = $foreigner['foreign_table']; + } + } + } + + /* + * Now merge the master and foreign arrays/tables + */ + foreach ($foreign_tables as $foreign_table) { + if (!in_array($foreign_table, $all_tables)) { + $all_tables[] = $foreign_table; + } + } + } + } + + if (isset($this->autoLayoutInternal) || isset($this->autoLayoutForeign)) { + $this->addRelationCoordinates($all_tables,$pageNumber,$db, $cfgRelation,$query_default_option); + } + + $this->choosenPage = $pageNumber; + } + + /** + * Add X and Y coordinates for a table + * + * @param string db The database name + * @param array cfgRelation relation settings + * @param integer pageNumber document number/Id + * @param array all_tables A list of all tables involved + * @return void + * @access private + */ + public function addRelationCoordinates($all_tables,$pageNumber,$db, $cfgRelation,$query_default_option) + { + /* + * Now generate the coordinates for the schema + * in a clockwise spiral and add to co-ordinates table + */ + $pos_x = 300; + $pos_y = 300; + $delta = 110; + $delta_mult = 1.10; + $direction = "right"; + foreach ($all_tables as $current_table) { + /* + * save current table's coordinates + */ + $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pageNumber . ',' . $pos_x . ',' . $pos_y . ')'; + PMA_query_as_controluser($insert_query, FALSE, $query_default_option); + + /* + * compute for the next table + */ + switch ($direction) { + case 'right': + $pos_x += $delta; + $direction = "down"; + $delta *= $delta_mult; + break; + case 'down': + $pos_y += $delta; + $direction = "left"; + $delta *= $delta_mult; + break; + case 'left': + $pos_x -= $delta; + $direction = "up"; + $delta *= $delta_mult; + break; + case 'up': + $pos_y -= $delta; + $direction = "right"; + $delta *= $delta_mult; + break; + } + } + } + + /** + * update X and Y coordinates for a table + * + * @param string db The database name + * @param array cfgRelation relation settings + * @return void + * @access private + */ + private function _editCoordinates($db, $cfgRelation,$query_default_option) + { + for ($i = 0; $i < $this->c_table_rows; $i++) { + $arrvalue = 'c_table_' . $i; + global $$arrvalue; + $arrvalue = $$arrvalue; + if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { + $arrvalue['x'] = 0; + } + if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { + $arrvalue['y'] = 0; + } + if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; + $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); + //echo $test_query; + if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { + if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; + } else { + $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; + } + } else { + $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($this->choosenPage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; + } + //echo $ch_query; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + } // end if + } // end for + } +} +?> \ No newline at end of file diff --git a/libraries/schema/Visio_Relation_Schema.class.php b/libraries/schema/Visio_Relation_Schema.class.php new file mode 100644 index 000000000..e3aaba12f --- /dev/null +++ b/libraries/schema/Visio_Relation_Schema.class.php @@ -0,0 +1,599 @@ + + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + +class PMA_VISIO extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + /** + * The "PMA_VISIO" constructor + * + * Upon instantiation This starts writing the Visio XML .VDX document + * + * @return void + * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument() + */ + function __construct() + { + $this->openMemory(); + /* + * Set indenting using three spaces, + * so output is formatted + */ + + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* + * Create the XML document + */ + + $this->startDocument('1.0','UTF-8'); + } + + /** + * Starts Visio XML .VDX Document + * + * Visio XML document starts by first initializing VisioDocument tag + * then DocumentProperties & DocumentSettings contains all the + * attributes that needed to define the document. Order of elements + * should be maintained while generating XML of Visio. + * + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),_documentProperties,_documentSettings + */ + function startVisioDoc() + { + $this->startElement('VisioDocument'); + $this->writeAttribute('xmlns', 'http://schemas.microsoft.com/visio/2003/core'); + $this->writeAttribute('xmlns:vx', 'http://schemas.microsoft.com/visio/2006/extension'); + $this->writeAttribute('xml:space', 'preserve'); + $this->_documentProperties(); + $this->_documentSettings(); + } + + /** + * Set document title + * + * @param string value sets the title text + * @return void + * @access public + */ + function setTitle($value) + { + $this->title = $value; + } + + /** + * Set document author + * + * @param string value sets the author + * @return void + * @access public + */ + function setAuthor($value) + { + $this->author = $value; + } + + /** + * Sets Visio XML .VDX Document Properties + * + * DocumentProperties tag contains document property elements such as + the document's Title,Subject,Creator and templates tags + * + * @return void + * @access private + * @see XMLWriter::startElement(),XMLWriter::endElement(),XMLWriter::writeRaw() + */ + private function _documentProperties() + { + $this->startElement('DocumentProperties'); + $this->writeRaw(''.$this->title.''); + $this->writeRaw(''.$this->title.''); + $this->writeRaw(''.$this->author.''); + $this->writeRaw('phpMyAdmin'); + $this->writeRaw(''); + $this->endElement(); + } + + /** + * Sets Visio XML .VDX Document Settings + * + * DocumentSettings tag contains elements that specify document settings. + * + * @return void + * @access private + * @see XMLWriter::startElement(),XMLWriter::endElement() + */ + private function _documentSettings() + { + $this->startElement('DocumentSettings'); + $this->endElement(); + } + + /** + * Ends Visio XML Document + * + * @return void + * @access public + * @see XMLWriter::endElement(),XMLWriter::endDocument() + */ + function endVisioDoc() + { + $this->endElement(); + $this->endDocument(); + } + + /** + * Output Visio XML .VDX Document for download + * + * @param string fileName name of the Visio XML document + * @return void + * @access public + * @see XMLWriter::flush() + */ + function showOutput($fileName) + { + //if(ob_get_clean()){ + //ob_end_clean(); + //} + header('Content-type: application/visio'); + header('Content-Disposition: attachment; filename="'.$fileName.'.vdx"'); + $output = $this->flush(); + print $output; + } +} + + +/** + * Draws tables schema + */ +class Table_Stats +{ + /** + * Defines properties + */ + + private $_tableName; + private $_showInfo = false; + + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 0; + public $currentCell = 0; + public $x, $y; + public $primary = array(); + + /** + * The "Table_Stats" constructor + * + * @param string tableName The table name + * @param integer same_wide_width The max. with among tables + * @param boolean showKeys Whether to display keys or not + * @param boolean showInfo Whether to display table position or not + * @global object The current Visio XML document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_VISIO, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) + { + global $visio, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $visio->dieSchema($pageNumber,"VISIO",sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + + // height and width + $this->_setHeightTable($fontSize); + + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->_setWidthTable($font,$fontSize); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + $visio->dieSchema($pageNumber,"VISIO",sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $tableName); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions/co-ordinates of the table + * + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param string font The font name + * @param integer fontSize The font size + * @global object The current Visio XML document + * @access private + * @see PMA_VISIO + */ + function _setWidthTable($font,$fontSize) + { + global $visio; + + } + + /** + * Sets the height of the table + * + * @access private + */ + function _setHeightTable($fontSize) + { + $this->heightCell = $fontSize + 4; + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * draw the table + * + * @param boolean showColor Whether to display color + * @global object The current Visio XML document + * @access public + * @see PMA_VISIO + */ + public function tableDraw($showColor) + { + global $visio; + //echo $this->_tableName.'
    '; + + foreach ($this->fields as $field) { + $this->currentCell += $this->heightCell; + $showColor = 'none'; + if ($showColor) { + if (in_array($field, $this->primary)) { + $showColor = '#0c0'; + } + if ($field == $this->displayfield) { + $showColor = 'none'; + } + } + // code here for drawing table diagrams + } + } +} + +/** + * Draws relation links + * + * @access public + * @see PMA_VISIO + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir ; + public $destDir; + public $xDest, $yDest; + public $wTick = 10; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->yDest = $dest_pos[2]; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @global object The current Visio XML document + * @access public + * @see PMA_VISIO + */ + public function relationDraw($changeColor) + { + global $visio; + + if ($changeColor) { + $listOfColors = array( + 'red', + 'grey', + 'black', + 'yellow', + 'green', + 'cyan', + ' orange' + ); + shuffle($listOfColors); + $color = $listOfColors[0]; + } else { + $color = 'black'; + } + + // code here for making connections b/w relation objects + + } +} +/* +* end of the "Relation_Stats" class +*/ + +/** + * Visio Relation Schema Class + * + * Purpose of this class is to generate the Visio XML .VDX Document + * which is used for representing the database diagrams in any version of MS Visio IDE. + * This class uses Software and Database Template and Database model diagram of Visio + * and with the combination of these objects actually helps in preparing Visio XML .VDX document. + * + * Visio XML is generated by using XMLWriter php extension and this class + * inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Visio_Relation_Schema + * @author Muhammad Adnan + * @copyright + * @license + */ +class PMA_Visio_Relation_Schema extends PMA_Export_Relation_Schema +{ + /** + * The "PMA_Visio_Relation_Schema" constructor + * + * Upon instantiation This outputs the Visio XML document + * that user can download + * + * @return void + * @see PMA_VISIO,Table_Stats,Relation_Stats + */ + function __construct() + { + global $visio,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowGrid(isset($_POST['show_grid'])); + $this->setShowColor($_POST['show_color']); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setOrientation(isset($_POST['orientation'])); + $this->setPaper($_POST['paper']); + $this->setExportType($_POST['export_type']); + + $visio = new PMA_VISIO(); + $visio->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber)); + $visio->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $visio->startVisioDoc(); + $alltables = $this->getAllTables($db,$this->pageNumber); + + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table, $this->pageNumber, $this->showKeys); + } + } + + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'],$this->showKeys); + } + } + } + } + $this->_drawTables($this->showColor); + + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + $visio->endVisioDoc(); + $visio->showOutput($db.'-'.$this->pageNumber); + exit(); + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @return void + * @access private + * @see Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable, $masterField, $foreignTable, $foreignField, $showKeys) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $this->pageNumber, $showKeys); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable, $this->pageNumber, $showKeys); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation references + * + * connects master table's master field to + * foreign table's forein field. + * + * @param boolean changeColor Whether to use one color per relation or not + * @return void + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * + * @param boolean changeColor Whether to show color for tables text or not + * @return void + * @access private + * @see Table_Stats::tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } +} +?> \ No newline at end of file diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 26f2dc1ba..a6b707ac2 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2980,6 +2980,7 @@ if(!class_exists('TCPDF', false)) { if(ob_get_contents()) { $this->Error('Some data has already been output, can\'t send PDF file'); } + if(php_sapi_name()!='cli') { //We send to a browser header('Content-Type: application/pdf'); @@ -2997,6 +2998,7 @@ if(!class_exists('TCPDF', false)) { if(ob_get_contents()) { $this->Error('Some data has already been output, can\'t send PDF file'); } + if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { header('Content-Type: application/force-download'); } else { @@ -3005,6 +3007,7 @@ if(!class_exists('TCPDF', false)) { if(headers_sent()) { $this->Error('Some data has already been output to browser, can\'t send PDF file'); } + header('Content-Type: application/pdf'); header('Content-Length: '.strlen($this->buffer)); header('Content-disposition: attachment; filename="'.$name.'"'); echo $this->buffer; diff --git a/pdf_pages.php b/pdf_pages.php deleted file mode 100644 index 75d4622fd..000000000 --- a/pdf_pages.php +++ /dev/null @@ -1,559 +0,0 @@ -%s table not found or not set in %s'), 'relation', 'config.inc.php') . '
    ' . "\n" - . PMA_showDocu('relation') . "\n"; - require './libraries/footer.inc.php'; -} - -if (!$cfgRelation['displaywork']) { - echo sprintf(__('%s table not found or not set in %s'), 'table_info', 'config.inc.php') . '
    ' . "\n" - . PMA_showDocu('table_info') . "\n"; - require './libraries/footer.inc.php'; -} - -if (!isset($cfgRelation['table_coords'])){ - echo sprintf(__('%s table not found or not set in %s'), 'table_coords', 'config.inc.php') . '
    ' . "\n" - . PMA_showDocu('table_coords') . "\n"; - require './libraries/footer.inc.php'; -} -if (!isset($cfgRelation['pdf_pages'])) { - echo sprintf(__('%s table not found or not set in %s'), 'pdf_page', 'config.inc.php') . '
    ' . "\n" - . PMA_showDocu('pdf_pages') . "\n"; - require './libraries/footer.inc.php'; -} - -if ($cfgRelation['pdfwork']) { - // Now is the time to work on all changes - if (isset($do)) { - switch ($do) { - case 'choosepage': - if ($action_choose=="1") { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - unset($chpage); - } - break; - case 'createpage': - $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); - - // A u t o m a t i c l a y o u t - // ================================ - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - $all_tables = array(); - } - - if (isset($auto_layout_foreign)) { - // get the tables list - $tables = PMA_DBI_get_tables_full($db); - // find the ones who support FOREIGN KEY; it's not - // important that we group together InnoDB tables - // and PBXT tables, as this logic is just to put - // the tables on the layout, not to determine relations - $foreignkey_tables = array(); - foreach($tables as $table_name => $table_properties) { - if (PMA_foreignkey_supported($table_properties['ENGINE'])) { - $foreignkey_tables[] = $table_name; - } - } - $all_tables = $foreignkey_tables; - // could be improved by finding the tables which have the - // most references keys and placing them at the beginning - // of the array (so that they are all center of schema) - unset($tables, $foreignkey_tables); - } // endif auto_layout_foreign - - if (isset($auto_layout_internal)) { - // get the tables that have relations, by descending - // number of links - $master_tables = 'SELECT COUNT(master_table), master_table' - . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - . ' WHERE master_db = \'' . $db . '\'' - . ' GROUP BY master_table' - . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; - $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); - if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { - // first put all the master tables at beginning - // of the list, so they are near the center of - // the schema - while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { - $all_tables[] = $master_table; - } - - // then for each master, add its foreigns into an array - // of foreign tables, if not already there - // (a foreign might be foreign for more than - // one table, and might be a master itself) - - $foreign_tables = array(); - foreach ($all_tables AS $master_table) { - $foreigners = PMA_getForeigners($db, $master_table); - foreach ($foreigners AS $foreigner) { - if (!in_array($foreigner['foreign_table'], $foreign_tables)) { - $foreign_tables[] = $foreigner['foreign_table']; - } - } - } - - // then merge the arrays - foreach ($foreign_tables AS $foreign_table) { - if (!in_array($foreign_table, $all_tables)) { - $all_tables[] = $foreign_table; - } - } - } // endif there are master tables - } // endif auto_layout_internal - - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - // now generate the coordinates for the schema, - // in a clockwise spiral - - $pos_x = 300; - $pos_y = 300; - $delta = 110; - $delta_mult = 1.10; - $direction = "right"; - foreach ($all_tables AS $current_table) { - - // save current table's coordinates - $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; - PMA_query_as_controluser($insert_query, FALSE, $query_default_option); - - // compute for the next table - switch ($direction) { - case 'right': - $pos_x += $delta; - $direction = "down"; - $delta *= $delta_mult; - break; - case 'down': - $pos_y += $delta; - $direction = "left"; - $delta *= $delta_mult; - break; - case 'left': - $pos_x -= $delta; - $direction = "up"; - $delta *= $delta_mult; - break; - case 'up': - $pos_y -= $delta; - $direction = "right"; - $delta *= $delta_mult; - break; - } // end switch - } // end foreach - } // end if some auto-layout to do - - $chpage = $pdf_page_number; - - break; - - case 'edcoord': - for ($i = 0; $i < $c_table_rows; $i++) { - $arrvalue = 'c_table_' . $i; - $arrvalue = $$arrvalue; - if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { - $arrvalue['x'] = 0; - } - if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { - $arrvalue['y'] = 0; - } - if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } else { - $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } - } else { - $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; - } - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - } // end if - } // end for - break; - case 'deleteCrap': - foreach ($delrow AS $current_row) { - $d_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" - . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($d_query, FALSE, $query_default_option); - } - break; - } // end switch - } // end if (isset($do)) - - // We will need an array of all tables in this db - $selectboxall = array('--'); - $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); - while ($val = @PMA_DBI_fetch_row($alltab_rs)) { - $selectboxall[] = $val[0]; - } - - // Now first show some possibility to choose a page for the pdf - $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; - $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - - if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { - ?> -
    -
    - - - - - - - __('Edit'), - '1' => __('Delete')); - PMA_display_html_radio('action_choose', $choices, '0', false); - unset($choices); -?> -
    -
    -
    -
    -
    - -
    -
    - - - - - - - - - - - - - -
    -
    - -
    - -
    -
    -
    - -
    -
    - 0) { - echo "\n"; - ?> -
    - -

    - - - -
    - - -
    - - - - -
    - - - - - - - - - - - $sh_page) { - $_mtab = $sh_page['table_name']; - $tabExist[$_mtab] = FALSE; - echo "\n" . ' '; - $odd_row != $odd_row; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - $i++; - } // end while - // Do one more empty row - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . ' '; - echo "\n" . '
    XY
    ' - . "\n" . ' ' - . "\n" . ' ' - . "\n" . ' ' . __('Delete'); - echo "\n" . ' ' - . "\n" . ' '; - echo "\n" . ' ' - . "\n" . ' '; - echo "\n" . '
    ' - . "\n" . ' ' - . "\n" . ' ' - . "\n" . ' ' . __('Delete'); - echo "\n" . ' ' - . "\n" . ' '; - echo "\n" . ' ' - . "\n" . ' '; - echo "\n" . '
    ' . "\n"; - - echo "\n" . ' '; - echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' ' : ''); - echo "\n" . ' ' . __('Column names') . '
    '; - echo "\n" . ' '; - echo "\n" . '
    ' . "\n\n"; - } // end if - - // Check if there are tables that need to be deleted, - // if there are, ask the user for allowance - $_strtrans = ''; - $_strname = ''; - $shoot = FALSE; - if (!empty($tabExist) && is_array($tabExist)) { - foreach ($tabExist AS $key => $value) { - if (!$value) { - $_strtrans .= '' . "\n"; - $_strname .= '
  • ' . htmlspecialchars($key) . '
  • ' . "\n"; - $shoot = TRUE; - } - } - if ($shoot) { - echo '
    ' . "\n" - . PMA_generate_common_hidden_inputs($db, $table) - . '' . "\n" - . '' . "\n" - . __('The current page has references to tables that no longer exist. Would you like to delete those references?') - . '
      ' . "\n" - . $_strname - . '
    ' . "\n" - . $_strtrans - . '' . "\n" - . '
    '; - } - } - // ------------------------------------ - // d i s p l a y p d f s c h e m a - // ------------------------------------ - - if (isset($do) - && ($do == 'edcoord' - || ($do == 'choosepage' && isset($chpage)) - || ($do == 'createpage' && isset($chpage)))) { - include('./libraries/display_pdf_schema.lib.php'); - if ((isset($showwysiwyg) && $showwysiwyg == '1')) { -?> - - diff --git a/pdf_schema.php b/pdf_schema.php deleted file mode 100644 index dd7426f1b..000000000 --- a/pdf_schema.php +++ /dev/null @@ -1,1404 +0,0 @@ -' . __('Error') . '
    ' . "\n"; - $url_to_goto = ''; - echo sprintf(__('The phpMyAdmin configuration storage has been deactivated. To find out why click %shere%s.'), $url_to_goto, '') . "\n"; -} - -/** - * Font used in PDF. - * - * @todo Make this configuratble (at least Sans/Serif). - */ -define('PMA_PDF_FONT', 'DejaVuSans'); -require_once './libraries/tcpdf/tcpdf.php'; - -/** - * Extends the "FPDF" class and prepares the work - * - * @access public - * @see FPDF - * @package phpMyAdmin - */ -class PMA_PDF extends TCPDF { - /** - * Defines private properties - */ - var $x_min; - var $y_min; - var $l_marg = 10; - var $t_marg = 10; - var $scale; - var $PMA_links; - var $Outlines = array(); - var $def_outlines; - var $Alias = array(); - var $widths; - - public function getFh() - { - return $this->fh; - } - - public function getFw() - { - return $this->fw; - } - - public function setCMargin($c_margin) - { - $this->cMargin = $c_margin; - } - - function SetAlias($name, $value) - { - $this->Alias[$name] = $value ; - } - - function _putpages() - { - if (count($this->Alias) > 0) { - $nb = $this->page; - foreach ($this->Alias AS $alias => $value) { - for ($n = 1;$n <= $nb;$n++) - $this->pages[$n]=str_replace($alias, $value, $this->pages[$n]); - } - } - parent::_putpages(); - } - - /** - * Sets the scaling factor, defines minimum coordinates and margins - * - * @param float scale The scaling factor - * @param float x_min The minimum X coordinate - * @param float y_min The minimum Y coordinate - * @param float l_marg The left margin - * @param float t_marg The top margin - * @access public - */ - function PMA_PDF_setScale($scale = 1, $x_min = 0, $y_min = 0, $l_marg = -1, $t_marg = -1) - { - $this->scale = $scale; - $this->x_min = $x_min; - $this->y_min = $y_min; - if ($this->l_marg != -1) { - $this->l_marg = $l_marg; - } - if ($this->t_marg != -1) { - $this->t_marg = $t_marg; - } - } // end of the "PMA_PDF_setScale" function - /** - * Outputs a scaled cell - * - * @param float w The cell width - * @param float h The cell height - * @param string txt The text to output - * @param mixed border Whether to add borders or not - * @param integer ln Where to put the cursor once the output is done - * @param string align Align mode - * @param integer fill Whether to fill the cell with a color or not - * @access public - * @see FPDF::Cell() - */ - function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') - { - $h = $h / $this->scale; - $w = $w / $this->scale; - $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); - } // end of the "PMA_PDF_cellScale" function - /** - * Draws a scaled line - * - * @param float x1 The horizontal position of the starting point - * @param float y1 The vertical position of the starting point - * @param float x2 The horizontal position of the ending point - * @param float y2 The vertical position of the ending point - * @access public - * @see FPDF::Line() - */ - function PMA_PDF_lineScale($x1, $y1, $x2, $y2) - { - $x1 = ($x1 - $this->x_min) / $this->scale + $this->l_marg; - $y1 = ($y1 - $this->y_min) / $this->scale + $this->t_marg; - $x2 = ($x2 - $this->x_min) / $this->scale + $this->l_marg; - $y2 = ($y2 - $this->y_min) / $this->scale + $this->t_marg; - $this->Line($x1, $y1, $x2, $y2); - } // end of the "PMA_PDF_lineScale" function - /** - * Sets x and y scaled positions - * - * @param float x The x position - * @param float y The y position - * @access public - * @see FPDF::SetXY() - */ - function PMA_PDF_setXyScale($x, $y) - { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $y = ($y - $this->y_min) / $this->scale + $this->t_marg; - $this->SetXY($x, $y); - } // end of the "PMA_PDF_setXyScale" function - /** - * Sets the X scaled positions - * - * @param float x The x position - * @access public - * @see FPDF::SetX() - */ - function PMA_PDF_setXScale($x) - { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $this->SetX($x); - } // end of the "PMA_PDF_setXScale" function - /** - * Sets the scaled font size - * - * @param float size The font size (in points) - * @access public - * @see FPDF::SetFontSize() - */ - function PMA_PDF_setFontSizeScale($size) - { - // Set font size in points - $size = $size / $this->scale; - $this->SetFontSize($size); - } // end of the "PMA_PDF_setFontSizeScale" function - /** - * Sets the scaled line width - * - * @param float width The line width - * @access public - * @see FPDF::SetLineWidth() - */ - function PMA_PDF_setLineWidthScale($width) - { - $width = $width / $this->scale; - $this->SetLineWidth($width); - } // end of the "PMA_PDF_setLineWidthScale" function - /** - * Displays an error message - * - * @param string error_message the error mesage - * @global array the PMA configuration array - * @global integer the current server id - * @global string the current language - * @global string the charset to convert to - * @global string the current database name - * @global string the current charset - * @global string the current text direction - * @global string a localized string - * @global string an other localized string - * @access public - */ - function PMA_PDF_die($error_message = '') - { - global $cfg; - global $server, $lang, $db; - global $charset, $text_dir; - - require_once './libraries/header.inc.php'; - - echo '

    PDF - ' . __('Error') . '

    ' . "\n"; - if (!empty($error_message)) { - $error_message = htmlspecialchars($error_message); - } - echo '

    ' . "\n"; - echo ' ' . $error_message . "\n"; - echo '

    ' . "\n"; - - echo '' . __('Back') . ''; - echo "\n"; - - require './libraries/footer.inc.php'; - } // end of the "PMA_PDF_die()" function - /** - * Aliases the "Error()" function from the FPDF class to the - * "PMA_PDF_die()" one - * - * @param string error_message the error mesage - * @access public - * @see PMA_PDF_die - */ - function Error($error_message = '') - { - $this->PMA_PDF_die($error_message); - } // end of the "Error()" method - function Header() - { - // We only show this if we find something in the new pdf_pages table - - // This function must be named "Header" to work with the FPDF library - global $cfgRelation, $db, $pdf_page_number, $with_doc; - if ($with_doc) { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . $pdf_page_number . '\''; - $test_rs = PMA_query_as_controluser($test_query); - $pages = @PMA_DBI_fetch_assoc($test_rs); - $this->SetFont('', 'B', 14); - $this->Cell(0, 6, ucfirst($pages['page_descr']), 'B', 1, 'C'); - $this->SetFont('', ''); - $this->Ln(); - } - } - function Footer() - { - // This function must be named "Footer" to work with the FPDF library - global $with_doc; - if ($with_doc) { - $this->SetY(-15); - $this->SetFont('', '', 14); - $this->Cell(0, 6, __('Page number:') . ' ' . $this->PageNo() . '/{nb}', 'T', 0, 'C'); - $this->Cell(0, 6, PMA_localisedDate(), 0, 1, 'R'); - $this->SetY(20); - } - } - function Bookmark($txt, $level = 0, $y = 0) - { - // Add a bookmark - $this->Outlines[0][] = $level; - $this->Outlines[1][] = $txt; - $this->Outlines[2][] = $this->page; - if ($y == -1) { - $y = $this->GetY(); - } - $this->Outlines[3][] = round($this->hPt - $y * $this->k, 2); - } - - function _putbookmarks() - { - if (count($this->Outlines) > 0) { - // Save object number - $memo_n = $this->n; - // Take the number of sub elements for an outline - $nb_outlines = sizeof($this->Outlines[0]); - $first_level = array(); - $parent = array(); - $parent[0] = 1; - for ($i = 0; $i < $nb_outlines; $i++) { - $level = $this->Outlines[0][$i]; - $kids = 0; - $last = -1; - $prev = -1; - $next = -1; - if ($i > 0) { - $cursor = $i-1; - // Take the previous outline in the same level - while ($this->Outlines[0][$cursor] > $level && $cursor > 0) - $cursor--; - if ($this->Outlines[0][$cursor] == $level) { - $prev = $cursor; - } - } - if ($i < $nb_outlines-1) { - $cursor = $i + 1; - while (isset($this->Outlines[0][$cursor]) && $this->Outlines[0][$cursor] > $level) { - // Take the immediate kid in level + 1 - if ($this->Outlines[0][$cursor] == $level + 1) { - $kids++; - $last = $cursor; - } - $cursor++; - } - $cursor = $i + 1; - // Take the next outline in the same level - while ($this->Outlines[0][$cursor] > $level && ($cursor + 1 < sizeof($this->Outlines[0]))) - $cursor++; - if ($this->Outlines[0][$cursor] == $level) { - $next = $cursor; - } - } - $this->_newobj(); - $parent[$level + 1] = $this->n; - if ($level == 0) { - $first_level[] = $this->n; - } - $this->_out('<<'); - $this->_out('/Title (' . $this->Outlines[1][$i] . ')'); - $this->_out('/Parent ' . $parent[$level] . ' 0 R'); - if ($prev != -1) { - $this->_out('/Prev ' . ($memo_n + $prev + 1) . ' 0 R'); - } - if ($next != -1) { - $this->_out('/Next ' . ($this->n + $next - $i) . ' 0 R'); - } - $this->_out('/Dest [' . (1 + (2 * $this->Outlines[2][$i])) . ' 0 R /XYZ null ' . $this->Outlines[3][$i] . ' null]'); - if ($kids > 0) { - $this->_out('/First ' . ($this->n + 1) . ' 0 R'); - $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); - $this->_out('/Count -' . $kids); - } - $this->_out('>>'); - $this->_out('endobj'); - } - // First page of outlines - $this->_newobj(); - $this->def_outlines = $this->n; - $this->_out('<<'); - $this->_out('/Type'); - $this->_out('/Outlines'); - $this->_out('/First ' . $first_level[0] . ' 0 R'); - $this->_out('/Last ' . $first_level[sizeof($first_level)-1] . ' 0 R'); - $this->_out('/Count ' . sizeof($first_level)); - $this->_out('>>'); - $this->_out('endobj'); - } - } - - function _putresources() - { - parent::_putresources(); - $this->_putbookmarks(); - } - - function _putcatalog() - { - parent::_putcatalog(); - if (count($this->Outlines) > 0) { - $this->_out('/Outlines ' . $this->def_outlines . ' 0 R'); - $this->_out('/PageMode /UseOutlines'); - } - } - function SetWidths($w) - { - // column widths - $this->widths = $w; - } - - function Row($data, $links) - { - // line height - $nb = 0; - $data_cnt = count($data); - for ($i = 0;$i < $data_cnt;$i++) - $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); - $il = $this->FontSize; - $h = ($il + 1) * $nb; - // page break if necessary - $this->CheckPageBreak($h); - // draw the cells - $data_cnt = count($data); - for ($i = 0;$i < $data_cnt;$i++) { - $w = $this->widths[$i]; - // save current position - $x = $this->GetX(); - $y = $this->GetY(); - // draw the border - $this->Rect($x, $y, $w, $h); - if (isset($links[$i])) { - $this->Link($x, $y, $w, $h, $links[$i]); - } - // print text - $this->MultiCell($w, $il + 1, $data[$i], 0, 'L'); - // go to right side - $this->SetXY($x + $w, $y); - } - // go to line - $this->Ln($h); - } - - function CheckPageBreak($h) - { - // if height h overflows, manual page break - if ($this->GetY() + $h > $this->PageBreakTrigger) { - $this->AddPage($this->CurOrientation); - } - } - - function NbLines($w, $txt) - { - // compute number of lines used by a multicell of width w - $cw = &$this->CurrentFont['cw']; - if ($w == 0) { - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w-2 * $this->cMargin) * 1000 / $this->FontSize; - $s = str_replace("\r", '', $txt); - $nb = strlen($s); - if ($nb > 0 and $s[$nb-1] == "\n") { - $nb--; - } - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $nl = 1; - while ($i < $nb) { - $c = $s[$i]; - if ($c == "\n") { - $i++; - $sep = -1; - $j = $i; - $l = 0; - $nl++; - continue; - } - if ($c == ' ') { - $sep = $i; - } - $l += isset($cw[ord($c)])?$cw[ord($c)]:0 ; - if ($l > $wmax) { - if ($sep == -1) { - if ($i == $j) { - $i++; - } - } else { - $i = $sep + 1; - } - $sep = -1; - $j = $i; - $l = 0; - $nl++; - } else { - $i++; - } - } - return $nl; - } -} // end of the "PMA_PDF" class - - -/** - * Draws tables schema - * - * @access private - * @see PMA_RT - * @package phpMyAdmin - */ -class PMA_RT_Table { - /** - * Defines private properties - */ - var $nb_fiels; - var $table_name; - var $width = 0; - var $height; - var $fields = array(); - var $height_cell = 6; - var $x, $y; - var $primary = array(); - var $show_info = false; - - /** - * Returns title of the current table, - * title can have the dimensions of the table - * - * @access private - */ - function getTitle() - { - return ($this->show_info ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->table_name; - } // end of the "getTitle" function - /** - * Sets the width of the table - * - * @param integer ff The font size - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_setWidth($ff) - { - global $pdf; - - foreach ($this->fields AS $field) { - $this->width = max($this->width, $pdf->GetStringWidth($field)); - } - $this->width += $pdf->GetStringWidth(' '); - $pdf->SetFont($ff, 'B'); - // it is unknown what value must be added, because - // table title is affected by the tabe width value - while ($this->width < $pdf->GetStringWidth($this->getTitle())) { - $this->width += 5; - } - $pdf->SetFont($ff, ''); - } // end of the "PMA_RT_Table_setWidth()" method - /** - * Sets the height of the table - * - * @access private - */ - function PMA_RT_Table_setHeight() - { - $this->height = (count($this->fields) + 1) * $this->height_cell; - } // end of the "PMA_RT_Table_setHeight()" method - /** - * Do draw the table - * - * @param integer ff The font size - * @param boolean setcolortWhether to display color - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_draw($ff, $setcolor = 0) - { - global $pdf, $with_doc; - - $pdf->PMA_PDF_setXyScale($this->x, $this->y); - $pdf->SetFont($ff, 'B'); - if ($setcolor) { - $pdf->SetTextColor(200); - $pdf->SetFillColor(0, 0, 128); - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name]['-'], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name]['-'] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, $this->getTitle(), 1, 1, 'C', $setcolor, $pdf->PMA_links['doc'][$this->table_name]['-']); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFont($ff, ''); - $pdf->SetTextColor(0); - $pdf->SetFillColor(255); - - foreach ($this->fields AS $field) { - if ($setcolor) { - if (in_array($field, $this->primary)) { - $pdf->SetFillColor(215, 121, 123); - } - if ($field == $this->displayfield) { - $pdf->SetFillColor(142, 159, 224); - } - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name][$field], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name][$field] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, ' ' . $field, 1, 1, 'L', $setcolor, $pdf->PMA_links['doc'][$this->table_name][$field]); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFillColor(255); - } // end while - /*if ($pdf->PageNo() > 1) { - $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); - } */ - } // end of the "PMA_RT_Table_draw()" method - /** - * The "PMA_RT_Table" constructor - * - * @param string table_name The table name - * @param integer ff The font size - * @param integer same_width The max. with among tables - * @param boolean show_keys Whether to display keys or not - * @param boolean show_info Whether to display table position or not - * @global object The current PDF document - * @global integer The current page number (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @global array The relations settings - * @global string The current db name - * @access private - * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, - PMA_RT_Table::PMA_RT_Table_setHeight - */ - function __construct($table_name, $ff, &$same_wide_width, $show_keys = false, $show_info = false) - { - global $pdf, $pdf_page_number, $cfgRelation, $db; - - $this->table_name = $table_name; - $sql = 'DESCRIBE ' . PMA_backquote($table_name); - $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('The %s table doesn\'t exist!'), $table_name)); - } - // load fields - //check to see if it will load all fields or only the foreign keys - if ($show_keys) { - $indexes = PMA_Index::getFromTable($this->table_name, $db); - $all_columns = array(); - foreach ($indexes as $index) { - $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); - } - $this->fields = array_keys($all_columns); - } else { - while ($row = PMA_DBI_fetch_row($result)) { - $this->fields[] = $row[0]; - } - } - - $this->show_info = $show_info; - - // height and width - $this->PMA_RT_Table_setHeight(); - // setWidth must me after setHeight, because title - // can include table height which changes table width - $this->PMA_RT_Table_setWidth($ff); - if ($same_wide_width < $this->width) { - $same_wide_width = $this->width; - } - // x and y - $sql = 'SELECT x, y FROM ' - . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' - . ' AND pdf_page_number = ' . $pdf_page_number; - $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); - - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('Please configure the coordinates for table %s'), $table_name)); - } - list($this->x, $this->y) = PMA_DBI_fetch_row($result); - $this->x = (double) $this->x; - $this->y = (double) $this->y; - // displayfield - $this->displayfield = PMA_getDisplayField($db, $table_name); - // index - $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); - if (PMA_DBI_num_rows($result) > 0) { - while ($row = PMA_DBI_fetch_assoc($result)) { - if ($row['Key_name'] == 'PRIMARY') { - $this->primary[] = $row['Column_name']; - } - } - } // end if - } // end of the "PMA_RT_Table()" method -} // end class "PMA_RT_Table" -/** - * Draws relation links - * - * @access private - * @see PMA_RT - * @package phpMyAdmin - */ -class PMA_RT_Relation { - /** - * Defines private properties - */ - var $x_src, $y_src; - var $src_dir ; - var $dest_dir; - var $x_dest, $y_dest; - var $w_tick = 5; - - /** - * Gets arrows coordinates - * - * @param string table The current table name - * @param string column The relation column name - * @return array Arrows coordinates - * @access private - */ - function PMA_RT_Relation_getXy($table, $column) - { - $pos = array_search($column, $table->fields); - // x_left, x_right, y - return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->height_cell); - } // end of the "PMA_RT_Relation_getXy()" method - /** - * Do draws relation links - * - * @param boolean change_color Whether to use one color per relation or not - * @param integer i The id of the link to draw - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Relation_draw($change_color, $i) - { - global $pdf; - - if ($change_color) { - $d = $i % 6; - $j = ($i - $d) / 6; - $j = $j % 4; - $j++; - $case = array( - array(1, 0, 0), - array(0, 1, 0), - array(0, 0, 1), - array(1, 1, 0), - array(1, 0, 1), - array(0, 1, 1) - ); - list ($a, $b, $c) = $case[$d]; - $e = (1 - ($j - 1) / 6); - $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); - } else { - $pdf->SetDrawColor(0); - } // end if... else... - $pdf->PMA_PDF_setLineWidthScale(0.2); - $pdf->PMA_PDF_lineScale($this->x_src, $this->y_src, $this->x_src + $this->src_dir * $this->w_tick, $this->y_src); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest, $this->x_dest, $this->y_dest); - $pdf->PMA_PDF_setLineWidthScale(0.1); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick, $this->y_src, $this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest); - // arrow - $root2 = 2 * sqrt(2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src - $this->w_tick / $root2); - - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); - $pdf->SetDrawColor(0); - } // end of the "PMA_RT_Relation_draw()" method - /** - * The "PMA_RT_Relation" constructor - * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreigh_field The relation field in the foreign table - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_getXy - */ - function __construct($master_table, $master_field, $foreign_table, $foreign_field) - { - $src_pos = $this->PMA_RT_Relation_getXy($master_table, $master_field); - $dest_pos = $this->PMA_RT_Relation_getXy($foreign_table, $foreign_field); - $src_left = $src_pos[0] - $this->w_tick; - $src_right = $src_pos[1] + $this->w_tick; - $dest_left = $dest_pos[0] - $this->w_tick; - $dest_right = $dest_pos[1] + $this->w_tick; - - $d1 = abs($src_left - $dest_left); - $d2 = abs($src_right - $dest_left); - $d3 = abs($src_left - $dest_right); - $d4 = abs($src_right - $dest_right); - $d = min($d1, $d2, $d3, $d4); - - if ($d == $d1) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d2) { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d3) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } else { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } - $this->y_src = $src_pos[2]; - $this->y_dest = $dest_pos[2]; - } // end of the "PMA_RT_Relation()" method -} // end of the "PMA_RT_Relation" class -/** - * Draws and send the database schema - * - * @access public - * @see PMA_PDF - * @package phpMyAdmin - */ -class PMA_RT { - /** - * Defines private properties - */ - var $tables = array(); - var $relations = array(); - var $ff = PMA_PDF_FONT; - var $x_max = 0; - var $y_max = 0; - var $scale; - var $x_min = 100000; - var $y_min = 100000; - var $t_marg = 10; - var $b_marg = 10; - var $l_marg = 10; - var $r_marg = 10; - var $tablewidth; - var $same_wide = 0; - - /** - * Sets X and Y minimum and maximum for a table cell - * - * @param string table The table name - * @access private - */ - function PMA_RT_setMinMax($table) - { - $this->x_max = max($this->x_max, $table->x + $table->width); - $this->y_max = max($this->y_max, $table->y + $table->height); - $this->x_min = min($this->x_min, $table->x); - $this->y_min = min($this->y_min, $table->y); - } // end of the "PMA_RT_setMinMax()" method - /** - * Defines relation objects - * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreign_field The relation field in the foreign table - * @param boolean show_info Whether to display table position or not - * @access private - * @see PMA_RT_setMinMax - */ - function PMA_RT_addRelation($master_table, $master_field, $foreign_table, $foreign_field, $show_info) - { - if (!isset($this->tables[$master_table])) { - $this->tables[$master_table] = new PMA_RT_Table($master_table, $this->ff, $this->tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$master_table]); - } - if (!isset($this->tables[$foreign_table])) { - $this->tables[$foreign_table] = new PMA_RT_Table($foreign_table, $this->ff, $this->tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$foreign_table]); - } - $this->relations[] = new PMA_RT_Relation($this->tables[$master_table], $master_field, $this->tables[$foreign_table], $foreign_field); - } // end of the "PMA_RT_addRelation()" method - /** - * Draws the grid - * - * @global object the current PMA_PDF instance - * @access private - * @see PMA_PDF - */ - function PMA_RT_strokeGrid() - { - global $pdf; - - $pdf->SetMargins(0, 0); - $pdf->SetDrawColor(200, 200, 200); - // Draws horizontal lines - for ($l = 0; $l < 21; $l++) { - $pdf->line(0, $l * 10, $pdf->getFh(), $l * 10); - // Avoid duplicates - if ($l > 0) { - $pdf->SetXY(0, $l * 10); - $label = (string) sprintf('%.0f', ($l * 10 - $this->t_marg) * $this->scale + $this->y_min); - $pdf->Cell(5, 5, ' ' . $label); - } // end if - } // end for - // Draws vertical lines - for ($j = 0; $j < 30 ;$j++) { - $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); - $pdf->SetXY($j * 10, 0); - $label = (string) sprintf('%.0f', ($j * 10 - $this->l_marg) * $this->scale + $this->x_min); - $pdf->Cell(5, 7, $label); - } // end for - } // end of the "PMA_RT_strokeGrid()" method - /** - * Draws relation arrows - * - * @param boolean change_color Whether to use one color per relation or not - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_draw() - */ - function PMA_RT_drawRelations($change_color) - { - $i = 0; - foreach ($this->relations AS $relation) { - $relation->PMA_RT_Relation_draw($change_color, $i); - $i++; - } // end while - } // end of the "PMA_RT_drawRelations()" method - /** - * Draws tables - * - * @param boolean draw_color Whether to display table position or not - * @access private - * @see PMA_RT_Table::PMA_RT_Table_draw() - */ - function PMA_RT_drawTables($draw_color = 0) - { - foreach ($this->tables AS $table) { - $table->PMA_RT_Table_draw($this->ff, $draw_color); - } - } // end of the "PMA_RT_drawTables()" method - /** - * Ouputs the PDF document to a file - * - * @global object The current PDF document - * @global string The current database name - * @global integer The current page number (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @access private - * @see PMA_PDF - */ - function PMA_RT_showRt() - { - global $pdf, $db, $pdf_page_number, $cfgRelation; - - $pdf->SetFontSize(14); - $pdf->SetLineWidth(0.2); - $pdf->SetDisplayMode('fullpage'); - // Get the name of this pdfpage to use as filename (Mike Beck) - $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE page_nr = ' . $pdf_page_number; - $_name_rs = PMA_query_as_controluser($_name_sql); - if ($_name_rs) { - $_name_row = PMA_DBI_fetch_row($_name_rs); - $filename = $_name_row[0] . '.pdf'; - } - // i don't know if there is a chance for this to happen, but rather be on the safe side: - if (empty($filename)) { - $filename = $pdf_page_number . '.pdf'; - } - // $pdf->Output($db . '_' . $filename, TRUE); - $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline - } // end of the "PMA_RT_showRt()" method - /** - * The "PMA_RT" constructor - * - * @param integer which_rel The page number to draw (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @param boolean show_info Whether to display table position or not - * @param boolean change_color Was originally whether to use one color per - * relation or not, now enables/disables color - * everywhere, due to some problems printing with color - * @param boolean show_grid Whether to draw grids or not - * @param boolean all_tab_same_wide Whether all tables should have the same width or not - * @param boolean show_keys Wheter to show all field or only the keys - * @global object The current PDF document - * @global string The current db name - * @global array The relations settings - * @access private - * @see PMA_PDF - */ - function __construct($which_rel, $show_info = 0, $change_color = 0, $show_grid = 0, $all_tab_same_wide = 0, $orientation = 'L', $paper = 'A4', $show_keys = 0) - { - global $pdf, $db, $cfgRelation, $with_doc; - - $this->same_wide = $all_tab_same_wide; - // Initializes a new document - $pdf = new PMA_PDF('L', 'mm', $paper); - $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $which_rel)); - $pdf->setCMargin(0); - $pdf->Open(); - $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); - $pdf->AliasNbPages(); - $pdf->AddFont('DejaVuSans', '', 'dejavusans.php'); - $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); - $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); - $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); - $this->ff = PMA_PDF_FONT; - $pdf->SetFont($this->ff, '', 14); - $pdf->SetAutoPageBreak('auto'); - // Gets tables on this page - $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = ' . $which_rel; - $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); - if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { - $pdf->PMA_PDF_die(__('No tables')); - // die('No tables'); - } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { - $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); - // $intable = '\'' . implode('\', \'', $alltables) . '\''; - } - // make doc // - if ($with_doc) { - $pdf->SetAutoPageBreak('auto', 15); - $pdf->setCMargin(1); - PMA_RT_DOC($alltables); - $pdf->SetAutoPageBreak('auto'); - $pdf->setCMargin(0); - } - - $pdf->Addpage(); - - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); - $pdf->Bookmark(__('Relational schema')); - $pdf->SetAlias('{00}', $pdf->PageNo()) ; - $this->t_marg = 18; - $this->b_marg = 18; - } - - /* snip */ - - foreach ($alltables AS $table) { - if (!isset($this->tables[$table])) { - $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->tablewidth, $show_keys, $show_info); - } - - if ($this->same_wide) { - $this->tables[$table]->width = $this->tablewidth; - } - $this->PMA_RT_setMinMax($this->tables[$table]); - } - // Defines the scale factor - $this->scale = ceil( - max( - ($this->x_max - $this->x_min) / ($pdf->getFh() - $this->r_marg - $this->l_marg), - ($this->y_max - $this->y_min) / ($pdf->getFw() - $this->t_marg - $this->b_marg)) - * 100) / 100; - - $pdf->PMA_PDF_setScale($this->scale, $this->x_min, $this->y_min, $this->l_marg, $this->t_marg); - // Builds and save the PDF document - $pdf->PMA_PDF_setLineWidthScale(0.1); - - if ($show_grid) { - $pdf->SetFontSize(10); - $this->PMA_RT_strokeGrid(); - } - $pdf->PMA_PDF_setFontSizeScale(14); - // $sql = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - // . ' WHERE master_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND foreign_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND master_table IN (' . $intable . ')' - // . ' AND foreign_table IN (' . $intable . ')'; - // $result = PMA_query_as_controluser($sql); - - // previous logic was checking master tables and foreign tables - // but I think that looping on every table of the pdf page as a master - // and finding its foreigns is OK (then we can support innodb) - $seen_a_relation = false; - foreach ($alltables AS $one_table) { - $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); - if ($exist_rel) { - $seen_a_relation = true; - foreach ($exist_rel AS $master_field => $rel) { - // put the foreign table on the schema only if selected - // by the user - // (do not use array_search() because we would have to - // to do a === FALSE and this is not PHP3 compatible) - if (in_array($rel['foreign_table'], $alltables)) { - $this->PMA_RT_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); - } - } // end while - } // end if - } // end while - // also show tables without relations - // $norelations = TRUE; - // if ($result && PMA_DBI_num_rows($result) > 0) { - // $norelations = FALSE; - // while ($row = PMA_DBI_fetch_assoc($result)) { - // $this->PMA_RT_addRelation($row['master_table'], $row['master_field'], $row['foreign_table'], $row['foreign_field']); - // } - // } - // if ($norelations == FALSE) { - if ($seen_a_relation) { - $this->PMA_RT_drawRelations($change_color); - } - - $this->PMA_RT_drawTables($change_color); - - $this->PMA_RT_showRt(); - } // end of the "PMA_RT()" method -} // end of the "PMA_RT" class - -function PMA_RT_DOC($alltables) -{ - global $db, $pdf, $orientation, $paper; - // TOC - $pdf->addpage($GLOBALS['orientation']); - $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); - $pdf->Ln(15); - $i = 1; - foreach ($alltables AS $table) { - $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); - $pdf->SetX(10); - // $pdf->Ln(1); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); - // $pdf->Ln(1); - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); - while ($row = PMA_DBI_fetch_assoc($result)) { - $pdf->SetX(20); - $field_name = $row['Field']; - $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); - // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); - } - $lasttable = $table; - $i++; - } - $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); - $pdf->SetX(10); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); - $z = 0; - foreach ($alltables AS $table) { - $z++; - $pdf->addpage($GLOBALS['orientation']); - $pdf->Bookmark($table); - $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; - $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); - $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); - $pdf->SetFont('', 'B', 18); - $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); - $pdf->SetFont('', '', 8); - $pdf->ln(); - - $cfgRelation = PMA_getRelationsParam(); - $comments = PMA_getComments($db, $table); - if ($cfgRelation['mimework']) { - $mime_map = PMA_getMIME($db, $table, true); - } - - /** - * Gets table informations - */ - $showtable = PMA_Table::sGetStatusInfo($db, $table); - $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); - $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); - $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); - $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); - $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); - - /** - * Gets table keys and retains them - */ - $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); - $primary = ''; - $indexes = array(); - $lastIndex = ''; - $indexes_info = array(); - $indexes_data = array(); - $pk_array = array(); // will be use to emphasis prim. keys in the table - // view - while ($row = PMA_DBI_fetch_assoc($result)) { - // Backups the list of primary keys - if ($row['Key_name'] == 'PRIMARY') { - $primary .= $row['Column_name'] . ', '; - $pk_array[$row['Column_name']] = 1; - } - // Retains keys informations - if ($row['Key_name'] != $lastIndex) { - $indexes[] = $row['Key_name']; - $lastIndex = $row['Key_name']; - } - $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; - $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; - if (isset($row['Cardinality'])) { - $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; - } - // I don't know what does following column mean.... - // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; - $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; - - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; - if (isset($row['Sub_part'])) { - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; - } - } // end while - if ($result) { - PMA_DBI_free_result($result); - } - - /** - * Gets fields properties - */ - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); - $fields_cnt = PMA_DBI_num_rows($result); - // Check if we can use Relations (Mike Beck) - if (!empty($cfgRelation['relation'])) { - // Find which tables are related with the current one and write it in - // an array - $res_rel = PMA_getForeigners($db, $table); - - if (count($res_rel) > 0) { - $have_rel = true; - } else { - $have_rel = false; - } - } else { - $have_rel = false; - } // end if - /** - * Displays the comments of the table if MySQL >= 3.23 - */ - - $break = false; - if (!empty($show_comment)) { - $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); - $break = true; - } - - if (!empty($create_time)) { - $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); - $break = true; - } - - if (!empty($update_time)) { - $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); - $break = true; - } - - if (!empty($check_time)) { - $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); - $break = true; - } - - if ($break == true) { - $pdf->Cell(0, 3, '', 0, 1); - $pdf->Ln(); - } - - $pdf->SetFont('', 'B'); - if (isset($orientation) && $orientation == 'L') { - $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); - - if ($paper == 'A4') { - $comments_width = 67; - } else { - // this is really intended for 'letter' - /** - * @todo find optimal width for all formats - */ - $comments_width = 50; - } - $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); - } else { - $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); - } - $pdf->SetFont('', ''); - - while ($row = PMA_DBI_fetch_assoc($result)) { - $type = $row['Type']; - // reformat mysql query output - // set or enum types: slashes single quotes inside options - if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { - $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); - $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; - $type_nowrap = ''; - - $binary = 0; - $unsigned = 0; - $zerofill = 0; - } else { - $type_nowrap = ' nowrap="nowrap"'; - $type = preg_replace('@BINARY@i', '', $type); - $type = preg_replace('@ZEROFILL@i', '', $type); - $type = preg_replace('@UNSIGNED@i', '', $type); - if (empty($type)) { - $type = ' '; - } - - $binary = stristr($row['Type'], 'BINARY'); - $unsigned = stristr($row['Type'], 'UNSIGNED'); - $zerofill = stristr($row['Type'], 'ZEROFILL'); - } - $attribute = ' '; - if ($binary) { - $attribute = 'BINARY'; - } - if ($unsigned) { - $attribute = 'UNSIGNED'; - } - if ($zerofill) { - $attribute = 'UNSIGNED ZEROFILL'; - } - if (!isset($row['Default'])) { - if ($row['Null'] != '' && $row['Null'] != 'NO') { - $row['Default'] = 'NULL'; - } - } - $field_name = $row['Field']; - // $pdf->Ln(); - $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); - $pdf->Bookmark($field_name, 1, -1); - $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); - $pdf_row = array($field_name, - $type, - $attribute, - ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), - ((isset($row['Default'])) ? $row['Default'] : ''), - $row['Extra'], - ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), - ((isset($comments[$field_name])) ? $comments[$field_name] : ''), - ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') - ); - $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; - if (isset($res_rel[$field_name]['foreign_table']) AND - isset($res_rel[$field_name]['foreign_field']) AND - isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) - ) - { - $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; - } else { - unset($links[6]); - } - $pdf->Row($pdf_row, $links); - - /*$pdf->Cell(20, 8, $field_name, 1, 0, 'L', 0, $pdf->PMA_links['RT'][$table][$field_name]); - //echo ' ' . $field_name . ' ' . "\n"; - } - $pdf->Cell(20, 8, $type, 1, 0, 'L'); - $pdf->Cell(20, 8, $attribute, 1, 0, 'L'); - $pdf->Cell(15, 8, , 1, 0, 'L'); - $pdf->Cell(15, 8, ((isset($row['Default'])) ? $row['Default'] : ''),1,0,'L'); - $pdf->Cell(15, 8, $row['Extra'], 1, 0, 'L'); - if ($have_rel) { - if (isset($res_rel[$field_name])) { - $pdf->Cell(30, 8, $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'],1,0,'L'); - } - } - if ($cfgRelation['commwork']) { - if (isset($comments[$field_name])) { - $pdf->Cell(0, 8, $comments[$field_name], 1, 0, 'L'); - } - } */ - } // end while - $pdf->SetFont('', '', 14); - PMA_DBI_free_result($result); - } //end each -} // end function PMA_RT_DOC - -/** - * Main logic - */ -if (!isset($pdf_page_number)) { - $pdf_page_number = 1; -} - -$show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; -$show_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; -$show_table_dimension = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; -$all_tab_same_wide = (isset($all_tab_same_wide) && $all_tab_same_wide == 'on') ? 1 : 0; -$with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; -$orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; -$paper = isset($paper) ? $paper : 'A4'; -$show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; -PMA_DBI_select_db($db); - -$rt = new PMA_RT($pdf_page_number, $show_table_dimension, $show_color, $show_grid, $all_tab_same_wide, $orientation, $paper, $show_keys); - -?>