Feature #569 Load/Save Query By Example
Implement possibility to save/load/delete a QBE on a DB. Signed-off-by: Hugues Peccatte <hugues.peccatte@gmail.com>
This commit is contained in:
parent
b7f5acff6b
commit
8b84cf0d91
@ -60,6 +60,7 @@ $cfg['Servers'][$i]['AllowNoPassword'] = false;
|
||||
// $cfg['Servers'][$i]['users'] = 'pma__users';
|
||||
// $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups';
|
||||
// $cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding';
|
||||
// $cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches';
|
||||
/* Contrib / Swekey authentication */
|
||||
// $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf';
|
||||
|
||||
@ -145,7 +146,7 @@ $cfg['SaveDir'] = '';
|
||||
/**
|
||||
* Should error reporting be enabled for JavaScript errors
|
||||
*
|
||||
* default = 'ask'
|
||||
* default = 'ask'
|
||||
*/
|
||||
//$cfg['SendErrorReports'] = 'ask';
|
||||
|
||||
|
||||
43
db_qbe.php
43
db_qbe.php
@ -19,6 +19,47 @@ $response = PMA_Response::getInstance();
|
||||
// Gets the relation settings
|
||||
$cfgRelation = PMA_getRelationsParam();
|
||||
|
||||
$savedSearchList = array();
|
||||
$currentSearchId = null;
|
||||
if ($cfgRelation['savedsearcheswork']) {
|
||||
include 'libraries/SavedSearches.php';
|
||||
$header = $response->getHeader();
|
||||
$scripts = $header->getScripts();
|
||||
$scripts->addFile('db_qbe.js');
|
||||
|
||||
//Get saved search list.
|
||||
$savedSearch = new PMA_SavedSearches($GLOBALS);
|
||||
$savedSearch->setId($_REQUEST['searchId'])
|
||||
->setUsername($GLOBALS['cfg']['Server']['user'])
|
||||
->setDbname($_REQUEST['db'])
|
||||
->setSearchName($_REQUEST['searchName']);
|
||||
|
||||
//Criterias field is filled only when clicking on "Save search".
|
||||
if (!empty($_REQUEST['action'])) {
|
||||
if ('save' === $_REQUEST['action']) {
|
||||
$saveResult = $savedSearch->setCriterias($_REQUEST)
|
||||
->save();
|
||||
/*if (!$saveResult) {
|
||||
$response->addHTML('raté');
|
||||
exit();
|
||||
}*/
|
||||
} elseif ('delete' === $_REQUEST['action']) {
|
||||
$deleteResult = $savedSearch->delete();
|
||||
//After deletion, reset search.
|
||||
$savedSearch = new PMA_SavedSearches($GLOBALS);
|
||||
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
|
||||
->setDbname($_REQUEST['db']);
|
||||
$_REQUEST = array();
|
||||
} elseif ('load' === $_REQUEST['action']) {
|
||||
$loadResult = $savedSearch->load();
|
||||
}
|
||||
//Else, it's an "update query"
|
||||
}
|
||||
|
||||
$savedSearchList = $savedSearch->getList();
|
||||
$currentSearchId = $savedSearch->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* A query has been submitted -> (maybe) execute it
|
||||
*/
|
||||
@ -52,7 +93,7 @@ if ($message_to_display) {
|
||||
unset($message_to_display);
|
||||
|
||||
// create new qbe search instance
|
||||
$db_qbe = new PMA_DBQbe($GLOBALS['db']);
|
||||
$db_qbe = new PMA_DBQbe($GLOBALS['db'], $savedSearchList, $savedSearch);
|
||||
|
||||
/**
|
||||
* Displays the Query by example form
|
||||
|
||||
@ -47,4 +47,5 @@ foreach ($hosts as $host) {
|
||||
$cfg['Servers'][$i]['users'] = 'pma__users';
|
||||
$cfg['Servers'][$i]['usergroups'] = 'pma__usergroups';
|
||||
$cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding';
|
||||
$cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches';
|
||||
}
|
||||
|
||||
@ -1,40 +1,40 @@
|
||||
-- --------------------------------------------------------
|
||||
-- SQL Commands to set up the pmadb as described in the documentation.
|
||||
--
|
||||
--
|
||||
-- This file is meant for use with MySQL 5 and above!
|
||||
--
|
||||
--
|
||||
-- This script expects the user pma to already be existing. If we would put a
|
||||
-- line here to create him too many users might just use this script and end
|
||||
-- up with having the same password for the controluser.
|
||||
--
|
||||
-- This user "pma" must be defined in config.inc.php (controluser/controlpass)
|
||||
--
|
||||
-- Please don't forget to set up the tablenames in config.inc.php
|
||||
--
|
||||
--
|
||||
-- This user "pma" must be defined in config.inc.php (controluser/controlpass)
|
||||
--
|
||||
-- Please don't forget to set up the tablenames in config.inc.php
|
||||
--
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Database : `phpmyadmin`
|
||||
--
|
||||
--
|
||||
CREATE DATABASE IF NOT EXISTS `phpmyadmin`
|
||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
USE phpmyadmin;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Privileges
|
||||
--
|
||||
--
|
||||
-- (activate this statement if necessary)
|
||||
-- GRANT SELECT, INSERT, DELETE, UPDATE ON `phpmyadmin`.* TO
|
||||
-- 'pma'@localhost;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__bookmark`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__bookmark` (
|
||||
`id` int(11) NOT NULL auto_increment,
|
||||
@ -49,9 +49,9 @@ CREATE TABLE IF NOT EXISTS `pma__bookmark` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__column_info`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__column_info` (
|
||||
`id` int(5) unsigned NOT NULL auto_increment,
|
||||
@ -70,9 +70,9 @@ CREATE TABLE IF NOT EXISTS `pma__column_info` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__history`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__history` (
|
||||
`id` bigint(20) unsigned NOT NULL auto_increment,
|
||||
@ -89,9 +89,9 @@ CREATE TABLE IF NOT EXISTS `pma__history` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__pdf_pages`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__pdf_pages` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -136,9 +136,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_uiprefs` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__relation`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__relation` (
|
||||
`master_db` varchar(64) NOT NULL default '',
|
||||
@ -155,9 +155,9 @@ CREATE TABLE IF NOT EXISTS `pma__relation` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__table_coords`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__table_coords` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -172,9 +172,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_coords` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__table_info`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__table_info` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -187,9 +187,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_info` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__designer_coords`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__designer_coords` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -205,9 +205,9 @@ CREATE TABLE IF NOT EXISTS `pma__designer_coords` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__tracking`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__tracking` (
|
||||
`db_name` varchar(64) NOT NULL,
|
||||
@ -250,7 +250,7 @@ CREATE TABLE IF NOT EXISTS `pma__users` (
|
||||
`username` varchar(64) NOT NULL,
|
||||
`usergroup` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`username`,`usergroup`)
|
||||
)
|
||||
)
|
||||
COMMENT='Users and their assignments to user groups'
|
||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
|
||||
@ -265,10 +265,10 @@ CREATE TABLE IF NOT EXISTS `pma__usergroups` (
|
||||
`tab` varchar(64) NOT NULL,
|
||||
`allowed` enum('Y','N') NOT NULL DEFAULT 'N',
|
||||
PRIMARY KEY (`usergroup`,`tab`,`allowed`)
|
||||
)
|
||||
)
|
||||
COMMENT='User groups with configured menu items'
|
||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
@ -282,6 +282,24 @@ CREATE TABLE IF NOT EXISTS `pma__navigationhiding` (
|
||||
`db_name` varchar(64) NOT NULL,
|
||||
`table_name` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`username`,`item_name`,`item_type`,`db_name`,`table_name`)
|
||||
)
|
||||
)
|
||||
COMMENT='Hidden items of navigation tree'
|
||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `pma__savedsearches`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__savedsearches` (
|
||||
`id` int(5) unsigned NOT NULL auto_increment,
|
||||
`username` varchar(64) NOT NULL default '',
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
`search_name` varchar(64) NOT NULL default '',
|
||||
`search_data` text NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `u_savedsearches_username_dbname` (`username`,`db_name`,`search_name`)
|
||||
)
|
||||
COMMENT='Saved searches'
|
||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
-- --------------------------------------------------------
|
||||
-- SQL Commands to set up the pmadb as described in the documentation.
|
||||
--
|
||||
--
|
||||
-- This file is meant for use with Drizzle 2011.03.13 and above!
|
||||
--
|
||||
--
|
||||
-- This script expects that you take care of database permissions.
|
||||
--
|
||||
-- Please don't forget to set up the tablenames in config.inc.php
|
||||
--
|
||||
--
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Database : `phpmyadmin`
|
||||
--
|
||||
--
|
||||
CREATE DATABASE IF NOT EXISTS `phpmyadmin`
|
||||
COLLATE utf8_bin;
|
||||
USE phpmyadmin;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__bookmark`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__bookmark` (
|
||||
`id` int(11) NOT NULL auto_increment,
|
||||
@ -36,9 +36,9 @@ CREATE TABLE IF NOT EXISTS `pma__bookmark` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__column_info`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__column_info` (
|
||||
`id` int(5) NOT NULL auto_increment,
|
||||
@ -57,9 +57,9 @@ CREATE TABLE IF NOT EXISTS `pma__column_info` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__history`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__history` (
|
||||
`id` bigint(20) NOT NULL auto_increment,
|
||||
@ -76,9 +76,9 @@ CREATE TABLE IF NOT EXISTS `pma__history` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__pdf_pages`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__pdf_pages` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -123,9 +123,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_uiprefs` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__relation`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__relation` (
|
||||
`master_db` varchar(64) NOT NULL default '',
|
||||
@ -142,9 +142,9 @@ CREATE TABLE IF NOT EXISTS `pma__relation` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__table_coords`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__table_coords` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -159,9 +159,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_coords` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__table_info`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__table_info` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -174,9 +174,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_info` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__designer_coords`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__designer_coords` (
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
@ -192,9 +192,9 @@ CREATE TABLE IF NOT EXISTS `pma__designer_coords` (
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
--
|
||||
-- Table structure for table `pma__tracking`
|
||||
--
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__tracking` (
|
||||
`db_name` varchar(64) NOT NULL,
|
||||
@ -236,7 +236,7 @@ CREATE TABLE IF NOT EXISTS `pma__users` (
|
||||
`username` varchar(64) NOT NULL,
|
||||
`usergroup` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`username`,`usergroup`)
|
||||
)
|
||||
)
|
||||
COMMENT='Users and their assignments to user groups'
|
||||
COLLATE utf8_bin;
|
||||
|
||||
@ -251,10 +251,10 @@ CREATE TABLE IF NOT EXISTS `pma__usergroups` (
|
||||
`tab` varchar(64) NOT NULL,
|
||||
`allowed` enum('Y','N') NOT NULL DEFAULT 'N',
|
||||
PRIMARY KEY (`usergroup`,`tab`,`allowed`)
|
||||
)
|
||||
)
|
||||
COMMENT='User groups with configured menu items'
|
||||
COLLATE utf8_bin;
|
||||
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
@ -268,6 +268,24 @@ CREATE TABLE IF NOT EXISTS `pma__navigationhiding` (
|
||||
`db_name` varchar(64) NOT NULL,
|
||||
`table_name` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`username`,`item_name`,`item_type`,`db_name`,`table_name`)
|
||||
)
|
||||
)
|
||||
COMMENT='Hidden items of navigation tree'
|
||||
COLLATE utf8_bin;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `pma__savedsearches`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pma__savedsearches` (
|
||||
`id` int(5) unsigned NOT NULL auto_increment,
|
||||
`username` varchar(64) NOT NULL default '',
|
||||
`db_name` varchar(64) NOT NULL default '',
|
||||
`search_name` varchar(64) NOT NULL default '',
|
||||
`search_data` text NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `u_savedsearches_username_dbname` (`username`,`db_name`,`search_name`)
|
||||
)
|
||||
COMMENT='Saved searches'
|
||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
|
||||
55
js/db_qbe.js
Normal file
55
js/db_qbe.js
Normal file
@ -0,0 +1,55 @@
|
||||
/* vim: set expandtab sw=4 ts=4 sts=4: */
|
||||
/**
|
||||
* @fileoverview function used in QBE for DB
|
||||
* @name Database Operations
|
||||
*
|
||||
* @requires jQuery
|
||||
* @requires jQueryUI
|
||||
* @requires js/functions.js
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ajax event handlers here for db_qbe.php
|
||||
*
|
||||
* Actions Ajaxified here:
|
||||
* Select saved search
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unbind all event handlers before tearing down a page
|
||||
*/
|
||||
AJAX.registerTeardown('db_qbe.js', function () {
|
||||
$("#searchId").die('change');
|
||||
$("#saveSearch").die('click');
|
||||
$("#deleteSearch").die('click');
|
||||
});
|
||||
|
||||
AJAX.registerOnload('db_qbe.js', function () {
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Select saved search'
|
||||
*/
|
||||
$("#searchId").live('change', function (event) {
|
||||
if ('' == $(this).val()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#action').val('load');
|
||||
$('#formQBE').submit();
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Save search'
|
||||
*/
|
||||
$("#saveSearch").live('click', function (event) {
|
||||
$('#action').val('save');
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Delete search'
|
||||
*/
|
||||
$("#deleteSearch").live('click', function (event) {
|
||||
$('#action').val('delete');
|
||||
});
|
||||
});
|
||||
@ -72,6 +72,13 @@ class PMA_DbQbe
|
||||
* @var array
|
||||
*/
|
||||
private $_criteriaRowInsert;
|
||||
/**
|
||||
* Whether to delete a row
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $_criteriaRowDelete;
|
||||
/**
|
||||
* Already set criteria values
|
||||
*
|
||||
@ -149,6 +156,13 @@ class PMA_DbQbe
|
||||
* @var array
|
||||
*/
|
||||
private $_curAndOrCol;
|
||||
/**
|
||||
* Current criteria AND/OR row relations
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $_curAndOrRow;
|
||||
/**
|
||||
* New column count in case of add/delete
|
||||
*
|
||||
@ -163,15 +177,64 @@ class PMA_DbQbe
|
||||
* @var integer
|
||||
*/
|
||||
private $_new_row_count;
|
||||
/**
|
||||
* List of saved searches
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $_savedSearchList = null;
|
||||
/**
|
||||
* Current search
|
||||
*
|
||||
* @access private
|
||||
* @var PMA_SavedSearches
|
||||
*/
|
||||
private $_currentSearch = null;
|
||||
|
||||
/**
|
||||
* Initialize criterias
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
private function _loadCriterias()
|
||||
{
|
||||
if (null === $this->_currentSearch
|
||||
|| null === $this->_currentSearch->getCriterias()
|
||||
) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$criterias = $this->_currentSearch->getCriterias();
|
||||
$_REQUEST = $criterias + $_REQUEST;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for current search
|
||||
*
|
||||
* @return PMA_SavedSearches
|
||||
*/
|
||||
private function _getCurrentSearch()
|
||||
{
|
||||
return $this->_currentSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public Constructor
|
||||
*
|
||||
* @param string $db Database name
|
||||
* @param string $dbname Database name
|
||||
* @param array $savedSearchList List of saved searches
|
||||
* @param PMA_SavedSearches $currentSearch Current search id
|
||||
*/
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->_db = $db;
|
||||
public function __construct(
|
||||
$dbname, $savedSearchList = array(), $currentSearch = null
|
||||
) {
|
||||
$this->_db = $dbname;
|
||||
$this->_savedSearchList = $savedSearchList;
|
||||
$this->_currentSearch = $currentSearch;
|
||||
$this->_loadCriterias();
|
||||
// Sets criteria parameters
|
||||
$this->_setSearchParams();
|
||||
$this->_setCriteriaTablesAndColumns();
|
||||
@ -184,26 +247,7 @@ class PMA_DbQbe
|
||||
*/
|
||||
private function _setSearchParams()
|
||||
{
|
||||
// sets column count
|
||||
$criteriaColumnCount = PMA_ifSetOr(
|
||||
$_REQUEST['criteriaColumnCount'],
|
||||
3,
|
||||
'numeric'
|
||||
);
|
||||
$criteriaColumnAdd = PMA_ifSetOr(
|
||||
$_REQUEST['criteriaColumnAdd'],
|
||||
0,
|
||||
'numeric'
|
||||
);
|
||||
$this->_criteria_column_count = max(
|
||||
$criteriaColumnCount + $criteriaColumnAdd,
|
||||
0
|
||||
);
|
||||
|
||||
// sets row count
|
||||
$rows = PMA_ifSetOr($_REQUEST['rows'], 0, 'numeric');
|
||||
$criteriaRowAdd = PMA_ifSetOr($_REQUEST['criteriaRowAdd'], 0, 'numeric');
|
||||
$this->_criteria_row_count = max($rows + $criteriaRowAdd, 0);
|
||||
$criteriaColumnCount = $this->_initializeCriteriasCount();
|
||||
|
||||
$this->_criteriaColumnInsert = PMA_ifSetOr(
|
||||
$_REQUEST['criteriaColumnInsert'],
|
||||
@ -1130,20 +1174,10 @@ class PMA_DbQbe
|
||||
$unique_columns = $indexes['unique'];
|
||||
$index_columns = $indexes['index'];
|
||||
|
||||
// now we want to find the best.
|
||||
if (isset($unique_columns) && count($unique_columns) > 0) {
|
||||
$candidate_columns = $unique_columns;
|
||||
$needsort = 1;
|
||||
} elseif (isset($index_columns) && count($index_columns) > 0) {
|
||||
$candidate_columns = $index_columns;
|
||||
$needsort = 1;
|
||||
} elseif (isset($where_clause_columns) && count($where_clause_columns) > 0) {
|
||||
$candidate_columns = $where_clause_columns;
|
||||
$needsort = 0;
|
||||
} else {
|
||||
$candidate_columns = $all_tables;
|
||||
$needsort = 0;
|
||||
}
|
||||
list($candidate_columns, $needsort)
|
||||
= $this->_getLeftJoinColumnCandidatesBest(
|
||||
$all_tables, $where_clause_columns, $unique_columns, $index_columns
|
||||
);
|
||||
|
||||
// If we came up with $unique_columns (very good) or $index_columns (still
|
||||
// good) as $candidate_columns we want to check if we have any 'Y' there
|
||||
@ -1348,8 +1382,13 @@ class PMA_DbQbe
|
||||
*/
|
||||
public function getSelectionForm($cfgRelation)
|
||||
{
|
||||
$html_output = '<form action="db_qbe.php" method="post">';
|
||||
$html_output = '<form action="db_qbe.php" method="post" id="formQBE">';
|
||||
$html_output .= '<fieldset>';
|
||||
|
||||
if ($GLOBALS['cfgRelation']['savedsearcheswork']) {
|
||||
$html_output .= $this->_getSavedSearchesField();
|
||||
}
|
||||
|
||||
$html_output .= '<table class="data" style="width: 100%;">';
|
||||
// Get table's <tr> elements
|
||||
$html_output .= $this->_getColumnNamesRow();
|
||||
@ -1397,5 +1436,111 @@ class PMA_DbQbe
|
||||
$html_output .= '</form>';
|
||||
return $html_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fields to display
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function _getSavedSearchesField()
|
||||
{
|
||||
$html_output = __('Saved searches : ');
|
||||
$html_output .= '<select name="searchId" id="searchId">';
|
||||
$html_output .= '<option value="">New search</option>';
|
||||
|
||||
$currentSearch = $this->_getCurrentSearch();
|
||||
$currentSearchId = null;
|
||||
$currentSearchName = null;
|
||||
if (null != $currentSearch) {
|
||||
$currentSearchId = $currentSearch->getId();
|
||||
$currentSearchName = $currentSearch->getSearchName();
|
||||
}
|
||||
|
||||
foreach ($this->_savedSearchList as $id => $name) {
|
||||
$html_output .= '<option value="' . htmlspecialchars($id)
|
||||
. '" ' . (
|
||||
$id == $currentSearchId
|
||||
? 'selected="selected" '
|
||||
: ''
|
||||
)
|
||||
. '>'
|
||||
. htmlspecialchars($name)
|
||||
. '</option>';
|
||||
}
|
||||
$html_output .= '</select>';
|
||||
$html_output .= '<input type="text" name="searchName" id="searchName" '
|
||||
. 'value="' . $currentSearchName . '" />';
|
||||
$html_output .= '<input type="hidden" name="action" id="action" value="" />';
|
||||
$html_output .= '<input type="submit" name="saveSearch" id="saveSearch" '
|
||||
. 'value="' . __('Save search') . '" />';
|
||||
$html_output .= '<input type="submit" name="deleteSearch" id="deleteSearch" '
|
||||
. 'value="' . __('Delete search') . '" />';
|
||||
|
||||
return $html_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize _criteria_column_count
|
||||
*
|
||||
* @return int Previous number of columns
|
||||
*/
|
||||
private function _initializeCriteriasCount()
|
||||
{
|
||||
// sets column count
|
||||
$criteriaColumnCount = PMA_ifSetOr(
|
||||
$_REQUEST['criteriaColumnCount'],
|
||||
3,
|
||||
'numeric'
|
||||
);
|
||||
$criteriaColumnAdd = PMA_ifSetOr(
|
||||
$_REQUEST['criteriaColumnAdd'],
|
||||
0,
|
||||
'numeric'
|
||||
);
|
||||
$this->_criteria_column_count = max(
|
||||
$criteriaColumnCount + $criteriaColumnAdd,
|
||||
0
|
||||
);
|
||||
|
||||
// sets row count
|
||||
$rows = PMA_ifSetOr($_REQUEST['rows'], 0, 'numeric');
|
||||
$criteriaRowAdd = PMA_ifSetOr($_REQUEST['criteriaRowAdd'], 0, 'numeric');
|
||||
$this->_criteria_row_count = max($rows + $criteriaRowAdd, 0);
|
||||
|
||||
return $criteriaColumnCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get best
|
||||
*
|
||||
* @param array $all_tables All tables
|
||||
* @param array $where_clause_columns Columns with where clause
|
||||
* @param array $unique_columns Unique columns
|
||||
* @param array $index_columns Indexed columns
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _getLeftJoinColumnCandidatesBest(
|
||||
$all_tables, $where_clause_columns, $unique_columns, $index_columns
|
||||
) {
|
||||
// now we want to find the best.
|
||||
if (isset($unique_columns) && count($unique_columns) > 0) {
|
||||
$candidate_columns = $unique_columns;
|
||||
$needsort = 1;
|
||||
return array($candidate_columns, $needsort);
|
||||
} elseif (isset($index_columns) && count($index_columns) > 0) {
|
||||
$candidate_columns = $index_columns;
|
||||
$needsort = 1;
|
||||
return array($candidate_columns, $needsort);
|
||||
} elseif (isset($where_clause_columns) && count($where_clause_columns) > 0) {
|
||||
$candidate_columns = $where_clause_columns;
|
||||
$needsort = 0;
|
||||
return array($candidate_columns, $needsort);
|
||||
} else {
|
||||
$candidate_columns = $all_tables;
|
||||
$needsort = 0;
|
||||
return array($candidate_columns, $needsort);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
397
libraries/SavedSearches.php
Normal file
397
libraries/SavedSearches.php
Normal file
@ -0,0 +1,397 @@
|
||||
<?php
|
||||
/* vim: set expandtab sw=4 ts=4 sts=4: */
|
||||
/**
|
||||
* Saved searches managing
|
||||
*
|
||||
* @package PhpMyAdmin
|
||||
*/
|
||||
|
||||
if (! defined('PHPMYADMIN')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saved searches managing
|
||||
*
|
||||
* @package PhpMyAdmin
|
||||
*/
|
||||
class PMA_SavedSearches
|
||||
{
|
||||
/**
|
||||
* Global configuration
|
||||
* @var array
|
||||
*/
|
||||
private $_config = null;
|
||||
|
||||
/**
|
||||
* Id
|
||||
* @var int|null
|
||||
*/
|
||||
private $_id = null;
|
||||
|
||||
/**
|
||||
* Username
|
||||
* @var string
|
||||
*/
|
||||
private $_username = null;
|
||||
|
||||
/**
|
||||
* DB name
|
||||
* @var string
|
||||
*/
|
||||
private $_dbname = null;
|
||||
|
||||
/**
|
||||
* Saved search name
|
||||
* @var string
|
||||
*/
|
||||
private $_searchName = null;
|
||||
|
||||
/**
|
||||
* Setter of id
|
||||
*
|
||||
* @param int|null $searchId Id of search
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setId($searchId)
|
||||
{
|
||||
$searchId = (int)$searchId;
|
||||
if (empty($searchId)) {
|
||||
$searchId = null;
|
||||
}
|
||||
|
||||
$this->_id = $searchId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of id
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of searchName
|
||||
*
|
||||
* @param string $searchName Saved search name
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setSearchName($searchName)
|
||||
{
|
||||
$this->_searchName = $searchName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of searchName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSearchName()
|
||||
{
|
||||
return $this->_searchName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterias
|
||||
* @var array
|
||||
*/
|
||||
private $_criterias = null;
|
||||
|
||||
/**
|
||||
* Setter of config
|
||||
*
|
||||
* @param array $config Global configuration
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
||||
$this->_config = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for criterias
|
||||
*
|
||||
* @param array $criterias Criterias of saved searches
|
||||
* @param bool $json Criterias are in JSON format
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setCriterias($criterias, $json = false)
|
||||
{
|
||||
if (true === $json) {
|
||||
$this->_criterias = json_decode($criterias, true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$aListFieldsToGet = array(
|
||||
'criteriaColumn',
|
||||
'criteriaSort',
|
||||
'criteriaShow',
|
||||
'criteria',
|
||||
'criteriaAndOrRow',
|
||||
'criteriaAndOrColumn',
|
||||
'rows'
|
||||
);
|
||||
|
||||
$data = array();
|
||||
|
||||
$data['criteriaColumnCount'] = count($criterias['criteriaColumn']);
|
||||
|
||||
foreach ($aListFieldsToGet as $field) {
|
||||
$data[$field] = $criterias[$field];
|
||||
}
|
||||
|
||||
for ($i = 0; $i <= $data['rows']; $i++) {
|
||||
$data['Or' . $i] = $criterias['Or' . $i];
|
||||
}
|
||||
|
||||
$this->_criterias = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for criterias
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCriterias()
|
||||
{
|
||||
return $this->_criterias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for username
|
||||
*
|
||||
* @param string $username Username
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->_username = $username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->_username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for DB name
|
||||
*
|
||||
* @param string $dbname DB name
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setDbname($dbname)
|
||||
{
|
||||
$this->_dbname = $dbname;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for DB name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDbname()
|
||||
{
|
||||
return $this->_dbname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* @param array $config Global configuration
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->setConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the search
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if (null == $this->getUsername()
|
||||
|| null == $this->getDbname()
|
||||
|| null == $this->getSearchName()
|
||||
|| null == $this->getCriterias()
|
||||
) {
|
||||
PMA_Util::mysqlDie(__('Missing information to save the search.'));
|
||||
}
|
||||
|
||||
$savedSearchesTbl
|
||||
= PMA_Util::backquote($this->_config['cfgRelation']['db']) . "."
|
||||
. PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']);
|
||||
|
||||
//If it's an insert.
|
||||
if (null === $this->getId()) {
|
||||
$wheres = array(
|
||||
"search_name = '" . PMA_Util::sqlAddSlashes($this->getSearchName())
|
||||
. "'"
|
||||
);
|
||||
$existingSearches = $this->getList($wheres);
|
||||
|
||||
if (!empty($existingSearches)) {
|
||||
PMA_Util::mysqlDie(__('An entry with this name already exists.'));
|
||||
}
|
||||
|
||||
$sqlQuery = "INSERT INTO " . $savedSearchesTbl
|
||||
. "(`username`, `db_name`, `search_name`, `search_data`)"
|
||||
. " VALUES ("
|
||||
. "'" . PMA_Util::sqlAddSlashes($this->getUsername()) . "',"
|
||||
. "'" . PMA_Util::sqlAddSlashes($this->getDbname()) . "',"
|
||||
. "'" . PMA_Util::sqlAddSlashes($this->getSearchName()) . "',"
|
||||
. "'" . PMA_Util::sqlAddSlashes(json_encode($this->getCriterias()))
|
||||
. "')";
|
||||
|
||||
$result = (bool)PMA_queryAsControlUser($sqlQuery);
|
||||
if (!$result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->setId($GLOBALS['dbi']->insertId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Else, it's an update.
|
||||
$wheres = array(
|
||||
"id != " . $this->getId(),
|
||||
"search_name = '" . PMA_Util::sqlAddSlashes($this->getSearchName()) . "'"
|
||||
);
|
||||
$existingSearches = $this->getList($wheres);
|
||||
|
||||
if (!empty($existingSearches)) {
|
||||
PMA_Util::mysqlDie(__('An entry with this name already exists.'));
|
||||
}
|
||||
|
||||
$sqlQuery = "UPDATE " . $savedSearchesTbl
|
||||
. "SET `search_name` = '"
|
||||
. PMA_Util::sqlAddSlashes($this->getSearchName()) . "', "
|
||||
. "`search_data` = '"
|
||||
. PMA_Util::sqlAddSlashes(json_encode($this->getCriterias())) . "' "
|
||||
. "WHERE id = " . $this->getId();
|
||||
return (bool)PMA_queryAsControlUser($sqlQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the search
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if (null == $this->getId()) {
|
||||
PMA_Util::mysqlDie(__('Missing information to delete the search.'));
|
||||
}
|
||||
|
||||
$savedSearchesTbl
|
||||
= PMA_Util::backquote($this->_config['cfgRelation']['db']) . "."
|
||||
. PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']);
|
||||
|
||||
$sqlQuery = "DELETE FROM " . $savedSearchesTbl
|
||||
. "WHERE id = '" . PMA_Util::sqlAddSlashes($this->getId()) . "'";
|
||||
|
||||
return (bool)PMA_queryAsControlUser($sqlQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the current search from an id.
|
||||
*
|
||||
* @return bool Success
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if (null == $this->getId()) {
|
||||
PMA_Util::mysqlDie(__('Missing information to load the search.'));
|
||||
}
|
||||
|
||||
$savedSearchesTbl = PMA_Util::backquote($this->_config['cfgRelation']['db'])
|
||||
. "."
|
||||
. PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']);
|
||||
$sqlQuery = "SELECT id, search_name, search_data "
|
||||
. "FROM " . $savedSearchesTbl . " "
|
||||
. "WHERE id = '" . PMA_Util::sqlAddSlashes($this->getId()) . "' ";
|
||||
|
||||
$resList = PMA_queryAsControlUser($sqlQuery);
|
||||
|
||||
if (false === ($oneResult = $GLOBALS['dbi']->fetchArray($resList))) {
|
||||
PMA_Util::mysqlDie(__('Error while loading the search.'));
|
||||
}
|
||||
|
||||
$this->setSearchName($oneResult['search_name'])
|
||||
->setCriterias($oneResult['search_data'], true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of saved search of a user on a DB
|
||||
*
|
||||
* @param array $wheres List of filters
|
||||
*
|
||||
* @return array|bool List of saved search or false on failure
|
||||
*/
|
||||
public function getList(array $wheres = array())
|
||||
{
|
||||
if (null == $this->getUsername()
|
||||
|| null == $this->getDbname()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$savedSearchesTbl = PMA_Util::backquote($this->_config['cfgRelation']['db'])
|
||||
. "."
|
||||
. PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']);
|
||||
$sqlQuery = "SELECT id, search_name "
|
||||
. "FROM " . $savedSearchesTbl . " "
|
||||
. "WHERE "
|
||||
. "username = '" . PMA_Util::sqlAddSlashes($this->getUsername()) . "' "
|
||||
. "AND db_name = '" . PMA_Util::sqlAddSlashes($this->getDbname()) . "' ";
|
||||
|
||||
foreach ($wheres as $where) {
|
||||
$sqlQuery .= "AND " . $where . " ";
|
||||
}
|
||||
|
||||
$sqlQuery .= "order by search_name ASC ";
|
||||
|
||||
$resList = PMA_queryAsControlUser($sqlQuery);
|
||||
|
||||
$list = array();
|
||||
while ($oneResult = $GLOBALS['dbi']->fetchArray($resList)) {
|
||||
$list[$oneResult['id']] = $oneResult['search_name'];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@ -465,6 +465,15 @@ $cfg['Servers'][$i]['usergroups'] = '';
|
||||
*/
|
||||
$cfg['Servers'][$i]['navigationhiding'] = '';
|
||||
|
||||
/**
|
||||
* table to store information about saved searches from query-by-example on a db
|
||||
* - leave blank to disable saved searches feature
|
||||
* SUGGESTED: 'pma__savedsearches'
|
||||
*
|
||||
* @global string $cfg['Servers'][$i]['savedsearches']
|
||||
*/
|
||||
$cfg['Servers'][$i]['savedsearches'] = '';
|
||||
|
||||
/**
|
||||
* Maximum number of records saved in $cfg['Servers'][$i]['table_uiprefs'] table.
|
||||
*
|
||||
|
||||
@ -79,6 +79,7 @@ $forms['Servers']['Server_pmadb'] = array('Servers' => array(1 => array(
|
||||
'table_coords' => 'pma__table_coords',
|
||||
'pdf_pages' => 'pma__pdf_pages',
|
||||
'designer_coords' => 'pma__designer_coords',
|
||||
'saved_searches' => 'pma__savedsearches',
|
||||
'MaxTableUiprefs' => 100)));
|
||||
$forms['Servers']['Server_tracking'] = array('Servers' => array(1 => array(
|
||||
'tracking_version_auto_create',
|
||||
|
||||
@ -395,6 +395,7 @@ function PMA_checkRelationsParam()
|
||||
$cfgRelation['menuswork'] = false;
|
||||
$cfgRelation['navwork'] = false;
|
||||
$cfgRelation['allworks'] = false;
|
||||
$cfgRelation['savedsearcheswork'] = false;
|
||||
$cfgRelation['user'] = null;
|
||||
$cfgRelation['db'] = null;
|
||||
|
||||
@ -463,6 +464,8 @@ function PMA_checkRelationsParam()
|
||||
$cfgRelation['usergroups'] = $curr_table[0];
|
||||
} elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['navigationhiding']) {
|
||||
$cfgRelation['navigationhiding'] = $curr_table[0];
|
||||
} elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['savedsearches']) {
|
||||
$cfgRelation['savedsearches'] = $curr_table[0];
|
||||
}
|
||||
} // end while
|
||||
$GLOBALS['dbi']->freeResult($tab_rs);
|
||||
@ -521,6 +524,10 @@ function PMA_checkRelationsParam()
|
||||
$cfgRelation['navwork'] = true;
|
||||
}
|
||||
|
||||
if (isset($cfgRelation['savedsearches'])) {
|
||||
$cfgRelation['savedsearcheswork'] = true;
|
||||
}
|
||||
|
||||
if ($cfgRelation['relwork'] && $cfgRelation['displaywork']
|
||||
&& $cfgRelation['pdfwork'] && $cfgRelation['commwork']
|
||||
&& $cfgRelation['mimework'] && $cfgRelation['historywork']
|
||||
@ -528,6 +535,7 @@ function PMA_checkRelationsParam()
|
||||
&& $cfgRelation['trackingwork'] && $cfgRelation['userconfigwork']
|
||||
&& $cfgRelation['bookmarkwork'] && $cfgRelation['designerwork']
|
||||
&& $cfgRelation['menuswork'] && $cfgRelation['navwork']
|
||||
&& $cfgRelation['savedsearcheswork']
|
||||
) {
|
||||
$cfgRelation['allworks'] = true;
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ class PMA_Relation_Cleanup_Test extends PHPUnit_Framework_TestCase
|
||||
$GLOBALS['cfg']['Server']['users'] = 'users';
|
||||
$GLOBALS['cfg']['Server']['usergroups'] = 'usergroups';
|
||||
$GLOBALS['cfg']['Server']['navigationhiding'] = 'navigationhiding';
|
||||
$GLOBALS['cfg']['Server']['savedsearches'] = 'savedsearches';
|
||||
|
||||
$this->redefineRelation();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user