diff --git a/Sources/Maintenance/Database/MySQL.php b/Sources/Maintenance/Database/MySQL.php index f40d8dbcc0..ecffdb310f 100644 --- a/Sources/Maintenance/Database/MySQL.php +++ b/Sources/Maintenance/Database/MySQL.php @@ -15,6 +15,7 @@ namespace SMF\Maintenance\Database; +use SMF\Config; use SMF\Db\DatabaseApi as Db; use SMF\Maintenance\DatabaseInterface; @@ -170,6 +171,82 @@ public function setSqlMode(string $mode = 'default'): bool return true; } + + /** + * {@inheritDoc} + */ + public function processError(string $error_msg, string $query): mixed + { + $mysqli_errno = mysqli_errno(Db::$db_connection); + + $error_query = in_array(substr(trim($query), 0, 11), ['INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO']); + + // Error numbers: + // 1016: Can't open file '....MYI' + // 1050: Table already exists. + // 1054: Unknown column name. + // 1060: Duplicate column name. + // 1061: Duplicate key name. + // 1062: Duplicate entry for unique key. + // 1068: Multiple primary keys. + // 1072: Key column '%s' doesn't exist in table. + // 1091: Can't drop key, doesn't exist. + // 1146: Table doesn't exist. + // 2013: Lost connection to server during query. + + if ($mysqli_errno == 1016) { + if (preg_match('~\'([^\.\']+)~', $error_msg, $match) != 0 && !empty($match[1])) { + mysqli_query(Db::$db_connection, 'REPAIR TABLE `' . $match[1] . '`'); + $result = mysqli_query(Db::$db_connection, $query); + + if ($result !== false) { + return $result; + } + } + } elseif ($mysqli_errno == 2013) { + Db::$db_connection = mysqli_connect(Config::$db_server, Config::$db_user, Config::$db_passwd); + mysqli_select_db(Db::$db_connection, Config::$db_name); + + if (Db::$db_connection) { + $result = mysqli_query(Db::$db_connection, $query); + + if ($result !== false) { + return $result; + } + } + } + // Duplicate column name... should be okay ;). + elseif (in_array($mysqli_errno, [1060, 1061, 1068, 1091])) { + return false; + } + // Duplicate insert... make sure it's the proper type of query ;). + elseif (in_array($mysqli_errno, [1054, 1062, 1146]) && $error_query) { + return false; + } + // Creating an index on a non-existent column. + elseif ($mysqli_errno == 1072) { + return false; + } elseif ($mysqli_errno == 1050 && substr(trim($query), 0, 12) == 'RENAME TABLE') { + return false; + } + // Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts. + elseif (in_array($mysqli_errno, [1054, 1146]) && in_array(substr(trim($query), 0, 7), ['SELECT ', 'SHOW CO'])) { + return false; + } + + // If a table already exists don't go potty. + if (in_array(substr(trim($query), 0, 8), ['CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U'])) { + if (strpos($error_msg, 'exist') !== false) { + return false; + } + } elseif (strpos(trim($query), 'INSERT ') !== false) { + if (strpos($error_msg, 'duplicate') !== false) { + return false; + } + } + + return true; + } } ?> \ No newline at end of file diff --git a/Sources/Maintenance/Database/PostgreSQL.php b/Sources/Maintenance/Database/PostgreSQL.php index 3d79c9e926..6776c4d2f9 100644 --- a/Sources/Maintenance/Database/PostgreSQL.php +++ b/Sources/Maintenance/Database/PostgreSQL.php @@ -182,6 +182,25 @@ public function setSqlMode(string $mode = 'default'): bool { return true; } + + /** + * {@inheritDoc} + */ + public function processError(string $error_msg, string $query): mixed + { + if (in_array(substr(trim($query), 0, 8), ['CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U'])) { + if (strpos($error_msg, 'exist') !== false) { + return false; + } + } elseif (strpos(trim($query), 'INSERT ') !== false) { + if (strpos($error_msg, 'duplicate') !== false) { + return false; + } + } + + return true; + } + } ?> \ No newline at end of file diff --git a/Sources/Maintenance/DatabaseInterface.php b/Sources/Maintenance/DatabaseInterface.php index f630c1383b..fd3cb0f20b 100644 --- a/Sources/Maintenance/DatabaseInterface.php +++ b/Sources/Maintenance/DatabaseInterface.php @@ -131,6 +131,18 @@ public function utf8Configured(): bool; * @return bool */ public function setSqlMode(string $mode = 'default'): bool; + + /** + * When an error occurs with a query ran through a wrapper, we send errors here. + * + * @param string $error_msg as returend by the database interfaces call. + * @param string $query Query we ran + * @return mixed + * False if we should not do anything, + * True if we should stop for error. + * Result from a query can also be returned, if we are able to correct the query. + */ + public function processError(string $error_msg, string $query): mixed; } ?> \ No newline at end of file diff --git a/Sources/Maintenance/Migration/v2_1/Migration0201.php b/Sources/Maintenance/Migration/v2_1/Migration0201.php new file mode 100644 index 0000000000..383f948879 --- /dev/null +++ b/Sources/Maintenance/Migration/v2_1/Migration0201.php @@ -0,0 +1,209 @@ + [ + 'table' => 'admin_info_files', + 'field' => 'id_file', + ], + 'attachments_seq' => [ + 'table' => 'attachments', + 'field' => 'id_attach', + ], + 'ban_groups_seq' => [ + 'table' => 'ban_groups', + 'field' => 'id_ban_group', + ], + 'ban_items_seq' => [ + 'table' => 'ban_items', + 'field' => 'id_ban', + ], + 'boards_seq' => [ + 'table' => 'boards', + 'field' => 'id_board', + ], + 'calendar_seq' => [ + 'table' => 'calendar', + 'field' => 'id_event', + ], + 'calendar_holidays_seq' => [ + 'table' => 'calendar_holidays', + 'field' => 'id_holiday', + ], + 'categories_seq' => [ + 'table' => 'categories', + 'field' => 'id_cat', + ], + 'custom_fields_seq' => [ + 'table' => 'custom_fields', + 'field' => 'id_field', + ], + 'log_actions_seq' => [ + 'table' => 'log_actions', + 'field' => 'id_action', + ], + 'log_banned_seq' => [ + 'table' => 'log_banned', + 'field' => 'id_ban_log', + ], + 'log_comments_seq' => [ + 'table' => 'log_comments', + 'field' => 'id_comment', + ], + 'log_errors_seq' => [ + 'table' => 'log_errors', + 'field' => 'id_error', + ], + 'log_group_requests_seq' => [ + 'table' => 'log_group_requests', + 'field' => 'id_request', + ], + 'log_member_notices_seq' => [ + 'table' => 'log_member_notices', + 'field' => 'id_notice', + ], + 'log_packages_seq' => [ + 'table' => 'log_packages', + 'field' => 'id_install', + ], + 'log_reported_seq' => [ + 'table' => 'log_reported', + 'field' => 'id_report', + ], + 'log_reported_comments_seq' => [ + 'table' => 'log_reported_comments', + 'field' => 'id_comment', + ], + 'log_scheduled_tasks_seq' => [ + 'table' => 'log_scheduled_tasks', + 'field' => 'id_log', + ], + 'log_spider_hits_seq' => [ + 'table' => 'log_spider_hits', + 'field' => 'id_hit', + ], + 'log_subscribed_seq' => [ + 'table' => 'log_subscribed', + 'field' => 'id_sublog', + ], + 'mail_queue_seq' => [ + 'table' => 'mail_queue', + 'field' => 'id_mail', + ], + 'membergroups_seq' => [ + 'table' => 'membergroups', + 'field' => 'id_group', + ], + 'members_seq' => [ + 'table' => 'members', + 'field' => 'id_member', + ], + 'message_icons_seq' => [ + 'table' => 'message_icons', + 'field' => 'id_icon', + ], + 'messages_seq' => [ + 'table' => 'messages', + 'field' => 'id_msg', + ], + 'package_servers_seq' => [ + 'table' => 'package_servers', + 'field' => 'id_server', + ], + 'permission_profiles_seq' => [ + 'table' => 'permission_profiles', + 'field' => 'id_profile', + ], + 'personal_messages_seq' => [ + 'table' => 'personal_messages', + 'field' => 'id_pm', + ], + 'pm_rules_seq' => [ + 'table' => 'pm_rules', + 'field' => 'id_rule', + ], + 'polls_seq' => [ + 'table' => 'polls', + 'field' => 'id_poll', + ], + 'scheduled_tasks_seq' => [ + 'table' => 'scheduled_tasks', + 'field' => 'id_task', + ], + 'smileys_seq' => [ + 'table' => 'smileys', + 'field' => 'id_smiley', + ], + 'spiders_seq' => [ + 'table' => 'spiders', + 'field' => 'id_spider', + ], + 'subscriptions_seq' => [ + 'table' => 'subscriptions', + 'field' => 'id_subscribe', + ], + 'topics_seq' => [ + 'table' => 'topics', + 'field' => 'id_topic', + ], + ]; + + /** + * {@inheritDoc} + */ + public function isCandidate(): bool + { + return Config::$db_type == POSTGRE_TITLE; + } + + /** + * {@inheritDoc} + */ + public function execute(): bool + { + for ($key = Maintenance::getCurrentStart(); $key < count($this->sequences); Maintenance::setCurrentStart()) { + $this->handleTimeout(); + + $value = $this->sequences[$key]; + + $this->query( + '', + "SELECT setval('{raw:key}', (SELECT COALESCE(MAX({raw:field}),1) FROM {raw:table}))", + [ + 'key' => Config::$db_prefix . $key, + 'field' => $value['field'], + 'table' => $value['table'], + ], + ); + } + + return true; + } +} + +?> \ No newline at end of file diff --git a/Sources/Maintenance/Migration/v2_1/Migration0202.php b/Sources/Maintenance/Migration/v2_1/Migration0202.php new file mode 100644 index 0000000000..f42083eab8 --- /dev/null +++ b/Sources/Maintenance/Migration/v2_1/Migration0202.php @@ -0,0 +1,59 @@ +query( + '', + " + CREATE OR REPLACE FUNCTION FIND_IN_SET(needle text, haystack text) RETURNS integer AS ' + SELECT i AS result + FROM generate_series(1, array_upper(string_to_array($2,'',''), 1)) AS g(i) + WHERE (string_to_array($2,'',''))[i] = $1 + UNION ALL + SELECT 0 + LIMIT 1' + LANGUAGE 'sql'; + ", + ); + + return true; + } +} + +?> \ No newline at end of file diff --git a/Sources/Maintenance/Migration/v2_1/Migration1001.php b/Sources/Maintenance/Migration/v2_1/Migration1001.php new file mode 100644 index 0000000000..fa32cc4ef1 --- /dev/null +++ b/Sources/Maintenance/Migration/v2_1/Migration1001.php @@ -0,0 +1,82 @@ +query( + '', + ' + DELETE FROM {db_prefix}settings + WHERE variable IN ({array_string:karma_vars})', + [ + 'karma_vars' => ['karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'], + ], + ); + + $member_columns = Db::$db->list_columns('{db_prefix}members'); + + // Cleaning up old karma member settings. + if (in_array('karma_good', $member_columns)) { + Db::$db->remove_column('{db_prefix}members', 'karma_good'); + } + + // Does karma bad was enable? + if (in_array('karma_bad', $member_columns)) { + Db::$db->remove_column('{db_prefix}members', 'karma_bad'); + } + + // Cleaning up old karma permissions. + Db::$db->query( + '', + ' + DELETE FROM {db_prefix}permissions + WHERE permission = {string:karma_vars}', + [ + 'karma_vars' => 'karma_edit', + ], + ); + + // Cleaning up old log_karma table + Db::$db->drop_table('{db_prefix}log_karma'); + + return true; + } +} + +?> \ No newline at end of file diff --git a/Sources/Maintenance/Migration/v2_1/Migration1002.php b/Sources/Maintenance/Migration/v2_1/Migration1002.php new file mode 100644 index 0000000000..814e9f5fd4 --- /dev/null +++ b/Sources/Maintenance/Migration/v2_1/Migration1002.php @@ -0,0 +1,165 @@ +), which would be similar the more standard DATEFROMPARTS. + + // PostgreSQL does the query a bit different. + $is_pgsql = Config::$db_type == POSTGRE_TITLE; + + if (Maintenance::getCurrentStart() < 1 && $is_pgsql) { + $this->query('', ' + UPDATE {db_prefix}calendar + SET start_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM start_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM start_date), EXTRACT(DAY FROM start_date))::date + WHERE EXTRACT(YEAR FROM start_date) < 1004'); + } elseif (Maintenance::getCurrentStart() < 1) { + $this->query('', ' + UPDATE {db_prefix}calendar + SET start_date = DATE(CONCAT(1004, {literal:-}, MONTH(start_date), {literal:-}, DAY(start_date))) + WHERE YEAR(start_date) < 1004'); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + if (Maintenance::getCurrentStart() < 2 && $is_pgsql) { + $this->query('', ' + UPDATE {db_prefix}calendar + SET end_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM end_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM end_date), EXTRACT(DAY FROM end_date))::date + WHERE EXTRACT(YEAR FROM end_date) < 1004'); + } elseif (Maintenance::getCurrentStart() < 2) { + $this->query('', ' + UPDATE {$db_prefix}calendar + SET end_date = DATE(CONCAT(1004, {literal:-}, MONTH(end_date), {literal:-}, DAY(end_date))) + WHERE YEAR(end_date) < 1004'); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + if (Maintenance::getCurrentStart() < 3 && $is_pgsql) { + $this->query('', ' + UPDATE {db_prefix}calendar_holidays + SET event_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM event_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM event_date), EXTRACT(DAY FROM event_date))::date + WHERE EXTRACT(YEAR FROM event_date) < 1004'); + } elseif (Maintenance::getCurrentStart() < 3) { + $this->query('', ' + UPDATE {$db_prefix}calendar_holidays + SET event_date = DATE(CONCAT(1004, {literal:-}, MONTH(event_date), {literal:-}, DAY(event_date))) + WHERE YEAR(event_date) < 1004'); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + if (Maintenance::getCurrentStart() < 4 && $is_pgsql) { + $this->query('', ' + UPDATE {db_prefix}log_spider_stats + SET stat_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM stat_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM stat_date), EXTRACT(DAY FROM stat_date))::date + WHERE EXTRACT(YEAR FROM stat_date) < 1004', []); + } elseif (Maintenance::getCurrentStart() < 4) { + $this->query('', ' + UPDATE {$db_prefix}log_spider_stats + SET stat_date = DATE(CONCAT(1004, {literal:-}, MONTH(stat_date), {literal:-}, DAY(stat_date))) + WHERE YEAR(stat_date) < 1004'); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + if (Maintenance::getCurrentStart() < 5 && $is_pgsql) { + $this->query('', ' + UPDATE {db_prefix}log_spider_stats + SET birthdate = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM birthdate) < 1004 THEN 1004 END, CASE WHEN EXTRACT(MONTH FROM birthdate) < 1 THEN 1 ELSE EXTRACT(MONTH FROM birthdate) END, CASE WHEN EXTRACT(DAY FROM birthdate) < 1 THEN 1 ELSE EXTRACT(DAY FROM birthdate) END)::date + WHERE EXTRACT(YEAR FROM birthdate) < 1004 OR EXTRACT(MONTH FROM birthdate) < 1 OR EXTRACT(DAY FROM birthdate) < 1'); + } elseif (Maintenance::getCurrentStart() < 5) { + $this->query('', ' + UPDATE {$db_prefix}members + SET birthdate = DATE(CONCAT(IF(YEAR(birthdate) < 1004, 1004, YEAR(birthdate)), {literal:-}, IF(MONTH(birthdate) < 1, 1, MONTH(birthdate)), {literal:-}, IF(DAY(birthdate) < 1, 1, DAY(birthdate)))) + WHERE YEAR(birthdate) < 1004 OR MONTH(birthdate) < 1 OR DAY(birthdate) < 1'); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + if (Maintenance::getCurrentStart() < 6 && $is_pgsql) { + $this->query('', ' + UPDATE {$db_prefix}members + SET birthdate = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM birthdate) < 1004 THEN 1004 END, CASE WHEN EXTRACT(MONTH FROM birthdate) < 1 THEN 1 ELSE EXTRACT(MONTH FROM birthdate) END, CASE WHEN EXTRACT(DAY FROM birthdate) < 1 THEN 1 ELSE EXTRACT(DAY FROM birthdate) END)::date + WHERE EXTRACT(YEAR FROM birthdate) < 1004 OR EXTRACT(MONTH FROM birthdate) < 1 OR EXTRACT(DAY FROM birthdate) < 1', []); + } elseif (Maintenance::getCurrentStart() < 6) { + $this->query('', ' + UPDATE {db_prefix}members + SET birthdate = DATE(CONCAT(IF(YEAR(birthdate) < 1004, 1004, YEAR(birthdate)), {literal:-}, IF(MONTH(birthdate) < 1, 1, MONTH(birthdate)), {literal:-}, IF(DAY(birthdate) < 1, 1, DAY(birthdate)))) + WHERE YEAR(birthdate) < 1004 OR MONTH(birthdate) < 1 OR DAY(birthdate) < 1'); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + if (Maintenance::getCurrentStart() < 7) { + Db::$db->change_column( + '{db_prefix}log_activity', + 'DATE', + [ + 'not_null' => true, + 'default' => null, + ], + ); + } + Maintenance::setCurrentStart(); + $this->handleTimeout(); + + $fixes = [ + ['tbl' => '{db_prefix}calendar', 'col' => 'start_date'], + ['tbl' => '{db_prefix}calendar', 'col' => 'end_date'], + ['tbl' => '{db_prefix}calendar_holidays', 'col' => 'event_date'], + ['tbl' => '{db_prefix}log_spider_stats', 'col' => 'stat_date'], + ['tbl' => '{db_prefix}members', 'col' => 'birthdate'], + ]; + + for ($key = Maintenance::getCurrentStart(); $key < count($fixes); Maintenance::setCurrentStart()) { + $fix = $fixes[$key - 7]; + + Db::$db->change_column($fix['tbl'], $fix['col'], ['default' => '1004-01-01']); + $this->handleTimeout(); + } + + return true; + } +} + +?> \ No newline at end of file diff --git a/Sources/Maintenance/Migration/v3_0/Migration0001.php b/Sources/Maintenance/Migration/v3_0/Migration0001.php index 786851d374..7f728600ea 100644 --- a/Sources/Maintenance/Migration/v3_0/Migration0001.php +++ b/Sources/Maintenance/Migration/v3_0/Migration0001.php @@ -18,6 +18,7 @@ use SMF\Config; use SMF\Db\DatabaseApi as Db; use SMF\Lang; +use SMF\Maintenance; use SMF\Maintenance\Migration; class Migration0001 extends Migration @@ -47,7 +48,7 @@ public function execute(): bool while (!$is_done) { // @@ TODO: Handle sub steps. - nextSubStep($substep); + $this->handleTimeout(); // Skip errors here so we don't croak if the columns don't exist... $request = Db::$db->query( @@ -93,6 +94,8 @@ public function execute(): bool WHERE id_member IN ({array_int:search_members})', $args, ); + + Maintenance::setCurrentStart(); } // Rename the privacy policy records. @@ -118,6 +121,8 @@ public function execute(): bool unset($new_variable); } } + + return true; } } diff --git a/Sources/Maintenance/MigrationBase.php b/Sources/Maintenance/MigrationBase.php index 53c8dab651..dac8e39316 100644 --- a/Sources/Maintenance/MigrationBase.php +++ b/Sources/Maintenance/MigrationBase.php @@ -15,6 +15,12 @@ namespace SMF\Maintenance; +use SMF\Config; +use SMF\Db\DatabaseApi as Db; +use SMF\Lang; +use SMF\Maintenance; +use SMF\Sapi; + /** * Migration container for a maintenance task. */ @@ -28,7 +34,17 @@ class Migration public string $name; /** - * Cleanup task we will execute. + * Check if the task should be performed or not. + * + * @return bool True if this task needs ran, false otherwise. + */ + public function isCandidate(): bool + { + return true; + } + + /** + * Upgrade task we will execute. * * @return bool True if successful (or skipped), false otherwise. */ @@ -36,6 +52,89 @@ public function execute(): bool { return true; } + + /** + * Wrapper for the tool to handle timeout protection. + * + * If a timeout needs to occur, it is handled, ensure that prior to this call, all variables are updated.. + */ + protected function handleTimeout(?int $start = 0): void + { + Maintenance::setCurrentStart($start); + + Maintenance::$tool->checkAndHandleTimeout(); + } + + /** + * + * @var DatabaseInterface The database type we are working with. + */ + protected ?DatabaseInterface $db; + + /** + * Wrapper for the database query. + * + * Ensures the query runs without handling errors, as we do not have that luxury. + */ + protected function query(string $identifier, string $db_string, array $db_values = [], ?object $connection = null) + { + if (!Config::$modSettings['disableQueryCheck']) { + Config::$modSettings['disableQueryCheck'] = true; + } + + if (!empty($db_values['unbuffered'])) { + Db::$unbuffered = true; + } + + $db_values += [ + 'security_override' => true, + 'db_error_skip' => true, + ]; + + $result = Db::$db->query($identifier, $db_string, $db_values, $connection); + Db::$unbuffered = false; + + // Failure?! + if ($result !== false) { + return $result; + } + + $db_error_message = Db::$db->error(Db::$db_connection); + + if ($this->db === null) { + $this->db = Maintenance::$tool->loadMaintenanceDatabase(Config::$db_type); + } + + // Checks if we can fix this. + $result = $this->db->processError($db_error_message, Db::$db->quote($db_string, $db_values, $connection)); + + if ($result !== false) { + return $result; + } + + if (Sapi::isCLI()) { + echo 'Unsuccessful! Database error message:', "\n", $db_error_message, "\n"; + + die; + } + + Maintenance::$context['try_again'] = true; + Maintenance::$fatal_error = ' + ' . Lang::$txt['upgrade_unsuccessful'] . '
+
+ ' . Lang::getTxt( + 'query_failed', + [ + 'QUERY_STRING' => '
' . nl2br(htmlspecialchars(trim($db_string))) . ';
', + 'QUERY_ERROR' => '
' . nl2br(htmlspecialchars($db_error_message)) . '
', + ], + ) . + ' +
'; + + Maintenance::$tool->preExit(); + Maintenance::exit(); + } } ?> \ No newline at end of file diff --git a/Sources/Maintenance/Template/Upgrade.php b/Sources/Maintenance/Template/Upgrade.php index 2d8a3290ac..6ad1da6b8b 100644 --- a/Sources/Maintenance/Template/Upgrade.php +++ b/Sources/Maintenance/Template/Upgrade.php @@ -52,7 +52,8 @@ public static function lower(): void '; } - if (!empty(Maintenance::$context['continue']) || !empty(Maintenance::$context['skip'])) { + + if (!empty(Maintenance::$context['continue']) || !empty(Maintenance::$context['skip']) || !empty(Maintenance::$context['try_again'])) { echo '
'; @@ -61,6 +62,11 @@ public static function lower(): void '; } + if (!empty(Maintenance::$context['try_again'])) { + echo ' + '; + } + if (!empty(Maintenance::$context['skip'])) { echo ' '; diff --git a/Sources/Maintenance/Tools/Upgrade.php b/Sources/Maintenance/Tools/Upgrade.php index 0a07933e2b..2da37c48e2 100644 --- a/Sources/Maintenance/Tools/Upgrade.php +++ b/Sources/Maintenance/Tools/Upgrade.php @@ -31,7 +31,6 @@ use SMF\Time; use SMF\User; use SMF\Utils; -use ValueError; /** * Upgrade tool. @@ -742,6 +741,16 @@ public function upgradeOptions(): bool die; } + // Empty our error log. + if (!empty($_POST['empty_error'])) { + Db::$db->query( + 'truncate_table', + ' + TRUNCATE {db_prefix}log_errors', + [], + ); + } + // Are we doing debug? if (isset($_POST['debug'])) { $this->debug = true; @@ -1103,31 +1112,6 @@ private function toggleSmStats(array &$settings): void $settings[] = ['enable_sm_stats', null]; } } - - /** - * This will check if we need to handle a timeout, if so, it sets up data for the next round. - * - * @throws ValueError - * @throws Exception - */ - private function checkAndHandleTimeout(): void - { - if (!Maintenance::isOutOfTime()) { - return; - } - - // If this is not json, we need to do a few things. - if (!isset($_GET['json'])) { - // We're going to pause after this! - Maintenance::$context['pause'] = true; - - Maintenance::setQueryString(); - } - - Maintenance::exit(); - - throw new Exception('Zombies!'); - } } ?> \ No newline at end of file diff --git a/Sources/Maintenance/ToolsBase.php b/Sources/Maintenance/ToolsBase.php index f7285fe9aa..ec8b868263 100644 --- a/Sources/Maintenance/ToolsBase.php +++ b/Sources/Maintenance/ToolsBase.php @@ -459,6 +459,31 @@ final public function detectLanguages(array $key_files = ['General']): array return $languages; } + + /** + * This will check if we need to handle a timeout, if so, it sets up data for the next round. + * + * @throws \ValueError + * @throws \Exception + */ + public function checkAndHandleTimeout(): void + { + if (!Maintenance::isOutOfTime()) { + return; + } + + // If this is not json, we need to do a few things. + if (!isset($_GET['json'])) { + // We're going to pause after this! + Maintenance::$context['pause'] = true; + + Maintenance::setQueryString(); + } + + Maintenance::exit(); + + throw new \Exception('Zombies!'); + } } ?> \ No newline at end of file