diff options
| author | Charles Cabergs <me@cacharle.xyz> | 2020-07-27 10:05:23 +0200 |
|---|---|---|
| committer | Charles Cabergs <me@cacharle.xyz> | 2020-07-27 10:05:23 +0200 |
| commit | 5bf66662a9bdd62c5bccab15e607cd95cfb8fcab (patch) | |
| tree | 39a1a4629749056191c05dfd899f931701b7acf3 /srcs/phpmyadmin/libraries/classes/Rte | |
| parent | 5afd237bbd22028b85532b8c0b3fcead49a00764 (diff) | |
| download | ft_server-5bf66662a9bdd62c5bccab15e607cd95cfb8fcab.tar.gz ft_server-5bf66662a9bdd62c5bccab15e607cd95cfb8fcab.tar.bz2 ft_server-5bf66662a9bdd62c5bccab15e607cd95cfb8fcab.zip | |
Removed wordpress and phpmyadmin, my server doesn't handle it well and it brings shame on my famillyHEADmaster
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Rte')
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/Events.php | 680 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/Export.php | 168 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/Footer.php | 160 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/General.php | 118 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/Routines.php | 1743 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/RteList.php | 518 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/Triggers.php | 527 | ||||
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Rte/Words.php | 89 |
8 files changed, 0 insertions, 4003 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Rte/Events.php b/srcs/phpmyadmin/libraries/classes/Rte/Events.php deleted file mode 100644 index bb0d52b..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/Events.php +++ /dev/null @@ -1,680 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Functions for event management. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Message; -use PhpMyAdmin\Response; -use PhpMyAdmin\Url; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\Events class - * - * @package PhpMyAdmin - */ -class Events -{ - /** - * @var Export - */ - private $export; - - /** - * @var Footer - */ - private $footer; - - /** - * @var General - */ - private $general; - - /** - * @var RteList - */ - private $rteList; - - /** - * @var Words - */ - private $words; - - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * Events constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - $this->export = new Export($this->dbi); - $this->footer = new Footer($this->dbi); - $this->general = new General($this->dbi); - $this->rteList = new RteList($this->dbi); - $this->words = new Words(); - } - - /** - * Sets required globals - * - * @return void - */ - public function setGlobals() - { - global $event_status, $event_type, $event_interval; - - $event_status = [ - 'query' => [ - 'ENABLE', - 'DISABLE', - 'DISABLE ON SLAVE', - ], - 'display' => [ - 'ENABLED', - 'DISABLED', - 'SLAVESIDE_DISABLED', - ], - ]; - $event_type = [ - 'RECURRING', - 'ONE TIME', - ]; - $event_interval = [ - 'YEAR', - 'QUARTER', - 'MONTH', - 'DAY', - 'HOUR', - 'MINUTE', - 'WEEK', - 'SECOND', - 'YEAR_MONTH', - 'DAY_HOUR', - 'DAY_MINUTE', - 'DAY_SECOND', - 'HOUR_MINUTE', - 'HOUR_SECOND', - 'MINUTE_SECOND', - ]; - } - - /** - * Main function for the events functionality - * - * @return void - */ - public function main() - { - global $db; - - $this->setGlobals(); - /** - * Process all requests - */ - $this->handleEditor(); - $this->export->events(); - /** - * Display a list of available events - */ - $items = $this->dbi->getEvents($db); - echo $this->rteList->get('event', $items); - /** - * Display a link for adding a new event, if - * the user has the privileges and a link to - * toggle the state of the event scheduler. - */ - echo $this->footer->events(); - } - - /** - * Handles editor requests for adding or editing an item - * - * @return void - */ - public function handleEditor() - { - global $errors, $db; - - if (! empty($_POST['editor_process_add']) - || ! empty($_POST['editor_process_edit']) - ) { - $sql_query = ''; - - $item_query = $this->getQueryFromRequest(); - - if (! count($errors)) { // set by PhpMyAdmin\Rte\Routines::getQueryFromRequest() - // Execute the created query - if (! empty($_POST['editor_process_edit'])) { - // Backup the old trigger, in case something goes wrong - $create_item = $this->dbi->getDefinition( - $db, - 'EVENT', - $_POST['item_original_name'] - ); - $drop_item = "DROP EVENT " - . Util::backquote($_POST['item_original_name']) - . ";\n"; - $result = $this->dbi->tryQuery($drop_item); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($drop_item) - ) - . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - } else { - $result = $this->dbi->tryQuery($item_query); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($item_query) - ) - . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - // We dropped the old item, but were unable to create - // the new one. Try to restore the backup query - $result = $this->dbi->tryQuery($create_item); - $errors = $this->general->checkResult( - $result, - __( - 'Sorry, we failed to restore the dropped event.' - ), - $create_item, - $errors - ); - } else { - $message = Message::success( - __('Event %1$s has been modified.') - ); - $message->addParam( - Util::backquote($_POST['item_name']) - ); - $sql_query = $drop_item . $item_query; - } - } - } else { - // 'Add a new item' mode - $result = $this->dbi->tryQuery($item_query); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($item_query) - ) - . '<br><br>' - . __('MySQL said: ') . $this->dbi->getError(); - } else { - $message = Message::success( - __('Event %1$s has been created.') - ); - $message->addParam( - Util::backquote($_POST['item_name']) - ); - $sql_query = $item_query; - } - } - } - - if (count($errors)) { - $message = Message::error( - '<b>' - . __( - 'One or more errors have occurred while processing your request:' - ) - . '</b>' - ); - $message->addHtml('<ul>'); - foreach ($errors as $string) { - $message->addHtml('<li>' . $string . '</li>'); - } - $message->addHtml('</ul>'); - } - - $output = Util::getMessage($message, $sql_query); - $response = Response::getInstance(); - if ($response->isAjax()) { - if ($message->isSuccess()) { - $events = $this->dbi->getEvents($db, $_POST['item_name']); - $event = $events[0]; - $response->addJSON( - 'name', - htmlspecialchars( - mb_strtoupper($_POST['item_name']) - ) - ); - if (! empty($event)) { - $response->addJSON('new_row', $this->rteList->getEventRow($event)); - } - $response->addJSON('insert', ! empty($event)); - $response->addJSON('message', $output); - } else { - $response->setRequestStatus(false); - $response->addJSON('message', $message); - } - exit; - } - } - /** - * Display a form used to add/edit a trigger, if necessary - */ - if (count($errors) - || (empty($_POST['editor_process_add']) - && empty($_POST['editor_process_edit']) - && (! empty($_REQUEST['add_item']) - || ! empty($_REQUEST['edit_item']) - || ! empty($_POST['item_changetype']))) - ) { // FIXME: this must be simpler than that - $operation = ''; - if (! empty($_POST['item_changetype'])) { - $operation = 'change'; - } - // Get the data for the form (if any) - if (! empty($_REQUEST['add_item'])) { - $title = $this->words->get('add'); - $item = $this->getDataFromRequest(); - $mode = 'add'; - } elseif (! empty($_REQUEST['edit_item'])) { - $title = __("Edit event"); - if (! empty($_REQUEST['item_name']) - && empty($_POST['editor_process_edit']) - && empty($_POST['item_changetype']) - ) { - $item = $this->getDataFromName($_REQUEST['item_name']); - if ($item !== false) { - $item['item_original_name'] = $item['item_name']; - } - } else { - $item = $this->getDataFromRequest(); - } - $mode = 'edit'; - } - $this->general->sendEditor('EVN', $mode, $item, $title, $db, $operation); - } - } - - /** - * This function will generate the values that are required to for the editor - * - * @return array Data necessary to create the editor. - */ - public function getDataFromRequest() - { - $retval = []; - $indices = [ - 'item_name', - 'item_original_name', - 'item_status', - 'item_execute_at', - 'item_interval_value', - 'item_interval_field', - 'item_starts', - 'item_ends', - 'item_definition', - 'item_preserve', - 'item_comment', - 'item_definer', - ]; - foreach ($indices as $index) { - $retval[$index] = isset($_POST[$index]) ? $_POST[$index] : ''; - } - $retval['item_type'] = 'ONE TIME'; - $retval['item_type_toggle'] = 'RECURRING'; - if (isset($_POST['item_type']) && $_POST['item_type'] == 'RECURRING') { - $retval['item_type'] = 'RECURRING'; - $retval['item_type_toggle'] = 'ONE TIME'; - } - return $retval; - } - - /** - * This function will generate the values that are required to complete - * the "Edit event" form given the name of a event. - * - * @param string $name The name of the event. - * - * @return array|bool Data necessary to create the editor. - */ - public function getDataFromName($name) - { - global $db; - - $retval = []; - $columns = "`EVENT_NAME`, `STATUS`, `EVENT_TYPE`, `EXECUTE_AT`, " - . "`INTERVAL_VALUE`, `INTERVAL_FIELD`, `STARTS`, `ENDS`, " - . "`EVENT_DEFINITION`, `ON_COMPLETION`, `DEFINER`, `EVENT_COMMENT`"; - $where = "EVENT_SCHEMA " . Util::getCollateForIS() . "=" - . "'" . $this->dbi->escapeString($db) . "' " - . "AND EVENT_NAME='" . $this->dbi->escapeString($name) . "'"; - $query = "SELECT $columns FROM `INFORMATION_SCHEMA`.`EVENTS` WHERE $where;"; - $item = $this->dbi->fetchSingleRow($query); - if (! $item) { - return false; - } - $retval['item_name'] = $item['EVENT_NAME']; - $retval['item_status'] = $item['STATUS']; - $retval['item_type'] = $item['EVENT_TYPE']; - if ($retval['item_type'] == 'RECURRING') { - $retval['item_type_toggle'] = 'ONE TIME'; - } else { - $retval['item_type_toggle'] = 'RECURRING'; - } - $retval['item_execute_at'] = $item['EXECUTE_AT']; - $retval['item_interval_value'] = $item['INTERVAL_VALUE']; - $retval['item_interval_field'] = $item['INTERVAL_FIELD']; - $retval['item_starts'] = $item['STARTS']; - $retval['item_ends'] = $item['ENDS']; - $retval['item_preserve'] = ''; - if ($item['ON_COMPLETION'] == 'PRESERVE') { - $retval['item_preserve'] = " checked='checked'"; - } - $retval['item_definition'] = $item['EVENT_DEFINITION']; - $retval['item_definer'] = $item['DEFINER']; - $retval['item_comment'] = $item['EVENT_COMMENT']; - - return $retval; - } - - /** - * Displays a form used to add/edit an event - * - * @param string $mode If the editor will be used to edit an event - * or add a new one: 'edit' or 'add'. - * @param string $operation If the editor was previously invoked with - * JS turned off, this will hold the name of - * the current operation - * @param array $item Data for the event returned by - * getDataFromRequest() or getDataFromName() - * - * @return string HTML code for the editor. - */ - public function getEditorForm($mode, $operation, array $item) - { - global $db, $table, $event_status, $event_type, $event_interval; - - $modeToUpper = mb_strtoupper($mode); - - $response = Response::getInstance(); - - // Escape special characters - $need_escape = [ - 'item_original_name', - 'item_name', - 'item_type', - 'item_execute_at', - 'item_interval_value', - 'item_starts', - 'item_ends', - 'item_definition', - 'item_definer', - 'item_comment', - ]; - foreach ($need_escape as $index) { - $item[$index] = htmlentities((string) $item[$index], ENT_QUOTES); - } - $original_data = ''; - if ($mode == 'edit') { - $original_data = "<input name='item_original_name' " - . "type='hidden' value='{$item['item_original_name']}'>\n"; - } - // Handle some logic first - if ($operation == 'change') { - if ($item['item_type'] == 'RECURRING') { - $item['item_type'] = 'ONE TIME'; - $item['item_type_toggle'] = 'RECURRING'; - } else { - $item['item_type'] = 'RECURRING'; - $item['item_type_toggle'] = 'ONE TIME'; - } - } - if ($item['item_type'] == 'ONE TIME') { - $isrecurring_class = ' hide'; - $isonetime_class = ''; - } else { - $isrecurring_class = ''; - $isonetime_class = ' hide'; - } - // Create the output - $retval = ""; - $retval .= "<!-- START " . $modeToUpper . " EVENT FORM -->\n\n"; - $retval .= "<form class='rte_form' action='db_events.php' method='post'>\n"; - $retval .= "<input name='{$mode}_item' type='hidden' value='1'>\n"; - $retval .= $original_data; - $retval .= Url::getHiddenInputs($db, $table) . "\n"; - $retval .= "<fieldset>\n"; - $retval .= "<legend>" . __('Details') . "</legend>\n"; - $retval .= "<table class='rte_table'>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Event name') . "</td>\n"; - $retval .= " <td><input type='text' name='item_name' \n"; - $retval .= " value='{$item['item_name']}'\n"; - $retval .= " maxlength='64'></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Status') . "</td>\n"; - $retval .= " <td>\n"; - $retval .= " <select name='item_status'>\n"; - foreach ($event_status['display'] as $key => $value) { - $selected = ""; - if (! empty($item['item_status']) && $item['item_status'] == $value) { - $selected = " selected='selected'"; - } - $retval .= "<option$selected>$value</option>"; - } - $retval .= " </select>\n"; - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Event type') . "</td>\n"; - $retval .= " <td>\n"; - if ($response->isAjax()) { - $retval .= " <select name='item_type'>"; - foreach ($event_type as $key => $value) { - $selected = ""; - if (! empty($item['item_type']) && $item['item_type'] == $value) { - $selected = " selected='selected'"; - } - $retval .= "<option$selected>$value</option>"; - } - $retval .= " </select>\n"; - } else { - $retval .= " <input name='item_type' type='hidden' \n"; - $retval .= " value='{$item['item_type']}'>\n"; - $retval .= " <div class='font_weight_bold center half_width'>\n"; - $retval .= " {$item['item_type']}\n"; - $retval .= " </div>\n"; - $retval .= " <input type='submit'\n"; - $retval .= " name='item_changetype' class='half_width'\n"; - $retval .= " value='"; - $retval .= sprintf(__('Change to %s'), $item['item_type_toggle']); - $retval .= "'>\n"; - } - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr class='onetime_event_row $isonetime_class'>\n"; - $retval .= " <td>" . __('Execute at') . "</td>\n"; - $retval .= " <td class='nowrap'>\n"; - $retval .= " <input type='text' name='item_execute_at'\n"; - $retval .= " value='{$item['item_execute_at']}'\n"; - $retval .= " class='datetimefield'>\n"; - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr class='recurring_event_row $isrecurring_class'>\n"; - $retval .= " <td>" . __('Execute every') . "</td>\n"; - $retval .= " <td>\n"; - $retval .= " <input class='half_width' type='text'\n"; - $retval .= " name='item_interval_value'\n"; - $retval .= " value='{$item['item_interval_value']}'>\n"; - $retval .= " <select class='half_width' name='item_interval_field'>"; - foreach ($event_interval as $key => $value) { - $selected = ""; - if (! empty($item['item_interval_field']) - && $item['item_interval_field'] == $value - ) { - $selected = " selected='selected'"; - } - $retval .= "<option$selected>$value</option>"; - } - $retval .= " </select>\n"; - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr class='recurring_event_row$isrecurring_class'>\n"; - $retval .= " <td>" . _pgettext('Start of recurring event', 'Start'); - $retval .= " </td>\n"; - $retval .= " <td class='nowrap'>\n"; - $retval .= " <input type='text'\n name='item_starts'\n"; - $retval .= " value='{$item['item_starts']}'\n"; - $retval .= " class='datetimefield'>\n"; - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr class='recurring_event_row$isrecurring_class'>\n"; - $retval .= " <td>" . _pgettext('End of recurring event', 'End') . "</td>\n"; - $retval .= " <td class='nowrap'>\n"; - $retval .= " <input type='text' name='item_ends'\n"; - $retval .= " value='{$item['item_ends']}'\n"; - $retval .= " class='datetimefield'>\n"; - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Definition') . "</td>\n"; - $retval .= " <td><textarea name='item_definition' rows='15' cols='40'>"; - $retval .= $item['item_definition']; - $retval .= "</textarea></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('On completion preserve') . "</td>\n"; - $retval .= " <td><input type='checkbox'\n"; - $retval .= " name='item_preserve'{$item['item_preserve']}></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Definer') . "</td>\n"; - $retval .= " <td><input type='text' name='item_definer'\n"; - $retval .= " value='{$item['item_definer']}'></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Comment') . "</td>\n"; - $retval .= " <td><input type='text' name='item_comment' maxlength='64'\n"; - $retval .= " value='{$item['item_comment']}'></td>\n"; - $retval .= "</tr>\n"; - $retval .= "</table>\n"; - $retval .= "</fieldset>\n"; - if ($response->isAjax()) { - $retval .= "<input type='hidden' name='editor_process_{$mode}'\n"; - $retval .= " value='true'>\n"; - $retval .= "<input type='hidden' name='ajax_request' value='true'>\n"; - } else { - $retval .= "<fieldset class='tblFooters'>\n"; - $retval .= " <input type='submit' name='editor_process_{$mode}'\n"; - $retval .= " value='" . __('Go') . "'>\n"; - $retval .= "</fieldset>\n"; - } - $retval .= "</form>\n\n"; - $retval .= "<!-- END " . $modeToUpper . " EVENT FORM -->\n\n"; - - return $retval; - } - - /** - * Composes the query necessary to create an event from an HTTP request. - * - * @return string The CREATE EVENT query. - */ - public function getQueryFromRequest() - { - global $errors, $event_status, $event_type, $event_interval; - - $query = 'CREATE '; - if (! empty($_POST['item_definer'])) { - if (mb_strpos($_POST['item_definer'], '@') !== false - ) { - $arr = explode('@', $_POST['item_definer']); - $query .= 'DEFINER=' . Util::backquote($arr[0]); - $query .= '@' . Util::backquote($arr[1]) . ' '; - } else { - $errors[] = __('The definer must be in the "username@hostname" format!'); - } - } - $query .= 'EVENT '; - if (! empty($_POST['item_name'])) { - $query .= Util::backquote($_POST['item_name']) . ' '; - } else { - $errors[] = __('You must provide an event name!'); - } - $query .= 'ON SCHEDULE '; - if (! empty($_POST['item_type']) - && in_array($_POST['item_type'], $event_type) - ) { - if ($_POST['item_type'] == 'RECURRING') { - if (! empty($_POST['item_interval_value']) - && ! empty($_POST['item_interval_field']) - && in_array($_POST['item_interval_field'], $event_interval) - ) { - $query .= 'EVERY ' . intval($_POST['item_interval_value']) . ' '; - $query .= $_POST['item_interval_field'] . ' '; - } else { - $errors[] - = __('You must provide a valid interval value for the event.'); - } - if (! empty($_POST['item_starts'])) { - $query .= "STARTS '" - . $this->dbi->escapeString($_POST['item_starts']) - . "' "; - } - if (! empty($_POST['item_ends'])) { - $query .= "ENDS '" - . $this->dbi->escapeString($_POST['item_ends']) - . "' "; - } - } else { - if (! empty($_POST['item_execute_at'])) { - $query .= "AT '" - . $this->dbi->escapeString($_POST['item_execute_at']) - . "' "; - } else { - $errors[] - = __('You must provide a valid execution time for the event.'); - } - } - } else { - $errors[] = __('You must provide a valid type for the event.'); - } - $query .= 'ON COMPLETION '; - if (empty($_POST['item_preserve'])) { - $query .= 'NOT '; - } - $query .= 'PRESERVE '; - if (! empty($_POST['item_status'])) { - foreach ($event_status['display'] as $key => $value) { - if ($value == $_POST['item_status']) { - $query .= $event_status['query'][$key] . ' '; - break; - } - } - } - if (! empty($_POST['item_comment'])) { - $query .= "COMMENT '" . $this->dbi->escapeString( - $_POST['item_comment'] - ) . "' "; - } - $query .= 'DO '; - if (! empty($_POST['item_definition'])) { - $query .= $_POST['item_definition']; - } else { - $errors[] = __('You must provide an event definition.'); - } - - return $query; - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/Export.php b/srcs/phpmyadmin/libraries/classes/Rte/Export.php deleted file mode 100644 index 2ae19e6..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/Export.php +++ /dev/null @@ -1,168 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Common functions for the export functionality for Routines, Triggers and Events. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Message; -use PhpMyAdmin\Response; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\Export class - * - * @package PhpMyAdmin - */ -class Export -{ - /** - * @var Words - */ - private $words; - - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * Export constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - $this->words = new Words(); - } - - /** - * This function is called from one of the other functions in this file - * and it completes the handling of the export functionality. - * - * @param string $export_data The SQL query to create the requested item - * - * @return void - */ - private function handle($export_data) - { - global $db; - - $response = Response::getInstance(); - - $item_name = htmlspecialchars(Util::backquote($_GET['item_name'])); - if ($export_data !== false) { - $export_data = htmlspecialchars(trim($export_data)); - $title = sprintf($this->words->get('export'), $item_name); - if ($response->isAjax()) { - $response->addJSON('message', $export_data); - $response->addJSON('title', $title); - exit; - } else { - $export_data = '<textarea cols="40" rows="15" style="width: 100%;">' - . $export_data . '</textarea>'; - echo "<fieldset>\n" - , "<legend>$title</legend>\n" - , $export_data - , "</fieldset>\n"; - } - } else { - $_db = htmlspecialchars(Util::backquote($db)); - $message = __('Error in processing request:') . ' ' - . sprintf($this->words->get('no_view'), $item_name, $_db); - $message = Message::error($message); - - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $message); - exit; - } else { - $message->display(); - } - } - } - - /** - * If necessary, prepares event information and passes - * it to handle() for the actual export. - * - * @return void - */ - public function events() - { - global $db; - - if (! empty($_GET['export_item']) && ! empty($_GET['item_name'])) { - $item_name = $_GET['item_name']; - $export_data = $this->dbi->getDefinition($db, 'EVENT', $item_name); - if (! $export_data) { - $export_data = false; - } - $this->handle($export_data); - } - } - - /** - * If necessary, prepares routine information and passes - * it to handle() for the actual export. - * - * @return void - */ - public function routines() - { - global $db; - - if (! empty($_GET['export_item']) - && ! empty($_GET['item_name']) - && ! empty($_GET['item_type']) - ) { - if ($_GET['item_type'] == 'FUNCTION' || $_GET['item_type'] == 'PROCEDURE') { - $rtn_definition - = $this->dbi->getDefinition( - $db, - $_GET['item_type'], - $_GET['item_name'] - ); - if ($rtn_definition === null) { - $export_data = false; - } else { - $export_data = "DELIMITER $$\n" - . $rtn_definition - . "$$\nDELIMITER ;\n"; - } - - $this->handle($export_data); - } - } - } - - /** - * If necessary, prepares trigger information and passes - * it to handle() for the actual export. - * - * @return void - */ - public function triggers() - { - global $db, $table; - - if (! empty($_GET['export_item']) && ! empty($_GET['item_name'])) { - $item_name = $_GET['item_name']; - $triggers = $this->dbi->getTriggers($db, $table, ''); - $export_data = false; - foreach ($triggers as $trigger) { - if ($trigger['name'] === $item_name) { - $export_data = $trigger['create']; - break; - } - } - $this->handle($export_data); - } - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/Footer.php b/srcs/phpmyadmin/libraries/classes/Rte/Footer.php deleted file mode 100644 index 5181b00..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/Footer.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Common functions for generating the footer for Routines, Triggers and Events. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\Footer class - * - * @package PhpMyAdmin - */ -class Footer -{ - /** - * @var Words - */ - private $words; - - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * Footer constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - $this->words = new Words(); - } - - /** - * Creates a fieldset for adding a new item, if the user has the privileges. - * - * @param string $docu String used to create a link to the MySQL docs - * @param string $priv Privilege to check for adding a new item - * @param string $name MySQL name of the item - * - * @return string An HTML snippet with the link to add a new item - */ - private function getLinks($docu, $priv, $name) - { - global $db, $table, $url_query; - - $icon = mb_strtolower($name) . '_add'; - $retval = ""; - $retval .= "<!-- ADD " . $name . " FORM START -->\n"; - $retval .= "<fieldset class='left'>\n"; - $retval .= "<legend>" . _pgettext('Create new procedure', 'New') . "</legend>\n"; - $retval .= " <div class='wrap'>\n"; - if (Util::currentUserHasPrivilege($priv, $db, $table)) { - $retval .= ' <a class="ajax add_anchor" '; - $retval .= "href='db_" . mb_strtolower($name) . "s.php"; - $retval .= "$url_query&add_item=1' "; - $retval .= "onclick='$.datepicker.initialized = false;'>"; - $icon = 'b_' . $icon; - $retval .= Util::getIcon($icon); - $retval .= $this->words->get('add') . "</a>\n"; - } else { - $icon = 'bd_' . $icon; - $retval .= Util::getIcon($icon); - $retval .= $this->words->get('add') . "\n"; - } - $retval .= " " . Util::showMySQLDocu($docu) . "\n"; - $retval .= " </div>\n"; - $retval .= "</fieldset>\n"; - $retval .= "<!-- ADD " . $name . " FORM END -->\n\n"; - - return $retval; - } - - /** - * Creates a fieldset for adding a new routine, if the user has the privileges. - * - * @return string HTML code with containing the footer fieldset - */ - public function routines() - { - return $this->getLinks('CREATE_PROCEDURE', 'CREATE ROUTINE', 'ROUTINE'); - } - - /** - * Creates a fieldset for adding a new trigger, if the user has the privileges. - * - * @return string HTML code with containing the footer fieldset - */ - public function triggers() - { - return $this->getLinks('CREATE_TRIGGER', 'TRIGGER', 'TRIGGER'); - } - - /** - * Creates a fieldset for adding a new event, if the user has the privileges. - * - * @return string HTML code with containing the footer fieldset - */ - public function events() - { - global $db, $url_query; - - /** - * For events, we show the usual 'Add event' form and also - * a form for toggling the state of the event scheduler - */ - // Init options for the event scheduler toggle functionality - $es_state = $this->dbi->fetchValue( - "SHOW GLOBAL VARIABLES LIKE 'event_scheduler'", - 0, - 1 - ); - $es_state = mb_strtolower($es_state); - $options = [ - 0 => [ - 'label' => __('OFF'), - 'value' => "SET GLOBAL event_scheduler=\"OFF\"", - 'selected' => $es_state != 'on', - ], - 1 => [ - 'label' => __('ON'), - 'value' => "SET GLOBAL event_scheduler=\"ON\"", - 'selected' => $es_state == 'on', - ], - ]; - // Generate output - $retval = "<!-- FOOTER LINKS START -->\n"; - $retval .= "<div class='doubleFieldset'>\n"; - // show the usual footer - $retval .= $this->getLinks('CREATE_EVENT', 'EVENT', 'EVENT'); - $retval .= " <fieldset class='right'>\n"; - $retval .= " <legend>\n"; - $retval .= " " . __('Event scheduler status') . "\n"; - $retval .= " </legend>\n"; - $retval .= " <div class='wrap'>\n"; - // show the toggle button - $retval .= Util::toggleButton( - "sql.php$url_query&goto=db_events.php" . urlencode("?db=$db"), - 'sql_query', - $options, - 'Functions.slidingMessage(data.sql_query);' - ); - $retval .= " </div>\n"; - $retval .= " </fieldset>\n"; - $retval .= " <div class='clearfloat'></div>\n"; - $retval .= "</div>"; - $retval .= "<!-- FOOTER LINKS END -->\n"; - - return $retval; - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/General.php b/srcs/phpmyadmin/libraries/classes/Rte/General.php deleted file mode 100644 index 37962b5..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/General.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * General functions. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Message; -use PhpMyAdmin\Response; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\General class - * - * @package PhpMyAdmin - */ -class General -{ - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * General constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - } - - /** - * Check result - * - * @param resource|bool $result Query result - * @param string $error Error to add - * @param string $createStatement Query - * @param array $errors Errors - * - * @return array - */ - public function checkResult($result, $error, $createStatement, array $errors) - { - if ($result) { - return $errors; - } - - // OMG, this is really bad! We dropped the query, - // failed to create a new one - // and now even the backup query does not execute! - // This should not happen, but we better handle - // this just in case. - $errors[] = $error . '<br>' - . __('The backed up query was:') - . "\"" . htmlspecialchars($createStatement) . "\"" . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - - return $errors; - } - - /** - * Send TRI or EVN editor via ajax or by echoing. - * - * @param string $type TRI or EVN - * @param string $mode Editor mode 'add' or 'edit' - * @param array $item Data necessary to create the editor - * @param string $title Title of the editor - * @param string $db Database - * @param string $operation Operation 'change' or '' - * - * @return void - */ - public function sendEditor($type, $mode, array $item, $title, $db, $operation = null) - { - $events = new Events($this->dbi); - $triggers = new Triggers($this->dbi); - $words = new Words(); - $response = Response::getInstance(); - if ($item !== false) { - // Show form - if ($type == 'TRI') { - $editor = $triggers->getEditorForm($mode, $item); - } else { // EVN - $editor = $events->getEditorForm($mode, $operation, $item); - } - if ($response->isAjax()) { - $response->addJSON('message', $editor); - $response->addJSON('title', $title); - } else { - echo "\n\n<h2>$title</h2>\n\n$editor"; - unset($_POST); - } - exit; - } else { - $message = __('Error in processing request:') . ' '; - $message .= sprintf( - $words->get('not_found'), - htmlspecialchars(Util::backquote($_REQUEST['item_name'])), - htmlspecialchars(Util::backquote($db)) - ); - $message = Message::error($message); - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $message); - exit; - } else { - $message->display(); - } - } - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/Routines.php b/srcs/phpmyadmin/libraries/classes/Rte/Routines.php deleted file mode 100644 index 24b0dd5..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/Routines.php +++ /dev/null @@ -1,1743 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Functions for routine management. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\Charsets; -use PhpMyAdmin\Charsets\Charset; -use PhpMyAdmin\Core; -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Message; -use PhpMyAdmin\Response; -use PhpMyAdmin\SqlParser\Parser; -use PhpMyAdmin\SqlParser\Statements\CreateStatement; -use PhpMyAdmin\SqlParser\Utils\Routine; -use PhpMyAdmin\Template; -use PhpMyAdmin\Url; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\Routines class - * - * @package PhpMyAdmin - */ -class Routines -{ - /** - * @var Export - */ - private $export; - - /** - * @var Footer - */ - private $footer; - - /** - * @var General - */ - private $general; - - /** - * @var RteList - */ - private $rteList; - - /** - * @var Words - */ - private $words; - - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * Routines constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - $this->export = new Export($this->dbi); - $this->footer = new Footer($this->dbi); - $this->general = new General($this->dbi); - $this->rteList = new RteList($this->dbi); - $this->words = new Words(); - } - - /** - * Sets required globals - * - * @return void - */ - public function setGlobals() - { - global $param_directions, $param_opts_num, $param_sqldataaccess; - - $param_directions = [ - 'IN', - 'OUT', - 'INOUT', - ]; - $param_opts_num = [ - 'UNSIGNED', - 'ZEROFILL', - 'UNSIGNED ZEROFILL', - ]; - $param_sqldataaccess = [ - 'NO SQL', - 'CONTAINS SQL', - 'READS SQL DATA', - 'MODIFIES SQL DATA', - ]; - } - - /** - * Main function for the routines functionality - * - * @param string $type 'FUNCTION' for functions, - * 'PROCEDURE' for procedures, - * null for both - * - * @return void - */ - public function main($type) - { - global $db; - - $this->setGlobals(); - /** - * Process all requests - */ - $this->handleEditor(); - $this->handleExecute(); - $this->export->routines(); - /** - * Display a list of available routines - */ - if (! Core::isValid($type, ['FUNCTION', 'PROCEDURE'])) { - $type = null; - } - $items = $this->dbi->getRoutines($db, $type); - echo $this->rteList->get('routine', $items); - /** - * Display the form for adding a new routine, if the user has the privileges. - */ - echo $this->footer->routines(); - /** - * Display a warning for users with PHP's old "mysql" extension. - */ - if (! DatabaseInterface::checkDbExtension('mysqli')) { - trigger_error( - __( - 'You are using PHP\'s deprecated \'mysql\' extension, ' - . 'which is not capable of handling multi queries. ' - . '[strong]The execution of some stored routines may fail![/strong] ' - . 'Please use the improved \'mysqli\' extension to ' - . 'avoid any problems.' - ), - E_USER_WARNING - ); - } - } - - /** - * Handles editor requests for adding or editing an item - * - * @return void - */ - public function handleEditor() - { - global $db, $errors; - - $errors = $this->handleRequestCreateOrEdit($errors, $db); - $response = Response::getInstance(); - - /** - * Display a form used to add/edit a routine, if necessary - */ - // FIXME: this must be simpler than that - if (count($errors) - || ( empty($_POST['editor_process_add']) - && empty($_POST['editor_process_edit']) - && (! empty($_REQUEST['add_item']) || ! empty($_REQUEST['edit_item']) - || ! empty($_POST['routine_addparameter']) - || ! empty($_POST['routine_removeparameter']) - || ! empty($_POST['routine_changetype']))) - ) { - // Handle requests to add/remove parameters and changing routine type - // This is necessary when JS is disabled - $operation = ''; - if (! empty($_POST['routine_addparameter'])) { - $operation = 'add'; - } elseif (! empty($_POST['routine_removeparameter'])) { - $operation = 'remove'; - } elseif (! empty($_POST['routine_changetype'])) { - $operation = 'change'; - } - // Get the data for the form (if any) - if (! empty($_REQUEST['add_item'])) { - $title = $this->words->get('add'); - $routine = $this->getDataFromRequest(); - $mode = 'add'; - } elseif (! empty($_REQUEST['edit_item'])) { - $title = __("Edit routine"); - if (! $operation && ! empty($_GET['item_name']) - && empty($_POST['editor_process_edit']) - ) { - $routine = $this->getDataFromName( - $_GET['item_name'], - $_GET['item_type'] - ); - if ($routine !== false) { - $routine['item_original_name'] = $routine['item_name']; - $routine['item_original_type'] = $routine['item_type']; - } - } else { - $routine = $this->getDataFromRequest(); - } - $mode = 'edit'; - } - if ($routine !== false) { - // Show form - $editor = $this->getEditorForm($mode, $operation, $routine); - if ($response->isAjax()) { - $response->addJSON('message', $editor); - $response->addJSON('title', $title); - $response->addJSON('paramTemplate', $this->getParameterRow()); - $response->addJSON('type', $routine['item_type']); - } else { - echo "\n\n<h2>$title</h2>\n\n$editor"; - } - exit; - } else { - $message = __('Error in processing request:') . ' '; - $message .= sprintf( - $this->words->get('no_edit'), - htmlspecialchars( - Util::backquote($_REQUEST['item_name']) - ), - htmlspecialchars(Util::backquote($db)) - ); - - $message = Message::error($message); - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $message); - exit; - } else { - $message->display(); - } - } - } - } - - /** - * Handle request to create or edit a routine - * - * @param array $errors Errors - * @param string $db DB name - * - * @return array - */ - public function handleRequestCreateOrEdit(array $errors, $db) - { - if (empty($_POST['editor_process_add']) - && empty($_POST['editor_process_edit']) - ) { - return $errors; - } - - $sql_query = ''; - $routine_query = $this->getQueryFromRequest(); - if (! count($errors)) { - // Execute the created query - if (! empty($_POST['editor_process_edit'])) { - $isProcOrFunc = in_array( - $_POST['item_original_type'], - [ - 'PROCEDURE', - 'FUNCTION', - ] - ); - - if (! $isProcOrFunc) { - $errors[] = sprintf( - __('Invalid routine type: "%s"'), - htmlspecialchars($_POST['item_original_type']) - ); - } else { - // Backup the old routine, in case something goes wrong - $create_routine = $this->dbi->getDefinition( - $db, - $_POST['item_original_type'], - $_POST['item_original_name'] - ); - - $privilegesBackup = $this->backupPrivileges(); - - $drop_routine = "DROP {$_POST['item_original_type']} " - . Util::backquote($_POST['item_original_name']) - . ";\n"; - $result = $this->dbi->tryQuery($drop_routine); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($drop_routine) - ) - . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - } else { - list($newErrors, $message) = $this->create( - $routine_query, - $create_routine, - $privilegesBackup - ); - if (empty($newErrors)) { - $sql_query = $drop_routine . $routine_query; - } else { - $errors = array_merge($errors, $newErrors); - } - unset($newErrors); - if (null === $message) { - unset($message); - } - } - } - } else { - // 'Add a new routine' mode - $result = $this->dbi->tryQuery($routine_query); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($routine_query) - ) - . '<br><br>' - . __('MySQL said: ') . $this->dbi->getError(); - } else { - $message = Message::success( - __('Routine %1$s has been created.') - ); - $message->addParam( - Util::backquote($_POST['item_name']) - ); - $sql_query = $routine_query; - } - } - } - - if (count($errors)) { - $message = Message::error( - __( - 'One or more errors have occurred while' - . ' processing your request:' - ) - ); - $message->addHtml('<ul>'); - foreach ($errors as $string) { - $message->addHtml('<li>' . $string . '</li>'); - } - $message->addHtml('</ul>'); - } - - $output = Util::getMessage($message, $sql_query); - $response = Response::getInstance(); - if (! $response->isAjax()) { - return $errors; - } - - if (! $message->isSuccess()) { - $response->setRequestStatus(false); - $response->addJSON('message', $output); - exit; - } - - $routines = $this->dbi->getRoutines( - $db, - $_POST['item_type'], - $_POST['item_name'] - ); - $routine = $routines[0]; - $response->addJSON( - 'name', - htmlspecialchars( - mb_strtoupper($_POST['item_name']) - ) - ); - $response->addJSON('new_row', $this->rteList->getRoutineRow($routine)); - $response->addJSON('insert', ! empty($routine)); - $response->addJSON('message', $output); - exit; - } - - /** - * Backup the privileges - * - * @return array - */ - public function backupPrivileges() - { - if (! $GLOBALS['proc_priv'] || ! $GLOBALS['is_reload_priv']) { - return []; - } - - // Backup the Old Privileges before dropping - // if $_POST['item_adjust_privileges'] set - if (! isset($_POST['item_adjust_privileges']) - || empty($_POST['item_adjust_privileges']) - ) { - return []; - } - - $privilegesBackupQuery = 'SELECT * FROM ' . Util::backquote( - 'mysql' - ) - . '.' . Util::backquote('procs_priv') - . ' where Routine_name = "' . $_POST['item_original_name'] - . '" AND Routine_type = "' . $_POST['item_original_type'] - . '";'; - - $privilegesBackup = $this->dbi->fetchResult( - $privilegesBackupQuery, - 0 - ); - - return $privilegesBackup; - } - - /** - * Create the routine - * - * @param string $routine_query Query to create routine - * @param string $create_routine Query to restore routine - * @param array $privilegesBackup Privileges backup - * - * @return array - */ - public function create( - $routine_query, - $create_routine, - array $privilegesBackup - ) { - $result = $this->dbi->tryQuery($routine_query); - if (! $result) { - $errors = []; - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($routine_query) - ) - . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - // We dropped the old routine, - // but were unable to create the new one - // Try to restore the backup query - $result = $this->dbi->tryQuery($create_routine); - $errors = $this->general->checkResult( - $result, - __( - 'Sorry, we failed to restore' - . ' the dropped routine.' - ), - $create_routine, - $errors - ); - - return [ - $errors, - null, - ]; - } - - // Default value - $resultAdjust = false; - - if ($GLOBALS['proc_priv'] - && $GLOBALS['is_reload_priv'] - ) { - // Insert all the previous privileges - // but with the new name and the new type - foreach ($privilegesBackup as $priv) { - $adjustProcPrivilege = 'INSERT INTO ' - . Util::backquote('mysql') . '.' - . Util::backquote('procs_priv') - . ' VALUES("' . $priv[0] . '", "' - . $priv[1] . '", "' . $priv[2] . '", "' - . $_POST['item_name'] . '", "' - . $_POST['item_type'] . '", "' - . $priv[5] . '", "' - . $priv[6] . '", "' - . $priv[7] . '");'; - $resultAdjust = $this->dbi->query( - $adjustProcPrivilege - ); - } - } - - $message = $this->flushPrivileges($resultAdjust); - - return [ - [], - $message, - ]; - } - - /** - * Flush privileges and get message - * - * @param bool $flushPrivileges Flush privileges - * - * @return Message - */ - public function flushPrivileges($flushPrivileges) - { - if ($flushPrivileges) { - // Flush the Privileges - $flushPrivQuery = 'FLUSH PRIVILEGES;'; - $this->dbi->query($flushPrivQuery); - - $message = Message::success( - __( - 'Routine %1$s has been modified. Privileges have been adjusted.' - ) - ); - } else { - $message = Message::success( - __('Routine %1$s has been modified.') - ); - } - $message->addParam( - Util::backquote($_POST['item_name']) - ); - - return $message; - } - - /** - * This function will generate the values that are required to - * complete the editor form. It is especially necessary to handle - * the 'Add another parameter', 'Remove last parameter' and - * 'Change routine type' functionalities when JS is disabled. - * - * @return array Data necessary to create the routine editor. - */ - public function getDataFromRequest() - { - global $param_directions, $param_sqldataaccess; - - $retval = []; - $indices = [ - 'item_name', - 'item_original_name', - 'item_returnlength', - 'item_returnopts_num', - 'item_returnopts_text', - 'item_definition', - 'item_comment', - 'item_definer', - ]; - foreach ($indices as $index) { - $retval[$index] = isset($_POST[$index]) ? $_POST[$index] : ''; - } - - $retval['item_type'] = 'PROCEDURE'; - $retval['item_type_toggle'] = 'FUNCTION'; - if (isset($_REQUEST['item_type']) && $_REQUEST['item_type'] == 'FUNCTION') { - $retval['item_type'] = 'FUNCTION'; - $retval['item_type_toggle'] = 'PROCEDURE'; - } - $retval['item_original_type'] = 'PROCEDURE'; - if (isset($_POST['item_original_type']) - && $_POST['item_original_type'] == 'FUNCTION' - ) { - $retval['item_original_type'] = 'FUNCTION'; - } - $retval['item_num_params'] = 0; - $retval['item_param_dir'] = []; - $retval['item_param_name'] = []; - $retval['item_param_type'] = []; - $retval['item_param_length'] = []; - $retval['item_param_opts_num'] = []; - $retval['item_param_opts_text'] = []; - if (isset($_POST['item_param_name']) - && isset($_POST['item_param_type']) - && isset($_POST['item_param_length']) - && isset($_POST['item_param_opts_num']) - && isset($_POST['item_param_opts_text']) - && is_array($_POST['item_param_name']) - && is_array($_POST['item_param_type']) - && is_array($_POST['item_param_length']) - && is_array($_POST['item_param_opts_num']) - && is_array($_POST['item_param_opts_text']) - ) { - if ($_POST['item_type'] == 'PROCEDURE') { - $retval['item_param_dir'] = $_POST['item_param_dir']; - foreach ($retval['item_param_dir'] as $key => $value) { - if (! in_array($value, $param_directions, true)) { - $retval['item_param_dir'][$key] = ''; - } - } - } - $retval['item_param_name'] = $_POST['item_param_name']; - $retval['item_param_type'] = $_POST['item_param_type']; - foreach ($retval['item_param_type'] as $key => $value) { - if (! in_array($value, Util::getSupportedDatatypes(), true)) { - $retval['item_param_type'][$key] = ''; - } - } - $retval['item_param_length'] = $_POST['item_param_length']; - $retval['item_param_opts_num'] = $_POST['item_param_opts_num']; - $retval['item_param_opts_text'] = $_POST['item_param_opts_text']; - $retval['item_num_params'] = max( - count($retval['item_param_name']), - count($retval['item_param_type']), - count($retval['item_param_length']), - count($retval['item_param_opts_num']), - count($retval['item_param_opts_text']) - ); - } - $retval['item_returntype'] = ''; - if (isset($_POST['item_returntype']) - && in_array($_POST['item_returntype'], Util::getSupportedDatatypes()) - ) { - $retval['item_returntype'] = $_POST['item_returntype']; - } - - $retval['item_isdeterministic'] = ''; - if (isset($_POST['item_isdeterministic']) - && mb_strtolower($_POST['item_isdeterministic']) == 'on' - ) { - $retval['item_isdeterministic'] = " checked='checked'"; - } - $retval['item_securitytype_definer'] = ''; - $retval['item_securitytype_invoker'] = ''; - if (isset($_POST['item_securitytype'])) { - if ($_POST['item_securitytype'] === 'DEFINER') { - $retval['item_securitytype_definer'] = " selected='selected'"; - } elseif ($_POST['item_securitytype'] === 'INVOKER') { - $retval['item_securitytype_invoker'] = " selected='selected'"; - } - } - $retval['item_sqldataaccess'] = ''; - if (isset($_POST['item_sqldataaccess']) - && in_array($_POST['item_sqldataaccess'], $param_sqldataaccess, true) - ) { - $retval['item_sqldataaccess'] = $_POST['item_sqldataaccess']; - } - - return $retval; - } - - /** - * This function will generate the values that are required to complete - * the "Edit routine" form given the name of a routine. - * - * @param string $name The name of the routine. - * @param string $type Type of routine (ROUTINE|PROCEDURE) - * @param bool $all Whether to return all data or just the info about parameters. - * - * @return array|bool Data necessary to create the routine editor. - */ - public function getDataFromName($name, $type, $all = true) - { - global $db; - - $retval = []; - - // Build and execute the query - $fields = "SPECIFIC_NAME, ROUTINE_TYPE, DTD_IDENTIFIER, " - . "ROUTINE_DEFINITION, IS_DETERMINISTIC, SQL_DATA_ACCESS, " - . "ROUTINE_COMMENT, SECURITY_TYPE"; - $where = "ROUTINE_SCHEMA " . Util::getCollateForIS() . "=" - . "'" . $this->dbi->escapeString($db) . "' " - . "AND SPECIFIC_NAME='" . $this->dbi->escapeString($name) . "'" - . "AND ROUTINE_TYPE='" . $this->dbi->escapeString($type) . "'"; - $query = "SELECT $fields FROM INFORMATION_SCHEMA.ROUTINES WHERE $where;"; - - $routine = $this->dbi->fetchSingleRow($query, 'ASSOC'); - - if (! $routine) { - return false; - } - - // Get required data - $retval['item_name'] = $routine['SPECIFIC_NAME']; - $retval['item_type'] = $routine['ROUTINE_TYPE']; - - $definition - = $this->dbi->getDefinition( - $db, - $routine['ROUTINE_TYPE'], - $routine['SPECIFIC_NAME'] - ); - - if ($definition === null) { - return false; - } - - $parser = new Parser($definition); - - /** - * @var CreateStatement $stmt - */ - $stmt = $parser->statements[0]; - - $params = Routine::getParameters($stmt); - $retval['item_num_params'] = $params['num']; - $retval['item_param_dir'] = $params['dir']; - $retval['item_param_name'] = $params['name']; - $retval['item_param_type'] = $params['type']; - $retval['item_param_length'] = $params['length']; - $retval['item_param_length_arr'] = $params['length_arr']; - $retval['item_param_opts_num'] = $params['opts']; - $retval['item_param_opts_text'] = $params['opts']; - - // Get extra data - if (! $all) { - return $retval; - } - - if ($retval['item_type'] == 'FUNCTION') { - $retval['item_type_toggle'] = 'PROCEDURE'; - } else { - $retval['item_type_toggle'] = 'FUNCTION'; - } - $retval['item_returntype'] = ''; - $retval['item_returnlength'] = ''; - $retval['item_returnopts_num'] = ''; - $retval['item_returnopts_text'] = ''; - - if (! empty($routine['DTD_IDENTIFIER'])) { - $options = []; - foreach ($stmt->return->options->options as $opt) { - $options[] = is_string($opt) ? $opt : $opt['value']; - } - - $retval['item_returntype'] = $stmt->return->name; - $retval['item_returnlength'] = implode(',', $stmt->return->parameters); - $retval['item_returnopts_num'] = implode(' ', $options); - $retval['item_returnopts_text'] = implode(' ', $options); - } - - $retval['item_definer'] = $stmt->options->has('DEFINER'); - $retval['item_definition'] = $routine['ROUTINE_DEFINITION']; - $retval['item_isdeterministic'] = ''; - if ($routine['IS_DETERMINISTIC'] == 'YES') { - $retval['item_isdeterministic'] = " checked='checked'"; - } - $retval['item_securitytype_definer'] = ''; - $retval['item_securitytype_invoker'] = ''; - if ($routine['SECURITY_TYPE'] == 'DEFINER') { - $retval['item_securitytype_definer'] = " selected='selected'"; - } elseif ($routine['SECURITY_TYPE'] == 'INVOKER') { - $retval['item_securitytype_invoker'] = " selected='selected'"; - } - $retval['item_sqldataaccess'] = $routine['SQL_DATA_ACCESS']; - $retval['item_comment'] = $routine['ROUTINE_COMMENT']; - - return $retval; - } - - /** - * Creates one row for the parameter table used in the routine editor. - * - * @param array $routine Data for the routine returned by - * getDataFromRequest() or getDataFromName() - * @param mixed $index Either a numeric index of the row being processed - * or NULL to create a template row for AJAX request - * @param string $class Class used to hide the direction column, if the - * row is for a stored function. - * - * @return string HTML code of one row of parameter table for the editor. - */ - public function getParameterRow(array $routine = [], $index = null, $class = '') - { - global $param_directions, $param_opts_num; - - if ($index === null) { - // template row for AJAX request - $i = 0; - $index = '%s'; - $drop_class = ''; - $routine = [ - 'item_param_dir' => [0 => ''], - 'item_param_name' => [0 => ''], - 'item_param_type' => [0 => ''], - 'item_param_length' => [0 => ''], - 'item_param_opts_num' => [0 => ''], - 'item_param_opts_text' => [0 => ''], - ]; - } elseif (! empty($routine)) { - // regular row for routine editor - $drop_class = ' hide'; - $i = $index; - } else { - // No input data. This shouldn't happen, - // but better be safe than sorry. - return ''; - } - - $allCharsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']); - $charsets = []; - /** @var Charset $charset */ - foreach ($allCharsets as $charset) { - $charsets[] = [ - 'name' => $charset->getName(), - 'description' => $charset->getDescription(), - 'is_selected' => $charset->getName() === $routine['item_param_opts_text'][$i], - ]; - } - - $template = new Template(); - return $template->render('rte/routines/parameter_row', [ - 'class' => $class, - 'index' => $index, - 'param_directions' => $param_directions, - 'param_opts_num' => $param_opts_num, - 'item_param_dir' => $routine['item_param_dir'][$i] ?? '', - 'item_param_name' => $routine['item_param_name'][$i] ?? '', - 'item_param_length' => $routine['item_param_length'][$i] ?? '', - 'item_param_opts_num' => $routine['item_param_opts_num'][$i] ?? '', - 'supported_datatypes' => Util::getSupportedDatatypes( - true, - $routine['item_param_type'][$i] - ), - 'charsets' => $charsets, - 'drop_class' => $drop_class, - ]); - } - - /** - * Displays a form used to add/edit a routine - * - * @param string $mode If the editor will be used to edit a routine - * or add a new one: 'edit' or 'add'. - * @param string $operation If the editor was previously invoked with - * JS turned off, this will hold the name of - * the current operation - * @param array $routine Data for the routine returned by - * getDataFromRequest() or getDataFromName() - * - * @return string HTML code for the editor. - */ - public function getEditorForm($mode, $operation, array $routine) - { - global $db, $errors, $param_sqldataaccess, $param_opts_num; - - $response = Response::getInstance(); - - // Escape special characters - $need_escape = [ - 'item_original_name', - 'item_name', - 'item_returnlength', - 'item_definition', - 'item_definer', - 'item_comment', - ]; - foreach ($need_escape as $key => $index) { - $routine[$index] = htmlentities($routine[$index], ENT_QUOTES, 'UTF-8'); - } - for ($i = 0; $i < $routine['item_num_params']; $i++) { - $routine['item_param_name'][$i] = htmlentities( - $routine['item_param_name'][$i], - ENT_QUOTES - ); - $routine['item_param_length'][$i] = htmlentities( - $routine['item_param_length'][$i], - ENT_QUOTES - ); - } - - // Handle some logic first - if ($operation == 'change') { - if ($routine['item_type'] == 'PROCEDURE') { - $routine['item_type'] = 'FUNCTION'; - $routine['item_type_toggle'] = 'PROCEDURE'; - } else { - $routine['item_type'] = 'PROCEDURE'; - $routine['item_type_toggle'] = 'FUNCTION'; - } - } elseif ($operation == 'add' - || ($routine['item_num_params'] == 0 && $mode == 'add' && ! $errors) - ) { - $routine['item_param_dir'][] = ''; - $routine['item_param_name'][] = ''; - $routine['item_param_type'][] = ''; - $routine['item_param_length'][] = ''; - $routine['item_param_opts_num'][] = ''; - $routine['item_param_opts_text'][] = ''; - $routine['item_num_params']++; - } elseif ($operation == 'remove') { - unset($routine['item_param_dir'][$routine['item_num_params'] - 1]); - unset($routine['item_param_name'][$routine['item_num_params'] - 1]); - unset($routine['item_param_type'][$routine['item_num_params'] - 1]); - unset($routine['item_param_length'][$routine['item_num_params'] - 1]); - unset($routine['item_param_opts_num'][$routine['item_num_params'] - 1]); - unset($routine['item_param_opts_text'][$routine['item_num_params'] - 1]); - $routine['item_num_params']--; - } - $disableRemoveParam = ''; - if (! $routine['item_num_params']) { - $disableRemoveParam = " class='isdisableremoveparam_class' disabled=disabled"; - } - $original_routine = ''; - if ($mode == 'edit') { - $original_routine = "<input name='item_original_name' " - . "type='hidden' " - . "value='{$routine['item_original_name']}'>\n" - . "<input name='item_original_type' " - . "type='hidden' " - . "value='{$routine['item_original_type']}'>\n"; - } - $isfunction_class = ''; - $isprocedure_class = ''; - $isfunction_select = ''; - $isprocedure_select = ''; - if ($routine['item_type'] == 'PROCEDURE') { - $isfunction_class = ' hide'; - $isprocedure_select = " selected='selected'"; - } else { - $isprocedure_class = ' hide'; - $isfunction_select = " selected='selected'"; - } - - // Create the output - $retval = ""; - $retval .= "<!-- START " . mb_strtoupper($mode) - . " ROUTINE FORM -->\n\n"; - $retval .= "<form class='rte_form' action='db_routines.php' method='post'>\n"; - $retval .= "<input name='{$mode}_item' type='hidden' value='1'>\n"; - $retval .= $original_routine; - $retval .= Url::getHiddenInputs($db) . "\n"; - $retval .= "<fieldset>\n"; - $retval .= "<legend>" . __('Details') . "</legend>\n"; - $retval .= "<table class='rte_table'>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Routine name') . "</td>\n"; - $retval .= " <td><input type='text' name='item_name' maxlength='64'\n"; - $retval .= " value='{$routine['item_name']}'></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Type') . "</td>\n"; - $retval .= " <td>\n"; - if ($response->isAjax()) { - $retval .= " <select name='item_type'>\n" - . "<option value='PROCEDURE'$isprocedure_select>PROCEDURE</option>\n" - . "<option value='FUNCTION'$isfunction_select>FUNCTION</option>\n" - . "</select>\n"; - } else { - $retval .= "<input name='item_type' type='hidden'" - . " value='{$routine['item_type']}'>\n" - . "<div class='font_weight_bold center half_width'>\n" - . $routine['item_type'] . "\n" - . "</div>\n" - . "<input type='submit' name='routine_changetype'\n" - . " value='" . sprintf(__('Change to %s'), $routine['item_type_toggle']) - . "'>\n"; - } - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Parameters') . "</td>\n"; - $retval .= " <td>\n"; - // parameter handling start - $retval .= " <table class='routine_params_table'>\n"; - $retval .= " <thead>\n"; - $retval .= " <tr>\n"; - $retval .= " <td></td>\n"; - $retval .= " <th class='routine_direction_cell$isprocedure_class'>" - . __('Direction') . "</th>\n"; - $retval .= " <th>" . __('Name') . "</th>\n"; - $retval .= " <th>" . __('Type') . "</th>\n"; - $retval .= " <th>" . __('Length/Values') . "</th>\n"; - $retval .= " <th colspan='2'>" . __('Options') . "</th>\n"; - $retval .= " <th class='routine_param_remove hide'> </th>\n"; - $retval .= " </tr>"; - $retval .= " </thead>\n"; - $retval .= " <tbody>\n"; - for ($i = 0; $i < $routine['item_num_params']; $i++) { // each parameter - $retval .= $this->getParameterRow($routine, $i, $isprocedure_class); - } - $retval .= " </tbody>\n"; - $retval .= " </table>"; - $retval .= " </td>"; - $retval .= "</tr>"; - $retval .= "<tr>"; - $retval .= " <td> </td>"; - $retval .= " <td>"; - $retval .= " <input type='button'"; - $retval .= " name='routine_addparameter'"; - $retval .= " value='" . __('Add parameter') . "'>"; - $retval .= " <input " . $disableRemoveParam . ""; - $retval .= " type='submit' "; - $retval .= " name='routine_removeparameter'"; - $retval .= " value='" . __('Remove last parameter') . "'>"; - $retval .= " </td>"; - $retval .= "</tr>"; - // parameter handling end - $retval .= "<tr class='routine_return_row" . $isfunction_class . "'>"; - $retval .= " <td>" . __('Return type') . "</td>"; - $retval .= " <td><select name='item_returntype'>"; - $retval .= Util::getSupportedDatatypes(true, $routine['item_returntype']); - $retval .= " </select></td>"; - $retval .= "</tr>"; - $retval .= "<tr class='routine_return_row" . $isfunction_class . "'>"; - $retval .= " <td>" . __('Return length/values') . "</td>"; - $retval .= " <td><input type='text' name='item_returnlength'"; - $retval .= " value='" . $routine['item_returnlength'] . "'></td>"; - $retval .= " <td class='hide no_len'>---</td>"; - $retval .= "</tr>"; - $retval .= "<tr class='routine_return_row" . $isfunction_class . "'>"; - $retval .= " <td>" . __('Return options') . "</td>"; - $retval .= " <td><div>"; - $retval .= '<select lang="en" dir="ltr" name="item_returnopts_text">' . "\n"; - $retval .= '<option value="">' . __('Charset') . '</option>' . "\n"; - $retval .= '<option value=""></option>' . "\n"; - - $charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']); - /** @var Charset $charset */ - foreach ($charsets as $charset) { - $retval .= '<option value="' . $charset->getName() - . '" title="' . $charset->getDescription() . '"' - . ($routine['item_returnopts_text'] == $charset->getName() ? ' selected' : '') . '>' - . $charset->getName() . '</option>' . "\n"; - } - - $retval .= '</select>' . "\n"; - $retval .= " </div>"; - $retval .= " <div><select name='item_returnopts_num'>"; - $retval .= " <option value=''></option>"; - foreach ($param_opts_num as $key => $value) { - $selected = ""; - if (! empty($routine['item_returnopts_num']) - && $routine['item_returnopts_num'] == $value - ) { - $selected = " selected='selected'"; - } - $retval .= "<option" . $selected . ">" . $value . "</option>"; - } - $retval .= " </select></div>"; - $retval .= " <div class='hide no_opts'>---</div>"; - $retval .= "</td>"; - $retval .= "</tr>"; - $retval .= "<tr>"; - $retval .= " <td>" . __('Definition') . "</td>"; - $retval .= " <td><textarea name='item_definition' rows='15' cols='40'>"; - $retval .= $routine['item_definition']; - $retval .= "</textarea></td>"; - $retval .= "</tr>"; - $retval .= "<tr>"; - $retval .= " <td>" . __('Is deterministic') . "</td>"; - $retval .= " <td><input type='checkbox' name='item_isdeterministic'" - . $routine['item_isdeterministic'] . "></td>"; - $retval .= "</tr>"; - if (isset($_REQUEST['edit_item']) - && ! empty($_REQUEST['edit_item']) - ) { - $retval .= "<tr>"; - $retval .= " <td>" . __('Adjust privileges'); - $retval .= Util::showDocu('faq', 'faq6-39'); - $retval .= "</td>"; - if ($GLOBALS['proc_priv'] - && $GLOBALS['is_reload_priv'] - ) { - $retval .= " <td><input type='checkbox' " - . "name='item_adjust_privileges' value='1' checked></td>"; - } else { - $retval .= " <td><input type='checkbox' " - . "name='item_adjust_privileges' value='1' title='" . __( - "You do not have sufficient privileges to perform this " - . "operation; Please refer to the documentation for more " - . "details" - ) - . "' disabled></td>"; - } - $retval .= "</tr>"; - } - - $retval .= "<tr>"; - $retval .= " <td>" . __('Definer') . "</td>"; - $retval .= " <td><input type='text' name='item_definer'"; - $retval .= " value='" . $routine['item_definer'] . "'></td>"; - $retval .= "</tr>"; - $retval .= "<tr>"; - $retval .= " <td>" . __('Security type') . "</td>"; - $retval .= " <td><select name='item_securitytype'>"; - $retval .= " <option value='DEFINER'" - . $routine['item_securitytype_definer'] . ">DEFINER</option>"; - $retval .= " <option value='INVOKER'" - . $routine['item_securitytype_invoker'] . ">INVOKER</option>"; - $retval .= " </select></td>"; - $retval .= "</tr>"; - $retval .= "<tr>"; - $retval .= " <td>" . __('SQL data access') . "</td>"; - $retval .= " <td><select name='item_sqldataaccess'>"; - foreach ($param_sqldataaccess as $key => $value) { - $selected = ""; - if ($routine['item_sqldataaccess'] == $value) { - $selected = " selected='selected'"; - } - $retval .= " <option" . $selected . ">" . $value . "</option>"; - } - $retval .= " </select></td>"; - $retval .= "</tr>"; - $retval .= "<tr>"; - $retval .= " <td>" . __('Comment') . "</td>"; - $retval .= " <td><input type='text' name='item_comment' maxlength='64'"; - $retval .= " value='" . $routine['item_comment'] . "'></td>"; - $retval .= "</tr>"; - $retval .= "</table>"; - $retval .= "</fieldset>"; - if ($response->isAjax()) { - $retval .= "<input type='hidden' name='editor_process_" . $mode . "'"; - $retval .= " value='true'>"; - $retval .= "<input type='hidden' name='ajax_request' value='true'>"; - } else { - $retval .= "<fieldset class='tblFooters'>"; - $retval .= " <input type='submit' name='editor_process_" . $mode . "'"; - $retval .= " value='" . __('Go') . "'>"; - $retval .= "</fieldset>"; - } - $retval .= "</form>"; - $retval .= "<!-- END " . mb_strtoupper($mode) . " ROUTINE FORM -->"; - - return $retval; - } - - /** - * Composes the query necessary to create a routine from an HTTP request. - * - * @return string The CREATE [ROUTINE | PROCEDURE] query. - */ - public function getQueryFromRequest() - { - global $errors, $param_sqldataaccess, $param_directions, $dbi; - - $_POST['item_type'] = isset($_POST['item_type']) - ? $_POST['item_type'] : ''; - - $query = 'CREATE '; - if (! empty($_POST['item_definer'])) { - if (mb_strpos($_POST['item_definer'], '@') !== false) { - $arr = explode('@', $_POST['item_definer']); - - $do_backquote = true; - if (substr($arr[0], 0, 1) === "`" - && substr($arr[0], -1) === "`" - ) { - $do_backquote = false; - } - $query .= 'DEFINER=' . Util::backquote($arr[0], $do_backquote); - - $do_backquote = true; - if (substr($arr[1], 0, 1) === "`" - && substr($arr[1], -1) === "`" - ) { - $do_backquote = false; - } - $query .= '@' . Util::backquote($arr[1], $do_backquote) . ' '; - } else { - $errors[] = __('The definer must be in the "username@hostname" format!'); - } - } - if ($_POST['item_type'] == 'FUNCTION' - || $_POST['item_type'] == 'PROCEDURE' - ) { - $query .= $_POST['item_type'] . ' '; - } else { - $errors[] = sprintf( - __('Invalid routine type: "%s"'), - htmlspecialchars($_POST['item_type']) - ); - } - if (! empty($_POST['item_name'])) { - $query .= Util::backquote($_POST['item_name']); - } else { - $errors[] = __('You must provide a routine name!'); - } - $params = ''; - $warned_about_dir = false; - $warned_about_length = false; - - if (! empty($_POST['item_param_name']) - && ! empty($_POST['item_param_type']) - && ! empty($_POST['item_param_length']) - && is_array($_POST['item_param_name']) - && is_array($_POST['item_param_type']) - && is_array($_POST['item_param_length']) - ) { - $item_param_name = $_POST['item_param_name']; - $item_param_type = $_POST['item_param_type']; - $item_param_length = $_POST['item_param_length']; - - for ($i = 0, $nb = count($item_param_name); $i < $nb; $i++) { - if (! empty($item_param_name[$i]) - && ! empty($item_param_type[$i]) - ) { - if ($_POST['item_type'] == 'PROCEDURE' - && ! empty($_POST['item_param_dir'][$i]) - && in_array($_POST['item_param_dir'][$i], $param_directions) - ) { - $params .= $_POST['item_param_dir'][$i] . " " - . Util::backquote($item_param_name[$i]) - . " " . $item_param_type[$i]; - } elseif ($_POST['item_type'] == 'FUNCTION') { - $params .= Util::backquote($item_param_name[$i]) - . " " . $item_param_type[$i]; - } elseif (! $warned_about_dir) { - $warned_about_dir = true; - $errors[] = sprintf( - __('Invalid direction "%s" given for parameter.'), - htmlspecialchars($_POST['item_param_dir'][$i]) - ); - } - if ($item_param_length[$i] != '' - && ! preg_match( - '@^(DATE|TINYBLOB|TINYTEXT|BLOB|TEXT|' - . 'MEDIUMBLOB|MEDIUMTEXT|LONGBLOB|LONGTEXT|' - . 'SERIAL|BOOLEAN)$@i', - $item_param_type[$i] - ) - ) { - $params .= "(" . $item_param_length[$i] . ")"; - } elseif ($item_param_length[$i] == '' - && preg_match( - '@^(ENUM|SET|VARCHAR|VARBINARY)$@i', - $item_param_type[$i] - ) - ) { - if (! $warned_about_length) { - $warned_about_length = true; - $errors[] = __( - 'You must provide length/values for routine parameters' - . ' of type ENUM, SET, VARCHAR and VARBINARY.' - ); - } - } - if (! empty($_POST['item_param_opts_text'][$i])) { - if ($dbi->types->getTypeClass($item_param_type[$i]) == 'CHAR') { - if (! in_array($item_param_type[$i], ['VARBINARY', 'BINARY'])) { - $params .= ' CHARSET ' - . mb_strtolower( - $_POST['item_param_opts_text'][$i] - ); - } - } - } - if (! empty($_POST['item_param_opts_num'][$i])) { - if ($dbi->types->getTypeClass($item_param_type[$i]) == 'NUMBER') { - $params .= ' ' - . mb_strtoupper( - $_POST['item_param_opts_num'][$i] - ); - } - } - if ($i != (count($item_param_name) - 1)) { - $params .= ", "; - } - } else { - $errors[] = __( - 'You must provide a name and a type for each routine parameter.' - ); - break; - } - } - } - $query .= "(" . $params . ") "; - if ($_POST['item_type'] == 'FUNCTION') { - $item_returntype = isset($_POST['item_returntype']) - ? $_POST['item_returntype'] - : null; - - if (! empty($item_returntype) - && in_array( - $item_returntype, - Util::getSupportedDatatypes() - ) - ) { - $query .= "RETURNS " . $item_returntype; - } else { - $errors[] = __('You must provide a valid return type for the routine.'); - } - if (! empty($_POST['item_returnlength']) - && ! preg_match( - '@^(DATE|DATETIME|TIME|TINYBLOB|TINYTEXT|BLOB|TEXT|' - . 'MEDIUMBLOB|MEDIUMTEXT|LONGBLOB|LONGTEXT|SERIAL|BOOLEAN)$@i', - $item_returntype - ) - ) { - $query .= "(" . $_POST['item_returnlength'] . ")"; - } elseif (empty($_POST['item_returnlength']) - && preg_match( - '@^(ENUM|SET|VARCHAR|VARBINARY)$@i', - $item_returntype - ) - ) { - if (! $warned_about_length) { - $errors[] = __( - 'You must provide length/values for routine parameters' - . ' of type ENUM, SET, VARCHAR and VARBINARY.' - ); - } - } - if (! empty($_POST['item_returnopts_text'])) { - if ($dbi->types->getTypeClass($item_returntype) == 'CHAR') { - $query .= ' CHARSET ' - . mb_strtolower($_POST['item_returnopts_text']); - } - } - if (! empty($_POST['item_returnopts_num'])) { - if ($dbi->types->getTypeClass($item_returntype) == 'NUMBER') { - $query .= ' ' - . mb_strtoupper($_POST['item_returnopts_num']); - } - } - $query .= ' '; - } - if (! empty($_POST['item_comment'])) { - $query .= "COMMENT '" . $this->dbi->escapeString($_POST['item_comment']) - . "' "; - } - if (isset($_POST['item_isdeterministic'])) { - $query .= 'DETERMINISTIC '; - } else { - $query .= 'NOT DETERMINISTIC '; - } - if (! empty($_POST['item_sqldataaccess']) - && in_array($_POST['item_sqldataaccess'], $param_sqldataaccess) - ) { - $query .= $_POST['item_sqldataaccess'] . ' '; - } - if (! empty($_POST['item_securitytype'])) { - if ($_POST['item_securitytype'] == 'DEFINER' - || $_POST['item_securitytype'] == 'INVOKER' - ) { - $query .= 'SQL SECURITY ' . $_POST['item_securitytype'] . ' '; - } - } - if (! empty($_POST['item_definition'])) { - $query .= $_POST['item_definition']; - } else { - $errors[] = __('You must provide a routine definition.'); - } - - return $query; - } - - /** - * Handles requests for executing a routine - * - * @return void - */ - public function handleExecute() - { - global $db; - - $response = Response::getInstance(); - - /** - * Handle all user requests other than the default of listing routines - */ - if (! empty($_POST['execute_routine']) && ! empty($_POST['item_name'])) { - // Build the queries - $routine = $this->getDataFromName( - $_POST['item_name'], - $_POST['item_type'], - false - ); - if ($routine === false) { - $message = __('Error in processing request:') . ' '; - $message .= sprintf( - $this->words->get('not_found'), - htmlspecialchars(Util::backquote($_POST['item_name'])), - htmlspecialchars(Util::backquote($db)) - ); - $message = Message::error($message); - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $message); - exit; - } else { - echo $message->getDisplay(); - unset($_POST); - } - } - - $queries = []; - $end_query = []; - $args = []; - $all_functions = $this->dbi->types->getAllFunctions(); - for ($i = 0; $i < $routine['item_num_params']; $i++) { - if (isset($_POST['params'][$routine['item_param_name'][$i]])) { - $value = $_POST['params'][$routine['item_param_name'][$i]]; - if (is_array($value)) { // is SET type - $value = implode(',', $value); - } - $value = $this->dbi->escapeString($value); - if (! empty($_POST['funcs'][$routine['item_param_name'][$i]]) - && in_array( - $_POST['funcs'][$routine['item_param_name'][$i]], - $all_functions - ) - ) { - $queries[] = "SET @p$i=" - . $_POST['funcs'][$routine['item_param_name'][$i]] - . "('$value');\n"; - } else { - $queries[] = "SET @p$i='$value';\n"; - } - $args[] = "@p$i"; - } else { - $args[] = "@p$i"; - } - if ($routine['item_type'] == 'PROCEDURE') { - if ($routine['item_param_dir'][$i] == 'OUT' - || $routine['item_param_dir'][$i] == 'INOUT' - ) { - $end_query[] = "@p$i AS " - . Util::backquote($routine['item_param_name'][$i]); - } - } - } - if ($routine['item_type'] == 'PROCEDURE') { - $queries[] = "CALL " . Util::backquote($routine['item_name']) - . "(" . implode(', ', $args) . ");\n"; - if (count($end_query)) { - $queries[] = "SELECT " . implode(', ', $end_query) . ";\n"; - } - } else { - $queries[] = "SELECT " . Util::backquote($routine['item_name']) - . "(" . implode(', ', $args) . ") " - . "AS " . Util::backquote($routine['item_name']) - . ";\n"; - } - - // Get all the queries as one SQL statement - $multiple_query = implode("", $queries); - - $outcome = true; - $affected = 0; - - // Execute query - if (! $this->dbi->tryMultiQuery($multiple_query)) { - $outcome = false; - } - - // Generate output - if ($outcome) { - // Pass the SQL queries through the "pretty printer" - $output = Util::formatSql(implode($queries, "\n")); - - // Display results - $output .= "<fieldset><legend>"; - $output .= sprintf( - __('Execution results of routine %s'), - Util::backquote(htmlspecialchars($routine['item_name'])) - ); - $output .= "</legend>"; - - $nbResultsetToDisplay = 0; - - do { - $result = $this->dbi->storeResult(); - $num_rows = $this->dbi->numRows($result); - - if (($result !== false) && ($num_rows > 0)) { - $output .= "<table><tr>"; - foreach ($this->dbi->getFieldsMeta($result) as $field) { - $output .= "<th>"; - $output .= htmlspecialchars($field->name); - $output .= "</th>"; - } - $output .= "</tr>"; - - while ($row = $this->dbi->fetchAssoc($result)) { - $output .= "<tr>" . $this->browseRow($row) . "</tr>"; - } - - $output .= "</table>"; - $nbResultsetToDisplay++; - $affected = $num_rows; - } - - if (! $this->dbi->moreResults()) { - break; - } - - $output .= "<br>"; - - $this->dbi->freeResult($result); - } while ($outcome = $this->dbi->nextResult()); - } - - if ($outcome) { - $output .= "</fieldset>"; - - $message = __('Your SQL query has been executed successfully.'); - if ($routine['item_type'] == 'PROCEDURE') { - $message .= '<br>'; - - // TODO : message need to be modified according to the - // output from the routine - $message .= sprintf( - _ngettext( - '%d row affected by the last statement inside the ' - . 'procedure.', - '%d rows affected by the last statement inside the ' - . 'procedure.', - $affected - ), - $affected - ); - } - $message = Message::success($message); - - if ($nbResultsetToDisplay == 0) { - $notice = __( - 'MySQL returned an empty result set (i.e. zero rows).' - ); - $output .= Message::notice($notice)->getDisplay(); - } - } else { - $output = ''; - $message = Message::error( - sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($multiple_query) - ) - . '<br><br>' - . __('MySQL said: ') . $this->dbi->getError() - ); - } - - // Print/send output - if ($response->isAjax()) { - $response->setRequestStatus($message->isSuccess()); - $response->addJSON('message', $message->getDisplay() . $output); - $response->addJSON('dialog', false); - exit; - } else { - echo $message->getDisplay() , $output; - if ($message->isError()) { - // At least one query has failed, so shouldn't - // execute any more queries, so we quit. - exit; - } - unset($_POST); - // Now deliberately fall through to displaying the routines list - } - return; - } elseif (! empty($_GET['execute_dialog']) && ! empty($_GET['item_name'])) { - /** - * Display the execute form for a routine. - */ - $routine = $this->getDataFromName( - $_GET['item_name'], - $_GET['item_type'], - true - ); - if ($routine !== false) { - $form = $this->getExecuteForm($routine); - if ($response->isAjax()) { - $title = __("Execute routine") . " " . Util::backquote( - htmlentities($_GET['item_name'], ENT_QUOTES) - ); - $response->addJSON('message', $form); - $response->addJSON('title', $title); - $response->addJSON('dialog', true); - } else { - echo "\n\n<h2>" . __("Execute routine") . "</h2>\n\n"; - echo $form; - } - exit; - } elseif ($response->isAjax()) { - $message = __('Error in processing request:') . ' '; - $message .= sprintf( - $this->words->get('not_found'), - htmlspecialchars(Util::backquote($_GET['item_name'])), - htmlspecialchars(Util::backquote($db)) - ); - $message = Message::error($message); - - $response->setRequestStatus(false); - $response->addJSON('message', $message); - exit; - } - } - } - - /** - * Browse row array - * - * @param array $row Columns - * - * @return string - */ - private function browseRow(array $row) - { - $output = null; - foreach ($row as $value) { - if ($value === null) { - $value = '<i>NULL</i>'; - } else { - $value = htmlspecialchars($value); - } - $output .= "<td>" . $value . "</td>"; - } - return $output; - } - - /** - * Creates the HTML code that shows the routine execution dialog. - * - * @param array $routine Data for the routine returned by - * getDataFromName() - * - * @return string HTML code for the routine execution dialog. - */ - public function getExecuteForm(array $routine) - { - global $db, $cfg; - - $response = Response::getInstance(); - - // Escape special characters - $routine['item_name'] = htmlentities($routine['item_name'], ENT_QUOTES); - for ($i = 0; $i < $routine['item_num_params']; $i++) { - $routine['item_param_name'][$i] = htmlentities( - $routine['item_param_name'][$i], - ENT_QUOTES - ); - } - - // Create the output - $retval = ""; - $retval .= "<!-- START ROUTINE EXECUTE FORM -->\n\n"; - $retval .= "<form action='db_routines.php' method='post'\n"; - $retval .= " class='rte_form ajax' onsubmit='return false'>\n"; - $retval .= "<input type='hidden' name='item_name'\n"; - $retval .= " value='{$routine['item_name']}'>\n"; - $retval .= "<input type='hidden' name='item_type'\n"; - $retval .= " value='{$routine['item_type']}'>\n"; - $retval .= Url::getHiddenInputs($db) . "\n"; - $retval .= "<fieldset>\n"; - if (! $response->isAjax()) { - $retval .= "<legend>{$routine['item_name']}</legend>\n"; - $retval .= "<table class='rte_table'>\n"; - $retval .= "<caption class='tblHeaders'>\n"; - $retval .= __('Routine parameters'); - $retval .= "</caption>\n"; - } else { - $retval .= "<legend>" . __('Routine parameters') . "</legend>\n"; - $retval .= "<table class='rte_table'>\n"; - } - $retval .= "<tr>\n"; - $retval .= "<th>" . __('Name') . "</th>\n"; - $retval .= "<th>" . __('Type') . "</th>\n"; - if ($cfg['ShowFunctionFields']) { - $retval .= "<th>" . __('Function') . "</th>\n"; - } - $retval .= "<th>" . __('Value') . "</th>\n"; - $retval .= "</tr>\n"; - // Get a list of data types that are not yet supported. - $no_support_types = Util::unsupportedDatatypes(); - for ($i = 0; $i < $routine['item_num_params']; $i++) { // Each parameter - if ($routine['item_type'] == 'PROCEDURE' - && $routine['item_param_dir'][$i] == 'OUT' - ) { - continue; - } - $retval .= "\n<tr>\n"; - $retval .= "<td>{$routine['item_param_name'][$i]}</td>\n"; - $retval .= "<td>{$routine['item_param_type'][$i]}</td>\n"; - if ($cfg['ShowFunctionFields']) { - $retval .= "<td>\n"; - if (false !== stripos($routine['item_param_type'][$i], 'enum') - || false !== stripos($routine['item_param_type'][$i], 'set') - || in_array( - mb_strtolower($routine['item_param_type'][$i]), - $no_support_types - ) - ) { - $retval .= "--\n"; - } else { - $field = [ - 'True_Type' => mb_strtolower( - $routine['item_param_type'][$i] - ), - 'Type' => '', - 'Key' => '', - 'Field' => '', - 'Default' => '', - 'first_timestamp' => false, - ]; - $retval .= "<select name='funcs[" - . $routine['item_param_name'][$i] . "]'>"; - $retval .= Util::getFunctionsForField($field, false, []); - $retval .= "</select>"; - } - $retval .= "</td>\n"; - } - // Append a class to date/time fields so that - // jQuery can attach a datepicker to them - $class = ''; - if ($routine['item_param_type'][$i] == 'DATETIME' - || $routine['item_param_type'][$i] == 'TIMESTAMP' - ) { - $class = 'datetimefield'; - } elseif ($routine['item_param_type'][$i] == 'DATE') { - $class = 'datefield'; - } - $retval .= "<td class='nowrap'>\n"; - if (in_array($routine['item_param_type'][$i], ['ENUM', 'SET'])) { - if ($routine['item_param_type'][$i] == 'ENUM') { - $input_type = 'radio'; - } else { - $input_type = 'checkbox'; - } - foreach ($routine['item_param_length_arr'][$i] as $value) { - $value = htmlentities(Util::unQuote($value), ENT_QUOTES); - $retval .= "<input name='params[" - . $routine['item_param_name'][$i] . "][]' " - . "value='" . $value . "' type='" - . $input_type . "'>" - . $value . "<br>\n"; - } - } elseif (in_array( - mb_strtolower($routine['item_param_type'][$i]), - $no_support_types - )) { - $retval .= "\n"; - } else { - $retval .= "<input class='$class' type='text' name='params[" - . $routine['item_param_name'][$i] . "]'>\n"; - } - $retval .= "</td>\n"; - $retval .= "</tr>\n"; - } - $retval .= "\n</table>\n"; - if (! $response->isAjax()) { - $retval .= "</fieldset>\n\n"; - $retval .= "<fieldset class='tblFooters'>\n"; - $retval .= " <input type='submit' name='execute_routine'\n"; - $retval .= " value='" . __('Go') . "'>\n"; - $retval .= "</fieldset>\n"; - } else { - $retval .= "<input type='hidden' name='execute_routine' value='true'>"; - $retval .= "<input type='hidden' name='ajax_request' value='true'>"; - } - $retval .= "</form>\n\n"; - $retval .= "<!-- END ROUTINE EXECUTE FORM -->\n\n"; - - return $retval; - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/RteList.php b/srcs/phpmyadmin/libraries/classes/Rte/RteList.php deleted file mode 100644 index 0e87c6c..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/RteList.php +++ /dev/null @@ -1,518 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Common functions for generating lists of Routines, Triggers and Events. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Response; -use PhpMyAdmin\SqlParser\Parser; -use PhpMyAdmin\SqlParser\Statements\CreateStatement; -use PhpMyAdmin\SqlParser\Utils\Routine; -use PhpMyAdmin\Template; -use PhpMyAdmin\Url; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\RteList class - * - * @package PhpMyAdmin - */ -class RteList -{ - /** - * @var Words - */ - private $words; - - /** - * @var Template - */ - public $template; - - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * RteList constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - $this->words = new Words(); - $this->template = new Template(); - } - - /** - * Creates a list of items containing the relevant - * information and some action links. - * - * @param string $type One of ['routine'|'trigger'|'event'] - * @param array $items An array of items - * - * @return string HTML code of the list of items - */ - public function get($type, array $items) - { - global $table; - - /** - * Conditional classes switch the list on or off - */ - $class1 = 'hide'; - $class2 = ''; - if (! $items) { - $class1 = ''; - $class2 = ' hide'; - } - /** - * Generate output - */ - $retval = "<!-- LIST OF " . $this->words->get('docu') . " START -->\n"; - $retval .= '<form id="rteListForm" class="ajax" action="'; - switch ($type) { - case 'routine': - $retval .= 'db_routines.php'; - break; - case 'trigger': - if (! empty($table)) { - $retval .= 'tbl_triggers.php'; - } else { - $retval .= 'db_triggers.php'; - } - break; - case 'event': - $retval .= 'db_events.php'; - break; - default: - break; - } - $retval .= '">'; - $retval .= Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table']); - $retval .= "<fieldset>\n"; - $retval .= " <legend>\n"; - $retval .= " " . $this->words->get('title') . "\n"; - $retval .= " " - . Util::showMySQLDocu($this->words->get('docu')) . "\n"; - $retval .= " </legend>\n"; - $retval .= " <div class='$class1' id='nothing2display'>\n"; - $retval .= " " . $this->words->get('nothing') . "\n"; - $retval .= " </div>\n"; - $retval .= " <table class='data$class2'>\n"; - $retval .= " <!-- TABLE HEADERS -->\n"; - $retval .= " <tr>\n"; - // th cells with a colspan need corresponding td cells, according to W3C - switch ($type) { - case 'routine': - $retval .= " <th></th>\n"; - $retval .= " <th>" . __('Name') . "</th>\n"; - $retval .= " <th colspan='4'>" . __('Action') . "</th>\n"; - $retval .= " <th>" . __('Type') . "</th>\n"; - $retval .= " <th>" . __('Returns') . "</th>\n"; - $retval .= " </tr>\n"; - $retval .= " <tr class='hide'>\n"; // see comment above - for ($i = 0; $i < 7; $i++) { - $retval .= " <td></td>\n"; - } - break; - case 'trigger': - $retval .= " <th></th>\n"; - $retval .= " <th>" . __('Name') . "</th>\n"; - if (empty($table)) { - $retval .= " <th>" . __('Table') . "</th>\n"; - } - $retval .= " <th colspan='3'>" . __('Action') . "</th>\n"; - $retval .= " <th>" . __('Time') . "</th>\n"; - $retval .= " <th>" . __('Event') . "</th>\n"; - $retval .= " </tr>\n"; - $retval .= " <tr class='hide'>\n"; // see comment above - for ($i = 0; $i < (empty($table) ? 7 : 6); $i++) { - $retval .= " <td></td>\n"; - } - break; - case 'event': - $retval .= " <th></th>\n"; - $retval .= " <th>" . __('Name') . "</th>\n"; - $retval .= " <th>" . __('Status') . "</th>\n"; - $retval .= " <th colspan='3'>" . __('Action') . "</th>\n"; - $retval .= " <th>" . __('Type') . "</th>\n"; - $retval .= " </tr>\n"; - $retval .= " <tr class='hide'>\n"; // see comment above - for ($i = 0; $i < 6; $i++) { - $retval .= " <td></td>\n"; - } - break; - default: - break; - } - $retval .= " </tr>\n"; - $retval .= " <!-- TABLE DATA -->\n"; - $response = Response::getInstance(); - foreach ($items as $item) { - if ($response->isAjax() && empty($_REQUEST['ajax_page_request'])) { - $rowclass = 'ajaxInsert hide'; - } else { - $rowclass = ''; - } - // Get each row from the correct function - switch ($type) { - case 'routine': - $retval .= $this->getRoutineRow($item, $rowclass); - break; - case 'trigger': - $retval .= $this->getTriggerRow($item, $rowclass); - break; - case 'event': - $retval .= $this->getEventRow($item, $rowclass); - break; - default: - break; - } - } - $retval .= " </table>\n"; - - if (count($items)) { - $retval .= '<div class="withSelected">'; - $retval .= $this->template->render('select_all', [ - 'pma_theme_image' => $GLOBALS['pmaThemeImage'], - 'text_dir' => $GLOBALS['text_dir'], - 'form_name' => 'rteListForm', - ]); - $retval .= Util::getButtonOrImage( - 'submit_mult', - 'mult_submit', - __('Export'), - 'b_export', - 'export' - ); - $retval .= Util::getButtonOrImage( - 'submit_mult', - 'mult_submit', - __('Drop'), - 'b_drop', - 'drop' - ); - $retval .= '</div>'; - } - - $retval .= "</fieldset>\n"; - $retval .= "</form>\n"; - $retval .= "<!-- LIST OF " . $this->words->get('docu') . " END -->\n"; - - return $retval; - } - - /** - * Creates the contents for a row in the list of routines - * - * @param array $routine An array of routine data - * @param string $rowclass Additional class - * - * @return string HTML code of a row for the list of routines - */ - public function getRoutineRow(array $routine, $rowclass = '') - { - global $url_query, $db, $titles; - - $sql_drop = sprintf( - 'DROP %s IF EXISTS %s', - $routine['type'], - Util::backquote($routine['name']) - ); - $type_link = "item_type={$routine['type']}"; - - $retval = " <tr class='$rowclass'>\n"; - $retval .= " <td>\n"; - $retval .= ' <input type="checkbox"' - . ' class="checkall" name="item_name[]"' - . ' value="' . htmlspecialchars($routine['name']) . '">'; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " <span class='drop_sql hide'>" - . htmlspecialchars($sql_drop) . "</span>\n"; - $retval .= " <strong>\n"; - $retval .= " " - . htmlspecialchars($routine['name']) . "\n"; - $retval .= " </strong>\n"; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - - // this is for our purpose to decide whether to - // show the edit link or not, so we need the DEFINER for the routine - $where = "ROUTINE_SCHEMA " . Util::getCollateForIS() . "=" - . "'" . $this->dbi->escapeString($db) . "' " - . "AND SPECIFIC_NAME='" . $this->dbi->escapeString($routine['name']) . "'" - . "AND ROUTINE_TYPE='" . $this->dbi->escapeString($routine['type']) . "'"; - $query = "SELECT `DEFINER` FROM INFORMATION_SCHEMA.ROUTINES WHERE $where;"; - $routine_definer = $this->dbi->fetchValue($query); - - $curr_user = $this->dbi->getCurrentUser(); - - // Since editing a procedure involved dropping and recreating, check also for - // CREATE ROUTINE privilege to avoid lost procedures. - if ((Util::currentUserHasPrivilege('CREATE ROUTINE', $db) - && $curr_user == $routine_definer) - || $this->dbi->isSuperuser() - ) { - $retval .= ' <a class="ajax edit_anchor"' - . ' href="db_routines.php' - . $url_query - . '&edit_item=1' - . '&item_name=' - . urlencode($routine['name']) - . '&' . $type_link - . '">' . $titles['Edit'] . "</a>\n"; - } else { - $retval .= " {$titles['NoEdit']}\n"; - } - $retval .= " </td>\n"; - $retval .= " <td>\n"; - - // There is a problem with Util::currentUserHasPrivilege(): - // it does not detect all kinds of privileges, for example - // a direct privilege on a specific routine. So, at this point, - // we show the Execute link, hoping that the user has the correct rights. - // Also, information_schema might be hiding the ROUTINE_DEFINITION - // but a routine with no input parameters can be nonetheless executed. - - // Check if the routine has any input parameters. If it does, - // we will show a dialog to get values for these parameters, - // otherwise we can execute it directly. - - $definition = $this->dbi->getDefinition( - $db, - $routine['type'], - $routine['name'] - ); - if ($definition !== null) { - $parser = new Parser($definition); - - /** - * @var CreateStatement $stmt - */ - $stmt = $parser->statements[0]; - - $params = Routine::getParameters($stmt); - - if (Util::currentUserHasPrivilege('EXECUTE', $db)) { - $execute_action = 'execute_routine'; - for ($i = 0; $i < $params['num']; $i++) { - if ($routine['type'] == 'PROCEDURE' - && $params['dir'][$i] == 'OUT' - ) { - continue; - } - $execute_action = 'execute_dialog'; - break; - } - $query_part = $execute_action . '=1&item_name=' - . urlencode($routine['name']) . '&' . $type_link; - $retval .= ' <a class="ajax exec_anchor"' - . ' href="db_routines.php' - . $url_query - . ($execute_action == 'execute_routine' - ? '" data-post="' . $query_part - : '&' . $query_part) - . '">' . $titles['Execute'] . "</a>\n"; - } else { - $retval .= " {$titles['NoExecute']}\n"; - } - } - - $retval .= " </td>\n"; - $retval .= " <td>\n"; - if ((Util::currentUserHasPrivilege('CREATE ROUTINE', $db) - && $curr_user == $routine_definer) - || $this->dbi->isSuperuser() - ) { - $retval .= ' <a class="ajax export_anchor"' - . ' href="db_routines.php' - . $url_query - . '&export_item=1' - . '&item_name=' - . urlencode($routine['name']) - . '&' . $type_link - . '">' . $titles['Export'] . "</a>\n"; - } else { - $retval .= " {$titles['NoExport']}\n"; - } - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= Util::linkOrButton( - 'sql.php' . $url_query . '&sql_query=' . urlencode($sql_drop) . '&goto=db_routines.php' . urlencode("?db={$db}"), - $titles['Drop'], - ['class' => 'ajax drop_anchor'] - ); - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " {$routine['type']}\n"; - $retval .= " </td>\n"; - $retval .= " <td dir=\"ltr\">\n"; - $retval .= " " - . htmlspecialchars($routine['returns']) . "\n"; - $retval .= " </td>\n"; - $retval .= " </tr>\n"; - - return $retval; - } - - /** - * Creates the contents for a row in the list of triggers - * - * @param array $trigger An array of routine data - * @param string $rowclass Additional class - * - * @return string HTML code of a cell for the list of triggers - */ - public function getTriggerRow(array $trigger, $rowclass = '') - { - global $url_query, $db, $table, $titles; - - $retval = " <tr class='$rowclass'>\n"; - $retval .= " <td>\n"; - $retval .= ' <input type="checkbox"' - . ' class="checkall" name="item_name[]"' - . ' value="' . htmlspecialchars($trigger['name']) . '">'; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " <span class='drop_sql hide'>" - . htmlspecialchars($trigger['drop']) . "</span>\n"; - $retval .= " <strong>\n"; - $retval .= " " . htmlspecialchars($trigger['name']) . "\n"; - $retval .= " </strong>\n"; - $retval .= " </td>\n"; - if (empty($table)) { - $retval .= " <td>\n"; - $retval .= "<a href='db_triggers.php{$url_query}" - . "&table=" . urlencode($trigger['table']) . "'>" - . htmlspecialchars($trigger['table']) . "</a>"; - $retval .= " </td>\n"; - } - $retval .= " <td>\n"; - if (Util::currentUserHasPrivilege('TRIGGER', $db, $table)) { - $retval .= ' <a class="ajax edit_anchor"' - . ' href="db_triggers.php' - . $url_query - . '&edit_item=1' - . '&item_name=' - . urlencode($trigger['name']) - . '">' . $titles['Edit'] . "</a>\n"; - } else { - $retval .= " {$titles['NoEdit']}\n"; - } - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= ' <a class="ajax export_anchor"' - . ' href="db_triggers.php' - . $url_query - . '&export_item=1' - . '&item_name=' - . urlencode($trigger['name']) - . '">' . $titles['Export'] . "</a>\n"; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - if (Util::currentUserHasPrivilege('TRIGGER', $db)) { - $retval .= Util::linkOrButton( - 'sql.php' . $url_query . '&sql_query=' . urlencode($trigger['drop']) . '&goto=db_triggers.php' . urlencode("?db={$db}"), - $titles['Drop'], - ['class' => 'ajax drop_anchor'] - ); - } else { - $retval .= " {$titles['NoDrop']}\n"; - } - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " {$trigger['action_timing']}\n"; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " {$trigger['event_manipulation']}\n"; - $retval .= " </td>\n"; - $retval .= " </tr>\n"; - - return $retval; - } - - /** - * Creates the contents for a row in the list of events - * - * @param array $event An array of routine data - * @param string $rowclass Additional class - * - * @return string HTML code of a cell for the list of events - */ - public function getEventRow(array $event, $rowclass = '') - { - global $url_query, $db, $titles; - - $sql_drop = sprintf( - 'DROP EVENT IF EXISTS %s', - Util::backquote($event['name']) - ); - - $retval = " <tr class='$rowclass'>\n"; - $retval .= " <td>\n"; - $retval .= ' <input type="checkbox"' - . ' class="checkall" name="item_name[]"' - . ' value="' . htmlspecialchars($event['name']) . '">'; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " <span class='drop_sql hide'>" - . htmlspecialchars($sql_drop) . "</span>\n"; - $retval .= " <strong>\n"; - $retval .= " " - . htmlspecialchars($event['name']) . "\n"; - $retval .= " </strong>\n"; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " {$event['status']}\n"; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - if (Util::currentUserHasPrivilege('EVENT', $db)) { - $retval .= ' <a class="ajax edit_anchor"' - . ' href="db_events.php' - . $url_query - . '&edit_item=1' - . '&item_name=' - . urlencode($event['name']) - . '">' . $titles['Edit'] . "</a>\n"; - } else { - $retval .= " {$titles['NoEdit']}\n"; - } - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= ' <a class="ajax export_anchor"' - . ' href="db_events.php' - . $url_query - . '&export_item=1' - . '&item_name=' - . urlencode($event['name']) - . '">' . $titles['Export'] . "</a>\n"; - $retval .= " </td>\n"; - $retval .= " <td>\n"; - if (Util::currentUserHasPrivilege('EVENT', $db)) { - $retval .= Util::linkOrButton( - 'sql.php' . $url_query . '&sql_query=' . urlencode($sql_drop) . '&goto=db_events.php' . urlencode("?db={$db}"), - $titles['Drop'], - ['class' => 'ajax drop_anchor'] - ); - } else { - $retval .= " {$titles['NoDrop']}\n"; - } - $retval .= " </td>\n"; - $retval .= " <td>\n"; - $retval .= " {$event['type']}\n"; - $retval .= " </td>\n"; - $retval .= " </tr>\n"; - - return $retval; - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/Triggers.php b/srcs/phpmyadmin/libraries/classes/Rte/Triggers.php deleted file mode 100644 index 45ce02f..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/Triggers.php +++ /dev/null @@ -1,527 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Functions for trigger management. - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Message; -use PhpMyAdmin\Response; -use PhpMyAdmin\Url; -use PhpMyAdmin\Util; - -/** - * PhpMyAdmin\Rte\Triggers class - * - * @package PhpMyAdmin - */ -class Triggers -{ - /** - * @var Export - */ - private $export; - - /** - * @var Footer - */ - private $footer; - - /** - * @var General - */ - private $general; - - /** - * @var RteList - */ - private $rteList; - - /** - * @var Words - */ - private $words; - - /** - * @var DatabaseInterface - */ - private $dbi; - - /** - * Triggers constructor. - * - * @param DatabaseInterface $dbi DatabaseInterface object - */ - public function __construct(DatabaseInterface $dbi) - { - $this->dbi = $dbi; - $this->export = new Export($this->dbi); - $this->footer = new Footer($this->dbi); - $this->general = new General($this->dbi); - $this->rteList = new RteList($this->dbi); - $this->words = new Words(); - } - - /** - * Sets required globals - * - * @return void - */ - public function setGlobals() - { - global $action_timings, $event_manipulations; - - // Some definitions for triggers - $action_timings = [ - 'BEFORE', - 'AFTER', - ]; - $event_manipulations = [ - 'INSERT', - 'UPDATE', - 'DELETE', - ]; - } - - /** - * Main function for the triggers functionality - * - * @return void - */ - public function main() - { - global $db, $table; - - $this->setGlobals(); - /** - * Process all requests - */ - $this->handleEditor(); - $this->export->triggers(); - /** - * Display a list of available triggers - */ - $items = $this->dbi->getTriggers($db, $table); - echo $this->rteList->get('trigger', $items); - /** - * Display a link for adding a new trigger, - * if the user has the necessary privileges - */ - echo $this->footer->triggers(); - } - - /** - * Handles editor requests for adding or editing an item - * - * @return void - */ - public function handleEditor() - { - global $errors, $db, $table; - - if (! empty($_POST['editor_process_add']) - || ! empty($_POST['editor_process_edit']) - ) { - $sql_query = ''; - - $item_query = $this->getQueryFromRequest(); - - if (! count($errors)) { // set by PhpMyAdmin\Rte\Routines::getQueryFromRequest() - // Execute the created query - if (! empty($_POST['editor_process_edit'])) { - // Backup the old trigger, in case something goes wrong - $trigger = $this->getDataFromName($_POST['item_original_name']); - $create_item = $trigger['create']; - $drop_item = $trigger['drop'] . ';'; - $result = $this->dbi->tryQuery($drop_item); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($drop_item) - ) - . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - } else { - $result = $this->dbi->tryQuery($item_query); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($item_query) - ) - . '<br>' - . __('MySQL said: ') . $this->dbi->getError(); - // We dropped the old item, but were unable to create the - // new one. Try to restore the backup query. - $result = $this->dbi->tryQuery($create_item); - - $errors = $this->general->checkResult( - $result, - __( - 'Sorry, we failed to restore the dropped trigger.' - ), - $create_item, - $errors - ); - } else { - $message = Message::success( - __('Trigger %1$s has been modified.') - ); - $message->addParam( - Util::backquote($_POST['item_name']) - ); - $sql_query = $drop_item . $item_query; - } - } - } else { - // 'Add a new item' mode - $result = $this->dbi->tryQuery($item_query); - if (! $result) { - $errors[] = sprintf( - __('The following query has failed: "%s"'), - htmlspecialchars($item_query) - ) - . '<br><br>' - . __('MySQL said: ') . $this->dbi->getError(); - } else { - $message = Message::success( - __('Trigger %1$s has been created.') - ); - $message->addParam( - Util::backquote($_POST['item_name']) - ); - $sql_query = $item_query; - } - } - } - - if (count($errors)) { - $message = Message::error( - '<b>' - . __( - 'One or more errors have occurred while processing your request:' - ) - . '</b>' - ); - $message->addHtml('<ul>'); - foreach ($errors as $string) { - $message->addHtml('<li>' . $string . '</li>'); - } - $message->addHtml('</ul>'); - } - - $output = Util::getMessage($message, $sql_query); - $response = Response::getInstance(); - if ($response->isAjax()) { - if ($message->isSuccess()) { - $items = $this->dbi->getTriggers($db, $table, ''); - $trigger = false; - foreach ($items as $value) { - if ($value['name'] == $_POST['item_name']) { - $trigger = $value; - } - } - $insert = false; - if (empty($table) - || ($trigger !== false && $table == $trigger['table']) - ) { - $insert = true; - $response->addJSON('new_row', $this->rteList->getTriggerRow($trigger)); - $response->addJSON( - 'name', - htmlspecialchars( - mb_strtoupper( - $_POST['item_name'] - ) - ) - ); - } - $response->addJSON('insert', $insert); - $response->addJSON('message', $output); - } else { - $response->addJSON('message', $message); - $response->setRequestStatus(false); - } - exit; - } - } - - /** - * Display a form used to add/edit a trigger, if necessary - */ - if (count($errors) - || (empty($_POST['editor_process_add']) - && empty($_POST['editor_process_edit']) - && (! empty($_REQUEST['add_item']) - || ! empty($_REQUEST['edit_item']))) // FIXME: this must be simpler than that - ) { - // Get the data for the form (if any) - if (! empty($_REQUEST['add_item'])) { - $title = $this->words->get('add'); - $item = $this->getDataFromRequest(); - $mode = 'add'; - } elseif (! empty($_REQUEST['edit_item'])) { - $title = __("Edit trigger"); - if (! empty($_REQUEST['item_name']) - && empty($_POST['editor_process_edit']) - ) { - $item = $this->getDataFromName($_REQUEST['item_name']); - if ($item !== false) { - $item['item_original_name'] = $item['item_name']; - } - } else { - $item = $this->getDataFromRequest(); - } - $mode = 'edit'; - } - $this->general->sendEditor('TRI', $mode, $item, $title, $db); - } - } - - /** - * This function will generate the values that are required to for the editor - * - * @return array Data necessary to create the editor. - */ - public function getDataFromRequest() - { - $retval = []; - $indices = [ - 'item_name', - 'item_table', - 'item_original_name', - 'item_action_timing', - 'item_event_manipulation', - 'item_definition', - 'item_definer', - ]; - foreach ($indices as $index) { - $retval[$index] = isset($_POST[$index]) ? $_POST[$index] : ''; - } - return $retval; - } - - /** - * This function will generate the values that are required to complete - * the "Edit trigger" form given the name of a trigger. - * - * @param string $name The name of the trigger. - * - * @return array|bool Data necessary to create the editor. - */ - public function getDataFromName($name) - { - global $db, $table; - - $temp = []; - $items = $this->dbi->getTriggers($db, $table, ''); - foreach ($items as $value) { - if ($value['name'] == $name) { - $temp = $value; - } - } - if (empty($temp)) { - return false; - } else { - $retval = []; - $retval['create'] = $temp['create']; - $retval['drop'] = $temp['drop']; - $retval['item_name'] = $temp['name']; - $retval['item_table'] = $temp['table']; - $retval['item_action_timing'] = $temp['action_timing']; - $retval['item_event_manipulation'] = $temp['event_manipulation']; - $retval['item_definition'] = $temp['definition']; - $retval['item_definer'] = $temp['definer']; - return $retval; - } - } - - /** - * Displays a form used to add/edit a trigger - * - * @param string $mode If the editor will be used to edit a trigger - * or add a new one: 'edit' or 'add'. - * @param array $item Data for the trigger returned by getDataFromRequest() - * or getDataFromName() - * - * @return string HTML code for the editor. - */ - public function getEditorForm($mode, array $item) - { - global $db, $table, $event_manipulations, $action_timings; - - $modeToUpper = mb_strtoupper($mode); - $response = Response::getInstance(); - - // Escape special characters - $need_escape = [ - 'item_original_name', - 'item_name', - 'item_definition', - 'item_definer', - ]; - foreach ($need_escape as $key => $index) { - $item[$index] = htmlentities($item[$index], ENT_QUOTES, 'UTF-8'); - } - $original_data = ''; - if ($mode == 'edit') { - $original_data = "<input name='item_original_name' " - . "type='hidden' value='{$item['item_original_name']}'>\n"; - } - $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); - - // Create the output - $retval = ""; - $retval .= "<!-- START " . $modeToUpper . " TRIGGER FORM -->\n\n"; - $retval .= "<form class='rte_form' action='db_triggers.php' method='post'>\n"; - $retval .= "<input name='{$mode}_item' type='hidden' value='1'>\n"; - $retval .= $original_data; - $retval .= Url::getHiddenInputs($db, $table) . "\n"; - $retval .= "<fieldset>\n"; - $retval .= "<legend>" . __('Details') . "</legend>\n"; - $retval .= "<table class='rte_table'>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Trigger name') . "</td>\n"; - $retval .= " <td><input type='text' name='item_name' maxlength='64'\n"; - $retval .= " value='{$item['item_name']}'></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Table') . "</td>\n"; - $retval .= " <td>\n"; - $retval .= " <select name='item_table'>\n"; - foreach ($tables as $key => $value) { - $selected = ""; - if ($mode == 'add' && $value == $table) { - $selected = " selected='selected'"; - } elseif ($mode == 'edit' && $value == $item['item_table']) { - $selected = " selected='selected'"; - } - $retval .= "<option$selected>"; - $retval .= htmlspecialchars($value); - $retval .= "</option>\n"; - } - $retval .= " </select>\n"; - $retval .= " </td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . _pgettext('Trigger action time', 'Time') . "</td>\n"; - $retval .= " <td><select name='item_timing'>\n"; - foreach ($action_timings as $key => $value) { - $selected = ""; - if (! empty($item['item_action_timing']) - && $item['item_action_timing'] == $value - ) { - $selected = " selected='selected'"; - } - $retval .= "<option$selected>$value</option>"; - } - $retval .= " </select></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Event') . "</td>\n"; - $retval .= " <td><select name='item_event'>\n"; - foreach ($event_manipulations as $key => $value) { - $selected = ""; - if (! empty($item['item_event_manipulation']) - && $item['item_event_manipulation'] == $value - ) { - $selected = " selected='selected'"; - } - $retval .= "<option$selected>$value</option>"; - } - $retval .= " </select></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Definition') . "</td>\n"; - $retval .= " <td><textarea name='item_definition' rows='15' cols='40'>"; - $retval .= $item['item_definition']; - $retval .= "</textarea></td>\n"; - $retval .= "</tr>\n"; - $retval .= "<tr>\n"; - $retval .= " <td>" . __('Definer') . "</td>\n"; - $retval .= " <td><input type='text' name='item_definer'\n"; - $retval .= " value='{$item['item_definer']}'></td>\n"; - $retval .= "</tr>\n"; - $retval .= "</table>\n"; - $retval .= "</fieldset>\n"; - if ($response->isAjax()) { - $retval .= "<input type='hidden' name='editor_process_{$mode}'\n"; - $retval .= " value='true'>\n"; - $retval .= "<input type='hidden' name='ajax_request' value='true'>\n"; - } else { - $retval .= "<fieldset class='tblFooters'>\n"; - $retval .= " <input type='submit' name='editor_process_{$mode}'\n"; - $retval .= " value='" . __('Go') . "'>\n"; - $retval .= "</fieldset>\n"; - } - $retval .= "</form>\n\n"; - $retval .= "<!-- END " . $modeToUpper . " TRIGGER FORM -->\n\n"; - - return $retval; - } - - /** - * Composes the query necessary to create a trigger from an HTTP request. - * - * @return string The CREATE TRIGGER query. - */ - public function getQueryFromRequest() - { - global $db, $errors, $action_timings, $event_manipulations; - - $query = 'CREATE '; - if (! empty($_POST['item_definer'])) { - if (mb_strpos($_POST['item_definer'], '@') !== false - ) { - $arr = explode('@', $_POST['item_definer']); - $query .= 'DEFINER=' . Util::backquote($arr[0]); - $query .= '@' . Util::backquote($arr[1]) . ' '; - } else { - $errors[] = __('The definer must be in the "username@hostname" format!'); - } - } - $query .= 'TRIGGER '; - if (! empty($_POST['item_name'])) { - $query .= Util::backquote($_POST['item_name']) . ' '; - } else { - $errors[] = __('You must provide a trigger name!'); - } - if (! empty($_POST['item_timing']) - && in_array($_POST['item_timing'], $action_timings) - ) { - $query .= $_POST['item_timing'] . ' '; - } else { - $errors[] = __('You must provide a valid timing for the trigger!'); - } - if (! empty($_POST['item_event']) - && in_array($_POST['item_event'], $event_manipulations) - ) { - $query .= $_POST['item_event'] . ' '; - } else { - $errors[] = __('You must provide a valid event for the trigger!'); - } - $query .= 'ON '; - if (! empty($_POST['item_table']) - && in_array($_POST['item_table'], $this->dbi->getTables($db)) - ) { - $query .= Util::backquote($_POST['item_table']); - } else { - $errors[] = __('You must provide a valid table name!'); - } - $query .= ' FOR EACH ROW '; - if (! empty($_POST['item_definition'])) { - $query .= $_POST['item_definition']; - } else { - $errors[] = __('You must provide a trigger definition.'); - } - - return $query; - } -} diff --git a/srcs/phpmyadmin/libraries/classes/Rte/Words.php b/srcs/phpmyadmin/libraries/classes/Rte/Words.php deleted file mode 100644 index f308003..0000000 --- a/srcs/phpmyadmin/libraries/classes/Rte/Words.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * Helper functions for RTE - * - * @package PhpMyAdmin - */ -declare(strict_types=1); - -namespace PhpMyAdmin\Rte; - -/** - * PhpMyAdmin\Rte\Words class - * - * @package PhpMyAdmin - */ -class Words -{ - /** - * This function is used to retrieve some language strings that are used - * in features that are common to routines, triggers and events. - * - * @param string $index The index of the string to get - * - * @return string The requested string or an empty string, if not available - */ - public function get($index) - { - global $_PMA_RTE; - - switch ($_PMA_RTE) { - case 'RTN': - $words = [ - 'add' => __('Add routine'), - 'docu' => 'STORED_ROUTINES', - 'export' => __('Export of routine %s'), - 'human' => __('routine'), - 'no_create' => __( - 'You do not have the necessary privileges to create a routine.' - ), - 'no_edit' => __( - 'No routine with name %1$s found in database %2$s. ' - . 'You might be lacking the necessary privileges to edit this routine.' - ), - 'no_view' => __( - 'No routine with name %1$s found in database %2$s. ' - . 'You might be lacking the necessary privileges to view/export this routine.' - ), - 'not_found' => __('No routine with name %1$s found in database %2$s.'), - 'nothing' => __('There are no routines to display.'), - 'title' => __('Routines'), - ]; - break; - case 'TRI': - $words = [ - 'add' => __('Add trigger'), - 'docu' => 'TRIGGERS', - 'export' => __('Export of trigger %s'), - 'human' => __('trigger'), - 'no_create' => __( - 'You do not have the necessary privileges to create a trigger.' - ), - 'not_found' => __('No trigger with name %1$s found in database %2$s.'), - 'nothing' => __('There are no triggers to display.'), - 'title' => __('Triggers'), - ]; - break; - case 'EVN': - $words = [ - 'add' => __('Add event'), - 'docu' => 'EVENTS', - 'export' => __('Export of event %s'), - 'human' => __('event'), - 'no_create' => __( - 'You do not have the necessary privileges to create an event.' - ), - 'not_found' => __('No event with name %1$s found in database %2$s.'), - 'nothing' => __('There are no events to display.'), - 'title' => __('Events'), - ]; - break; - default: - $words = []; - break; - } - - return isset($words[$index]) ? $words[$index] : ''; - } -} |
