diff --git a/libraries/classes/CentralColumns.php b/libraries/classes/CentralColumns.php index 3f2b600bab..6a8e80fb03 100644 --- a/libraries/classes/CentralColumns.php +++ b/libraries/classes/CentralColumns.php @@ -10,6 +10,7 @@ namespace PhpMyAdmin; use PhpMyAdmin\Charsets\Charset; use PhpMyAdmin\Charsets\Collation; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; /** @@ -934,14 +935,14 @@ class CentralColumns 'text_dir' => $text_dir, 'form_name' => 'tableslistcontainer', ]); - $html_output .= Util::getButtonOrImage( + $html_output .= Generator::getButtonOrImage( 'edit_central_columns', 'mult_submit change_central_columns', __('Edit'), 'b_edit', 'edit central columns' ); - $html_output .= Util::getButtonOrImage( + $html_output .= Generator::getButtonOrImage( 'delete_central_columns', 'mult_submit', __('Delete'), diff --git a/libraries/classes/Config/FormDisplay.php b/libraries/classes/Config/FormDisplay.php index 79ad04d4d4..0e69a89cd7 100644 --- a/libraries/classes/Config/FormDisplay.php +++ b/libraries/classes/Config/FormDisplay.php @@ -16,6 +16,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Config; use PhpMyAdmin\Config\Forms\User\UserFormList; +use PhpMyAdmin\Html\MySQLDocumentation; use PhpMyAdmin\Sanitize; use PhpMyAdmin\Util; @@ -785,7 +786,7 @@ class FormDisplay if ($test == 'Import' || $test == 'Export') { return ''; } - return Util::getDocuLink( + return MySQLDocumentation::getDocumentationLink( 'config', 'cfg_' . $this->_getOptName($path) ); diff --git a/libraries/classes/Config/FormDisplayTemplate.php b/libraries/classes/Config/FormDisplayTemplate.php index b680d635a1..c31d7c91a0 100644 --- a/libraries/classes/Config/FormDisplayTemplate.php +++ b/libraries/classes/Config/FormDisplayTemplate.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Config; use PhpMyAdmin\Config; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Sanitize; use PhpMyAdmin\Template; use PhpMyAdmin\Url; @@ -229,7 +230,7 @@ class FormDisplayTemplate } else { // In this case we just use getImage() because it's available foreach ($iconInit as $k => $v) { - $icons[$k] = Util::getImage( + $icons[$k] = Generator::getImage( $v[0], $v[1] ); diff --git a/libraries/classes/Console.php b/libraries/classes/Console.php index 28fcde1af7..cc0190de91 100644 --- a/libraries/classes/Console.php +++ b/libraries/classes/Console.php @@ -139,7 +139,7 @@ class Console $GLOBALS['cfg']['Server']['user'] ); - $image = Util::getImage('console', __('SQL Query Console')); + $image = Html\Generator::getImage('console', __('SQL Query Console')); $_sql_history = $this->relation->getHistory( $GLOBALS['cfg']['Server']['user'] ); diff --git a/libraries/classes/Controllers/Database/RoutinesController.php b/libraries/classes/Controllers/Database/RoutinesController.php index e4e72e0ecc..020bcdebdf 100644 --- a/libraries/classes/Controllers/Database/RoutinesController.php +++ b/libraries/classes/Controllers/Database/RoutinesController.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Database; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Rte\Routines; use PhpMyAdmin\Util; diff --git a/libraries/classes/Controllers/Database/StructureController.php b/libraries/classes/Controllers/Database/StructureController.php index a7481c0cc1..f9c01434c5 100644 --- a/libraries/classes/Controllers/Database/StructureController.php +++ b/libraries/classes/Controllers/Database/StructureController.php @@ -13,6 +13,7 @@ use PhpMyAdmin\Config\PageSettings; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Display\CreateTable; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; use PhpMyAdmin\RecentFavoriteTable; use PhpMyAdmin\Relation; @@ -97,17 +98,8 @@ class StructureController extends AbstractController */ private function getDatabaseInfo(string $subPart): void { - list( - $tables, - $numTables, - $totalNumTables, - , - $isShowStats, - $dbIsSystemSchema, - , - , - $position - ) = Util::getDbInfo($this->db, $subPart); + [$tables, $numTables, $totalNumTables,, $isShowStats, $dbIsSystemSchema,,, $position] + = Util::getDbInfo($this->db, $subPart); $this->tables = $tables; $this->numTables = $numTables; @@ -171,7 +163,7 @@ class StructureController extends AbstractController if (isset($parameters['sort_order'])) { $urlParams['sort_order'] = $parameters['sort_order']; } - $listNavigator = Util::getListNavigator( + $listNavigator = Generator::getListNavigator( $this->totalNumTables, $this->position, $urlParams, @@ -395,13 +387,20 @@ class StructureController extends AbstractController ]; // do not list the previous table's size info for a view - list($current_table, $formatted_size, $unit, $formatted_overhead, - $overhead_unit, $overhead_size, $table_is_view, $sum_size) - = $this->getStuffForEngineTypeTable( - $current_table, - $sum_size, - $overhead_size - ); + [ + $current_table, + $formatted_size, + $unit, + $formatted_overhead, + $overhead_unit, + $overhead_size, + $table_is_view, + $sum_size, + ] = $this->getStuffForEngineTypeTable( + $current_table, + $sum_size, + $overhead_size + ); $curTable = $this->dbi ->getTable($this->db, $current_table['TABLE_NAME']); @@ -544,12 +543,12 @@ class StructureController extends AbstractController $structure_table_rows = []; } - list($approx_rows, $show_superscript) = $this->isRowCountApproximated( + [$approx_rows, $show_superscript] = $this->isRowCountApproximated( $current_table, $table_is_view ); - list($do, $ignored) = $this->getReplicationStatus($truename); + [$do, $ignored] = $this->getReplicationStatus($truename); $structure_table_rows[] = [ 'table_name_hash' => md5($current_table['TABLE_NAME']), @@ -739,7 +738,7 @@ class StructureController extends AbstractController && $current_table['TABLE_ROWS'] >= $GLOBALS['cfg']['MaxExactCountViews'] ) { $approx_rows = true; - $show_superscript = Util::showHint( + $show_superscript = Generator::showHint( Sanitize::sanitizeMessage( sprintf( __( @@ -928,9 +927,15 @@ class StructureController extends AbstractController case 'ARCHIVE': case 'Aria': case 'Maria': - list($current_table, $formatted_size, $unit, $formatted_overhead, - $overhead_unit, $overhead_size, $sum_size) - = $this->getValuesForAriaTable( + [ + $current_table, + $formatted_size, + $unit, + $formatted_overhead, + $overhead_unit, + $overhead_size, + $sum_size, + ] = $this->getValuesForAriaTable( $current_table, $sum_size, $overhead_size, @@ -946,7 +951,7 @@ class StructureController extends AbstractController // InnoDB table: Row count is not accurate but data and index sizes are. // PBMS table in Drizzle: TABLE_ROWS is taken from table cache, // so it may be unavailable - list($current_table, $formatted_size, $unit, $sum_size) + [$current_table, $formatted_size, $unit, $sum_size] = $this->getValuesForInnodbTable( $current_table, $sum_size @@ -1033,7 +1038,7 @@ class StructureController extends AbstractController $tblsize = $current_table['Data_length'] + $current_table['Index_length']; $sum_size += $tblsize; - list($formatted_size, $unit) = Util::formatByteDown( + [$formatted_size, $unit] = Util::formatByteDown( $tblsize, 3, $tblsize > 0 ? 1 : 0 @@ -1041,7 +1046,7 @@ class StructureController extends AbstractController if (isset($current_table['Data_free']) && $current_table['Data_free'] > 0 ) { - list($formatted_overhead, $overhead_unit) + [$formatted_overhead, $overhead_unit] = Util::formatByteDown( $current_table['Data_free'], 3, @@ -1091,7 +1096,7 @@ class StructureController extends AbstractController $tblsize = $current_table['Data_length'] + $current_table['Index_length']; $sum_size += $tblsize; - list($formatted_size, $unit) = Util::formatByteDown( + [$formatted_size, $unit] = Util::formatByteDown( $tblsize, 3, ($tblsize > 0 ? 1 : 0) diff --git a/libraries/classes/Controllers/HomeController.php b/libraries/classes/Controllers/HomeController.php index e8ed894e3c..0f8af98bcc 100644 --- a/libraries/classes/Controllers/HomeController.php +++ b/libraries/classes/Controllers/HomeController.php @@ -15,6 +15,7 @@ use PhpMyAdmin\CheckUserPrivileges; use PhpMyAdmin\Config; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Display\GitRevision; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\LanguageManager; use PhpMyAdmin\Message; use PhpMyAdmin\RecentFavoriteTable; @@ -84,7 +85,7 @@ class HomeController extends AbstractController $languageManager = LanguageManager::getInstance(); if (! empty($message)) { - $displayMessage = Util::getMessage($message); + $displayMessage = Generator::getMessage($message); unset($message); } if (isset($_SESSION['partial_logout'])) { @@ -114,7 +115,7 @@ class HomeController extends AbstractController if (($cfg['Server']['auth_type'] != 'config') && $cfg['ShowChgPassword']) { $changePassword = $this->template->render('list/item', [ - 'content' => Util::getImage('s_passwd') . ' ' . __( + 'content' => Generator::getImage('s_passwd') . ' ' . __( 'Change password' ), 'id' => 'li_change_password', @@ -151,7 +152,7 @@ class HomeController extends AbstractController } $userPreferences = $this->template->render('list/item', [ - 'content' => Util::getImage('b_tblops') . ' ' . __( + 'content' => Generator::getImage('b_tblops') . ' ' . __( 'More settings' ), 'id' => 'li_user_preferences', @@ -197,7 +198,7 @@ class HomeController extends AbstractController $databaseServer = [ 'host' => $hostInfo, 'type' => Util::getServerType(), - 'connection' => Util::getServerSSL(), + 'connection' => Generator::getServerSSL(), 'version' => $this->dbi->getVersionString() . ' - ' . $this->dbi->getVersionComment(), 'protocol' => $this->dbi->getProtoInfo(), 'user' => $this->dbi->fetchValue('SELECT USER();'), diff --git a/libraries/classes/Controllers/Server/BinlogController.php b/libraries/classes/Controllers/Server/BinlogController.php index 03474733e5..3f5a8ef1d1 100644 --- a/libraries/classes/Controllers/Server/BinlogController.php +++ b/libraries/classes/Controllers/Server/BinlogController.php @@ -10,6 +10,7 @@ namespace PhpMyAdmin\Controllers\Server; use PhpMyAdmin\Controllers\AbstractController; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; use PhpMyAdmin\Response; use PhpMyAdmin\Template; @@ -112,7 +113,7 @@ class BinlogController extends AbstractController 'url_params' => $urlParams, 'binary_logs' => $this->binaryLogs, 'log' => $params['log'], - 'sql_message' => Util::getMessage(Message::success(), $sqlQuery), + 'sql_message' => Generator::getMessage(Message::success(), $sqlQuery), 'values' => $values, 'has_previous' => $position > 0, 'has_next' => $numRows >= $cfg['MaxRows'], diff --git a/libraries/classes/Controllers/Server/DatabasesController.php b/libraries/classes/Controllers/Server/DatabasesController.php index 475f876e55..c35d303bd1 100644 --- a/libraries/classes/Controllers/Server/DatabasesController.php +++ b/libraries/classes/Controllers/Server/DatabasesController.php @@ -14,6 +14,7 @@ use PhpMyAdmin\Charsets\Collation; use PhpMyAdmin\CheckUserPrivileges; use PhpMyAdmin\Controllers\AbstractController; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; use PhpMyAdmin\Response; use PhpMyAdmin\Template; @@ -192,7 +193,7 @@ class DatabasesController extends AbstractController */ $sqlQuery = 'CREATE DATABASE ' . Util::backquote($params['new_db']); if (! empty($params['db_collation'])) { - list($databaseCharset) = explode('_', $params['db_collation']); + [$databaseCharset] = explode('_', $params['db_collation']); $charsets = Charsets::getCharsets( $this->dbi, $cfg['Server']['DisableIS'] @@ -233,7 +234,7 @@ class DatabasesController extends AbstractController $json = [ 'message' => $message, - 'sql_query' => Util::getMessage(null, $sqlQuery, 'success'), + 'sql_query' => Generator::getMessage(null, $sqlQuery, 'success'), 'url_query' => $scriptName . Url::getCommon( ['db' => $params['new_db']], strpos($scriptName, '?') === false ? '?' : '&' diff --git a/libraries/classes/Controllers/Server/Status/ProcessesController.php b/libraries/classes/Controllers/Server/Status/ProcessesController.php index 8a788fe19c..33d91aa02f 100644 --- a/libraries/classes/Controllers/Server/Status/ProcessesController.php +++ b/libraries/classes/Controllers/Server/Status/ProcessesController.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Server\Status; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; use PhpMyAdmin\Util; @@ -237,7 +238,7 @@ class ProcessesController extends AbstractController 'time' => $process['Time'], 'state' => ! empty($process['State']) ? $process['State'] : '---', 'progress' => ! empty($process['Progress']) ? $process['Progress'] : '---', - 'info' => ! empty($process['Info']) ? Util::formatSql( + 'info' => ! empty($process['Info']) ? Generator::formatSql( $process['Info'], ! $showFullSql ) : '---', diff --git a/libraries/classes/Controllers/Server/Status/VariablesController.php b/libraries/classes/Controllers/Server/Status/VariablesController.php index f7ecffe696..f20ca286e2 100644 --- a/libraries/classes/Controllers/Server/Status/VariablesController.php +++ b/libraries/classes/Controllers/Server/Status/VariablesController.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Server\Status; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Util; /** @@ -79,7 +80,7 @@ class VariablesController extends AbstractController // Fields containing % are calculated, // they can not be described in MySQL documentation if (mb_strpos($name, '%') === false) { - $variables[$name]['doc'] = Util::linkToVarDocumentation( + $variables[$name]['doc'] = Generator::linkToVarDocumentation( $name, $this->dbi->isMariaDB() ); diff --git a/libraries/classes/Controllers/Server/VariablesController.php b/libraries/classes/Controllers/Server/VariablesController.php index 89cd9f3d70..038bcfba62 100644 --- a/libraries/classes/Controllers/Server/VariablesController.php +++ b/libraries/classes/Controllers/Server/VariablesController.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Server; use PhpMyAdmin\Controllers\AbstractController; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Util; use Williamdes\MariaDBMySQLKBS\KBException; use Williamdes\MariaDBMySQLKBS\Search as KBSearch; @@ -54,15 +55,17 @@ class VariablesController extends AbstractController foreach ($serverVars as $name => $value) { $hasSessionValue = isset($serverVarsSession[$name]) && $serverVarsSession[$name] !== $value; - $docLink = Util::linkToVarDocumentation( + $docLink = Generator::linkToVarDocumentation( $name, $this->dbi->isMariaDB(), str_replace('_', ' ', $name) ); - list($formattedValue, $isEscaped) = $this->formatVariable($name, $value); + [$formattedValue, $isEscaped] = $this->formatVariable($name, $value); if ($hasSessionValue) { - list($sessionFormattedValue, ) = $this->formatVariable( + [ + $sessionFormattedValue, + ] = $this->formatVariable( $name, $serverVarsSession[$name] ); @@ -187,7 +190,7 @@ class VariablesController extends AbstractController . '";', 'NUM' ); - list($formattedValue, $isHtmlFormatted) = $this->formatVariable( + [$formattedValue, $isHtmlFormatted] = $this->formatVariable( $params['varName'], $varValue[1] ); diff --git a/libraries/classes/Controllers/Table/IndexesController.php b/libraries/classes/Controllers/Table/IndexesController.php index 263bed964d..8afb823765 100644 --- a/libraries/classes/Controllers/Table/IndexesController.php +++ b/libraries/classes/Controllers/Table/IndexesController.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Table; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Index; use PhpMyAdmin\Message; use PhpMyAdmin\Response; @@ -158,7 +159,7 @@ class IndexesController extends AbstractController $message->addParam($this->table); $this->response->addJSON( 'message', - Util::getMessage($message, $sql_query, 'success') + Generator::getMessage($message, $sql_query, 'success') ); $this->response->addJSON( 'index_table', diff --git a/libraries/classes/Controllers/Table/RelationController.php b/libraries/classes/Controllers/Table/RelationController.php index 5c384bfe57..146706f44f 100644 --- a/libraries/classes/Controllers/Table/RelationController.php +++ b/libraries/classes/Controllers/Table/RelationController.php @@ -10,6 +10,7 @@ namespace PhpMyAdmin\Controllers\Table; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Index; use PhpMyAdmin\Relation; use PhpMyAdmin\Response; @@ -230,7 +231,7 @@ class RelationController extends AbstractController ) ) { $this->response->addHTML( - Util::getMessage( + Generator::getMessage( __('Display column was successfully updated.'), '', 'success' @@ -256,7 +257,7 @@ class RelationController extends AbstractController // foreign db is not the same) if (isset($_POST['destination_foreign_db'], $_POST['destination_foreign_table']) && isset($_POST['destination_foreign_column'])) { - list($html, $preview_sql_data, $display_query, $seen_error) + [$html, $preview_sql_data, $display_query, $seen_error] = $this->upd_query->updateForeignKeys( $_POST['destination_foreign_db'], $multi_edit_columns_name, @@ -279,7 +280,7 @@ class RelationController extends AbstractController if (! empty($display_query) && ! $seen_error) { $GLOBALS['display_query'] = $display_query; $this->response->addHTML( - Util::getMessage( + Generator::getMessage( __('Your SQL query has been executed successfully.'), null, 'success' @@ -309,7 +310,7 @@ class RelationController extends AbstractController ) ) { $this->response->addHTML( - Util::getMessage( + Generator::getMessage( __('Internal relationships were successfully updated.'), '', 'success' diff --git a/libraries/classes/Controllers/Table/SearchController.php b/libraries/classes/Controllers/Table/SearchController.php index 5f571082e2..d6e9f6fda7 100644 --- a/libraries/classes/Controllers/Table/SearchController.php +++ b/libraries/classes/Controllers/Table/SearchController.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Table; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Relation; use PhpMyAdmin\Response; use PhpMyAdmin\Sql; @@ -375,7 +376,7 @@ class SearchController extends AbstractController //Displays form for point data and scatter plot $titles = [ - 'Browse' => Util::getIcon( + 'Browse' => Generator::getIcon( 'b_browse', __('Browse foreign values') ), @@ -629,7 +630,7 @@ class SearchController extends AbstractController $this->_connectionCharSet ); $this->response->addHTML( - Util::getMessage( + Generator::getMessage( __('Your SQL query has been executed successfully.'), null, 'success' @@ -933,7 +934,7 @@ class SearchController extends AbstractController $entered_value = (isset($_POST['criteriaValues']) ? $_POST['criteriaValues'] : ''); $titles = [ - 'Browse' => Util::getIcon( + 'Browse' => Generator::getIcon( 'b_browse', __('Browse foreign values') ), diff --git a/libraries/classes/Controllers/Table/StructureController.php b/libraries/classes/Controllers/Table/StructureController.php index 011a5853c4..922a3dba4b 100644 --- a/libraries/classes/Controllers/Table/StructureController.php +++ b/libraries/classes/Controllers/Table/StructureController.php @@ -16,6 +16,7 @@ use PhpMyAdmin\Core; use PhpMyAdmin\CreateAddField; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Engines\Innodb; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Index; use PhpMyAdmin\Message; use PhpMyAdmin\ParseAnalyze; @@ -252,16 +253,18 @@ class StructureController extends AbstractController // handle confirmation of deleting multiple columns $action = Url::getFromRoute('/table/structure'); $GLOBALS['selected'] = $_POST['selected_fld']; - list( - $what_ret, $query_type_ret, $is_unset_submit_mult, - $mult_btn_ret, $centralColsError - ) - = $this->getDataForSubmitMult( - $submit_mult, - $_POST['selected_fld'], - $action, - $containerBuilder - ); + [ + $what_ret, + $query_type_ret, + $is_unset_submit_mult, + $mult_btn_ret, + $centralColsError, + ] = $this->getDataForSubmitMult( + $submit_mult, + $_POST['selected_fld'], + $action, + $containerBuilder + ); //update the existing variables // todo: refactor mult_submits.inc.php such as // below globals are not needed anymore @@ -289,7 +292,7 @@ class StructureController extends AbstractController $message = Message::success(); } $this->response->addHTML( - Util::getMessage($message, $sql_query) + Generator::getMessage($message, $sql_query) ); } } else { @@ -777,7 +780,7 @@ class StructureController extends AbstractController ); $message->addParam($this->table); $this->response->addHTML( - Util::getMessage($message, $sql_query, 'success') + Generator::getMessage($message, $sql_query, 'success') ); } else { $this->response->setRequestStatus(false); @@ -852,10 +855,10 @@ class StructureController extends AbstractController // Parse and analyze the query $db = &$this->db; - list( + [ $analyzed_sql_results, $db, - ) = ParseAnalyze::sqlQuery($sql_query, $db); + ] = ParseAnalyze::sqlQuery($sql_query, $db); // @todo: possibly refactor extract($analyzed_sql_results); @@ -965,7 +968,7 @@ class StructureController extends AbstractController // To allow replication, we first select the db to use // and then run queries on this db. if (! $this->dbi->selectDb($this->db)) { - Util::mysqlDie( + Generator::mysqlDie( $this->dbi->getError(), 'USE ' . Util::backquote($this->db) . ';', false, @@ -1044,7 +1047,7 @@ class StructureController extends AbstractController $message->addParam($this->table); $this->response->addHTML( - Util::getMessage($message, $sql_query, 'success') + Generator::getMessage($message, $sql_query, 'success') ); } else { // An error happened while inserting/updating a table definition @@ -1265,20 +1268,20 @@ class StructureController extends AbstractController $columns_list = []; $titles = [ - 'Change' => Util::getIcon('b_edit', __('Change')), - 'Drop' => Util::getIcon('b_drop', __('Drop')), - 'NoDrop' => Util::getIcon('b_drop', __('Drop')), - 'Primary' => Util::getIcon('b_primary', __('Primary')), - 'Index' => Util::getIcon('b_index', __('Index')), - 'Unique' => Util::getIcon('b_unique', __('Unique')), - 'Spatial' => Util::getIcon('b_spatial', __('Spatial')), - 'IdxFulltext' => Util::getIcon('b_ftext', __('Fulltext')), - 'NoPrimary' => Util::getIcon('bd_primary', __('Primary')), - 'NoIndex' => Util::getIcon('bd_index', __('Index')), - 'NoUnique' => Util::getIcon('bd_unique', __('Unique')), - 'NoSpatial' => Util::getIcon('bd_spatial', __('Spatial')), - 'NoIdxFulltext' => Util::getIcon('bd_ftext', __('Fulltext')), - 'DistinctValues' => Util::getIcon('b_browse', __('Distinct values')), + 'Change' => Generator::getIcon('b_edit', __('Change')), + 'Drop' => Generator::getIcon('b_drop', __('Drop')), + 'NoDrop' => Generator::getIcon('b_drop', __('Drop')), + 'Primary' => Generator::getIcon('b_primary', __('Primary')), + 'Index' => Generator::getIcon('b_index', __('Index')), + 'Unique' => Generator::getIcon('b_unique', __('Unique')), + 'Spatial' => Generator::getIcon('b_spatial', __('Spatial')), + 'IdxFulltext' => Generator::getIcon('b_ftext', __('Fulltext')), + 'NoPrimary' => Generator::getIcon('bd_primary', __('Primary')), + 'NoIndex' => Generator::getIcon('bd_index', __('Index')), + 'NoUnique' => Generator::getIcon('bd_unique', __('Unique')), + 'NoSpatial' => Generator::getIcon('bd_spatial', __('Spatial')), + 'NoIdxFulltext' => Generator::getIcon('bd_ftext', __('Fulltext')), + 'DistinctValues' => Generator::getIcon('b_browse', __('Distinct values')), ]; /** @@ -1329,12 +1332,12 @@ class StructureController extends AbstractController if ($primary_index && $primary_index->hasColumn($field['Field'])) { $displayed_fields[$rownum]->icon .= - Util::getImage('b_primary', __('Primary')); + Generator::getImage('b_primary', __('Primary')); } if (in_array($field['Field'], $columns_with_index)) { $displayed_fields[$rownum]->icon .= - Util::getImage('bd_primary', __('Index')); + Generator::getImage('bd_primary', __('Index')); } $collation = Charsets::findCollationByName( @@ -1426,25 +1429,25 @@ class StructureController extends AbstractController // this is to display for example 261.2 MiB instead of 268k KiB $max_digits = 3; $decimals = 1; - list($data_size, $data_unit) = Util::formatByteDown( + [$data_size, $data_unit] = Util::formatByteDown( $this->_showtable['Data_length'], $max_digits, $decimals ); if ($mergetable === false) { - list($index_size, $index_unit) = Util::formatByteDown( + [$index_size, $index_unit] = Util::formatByteDown( $this->_showtable['Index_length'], $max_digits, $decimals ); } if (isset($this->_showtable['Data_free'])) { - list($free_size, $free_unit) = Util::formatByteDown( + [$free_size, $free_unit] = Util::formatByteDown( $this->_showtable['Data_free'], $max_digits, $decimals ); - list($effect_size, $effect_unit) = Util::formatByteDown( + [$effect_size, $effect_unit] = Util::formatByteDown( $this->_showtable['Data_length'] + $this->_showtable['Index_length'] - $this->_showtable['Data_free'], @@ -1452,20 +1455,20 @@ class StructureController extends AbstractController $decimals ); } else { - list($effect_size, $effect_unit) = Util::formatByteDown( + [$effect_size, $effect_unit] = Util::formatByteDown( $this->_showtable['Data_length'] + $this->_showtable['Index_length'], $max_digits, $decimals ); } - list($tot_size, $tot_unit) = Util::formatByteDown( + [$tot_size, $tot_unit] = Util::formatByteDown( $this->_showtable['Data_length'] + $this->_showtable['Index_length'], $max_digits, $decimals ); if ($this->_table_info_num_rows > 0) { - list($avg_size, $avg_unit) = Util::formatByteDown( + [$avg_size, $avg_unit] = Util::formatByteDown( ($this->_showtable['Data_length'] + $this->_showtable['Index_length']) / $this->_showtable['Rows'], diff --git a/libraries/classes/Core.php b/libraries/classes/Core.php index a0f3fe4d68..e169345bb1 100644 --- a/libraries/classes/Core.php +++ b/libraries/classes/Core.php @@ -813,10 +813,10 @@ class Core $retval .= __('No change'); } elseif (is_array($query_data)) { foreach ($query_data as $query) { - $retval .= Util::formatSql($query); + $retval .= Html\Generator::formatSql($query); } } else { - $retval .= Util::formatSql($query_data); + $retval .= Html\Generator::formatSql($query_data); } $retval .= ''; $response = Response::getInstance(); diff --git a/libraries/classes/CreateAddField.php b/libraries/classes/CreateAddField.php index 02b0d5fe70..0396c787ff 100644 --- a/libraries/classes/CreateAddField.php +++ b/libraries/classes/CreateAddField.php @@ -8,6 +8,8 @@ declare(strict_types=1); namespace PhpMyAdmin; +use PhpMyAdmin\Html\Generator; + /** * Set of functions for /table/create and /table/addfield * @@ -285,14 +287,14 @@ class CreateAddField private function getColumnCreationStatements(bool $isCreateTable = true): string { $sqlStatement = ""; - list( + [ $fieldCount, $fieldPrimary, $fieldIndex, $fieldUnique, $fieldFullText, - $fieldSpatial - ) = $this->getIndexedColumns(); + $fieldSpatial, + ] = $this->getIndexedColumns(); $definitions = $this->buildColumnCreationStatement( $fieldCount, $isCreateTable @@ -533,7 +535,7 @@ class CreateAddField // To allow replication, we first select the db to use and then run queries // on this db. if (! $this->dbi->selectDb($db)) { - Util::mysqlDie( + Generator::mysqlDie( $this->dbi->getError(), 'USE ' . Util::backquote($db), false, diff --git a/libraries/classes/Database/Qbe.php b/libraries/classes/Database/Qbe.php index baa2c36abd..5bcc305d91 100644 --- a/libraries/classes/Database/Qbe.php +++ b/libraries/classes/Database/Qbe.php @@ -10,6 +10,7 @@ namespace PhpMyAdmin\Database; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; use PhpMyAdmin\Relation; use PhpMyAdmin\SavedSearches; @@ -363,7 +364,7 @@ class Qbe exit; } // The tables list gets from MySQL - while (list($table) = $this->dbi->fetchRow($all_tables)) { + while ([$table] = $this->dbi->fetchRow($all_tables)) { $columns = $this->dbi->getColumns($this->_db, $table); if (empty($this->_criteriaTables[$table]) @@ -1310,7 +1311,7 @@ class Qbe $unique_columns = $indexes['unique']; $index_columns = $indexes['index']; - list($candidate_columns, $needsort) + [$candidate_columns, $needsort] = $this->_getLeftJoinColumnCandidatesBest( $search_tables, $where_clause_columns, @@ -1805,7 +1806,7 @@ class Qbe $html_output .= '' . sprintf( __('SQL query on database %s:'), - Util::getDbLink($this->_db) + Generator::getDbLink($this->_db) ); $html_output .= ''; $text_dir = 'ltr'; diff --git a/libraries/classes/Database/Search.php b/libraries/classes/Database/Search.php index 68488043fe..09ed4c7c3d 100644 --- a/libraries/classes/Database/Search.php +++ b/libraries/classes/Database/Search.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Database; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\MySQLDocumentation; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Template; use PhpMyAdmin\Util; @@ -321,16 +323,16 @@ class Search { $choices = [ '1' => $this->searchTypes[1] . ' ' - . Util::showHint( + . Generator::showHint( __('Words are separated by a space character (" ").') ), '2' => $this->searchTypes[2] . ' ' - . Util::showHint( + . Generator::showHint( __('Words are separated by a space character (" ").') ), '3' => $this->searchTypes[3], '4' => $this->searchTypes[4], - '5' => $this->searchTypes[5] . ' ' . Util::showMySQLDocu('Regexp'), + '5' => $this->searchTypes[5] . ' ' . MySQLDocumentation::show('Regexp'), ]; return $this->template->render('database/search/main', [ 'db' => $this->db, diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index ad56948083..4ffca7a6f2 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -12,6 +12,8 @@ use mysqli_result; use PhpMyAdmin\Database\DatabaseList; use PhpMyAdmin\Dbi\DbiExtension; use PhpMyAdmin\Dbi\DbiMysqli; +use PhpMyAdmin\Html\Generator; +use PhpMyAdmin\Html\MySQLDocumentation; use PhpMyAdmin\SqlParser\Context; /** @@ -159,7 +161,7 @@ class DatabaseInterface bool $cache_affected_rows = true ) { $res = $this->tryQuery($query, $link, $options, $cache_affected_rows) - or Util::mysqlDie($this->getError($link), $query); + or Generator::mysqlDie($this->getError($link), $query); return $res; } @@ -950,7 +952,7 @@ class DatabaseInterface $mysql_error = $this->getError($link); if (! count($databases) && $GLOBALS['errno']) { - Util::mysqlDie($mysql_error, $sql); + Generator::mysqlDie($mysql_error, $sql); } // display only databases also in official database list @@ -2337,12 +2339,12 @@ class DatabaseInterface if ($type === 'super') { $query = 'SELECT 1 FROM mysql.user LIMIT 1'; } elseif ($type === 'create') { - list($user, $host) = $this->getCurrentUserAndHost(); + [$user, $host] = $this->getCurrentUserAndHost(); $query = "SELECT 1 FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` " . "WHERE `PRIVILEGE_TYPE` = 'CREATE USER' AND " . "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1"; } elseif ($type === 'grant') { - list($user, $host) = $this->getCurrentUserAndHost(); + [$user, $host] = $this->getCurrentUserAndHost(); $query = "SELECT 1 FROM (" . "SELECT `GRANTEE`, `IS_GRANTABLE` FROM " . "`INFORMATION_SCHEMA`.`COLUMN_PRIVILEGES` UNION " @@ -2591,7 +2593,7 @@ class DatabaseInterface */ public function connect(int $mode, ?array $server = null, ?int $target = null) { - list($user, $password, $server) = $this->getConnectionParams($mode, $server); + [$user, $password, $server] = $this->getConnectionParams($mode, $server); if ($target === null) { $target = $mode; @@ -3144,7 +3146,7 @@ class DatabaseInterface } if (! self::checkDbExtension('mysqli')) { - $docUrl = Util::getDocuLink('faq', 'faqmysql'); + $docUrl = MySQLDocumentation::getDocumentationLink('faq', 'faqmysql'); $docLink = sprintf( __('See %sour documentation%s for more information.'), '[a@' . $docUrl . '@documentation]', diff --git a/libraries/classes/Display/ChangePassword.php b/libraries/classes/Display/ChangePassword.php index 24cbe6c11b..88a6949c10 100644 --- a/libraries/classes/Display/ChangePassword.php +++ b/libraries/classes/Display/ChangePassword.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Display; +use PhpMyAdmin\Html\MySQLDocumentation; use PhpMyAdmin\Message; use PhpMyAdmin\Relation; use PhpMyAdmin\RelationCleanup; @@ -151,7 +152,7 @@ class ChangePassword . 'or an \'unencrypted connection that encrypts the ' . 'password using RSA\'; while connecting to the server.' ) - . Util::showMySQLDocu( + . MySQLDocumentation::show( 'sha256-authentication-plugin' ) ) diff --git a/libraries/classes/Display/Export.php b/libraries/classes/Display/Export.php index e84b01b322..3b601ee281 100644 --- a/libraries/classes/Display/Export.php +++ b/libraries/classes/Display/Export.php @@ -11,6 +11,7 @@ namespace PhpMyAdmin\Display; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Encoding; +use PhpMyAdmin\Html\MySQLDocumentation; use PhpMyAdmin\Message; use PhpMyAdmin\Plugins; use PhpMyAdmin\Plugins\ExportPlugin; @@ -362,7 +363,7 @@ class Export ); $msg->addParamHtml(''); $msg->addParam($trans); - $docUrl = Util::getDocuLink('faq', 'faq6-27'); + $docUrl = MySQLDocumentation::getDocumentationLink('faq', 'faq6-27'); $msg->addParamHtml( '' ); diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php index 5e5ba2dc5a..d5ce1a28ed 100644 --- a/libraries/classes/Display/Results.php +++ b/libraries/classes/Display/Results.php @@ -11,6 +11,7 @@ namespace PhpMyAdmin\Display; use PhpMyAdmin\Config\SpecialSchemaLinks; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Index; use PhpMyAdmin\Message; use PhpMyAdmin\Plugins\Transformations\Output\Text_Octetstream_Sql; @@ -878,10 +879,10 @@ class Results $pageSelector = ''; $numberTotalPage = 1; if (! $isShowingAll) { - list( + [ $pageSelector, - $numberTotalPage - ) = $this->_getHtmlPageSelector(); + $numberTotalPage, + ] = $this->_getHtmlPageSelector(); } // Move to the next page or to the last one @@ -1068,7 +1069,7 @@ class Results // ($GLOBALS['cfg']['ShowBrowseComments']). $comments_map = $this->_getTableCommentsArray($analyzed_sql_results); - list($col_order, $col_visib) = $this->_getColumnParams( + [$col_order, $col_visib] = $this->_getColumnParams( $analyzed_sql_results ); @@ -1095,7 +1096,7 @@ class Results $display_params = $this->__get('display_params'); if (($displayParts['sort_lnk'] == '1') && ! $is_limited_display) { - list($order_link, $sorted_header_html) + [$order_link, $sorted_header_html] = $this->_getOrderLinkAndSortedHeaderHtml( $fields_meta[$i], $sort_expression, @@ -1193,7 +1194,7 @@ class Results // 1. Set $colspan and generate html with full/partial // text button or link - list($colspan, $buttonHtml) = $this->_getFieldVisibilityParams( + [$colspan, $buttonHtml] = $this->_getFieldVisibilityParams( $displayParts, $fullOrPartialTextLink ); @@ -1518,7 +1519,7 @@ class Results return ''; } - list($columnOrder, $columnVisibility) = $this->_getColumnParams( + [$columnOrder, $columnVisibility] = $this->_getColumnParams( $analyzedSqlResults ); @@ -1609,7 +1610,7 @@ class Results . $tmp_txt . '" title="' . $tmp_txt . '">'; $tmp_url = Url::getFromRoute('/sql', $url_params_full_text); - return Util::linkOrButton($tmp_url, $tmp_image); + return Generator::linkOrButton($tmp_url, $tmp_image); } /** @@ -1687,7 +1688,7 @@ class Results // Generates the orderby clause part of the query which is part // of URL - list($single_sort_order, $multi_sort_order, $order_img) + [$single_sort_order, $multi_sort_order, $order_img] = $this->_getSingleAndMultiSortUrls( $sort_expression, $sort_expression_nodirection, @@ -1854,7 +1855,7 @@ class Results ) . ' '; } if ($is_in_sort) { - list($single_sort_order, $order_img) + [$single_sort_order, $order_img] = $this->_getSortingUrlParams( $sort_direction, $single_sort_order, @@ -1866,7 +1867,7 @@ class Results } if ($current_name == $name_to_use_in_sort && $is_in_sort) { // We need to generate the arrow button and related html - list($sort_order, $order_img) = $this->_getSortingUrlParams( + [$sort_order, $order_img] = $this->_getSortingUrlParams( $sort_direction, $sort_order, $index @@ -1992,40 +1993,40 @@ class Results { if (strtoupper(trim($sort_direction[$index])) == self::DESCENDING_SORT_DIR) { $sort_order .= ' ASC'; - $order_img = ' ' . Util::getImage( - 's_desc', - __('Descending'), - [ - 'class' => "soimg", - 'title' => '', - ] - ); - $order_img .= ' ' . Util::getImage( - 's_asc', - __('Ascending'), - [ - 'class' => "soimg hide", - 'title' => '', - ] - ); + $order_img = ' ' . Generator::getImage( + 's_desc', + __('Descending'), + [ + 'class' => "soimg", + 'title' => '', + ] + ); + $order_img .= ' ' . Generator::getImage( + 's_asc', + __('Ascending'), + [ + 'class' => "soimg hide", + 'title' => '', + ] + ); } else { $sort_order .= ' DESC'; - $order_img = ' ' . Util::getImage( - 's_asc', - __('Ascending'), - [ - 'class' => "soimg", - 'title' => '', - ] - ); - $order_img .= ' ' . Util::getImage( - 's_desc', - __('Descending'), - [ - 'class' => "soimg hide", - 'title' => '', - ] - ); + $order_img = ' ' . Generator::getImage( + 's_asc', + __('Ascending'), + [ + 'class' => "soimg", + 'title' => '', + ] + ); + $order_img .= ' ' . Generator::getImage( + 's_desc', + __('Descending'), + [ + 'class' => "soimg hide", + 'title' => '', + ] + ); } return [ $sort_order, @@ -2061,7 +2062,7 @@ class Results $inner_link_content = $order_link_content . $order_img . ''; - return Util::linkOrButton( + return Generator::linkOrButton( $order_url, $inner_link_content, $order_link_params @@ -2468,7 +2469,7 @@ class Results } // prepare to get the column order, if available - list($col_order, $col_visib) = $this->_getColumnParams( + [$col_order, $col_visib] = $this->_getColumnParams( $analyzed_sql_results ); @@ -2525,7 +2526,7 @@ class Results * with only one field and it's a BLOB; in this case, * avoid to display the delete and edit links */ - list($where_clause, $clause_is_unique, $condition_array) + [$where_clause, $clause_is_unique, $condition_array] = Util::getUniqueCondition( $dt_result, // handle $this->__get('fields_cnt'), // fields_cnt @@ -2542,8 +2543,13 @@ class Results // 1.2.1 Modify link(s) - update row case if ($displayParts['edit_lnk'] == self::UPDATE_ROW) { - list($edit_url, $copy_url, $edit_str, $copy_str, - $edit_anchor_class) + [ + $edit_url, + $copy_url, + $edit_str, + $copy_str, + $edit_anchor_class, + ] = $this->_getModifiedLinks( $where_clause, $clause_is_unique, @@ -2552,7 +2558,7 @@ class Results } // end if (1.2.1) // 1.2.2 Delete/Kill link(s) - list($del_url, $del_str, $js_conf) + [$del_url, $del_str, $js_conf] = $this->_getDeleteAndKillLinks( $where_clause, $clause_is_unique, @@ -3349,7 +3355,7 @@ class Results $del_url = Url::getFromRoute('/sql', $_url_params); $js_conf = $kill; - $del_str = Util::getIcon( + $del_str = Generator::getIcon( 'b_drop', __('Kill') ); @@ -3385,7 +3391,7 @@ class Results && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_ICONS ) { $linkContent .= '' - . Util::getImage( + . Generator::getImage( $icon, $display_text ) @@ -3395,7 +3401,7 @@ class Results ) { $linkContent .= '' . $display_text . ''; } else { - $linkContent .= Util::getIcon( + $linkContent .= Generator::getIcon( $icon, $display_text ); @@ -3670,11 +3676,11 @@ class Results // Convert to WKT format $wktval = Util::asWKT($column); - list( + [ $is_field_truncated, $displayedColumn, // skip 3rd param - ) = $this->_getPartialText($wktval); + ] = $this->_getPartialText($wktval); $cell = $this->_getRowData( $class, @@ -3701,11 +3707,11 @@ class Results $where_comparison = ' = ' . $column; $wkbval = substr(bin2hex($column), 8); - list( + [ $is_field_truncated, $displayedColumn, // skip 3rd param - ) = $this->_getPartialText($wkbval); + ] = $this->_getPartialText($wkbval); $cell = $this->_getRowData( $class, @@ -3832,11 +3838,11 @@ class Results && strpos($transformation_plugin->getName(), 'Link') !== false) && false === stripos($field_flags, self::BINARY_FIELD) ) { - list( + [ $is_field_truncated, $column, - $original_length - ) = $this->_getPartialText($column); + $original_length, + ] = $this->_getPartialText($column); } $formatted = false; @@ -4134,7 +4140,7 @@ class Results $sql = new Sql(); if ($is_innodb && $sql->isJustBrowsing($analyzed_sql_results, true)) { $pre_count = '~'; - $after_count = Util::showHint( + $after_count = Generator::showHint( Sanitize::sanitizeMessage( __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc].') ) @@ -4149,16 +4155,16 @@ class Results // 1.1 Gets the information about which functionalities should be // displayed - list( + [ $displayParts, - $total - ) = $this->_setDisplayPartsAndTotal($displayParts); + $total, + ] = $this->_setDisplayPartsAndTotal($displayParts); // 1.2 Defines offsets for the next and previous pages $pos_next = null; $pos_prev = null; if ($displayParts['nav_bar'] == '1') { - list($pos_next, $pos_prev) = $this->_getOffsets(); + [$pos_next, $pos_prev] = $this->_getOffsets(); } // end if // 1.3 Extract sorting expressions. @@ -4205,13 +4211,13 @@ class Results $after_count ); - $sqlQueryMessage = Util::getMessage( + $sqlQueryMessage = Generator::getMessage( $message, $this->__get('sql_query'), 'success' ); } elseif ((! isset($printview) || ($printview != '1')) && ! $is_limited_display) { - $sqlQueryMessage = Util::getMessage( + $sqlQueryMessage = Generator::getMessage( __('Your SQL query has been executed successfully.'), $this->__get('sql_query'), 'success' @@ -4233,7 +4239,7 @@ class Results // can the result be sorted? if ($displayParts['sort_lnk'] == '1' && $analyzed_sql_results['statement'] !== null) { // At this point, $sort_expression is an array - list($unsorted_sql_query, $sort_by_key_html) + [$unsorted_sql_query, $sort_by_key_html] = $this->_getUnsortedSqlAndSortByKeyDropDown( $analyzed_sql_results, $sort_expression @@ -4388,7 +4394,7 @@ class Results $sort_table = $this->__get('table'); $sort_column = $sort_expression_nodirection; } else { - list($sort_table, $sort_column) + [$sort_table, $sort_column] = explode('.', $sort_expression_nodirection); } @@ -4546,7 +4552,7 @@ class Results $message->addParam('[doc@cfg_MaxExactCount]'); $message->addParam('[/doc]'); - $message_view_warning = Util::showHint($message); + $message_view_warning = Generator::showHint($message); } else { $message_view_warning = false; } @@ -4686,7 +4692,7 @@ class Results 'form_name' => 'resultsForm_' . $this->__get('unique_id'), ]); - $links_html .= Util::getButtonOrImage( + $links_html .= Generator::getButtonOrImage( 'submit_mult', 'mult_submit', __('Edit'), @@ -4694,7 +4700,7 @@ class Results 'edit' ); - $links_html .= Util::getButtonOrImage( + $links_html .= Generator::getButtonOrImage( 'submit_mult', 'mult_submit', __('Copy'), @@ -4702,7 +4708,7 @@ class Results 'copy' ); - $links_html .= Util::getButtonOrImage( + $links_html .= Generator::getButtonOrImage( 'submit_mult', 'mult_submit', $delete_text, @@ -4711,7 +4717,7 @@ class Results ); if ($analyzed_sql_results['querytype'] == 'SELECT') { - $links_html .= Util::getButtonOrImage( + $links_html .= Generator::getButtonOrImage( 'submit_mult', 'mult_submit', __('Export'), @@ -4742,7 +4748,7 @@ class Results // $clause_is_unique is needed by getTable() to generate the proper param // in the multi-edit and multi-delete form - list($where_clause, $clause_is_unique, $condition_array) + [$where_clause, $clause_is_unique, $condition_array] = Util::getUniqueCondition( $dt_result, // handle $this->__get('fields_cnt'), // fields_cnt @@ -4782,9 +4788,9 @@ class Results $results_operations_html = ''; if (empty($analyzed_sql_results['procedure'])) { $results_operations_html .= '' - . Util::linkOrButton( + . Generator::linkOrButton( Url::getFromRoute('/view/create', $params), - Util::getIcon( + Generator::getIcon( 'b_view_add', __('Create view'), true @@ -4828,9 +4834,9 @@ class Results */ private function _getCopytoclipboardLinks() { - return Util::linkOrButton( + return Generator::linkOrButton( '#', - Util::getIcon( + Generator::getIcon( 'b_insrow', __('Copy to clipboard'), true @@ -4848,9 +4854,9 @@ class Results */ private function _getPrintviewLinks() { - return Util::linkOrButton( + return Generator::linkOrButton( '#', - Util::getIcon( + Generator::getIcon( 'b_print', __('Print'), true @@ -4956,25 +4962,25 @@ class Results } } - $results_operations_html .= Util::linkOrButton( - Url::getFromRoute('/table/export', $_url_params), - Util::getIcon( - 'b_tblexport', - __('Export'), - true + $results_operations_html .= Generator::linkOrButton( + Url::getFromRoute('/table/export', $_url_params), + Generator::getIcon( + 'b_tblexport', + __('Export'), + true + ) ) - ) . "\n"; // prepare chart - $results_operations_html .= Util::linkOrButton( - Url::getFromRoute('/table/chart', $_url_params), - Util::getIcon( - 'b_chart', - __('Display chart'), - true + $results_operations_html .= Generator::linkOrButton( + Url::getFromRoute('/table/chart', $_url_params), + Generator::getIcon( + 'b_chart', + __('Display chart'), + true + ) ) - ) . "\n"; // prepare GIS chart @@ -4989,9 +4995,9 @@ class Results if ($geometry_found) { $results_operations_html - .= Util::linkOrButton( + .= Generator::linkOrButton( Url::getFromRoute('/table/gis_visualization', $_url_params), - Util::getIcon( + Generator::getIcon( 'b_globe', __('Visualize GIS data'), true @@ -5119,11 +5125,11 @@ class Results } else { $result = '0x' . bin2hex($content); } - list( + [ $is_truncated, $result, // skip 3rd param - ) = $this->_getPartialText($result); + ] = $this->_getPartialText($result); } /* Create link to download */ @@ -5173,7 +5179,7 @@ class Results ); if ($dispresult && $GLOBALS['dbi']->numRows($dispresult) > 0) { - list($dispval) = $GLOBALS['dbi']->fetchRow($dispresult, 0); + [$dispval] = $GLOBALS['dbi']->fetchRow($dispresult, 0); } else { $dispval = __('Link not found!'); } @@ -5340,7 +5346,7 @@ class Results if (strpos($class, 'grid_edit') !== false) { $tag_params['class'] = 'ajax'; } - $result .= Util::linkOrButton( + $result .= Generator::linkOrButton( Url::getFromRoute('/sql', $_url_params), $displayedData, $tag_params @@ -5437,7 +5443,7 @@ class Results if (! empty($edit_url)) { $ret .= '' . '' - . Util::linkOrButton($edit_url, $edit_str); + . Generator::linkOrButton($edit_url, $edit_str); /* * Where clause for selecting this row uniquely is provided as * a hidden input. Used by jQuery scripts for handling grid editing @@ -5482,7 +5488,7 @@ class Results } $ret .= 'text-center print_ignore">' - . Util::linkOrButton($copy_url, $copy_str); + . Generator::linkOrButton($copy_url, $copy_str); /* * Where clause for selecting this row uniquely is provided as @@ -5526,7 +5532,7 @@ class Results } $ajax = Response::getInstance()->isAjax() ? ' ajax' : ''; $ret .= 'text-center print_ignore">' - . Util::linkOrButton( + . Generator::linkOrButton( $del_url, $del_str, ['class' => 'delete_row requireConfirm' . $ajax] diff --git a/libraries/classes/Footer.php b/libraries/classes/Footer.php index 7b53a07191..c490322d0b 100644 --- a/libraries/classes/Footer.php +++ b/libraries/classes/Footer.php @@ -214,7 +214,7 @@ class Footer $retval .= ''; if (Util::showIcons('TabsMode')) { - $retval .= Util::getImage( + $retval .= Html\Generator::getImage( 'window-new', __('Open new phpMyAdmin window') ); diff --git a/libraries/classes/Header.php b/libraries/classes/Header.php index 6376ea74d3..acfc35e654 100644 --- a/libraries/classes/Header.php +++ b/libraries/classes/Header.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Navigation\Navigation; /** @@ -506,7 +507,7 @@ class Header if (isset($GLOBALS['buffer_message'])) { $buffer_message = $GLOBALS['buffer_message']; } - $retval .= Util::getMessage($message); + $retval .= Generator::getMessage($message); if (isset($buffer_message)) { $GLOBALS['buffer_message'] = $buffer_message; } diff --git a/libraries/classes/Html/ActionLinksModes/ActionLinksModesInterface.php b/libraries/classes/Html/ActionLinksModes/ActionLinksModesInterface.php new file mode 100644 index 0000000000..58549e483f --- /dev/null +++ b/libraries/classes/Html/ActionLinksModes/ActionLinksModesInterface.php @@ -0,0 +1,36 @@ +' . "\n" + . Generator::getIcon($image, $text) + . '' . "\n"; + } +} diff --git a/libraries/classes/Html/ActionLinksModes/Text.php b/libraries/classes/Html/ActionLinksModes/Text.php new file mode 100644 index 0000000000..7b5ac23cdb --- /dev/null +++ b/libraries/classes/Html/ActionLinksModes/Text.php @@ -0,0 +1,43 @@ +' . "\n"; + } +} diff --git a/libraries/classes/Html/Forms/Fields/BrowseUploadFileBlock.php b/libraries/classes/Html/Forms/Fields/BrowseUploadFileBlock.php new file mode 100644 index 0000000000..676dd0a731 --- /dev/null +++ b/libraries/classes/Html/Forms/Fields/BrowseUploadFileBlock.php @@ -0,0 +1,50 @@ +'; + } else { + $block_html .= '' + . '
' + . '
' + . '' + . Util::getFormattedMaximumUploadSize($max_upload_size) . "\n" + // some browsers should respect this :) + . Forms\Fields\MaxFileSize::generate($max_upload_size) . "\n"; + + return $block_html; + } +} diff --git a/libraries/classes/Html/Forms/Fields/DropDown.php b/libraries/classes/Html/Forms/Fields/DropDown.php new file mode 100644 index 0000000000..ae70b8f760 --- /dev/null +++ b/libraries/classes/Html/Forms/Fields/DropDown.php @@ -0,0 +1,80 @@ + $one_choice_label) { + $resultOptions[$one_choice_value]['value'] = $one_choice_value; + $resultOptions[$one_choice_value]['selected'] = false; + + if ($one_choice_value == $active_choice) { + $resultOptions[$one_choice_value]['selected'] = true; + $selected = true; + } + $resultOptions[$one_choice_value]['label'] = $one_choice_label; + } + return $template->render( + 'dropdown', + [ + 'select_name' => $select_name, + 'id' => $id, + 'class' => $class, + 'placeholder' => $placeholder, + 'selected' => $selected, + 'result_options' => $resultOptions, + ] + ); + } +} diff --git a/libraries/classes/Html/Forms/Fields/DropDownUploadFileBlock.php b/libraries/classes/Html/Forms/Fields/DropDownUploadFileBlock.php new file mode 100644 index 0000000000..adfaa24b8e --- /dev/null +++ b/libraries/classes/Html/Forms/Fields/DropDownUploadFileBlock.php @@ -0,0 +1,86 @@ +' + . sprintf( + __('Select from the web server upload directory %s:'), + htmlspecialchars(Util::userDir($uploaddir)) + ) + . ''; + + $extensions = ''; + foreach ($import_list as $import_plugin) { + if (! empty($extensions)) { + $extensions .= '|'; + } + $extensions .= $import_plugin->getProperties() + ->getExtension(); + } + + $matcher = '@\.(' . $extensions . ')(\.(' + . $fileListing->supportedDecompressions() . '))?$@'; + + $active = (isset($GLOBALS['timeout_passed'], $GLOBALS['local_import_file']) && $GLOBALS['timeout_passed']) + ? $GLOBALS['local_import_file'] + : ''; + + $files = $fileListing->getFileSelectOptions( + Util::userDir($uploaddir), + $matcher, + $active + ); + + if ($files === false) { + Message::error( + __('The directory you set for upload work cannot be reached.') + ) + ->display(); + } elseif (! empty($files)) { + $block_html .= "\n" + . ' ' . "\n"; + } elseif (empty($files)) { + $block_html .= '' . __('There are no files to upload!') . ''; + } + + return $block_html; + } +} diff --git a/libraries/classes/Html/Forms/Fields/FKCheckbox.php b/libraries/classes/Html/Forms/Fields/FKCheckbox.php new file mode 100644 index 0000000000..c15941ada5 --- /dev/null +++ b/libraries/classes/Html/Forms/Fields/FKCheckbox.php @@ -0,0 +1,45 @@ +render( + 'fk_checkbox', + [ + 'checked' => Util::isForeignKeyCheck(), + ] + ); + } +} diff --git a/libraries/classes/Html/Forms/Fields/FieldGenerator.php b/libraries/classes/Html/Forms/Fields/FieldGenerator.php new file mode 100644 index 0000000000..bce9b67f25 --- /dev/null +++ b/libraries/classes/Html/Forms/Fields/FieldGenerator.php @@ -0,0 +1,19 @@ +'; + } +} diff --git a/libraries/classes/Html/Forms/Fields/RadioList.php b/libraries/classes/Html/Forms/Fields/RadioList.php new file mode 100644 index 0000000000..eb8c8381f2 --- /dev/null +++ b/libraries/classes/Html/Forms/Fields/RadioList.php @@ -0,0 +1,83 @@ + $choice_label) { + if (! $id_prefix) { + $id_prefix = $html_field_name; + } + $html_field_id = $id_prefix . '_' . $choice_value; + + if ($choice_value == $checked_choice) { + $checked = 1; + } else { + $checked = 0; + } + $radio_html .= $template->render( + 'radio_fields', + [ + 'class' => $class, + 'html_field_name' => $html_field_name, + 'html_field_id' => $html_field_id, + 'choice_value' => $choice_value, + 'is_line_break' => $line_break, + 'choice_label' => $choice_label, + 'escape_label' => $escape_label, + 'checked' => $checked, + ] + ); + } + + return $radio_html; + } +} diff --git a/libraries/classes/Html/Generator.php b/libraries/classes/Html/Generator.php new file mode 100644 index 0000000000..5e52e5f13d --- /dev/null +++ b/libraries/classes/Html/Generator.php @@ -0,0 +1,1624 @@ +' . __('Copy') . '
'; + } + + /** + * Get a link to variable documentation + * + * @param string $name The variable name + * @param boolean $useMariaDB Use only MariaDB documentation + * @param string $text (optional) The text for the link + * + * @return string link or empty string + */ + public static function linkToVarDocumentation( + string $name, + bool $useMariaDB = false, + string $text = null + ): string { + $html = ''; + try { + $type = KBSearch::MYSQL; + if ($useMariaDB) { + $type = KBSearch::MARIADB; + } + $docLink = KBSearch::getByName($name, $type); + $html = MySQLDocumentation::show( + $name, + false, + $docLink, + $text + ); + } catch (KBException $e) { + unset($e);// phpstan workaround + } + return $html; + } + + /** + * Returns HTML code for a tooltip + * + * @param string $message the message for the tooltip + * + * @return string + * + * @access public + */ + public static function showHint($message): string + { + if ($GLOBALS['cfg']['ShowHint']) { + $classClause = ' class="pma_hint"'; + } else { + $classClause = ''; + } + return '' + . self::getImage('b_help') + . '' . $message . '' + . ''; + } + + /** + * returns a tab for tabbed navigation. + * If the variables $link and $args ar left empty, an inactive tab is created + * + * @param array $tab array with all options + * @param array $url_params tab specific URL parameters + * + * @return string html code for one tab, a link if valid otherwise a span + * + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + * @access public + */ + public static function getHtmlTab(array $tab, array $url_params = []): string + { + $template = new Template(); + // default values + $defaults = [ + 'text' => '', + 'class' => '', + 'active' => null, + 'link' => '', + 'sep' => '?', + 'attr' => '', + 'args' => '', + 'warning' => '', + 'fragment' => '', + 'id' => '', + ]; + + $tab = array_merge($defaults, $tab); + + // determine additional style-class + if (empty($tab['class'])) { + if (! empty($tab['active']) + || Core::isValid($GLOBALS['active_page'], 'identical', $tab['link']) + ) { + $tab['class'] = 'active'; + } elseif ($tab['active'] === null && empty($GLOBALS['active_page']) + && (basename($GLOBALS['PMA_PHP_SELF']) == $tab['link']) + ) { + $tab['class'] = 'active'; + } + } + + // build the link + if (! empty($tab['link'])) { + // If there are any tab specific URL parameters, merge those with + // the general URL parameters + if (! empty($tab['args']) && is_array($tab['args'])) { + $url_params = array_merge($url_params, $tab['args']); + } + if (strpos($tab['link'], '?') === false) { + $tab['link'] = htmlentities($tab['link']) . Url::getCommon($url_params); + } else { + $tab['link'] = htmlentities($tab['link']) . Url::getCommon($url_params, '&'); + } + } + + if (! empty($tab['fragment'])) { + $tab['link'] .= $tab['fragment']; + } + + // display icon + if (isset($tab['icon'])) { + // avoid generating an alt tag, because it only illustrates + // the text that follows and if browser does not display + // images, the text is duplicated + $tab['text'] = self::getIcon( + $tab['icon'], + $tab['text'], + false, + true, + 'TabsMode' + ); + } elseif (empty($tab['text'])) { + // check to not display an empty link-text + $tab['text'] = '?'; + trigger_error( + 'empty linktext in function ' . __FUNCTION__ . '()', + E_USER_NOTICE + ); + } + + //Set the id for the tab, if set in the params + $tabId = (empty($tab['id']) ? null : $tab['id']); + + $item = []; + if (! empty($tab['link'])) { + $item = [ + 'content' => $tab['text'], + 'url' => [ + 'href' => empty($tab['link']) ? null : $tab['link'], + 'id' => $tabId, + 'class' => 'tab' . htmlentities($tab['class']), + ], + ]; + } else { + $item['content'] = '' . $tab['text'] . ''; + } + + $item['class'] = $tab['class'] === 'active' ? 'active' : ''; + + return $template->render('list/item', $item); + } + + /** + * returns html-code for a tab navigation + * + * @param array $tabs one element per tab + * @param array $url_params additional URL parameters + * @param string $menu_id HTML id attribute for the menu container + * @param bool $resizable whether to add a "resizable" class + * + * @return string html-code for tab-navigation + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + */ + public static function getHtmlTabs( + array $tabs, + array $url_params, + $menu_id, + $resizable = false + ): string { + $class = ''; + if ($resizable) { + $class = ' class="resizable-menu"'; + } + + $tab_navigation = '' . "\n"; + + return $tab_navigation; + } + + /** + * Generate a button or image tag + * + * @param string $button_name name of button element + * @param string $button_class class of button or image element + * @param string $text text to display + * @param string $image image to display + * @param string $value value + * + * @return string html content + * + * @access public + */ + public static function getButtonOrImage( + $button_name, + $button_class, + $text, + $image, + $value = '' + ): string { + return Factory::build($GLOBALS['cfg']['ActionLinksMode']) + ->generate($button_name, $button_class, $text, $image, $value); + } + + /** + * returns html code for db link to default db page + * + * @param string $database database + * + * @return string html link to default db page + */ + public static function getDbLink($database = ''): string + { + if ('' === (string) $database) { + if ('' === (string) $GLOBALS['db']) { + return ''; + } + $database = $GLOBALS['db']; + } else { + $database = Util::unescapeMysqlWildcards($database); + } + + $scriptName = Util::getScriptNameForOption( + $GLOBALS['cfg']['DefaultTabDatabase'], + 'database' + ); + return '' . htmlspecialchars($database) . ''; + } + + /** + * Prepare a lightbulb hint explaining a known external bug + * that affects a functionality + * + * @param string $functionality localized message explaining the func. + * @param string $component 'mysql' (eventually, 'php') + * @param string $minimum_version of this component + * @param string $bugref bug reference for this component + * + * @return String + */ + public static function getExternalBug( + $functionality, + $component, + $minimum_version, + $bugref + ): string { + $ext_but_html = ''; + if (($component === 'mysql') && ($GLOBALS['dbi']->getVersion() < $minimum_version)) { + $ext_but_html .= self::showHint( + sprintf( + __('The %s functionality is affected by a known bug, see %s'), + $functionality, + Core::linkURL('https://bugs.mysql.com/') . $bugref + ) + ); + } + return $ext_but_html; + } + + /** + * Generates a slider effect (jQjuery) + * Takes care of generating the initial
and the link + * controlling the slider; you have to generate the
yourself + * after the sliding section. + * + * @param string $id the id of the
on which to apply the effect + * @param string $message the message to show as a link + * @param string|null $overrideDefault override InitialSlidersState config + * + * @return string html div element + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + */ + public static function getDivForSliderEffect($id = '', $message = '', $overrideDefault = null): string + { + $template = new Template(); + return $template->render( + 'div_for_slider_effect', + [ + 'id' => $id, + 'initial_sliders_state' => ($overrideDefault != null) ? $overrideDefault + : $GLOBALS['cfg']['InitialSlidersState'], + 'message' => $message, + ] + ); + } + + /** + * Creates an AJAX sliding toggle button + * (or and equivalent form when AJAX is disabled) + * + * @param string $action The URL for the request to be executed + * @param string $select_name The name for the dropdown box + * @param array $options An array of options (see PhpMyAdmin\Rte\Footer) + * @param string $callback A JS snippet to execute when the request is + * successfully processed + * + * @return string HTML code for the toggle button + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + */ + public static function toggleButton($action, $select_name, array $options, $callback): string + { + $template = new Template(); + // Do the logic first + $link = "$action&" . urlencode($select_name) . '='; + $link_on = $link . urlencode($options[1]['value']); + $link_off = $link . urlencode($options[0]['value']); + + if ($options[1]['selected'] == true) { + $state = 'on'; + } elseif ($options[0]['selected'] == true) { + $state = 'off'; + } else { + $state = 'on'; + } + + return $template->render( + 'toggle_button', + [ + 'pma_theme_image' => $GLOBALS['pmaThemeImage'], + 'text_dir' => $GLOBALS['text_dir'], + 'link_on' => $link_on, + 'link_off' => $link_off, + 'toggle_on' => $options[1]['label'], + 'toggle_off' => $options[0]['label'], + 'callback' => $callback, + 'state' => $state, + ] + ); + } + + /** + * Returns an HTML IMG tag for a particular icon from a theme, + * which may be an actual file or an icon from a sprite. + * This function takes into account the ActionLinksMode + * configuration setting and wraps the image tag in a span tag. + * + * @param string $icon name of icon file + * @param string $alternate alternate text + * @param boolean $force_text whether to force alternate text to be displayed + * @param boolean $menu_icon whether this icon is for the menu bar or not + * @param string $control_param which directive controls the display + * + * @return string an html snippet + */ + public static function getIcon( + $icon, + $alternate = '', + $force_text = false, + $menu_icon = false, + $control_param = 'ActionLinksMode' + ): string { + $include_icon = $include_text = false; + if (Util::showIcons($control_param)) { + $include_icon = true; + } + if ($force_text + || Util::showText($control_param) + ) { + $include_text = true; + } + // Sometimes use a span (we rely on this in js/sql.js). But for menu bar + // we don't need a span + $button = $menu_icon ? '' : ''; + if ($include_icon) { + $button .= self::getImage($icon, $alternate); + } + if ($include_icon && $include_text) { + $button .= ' '; + } + if ($include_text) { + $button .= $alternate; + } + $button .= $menu_icon ? '' : ''; + + return $button; + } + + /** + * Returns information about SSL status for current connection + * + * @return string + */ + public static function getServerSSL(): string + { + $server = $GLOBALS['cfg']['Server']; + $class = 'caution'; + if (! $server['ssl']) { + $message = __('SSL is not being used'); + if (! empty($server['socket']) || $server['host'] === '127.0.0.1' || $server['host'] === 'localhost') { + $class = ''; + } + } elseif (! $server['ssl_verify']) { + $message = __('SSL is used with disabled verification'); + } elseif (empty($server['ssl_ca'])) { + $message = __('SSL is used without certification authority'); + } else { + $class = ''; + $message = __('SSL is used'); + } + return '' . $message . ' ' . MySQLDocumentation::showDocumentation( + 'setup', + 'ssl' + ); + } + + /** + * Returns default function for a particular column. + * + * @param array $field Data about the column for which + * to generate the dropdown + * @param bool $insert_mode Whether the operation is 'insert' + * + * @return string An HTML snippet of a dropdown list with function + * names appropriate for the requested column. + * @global mixed $data data of currently edited row + * (used to detect whether to choose defaults) + * + * @global array $cfg PMA configuration + */ + public static function getDefaultFunctionForField(array $field, $insert_mode): string + { + /* + * @todo Except for $cfg, no longer use globals but pass as parameters + * from higher levels + */ + global $cfg, $data; + + $default_function = ''; + + // Can we get field class based values? + $current_class = $GLOBALS['dbi']->types->getTypeClass($field['True_Type']); + if (! empty($current_class) && isset($cfg['DefaultFunctions']['FUNC_' . $current_class])) { + $default_function = $cfg['DefaultFunctions']['FUNC_' . $current_class]; + } + + // what function defined as default? + // for the first timestamp we don't set the default function + // if there is a default value for the timestamp + // (not including CURRENT_TIMESTAMP) + // and the column does not have the + // ON UPDATE DEFAULT TIMESTAMP attribute. + if (($field['True_Type'] === 'timestamp') + && $field['first_timestamp'] + && empty($field['Default']) + && empty($data) + && $field['Extra'] !== 'on update CURRENT_TIMESTAMP' + && $field['Null'] === 'NO' + ) { + $default_function = $cfg['DefaultFunctions']['first_timestamp']; + } + + // For primary keys of type char(36) or varchar(36) UUID if the default + // function + // Only applies to insert mode, as it would silently trash data on updates. + if ($insert_mode + && $field['Key'] === 'PRI' + && ($field['Type'] === 'char(36)' || $field['Type'] === 'varchar(36)') + ) { + $default_function = $cfg['DefaultFunctions']['FUNC_UUID']; + } + + return $default_function; + } + + /** + * Creates a dropdown box with MySQL functions for a particular column. + * + * @param array $field Data about the column for which + * to generate the dropdown + * @param bool $insert_mode Whether the operation is 'insert' + * @param array $foreignData Foreign data + * + * @return string An HTML snippet of a dropdown list with function + * names appropriate for the requested column. + */ + public static function getFunctionsForField(array $field, $insert_mode, array $foreignData): string + { + $default_function = self::getDefaultFunctionForField($field, $insert_mode); + $dropdown_built = []; + + // Create the output + $retval = '' . "\n"; + // loop on the dropdown array and print all available options for that + // field. + $functions = $GLOBALS['dbi']->types->getFunctions($field['True_Type']); + foreach ($functions as $function) { + $retval .= '' . "\n"; + $dropdown_built[$function] = true; + } + + // Create separator before all functions list + if (count($functions) > 0) { + $retval .= '' + . "\n"; + } + + // For compatibility's sake, do not let out all other functions. Instead + // print a separator (blank) and then show ALL functions which weren't + // shown yet. + $functions = $GLOBALS['dbi']->types->getAllFunctions(); + foreach ($functions as $function) { + // Skip already included functions + if (isset($dropdown_built[$function])) { + continue; + } + $retval .= '' . "\n"; + } // end for + + return $retval; + } + + /** + * Renders a single link for the top of the navigation panel + * + * @param string $link The url for the link + * @param bool $showText Whether to show the text or to + * only use it for title attributes + * @param string $text The text to display and use for title attributes + * @param bool $showIcon Whether to show the icon + * @param string $icon The filename of the icon to show + * @param string $linkId Value to use for the ID attribute + * @param boolean $disableAjax Whether to disable ajax page loading for this link + * @param string $linkTarget The name of the target frame for the link + * @param array $classes HTML classes to apply + * + * @return string HTML code for one link + */ + public static function getNavigationLink( + $link, + $showText, + $text, + $showIcon, + $icon, + $linkId = '', + $disableAjax = false, + $linkTarget = '', + array $classes = [] + ): string { + $retval = ''; + if ($showIcon) { + $retval .= self::getImage( + $icon, + $text + ); + } + if ($showText) { + $retval .= $text; + } + $retval .= ''; + if ($showText) { + $retval .= '
'; + } + return $retval; + } + + /** + * Function to get html for the start row and number of rows panel + * + * @param string $sql_query sql query + * + * @return string html + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + */ + public static function getStartAndNumberOfRowsPanel($sql_query): string + { + $template = new Template(); + + if (isset($_REQUEST['session_max_rows'])) { + $rows = $_REQUEST['session_max_rows']; + } elseif (isset($_SESSION['tmpval']['max_rows']) + && $_SESSION['tmpval']['max_rows'] !== 'all' + ) { + $rows = $_SESSION['tmpval']['max_rows']; + } else { + $rows = $GLOBALS['cfg']['MaxRows']; + $_SESSION['tmpval']['max_rows'] = $rows; + } + + if (isset($_REQUEST['pos'])) { + $pos = $_REQUEST['pos']; + } elseif (isset($_SESSION['tmpval']['pos'])) { + $pos = $_SESSION['tmpval']['pos']; + } else { + $number_of_line = (int) $_REQUEST['unlim_num_rows']; + $pos = ((ceil($number_of_line / $rows) - 1) * $rows); + $_SESSION['tmpval']['pos'] = $pos; + } + + return $template->render( + 'start_and_number_of_rows_panel', + [ + 'pos' => $pos, + 'unlim_num_rows' => (int) $_REQUEST['unlim_num_rows'], + 'rows' => $rows, + 'sql_query' => $sql_query, + ] + ); + } + + /** + * Execute an EXPLAIN query and formats results similar to MySQL command line + * utility. + * + * @param string $sqlQuery EXPLAIN query + * + * @return string query resuls + */ + private static function _generateRowQueryOutput($sqlQuery): string + { + $ret = ''; + $result = $GLOBALS['dbi']->query($sqlQuery); + if ($result) { + $devider = '+'; + $columnNames = '|'; + $fieldsMeta = $GLOBALS['dbi']->getFieldsMeta($result); + foreach ($fieldsMeta as $meta) { + $devider .= '---+'; + $columnNames .= ' ' . $meta->name . ' |'; + } + $devider .= "\n"; + + $ret .= $devider . $columnNames . "\n" . $devider; + while ($row = $GLOBALS['dbi']->fetchRow($result)) { + $values = '|'; + foreach ($row as $value) { + if ($value === null) { + $value = 'NULL'; + } + $values .= ' ' . $value . ' |'; + } + $ret .= $values . "\n"; + } + $ret .= $devider; + } + return $ret; + } + + /** + * Prepare the message and the query + * usually the message is the result of the query executed + * + * @param Message|string $message the message to display + * @param string $sql_query the query to display + * @param string $type the type (level) of the message + * + * @return string + * + * @throws Throwable + * @throws Twig_Error_Loader + * @throws Twig_Error_Runtime + * @throws Twig_Error_Syntax + * @access public + */ + public static function getMessage( + $message, + $sql_query = null, + $type = 'notice' + ): string { + global $cfg; + $template = new Template(); + $retval = ''; + + if (null === $sql_query) { + if (! empty($GLOBALS['display_query'])) { + $sql_query = $GLOBALS['display_query']; + } elseif (! empty($GLOBALS['unparsed_sql'])) { + $sql_query = $GLOBALS['unparsed_sql']; + } elseif (! empty($GLOBALS['sql_query'])) { + $sql_query = $GLOBALS['sql_query']; + } else { + $sql_query = ''; + } + } + + $render_sql = $cfg['ShowSQL'] == true && ! empty($sql_query) && $sql_query !== ';'; + + if (isset($GLOBALS['using_bookmark_message'])) { + $retval .= $GLOBALS['using_bookmark_message']->getDisplay(); + unset($GLOBALS['using_bookmark_message']); + } + + if ($render_sql) { + $retval .= '
' . "\n"; + } + + if ($message instanceof Message) { + if (isset($GLOBALS['special_message'])) { + $message->addText($GLOBALS['special_message']); + unset($GLOBALS['special_message']); + } + $retval .= $message->getDisplay(); + } else { + $context = 'primary'; + if ($type === 'error') { + $context = 'danger'; + } elseif ($type === 'success') { + $context = 'success'; + } + $retval .= ''; + } + + if ($render_sql) { + $query_too_big = false; + + $queryLength = mb_strlen($sql_query); + if ($queryLength > $cfg['MaxCharactersInDisplayedSQL']) { + // when the query is large (for example an INSERT of binary + // data), the parser chokes; so avoid parsing the query + $query_too_big = true; + $query_base = mb_substr( + $sql_query, + 0, + $cfg['MaxCharactersInDisplayedSQL'] + ) . '[...]'; + } else { + $query_base = $sql_query; + } + + // Html format the query to be displayed + // If we want to show some sql code it is easiest to create it here + /* SQL-Parser-Analyzer */ + + if (! empty($GLOBALS['show_as_php'])) { + $new_line = '\\n"
' . "\n" . '    . "'; + $query_base = htmlspecialchars(addslashes($query_base)); + $query_base = preg_replace( + '/((\015\012)|(\015)|(\012))/', + $new_line, + $query_base + ); + $query_base = '
' . "\n"
+                    . '$sql = "' . $query_base . '";' . "\n"
+                    . '
'; + } elseif ($query_too_big) { + $query_base = '
' . "\n" .
+                    htmlspecialchars($query_base) .
+                    '
'; + } else { + $query_base = self::formatSql($query_base); + } + + // Prepares links that may be displayed to edit/explain the query + // (don't go to default pages, we must go to the page + // where the query box is available) + + // Basic url query part + $url_params = []; + if (! isset($GLOBALS['db'])) { + $GLOBALS['db'] = ''; + } + if (strlen($GLOBALS['db']) > 0) { + $url_params['db'] = $GLOBALS['db']; + if (strlen($GLOBALS['table']) > 0) { + $url_params['table'] = $GLOBALS['table']; + $edit_link = Url::getFromRoute('/table/sql'); + } else { + $edit_link = Url::getFromRoute('/database/sql'); + } + } else { + $edit_link = Url::getFromRoute('/server/sql'); + } + + // Want to have the query explained + // but only explain a SELECT (that has not been explained) + /* SQL-Parser-Analyzer */ + $explain_link = ''; + $is_select = preg_match('@^SELECT[[:space:]]+@i', $sql_query); + if (! empty($cfg['SQLQuery']['Explain']) && ! $query_too_big) { + $explain_params = $url_params; + if ($is_select) { + $explain_params['sql_query'] = 'EXPLAIN ' . $sql_query; + $explain_link = ' [ ' + . self::linkOrButton( + Url::getFromRoute('/import', $explain_params), + __('Explain SQL') + ) . ' ]'; + } elseif (preg_match( + '@^EXPLAIN[[:space:]]+SELECT[[:space:]]+@i', + $sql_query + )) { + $explain_params['sql_query'] + = mb_substr($sql_query, 8); + $explain_link = ' [ ' + . self::linkOrButton( + Url::getFromRoute('/import', $explain_params), + __('Skip Explain SQL') + ) . ']'; + $url = 'https://mariadb.org/explain_analyzer/analyze/' + . '?client=phpMyAdmin&raw_explain=' + . urlencode(self::_generateRowQueryOutput($sql_query)); + $explain_link .= ' [' + . self::linkOrButton( + htmlspecialchars('url.php?url=' . urlencode($url)), + sprintf(__('Analyze Explain at %s'), 'mariadb.org'), + [], + '_blank' + ) . ' ]'; + } + } //show explain + + $url_params['sql_query'] = $sql_query; + $url_params['show_query'] = 1; + + // even if the query is big and was truncated, offer the chance + // to edit it (unless it's enormous, see linkOrButton() ) + if (! empty($cfg['SQLQuery']['Edit']) + && empty($GLOBALS['show_as_php']) + ) { + $edit_link .= Url::getCommon($url_params); + $edit_link = ' [ ' + . self::linkOrButton($edit_link, __('Edit')) + . ' ]'; + } else { + $edit_link = ''; + } + + // Also we would like to get the SQL formed in some nice + // php-code + if (! empty($cfg['SQLQuery']['ShowAsPHP']) && ! $query_too_big) { + if (! empty($GLOBALS['show_as_php'])) { + $php_link = ' [ ' + . self::linkOrButton( + Url::getFromRoute('/import', $url_params), + __('Without PHP code') + ) + . ' ]'; + + $php_link .= ' [ ' + . self::linkOrButton( + Url::getFromRoute('/import', $url_params), + __('Submit query') + ) + . ' ]'; + } else { + $php_params = $url_params; + $php_params['show_as_php'] = 1; + $php_link = ' [ ' + . self::linkOrButton( + Url::getFromRoute('/import', $php_params), + __('Create PHP code') + ) + . ' ]'; + } + } else { + $php_link = ''; + } //show as php + + // Refresh query + if (! empty($cfg['SQLQuery']['Refresh']) + && ! isset($GLOBALS['show_as_php']) // 'Submit query' does the same + && preg_match('@^(SELECT|SHOW)[[:space:]]+@i', $sql_query) + ) { + $refresh_link = Url::getFromRoute('/import', $url_params); + $refresh_link = ' [ ' + . self::linkOrButton($refresh_link, __('Refresh')) . ']'; + } else { + $refresh_link = ''; + } //refresh + + $retval .= '
'; + $retval .= $query_base; + $retval .= '
'; + + $retval .= ''; + + $retval .= '
'; + } + + return $retval; + } + + /** + * Displays a link to the PHP documentation + * + * @param string $target anchor in documentation + * + * @return string the html link + * + * @access public + */ + public static function showPHPDocumentation($target): string + { + return self::showDocumentationLink(Core::getPHPDocLink($target)); + } + + /** + * Displays a link to the documentation as an icon + * + * @param string $link documentation link + * @param string $target optional link target + * @param boolean $bbcode optional flag indicating whether to output bbcode + * + * @return string the html link + * + * @access public + */ + public static function showDocumentationLink($link, $target = 'documentation', $bbcode = false): string + { + if ($bbcode) { + return "[a@$link@$target][dochelpicon][/a]"; + } + + return '' + . self::getImage('b_help', __('Documentation')) + . ''; + } + + /** + * Displays a MySQL error message in the main panel when $exit is true. + * Returns the error message otherwise. + * + * @param string|bool $server_msg Server's error message. + * @param string $sql_query The SQL query that failed. + * @param bool $is_modify_link Whether to show a "modify" link or not. + * @param string $back_url URL for the "back" link (full path is + * not required). + * @param bool $exit Whether execution should be stopped or + * the error message should be returned. + * + * @return string + * + * @global string $table The current table. + * @global string $db The current database. + * + * @access public + */ + public static function mysqlDie( + $server_msg = '', + $sql_query = '', + $is_modify_link = true, + $back_url = '', + $exit = true + ): ?string { + global $table, $db; + + /** + * Error message to be built. + * @var string $error_msg + */ + $error_msg = ''; + + // Checking for any server errors. + if (empty($server_msg)) { + $server_msg = $GLOBALS['dbi']->getError(); + } + + // Finding the query that failed, if not specified. + if (empty($sql_query) && ! empty($GLOBALS['sql_query'])) { + $sql_query = $GLOBALS['sql_query']; + } + $sql_query = trim($sql_query); + + /** + * The lexer used for analysis. + * @var Lexer $lexer + */ + $lexer = new Lexer($sql_query); + + /** + * The parser used for analysis. + * @var Parser $parser + */ + $parser = new Parser($lexer->list); + + /** + * The errors found by the lexer and the parser. + * @var array $errors + */ + $errors = ParserError::get( + [ + $lexer, + $parser, + ] + ); + + if (empty($sql_query)) { + $formatted_sql = ''; + } elseif (count($errors)) { + $formatted_sql = htmlspecialchars($sql_query); + } else { + $formatted_sql = self::formatSql($sql_query, true); + } + + $error_msg .= ''; + $_SESSION['Import_message']['message'] = $error_msg; + + if (! $exit) { + return $error_msg; + } + + /** + * If this is an AJAX request, there is no "Back" link and + * `Response()` is used to send the response. + */ + $response = Response::getInstance(); + if ($response->isAjax()) { + $response->setRequestStatus(false); + $response->addJSON('message', $error_msg); + exit; + } + + if (! empty($back_url)) { + if (mb_strstr($back_url, '?')) { + $back_url .= '&no_history=true'; + } else { + $back_url .= '?no_history=true'; + } + + $_SESSION['Import_message']['go_back_url'] = $back_url; + + $error_msg .= '
' + . '[ ' . __('Back') . ' ]' + . '
' . "\n\n"; + } + + exit($error_msg); + } + + /** + * Returns an HTML IMG tag for a particular image from a theme + * + * The image name should match CSS class defined in icons.css.php + * + * @param string $image The name of the file to get + * @param string $alternate Used to set 'alt' and 'title' attributes + * of the image + * @param array $attributes An associative array of other attributes + * + * @return string an html IMG tag + */ + public static function getImage($image, $alternate = '', array $attributes = []): string + { + $alternate = htmlspecialchars($alternate); + + if (isset($attributes['class'])) { + $attributes['class'] = "icon ic_$image " . $attributes['class']; + } else { + $attributes['class'] = "icon ic_$image"; + } + + // set all other attributes + $attr_str = ''; + foreach ($attributes as $key => $value) { + if (! in_array($key, ['alt', 'title'])) { + $attr_str .= " $key=\"$value\""; + } + } + + // override the alt attribute + $alt = $attributes['alt'] ?? $alternate; + + // override the title attribute + $title = $attributes['title'] ?? $alternate; + + // generate the IMG tag + $template = '%s'; + return sprintf($template, $title, $alt, $attr_str); + } + + /** + * Displays a link, or a link with code to trigger POST request. + * + * POST is used in following cases: + * + * - URL is too long + * - URL components are over Suhosin limits + * - There is SQL query in the parameters + * + * @param string $url the URL + * @param string $message the link message + * @param mixed $tag_params string: js confirmation; array: additional tag + * params (f.e. style="") + * @param string $target target + * + * @return string the results to be echoed or saved in an array + */ + public static function linkOrButton( + $url, + $message, + $tag_params = [], + $target = '' + ): string { + $url_length = strlen($url); + + if (! is_array($tag_params)) { + $tmp = $tag_params; + $tag_params = []; + if (! empty($tmp)) { + $tag_params['onclick'] = 'return Functions.confirmLink(this, \'' + . Sanitize::escapeJsString($tmp) . '\')'; + } + unset($tmp); + } + if (! empty($target)) { + $tag_params['target'] = $target; + if ($target === '_blank' && strncmp($url, 'url.php?', 8) == 0) { + $tag_params['rel'] = 'noopener noreferrer'; + } + } + + // Suhosin: Check that each query parameter is not above maximum + $in_suhosin_limits = true; + if ($url_length <= $GLOBALS['cfg']['LinkLengthLimit']) { + $suhosin_get_MaxValueLength = ini_get('suhosin.get.max_value_length'); + if ($suhosin_get_MaxValueLength) { + $query_parts = Util::splitURLQuery($url); + foreach ($query_parts as $query_pair) { + if (strpos($query_pair, '=') === false) { + continue; + } + + [, $eachval] = explode('=', $query_pair); + if (strlen($eachval) > $suhosin_get_MaxValueLength) { + $in_suhosin_limits = false; + break; + } + } + } + } + + $tag_params_strings = []; + if (($url_length > $GLOBALS['cfg']['LinkLengthLimit']) + || ! $in_suhosin_limits + // Has as sql_query without a signature + || (strpos($url, 'sql_query=') !== false && strpos($url, 'sql_signature=') === false) + || strpos($url, 'view[as]=') !== false + ) { + $parts = explode('?', $url, 2); + /* + * The data-post indicates that client should do POST + * this is handled in js/ajax.js + */ + $tag_params_strings[] = 'data-post="' . (isset($parts[1]) ? $parts[1] : '') . '"'; + $url = $parts[0]; + if (array_key_exists('class', $tag_params) + && strpos($tag_params['class'], 'create_view') !== false + ) { + $url .= '?' . explode('&', $parts[1], 2)[0]; + } + } + + foreach ($tag_params as $par_name => $par_value) { + $tag_params_strings[] = $par_name . '="' . htmlspecialchars($par_value) . '"'; + } + + // no whitespace within an else Safari will make it part of the link + return '' + . $message . ''; + } + + /** + * Prepare navigation for a list + * + * @param int $count number of elements in the list + * @param int $pos current position in the list + * @param array $_url_params url parameters + * @param string $script script name for form target + * @param string $frame target frame + * @param int $max_count maximum number of elements to display from + * the list + * @param string $name the name for the request parameter + * @param string[] $classes additional classes for the container + * + * @return string the html content + * + * @access public + * + * @todo use $pos from $_url_params + */ + public static function getListNavigator( + $count, + $pos, + array $_url_params, + $script, + $frame, + $max_count, + $name = 'pos', + $classes = [] + ): string { + + // This is often coming from $cfg['MaxTableList'] and + // people sometimes set it to empty string + $max_count = intval($max_count); + if ($max_count <= 0) { + $max_count = 250; + } + + $class = $frame === 'frame_navigation' ? ' class="ajax"' : ''; + + $list_navigator_html = ''; + + if ($max_count < $count) { + $classes[] = 'pageselector'; + $list_navigator_html .= '
'; + + if ($frame !== 'frame_navigation') { + $list_navigator_html .= __('Page number:'); + } + + // Move to the beginning or to the previous page + if ($pos > 0) { + $caption1 = ''; + $caption2 = ''; + if (Util::showIcons('TableNavigationLinksMode')) { + $caption1 .= '<< '; + $caption2 .= '< '; + } + if (Util::showText('TableNavigationLinksMode')) { + $caption1 .= _pgettext('First page', 'Begin'); + $caption2 .= _pgettext('Previous page', 'Previous'); + } + $title1 = ' title="' . _pgettext('First page', 'Begin') . '"'; + $title2 = ' title="' . _pgettext('Previous page', 'Previous') . '"'; + + $_url_params[$name] = 0; + $list_navigator_html .= '' . $caption1 + . ''; + + $_url_params[$name] = $pos - $max_count; + $list_navigator_html .= ' ' + . $caption2 . ''; + } + + $list_navigator_html .= '
'; + + $list_navigator_html .= Url::getHiddenInputs($_url_params); + $list_navigator_html .= Util::pageselector( + $name, + $max_count, + Util::getPageFromPosition($pos, $max_count), + ceil($count / $max_count) + ); + $list_navigator_html .= '
'; + + if ($pos + $max_count < $count) { + $caption3 = ''; + $caption4 = ''; + if (Util::showText('TableNavigationLinksMode')) { + $caption3 .= _pgettext('Next page', 'Next'); + $caption4 .= _pgettext('Last page', 'End'); + } + if (Util::showIcons('TableNavigationLinksMode')) { + $caption3 .= ' >'; + $caption4 .= ' >>'; + } + $title3 = ' title="' . _pgettext('Next page', 'Next') . '"'; + $title4 = ' title="' . _pgettext('Last page', 'End') . '"'; + + $_url_params[$name] = $pos + $max_count; + $list_navigator_html .= '' . $caption3 + . ''; + + $_url_params[$name] = floor($count / $max_count) * $max_count; + if ($_url_params[$name] == $count) { + $_url_params[$name] = $count - $max_count; + } + + $list_navigator_html .= ' ' + . $caption4 . ''; + } + $list_navigator_html .= '
' . "\n"; + } + + return $list_navigator_html; + } + + /** + * format sql strings + * + * @param string $sqlQuery raw SQL string + * @param boolean $truncate truncate the query if it is too long + * + * @return string the formatted sql + * + * @global array $cfg the configuration array + * + * @access public + */ + public static function formatSql($sqlQuery, $truncate = false): string + { + global $cfg; + + if ($truncate + && mb_strlen($sqlQuery) > $cfg['MaxCharactersInDisplayedSQL'] + ) { + $sqlQuery = mb_substr( + $sqlQuery, + 0, + $cfg['MaxCharactersInDisplayedSQL'] + ) . '[...]'; + } + return '
' . "\n"
+            . htmlspecialchars($sqlQuery) . "\n"
+            . '
'; + } + + /** + * This function processes the datatypes supported by the DB, + * as specified in Types->getColumns() and returns an HTML snippet that + * creates a drop-down list. + * + * @param string $selected The value to mark as selected in HTML mode + * + * @return string + */ + public static function getSupportedDatatypes($selected): string + { + // NOTE: the SELECT tag is not included in this snippet. + $retval = ''; + + foreach ($GLOBALS['dbi']->types->getColumns() as $key => $value) { + if (is_array($value)) { + $retval .= ""; + foreach ($value as $subvalue) { + if ($subvalue == $selected) { + $retval .= sprintf( + '', + $GLOBALS['dbi']->types->getTypeDescription($subvalue), + $subvalue + ); + } elseif ($subvalue === '-') { + $retval .= ''; + } else { + $retval .= sprintf( + '', + $GLOBALS['dbi']->types->getTypeDescription($subvalue), + $subvalue + ); + } + } + $retval .= ''; + } elseif ($selected == $value) { + $retval .= sprintf( + '', + $GLOBALS['dbi']->types->getTypeDescription($value), + $value + ); + } else { + $retval .= sprintf( + '', + $GLOBALS['dbi']->types->getTypeDescription($value), + $value + ); + } + } + return $retval; + } +} diff --git a/libraries/classes/Html/MySQLDocumentation.php b/libraries/classes/Html/MySQLDocumentation.php new file mode 100644 index 0000000000..44dbb1cc14 --- /dev/null +++ b/libraries/classes/Html/MySQLDocumentation.php @@ -0,0 +1,101 @@ +'; + $closeLink = ''; + + if ($bigIcon) { + $html = $openLink . + Generator::getImage('b_sqlhelp', __('Documentation')) + . $closeLink; + } elseif ($text !== null) { + $html = $openLink . $text . $closeLink; + } else { + $html = Generator::showDocumentationLink($url, 'mysql_doc'); + } + + return $html; + } + + /** + * Displays a link to the phpMyAdmin documentation + * + * @param string $page Page in documentation + * @param string $anchor Optional anchor in page + * @param boolean $bbcode Optional flag indicating whether to output bbcode + * + * @return string the html link + * + * @access public + */ + public static function showDocumentation($page, $anchor = '', $bbcode = false): string + { + return Generator::showDocumentationLink(self::getDocumentationLink($page, $anchor), 'documentation', $bbcode); + } + + /** + * Returns link to documentation. + * + * @param string $page Page in documentation + * @param string $anchor Optional anchor in page + * + * @return string URL + */ + public static function getDocumentationLink($page, $anchor = ''): string + { + /* Construct base URL */ + $url = $page . '.html'; + if (! empty($anchor)) { + $url .= '#' . $anchor; + } + + /* Check if we have built local documentation, however + * provide consistent URL for testsuite + */ + if (! defined('TESTSUITE') && @file_exists(ROOT_PATH . 'doc/html/index.html')) { + return 'doc/html/' . $url; + } + + return Core::linkURL('https://docs.phpmyadmin.net/en/latest/' . $url); + } +} diff --git a/libraries/classes/Import.php b/libraries/classes/Import.php index f153154945..dc058fae4d 100644 --- a/libraries/classes/Import.php +++ b/libraries/classes/Import.php @@ -1451,7 +1451,7 @@ class Import $matched_rows_url = Url::getFromRoute('/sql', $_url_params); return [ - 'sql_query' => Util::formatSql($analyzed_sql_results['query']), + 'sql_query' => Html\Generator::formatSql($analyzed_sql_results['query']), 'matched_rows' => $matched_rows, 'matched_rows_url' => $matched_rows_url, ]; diff --git a/libraries/classes/Index.php b/libraries/classes/Index.php index 5c69265303..62a454f37c 100644 --- a/libraries/classes/Index.php +++ b/libraries/classes/Index.php @@ -8,6 +8,9 @@ declare(strict_types=1); namespace PhpMyAdmin; +use PhpMyAdmin\Html\Generator; +use PhpMyAdmin\Html\MySQLDocumentation; + /** * Index manipulation class * @@ -533,7 +536,7 @@ class Index $types[$type] = $type; } - return Util::getDropdown( + return Html\Forms\Fields\DropDown::generate( "index[Index_type]", $types, $this->_type, @@ -689,7 +692,7 @@ class Index if (! $print_mode) { $r = '
'; $r .= '' . __('Indexes'); - $r .= Util::showMySQLDocu('optimizing-database-structure'); + $r .= MySQLDocumentation::show('optimizing-database-structure'); $r .= ''; $r .= $no_indexes; @@ -738,7 +741,7 @@ class Index . ' ' . Util::getIcon('b_edit', __('Edit')) . '' + . '">' . Generator::getIcon('b_edit', __('Edit')) . '' . '' . "\n"; $this_params = $GLOBALS['url_params']; if ($index->getName() == 'PRIMARY') { @@ -762,9 +765,9 @@ class Index $r .= ''; $r .= ''; - $r .= Util::linkOrButton( + $r .= Generator::linkOrButton( Url::getFromRoute('/sql', $this_params), - Util::getIcon('b_drop', __('Drop')), + Generator::getIcon('b_drop', __('Drop')), ['class' => 'drop_primary_key_index_anchor ajax'] ); $r .= '' . "\n"; diff --git a/libraries/classes/InsertEdit.php b/libraries/classes/InsertEdit.php index 0153c9a4d8..f50bbf65ae 100644 --- a/libraries/classes/InsertEdit.php +++ b/libraries/classes/InsertEdit.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Plugins\TransformationsPlugin; /** @@ -188,7 +189,7 @@ class InsertEdit if (! $rows[$key_id]) { unset($rows[$key_id], $where_clause_array[$key_id]); Response::getInstance()->addHTML( - Util::getMessage( + Generator::getMessage( __('MySQL returned an empty result set (i.e. zero rows).'), $local_query ) @@ -200,7 +201,7 @@ class InsertEdit } else {// end if (no row returned) $meta = $this->dbi->getFieldsMeta($result[$key_id]); - list($unique_condition, $tmp_clause_is_unique) + [$unique_condition, $tmp_clause_is_unique] = Util::getUniqueCondition( $result[$key_id], // handle count($meta), // fields_cnt @@ -372,7 +373,7 @@ class InsertEdit ] ); - list($column['pma_type'], $column['wrap'], $column['first_timestamp']) + [$column['pma_type'], $column['wrap'], $column['first_timestamp']] = $this->getEnumSetAndTimestampColumns($column, $timestamp_seen); return $column; @@ -490,7 +491,7 @@ class InsertEdit $insert_mode, $readOnly, array $foreignData - ) { + ): string { $html_output = ''; if (($GLOBALS['cfg']['ProtectBinary'] === 'blob' && $column['is_blob'] && ! $is_upload) @@ -513,7 +514,7 @@ class InsertEdit . ' ' . $onChangeClause . ' tabindex="' . ($tabindex + $tabindex_for_function) . '"' . ' id="field_' . $idindex . '_1">'; - $html_output .= Util::getFunctionsForField( + $html_output .= Generator::getFunctionsForField( $column, $insert_mode, $foreignData @@ -867,7 +868,7 @@ class InsertEdit array $titles, $readOnly ) { - list($table, $db) = $paramTableDbArray; + [$table, $db] = $paramTableDbArray; $html_output = ''; $html_output .= $backup_field . "\n"; @@ -1264,7 +1265,7 @@ class InsertEdit $data, $readOnly ) { - list($column_set_values, $select_size) = $this->getColumnSetValueAndSelectSize( + [$column_set_values, $select_size] = $this->getColumnSetValueAndSelectSize( $column, $extracted_columnspec ); @@ -1436,7 +1437,9 @@ class InsertEdit . ' name="fields_upload' . $vkey . '[' . $column['Field_md5'] . ']"' . ' class="textfield noDragDrop" id="field_' . $idindex . '_3" size="10"' . ' ' . $onChangeClause . '> '; - list($html_out,) = $this->getMaxUploadSize( + [ + $html_out, + ] = $this->getMaxUploadSize( $column, $biggest_max_file_size ); @@ -1741,9 +1744,9 @@ class InsertEdit */ private function getHtmlForGisDataTypes() { - $edit_str = Util::getIcon('b_edit', __('Edit/Insert')); + $edit_str = Generator::getIcon('b_edit', __('Edit/Insert')); return '' - . Util::linkOrButton( + . Generator::linkOrButton( '#', $edit_str, [], @@ -1921,21 +1924,21 @@ class InsertEdit private function getSubmitAndResetButtonForActionsPanel($tabindex, $tabindex_for_value) { return '' - . Util::showHint( - __( - 'Use TAB key to move from value to value,' - . ' or CTRL+arrows to move anywhere.' + . Generator::showHint( + __( + 'Use TAB key to move from value to value,' + . ' or CTRL+arrows to move anywhere.' + ) ) - ) - . '' - . '' - . '' - . '' - . '' - . ''; + . '' + . '' + . '' + . '' + . '' + . ''; } /** @@ -2217,7 +2220,7 @@ class InsertEdit $meta = $this->dbi->getFieldsMeta($res); // must find a unique condition based on unique key, // not a combination of all fields - list($unique_condition, $clause_is_unique) + [$unique_condition, $clause_is_unique] = Util::getUniqueCondition( $res, // handle count($meta), // fields_cnt @@ -2441,7 +2444,7 @@ class InsertEdit DatabaseInterface::QUERY_STORE ); if ($dispresult && $this->dbi->numRows($dispresult) > 0) { - list($dispval) = $this->dbi->fetchRow($dispresult); + [$dispval] = $this->dbi->fetchRow($dispresult); } else { $dispval = ''; } @@ -2928,7 +2931,7 @@ class InsertEdit // we are editing $insert_mode = false; $where_clause_array = $this->getWhereClauseArray($where_clause); - list($where_clauses, $result, $rows, $found_unique_key) + [$where_clauses, $result, $rows, $found_unique_key] = $this->analyzeWhereClauses( $where_clause_array, $table, @@ -2938,7 +2941,7 @@ class InsertEdit // we are inserting $insert_mode = true; $where_clause = null; - list($result, $rows) = $this->loadFirstRow($table, $db); + [$result, $rows] = $this->loadFirstRow($table, $db); $where_clauses = null; $where_clause_array = []; $found_unique_key = false; @@ -3226,10 +3229,13 @@ class InsertEdit $special_chars_encoded = ''; if (! empty($current_row)) { // (we are editing) - list( - $real_null_value, $special_chars_encoded, $special_chars, - $data, $backup_field - ) + [ + $real_null_value, + $special_chars_encoded, + $special_chars, + $data, + $backup_field, + ] = $this->getSpecialCharsAndBackupFieldForExistingRow( $current_row, $column, @@ -3246,9 +3252,13 @@ class InsertEdit if (isset($repopulate[$column['Field_md5']])) { $tmp['Default'] = $repopulate[$column['Field_md5']]; } - list($real_null_value, $data, $special_chars, $backup_field, - $special_chars_encoded - ) + [ + $real_null_value, + $data, + $special_chars, + $backup_field, + $special_chars_encoded, + ] = $this->getSpecialCharsAndBackupFieldForInsertingMode( $tmp, $real_null_value diff --git a/libraries/classes/LanguageManager.php b/libraries/classes/LanguageManager.php index 55fe739aa7..9ef41b6e04 100644 --- a/libraries/classes/LanguageManager.php +++ b/libraries/classes/LanguageManager.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace PhpMyAdmin; use PhpMyAdmin\Core; +use PhpMyAdmin\Html\MySQLDocumentation; use PhpMyAdmin\Language; use PhpMyAdmin\Template; use PhpMyAdmin\Url; @@ -952,7 +953,7 @@ class LanguageManager $language_title = __('Language') . (__('Language') != 'Language' ? ' - Language' : ''); if ($show_doc) { - $language_title .= Util::showDocu('faq', 'faq7-2'); + $language_title .= MySQLDocumentation::showDocumentation('faq', 'faq7-2'); } $available_languages = $this->sortedLanguages(); diff --git a/libraries/classes/Menu.php b/libraries/classes/Menu.php index 7dc6e8bb48..a7fea42f1d 100644 --- a/libraries/classes/Menu.php +++ b/libraries/classes/Menu.php @@ -8,6 +8,8 @@ declare(strict_types=1); namespace PhpMyAdmin; +use PhpMyAdmin\Html\Generator; + /** * Class for generating the top menu * @@ -113,7 +115,7 @@ class Menu unset($tabs[$key]); } } - return Util::getHtmlTabs($tabs, $url_params, 'topmenu', true); + return Generator::getHtmlTabs($tabs, $url_params, 'topmenu', true); } /** @@ -190,7 +192,7 @@ class Menu $retval .= "
"; $retval .= "
"; if (Util::showIcons('TabsMode')) { - $retval .= Util::getImage( + $retval .= Generator::getImage( 's_host', '', ['class' => 'item'] @@ -211,7 +213,7 @@ class Menu if (strlen($this->_db) > 0) { $retval .= $separator; if (Util::showIcons('TabsMode')) { - $retval .= Util::getImage( + $retval .= Generator::getImage( 's_db', '', ['class' => 'item'] @@ -247,7 +249,7 @@ class Menu $retval .= $separator; if (Util::showIcons('TabsMode')) { $icon = $tbl_is_view ? 'b_views' : 's_tbl'; - $retval .= Util::getImage( + $retval .= Generator::getImage( $icon, '', ['class' => 'item'] diff --git a/libraries/classes/Message.php b/libraries/classes/Message.php index b6bf75b3f2..1350675990 100644 --- a/libraries/classes/Message.php +++ b/libraries/classes/Message.php @@ -817,7 +817,7 @@ class Message } else { $image = 's_notice'; } - $message = self::notice(Util::getImage($image)) . " " . $message; + $message = self::notice(Html\Generator::getImage($image)) . " " . $message; return $message; } } diff --git a/libraries/classes/MultSubmits.php b/libraries/classes/MultSubmits.php index 47bfb6cd9d..11162d6867 100644 --- a/libraries/classes/MultSubmits.php +++ b/libraries/classes/MultSubmits.php @@ -10,6 +10,8 @@ declare(strict_types=1); namespace PhpMyAdmin; +use PhpMyAdmin\Html\Forms\Fields\FKCheckbox; + /** * Functions for multi submit forms * @@ -523,7 +525,7 @@ class MultSubmits // Display option to disable foreign key checks while dropping tables if ($what === 'drop_tbl' || $what === 'empty_tbl' || $what === 'row_delete') { $html .= '
'; - $html .= Util::getFKCheckbox(); + $html .= FKCheckbox::generate(); $html .= '
'; } $html .= ''; $retval .= '', + "
\n
" + ); + } + + /** + * Test for Util::getIcon + * + * @return void + */ + public function testGetIconWithoutActionLinksMode(): void + { + $GLOBALS['cfg']['ActionLinksMode'] = 'text'; + + $this->assertEquals( + '', + Generator::getIcon('b_comment') + ); + } + + /** + * Test for Util::getIcon + * + * @return void + */ + public function testGetIconWithActionLinksMode(): void + { + $GLOBALS['cfg']['ActionLinksMode'] = 'icons'; + + $this->assertEquals( + '', + Generator::getIcon('b_comment') + ); + } + + /** + * Test for Util::getIcon + * + * @return void + */ + public function testGetIconAlternate(): void + { + $GLOBALS['cfg']['ActionLinksMode'] = 'icons'; + $alternate_text = 'alt_str'; + + $this->assertEquals( + '' . $alternate_text
+            . '', + Generator::getIcon('b_comment', $alternate_text) + ); + } + + /** + * Test for Util::getIcon + * + * @return void + */ + public function testGetIconWithForceText(): void + { + $GLOBALS['cfg']['ActionLinksMode'] = 'icons'; + $alternate_text = 'alt_str'; + + // Here we are checking for an icon embedded inside a span (i.e not a menu + // bar icon + $this->assertEquals( + '' . $alternate_text
+            . ' ' . $alternate_text . '', + Generator::getIcon('b_comment', $alternate_text, true, false) + ); + } + + /** + * Test for showPHPDocumentation + * + * @return void + */ + public function testShowPHPDocumentation(): void + { + $GLOBALS['server'] = 99; + $GLOBALS['cfg']['ServerDefault'] = 0; + + $target = 'docu'; + $lang = _pgettext('PHP documentation language', 'en'); + $expected = '' + . ''
+            . __('Documentation') . ''; + + $this->assertEquals( + $expected, + Generator::showPHPDocumentation($target) + ); + } + + /** + * Test for Generator::linkOrButton + * + * @param array $params params + * @param int $limit limit + * @param string $match match + * + * @return void + * + * @dataProvider linksOrButtons + */ + public function testLinkOrButton(array $params, $limit, $match): void + { + $restore = $GLOBALS['cfg']['LinkLengthLimit'] ?? 1000; + $GLOBALS['cfg']['LinkLengthLimit'] = $limit; + try { + $result = call_user_func_array( + [ + Generator::class, + 'linkOrButton', + ], + $params + ); + $this->assertEquals($match, $result); + } finally { + $GLOBALS['cfg']['LinkLengthLimit'] = $restore; + } + } + + /** + * Data provider for Generator::linkOrButton test + * + * @return array + */ + public function linksOrButtons(): array + { + return [ + [ + [ + 'index.php', + 'text', + ], + 1000, + 'text', + ], + [ + [ + 'index.php?some=parameter', + 'text', + ], + 20, + 'text', + ], + [ + [ + 'index.php', + 'text', + [], + 'target', + ], + 1000, + 'text', + ], + [ + [ + 'url.php?url=http://phpmyadmin.net/', + 'text', + [], + '_blank', + ], + 1000, + 'text', + ], + ]; + } + + /** + * Test for formatSql + * + * @covers \PhpMyAdmin\Html\Generator::formatSql + * + * @return void + */ + public function testFormatSql(): void + { + $this->assertEquals( + '
' . "\n"
+            . 'SELECT 1 < 2' . "\n"
+            . '
', + Generator::formatSql('SELECT 1 < 2') + ); + + $GLOBALS['cfg']['MaxCharactersInDisplayedSQL'] = 6; + + $this->assertEquals( + '
' . "\n"
+            . 'SELECT[...]' . "\n"
+            . '
', + Generator::formatSql('SELECT 1 < 2', true) + ); + } +} diff --git a/test/classes/Html/MySQLDocumentationTest.php b/test/classes/Html/MySQLDocumentationTest.php new file mode 100644 index 0000000000..486e93d4bf --- /dev/null +++ b/test/classes/Html/MySQLDocumentationTest.php @@ -0,0 +1,39 @@ +assertEquals( + 'Documentation', + MySQLDocumentation::showDocumentation('page', 'anchor') + ); + } +} diff --git a/test/classes/ImportTest.php b/test/classes/ImportTest.php index a9518fa04c..2e0b49abaf 100644 --- a/test/classes/ImportTest.php +++ b/test/classes/ImportTest.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Import; use PhpMyAdmin\SqlParser\Parser; use PhpMyAdmin\Url; @@ -573,7 +574,7 @@ class ImportTest extends TestCase $this->assertEquals( [ - 'sql_query' => Util::formatSql( + 'sql_query' => Generator::formatSql( $analyzed_sql_results['query'] ), 'matched_rows' => 2, diff --git a/test/classes/NormalizationTest.php b/test/classes/NormalizationTest.php index 3fe67f2f53..fde4ddeb1a 100644 --- a/test/classes/NormalizationTest.php +++ b/test/classes/NormalizationTest.php @@ -9,14 +9,13 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Forms\Fields\RadioList; use PhpMyAdmin\Normalization; use PhpMyAdmin\Relation; use PhpMyAdmin\Template; -use PhpMyAdmin\Theme; use PhpMyAdmin\Transformations; use PhpMyAdmin\Types; use PhpMyAdmin\Url; -use PhpMyAdmin\Util; use PHPUnit\Framework\TestCase; use ReflectionClass; use stdClass; @@ -550,7 +549,7 @@ class NormalizationTest extends TestCase '3nf' => __('Third step of normalization (1NF+2NF+3NF)'), ]; - $htmlTmp = Util::getRadioFields( + $htmlTmp = RadioList::generate( 'normalizeTo', $choices, '1nf', diff --git a/test/classes/Rte/EventsTest.php b/test/classes/Rte/EventsTest.php index b4988263fb..5a5dcf6506 100644 --- a/test/classes/Rte/EventsTest.php +++ b/test/classes/Rte/EventsTest.php @@ -245,8 +245,8 @@ class EventsTest extends TestCase /** * Test for getEditorForm * - * @param array $data Data for routine - * @param array $matcher Matcher + * @param array $data Data for routine + * @param string $matcher Matcher * * @return void * @@ -266,7 +266,7 @@ class EventsTest extends TestCase * * @return array */ - public function providerGetEditorFormEdit() + public function providerGetEditorFormEdit(): array { $data = [ 'item_name' => 'foo', diff --git a/test/classes/Server/PrivilegesTest.php b/test/classes/Server/PrivilegesTest.php index b5cbf5df0d..d0f5481caa 100644 --- a/test/classes/Server/PrivilegesTest.php +++ b/test/classes/Server/PrivilegesTest.php @@ -10,6 +10,7 @@ namespace PhpMyAdmin\Tests\Server; use PhpMyAdmin\Config; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Relation; use PhpMyAdmin\RelationCleanup; use PhpMyAdmin\Server\Privileges; @@ -133,10 +134,15 @@ class PrivilegesTest extends TestCase $_REQUEST['hostname'] = "PMA_hostname"; $_REQUEST['tablename'] = "PMA_tablename"; $_REQUEST['dbname'] = "PMA_dbname"; - list( - $username, $hostname, $dbname, $tablename, $routinename, - $db_and_table, $dbname_is_wildcard - ) = $this->serverPrivileges->getDataForDBInfo(); + [ + $username, + $hostname, + $dbname, + $tablename, + $routinename, + $db_and_table, + $dbname_is_wildcard, + ] = $this->serverPrivileges->getDataForDBInfo(); $this->assertEquals( "PMA_username", $username @@ -166,8 +172,12 @@ class PrivilegesTest extends TestCase $_POST['pred_tablename'] = "PMA_pred__tablename"; $_POST['pred_dbname'] = ["PMA_pred_dbname"]; list( - ,, $dbname, $tablename, $routinename, - $db_and_table, $dbname_is_wildcard + ,, + $dbname, + $tablename, + $routinename, + $db_and_table, + $dbname_is_wildcard, ) = $this->serverPrivileges->getDataForDBInfo(); $this->assertEquals( "PMA_pred_dbname", @@ -507,7 +517,7 @@ class PrivilegesTest extends TestCase public function testGetDataForChangeOrCopyUser() { //$_POST['change_copy'] not set - list($queries, $password) = $this->serverPrivileges->getDataForChangeOrCopyUser(); + [$queries, $password] = $this->serverPrivileges->getDataForChangeOrCopyUser(); $this->assertEquals( null, $queries @@ -521,7 +531,7 @@ class PrivilegesTest extends TestCase $_POST['change_copy'] = true; $_POST['old_username'] = 'PMA_old_username'; $_POST['old_hostname'] = 'PMA_old_hostname'; - list($queries, $password) = $this->serverPrivileges->getDataForChangeOrCopyUser(); + [$queries, $password] = $this->serverPrivileges->getDataForChangeOrCopyUser(); $this->assertEquals( 'pma_password', $password @@ -544,7 +554,7 @@ class PrivilegesTest extends TestCase $username = "PMA_username"; $hostname = "PMA_hostname"; - list($title, $export) + [$title, $export] = $this->serverPrivileges->getListForExportUserDefinition($username, $hostname); //validate 1: $export @@ -593,10 +603,11 @@ class PrivilegesTest extends TestCase $_POST['userGroup'] = "username"; $_POST['authentication_plugin'] = 'mysql_native_password'; - list( - $ret_message,,, $sql_query, - $_add_user_error - ) = $this->serverPrivileges->addUser( + [ + $ret_message,,, + $sql_query, + $_add_user_error, + ] = $this->serverPrivileges->addUser( $dbname, $username, $hostname, @@ -641,10 +652,11 @@ class PrivilegesTest extends TestCase $_POST['userGroup'] = "username"; $_POST['authentication_plugin'] = 'mysql_native_password'; - list( - $ret_message,,, $sql_query, - $_add_user_error - ) = $this->serverPrivileges->addUser( + [ + $ret_message,,, + $sql_query, + $_add_user_error, + ] = $this->serverPrivileges->addUser( $dbname, $username, $hostname, @@ -712,7 +724,7 @@ class PrivilegesTest extends TestCase $_POST['createdb-3'] = true; $_POST['Grant_priv'] = 'Y'; $_POST['max_questions'] = 1000; - list ($message, $sql_query) + [$message, $sql_query] = $this->serverPrivileges->getMessageAndSqlQueryForPrivilegesRevoke( $dbname, $tablename, @@ -751,7 +763,7 @@ class PrivilegesTest extends TestCase $_POST['createdb-3'] = true; $_POST['Grant_priv'] = 'Y'; $_POST['max_questions'] = 1000; - list($sql_query, $message) = $this->serverPrivileges->updatePrivileges( + [$sql_query, $message] = $this->serverPrivileges->updatePrivileges( $username, $hostname, $tablename, @@ -973,20 +985,19 @@ class PrivilegesTest extends TestCase $_POST['authentication_plugin'] = 'mysql_native_password'; $dbname = "PMA_db"; - list( + [ $create_user_real, $create_user_show, $real_sql_query, - $sql_query, - , - , + $sql_query,,, $alter_real_sql_query, - $alter_sql_query - ) = $this->serverPrivileges->getSqlQueriesForDisplayAndAddUser( - $username, - $hostname, - $password - ); + $alter_sql_query, + ] + = $this->serverPrivileges->getSqlQueriesForDisplayAndAddUser( + $username, + $hostname, + $password + ); //validate 1: $create_user_real $this->assertEquals( @@ -1025,7 +1036,7 @@ class PrivilegesTest extends TestCase ); //Test for addUserAndCreateDatabase - list($sql_query, $message) = $this->serverPrivileges->addUserAndCreateDatabase( + [$sql_query, $message] = $this->serverPrivileges->addUserAndCreateDatabase( false, $real_sql_query, $sql_query, @@ -1177,7 +1188,7 @@ class PrivilegesTest extends TestCase $html ); - $output = Util::showHint( + $output = Generator::showHint( __( 'When Host table is used, this field is ignored ' . 'and values stored in Host table are used instead.' @@ -1429,7 +1440,7 @@ class PrivilegesTest extends TestCase //sql_query $this->assertEquals( - Util::getMessage(null, $sql_query), + Generator::getMessage(null, $sql_query), $extra_data['sql_query'] ); @@ -1532,7 +1543,7 @@ class PrivilegesTest extends TestCase //Util::showHint $this->assertStringContainsString( - Util::showHint( + Generator::showHint( __('Note: MySQL privilege names are expressed in English.') ), $html @@ -1640,7 +1651,7 @@ class PrivilegesTest extends TestCase $html ); $this->assertStringContainsString( - Util::getIcon('b_usradd'), + Generator::getIcon('b_usradd'), $html ); $this->assertStringContainsString( diff --git a/test/classes/SqlQueryFormTest.php b/test/classes/SqlQueryFormTest.php index dc35838885..8e5e79d094 100644 --- a/test/classes/SqlQueryFormTest.php +++ b/test/classes/SqlQueryFormTest.php @@ -10,10 +10,10 @@ namespace PhpMyAdmin\Tests; use PhpMyAdmin\Core; use PhpMyAdmin\Encoding; +use PhpMyAdmin\Html\MySQLDocumentation; use PhpMyAdmin\SqlQueryForm; use PhpMyAdmin\Template; use PhpMyAdmin\Url; -use PhpMyAdmin\Util; use PHPUnit\Framework\TestCase; /** @@ -129,9 +129,9 @@ class SqlQueryFormTest extends TestCase $html ); - //validate 3: showMySQLDocu + //validate 3: MySQLDocumentation::show $this->assertStringContainsString( - Util::showMySQLDocu('SELECT'), + MySQLDocumentation::show('SELECT'), $html ); diff --git a/test/classes/UtilTest.php b/test/classes/UtilTest.php index 2adb40caf3..badbd4903e 100644 --- a/test/classes/UtilTest.php +++ b/test/classes/UtilTest.php @@ -10,10 +10,11 @@ namespace PhpMyAdmin\Tests; use PhpMyAdmin\Config; use PhpMyAdmin\Core; +use PhpMyAdmin\Html\Forms; +use PhpMyAdmin\Html\Generator; use PhpMyAdmin\MoTranslator\Loader; use PhpMyAdmin\SqlParser\Context; use PhpMyAdmin\SqlParser\Token; -use PhpMyAdmin\Tests\PmaTestCase; use PhpMyAdmin\Util; /** @@ -172,115 +173,6 @@ class UtilTest extends PmaTestCase $this->assertEquals(16, strlen(Util::generateRandom(16))); } - /** - * Test for PhpMyAdmin\Util::getBrowseUploadFileBlock - * - * @param int $size Size - * @param string $unit Unit - * @param string $res Result - * - * @return void - * - * @covers \PhpMyAdmin\Util::getBrowseUploadFileBlock - * @dataProvider providerGetBrowseUploadFileBlock - */ - public function testGetBrowseUploadFileBlock($size, $unit, $res): void - { - $GLOBALS['is_upload'] = false; - $this->assertEquals( - Util::getBrowseUploadFileBlock($size), - '' - . '
' - . '
' - . '' - . "(" . __('Max: ') . $res . $unit . ')' . "\n" - . '' . "\n" - ); - } - - /** - * Data provider for testGetBrowseUploadFileBlock - * - * @return array - */ - public function providerGetBrowseUploadFileBlock() - { - return [ - [ - 10, - __('B'), - "10", - ], - [ - 100, - __('B'), - "100", - ], - [ - 1024, - __('B'), - "1,024", - ], - [ - 102400, - __('KiB'), - "100", - ], - [ - 10240000, - __('MiB'), - "10", - ], - [ - 2147483648, - __('MiB'), - "2,048", - ], - [ - 21474836480, - __('GiB'), - "20", - ], - ]; - } - - /** - * Test for PhpMyAdmin\Util::buildActionTitles - * - * @covers \PhpMyAdmin\Util::buildActionTitles - * - * @return void - */ - public function testBuildActionTitles() - { - $GLOBALS['cfg'] = ['ActionLinksMode' => 'both']; - - $titles = []; - $titles['Browse'] = Util::getIcon('b_browse', __('Browse')); - $titles['NoBrowse'] = Util::getIcon('bd_browse', __('Browse')); - $titles['Search'] = Util::getIcon('b_select', __('Search')); - $titles['NoSearch'] = Util::getIcon('bd_select', __('Search')); - $titles['Insert'] = Util::getIcon('b_insrow', __('Insert')); - $titles['NoInsert'] = Util::getIcon('bd_insrow', __('Insert')); - $titles['Structure'] = Util::getIcon('b_props', __('Structure')); - $titles['Drop'] = Util::getIcon('b_drop', __('Drop')); - $titles['NoDrop'] = Util::getIcon('bd_drop', __('Drop')); - $titles['Empty'] = Util::getIcon('b_empty', __('Empty')); - $titles['NoEmpty'] = Util::getIcon('bd_empty', __('Empty')); - $titles['Edit'] = Util::getIcon('b_edit', __('Edit')); - $titles['NoEdit'] = Util::getIcon('bd_edit', __('Edit')); - $titles['Export'] = Util::getIcon('b_export', __('Export')); - $titles['NoExport'] = Util::getIcon('bd_export', __('Export')); - $titles['Execute'] = Util::getIcon('b_nextpage', __('Execute')); - $titles['NoExecute'] = Util::getIcon('bd_nextpage', __('Execute')); - $titles['Favorite'] = Util::getIcon('b_favorite', ''); - $titles['NoFavorite'] = Util::getIcon('b_no_favorite', ''); - - $this->assertEquals($titles, Util::buildActionTitles()); - } - /** * Test if cached data is available after set * @@ -885,32 +777,6 @@ class UtilTest extends PmaTestCase ]; } - /** - * Test for formatSql - * - * @covers \PhpMyAdmin\Util::formatSql - * - * @return void - */ - public function testFormatSql() - { - $this->assertEquals( - '
' . "\n"
-            . 'SELECT 1 < 2' . "\n"
-            . '
', - Util::formatSql('SELECT 1 < 2') - ); - - $GLOBALS['cfg']['MaxCharactersInDisplayedSQL'] = 6; - - $this->assertEquals( - '
' . "\n"
-            . 'SELECT[...]' . "\n"
-            . '
', - Util::formatSql('SELECT 1 < 2', true) - ); - } - /** * format byte test, globals are defined * @@ -1203,309 +1069,6 @@ class UtilTest extends PmaTestCase ]; } - /** - * Test for Util::generateHiddenMaxFileSize - * - * @param int $size Size - * - * @return void - * - * @covers \PhpMyAdmin\Util::generateHiddenMaxFileSize - * @dataProvider providerGenerateHiddenMaxFileSize - */ - public function testGenerateHiddenMaxFileSize($size): void - { - $this->assertEquals( - Util::generateHiddenMaxFileSize($size), - '' - ); - } - - /** - * Data provider for testGenerateHiddenMaxFileSize - * - * @return array - */ - public function providerGenerateHiddenMaxFileSize() - { - return [ - [10], - ["100"], - [1024], - ["1024Mb"], - [2147483648], - ["some_string"], - ]; - } - - /** - * Test for getDbLink - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDbLink - * @group medium - */ - public function testGetDbLinkEmpty() - { - $GLOBALS['db'] = null; - $this->assertEmpty(Util::getDbLink()); - } - - /** - * Test for getDbLink - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDbLink - * @group medium - */ - public function testGetDbLinkNull() - { - global $cfg; - $GLOBALS['db'] = 'test_db'; - $GLOBALS['server'] = 99; - $database = $GLOBALS['db']; - $this->assertEquals( - '' - . htmlspecialchars($database) . '', - Util::getDbLink() - ); - } - - /** - * Test for getDbLink - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDbLink - */ - public function testGetDbLink() - { - global $cfg; - $GLOBALS['server'] = 99; - $database = 'test_database'; - $this->assertEquals( - '' - . htmlspecialchars($database) . '', - Util::getDbLink($database) - ); - } - - /** - * Test for getDbLink - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDbLink - */ - public function testGetDbLinkWithSpecialChars() - { - global $cfg; - $GLOBALS['server'] = 99; - $database = 'test&data\'base'; - $this->assertEquals( - '' - . htmlspecialchars($database) . '', - Util::getDbLink($database) - ); - } - - /** - * Test for getDivForSliderEffect - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDivForSliderEffect - */ - public function testGetDivForSliderEffectTest() - { - global $cfg; - $cfg['InitialSlidersState'] = 'undefined'; - - $id = "test_id"; - $message = "test_message"; - - $this->assertXmlStringEqualsXmlString( - "" . Util::getDivForSliderEffect($id, $message) . "
", - "
\n
" - ); - } - - /** - * Test for getDivForSliderEffect - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDivForSliderEffect - */ - public function testGetDivForSliderEffectTestClosed() - { - global $cfg; - $cfg['InitialSlidersState'] = 'closed'; - - $id = "test_id"; - $message = "test_message"; - - $this->assertXmlStringEqualsXmlString( - "" . Util::getDivForSliderEffect($id, $message) . "
", - "
\n
" - ); - } - - /** - * Test for getDivForSliderEffect - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDivForSliderEffect - */ - public function testGetDivForSliderEffectTestDisabled() - { - global $cfg; - $cfg['InitialSlidersState'] = 'disabled'; - - $id = "test_id"; - $message = "test_message"; - - $this->assertXmlStringEqualsXmlString( - "" . Util::getDivForSliderEffect($id, $message) . "", - "
\n
" - ); - } - - /** - * Test for getDropdown - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDropdown - */ - public function testGetDropdownEmpty() - { - $name = "test_dropdown_name"; - $choices = []; - $active_choice = null; - $id = "test_<dropdown>_name"; - - $result = '' . "\n"; - - $this->assertEquals( - $result, - Util::getDropdown( - $name, - $choices, - $active_choice, - $id - ) - ); - } - - /** - * Test for getDropdown - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDropdown - */ - public function testGetDropdown() - { - $name = "&test_dropdown_name"; - $choices = [ - "value_1" => "label_1", - "value&_2\"" => "label_2", - ]; - $active_choice = null; - $id = "test_<dropdown>_name"; - - $result = '' . "\n"; - - $this->assertEquals( - $result, - Util::getDropdown( - $name, - $choices, - $active_choice, - $id - ) - ); - } - - /** - * Test for getDropdown - * - * @return void - * - * @covers \PhpMyAdmin\Util::getDropdown - */ - public function testGetDropdownWithActive() - { - $name = "&test_dropdown_name"; - $choices = [ - "value_1" => "label_1", - "value&_2\"" => "label_2", - ]; - $active_choice = "value&_2\""; - $id = "test_<dropdown>_name"; - - $result = '' . "\n"; - - $this->assertEquals( - $result, - Util::getDropdown( - $name, - $choices, - $active_choice, - $id - ) - ); - } - /** * Test for Util::getFormattedMaximumUploadSize * @@ -1572,405 +1135,6 @@ class UtilTest extends PmaTestCase ]; } - /** - * Test for Util::getIcon - * - * @return void - * - * @covers \PhpMyAdmin\Util::getIcon - */ - public function testGetIconWithoutActionLinksMode() - { - $GLOBALS['cfg']['ActionLinksMode'] = 'text'; - - $this->assertEquals( - '', - Util::getIcon('b_comment') - ); - } - - /** - * Test for Util::getIcon - * - * @return void - * - * @covers \PhpMyAdmin\Util::getIcon - */ - public function testGetIconWithActionLinksMode() - { - $GLOBALS['cfg']['ActionLinksMode'] = 'icons'; - - $this->assertEquals( - '', - Util::getIcon('b_comment') - ); - } - - /** - * Test for Util::getIcon - * - * @return void - * - * @covers \PhpMyAdmin\Util::getIcon - */ - public function testGetIconAlternate() - { - $GLOBALS['cfg']['ActionLinksMode'] = 'icons'; - $alternate_text = 'alt_str'; - - $this->assertEquals( - '' . $alternate_text
-            . '', - Util::getIcon('b_comment', $alternate_text) - ); - } - - /** - * Test for Util::getIcon - * - * @return void - * - * @covers \PhpMyAdmin\Util::getIcon - */ - public function testGetIconWithForceText() - { - $GLOBALS['cfg']['ActionLinksMode'] = 'icons'; - $alternate_text = 'alt_str'; - - // Here we are checking for an icon embedded inside a span (i.e not a menu - // bar icon - $this->assertEquals( - '' . $alternate_text
-            . ' ' . $alternate_text . '', - Util::getIcon('b_comment', $alternate_text, true, false) - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsEmpty() - { - $name = "test_display_radio"; - $choices = []; - - $this->assertEquals( - Util::getRadioFields($name, $choices), - "" - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFields() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value_2' => 'choice_2', - ]; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '' . $choice_label - . ''; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields($name, $choices), - $out - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsWithChecked() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value_2' => 'choice_2', - ]; - $checked_choice = "value_2"; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '' . $choice_label - . ''; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields( - $name, - $choices, - $checked_choice - ), - $out - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsWithCheckedWithClass() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value_2' => 'choice_2', - ]; - $checked_choice = "value_2"; - $class = "test_class"; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '
'; - $out .= "\n"; - $out .= '' . $choice_label - . ''; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields( - $name, - $choices, - $checked_choice, - true, - false, - $class - ), - $out - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsWithoutBR() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value&_<2>' => 'choice_2', - ]; - $checked_choice = "choice_2"; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '' . $choice_label - . ''; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields( - $name, - $choices, - $checked_choice, - false - ), - $out - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsEscapeLabelEscapeLabel() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value_&2' => 'choice&_<2>', - ]; - $checked_choice = "value_2"; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '' - . htmlspecialchars($choice_label) . ''; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields( - $name, - $choices, - $checked_choice, - true, - true - ), - $out - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsEscapeLabelNotEscapeLabel() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value_&2' => 'choice&_<2>', - ]; - $checked_choice = "value_2"; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '' . $choice_label - . ''; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields( - $name, - $choices, - $checked_choice, - true, - false - ), - $out - ); - } - - /** - * Test for getRadioFields - * - * @return void - * - * @covers \PhpMyAdmin\Util::getRadioFields - */ - public function testGetRadioFieldsEscapeLabelEscapeLabelWithClass() - { - $name = "test_display_radio"; - $choices = [ - 'value_1' => 'choice_1', - 'value_&2' => 'choice&_<2>', - ]; - $checked_choice = "value_2"; - $class = "test_class"; - - $out = ""; - foreach ($choices as $choice_value => $choice_label) { - $html_field_id = $name . '_' . $choice_value; - $out .= '
'; - $out .= "\n"; - $out .= '' - . htmlspecialchars($choice_label) . ''; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - $out .= '
'; - $out .= "\n"; - } - - $this->assertEquals( - Util::getRadioFields( - $name, - $choices, - $checked_choice, - true, - true, - $class - ), - $out - ); - } - /** * Test for Util::getTitleForTarget * @@ -2403,49 +1567,6 @@ class UtilTest extends PmaTestCase } } - /** - * Test for Util::showDocu - * - * @return void - * - * @covers \PhpMyAdmin\Util::showDocu - */ - public function testShowDocu() - { - $GLOBALS['server'] = '99'; - $GLOBALS['cfg']['ServerDefault'] = 1; - - $this->assertEquals( - 'Documentation', - Util::showDocu('page', 'anchor') - ); - } - - /** - * Test for showPHPDocu - * - * @return void - * - * @covers \PhpMyAdmin\Util::showPHPDocu - */ - public function testShowPHPDocu() - { - $GLOBALS['server'] = 99; - $GLOBALS['cfg']['ServerDefault'] = 0; - - $target = "docu"; - $lang = _pgettext('PHP documentation language', 'en'); - $expected = '' - . ''
-            . __('Documentation') . ''; - - $this->assertEquals( - $expected, - Util::showPHPDocu($target) - ); - } - /** * test of generating user dir, globals are defined * @@ -2550,7 +1671,7 @@ class UtilTest extends PmaTestCase * * @return void * - * @covers PhpMyAdmin\Util::getPageFromPosition + * @covers \PhpMyAdmin\Util::getPageFromPosition */ public function testGetPageFromPosition() { @@ -2561,79 +1682,36 @@ class UtilTest extends PmaTestCase } /** - * Test for Util::linkOrButton - * - * @param array $params params - * @param int $limit limit - * @param string $match match + * Test for PhpMyAdmin\Util::buildActionTitles * * @return void - * - * @dataProvider linksOrButtons */ - public function testLinkOrButton(array $params, $limit, $match): void + public function testBuildActionTitles(): void { - $restore = $GLOBALS['cfg']['LinkLengthLimit'] ?? 1000; - $GLOBALS['cfg']['LinkLengthLimit'] = $limit; - try { - $result = call_user_func_array( - [ - 'PhpMyAdmin\Util', - 'linkOrButton', - ], - $params - ); - $this->assertEquals($match, $result); - } finally { - $GLOBALS['cfg']['LinkLengthLimit'] = $restore; - } - } + $GLOBALS['cfg'] = ['ActionLinksMode' => 'both']; - /** - * Data provider for Util::linkOrButton test - * - * @return array - */ - public function linksOrButtons() - { - return [ - [ - [ - 'index.php', - 'text', - ], - 1000, - 'text', - ], - [ - [ - 'index.php?some=parameter', - 'text', - ], - 20, - 'text', - ], - [ - [ - 'index.php', - 'text', - [], - 'target', - ], - 1000, - 'text', - ], - [ - [ - 'url.php?url=http://phpmyadmin.net/', - 'text', - [], - '_blank', - ], - 1000, - 'text', - ], - ]; + $titles = []; + $titles['Browse'] = Generator::getIcon('b_browse', __('Browse')); + $titles['NoBrowse'] = Generator::getIcon('bd_browse', __('Browse')); + $titles['Search'] = Generator::getIcon('b_select', __('Search')); + $titles['NoSearch'] = Generator::getIcon('bd_select', __('Search')); + $titles['Insert'] = Generator::getIcon('b_insrow', __('Insert')); + $titles['NoInsert'] = Generator::getIcon('bd_insrow', __('Insert')); + $titles['Structure'] = Generator::getIcon('b_props', __('Structure')); + $titles['Drop'] = Generator::getIcon('b_drop', __('Drop')); + $titles['NoDrop'] = Generator::getIcon('bd_drop', __('Drop')); + $titles['Empty'] = Generator::getIcon('b_empty', __('Empty')); + $titles['NoEmpty'] = Generator::getIcon('bd_empty', __('Empty')); + $titles['Edit'] = Generator::getIcon('b_edit', __('Edit')); + $titles['NoEdit'] = Generator::getIcon('bd_edit', __('Edit')); + $titles['Export'] = Generator::getIcon('b_export', __('Export')); + $titles['NoExport'] = Generator::getIcon('bd_export', __('Export')); + $titles['Execute'] = Generator::getIcon('b_nextpage', __('Execute')); + $titles['NoExecute'] = Generator::getIcon('bd_nextpage', __('Execute')); + $titles['Favorite'] = Generator::getIcon('b_favorite', ''); + $titles['NoFavorite'] = Generator::getIcon('b_no_favorite', ''); + + $this->assertEquals($titles, Util::buildActionTitles()); } /**