Translated using Weblate (Slovenian)
[phpmyadmin.git] / libraries / classes / ReplicationGui.php
blob0e109f52b0246a8c944000659b019a0630937958
1 <?php
2 /**
3 * Functions for the replication GUI
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin;
10 use PhpMyAdmin\Query\Utilities;
12 use function htmlspecialchars;
13 use function in_array;
14 use function is_array;
15 use function mb_strrpos;
16 use function mb_strtolower;
17 use function mb_substr;
18 use function sprintf;
19 use function str_replace;
20 use function strlen;
21 use function strtok;
22 use function time;
24 /**
25 * Functions for the replication GUI
27 class ReplicationGui
29 /** @var Replication */
30 private $replication;
32 /** @var Template */
33 private $template;
35 /**
36 * @param Replication $replication Replication instance
37 * @param Template $template Template instance
39 public function __construct(Replication $replication, Template $template)
41 $this->replication = $replication;
42 $this->template = $template;
45 /**
46 * returns HTML for error message
48 * @return string HTML code
50 public function getHtmlForErrorMessage(): string
52 $html = '';
53 if (isset($_SESSION['replication']['sr_action_status'], $_SESSION['replication']['sr_action_info'])) {
54 if ($_SESSION['replication']['sr_action_status'] === 'error') {
55 $errorMessage = $_SESSION['replication']['sr_action_info'];
56 $html .= Message::error($errorMessage)->getDisplay();
57 $_SESSION['replication']['sr_action_status'] = 'unknown';
58 } elseif ($_SESSION['replication']['sr_action_status'] === 'success') {
59 $successMessage = $_SESSION['replication']['sr_action_info'];
60 $html .= Message::success($successMessage)->getDisplay();
61 $_SESSION['replication']['sr_action_status'] = 'unknown';
65 return $html;
68 /**
69 * returns HTML for master replication
71 * @return string HTML code
73 public function getHtmlForMasterReplication(): string
75 global $dbi;
77 if (! isset($_POST['repl_clear_scr'])) {
78 $masterStatusTable = $this->getHtmlForReplicationStatusTable('master', true, false);
79 $slaves = $dbi->fetchResult('SHOW SLAVE HOSTS', null, null);
81 $urlParams = $GLOBALS['urlParams'];
82 $urlParams['mr_adduser'] = true;
83 $urlParams['repl_clear_scr'] = true;
86 if (isset($_POST['mr_adduser'])) {
87 $masterAddSlaveUser = $this->getHtmlForReplicationMasterAddSlaveUser();
90 return $this->template->render('server/replication/master_replication', [
91 'clear_screen' => isset($_POST['repl_clear_scr']),
92 'master_status_table' => $masterStatusTable ?? '',
93 'slaves' => $slaves ?? [],
94 'url_params' => $urlParams ?? [],
95 'master_add_user' => isset($_POST['mr_adduser']),
96 'master_add_slave_user' => $masterAddSlaveUser ?? '',
97 ]);
101 * returns HTML for master replication configuration
103 * @return string HTML code
105 public function getHtmlForMasterConfiguration(): string
107 $databaseMultibox = $this->getHtmlForReplicationDbMultibox();
109 return $this->template->render(
110 'server/replication/master_configuration',
111 ['database_multibox' => $databaseMultibox]
116 * returns HTML for slave replication configuration
118 * @param bool $serverSlaveStatus Whether it is Master or Slave
119 * @param array $serverSlaveReplication Slave replication
121 * @return string HTML code
123 public function getHtmlForSlaveConfiguration(
124 $serverSlaveStatus,
125 array $serverSlaveReplication
126 ): string {
127 global $dbi;
129 $serverSlaveMultiReplication = $dbi->fetchResult(
130 'SHOW ALL SLAVES STATUS'
132 if ($serverSlaveStatus) {
133 $urlParams = $GLOBALS['urlParams'];
134 $urlParams['sr_take_action'] = true;
135 $urlParams['sr_slave_server_control'] = true;
137 if ($serverSlaveReplication[0]['Slave_IO_Running'] === 'No') {
138 $urlParams['sr_slave_action'] = 'start';
139 } else {
140 $urlParams['sr_slave_action'] = 'stop';
143 $urlParams['sr_slave_control_parm'] = 'IO_THREAD';
144 $slaveControlIoLink = Url::getCommon($urlParams, '');
146 if ($serverSlaveReplication[0]['Slave_SQL_Running'] === 'No') {
147 $urlParams['sr_slave_action'] = 'start';
148 } else {
149 $urlParams['sr_slave_action'] = 'stop';
152 $urlParams['sr_slave_control_parm'] = 'SQL_THREAD';
153 $slaveControlSqlLink = Url::getCommon($urlParams, '');
155 if (
156 $serverSlaveReplication[0]['Slave_IO_Running'] === 'No'
157 || $serverSlaveReplication[0]['Slave_SQL_Running'] === 'No'
159 $urlParams['sr_slave_action'] = 'start';
160 } else {
161 $urlParams['sr_slave_action'] = 'stop';
164 $urlParams['sr_slave_control_parm'] = null;
165 $slaveControlFullLink = Url::getCommon($urlParams, '');
167 $urlParams['sr_slave_action'] = 'reset';
168 $slaveControlResetLink = Url::getCommon($urlParams, '');
170 $urlParams = $GLOBALS['urlParams'];
171 $urlParams['sr_take_action'] = true;
172 $urlParams['sr_slave_skip_error'] = true;
173 $slaveSkipErrorLink = Url::getCommon($urlParams, '');
175 $urlParams = $GLOBALS['urlParams'];
176 $urlParams['sl_configure'] = true;
177 $urlParams['repl_clear_scr'] = true;
179 $reconfigureMasterLink = Url::getCommon($urlParams, '');
181 $slaveStatusTable = $this->getHtmlForReplicationStatusTable('slave', true, false);
183 $slaveIoRunning = $serverSlaveReplication[0]['Slave_IO_Running'] !== 'No';
184 $slaveSqlRunning = $serverSlaveReplication[0]['Slave_SQL_Running'] !== 'No';
187 return $this->template->render('server/replication/slave_configuration', [
188 'server_slave_multi_replication' => $serverSlaveMultiReplication,
189 'url_params' => $GLOBALS['urlParams'],
190 'master_connection' => $_POST['master_connection'] ?? '',
191 'server_slave_status' => $serverSlaveStatus,
192 'slave_status_table' => $slaveStatusTable ?? '',
193 'slave_sql_running' => $slaveSqlRunning ?? false,
194 'slave_io_running' => $slaveIoRunning ?? false,
195 'slave_control_full_link' => $slaveControlFullLink ?? '',
196 'slave_control_reset_link' => $slaveControlResetLink ?? '',
197 'slave_control_sql_link' => $slaveControlSqlLink ?? '',
198 'slave_control_io_link' => $slaveControlIoLink ?? '',
199 'slave_skip_error_link' => $slaveSkipErrorLink ?? '',
200 'reconfigure_master_link' => $reconfigureMasterLink ?? '',
201 'has_slave_configure' => isset($_POST['sl_configure']),
206 * returns HTML code for selecting databases
208 * @return string HTML code
210 public function getHtmlForReplicationDbMultibox(): string
212 $databases = [];
213 foreach ($GLOBALS['dblist']->databases as $database) {
214 if (Utilities::isSystemSchema($database)) {
215 continue;
218 $databases[] = $database;
221 return $this->template->render('server/replication/database_multibox', ['databases' => $databases]);
225 * returns HTML for changing master
227 * @param string $submitName submit button name
229 * @return string HTML code
231 public function getHtmlForReplicationChangeMaster($submitName): string
234 $usernameLength,
235 $hostnameLength,
236 ] = $this->getUsernameHostnameLength();
238 return $this->template->render('server/replication/change_master', [
239 'server_id' => time(),
240 'username_length' => $usernameLength,
241 'hostname_length' => $hostnameLength,
242 'submit_name' => $submitName,
247 * This function returns html code for table with replication status.
249 * @param string $type either master or slave
250 * @param bool $isHidden if true, then default style is set to hidden,
251 * default value false
252 * @param bool $hasTitle if true, then title is displayed, default true
254 * @return string HTML code
256 public function getHtmlForReplicationStatusTable(
257 $type,
258 $isHidden = false,
259 $hasTitle = true
260 ): string {
261 global $dbi;
263 $replicationInfo = new ReplicationInfo($dbi);
264 $replicationInfo->load($_POST['master_connection'] ?? null);
266 $replicationVariables = $replicationInfo->primaryVariables;
267 $variablesAlerts = null;
268 $variablesOks = null;
269 $serverReplication = $replicationInfo->getPrimaryStatus();
270 if ($type === 'slave') {
271 $replicationVariables = $replicationInfo->replicaVariables;
272 $variablesAlerts = [
273 'Slave_IO_Running' => 'No',
274 'Slave_SQL_Running' => 'No',
276 $variablesOks = [
277 'Slave_IO_Running' => 'Yes',
278 'Slave_SQL_Running' => 'Yes',
280 $serverReplication = $replicationInfo->getReplicaStatus();
283 $variables = [];
284 foreach ($replicationVariables as $variable) {
285 $serverReplicationVariable = is_array($serverReplication) && isset($serverReplication[0])
286 ? $serverReplication[0][$variable]
287 : '';
289 $variables[$variable] = [
290 'name' => $variable,
291 'status' => '',
292 'value' => $serverReplicationVariable,
295 if (
296 isset($variablesAlerts[$variable])
297 && $variablesAlerts[$variable] === $serverReplicationVariable
299 $variables[$variable]['status'] = 'text-danger';
300 } elseif (
301 isset($variablesOks[$variable])
302 && $variablesOks[$variable] === $serverReplicationVariable
304 $variables[$variable]['status'] = 'text-success';
307 $variablesWrap = [
308 'Replicate_Do_DB',
309 'Replicate_Ignore_DB',
310 'Replicate_Do_Table',
311 'Replicate_Ignore_Table',
312 'Replicate_Wild_Do_Table',
313 'Replicate_Wild_Ignore_Table',
315 if (! in_array($variable, $variablesWrap)) {
316 continue;
319 $variables[$variable]['value'] = str_replace(
320 ',',
321 ', ',
322 $serverReplicationVariable
326 return $this->template->render('server/replication/status_table', [
327 'type' => $type,
328 'is_hidden' => $isHidden,
329 'has_title' => $hasTitle,
330 'variables' => $variables,
335 * get the correct username and hostname lengths for this MySQL server
337 * @return array<int,int> username length, hostname length
339 public function getUsernameHostnameLength(): array
341 global $dbi;
343 $fieldsInfo = $dbi->getColumns('mysql', 'user');
344 $usernameLength = 16;
345 $hostnameLength = 41;
346 foreach ($fieldsInfo as $val) {
347 if ($val['Field'] === 'User') {
348 strtok($val['Type'], '()');
349 $v = strtok('()');
350 if (Util::isInteger($v)) {
351 $usernameLength = (int) $v;
353 } elseif ($val['Field'] === 'Host') {
354 strtok($val['Type'], '()');
355 $v = strtok('()');
356 if (Util::isInteger($v)) {
357 $hostnameLength = (int) $v;
362 return [
363 $usernameLength,
364 $hostnameLength,
369 * returns html code to add a replication slave user to the master
371 * @return string HTML code
373 public function getHtmlForReplicationMasterAddSlaveUser(): string
375 global $dbi;
378 $usernameLength,
379 $hostnameLength,
380 ] = $this->getUsernameHostnameLength();
382 if (isset($_POST['username']) && strlen($_POST['username']) === 0) {
383 $GLOBALS['pred_username'] = 'any';
386 $username = '';
387 if (! empty($_POST['username'])) {
388 $username = $GLOBALS['new_username'] ?? $_POST['username'];
391 $currentUser = $dbi->fetchValue('SELECT USER();');
392 if (! empty($currentUser)) {
393 $userHost = str_replace(
394 "'",
396 mb_substr(
397 $currentUser,
398 mb_strrpos($currentUser, '@') + 1
401 if ($userHost !== 'localhost' && $userHost !== '127.0.0.1') {
402 $thisHost = $userHost;
406 // when we start editing a user, $GLOBALS['pred_hostname'] is not defined
407 if (! isset($GLOBALS['pred_hostname']) && isset($_POST['hostname'])) {
408 switch (mb_strtolower($_POST['hostname'])) {
409 case 'localhost':
410 case '127.0.0.1':
411 $GLOBALS['pred_hostname'] = 'localhost';
412 break;
413 case '%':
414 $GLOBALS['pred_hostname'] = 'any';
415 break;
416 default:
417 $GLOBALS['pred_hostname'] = 'userdefined';
418 break;
422 return $this->template->render('server/replication/master_add_slave_user', [
423 'username_length' => $usernameLength,
424 'hostname_length' => $hostnameLength,
425 'has_username' => isset($_POST['username']),
426 'username' => $username,
427 'hostname' => $_POST['hostname'] ?? '',
428 'predefined_username' => $GLOBALS['pred_username'] ?? '',
429 'predefined_hostname' => $GLOBALS['pred_hostname'] ?? '',
430 'this_host' => $thisHost ?? null,
435 * handle control requests
437 public function handleControlRequest(): void
439 if (! isset($_POST['sr_take_action'])) {
440 return;
443 $refresh = false;
444 $result = false;
445 $messageSuccess = '';
446 $messageError = '';
448 if (isset($_POST['slave_changemaster']) && ! $GLOBALS['cfg']['AllowArbitraryServer']) {
449 $_SESSION['replication']['sr_action_status'] = 'error';
450 $_SESSION['replication']['sr_action_info'] = __(
451 'Connection to server is disabled, please enable'
452 . ' $cfg[\'AllowArbitraryServer\'] in phpMyAdmin configuration.'
454 } elseif (isset($_POST['slave_changemaster'])) {
455 $result = $this->handleRequestForSlaveChangeMaster();
456 } elseif (isset($_POST['sr_slave_server_control'])) {
457 $result = $this->handleRequestForSlaveServerControl();
458 $refresh = true;
460 switch ($_POST['sr_slave_action']) {
461 case 'start':
462 $messageSuccess = __('Replication started successfully.');
463 $messageError = __('Error starting replication.');
464 break;
465 case 'stop':
466 $messageSuccess = __('Replication stopped successfully.');
467 $messageError = __('Error stopping replication.');
468 break;
469 case 'reset':
470 $messageSuccess = __('Replication resetting successfully.');
471 $messageError = __('Error resetting replication.');
472 break;
473 default:
474 $messageSuccess = __('Success.');
475 $messageError = __('Error.');
476 break;
478 } elseif (isset($_POST['sr_slave_skip_error'])) {
479 $result = $this->handleRequestForSlaveSkipError();
482 if ($refresh) {
483 $response = Response::getInstance();
484 if ($response->isAjax()) {
485 $response->setRequestStatus($result);
486 $response->addJSON(
487 'message',
488 $result
489 ? Message::success($messageSuccess)
490 : Message::error($messageError)
492 } else {
493 Core::sendHeaderLocation(
494 './index.php?route=/server/replication'
495 . Url::getCommonRaw($GLOBALS['urlParams'], '&')
500 unset($refresh);
504 * handle control requests for Slave Change Master
506 public function handleRequestForSlaveChangeMaster(): bool
508 global $dbi;
510 $sr = [
511 'username' => $dbi->escapeString($_POST['username']),
512 'pma_pw' => $dbi->escapeString($_POST['pma_pw']),
513 'hostname' => $dbi->escapeString($_POST['hostname']),
514 'port' => (int) $dbi->escapeString($_POST['text_port']),
517 $_SESSION['replication']['m_username'] = $sr['username'];
518 $_SESSION['replication']['m_password'] = $sr['pma_pw'];
519 $_SESSION['replication']['m_hostname'] = $sr['hostname'];
520 $_SESSION['replication']['m_port'] = $sr['port'];
521 $_SESSION['replication']['m_correct'] = '';
522 $_SESSION['replication']['sr_action_status'] = 'error';
523 $_SESSION['replication']['sr_action_info'] = __('Unknown error');
525 // Attempt to connect to the new master server
526 $linkToMaster = $this->replication->connectToMaster(
527 $sr['username'],
528 $sr['pma_pw'],
529 $sr['hostname'],
530 $sr['port']
533 if (! $linkToMaster) {
534 $_SESSION['replication']['sr_action_status'] = 'error';
535 $_SESSION['replication']['sr_action_info'] = sprintf(
536 __('Unable to connect to master %s.'),
537 htmlspecialchars($sr['hostname'])
539 } else {
540 // Read the current master position
541 $position = $this->replication->slaveBinLogMaster(DatabaseInterface::CONNECT_AUXILIARY);
543 if (empty($position)) {
544 $_SESSION['replication']['sr_action_status'] = 'error';
545 $_SESSION['replication']['sr_action_info'] = __(
546 'Unable to read master log position. '
547 . 'Possible privilege problem on master.'
549 } else {
550 $_SESSION['replication']['m_correct'] = true;
552 if (
553 ! $this->replication->slaveChangeMaster(
554 $sr['username'],
555 $sr['pma_pw'],
556 $sr['hostname'],
557 $sr['port'],
558 $position,
559 true,
560 false,
561 DatabaseInterface::CONNECT_USER
564 $_SESSION['replication']['sr_action_status'] = 'error';
565 $_SESSION['replication']['sr_action_info'] = __('Unable to change master!');
566 } else {
567 $_SESSION['replication']['sr_action_status'] = 'success';
568 $_SESSION['replication']['sr_action_info'] = sprintf(
569 __('Master server changed successfully to %s.'),
570 htmlspecialchars($sr['hostname'])
576 return $_SESSION['replication']['sr_action_status'] === 'success';
580 * handle control requests for Slave Server Control
582 public function handleRequestForSlaveServerControl(): bool
584 global $dbi;
586 if (empty($_POST['sr_slave_control_parm'])) {
587 $_POST['sr_slave_control_parm'] = null;
590 if ($_POST['sr_slave_action'] === 'reset') {
591 $qStop = $this->replication->slaveControl('STOP', null, DatabaseInterface::CONNECT_USER);
592 $qReset = $dbi->tryQuery('RESET SLAVE;');
593 $qStart = $this->replication->slaveControl('START', null, DatabaseInterface::CONNECT_USER);
595 $result = $qStop !== false && $qStop !== -1 &&
596 $qReset !== false && $qReset !== -1 &&
597 $qStart !== false && $qStart !== -1;
598 } else {
599 $qControl = $this->replication->slaveControl(
600 $_POST['sr_slave_action'],
601 $_POST['sr_slave_control_parm'],
602 DatabaseInterface::CONNECT_USER
605 $result = $qControl !== false && $qControl !== -1;
608 return $result;
612 * handle control requests for Slave Skip Error
614 public function handleRequestForSlaveSkipError(): bool
616 global $dbi;
618 $count = 1;
619 if (isset($_POST['sr_skip_errors_count'])) {
620 $count = $_POST['sr_skip_errors_count'] * 1;
623 $qStop = $this->replication->slaveControl('STOP', null, DatabaseInterface::CONNECT_USER);
624 $qSkip = $dbi->tryQuery(
625 'SET GLOBAL SQL_SLAVE_SKIP_COUNTER = ' . $count . ';'
627 $qStart = $this->replication->slaveControl('START', null, DatabaseInterface::CONNECT_USER);
629 return $qStop !== false && $qStop !== -1 &&
630 $qSkip !== false && $qSkip !== -1 &&
631 $qStart !== false && $qStart !== -1;