Merge pull request #18854 from kamil-tekiela/More-Designer-fixes

More Designer refactorings
This commit is contained in:
Maurício Meneghini Fauth 2023-12-23 21:02:45 -03:00 committed by GitHub
commit 76f2341f41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 249 deletions

View File

@ -1610,11 +1610,6 @@ parameters:
count: 2
path: src/Controllers/Database/DesignerController.php
-
message: "#^Parameter \\#1 \\$db of method PhpMyAdmin\\\\Database\\\\Designer\\:\\:getDatabaseTables\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Controllers/Database/DesignerController.php
-
message: "#^Parameter \\#1 \\$db of method PhpMyAdmin\\\\Database\\\\Designer\\:\\:getHtmlForEditOrDeletePages\\(\\) expects string, mixed given\\.$#"
count: 2
@ -1650,6 +1645,11 @@ parameters:
count: 1
path: src/Controllers/Database/DesignerController.php
-
message: "#^Parameter \\#1 \\$db of static method PhpMyAdmin\\\\Url\\:\\:getHiddenInputs\\(\\) expects array\\|string, mixed given\\.$#"
count: 1
path: src/Controllers/Database/DesignerController.php
-
message: "#^Parameter \\#1 \\$index of method PhpMyAdmin\\\\Database\\\\Designer\\\\Common\\:\\:saveSetting\\(\\) expects string, mixed given\\.$#"
count: 1
@ -1710,11 +1710,6 @@ parameters:
count: 1
path: src/Controllers/Database/DesignerController.php
-
message: "#^Parameter \\#2 \\$getDb of method PhpMyAdmin\\\\Database\\\\Designer\\:\\:getHtmlForMain\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Controllers/Database/DesignerController.php
-
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\Database\\\\Designer\\\\Common\\:\\:getTablesInfo\\(\\) expects string\\|null, mixed given\\.$#"
count: 2
@ -6965,11 +6960,6 @@ parameters:
count: 1
path: src/Database/Designer/Common.php
-
message: "#^Only booleans are allowed in a negated boolean, bool\\|string given\\.$#"
count: 1
path: src/Database/Designer/Common.php
-
message: "#^Only booleans are allowed in an if condition, PhpMyAdmin\\\\Dbal\\\\ResultInterface\\|false given\\.$#"
count: 2

View File

