diff --git a/libraries/classes/Database/Search.php b/libraries/classes/Database/Search.php index a7c088936e..151fb12351 100644 --- a/libraries/classes/Database/Search.php +++ b/libraries/classes/Database/Search.php @@ -7,8 +7,7 @@ */ namespace PhpMyAdmin\Database; -use PhpMyAdmin\Sanitize; -use PhpMyAdmin\Url; +use PhpMyAdmin\Template; use PhpMyAdmin\Util; /** @@ -24,56 +23,63 @@ class Search * @access private * @var string */ - private $_db; + private $db; + /** * Table Names * * @access private * @var array */ - private $_tables_names_only; + private $tablesNamesOnly; + /** * Type of search * * @access private * @var array */ - private $_searchTypes; + private $searchTypes; + /** * Already set search type * * @access private * @var integer */ - private $_criteriaSearchType; + private $criteriaSearchType; + /** * Already set search type's description * * @access private * @var string */ - private $_searchTypeDescription; + private $searchTypeDescription; + /** * Search string/regexp * * @access private * @var string */ - private $_criteriaSearchString; + private $criteriaSearchString; + /** * Criteria Tables to search in * * @access private * @var array */ - private $_criteriaTables; + private $criteriaTables; + /** * Restrict the search to this column * * @access private * @var string */ - private $_criteriaColumnName; + private $criteriaColumnName; /** * Public Constructor @@ -82,8 +88,8 @@ class Search */ public function __construct($db) { - $this->_db = $db; - $this->_searchTypes = array( + $this->db = $db; + $this->searchTypes = array( '1' => __('at least one of the words'), '2' => __('all of the words'), '3' => __('the exact phrase as substring'), @@ -91,7 +97,7 @@ class Search '5' => __('as regular expression'), ); // Sets criteria parameters - $this->_setSearchParams(); + $this->setSearchParams(); } /** @@ -99,51 +105,51 @@ class Search * * @return void */ - private function _setSearchParams() + private function setSearchParams() { - $this->_tables_names_only = $GLOBALS['dbi']->getTables($this->_db); + $this->tablesNamesOnly = $GLOBALS['dbi']->getTables($this->db); if (empty($_REQUEST['criteriaSearchType']) || ! is_string($_REQUEST['criteriaSearchType']) || ! array_key_exists( $_REQUEST['criteriaSearchType'], - $this->_searchTypes + $this->searchTypes ) ) { - $this->_criteriaSearchType = 1; + $this->criteriaSearchType = 1; unset($_REQUEST['submit_search']); } else { - $this->_criteriaSearchType = (int) $_REQUEST['criteriaSearchType']; - $this->_searchTypeDescription - = $this->_searchTypes[$_REQUEST['criteriaSearchType']]; + $this->criteriaSearchType = (int) $_REQUEST['criteriaSearchType']; + $this->searchTypeDescription + = $this->searchTypes[$_REQUEST['criteriaSearchType']]; } if (empty($_REQUEST['criteriaSearchString']) || ! is_string($_REQUEST['criteriaSearchString']) ) { - $this->_criteriaSearchString = ''; + $this->criteriaSearchString = ''; unset($_REQUEST['submit_search']); } else { - $this->_criteriaSearchString = $_REQUEST['criteriaSearchString']; + $this->criteriaSearchString = $_REQUEST['criteriaSearchString']; } - $this->_criteriaTables = array(); + $this->criteriaTables = array(); if (empty($_REQUEST['criteriaTables']) || ! is_array($_REQUEST['criteriaTables']) ) { unset($_REQUEST['submit_search']); } else { - $this->_criteriaTables = array_intersect( - $_REQUEST['criteriaTables'], $this->_tables_names_only + $this->criteriaTables = array_intersect( + $_REQUEST['criteriaTables'], $this->tablesNamesOnly ); } if (empty($_REQUEST['criteriaColumnName']) || ! is_string($_REQUEST['criteriaColumnName']) ) { - unset($this->_criteriaColumnName); + unset($this->criteriaColumnName); } else { - $this->_criteriaColumnName = $GLOBALS['dbi']->escapeString( + $this->criteriaColumnName = $GLOBALS['dbi']->escapeString( $_REQUEST['criteriaColumnName'] ); } @@ -165,7 +171,7 @@ class Search * count * strlen */ - private function _getSearchSqls($table) + private function getSearchSqls($table) { // Statement types $sqlstr_select = 'SELECT'; @@ -175,7 +181,7 @@ class Search . Util::backquote($GLOBALS['db']) . '.' . Util::backquote($table); // Gets where clause for the query - $where_clause = $this->_getWhereClause($table); + $where_clause = $this->getWhereClause($table); // Builds complete queries $sql = array(); $sql['select_columns'] = $sqlstr_select . ' * ' . $sqlstr_from @@ -196,22 +202,22 @@ class Search * * @return string The generated where clause */ - private function _getWhereClause($table) + private function getWhereClause($table) { // Columns to select $allColumns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table); $likeClauses = array(); // Based on search type, decide like/regex & '%'/'' - $like_or_regex = (($this->_criteriaSearchType == 5) ? 'REGEXP' : 'LIKE'); - $automatic_wildcard = (($this->_criteriaSearchType < 4) ? '%' : ''); + $like_or_regex = (($this->criteriaSearchType == 5) ? 'REGEXP' : 'LIKE'); + $automatic_wildcard = (($this->criteriaSearchType < 4) ? '%' : ''); // For "as regular expression" (search option 5), LIKE won't be used // Usage example: If user is searching for a literal $ in a regexp search, // he should enter \$ as the value. $criteriaSearchStringEscaped = $GLOBALS['dbi']->escapeString( - $this->_criteriaSearchString + $this->criteriaSearchString ); // Extract search words or pattern - $search_words = (($this->_criteriaSearchType > 2) + $search_words = (($this->criteriaSearchType > 2) ? array($criteriaSearchStringEscaped) : explode(' ', $criteriaSearchStringEscaped)); @@ -223,9 +229,9 @@ class Search $likeClausesPerColumn = array(); // for each column in the table foreach ($allColumns as $column) { - if (! isset($this->_criteriaColumnName) - || strlen($this->_criteriaColumnName) === 0 - || $column['Field'] == $this->_criteriaColumnName + if (! isset($this->criteriaColumnName) + || strlen($this->criteriaColumnName) === 0 + || $column['Field'] == $this->criteriaColumnName ) { $column = 'CONVERT(' . Util::backquote($column['Field']) . ' USING utf8)'; @@ -240,7 +246,7 @@ class Search } } // end for // Use 'OR' if 'at least one word' is to be searched, else use 'AND' - $implode_str = ($this->_criteriaSearchType == 1 ? ' OR ' : ' AND '); + $implode_str = ($this->criteriaSearchType == 1 ? ' OR ' : ' AND '); if (empty($likeClauses)) { // this could happen when the "inside column" does not exist // in any selected tables @@ -260,104 +266,33 @@ class Search */ public function getSearchResults() { - $html_output = ''; - // Displays search string - $html_output .= '
' - . '' - . ''; - - $num_search_result_total = 0; + $resultTotal = 0; + $rows = []; // For each table selected as search criteria - foreach ($this->_criteriaTables as $each_table) { + foreach ($this->criteriaTables as $eachTable) { // Gets the SQL statements - $newsearchsqls = $this->_getSearchSqls($each_table); + $newSearchSqls = $this->getSearchSqls($eachTable); // Executes the "COUNT" statement - $res_cnt = intval($GLOBALS['dbi']->fetchValue($newsearchsqls['select_count'])); - $num_search_result_total += $res_cnt; + $resultCount = intval($GLOBALS['dbi']->fetchValue( + $newSearchSqls['select_count'] + )); + $resultTotal += $resultCount; // Gets the result row's HTML for a table - $html_output .= $this->_getResultsRow( - $each_table, $newsearchsqls, $res_cnt - ); - } // end for - $html_output .= '
' - . sprintf( - __('Search results for "%s" %s:'), - htmlspecialchars($this->_criteriaSearchString), - $this->_searchTypeDescription - ) - . '
'; - // Displays total number of matches - if (count($this->_criteriaTables) > 1) { - $html_output .= '

'; - $html_output .= sprintf( - _ngettext( - 'Total: %s match', - 'Total: %s matches', - $num_search_result_total - ), - $num_search_result_total - ); - $html_output .= '

'; + $rows[] = [ + 'table' => $eachTable, + 'new_search_sqls' => $newSearchSqls, + 'result_count' => $resultCount, + ]; } - return $html_output; - } - /** - * Provides search results row with browse/delete links. - * (for a table) - * - * @param string $each_table One of the tables on which search was performed - * @param array $newsearchsqls Contains SQL queries - * @param integer $res_cnt Number of results found - * - * @return string HTML row - */ - private function _getResultsRow($each_table, array $newsearchsqls, $res_cnt) - { - $this_url_params = array( - 'db' => $GLOBALS['db'], - 'table' => $each_table, - 'goto' => 'db_sql.php', - 'pos' => 0, - 'is_js_confirmed' => 0, - ); - // Start forming search results row - $html_output = ''; - // Displays results count for a table - $html_output .= ''; - $html_output .= sprintf( - _ngettext( - '%1$s match in %2$s', - '%1$s matches in %2$s', $res_cnt - ), - $res_cnt, htmlspecialchars($each_table) - ); - $html_output .= ''; - // Displays browse/delete link if result count > 0 - if ($res_cnt > 0) { - $this_url_params['db'] = htmlspecialchars($GLOBALS['db']); - $this_url_params['table'] = htmlspecialchars($each_table); - $browse_result_path = 'sql.php' . Url::getCommon($this_url_params); - $html_output .= '' - . __('Browse') . ''; - - $delete_result_path = $browse_result_path; - $html_output .= '' - . __('Delete') . ''; - } else { - $html_output .= ' ' - . ' '; - }// end if else - $html_output .= ''; - return $html_output; + return Template::get('database/search/results')->render([ + 'db' => $this->db, + 'rows' => $rows, + 'result_total' => $resultTotal, + 'criteria_tables' => $this->criteriaTables, + 'criteria_search_string' => $this->criteriaSearchString, + 'search_type_description' => $this->searchTypeDescription, + ]); } /** @@ -367,100 +302,29 @@ class Search */ public function getSelectionForm() { - $html_output = ''; - $html_output .= '
'; - $html_output .= Url::getHiddenInputs($GLOBALS['db']); - $html_output .= '
'; - // set legend caption - $html_output .= '' . __('Search in database') . ''; - $html_output .= ''; - // inputbox for search phrase - $html_output .= ''; - $html_output .= ''; - $html_output .= ''; - $html_output .= ''; - // choices for types of search - $html_output .= ''; - $html_output .= ''; - $html_output .= ''; - // displays table names as select options - $html_output .= ''; - $html_output .= ''; - $html_output .= ''; - // Displays 'select all' and 'unselect all' links - $alter_select = '' - . __('Select all') . '  / '; - $alter_select .= '' - . __('Unselect all') . ''; - $html_output .= ''; - // Inputbox for column name entry - $html_output .= ''; - $html_output .= ''; - $html_output .= ''; - $html_output .= ''; - $html_output .= '
' . __('Words or values to search for (wildcard: "%"):') - . ''; - $html_output .= '
' . __('Find:') . ''; $choices = array( - '1' => $this->_searchTypes[1] . ' ' + '1' => $this->searchTypes[1] . ' ' . Util::showHint( __('Words are separated by a space character (" ").') ), - '2' => $this->_searchTypes[2] . ' ' + '2' => $this->searchTypes[2] . ' ' . Util::showHint( __('Words are separated by a space character (" ").') ), - '3' => $this->_searchTypes[3], - '4' => $this->_searchTypes[4], - '5' => $this->_searchTypes[5] . ' ' - . Util::showMySQLDocu('Regexp') + '3' => $this->searchTypes[3], + '4' => $this->searchTypes[4], + '5' => $this->searchTypes[5] . ' ' . Util::showMySQLDocu('Regexp') ); - // 4th parameter set to true to add line breaks - // 5th parameter set to false to avoid htmlspecialchars() escaping - // in the label since we have some HTML in some labels - $html_output .= Util::getRadioFields( - 'criteriaSearchType', $choices, $this->_criteriaSearchType, true, false - ); - $html_output .= '
' . __('Inside tables:') . ''; - $html_output .= ''; - $html_output .= '
' - . $alter_select . '
' . __('Inside column:') . '
'; - $html_output .= '
'; - $html_output .= '
'; - $html_output .= ''; - $html_output .= '
'; - $html_output .= '
'; - $html_output .= '
' - . '
'; - - return $html_output; + return Template::get('database/search/selection_form')->render([ + 'db' => $this->db, + 'choices' => $choices, + 'criteria_search_string' => $this->criteriaSearchString, + 'criteria_search_type' => $this->criteriaSearchType, + 'criteria_tables' => $this->criteriaTables, + 'tables_names_only' => $this->tablesNamesOnly, + 'criteria_column_name' => isset($this->criteriaColumnName) + ? $this->criteriaColumnName : null, + ]); } /** @@ -470,23 +334,6 @@ class Search */ public function getResultDivs() { - $html_output = ''; - $html_output .= '
'; - $html_output .= ''; - $html_output .= '
'; - // div for browsing results - $html_output .= '
'; - $html_output .= ''; - $html_output .= '
'; - $html_output .= '
'; - $html_output .= '
'; - $html_output .= ''; - $html_output .= '
'; - $html_output .= ''; - $html_output .= ''; - return $html_output; + return Template::get('database/search/result_divs')->render(); } } diff --git a/templates/database/search/result_divs.twig b/templates/database/search/result_divs.twig new file mode 100644 index 0000000000..9f5f19ffdd --- /dev/null +++ b/templates/database/search/result_divs.twig @@ -0,0 +1,13 @@ +{# These two table-image and table-link elements display the table name in browse search results #} +
+ +
+{# Div for browsing results #} +
+ {# This browse-results div is used to load the browse and delete results in the db search #} +
+
+ {# This sqlqueryform div is used to load the delete form in the db search #} +
+{# Toggle query box link #} + diff --git a/templates/database/search/results.twig b/templates/database/search/results.twig new file mode 100644 index 0000000000..efe5529615 --- /dev/null +++ b/templates/database/search/results.twig @@ -0,0 +1,62 @@ + + + {% for row in rows %} + + + {% if row.result_count > 0 %} + {% set url_params = { + 'db': db, + 'table': row.table, + 'goto': 'db_sql.php', + 'pos': 0, + 'is_js_confirmed': 0 + } %} + + + {% else %} + + + {% endif %} + + {% endfor %} +
+ {{ 'Search results for "%s" %s:'|format( + criteria_search_string, + search_type_description + )|raw }} +
+ {% set result_message %} + {% trans %} + %1$s match in %2$s + {% plural row.result_count %} + %1$s matches in %2$s + {% endtrans %} + {% endset %} + {{ result_message|format(row.result_count, row.table)|raw }} + + + {% trans 'Browse' %} + + + + {% trans 'Delete' %} + +
+ +{% if criteria_tables|length > 1 %} +

+ {% trans %} + Total: {{ count }} match + {% plural result_total %} + Total: {{ count }} matches + {% endtrans %} +

+{% endif %} diff --git a/templates/database/search/selection_form.twig b/templates/database/search/selection_form.twig new file mode 100644 index 0000000000..5ae4256b53 --- /dev/null +++ b/templates/database/search/selection_form.twig @@ -0,0 +1,70 @@ + +
+ {{ Url_getHiddenInputs(db) }} +
+ {% trans 'Search in database' %} + + + + + + + + + + + + + + + + + {# Inputbox for column name entry #} + + + + +
+ {% trans 'Words or values to search for (wildcard: "%"):' %} + + +
{% trans 'Find:' %} + {# 4th parameter set to true to add line breaks #} + {# 5th parameter set to false to avoid htmlspecialchars() escaping + in the label since we have some HTML in some labels #} + {{ Util_getRadioFields( + 'criteriaSearchType', + choices, + criteria_search_type, + true, + false + ) }} +
{% trans 'Inside tables:' %} + +
+ + {% trans 'Select all' %} + / + + {% trans 'Unselect all' %} + +
{% trans 'Inside column:' %} + +
+
+
+ +
+
+
+ +
diff --git a/test/classes/Database/SearchTest.php b/test/classes/Database/SearchTest.php index d2da90c801..93dfd3c5a0 100644 --- a/test/classes/Database/SearchTest.php +++ b/test/classes/Database/SearchTest.php @@ -76,7 +76,7 @@ class SearchTest extends PmaTestCase * * @return the output from the protected method. */ - private function _callProtectedFunction($name, $params) + private function callProtectedFunction($name, $params) { $class = new ReflectionClass(Search::class); $method = $class->getMethod($name); @@ -97,8 +97,8 @@ class SearchTest extends PmaTestCase $this->object = new Search('pma_test'); $this->assertEquals( $expected, - $this->_callProtectedFunction( - '_getWhereClause', + $this->callProtectedFunction( + 'getWhereClause', array('table1') ) ); @@ -149,8 +149,8 @@ class SearchTest extends PmaTestCase 'WHERE FALSE', 'delete' => 'DELETE FROM `pma`.`table1` WHERE FALSE' ), - $this->_callProtectedFunction( - '_getSearchSqls', + $this->callProtectedFunction( + 'getSearchSqls', array('table1') ) ); @@ -163,72 +163,12 @@ class SearchTest extends PmaTestCase */ public function testGetSearchResults() { - $this->assertEquals( - '
Search results ' - . 'for "" :
', + $this->assertContains( + 'Search results for "" :', $this->object->getSearchResults() ); } - /** - * Test for _getResultsRow - * - * @param string $each_table Tables on which search is to be performed - * @param array $newsearchsqls Contains SQL queries - * @param string $output Expected HTML output - * - * @return void - * - * @dataProvider providerForTestGetResultsRow - */ - public function testGetResultsRow( - $each_table, $newsearchsqls, $output - ) { - - $this->assertEquals( - $output, - $this->_callProtectedFunction( - '_getResultsRow', - array($each_table, $newsearchsqls, 2) - ) - ); - } - - /** - * Data provider for testGetResultRow - * - * @return array provider for testGetResultsRow - */ - public function providerForTestGetResultsRow() - { - return array( - array( - 'table1', - array( - 'SELECT * FROM `pma`.`table1` WHERE FALSE', - 'SELECT COUNT(*) AS `count` FROM `pma`.`table1` WHERE FALSE', - 'select_count' => 2, - 'select_columns' => 'column1', - 'delete' => 'column2' - ), - '2 matches in table1' - . 'Browse' - . 'Delete' - ) - ); - } - /** * Test for getSelectionForm * @@ -249,20 +189,29 @@ class SearchTest extends PmaTestCase */ public function testGetResultDivs() { - $this->assertEquals( - '
' - . '
' - . '

' - . '
' - . '', - $this->_callProtectedFunction( - 'getResultDivs', - array() - ) + $actual = $this->callProtectedFunction( + 'getResultDivs', + array() + ); + $this->assertContains( + '
assertContains( + 'assertContains( + '
assertContains( + '
assertContains( + '