Merge pull request #18456 from MauricioFauth/triggers

Refactor the Triggers page
This commit is contained in:
Maurício Meneghini Fauth 2023-05-27 13:18:40 -03:00 committed by GitHub
commit 210521bae8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 602 additions and 550 deletions

View File

@ -66,7 +66,6 @@ jobs:
- "Database/Operations"
- "Database/Procedures"
- "Database/Structure"
- "Database/Triggers"
- "Export"
- "Import"
- "Login"
@ -79,6 +78,7 @@ jobs:
- "Table/Operations"
- "Table/Structure"
- "Tracking"
- "Triggers"
- "Xss"
steps:
- name: Checkout code

View File

@ -1,11 +1,11 @@
import $ from 'jquery';
import { AJAX } from '../modules/ajax.ts';
import { Functions } from '../modules/functions.ts';
import { Navigation } from '../modules/navigation.ts';
import { ajaxRemoveMessage, ajaxShowMessage } from '../modules/ajax-message.ts';
import getJsConfirmCommonParam from '../modules/functions/getJsConfirmCommonParam.ts';
import { AJAX } from './modules/ajax.ts';
import { Functions } from './modules/functions.ts';
import { Navigation } from './modules/navigation.ts';
import { ajaxRemoveMessage, ajaxShowMessage } from './modules/ajax-message.ts';
import getJsConfirmCommonParam from './modules/functions/getJsConfirmCommonParam.ts';
AJAX.registerTeardown('database/triggers.js', function () {
AJAX.registerTeardown('triggers.js', function () {
$(document).off('click', 'a.ajax.add_anchor, a.ajax.edit_anchor');
$(document).off('click', 'a.ajax.export_anchor');
$(document).off('click', '#bulkActionExportButton');
@ -559,7 +559,7 @@ const DatabaseTriggers = {
}
};
AJAX.registerOnload('database/triggers.js', function () {
AJAX.registerOnload('triggers.js', function () {
/**
* Attach Ajax event handlers for the Add/Edit functionality.
*/

View File

@ -1,78 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function in_array;
use function strlen;
/**
* Triggers management.
*/
class TriggersController extends AbstractController
{
public function __construct(
ResponseRenderer $response,
Template $template,
private DatabaseInterface $dbi,
private Triggers $triggers,
) {
parent::__construct($response, $template);
}
public function __invoke(ServerRequest $request): void
{
$GLOBALS['errors'] ??= null;
$GLOBALS['urlParams'] ??= null;
$GLOBALS['errorUrl'] ??= null;
$this->addScriptFiles(['database/triggers.js']);
if (! $this->response->isAjax()) {
/**
* Displays the header and tabs
*/
if (! empty($GLOBALS['table']) && in_array($GLOBALS['table'], $this->dbi->getTables($GLOBALS['db']))) {
$this->checkParameters(['db', 'table']);
$GLOBALS['urlParams'] = ['db' => $GLOBALS['db'], 'table' => $GLOBALS['table']];
$GLOBALS['errorUrl'] = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabTable'], 'table');
$GLOBALS['errorUrl'] .= Url::getCommon($GLOBALS['urlParams'], '&');
DbTableExists::check($GLOBALS['db'], $GLOBALS['table']);
} else {
$GLOBALS['table'] = '';
$this->checkParameters(['db']);
$GLOBALS['errorUrl'] = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabDatabase'], 'database');
$GLOBALS['errorUrl'] .= Url::getCommon(['db' => $GLOBALS['db']], '&');
if (! $this->hasDatabase()) {
return;
}
}
} elseif (strlen($GLOBALS['db']) > 0) {
$this->dbi->selectDb($GLOBALS['db']);
}
/**
* Keep a list of errors that occurred while
* processing an 'Add' or 'Edit' operation.
*/
$GLOBALS['errors'] = [];
$this->triggers->main();
}
}

View File

@ -2,15 +2,15 @@
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
namespace PhpMyAdmin\Controllers\Triggers;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
@ -20,7 +20,7 @@ use function strlen;
/**
* Triggers management.
*/
class TriggersController extends AbstractController
final class IndexController extends AbstractController
{
public function __construct(
ResponseRenderer $response,
@ -37,7 +37,7 @@ class TriggersController extends AbstractController
$GLOBALS['urlParams'] ??= null;
$GLOBALS['errorUrl'] ??= null;
$this->addScriptFiles(['database/triggers.js']);
$this->addScriptFiles(['triggers.js']);
if (! $this->response->isAjax()) {
/**
@ -73,6 +73,19 @@ class TriggersController extends AbstractController
*/
$GLOBALS['errors'] = [];
$this->triggers->main();
$this->triggers->handleEditor();
$this->triggers->export();
$triggers = Triggers::getDetails($this->dbi, $GLOBALS['db'], $GLOBALS['table']);
$hasTriggerPrivilege = Util::currentUserHasPrivilege('TRIGGER', $GLOBALS['db'], $GLOBALS['table']);
$isAjax = $this->response->isAjax() && empty($_REQUEST['ajax_page_request']);
$this->render('triggers/list', [
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table'],
'triggers' => $triggers,
'has_privilege' => $hasTriggerPrivilege,
'is_ajax' => $isAjax,
]);
}
}

View File

@ -303,10 +303,10 @@ class Menu
}
if (! $isSystemSchema && Util::currentUserHasPrivilege('TRIGGER', $this->db, $this->table) && ! $tableIsView) {
$tabs['triggers']['route'] = '/table/triggers';
$tabs['triggers']['route'] = '/triggers';
$tabs['triggers']['text'] = __('Triggers');
$tabs['triggers']['icon'] = 'b_triggers';
$tabs['triggers']['active'] = $route === '/table/triggers';
$tabs['triggers']['active'] = $route === '/triggers';
}
return $tabs;
@ -396,10 +396,10 @@ class Menu
}
if (Util::currentUserHasPrivilege('TRIGGER', $this->db)) {
$tabs['triggers']['route'] = '/database/triggers';
$tabs['triggers']['route'] = '/triggers';
$tabs['triggers']['text'] = __('Triggers');
$tabs['triggers']['icon'] = 'b_triggers';
$tabs['triggers']['active'] = $route === '/database/triggers';
$tabs['triggers']['active'] = $route === '/triggers';
}
}

View File

@ -29,11 +29,11 @@ class NodeTrigger extends Node
$this->icon = ['image' => 'b_triggers', 'title' => __('Trigger')];
$this->links = [
'text' => [
'route' => '/database/triggers',
'route' => '/triggers',
'params' => ['edit_item' => 1, 'db' => null, 'item_name' => null],
],
'icon' => [
'route' => '/database/triggers',
'route' => '/triggers',
'params' => ['export_item' => 1, 'db' => null, 'item_name' => null],
],
];

View File

@ -26,8 +26,8 @@ class NodeTriggerContainer extends Node
$this->icon = ['image' => 'b_triggers', 'title' => __('Triggers')];
$this->links = [
'text' => ['route' => '/database/triggers', 'params' => ['db' => null, 'table' => null]],
'icon' => ['route' => '/database/triggers', 'params' => ['db' => null, 'table' => null]],
'text' => ['route' => '/triggers', 'params' => ['db' => null, 'table' => null]],
'icon' => ['route' => '/triggers', 'params' => ['db' => null, 'table' => null]],
];
$this->realName = 'triggers';
@ -35,8 +35,8 @@ class NodeTriggerContainer extends Node
$new = NodeFactory::getInstanceForNewNode($newLabel, 'new_trigger italics');
$new->icon = ['image' => 'b_trigger_add', 'title' => $newLabel];
$new->links = [
'text' => ['route' => '/database/triggers', 'params' => ['add_item' => 1, 'db' => null]],
'icon' => ['route' => '/database/triggers', 'params' => ['add_item' => 1, 'db' => null]],
'text' => ['route' => '/triggers', 'params' => ['add_item' => 1, 'db' => null]],
'icon' => ['route' => '/triggers', 'params' => ['add_item' => 1, 'db' => null]],
];
$this->addChild($new);
}

View File

@ -7,11 +7,11 @@ namespace PhpMyAdmin;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Database\Events;
use PhpMyAdmin\Database\Routines;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\Dbal\DatabaseName;
use PhpMyAdmin\Engines\Innodb;
use PhpMyAdmin\Partitioning\Partition;
use PhpMyAdmin\Plugins\Export\ExportSql;
use PhpMyAdmin\Triggers\Triggers;
use function __;
use function array_merge;

View File

@ -7,7 +7,6 @@ declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Dbal\ResultInterface;
@ -18,6 +17,7 @@ use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util;
use function __;

View File

@ -7,7 +7,6 @@ declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\FieldMetadata;
@ -19,6 +18,7 @@ use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util;
use function __;

View File

@ -10,7 +10,6 @@ namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\Database\Events;
use PhpMyAdmin\Database\Routines;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\FieldMetadata;
@ -30,6 +29,7 @@ use PhpMyAdmin\SqlParser\Context;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
use PhpMyAdmin\SqlParser\Token;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util;
use PhpMyAdmin\Version;

View File

@ -7,7 +7,6 @@ declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Dbal\ResultInterface;
@ -18,6 +17,7 @@ use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util;
use function __;

View File

@ -6,7 +6,6 @@ namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\Database\Events;
use PhpMyAdmin\Database\Routines;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Plugins\ExportPlugin;
@ -15,6 +14,7 @@ use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util;
use PhpMyAdmin\Version;

View File

@ -8,13 +8,13 @@ declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export\Helpers;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Dbal\ResultInterface;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\Pdf as PdfLib;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util;
use TCPDF_STATIC;

View File

@ -215,11 +215,10 @@ class Generator
. ', EVENT_OBJECT_TABLE, ACTION_TIMING, ACTION_STATEMENT'
. ', EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER'
. ' FROM information_schema.TRIGGERS'
. ' WHERE EVENT_OBJECT_SCHEMA ' . Util::getCollateForIS() . '='
. ' \'' . $escapedDb . '\'';
. ' WHERE EVENT_OBJECT_SCHEMA ' . Util::getCollateForIS() . '= ' . $escapedDb;
if ($escapedTable !== null) {
$query .= ' AND EVENT_OBJECT_TABLE ' . Util::getCollateForIS() . " = '" . $escapedTable . "';";
$query .= ' AND EVENT_OBJECT_TABLE ' . Util::getCollateForIS() . ' = ' . $escapedTable . ';';
}
return $query;

View File

@ -8,7 +8,6 @@ use PhpMyAdmin\ConfigStorage\Features\DisplayFeature;
use PhpMyAdmin\ConfigStorage\Features\RelationFeature;
use PhpMyAdmin\ConfigStorage\Features\UiPreferencesFeature;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Html\MySQLDocumentation;
@ -23,6 +22,7 @@ use PhpMyAdmin\SqlParser\Statements\AlterStatement;
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
use PhpMyAdmin\SqlParser\Statements\DropStatement;
use PhpMyAdmin\SqlParser\Utils\Table as TableUtils;
use PhpMyAdmin\Triggers\Triggers;
use Stringable;
use function __;

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Triggers;
use Webmozart\Assert\Assert;
use Webmozart\Assert\InvalidArgumentException;
final class Trigger
{
public function __construct(
public readonly string $name,
public readonly string $timing,
public readonly string $event,
public readonly string $table,
public readonly string $statement,
public readonly string $definer,
) {
}
/** @param mixed[] $trigger */
public static function tryFromArray(array $trigger): self|null
{
try {
$name = $trigger['Trigger'] ?? $trigger['TRIGGER_NAME'] ?? null;
$timing = $trigger['Timing'] ?? $trigger['ACTION_TIMING'] ?? null;
$event = $trigger['Event'] ?? $trigger['EVENT_MANIPULATION'] ?? null;
$table = $trigger['Table'] ?? $trigger['EVENT_OBJECT_TABLE'] ?? null;
$statement = $trigger['Statement'] ?? $trigger['ACTION_STATEMENT'] ?? null;
$definer = $trigger['Definer'] ?? $trigger['DEFINER'] ?? null;
Assert::string($name);
Assert::string($timing);
Assert::string($event);
Assert::string($table);
Assert::string($statement);
Assert::string($definer);
return new self($name, $timing, $event, $table, $statement, $definer);
} catch (InvalidArgumentException) {
return null;
}
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace PhpMyAdmin\Database;
namespace PhpMyAdmin\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
@ -11,6 +11,7 @@ use PhpMyAdmin\Query\Generator as QueryGenerator;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use Webmozart\Assert\Assert;
use function __;
use function array_column;
@ -44,42 +45,6 @@ class Triggers
) {
}
/**
* Main function for the triggers functionality
*/
public function main(): void
{
/**
* Process all requests
*/
$this->handleEditor();
$this->export();
$items = self::getDetails($this->dbi, $GLOBALS['db'], $GLOBALS['table']);
$hasTriggerPrivilege = Util::currentUserHasPrivilege('TRIGGER', $GLOBALS['db'], $GLOBALS['table']);
$isAjax = $this->response->isAjax() && empty($_REQUEST['ajax_page_request']);
$rows = '';
foreach ($items as $item) {
$rows .= $this->template->render('database/triggers/row', [
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table'],
'trigger' => $item,
'has_drop_privilege' => $hasTriggerPrivilege,
'has_edit_privilege' => $hasTriggerPrivilege,
'row_class' => $isAjax ? 'ajaxInsert hide' : '',
]);
}
echo $this->template->render('database/triggers/list', [
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table'],
'items' => $items,
'rows' => $rows,
'has_privilege' => $hasTriggerPrivilege,
]);
}
/**
* Handles editor requests for adding or editing an item
*/
@ -197,7 +162,7 @@ class Triggers
);
$this->response->addJSON(
'new_row',
$this->template->render('database/triggers/row', [
$this->template->render('triggers/row', [
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table'],
'trigger' => $trigger,
@ -335,12 +300,9 @@ class Triggers
*/
public function getEditorForm(string $db, string $table, string $mode, array $item): string
{
$query = 'SELECT `TABLE_NAME` FROM `INFORMATION_SCHEMA`.`TABLES` ';
$query .= 'WHERE `TABLE_SCHEMA`=\'' . $this->dbi->escapeString($db) . '\' ';
$query .= 'AND `TABLE_TYPE` IN (\'BASE TABLE\', \'SYSTEM VERSIONED\')';
$tables = $this->dbi->fetchResult($query);
$tables = $this->getTables($db);
return $this->template->render('database/triggers/editor_form', [
return $this->template->render('triggers/editor_form', [
'db' => $db,
'table' => $table,
'is_edit' => $mode === 'edit',
@ -469,7 +431,7 @@ class Triggers
echo $message->getDisplay();
}
private function export(): void
public function export(): void
{
if (empty($_GET['export_item']) || empty($_GET['item_name'])) {
return;
@ -496,7 +458,7 @@ class Triggers
exit;
}
$this->response->addHTML($this->template->render('database/triggers/export', [
$this->response->addHTML($this->template->render('triggers/export', [
'data' => $exportData,
'item_name' => $itemName,
]));
@ -539,48 +501,46 @@ class Triggers
$result = [];
if (! $GLOBALS['cfg']['Server']['DisableIS']) {
$query = QueryGenerator::getInformationSchemaTriggersRequest(
$dbi->escapeString($db),
$table === '' ? null : $dbi->escapeString($table),
$dbi->quoteString($db),
$table === '' ? null : $dbi->quoteString($table),
);
} else {
$query = 'SHOW TRIGGERS FROM ' . Util::backquote($db);
if ($table !== '') {
$query .= " LIKE '" . $dbi->escapeString($table) . "';";
$query .= ' LIKE ' . $dbi->quoteString($table) . ';';
}
}
/** @var mixed[][] $triggers */
$triggers = $dbi->fetchResult($query);
foreach ($triggers as $trigger) {
if ($GLOBALS['cfg']['Server']['DisableIS']) {
$trigger['TRIGGER_NAME'] = $trigger['Trigger'];
$trigger['ACTION_TIMING'] = $trigger['Timing'];
$trigger['EVENT_MANIPULATION'] = $trigger['Event'];
$trigger['EVENT_OBJECT_TABLE'] = $trigger['Table'];
$trigger['ACTION_STATEMENT'] = $trigger['Statement'];
$trigger['DEFINER'] = $trigger['Definer'];
$newTrigger = Trigger::tryFromArray($trigger);
if ($newTrigger === null) {
continue;
}
$oneResult = [];
$oneResult['name'] = $trigger['TRIGGER_NAME'];
$oneResult['table'] = $trigger['EVENT_OBJECT_TABLE'];
$oneResult['action_timing'] = $trigger['ACTION_TIMING'];
$oneResult['event_manipulation'] = $trigger['EVENT_MANIPULATION'];
$oneResult['definition'] = $trigger['ACTION_STATEMENT'];
$oneResult['definer'] = $trigger['DEFINER'];
$oneResult['name'] = $newTrigger->name;
$oneResult['table'] = $newTrigger->table;
$oneResult['action_timing'] = $newTrigger->timing;
$oneResult['event_manipulation'] = $newTrigger->event;
$oneResult['definition'] = $newTrigger->statement;
$oneResult['definer'] = $newTrigger->definer;
// do not prepend the schema name; this way, importing the
// definition into another schema will work
$oneResult['full_trigger_name'] = Util::backquote($trigger['TRIGGER_NAME']);
$oneResult['drop'] = 'DROP TRIGGER IF EXISTS '
. $oneResult['full_trigger_name'];
$oneResult['create'] = 'CREATE TRIGGER '
. $oneResult['full_trigger_name'] . ' '
. $trigger['ACTION_TIMING'] . ' '
. $trigger['EVENT_MANIPULATION']
. ' ON ' . Util::backquote($trigger['EVENT_OBJECT_TABLE'])
. "\n" . ' FOR EACH ROW '
. $trigger['ACTION_STATEMENT'] . "\n" . $delimiter . "\n";
$oneResult['full_trigger_name'] = Util::backquote($newTrigger->name);
$oneResult['drop'] = 'DROP TRIGGER IF EXISTS ' . $oneResult['full_trigger_name'];
$oneResult['create'] = sprintf(
"CREATE TRIGGER %s %s %s ON %s\n FOR EACH ROW %s\n%s\n",
$oneResult['full_trigger_name'],
$newTrigger->timing,
$newTrigger->event,
Util::backquote($newTrigger->table),
$newTrigger->statement,
$delimiter,
);
$result[] = $oneResult;
}
@ -591,4 +551,19 @@ class Triggers
return $result;
}
/** @return list<non-empty-string> */
private function getTables(string $db): array
{
$query = sprintf(
'SELECT `TABLE_NAME` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=%s'
. " AND `TABLE_TYPE` IN ('BASE TABLE', 'SYSTEM VERSIONED')",
$this->dbi->quoteString($db),
);
$tables = $this->dbi->fetchResult($query);
Assert::allStringNotEmpty($tables);
Assert::isList($tables);
return $tables;
}
}

View File

@ -35,6 +35,7 @@ use PhpMyAdmin\Controllers\TableController;
use PhpMyAdmin\Controllers\ThemesController;
use PhpMyAdmin\Controllers\ThemeSetController;
use PhpMyAdmin\Controllers\Transformation;
use PhpMyAdmin\Controllers\Triggers;
use PhpMyAdmin\Controllers\UserPasswordController;
use PhpMyAdmin\Controllers\VersionCheckController;
use PhpMyAdmin\Controllers\View;
@ -108,7 +109,6 @@ return static function (RouteCollector $routes): void {
$routes->post('/show-create', Database\Structure\ShowCreateController::class);
});
$routes->addRoute(['GET', 'POST'], '/tracking', Database\TrackingController::class);
$routes->addRoute(['GET', 'POST'], '/triggers', Database\TriggersController::class);
});
$routes->post('/databases', DatabaseController::class);
$routes->addRoute(['GET', 'POST'], '/error-report', ErrorReportController::class);
@ -290,7 +290,6 @@ return static function (RouteCollector $routes): void {
$routes->post('/unique', Table\Structure\UniqueController::class);
});
$routes->addRoute(['GET', 'POST'], '/tracking', Table\TrackingController::class);
$routes->addRoute(['GET', 'POST'], '/triggers', Table\TriggersController::class);
$routes->addRoute(['GET', 'POST'], '/zoom-search', Table\ZoomSearchController::class);
});
$routes->post('/tables', TableController::class);
@ -302,6 +301,7 @@ return static function (RouteCollector $routes): void {
$routes->addRoute(['GET', 'POST'], '/overview', Transformation\OverviewController::class);
$routes->addRoute(['GET', 'POST'], '/wrapper', Transformation\WrapperController::class);
});
$routes->addRoute(['GET', 'POST'], '/triggers', Triggers\IndexController::class);
$routes->addRoute(['GET', 'POST'], '/user-password', UserPasswordController::class);
$routes->addRoute(['GET', 'POST'], '/version-check', VersionCheckController::class);
$routes->addGroup('/view', static function (RouteCollector $routes): void {

View File

@ -14,7 +14,6 @@ use PhpMyAdmin\Database\Designer;
use PhpMyAdmin\Database\Designer\Common;
use PhpMyAdmin\Database\Events;
use PhpMyAdmin\Database\Routines;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ErrorHandler;
use PhpMyAdmin\ErrorReport;
@ -50,6 +49,7 @@ use PhpMyAdmin\Theme\ThemeManager;
use PhpMyAdmin\Tracking\Tracking;
use PhpMyAdmin\Tracking\TrackingChecker;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\UserPassword;
use PhpMyAdmin\UserPreferences;
use PhpMyAdmin\Utils\HttpRequest;

View File

@ -34,6 +34,7 @@ use PhpMyAdmin\Controllers\TableController;
use PhpMyAdmin\Controllers\ThemesController;
use PhpMyAdmin\Controllers\ThemeSetController;
use PhpMyAdmin\Controllers\Transformation;
use PhpMyAdmin\Controllers\Triggers;
use PhpMyAdmin\Controllers\UserPasswordController;
use PhpMyAdmin\Controllers\VersionCheckController;
use PhpMyAdmin\Controllers\View;
@ -349,15 +350,6 @@ return [
'$dbi' => '@dbi',
],
],
Database\TriggersController::class => [
'class' => Database\TriggersController::class,
'arguments' => [
'$response' => '@response',
'$template' => '@template',
'$dbi' => '@dbi',
'$triggers' => '@triggers',
],
],
DatabaseController::class => [
'class' => DatabaseController::class,
'arguments' => ['$response' => '@response', '$template' => '@template'],
@ -1417,8 +1409,8 @@ return [
'$trackingChecker' => '@tracking_checker',
],
],
Table\TriggersController::class => [
'class' => Table\TriggersController::class,
Triggers\IndexController::class => [
'class' => Triggers\IndexController::class,
'arguments' => [
'$response' => '@response',
'$template' => '@template',

View File

@ -3135,136 +3135,6 @@ parameters:
count: 1
path: libraries/classes/Database/Search.php
-
message: "#^Cannot access offset 'ACTION_STATEMENT' on mixed\\.$#"
count: 3
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'ACTION_TIMING' on mixed\\.$#"
count: 3
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'DEFINER' on mixed\\.$#"
count: 2
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'Definer' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'EVENT_MANIPULATION' on mixed\\.$#"
count: 3
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'EVENT_OBJECT_TABLE' on mixed\\.$#"
count: 3
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'Event' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'Statement' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'TRIGGER_NAME' on mixed\\.$#"
count: 3
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'Table' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'Timing' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'Trigger' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'action_timing' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'create' on mixed\\.$#"
count: 2
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'definer' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'definition' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'drop' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'event_manipulation' on mixed\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'name' on mixed\\.$#"
count: 4
path: libraries/classes/Database/Triggers.php
-
message: "#^Cannot access offset 'table' on mixed\\.$#"
count: 2
path: libraries/classes/Database/Triggers.php
-
message: "#^Offset 'create' does not exist on array\\|null\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Offset 'drop' does not exist on array\\|null\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Parameter \\#1 \\$createStatement of method PhpMyAdmin\\\\Database\\\\Triggers\\:\\:checkResult\\(\\) expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Parameter \\#1 \\$identifier of static method PhpMyAdmin\\\\Util\\:\\:backquote\\(\\) expects string\\|Stringable\\|null, mixed given\\.$#"
count: 4
path: libraries/classes/Database/Triggers.php
-
message: "#^Parameter \\#1 \\$query of method PhpMyAdmin\\\\DatabaseInterface\\:\\:tryQuery\\(\\) expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Parameter \\#1 \\$string of function trim expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Database/Triggers.php
-
message: "#^Binary operation \"\\+\" between string\\|null and string\\|null results in an error\\.$#"
count: 1
@ -5741,7 +5611,7 @@ parameters:
path: libraries/classes/Plugins/Export/ExportXml.php
-
message: "#^Parameter \\#3 \\$table of static method PhpMyAdmin\\\\Database\\\\Triggers\\:\\:getDetails\\(\\) expects string, mixed given\\.$#"
message: "#^Parameter \\#3 \\$table of static method PhpMyAdmin\\\\Triggers\\\\Triggers\\:\\:getDetails\\(\\) expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Plugins/Export/ExportXml.php
@ -8500,6 +8370,76 @@ parameters:
count: 1
path: libraries/classes/Transformations.php
-
message: "#^Cannot access offset 'action_timing' on mixed\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'create' on mixed\\.$#"
count: 2
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'definer' on mixed\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'definition' on mixed\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'drop' on mixed\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'event_manipulation' on mixed\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'name' on mixed\\.$#"
count: 4
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Cannot access offset 'table' on mixed\\.$#"
count: 2
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Offset 'create' does not exist on array\\|null\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Offset 'drop' does not exist on array\\|null\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Parameter \\#1 \\$createStatement of method PhpMyAdmin\\\\Triggers\\\\Triggers\\:\\:checkResult\\(\\) expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Parameter \\#1 \\$identifier of static method PhpMyAdmin\\\\Util\\:\\:backquote\\(\\) expects string\\|Stringable\\|null, mixed given\\.$#"
count: 2
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Parameter \\#1 \\$query of method PhpMyAdmin\\\\DatabaseInterface\\:\\:tryQuery\\(\\) expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Parameter \\#1 \\$string of function trim expects string, mixed given\\.$#"
count: 1
path: libraries/classes/Triggers/Triggers.php
-
message: "#^Call to an undefined method object\\:\\:configure\\(\\)\\.$#"
count: 1

View File

@ -1681,22 +1681,6 @@
<code>__construct</code>
</PossiblyUnusedMethod>
</file>
<file src="libraries/classes/Controllers/Database/TriggersController.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</InvalidArrayOffset>
<MixedAssignment>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</MixedAssignment>
<PossiblyUnusedMethod>
<code>__construct</code>
</PossiblyUnusedMethod>
<PossiblyUnusedParam>
<code>$request</code>
</PossiblyUnusedParam>
</file>
<file src="libraries/classes/Controllers/DatabaseController.php">
<UnusedParam>
<code>$request</code>
@ -4216,19 +4200,6 @@
<code><![CDATA[$GLOBALS['text_dir']]]></code>
</PossiblyNullArgument>
</file>
<file src="libraries/classes/Controllers/Table/TriggersController.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</InvalidArrayOffset>
<MixedAssignment>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</MixedAssignment>
<PossiblyUnusedParam>
<code>$request</code>
</PossiblyUnusedParam>
</file>
<file src="libraries/classes/Controllers/Table/ZoomSearchController.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
@ -4332,6 +4303,19 @@
<code>__construct</code>
</PossiblyUnusedMethod>
</file>
<file src="libraries/classes/Controllers/Triggers/IndexController.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</InvalidArrayOffset>
<MixedAssignment>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</MixedAssignment>
<UnusedParam>
<code>$request</code>
</UnusedParam>
</file>
<file src="libraries/classes/Controllers/UserPasswordController.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['cfg']['ShowChgPassword']]]></code>
@ -5505,159 +5489,6 @@
<code><![CDATA[$this->searchTypeDescription]]></code>
</MixedAssignment>
</file>
<file src="libraries/classes/Database/Triggers.php">
<DeprecatedMethod>
<code>escapeString</code>
<code>escapeString</code>
<code>escapeString</code>
<code>escapeString</code>
</DeprecatedMethod>
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</InvalidArrayOffset>
<MixedArgument>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code>$createItem</code>
<code>$exportData</code>
<code><![CDATA[$trigger['EVENT_OBJECT_TABLE']]]></code>
<code><![CDATA[$trigger['TRIGGER_NAME']]]></code>
</MixedArgument>
<MixedArrayAccess>
<code><![CDATA[$temp['action_timing']]]></code>
<code><![CDATA[$temp['create']]]></code>
<code><![CDATA[$temp['definer']]]></code>
<code><![CDATA[$temp['definition']]]></code>
<code><![CDATA[$temp['drop']]]></code>
<code><![CDATA[$temp['event_manipulation']]]></code>
<code><![CDATA[$temp['name']]]></code>
<code><![CDATA[$temp['table']]]></code>
<code><![CDATA[$trigger['ACTION_STATEMENT']]]></code>
<code><![CDATA[$trigger['ACTION_STATEMENT']]]></code>
<code><![CDATA[$trigger['ACTION_TIMING']]]></code>
<code><![CDATA[$trigger['ACTION_TIMING']]]></code>
<code><![CDATA[$trigger['DEFINER']]]></code>
<code><![CDATA[$trigger['Definer']]]></code>
<code><![CDATA[$trigger['EVENT_MANIPULATION']]]></code>
<code><![CDATA[$trigger['EVENT_MANIPULATION']]]></code>
<code><![CDATA[$trigger['EVENT_OBJECT_TABLE']]]></code>
<code><![CDATA[$trigger['EVENT_OBJECT_TABLE']]]></code>
<code><![CDATA[$trigger['Event']]]></code>
<code><![CDATA[$trigger['Statement']]]></code>
<code><![CDATA[$trigger['TRIGGER_NAME']]]></code>
<code><![CDATA[$trigger['TRIGGER_NAME']]]></code>
<code><![CDATA[$trigger['Table']]]></code>
<code><![CDATA[$trigger['Timing']]]></code>
<code><![CDATA[$trigger['Trigger']]]></code>
<code><![CDATA[$trigger['create']]]></code>
<code><![CDATA[$trigger['name']]]></code>
<code><![CDATA[$trigger['table']]]></code>
<code><![CDATA[$value['name']]]></code>
<code><![CDATA[$value['name']]]></code>
</MixedArrayAccess>
<MixedArrayAssignment>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$trigger['ACTION_STATEMENT']]]></code>
<code><![CDATA[$trigger['ACTION_TIMING']]]></code>
<code><![CDATA[$trigger['DEFINER']]]></code>
<code><![CDATA[$trigger['EVENT_MANIPULATION']]]></code>
<code><![CDATA[$trigger['EVENT_OBJECT_TABLE']]]></code>
<code><![CDATA[$trigger['TRIGGER_NAME']]]></code>
</MixedArrayAssignment>
<MixedAssignment>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code>$createItem</code>
<code>$exportData</code>
<code>$item</code>
<code><![CDATA[$item['item_original_name']]]></code>
<code><![CDATA[$oneResult['action_timing']]]></code>
<code><![CDATA[$oneResult['definer']]]></code>
<code><![CDATA[$oneResult['definition']]]></code>
<code><![CDATA[$oneResult['event_manipulation']]]></code>
<code><![CDATA[$oneResult['name']]]></code>
<code><![CDATA[$oneResult['table']]]></code>
<code><![CDATA[$retval['create']]]></code>
<code><![CDATA[$retval['drop']]]></code>
<code><![CDATA[$retval['item_action_timing']]]></code>
<code><![CDATA[$retval['item_definer']]]></code>
<code><![CDATA[$retval['item_definition']]]></code>
<code><![CDATA[$retval['item_event_manipulation']]]></code>
<code><![CDATA[$retval['item_name']]]></code>
<code><![CDATA[$retval['item_table']]]></code>
<code>$string</code>
<code>$temp</code>
<code>$trigger</code>
<code>$trigger</code>
<code>$trigger</code>
<code><![CDATA[$trigger['ACTION_STATEMENT']]]></code>
<code><![CDATA[$trigger['ACTION_TIMING']]]></code>
<code><![CDATA[$trigger['DEFINER']]]></code>
<code><![CDATA[$trigger['EVENT_MANIPULATION']]]></code>
<code><![CDATA[$trigger['EVENT_OBJECT_TABLE']]]></code>
<code><![CDATA[$trigger['TRIGGER_NAME']]]></code>
<code>$value</code>
<code>$value</code>
</MixedAssignment>
<MixedOperand>
<code>$string</code>
<code><![CDATA[$trigger['ACTION_STATEMENT']]]></code>
<code><![CDATA[$trigger['ACTION_TIMING']]]></code>
<code><![CDATA[$trigger['EVENT_MANIPULATION']]]></code>
<code><![CDATA[$trigger['drop']]]></code>
</MixedOperand>
<PossiblyInvalidArgument>
<code><![CDATA[$_POST['item_definer']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_original_name']]]></code>
<code><![CDATA[$_POST['item_table']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
<code>$itemName</code>
<code>$itemName</code>
</PossiblyInvalidArgument>
<PossiblyInvalidCast>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_original_name']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
</PossiblyInvalidCast>
<PossiblyInvalidOperand>
<code><![CDATA[$_POST['item_definition']]]></code>
<code><![CDATA[$_POST['item_event']]]></code>
<code><![CDATA[$_POST['item_timing']]]></code>
</PossiblyInvalidOperand>
<PossiblyNullArgument>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['message']]]></code>
<code>$createItem</code>
</PossiblyNullArgument>
<PossiblyNullArrayAccess>
<code><![CDATA[$trigger['create']]]></code>
<code><![CDATA[$trigger['drop']]]></code>
</PossiblyNullArrayAccess>
<PossiblyNullIterator>
<code><![CDATA[$GLOBALS['errors']]]></code>
</PossiblyNullIterator>
<PossiblyNullReference>
<code>isSuccess</code>
</PossiblyNullReference>
</file>
<file src="libraries/classes/DatabaseInterface.php">
<DeprecatedMethod>
<code>escapeString</code>
@ -13037,6 +12868,111 @@
<code>bool</code>
</PossiblyUnusedReturnValue>
</file>
<file src="libraries/classes/Triggers/Triggers.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
</InvalidArrayOffset>
<MixedArgument>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code>$createItem</code>
<code>$exportData</code>
</MixedArgument>
<MixedArrayAccess>
<code><![CDATA[$temp['action_timing']]]></code>
<code><![CDATA[$temp['create']]]></code>
<code><![CDATA[$temp['definer']]]></code>
<code><![CDATA[$temp['definition']]]></code>
<code><![CDATA[$temp['drop']]]></code>
<code><![CDATA[$temp['event_manipulation']]]></code>
<code><![CDATA[$temp['name']]]></code>
<code><![CDATA[$temp['table']]]></code>
<code><![CDATA[$trigger['create']]]></code>
<code><![CDATA[$trigger['name']]]></code>
<code><![CDATA[$trigger['table']]]></code>
<code><![CDATA[$value['name']]]></code>
<code><![CDATA[$value['name']]]></code>
</MixedArrayAccess>
<MixedArrayAssignment>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
<code><![CDATA[$GLOBALS['errors'][]]]></code>
</MixedArrayAssignment>
<MixedAssignment>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code>$createItem</code>
<code>$exportData</code>
<code><![CDATA[$item['item_original_name']]]></code>
<code><![CDATA[$retval['create']]]></code>
<code><![CDATA[$retval['drop']]]></code>
<code><![CDATA[$retval['item_action_timing']]]></code>
<code><![CDATA[$retval['item_definer']]]></code>
<code><![CDATA[$retval['item_definition']]]></code>
<code><![CDATA[$retval['item_event_manipulation']]]></code>
<code><![CDATA[$retval['item_name']]]></code>
<code><![CDATA[$retval['item_table']]]></code>
<code>$string</code>
<code>$temp</code>
<code>$trigger</code>
<code>$trigger</code>
<code>$value</code>
<code>$value</code>
</MixedAssignment>
<MixedOperand>
<code>$string</code>
<code><![CDATA[$trigger['drop']]]></code>
</MixedOperand>
<PossiblyInvalidArgument>
<code><![CDATA[$_POST['item_definer']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_original_name']]]></code>
<code><![CDATA[$_POST['item_table']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
<code>$itemName</code>
<code>$itemName</code>
</PossiblyInvalidArgument>
<PossiblyInvalidCast>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_original_name']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
</PossiblyInvalidCast>
<PossiblyInvalidOperand>
<code><![CDATA[$_POST['item_definition']]]></code>
<code><![CDATA[$_POST['item_event']]]></code>
<code><![CDATA[$_POST['item_timing']]]></code>
</PossiblyInvalidOperand>
<PossiblyNullArgument>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['errors']]]></code>
<code><![CDATA[$GLOBALS['message']]]></code>
<code>$createItem</code>
</PossiblyNullArgument>
<PossiblyNullArrayAccess>
<code><![CDATA[$trigger['create']]]></code>
<code><![CDATA[$trigger['drop']]]></code>
</PossiblyNullArrayAccess>
<PossiblyNullIterator>
<code><![CDATA[$GLOBALS['errors']]]></code>
</PossiblyNullIterator>
<PossiblyNullReference>
<code>isSuccess</code>
</PossiblyNullReference>
</file>
<file src="libraries/classes/Twig/AssetExtension.php">
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['theme']]]></code>

View File

@ -1,4 +1,4 @@
<form class="rte_form" action="{{ url('/database/triggers') }}" method="post">
<form class="rte_form" action="{{ url('/triggers') }}" method="post">
{{ get_hidden_inputs(db, table) }}
<input name="{{ is_edit ? 'edit_item' : 'add_item' }}" type="hidden" value="1">
{% if is_edit %}

View File

@ -23,20 +23,20 @@
</div>
<div class="ms-auto">
<a class="ajax add_anchor btn btn-primary{{ not has_privilege ? ' disabled' }}" href="{{ url('/database/triggers', {'db': db, 'table': table, 'add_item': true}) }}" role="button"{{ not has_privilege ? ' tabindex="-1" aria-disabled="true"' }}>
<a class="ajax add_anchor btn btn-primary{{ not has_privilege ? ' disabled' }}" href="{{ url('/triggers', {'db': db, 'table': table, 'add_item': true}) }}" role="button"{{ not has_privilege ? ' tabindex="-1" aria-disabled="true"' }}>
{{ get_icon('b_trigger_add', 'Create new trigger'|trans) }}
</a>
</div>
</div>
<form id="rteListForm" class="ajax" action="{{ url(table is not empty ? '/table/triggers' : '/database/triggers') }}">
<form id="rteListForm" class="ajax" action="{{ url('/triggers') }}">
{{ get_hidden_inputs(db, table) }}
<div id="nothing2display"{{ items is not empty ? ' class="hide"' }}>
<div id="nothing2display"{{ triggers is not empty ? ' class="hide"' }}>
{% trans 'There are no triggers to display.' %}
</div>
<table id="triggersTable" class="table table-striped table-hover{{ items is empty ? ' hide' }} w-auto data">
<table id="triggersTable" class="table table-striped table-hover{{ triggers is empty ? ' hide' }} w-auto data">
<thead>
<tr>
<th></th>
@ -52,7 +52,20 @@
<tbody>
<tr class="hide">{% for i in 0..(table is empty ? 7 : 6) %}<td></td>{% endfor %}</tr>
{{ rows|raw }}
{% for trigger in triggers %}
{{ include(
'triggers/row.twig',
{
'db': db,
'table': table,
'trigger': trigger,
'has_drop_privilege': has_privilege,
'has_edit_privilege': has_privilege,
'row_class': is_ajax ? 'ajaxInsert hide' : '',
},
with_context = false
) }}
{% endfor %}
</tbody>
</table>
</form>

View File

@ -8,7 +8,7 @@
</td>
{% if table is empty %}
<td>
<a href="{{ url('/table/triggers', {'db': db, 'table': trigger.table}) }}">{{ trigger.table }}</a>
<a href="{{ url('/triggers', {'db': db, 'table': trigger.table}) }}">{{ trigger.table }}</a>
</td>
{% endif %}
<td>
@ -19,7 +19,7 @@
</td>
<td>
{% if has_edit_privilege %}
<a class="ajax edit_anchor" href="{{ url('/database/triggers', {
<a class="ajax edit_anchor" href="{{ url('/triggers', {
'db': db,
'table': table,
'edit_item': true,
@ -32,7 +32,7 @@
{% endif %}
</td>
<td>
<a class="ajax export_anchor" href="{{ url('/database/triggers', {
<a class="ajax export_anchor" href="{{ url('/triggers', {
'db': db,
'table': table,
'export_item': true,
@ -49,7 +49,7 @@
'db': db,
'table': table,
'sql_query': trigger.drop,
'goto': url('/database/triggers', {'db': db})
'goto': url('/triggers', {'db': db})
},
get_icon('b_drop', 'Drop'|trans),
{'class': 'ajax drop_anchor'}

View File

@ -2,19 +2,19 @@
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers\Table;
namespace PhpMyAdmin\Tests\Controllers\Triggers;
use PhpMyAdmin\Controllers\Table\TriggersController;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\Controllers\Triggers\IndexController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\DbiDummy;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PhpMyAdmin\Triggers\Triggers;
/** @covers \PhpMyAdmin\Controllers\Table\TriggersController */
class TriggersControllerTest extends AbstractTestCase
/** @covers \PhpMyAdmin\Controllers\Triggers\IndexController */
class IndexControllerTest extends AbstractTestCase
{
protected DatabaseInterface $dbi;
@ -49,14 +49,14 @@ class TriggersControllerTest extends AbstractTestCase
$template = new Template();
$response = new ResponseRenderer();
(new TriggersController(
(new IndexController(
$response,
$template,
$this->dbi,
new Triggers($this->dbi, $template, $response),
))($this->createStub(ServerRequest::class));
$items = [
$triggers = [
[
'name' => 'test_trigger',
'table' => 'test_table',
@ -70,21 +70,14 @@ class TriggersControllerTest extends AbstractTestCase
. ' FOR EACH ROW BEGIN END' . "\n" . '//' . "\n",
],
];
$rows = $template->render('database/triggers/row', [
$expected = $template->render('triggers/list', [
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table'],
'trigger' => $items[0],
'has_drop_privilege' => true,
'has_edit_privilege' => true,
'row_class' => '',
'triggers' => $triggers,
'has_privilege' => true,
'is_ajax' => false,
]);
$this->expectOutputString($template->render('database/triggers/list', [
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table'],
'items' => $items,
'rows' => $rows,
'has_privilege' => true,
]));
$this->assertSame($expected, $response->getHTMLResult());
}
}

View File

@ -29,8 +29,8 @@ class NodeTriggerContainerTest extends AbstractTestCase
$parent = new NodeTriggerContainer();
$this->assertEquals(
[
'text' => ['route' => '/database/triggers', 'params' => ['db' => null, 'table' => null]],
'icon' => ['route' => '/database/triggers', 'params' => ['db' => null, 'table' => null]],
'text' => ['route' => '/triggers', 'params' => ['db' => null, 'table' => null]],
'icon' => ['route' => '/triggers', 'params' => ['db' => null, 'table' => null]],
],
$parent->links,
);

View File

@ -30,11 +30,11 @@ class NodeTriggerTest extends AbstractTestCase
$this->assertEquals(
[
'text' => [
'route' => '/database/triggers',
'route' => '/triggers',
'params' => ['edit_item' => 1, 'db' => null, 'item_name' => null],
],
'icon' => [
'route' => '/database/triggers',
'route' => '/triggers',
'params' => ['export_item' => 1, 'db' => null, 'item_name' => null],
],
],

View File

@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Triggers;
use PhpMyAdmin\Triggers\Trigger;
use PHPUnit\Framework\TestCase;
/** @covers \PhpMyAdmin\Triggers\Trigger */
class TriggerTest extends TestCase
{
public function testTryFromArrayWithEmptyArray(): void
{
$this->assertNull(Trigger::tryFromArray([]));
}
/**
* @param mixed[] $trigger
*
* @dataProvider arrayWithValidValuesProvider
*/
public function testTryFromArrayWithValidValues(array $trigger): void
{
$actual = Trigger::tryFromArray($trigger);
$this->assertNotNull($actual);
$this->assertSame('trigger_name', $actual->name);
$this->assertSame('BEFORE', $actual->timing);
$this->assertSame('UPDATE', $actual->event);
$this->assertSame('test_table', $actual->table);
$this->assertSame('BEGIN END', $actual->statement);
$this->assertSame('definer@localhost', $actual->definer);
}
/** @return iterable<array-key, array{mixed[]}> */
public static function arrayWithValidValuesProvider(): iterable
{
yield [
[
'Trigger' => 'trigger_name',
'Timing' => 'BEFORE',
'Event' => 'UPDATE',
'Table' => 'test_table',
'Statement' => 'BEGIN END',
'Definer' => 'definer@localhost',
],
];
yield [
[
'TRIGGER_NAME' => 'trigger_name',
'ACTION_TIMING' => 'BEFORE',
'EVENT_MANIPULATION' => 'UPDATE',
'EVENT_OBJECT_TABLE' => 'test_table',
'ACTION_STATEMENT' => 'BEGIN END',
'DEFINER' => 'definer@localhost',
],
];
}
/**
* @param mixed[] $trigger
*
* @dataProvider arrayWithInvalidValuesProvider
*/
public function testTryFromArrayWithInvalidValues(array $trigger): void
{
$this->assertNull(Trigger::tryFromArray($trigger));
}
/** @return iterable<array-key, array{mixed[]}> */
public static function arrayWithInvalidValuesProvider(): iterable
{
yield [
[
'Trigger' => null,
'Timing' => null,
'Event' => null,
'Table' => null,
'Statement' => null,
'Definer' => null,
],
];
yield [
[
'TRIGGER_NAME' => null,
'ACTION_TIMING' => null,
'EVENT_MANIPULATION' => null,
'EVENT_OBJECT_TABLE' => null,
'ACTION_STATEMENT' => null,
'DEFINER' => null,
],
];
}
}

View File

@ -2,14 +2,17 @@
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Database;
namespace PhpMyAdmin\Tests\Triggers;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Triggers\Triggers;
/** @covers \PhpMyAdmin\Database\Triggers */
/**
* @covers \PhpMyAdmin\Triggers\Trigger
* @covers \PhpMyAdmin\Triggers\Triggers
*/
class TriggersTest extends AbstractTestCase
{
private Triggers $triggers;
@ -324,4 +327,132 @@ class TriggersTest extends AbstractTestCase
],
];
}
public function testGetDetails(): void
{
$GLOBALS['cfg']['Server']['DisableIS'] = true;
$dbiDummy = $this->createDbiDummy();
$dbiDummy->addResult(
'SHOW TRIGGERS FROM `test_db`',
[
['test_trigger', 'INSERT', 'test_table', 'BEGIN END', 'AFTER', 'definer@localhost'],
['a_trigger', 'UPDATE', 'test_table2', 'BEGIN END', 'BEFORE', 'definer2@localhost'],
],
['Trigger', 'Event', 'Table', 'Statement', 'Timing', 'Definer'],
);
$triggers = Triggers::getDetails($this->createDatabaseInterface($dbiDummy), 'test_db');
$expected = [
[
'name' => 'a_trigger',
'table' => 'test_table2',
'action_timing' => 'BEFORE',
'event_manipulation' => 'UPDATE',
'definition' => 'BEGIN END',
'definer' => 'definer2@localhost',
'full_trigger_name' => '`a_trigger`',
'drop' => 'DROP TRIGGER IF EXISTS `a_trigger`',
'create' => "CREATE TRIGGER `a_trigger` BEFORE UPDATE ON `test_table2`\n FOR EACH ROW BEGIN END\n//\n",
],
[
'name' => 'test_trigger',
'table' => 'test_table',
'action_timing' => 'AFTER',
'event_manipulation' => 'INSERT',
'definition' => 'BEGIN END',
'definer' => 'definer@localhost',
'full_trigger_name' => '`test_trigger`',
'drop' => 'DROP TRIGGER IF EXISTS `test_trigger`',
'create' => "CREATE TRIGGER `test_trigger` AFTER INSERT ON `test_table`\n FOR EACH ROW BEGIN END\n//\n",
],
];
$this->assertSame($expected, $triggers);
}
public function testGetDetails2(): void
{
$GLOBALS['cfg']['Server']['DisableIS'] = true;
$dbiDummy = $this->createDbiDummy();
$dbiDummy->addResult(
"SHOW TRIGGERS FROM `test_db` LIKE 'test_table2';",
[['a_trigger', 'UPDATE', 'test_table2', 'BEGIN END', 'BEFORE', 'definer2@localhost']],
['Trigger', 'Event', 'Table', 'Statement', 'Timing', 'Definer'],
);
$triggers = Triggers::getDetails($this->createDatabaseInterface($dbiDummy), 'test_db', 'test_table2', '$$');
$expected = [
[
'name' => 'a_trigger',
'table' => 'test_table2',
'action_timing' => 'BEFORE',
'event_manipulation' => 'UPDATE',
'definition' => 'BEGIN END',
'definer' => 'definer2@localhost',
'full_trigger_name' => '`a_trigger`',
'drop' => 'DROP TRIGGER IF EXISTS `a_trigger`',
'create' => "CREATE TRIGGER `a_trigger` BEFORE UPDATE ON `test_table2`\n FOR EACH ROW BEGIN END\n$$\n",
],
];
$this->assertSame($expected, $triggers);
}
/** @covers \PhpMyAdmin\Query\Generator */
public function testGetDetails3(): void
{
$GLOBALS['cfg']['Server']['DisableIS'] = false;
$dbiDummy = $this->createDbiDummy();
// phpcs:disable Generic.Files.LineLength.TooLong
$dbiDummy->addResult(
"SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING, ACTION_STATEMENT, EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA COLLATE utf8_bin= 'test_db'",
[['test_db', 'test_trigger', 'DELETE', 'test_table', 'AFTER', 'BEGIN END', 'test_db', 'test_table', 'definer@localhost']],
['TRIGGER_SCHEMA', 'TRIGGER_NAME', 'EVENT_MANIPULATION', 'EVENT_OBJECT_TABLE', 'ACTION_TIMING', 'ACTION_STATEMENT', 'EVENT_OBJECT_SCHEMA', 'EVENT_OBJECT_TABLE', 'DEFINER'],
);
// phpcs:enable
$triggers = Triggers::getDetails($this->createDatabaseInterface($dbiDummy), 'test_db');
$expected = [
[
'name' => 'test_trigger',
'table' => 'test_table',
'action_timing' => 'AFTER',
'event_manipulation' => 'DELETE',
'definition' => 'BEGIN END',
'definer' => 'definer@localhost',
'full_trigger_name' => '`test_trigger`',
'drop' => 'DROP TRIGGER IF EXISTS `test_trigger`',
'create' => "CREATE TRIGGER `test_trigger` AFTER DELETE ON `test_table`\n FOR EACH ROW BEGIN END\n//\n",
],
];
$this->assertSame($expected, $triggers);
}
/** @covers \PhpMyAdmin\Query\Generator */
public function testGetDetails4(): void
{
$GLOBALS['cfg']['Server']['DisableIS'] = false;
$dbiDummy = $this->createDbiDummy();
// phpcs:disable Generic.Files.LineLength.TooLong
$dbiDummy->addResult(
"SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING, ACTION_STATEMENT, EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA COLLATE utf8_bin= 'test_db' AND EVENT_OBJECT_TABLE COLLATE utf8_bin = 'test_table';",
[['test_db', 'test_trigger', 'DELETE', 'test_table', 'AFTER', 'BEGIN END', 'test_db', 'test_table', 'definer@localhost']],
['TRIGGER_SCHEMA', 'TRIGGER_NAME', 'EVENT_MANIPULATION', 'EVENT_OBJECT_TABLE', 'ACTION_TIMING', 'ACTION_STATEMENT', 'EVENT_OBJECT_SCHEMA', 'EVENT_OBJECT_TABLE', 'DEFINER'],
);
// phpcs:enable
$triggers = Triggers::getDetails($this->createDatabaseInterface($dbiDummy), 'test_db', 'test_table');
$expected = [
[
'name' => 'test_trigger',
'table' => 'test_table',
'action_timing' => 'AFTER',
'event_manipulation' => 'DELETE',
'definition' => 'BEGIN END',
'definer' => 'definer@localhost',
'full_trigger_name' => '`test_trigger`',
'drop' => 'DROP TRIGGER IF EXISTS `test_trigger`',
'create' => "CREATE TRIGGER `test_trigger` AFTER DELETE ON `test_table`\n FOR EACH ROW BEGIN END\n//\n",
],
];
$this->assertSame($expected, $triggers);
}
}

View File

@ -2,9 +2,7 @@
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Selenium\Database;
use PhpMyAdmin\Tests\Selenium\TestBase;
namespace PhpMyAdmin\Tests\Selenium;
/** @coversNothing */
class TriggersTest extends TestBase

View File

@ -31,7 +31,6 @@ module.exports = [
'database/search': rootPath + '/js/src/database/search.ts',
'database/structure': rootPath + '/js/src/database/structure.ts',
'database/tracking': rootPath + '/js/src/database/tracking.ts',
'database/triggers': rootPath + '/js/src/database/triggers.ts',
'designer/init': rootPath + '/js/src/designer/init.ts',
'drag_drop_import': rootPath + '/js/src/drag_drop_import.ts',
'error_report': rootPath + '/js/src/error_report.ts',
@ -77,6 +76,7 @@ module.exports = [
'transformations/sql_editor': rootPath + '/js/src/transformations/sql_editor.ts',
'transformations/xml': rootPath + '/js/src/transformations/xml.ts',
'transformations/xml_editor': rootPath + '/js/src/transformations/xml_editor.ts',
'triggers': rootPath + '/js/src/triggers.ts',
'u2f': rootPath + '/js/src/u2f.ts',
'validator-messages': rootPath + '/js/src/validator-messages.ts',
'webauthn': rootPath + '/js/src/webauthn.ts',