@ -70,12 +70,14 @@
designerTable.getTableName()|escape('url'),
tab_column[t_n][j].name|url_encode
] %}
{% if not designerTable.supportsForeignkeys() %}
{% set click_field_param = click_field_param|merge([tables_pk_or_unique_keys[tmp_column] is defined ? 1 : 0]) %}
{% else %}
{# if foreign keys are supported, it's not necessary that the
index is a primary key #}
{% set click_field_param = click_field_param|merge([tables_all_keys[tmp_column] is defined ? 1 : 0]) %}
{% if tables_all_keys[tmp_column] is defined %}
{% if not designerTable.supportsForeignkeys() %}
{% set click_field_param = click_field_param|merge([tables_all_keys[tmp_column] ? 1 : 0]) %}
{% else %}
{# if foreign keys are supported, it's not necessary that the
index is a primary key #}
{% set click_field_param = click_field_param|merge([1]) %}
{% endif %}
{% endif %}
{% set click_field_param = click_field_param|merge([db]) %}
<tr id="id_tr_{{ designerTable.getTableName()|escape('url') }}.{{ tab_column[t_n][j].name }}" class="tab_field

View File

@ -233,13 +233,11 @@ window.designerConfig = {{ designer_config|raw }};
{% include 'database/designer/database_tables.twig' with {
'db': db,
'text_dir': text_dir,
'get_db': get_db,
'has_query': has_query,
'tab_pos': tab_pos,
'display_page': display_page,
'tab_column': tab_column,
'tables_all_keys': tables_all_keys,
'tables_pk_or_unique_keys': tables_pk_or_unique_keys,
'columns_type': columns_type,
'tables': designerTables,
} only %}
@ -1122,5 +1120,25 @@ window.designerConfig = {{ designer_config|raw }};
</div>
</div>
{% if visual_builder %}
{{ include('modals/build_query.twig', {'get_db': get_db}) }}
<div class="modal fade" id="buildQueryModal" tabindex="-1" aria-labelledby="buildQueryModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="buildQueryModalLabel">{% trans 'Loading' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans 'Close' %}"></button>
</div>
<div id="box" class="modal-body">
<form method="post" action="{{ url('/database/qbe') }}" id="vqb_form">
<textarea cols="80" name="sql_query" id="textSqlquery" rows="15"></textarea>
<input type="hidden" name="submit_sql" value="true">
{{ hidden_input_fields|raw }}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Close' %}</button>
<button type="button" class="btn btn-secondary" id="buildQuerySubmitButton">{% trans 'Submit' %}</button>
</div>
</div>
</div>
</div>
{% endif %}

View File

@ -1,21 +0,0 @@
<div class="modal fade" id="buildQueryModal" tabindex="-1" aria-labelledby="buildQueryModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="buildQueryModalLabel">{% trans 'Loading' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans 'Close' %}"></button>
</div>
<div id="box" class="modal-body">
<form method="post" action="{{ url('/database/qbe') }}" id="vqb_form">
<textarea cols="80" name="sql_query" id="textSqlquery" rows="15"></textarea>
<input type="hidden" name="submit_sql" value="true">
{{ get_hidden_inputs(get_db) }}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Close' %}</button>
<button type="button" class="btn btn-secondary" id="buildQuerySubmitButton">{% trans 'Submit' %}</button>
</div>
</div>
</div>
</div>

View File

@ -21,6 +21,7 @@ use PhpMyAdmin\Util;
use function __;
use function htmlspecialchars;
use function in_array;
use function json_encode;
use function sprintf;
class DesignerController extends AbstractController
@ -39,6 +40,7 @@ class DesignerController extends AbstractController
{
$GLOBALS['message'] ??= null;
$GLOBALS['errorUrl'] ??= null;
$GLOBALS['text_dir'] ??= null;
$db = $request->getParsedBodyParam('db');
$table = $request->getParsedBodyParam('table');
@ -62,17 +64,19 @@ class DesignerController extends AbstractController
$scriptDisplayField = $this->designerCommon->getTablesInfo($db, $table);
$tableColumn = $this->designerCommon->getColumnsInfo($scriptDisplayField);
$tablesAllKeys = $this->designerCommon->getAllKeys($scriptDisplayField);
$tablesPkOrUniqueKeys = $this->designerCommon->getPkOrUniqueKeys($scriptDisplayField);
$columnsType = $this->databaseDesigner->getColumnTypes($tableColumn, $tablesAllKeys);
$html = $this->databaseDesigner->getDatabaseTables(
$db,
$scriptDisplayField,
[],
-1,
$tableColumn,
$tablesAllKeys,
$tablesPkOrUniqueKeys,
);
$html = $this->template->render('database/designer/database_tables', [
'db' => Current::$database,
'text_dir' => $GLOBALS['text_dir'],
'has_query' => isset($_REQUEST['query']),
'tab_pos' => [],
'display_page' => -1,
'tab_column' => $tableColumn,
'tables_all_keys' => $tablesAllKeys,
'tables' => $scriptDisplayField,
'columns_type' => $columnsType,
]);
}
if ($html !== '') {
@ -210,10 +214,8 @@ class DesignerController extends AbstractController
$tableColumn = $this->designerCommon->getColumnsInfo($scriptDisplayField);
$scriptTables = $this->designerCommon->getScriptTabs($scriptDisplayField);
$tablesPkOrUniqueKeys = $this->designerCommon->getPkOrUniqueKeys($scriptDisplayField);
$tablesAllKeys = $this->designerCommon->getAllKeys($scriptDisplayField);
$classesSideMenu = $this->databaseDesigner->returnClassNamesFromMenuButtons();
$scriptContr = $this->designerCommon->getScriptContr($scriptDisplayField);
$this->response->setMinimalFooter();
@ -222,26 +224,36 @@ class DesignerController extends AbstractController
$this->addScriptFiles(['designer/init.js']);
$columnsType = $this->databaseDesigner->getColumnTypes($tableColumn, $tablesAllKeys);
$designerConfig = $this->databaseDesigner->getDesignerConfig(
Current::$database,
$scriptDisplayField,
$scriptTables,
$scriptContr,
$displayPage,
);
$mainHtml = $this->template->render('database/designer/main', [
'db' => Current::$database,
'text_dir' => $GLOBALS['text_dir'],
'hidden_input_fields' => Url::getHiddenInputs($request->getQueryParam('db')),
'designer_config' => json_encode($designerConfig),
'display_page' => $displayPage,
'has_query' => $visualBuilderMode,
'visual_builder' => $visualBuilderMode,
'selected_page' => $selectedPage,
'params_array' => $classesSideMenu,
'tab_pos' => $tablePositions,
'tab_column' => $tableColumn,
'tables_all_keys' => $tablesAllKeys,
'designerTables' => $scriptDisplayField,
'columns_type' => $columnsType,
]);
// Embed some data into HTML, later it will be read
// by designer/init.js and converted to JS variables.
$this->response->addHTML(
$this->databaseDesigner->getHtmlForMain(
Current::$database,
$request->getQueryParam('db'),
$scriptDisplayField,
$scriptTables,
$scriptContr,
$scriptDisplayField,
$displayPage,
$visualBuilderMode,
$selectedPage,
$classesSideMenu,
$tablePositions,
$tableColumn,
$tablesAllKeys,
$tablesPkOrUniqueKeys,
),
);
$this->response->addHTML($mainHtml);
$this->response->addHTML('<div id="PMA_disable_floating_menubar"></div>');
}

View File

@ -6,7 +6,6 @@ namespace PhpMyAdmin\Database;
use PhpMyAdmin\Config;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Current;
use PhpMyAdmin\Database\Designer\ColumnInfo;
use PhpMyAdmin\Database\Designer\DesignerTable;
use PhpMyAdmin\DatabaseInterface;
@ -20,7 +19,6 @@ use function __;
use function intval;
use function is_array;
use function json_decode;
use function json_encode;
use function str_contains;
/**
@ -168,77 +166,33 @@ class Designer
*/
public function returnClassNamesFromMenuButtons(): array
{
$classesArray = [];
$paramsArray = $this->getSideMenuParamsArray();
$selectedButton = 'M_butt_Selected_down';
$normalButton = 'M_butt';
if (isset($paramsArray['angular_direct']) && $paramsArray['angular_direct'] === 'angular') {
$classesArray['angular_direct'] = 'M_butt_Selected_down';
} else {
$classesArray['angular_direct'] = 'M_butt';
}
if (isset($paramsArray['snap_to_grid']) && $paramsArray['snap_to_grid'] === 'on') {
$classesArray['snap_to_grid'] = 'M_butt_Selected_down';
} else {
$classesArray['snap_to_grid'] = 'M_butt';
}
if (isset($paramsArray['pin_text']) && $paramsArray['pin_text'] === 'true') {
$classesArray['pin_text'] = 'M_butt_Selected_down';
} else {
$classesArray['pin_text'] = 'M_butt';
}
if (isset($paramsArray['relation_lines']) && $paramsArray['relation_lines'] === 'false') {
$classesArray['relation_lines'] = 'M_butt_Selected_down';
} else {
$classesArray['relation_lines'] = 'M_butt';
}
if (isset($paramsArray['small_big_all']) && $paramsArray['small_big_all'] === 'v') {
$classesArray['small_big_all'] = 'M_butt_Selected_down';
} else {
$classesArray['small_big_all'] = 'M_butt';
}
if (isset($paramsArray['side_menu']) && $paramsArray['side_menu'] === 'true') {
$classesArray['side_menu'] = 'M_butt_Selected_down';
} else {
$classesArray['side_menu'] = 'M_butt';
}
return $classesArray;
return [
'angular_direct' => ($paramsArray['angular_direct'] ?? '') === 'angular' ? $selectedButton : $normalButton,
'snap_to_grid' => ($paramsArray['snap_to_grid'] ?? '') === 'on' ? $selectedButton : $normalButton,
'pin_text' => ($paramsArray['pin_text'] ?? '') === 'true' ? $selectedButton : $normalButton,
'relation_lines' => ($paramsArray['relation_lines'] ?? '') === 'false' ? $selectedButton : $normalButton,
'small_big_all' => ($paramsArray['small_big_all'] ?? '') === 'v' ? $selectedButton : $normalButton,
'side_menu' => ($paramsArray['side_menu'] ?? '') === 'true' ? $selectedButton : $normalButton,
];
}
/**
* Get HTML to display tables on designer page
* @param list<ColumnInfo>[] $tableColumnsInfo table column info
* @param array<string, bool> $tablesAllKeys unique or primary indices
*
* @param string $db The database name from the request
* @param DesignerTable[] $designerTables The designer tables
* @param mixed[] $tabPos tables positions
* @param int $displayPage page number of the selected page
* @param list<ColumnInfo>[] $tableColumnsInfo table column info
* @param mixed[] $tablesAllKeys all indices
* @param mixed[] $tablesPkOrUniqueKeys unique or primary indices
*
* @return string html
* @return array<string, string>
*/
public function getDatabaseTables(
string $db,
array $designerTables,
array $tabPos,
int $displayPage,
array $tableColumnsInfo,
array $tablesAllKeys,
array $tablesPkOrUniqueKeys,
): string {
$GLOBALS['text_dir'] ??= null;
public function getColumnTypes(array $tableColumnsInfo, array $tablesAllKeys): array
{
$columnsType = [];
foreach ($tableColumnsInfo as $tableName => $columnsInfo) {
foreach ($columnsInfo as $columnInfo) {
$tableColumnName = $tableName . '.' . $columnInfo->name;
if (isset($tablesPkOrUniqueKeys[$tableColumnName])) {
if (isset($tablesAllKeys[$tableColumnName]) && $tablesAllKeys[$tableColumnName]) {
$columnsType[$tableColumnName] = 'designer/FieldKey_small';
} else {
$columnsType[$tableColumnName] = 'designer/Field_small';
@ -265,93 +219,25 @@ class Designer
}
}
return $this->template->render('database/designer/database_tables', [
'db' => Current::$database,
'text_dir' => $GLOBALS['text_dir'],
'get_db' => $db,
'has_query' => isset($_REQUEST['query']),
'tab_pos' => $tabPos,
'display_page' => $displayPage,
'tab_column' => $tableColumnsInfo,
'tables_all_keys' => $tablesAllKeys,
'tables_pk_or_unique_keys' => $tablesPkOrUniqueKeys,
'tables' => $designerTables,
'columns_type' => $columnsType,
]);
return $columnsType;
}
/**
* Returns HTML for Designer page
*
* @param string $db database in use
* @param string $getDb database in url
* @param DesignerTable[] $designerTables The designer tables
* @param mixed[] $scriptTables array on foreign key support for each table
* @param mixed[] $scriptContr initialization data array
* @param DesignerTable[] $scriptDisplayField displayed tables in designer with their display fields
* @param int $displayPage page number of the selected page
* @param bool $visualBuilderMode whether this is visual query builder
* @param string|null $selectedPage name of the selected page
* @param mixed[] $paramsArray array with class name for various buttons on side menu
* @param mixed[] $tablePositions table positions
* @param list<ColumnInfo>[] $tableColumnsInfo table column info
* @param mixed[] $tablesAllKeys all indices
* @param mixed[] $tablesPkOrUniqueKeys unique or primary indices
*
* @return string html
* @param DesignerTable[] $designerTables The designer tables
* @param mixed[] $scriptTables array on foreign key support for each table
* @param mixed[] $scriptContr initialization data array
*/
public function getHtmlForMain(
public function getDesignerConfig(
string $db,
string $getDb,
array $designerTables,
array $scriptTables,
array $scriptContr,
array $scriptDisplayField,
int $displayPage,
bool $visualBuilderMode,
string|null $selectedPage,
array $paramsArray,
array $tablePositions,
array $tableColumnsInfo,
array $tablesAllKeys,
array $tablesPkOrUniqueKeys,
): string {
$GLOBALS['text_dir'] ??= null;
): stdClass {
$relationParameters = $this->relation->getRelationParameters();
$columnsType = [];
foreach ($tableColumnsInfo as $tableName => $columnsInfo) {
foreach ($columnsInfo as $columnInfo) {
$tableColumnName = $tableName . '.' . $columnInfo->name;
if (isset($tablesPkOrUniqueKeys[$tableColumnName])) {
$columnsType[$tableColumnName] = 'designer/FieldKey_small';
} else {
$columnsType[$tableColumnName] = 'designer/Field_small';
if (
str_contains($columnInfo->type, 'char')
|| str_contains($columnInfo->type, 'text')
) {
$columnsType[$tableColumnName] .= '_char';
} elseif (
str_contains($columnInfo->type, 'int')
|| str_contains($columnInfo->type, 'float')
|| str_contains($columnInfo->type, 'double')
|| str_contains($columnInfo->type, 'decimal')
) {
$columnsType[$tableColumnName] .= '_int';
} elseif (
str_contains($columnInfo->type, 'date')
|| str_contains($columnInfo->type, 'time')
|| str_contains($columnInfo->type, 'year')
) {
$columnsType[$tableColumnName] .= '_date';
}
}
}
}
$displayedFields = [];
foreach ($scriptDisplayField as $designerTable) {
foreach ($designerTables as $designerTable) {
if ($designerTable->getDisplayField() === null) {
continue;
}
@ -368,22 +254,6 @@ class Designer
$designerConfig->displayPage = $displayPage;
$designerConfig->tablesEnabled = $relationParameters->pdfFeature !== null;
return $this->template->render('database/designer/main', [
'db' => $db,
'text_dir' => $GLOBALS['text_dir'],
'get_db' => $getDb,
'designer_config' => json_encode($designerConfig),
'display_page' => $displayPage,
'has_query' => $visualBuilderMode,
'visual_builder' => $visualBuilderMode,
'selected_page' => $selectedPage,
'params_array' => $paramsArray,
'tab_pos' => $tablePositions,
'tab_column' => $tableColumnsInfo,
'tables_all_keys' => $tablesAllKeys,
'tables_pk_or_unique_keys' => $tablesPkOrUniqueKeys,
'designerTables' => $designerTables,
'columns_type' => $columnsType,
]);
return $designerConfig;
}
}

View File

@ -175,27 +175,14 @@ class Common
return $retval;
}
/**
* Returns UNIQUE and PRIMARY indices
*
* @param DesignerTable[] $designerTables The designer tables
*
* @return mixed[] unique or primary indices
*/
public function getPkOrUniqueKeys(array $designerTables): array
{
return $this->getAllKeys($designerTables, true);
}
/**
* Returns all indices
*
* @param DesignerTable[] $designerTables The designer tables
* @param bool $uniqueOnly whether to include only unique ones
*
* @return mixed[] indices
* @return array<string, bool>
*/
public function getAllKeys(array $designerTables, bool $uniqueOnly = false): array
public function getAllKeys(array $designerTables): array
{
$keys = [];
@ -203,13 +190,10 @@ class Common
$schema = $designerTable->getDatabaseName();
// for now, take into account only the first index segment
foreach (Index::getFromTable($this->dbi, $designerTable->getTableName(), $schema) as $index) {
if ($uniqueOnly && ! $index->isUnique()) {
continue;
}
$columns = $index->getColumns();
foreach (array_keys($columns) as $columnName) {
$keys[$schema . '.' . $designerTable->getTableName() . '.' . $columnName] = 1;
$key = $schema . '.' . $designerTable->getTableName() . '.' . $columnName;
$keys[$key] = ! $index->getNonUnique();
}
}
}