Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
29ffa1e
Fixes some calls to JavaScript function doAutoSubmit()
Sesquipedalian Feb 26, 2026
506a12d
More reliable way to get theme dir in Maintenance::__construct()
Sesquipedalian Feb 26, 2026
75723eb
Don't mess with redirects or canonical URLs during install or upgrade
Sesquipedalian Mar 1, 2026
5950e36
Removes unnecessary param for SMF\Db\Schema\Table::getInitializers()
Sesquipedalian Mar 4, 2026
7ee51c7
Removes old comments when rebuilding Settings.php during upgrade
Sesquipedalian Mar 4, 2026
6e6c9d0
Fixes type error when setting Config::$db_port in upgrader
Sesquipedalian Mar 5, 2026
86a392b
Fixes some type casting inside Config::set()
Sesquipedalian Mar 6, 2026
5140764
Loads minimal user data for User::$me in upgrader
Sesquipedalian Mar 8, 2026
1b10879
Only show box for detailed info about migration steps when requested
Sesquipedalian Mar 9, 2026
865c313
Uses JavaScript to update "time elapsed" counter in maintenance tools
Sesquipedalian Mar 9, 2026
73c5efb
Fixes bizarre embedded template in critical error message
Sesquipedalian Mar 13, 2026
1e99772
Handles generated columns correctly in Db\APIs\MySQL::backup_table()
Sesquipedalian Mar 19, 2026
239ffd5
Maintenance\Cleanup\v3_0\OldFiles → Maintenance\Cleanup\OldFilesBase
Sesquipedalian Mar 20, 2026
e9dbf3c
Upgrades board descriptions after upgrading smileys
Sesquipedalian Mar 21, 2026
8a061d2
Uses correct table names in PostgreSqlSequences migration step
Sesquipedalian Mar 29, 2026
7e27a60
Explicit null default for expire_time in Db\Schema\v2_1\BanGroups
Sesquipedalian Mar 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions Languages/en_US/Maintenance.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,7 @@
$txt['maintenance_step'] = 'Step';
$txt['maintenance_overall_progress'] = 'Overall Progress';
$txt['maintenance_substep_progress'] = 'Step Progress';
$txt['maintenance_time_elasped_ms'] = 'Time Elapsed {m, plural,
one {# minute}
other {# minutes}
} and {s, plural,
one {# second}
other {# seconds}
}';
$txt['maintenance_time_elapsed'] = 'Time Elapsed: ';

// File Permissions.
$txt['chmod_linux_info'] = 'If you have a shell account, the following command can automatically correct permissions on these files';
Expand Down
10 changes: 5 additions & 5 deletions Sources/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -998,23 +998,23 @@ public static function set(array $settings): void
}

// Make sure the paths are correct... at least try to fix them.
if (empty(self::$boarddir) || !is_dir(realpath(self::$boarddir))) {
if (empty(self::$boarddir) || !is_dir((string) realpath(self::$boarddir))) {
self::$boarddir = !empty($_SERVER['SCRIPT_FILENAME']) ? \dirname(realpath($_SERVER['SCRIPT_FILENAME'])) : \dirname(__DIR__);
}

if ((empty(self::$sourcedir) || !is_dir(realpath(self::$sourcedir))) && is_dir(self::$boarddir . '/Sources')) {
if ((empty(self::$sourcedir) || !is_dir((string) realpath(self::$sourcedir))) && is_dir(self::$boarddir . '/Sources')) {
self::$sourcedir = self::$boarddir . '/Sources';
}

if ((empty(self::$vendordir) || !is_dir(realpath(self::$vendordir))) && is_dir(self::$boarddir . '/vendor')) {
if ((empty(self::$vendordir) || !is_dir((string) realpath(self::$vendordir))) && is_dir(self::$boarddir . '/vendor')) {
self::$vendordir = self::$boarddir . '/vendor';
}

if ((empty(self::$packagesdir) || !is_dir(realpath(self::$packagesdir))) && is_dir(self::$boarddir . '/Packages')) {
if ((empty(self::$packagesdir) || !is_dir((string) realpath(self::$packagesdir))) && is_dir(self::$boarddir . '/Packages')) {
self::$packagesdir = self::$boarddir . '/Packages';
}

if ((empty(self::$languagesdir) || !is_dir(realpath(self::$languagesdir))) && is_dir(self::$boarddir . '/Languages')) {
if ((empty(self::$languagesdir) || !is_dir((string) realpath(self::$languagesdir))) && is_dir(self::$boarddir . '/Languages')) {
self::$languagesdir = self::$boarddir . '/Languages';
}

Expand Down
25 changes: 21 additions & 4 deletions Sources/Db/APIs/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -938,19 +938,29 @@ public function backup_table(string $table, string $backup_table): object|bool
],
);

// If this failed, we go old school.
if ($result) {
$columns = [];

// Do we have any generated columns to deal with?
foreach ($this->list_columns($table, true) as $column) {
// Skip generated columns in the insert statement.
if (empty($column['generation_expression'])) {
$columns[] = $column['name'];
}
}

$request = $this->query(
'INSERT INTO {raw:backup_table}
SELECT *
({raw:columns})
SELECT {raw:columns}
FROM {raw:table}',
[
'backup_table' => $backup_table,
'table' => $table,
'columns' => implode(', ', $columns),
],
);

// Old school or no school?
if ($request) {
return $request;
}
Expand Down Expand Up @@ -1047,6 +1057,13 @@ public function backup_table(string $table, string $backup_table): object|bool
);
}

// Restore the generation expressions on any generated columns.
foreach ($this->list_columns($table, true) as $column) {
if (!empty($column['generation_expression'])) {
$this->change_column($backup_table, $column['name'], $column);
}
}

return $request;
}

Expand Down Expand Up @@ -2136,7 +2153,7 @@ public function list_columns(string $table_name, bool $detail = false, array $pa
}

if (str_contains($row['Extra'], 'GENERATED')) {
$columns[$row['Field']]['generation_expression'] = $row['generation_expression'];
$columns[$row['Field']]['generation_expression'] = $this->unescape_string($row['generation_expression']);
$columns[$row['Field']]['stored'] = str_contains($row['Extra'], 'STORED');
}
}
Expand Down
10 changes: 6 additions & 4 deletions Sources/Db/Schema/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ public function populate(bool $replace = false): int
/**
* Gets all known table schemas.
*
* @param string $schema_version A string like 'v3_0'.
* @return array All known table schemas.
*/
final public static function getAll(string $schema_version): array
Expand Down Expand Up @@ -594,15 +595,16 @@ final public static function getAll(string $schema_version): array
}

/**
* Gets all known table schemas.
* Gets database initializer queries for the indicated SMF version.
*
* @param string $schema_version A string like 'v3_0'.
* @return array All known table schemas.
*/
final public static function getInitializers(string $schema_version, string $title): array
final public static function getInitializers(string $schema_version): array
{
if (file_exists(__DIR__ . '/' . $schema_version . '/Initialize/' . $title . '.php')) {
if (file_exists(__DIR__ . '/' . $schema_version . '/Initialize/' . Db::$db->title . '.php')) {

$fully_qualified_class_name = __NAMESPACE__ . '\\' . $schema_version . '\\Initialize\\' . $title;
$fully_qualified_class_name = __NAMESPACE__ . '\\' . $schema_version . '\\Initialize\\' . Db::$db->title;

if (!class_exists($fully_qualified_class_name)) {
return [];
Expand Down
2 changes: 2 additions & 0 deletions Sources/Db/Schema/v2_1/BanGroups.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public function __construct()
name: 'expire_time',
type: 'int',
unsigned: true,
not_null: false,
default: null,
),
'cannot_access' => new Column(
name: 'cannot_access',
Expand Down
22 changes: 21 additions & 1 deletion Sources/Db/Schema/v3_0/Initialize/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,41 @@ class Base
* Public methods
****************/

public function __construct(?string $version)
/**
* Constructor.
*
* @param string $version Version of the database engine.
*/
public function __construct(string $version)
{
$this->version = $version;
}

/**
* Gets queries that create custom SQL functions and operators.
*
* @return array SQL queries.
*/
public function getAll(): array
{
return $this->functions() + $this->operators();
}

/**
* Gets queries that create custom SQL functions.
*
* @return array SQL queries.
*/
public function functions(): array
{
return [];
}

/**
* Gets queries that create custom SQL operators.
*
* @return array SQL queries.
*/
public function operators(): array
{
return [];
Expand Down
2 changes: 0 additions & 2 deletions Sources/Maintenance/Cleanup/CleanupBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ abstract class CleanupBase implements SubStepInterface
****************/

/**
* Check if the task should be performed or not.
*
* @return bool True if this task needs to be run, false otherwise.
*/
public function isCandidate(): bool
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@

declare(strict_types=1);

namespace SMF\Maintenance\Cleanup\v3_0;
namespace SMF\Maintenance\Cleanup;

use SMF\Config;
use SMF\Maintenance\Cleanup\CleanupBase;
use SMF\Utils;

class OldFiles extends CleanupBase
/**
* Base class for cleanup tasks that delete files that have been removed in a
* new version of SMF.
*/
abstract class OldFilesBase extends CleanupBase
{
/*******************
* Public properties
Expand All @@ -37,7 +40,7 @@ class OldFiles extends CleanupBase
/**
* @var array
*
* List of files removed in SMF 3.0.
* List of files removed in the relevant version of SMF.
*/
protected array $removed = [
// Files in the Themes directory.
Expand All @@ -57,9 +60,7 @@ class OldFiles extends CleanupBase
****************/

/**
* Check if the task should be performed or not.
*
* @return bool True if this task needs to be run, false otherwise.
*/
public function isCandidate(): bool
{
Expand Down
4 changes: 2 additions & 2 deletions Sources/Maintenance/Cleanup/v2_1/OldFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

namespace SMF\Maintenance\Cleanup\v2_1;

use SMF\Maintenance\Cleanup\v3_0\OldFiles as OldFilesBase;
use SMF\Maintenance\Cleanup\OldFilesBase;

/**
* Just like the v3_0 version of OldFiles, but with a different list of files.
* Deletes files that were present in SMF 2.0 but not in SMF 2.1.
*/
class OldFiles extends OldFilesBase
{
Expand Down
2 changes: 0 additions & 2 deletions Sources/Maintenance/Cleanup/v3_0/TasksDirCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ class TasksDirCase extends CleanupBase
****************/

/**
* Check if the task should be performed or not.
*
* @return bool True if this task needs to be run, false otherwise.
*/
public function isCandidate(): bool
{
Expand Down
17 changes: 11 additions & 6 deletions Sources/Maintenance/Maintenance.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class Maintenance
public function __construct()
{
Security::frameOptionsHeader('SAMEORIGIN');
self::$theme_dir = \dirname(SMF_SETTINGS_FILE) . '/Themes/default';
self::$theme_dir = self::getBaseDir() . '/Themes/default';

// This might be overwritten by the tool, but we need a default value.
self::$context['started'] = (int) TIME_START;
Expand Down Expand Up @@ -744,12 +744,17 @@ public static function loginWithDatabasePassword(
*/
public static function getTimeElapsed(): string
{
// How long have we been running this?
$elapsed = time() - (int) self::$context['started'];
$mins = (int) ($elapsed / 60);
$seconds = $elapsed - $mins * 60;
$duration = (new \DateTime('@' . self::$context['started']))->diff(new \DateTime());

return Lang::getTxt('maintenance_time_elasped_ms', ['m' => $mins, 's' => $seconds]);
if ((int) $duration->format('%a') > 0) {
return \strval((int) $duration->format('%h') + ((int) $duration->format('%a') * 24)) . $duration->format(':%I:%S');
}

if ((int) $duration->format('%h') > 0) {
return $duration->format('%h:%I:%S');
}

return $duration->format('%i:%S');
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Sources/Maintenance/Migration/v2_1/PostgreSqlSequences.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public function execute(): bool
[
'key' => Config::$db_prefix . $value['key'],
'field' => $value['field'],
'table' => $value['table'],
'table' => Config::$db_prefix . $value['table'],
],
);
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Maintenance/Migration/v3_0/PostgreSqlFunctions.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function execute(): bool
{
$schema_version = substr(__NAMESPACE__, strrpos(__NAMESPACE__, '\\', -1) + 1);

$queries = Table::getInitializers($schema_version, POSTGRE_TITLE);
$queries = Table::getInitializers($schema_version);

foreach ($queries as $query) {
// Use the upgrade query handler.
Expand Down
2 changes: 1 addition & 1 deletion Sources/Maintenance/Tools/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ public function databasePopulation(): bool
// Some initialization may exist.
Db::$db->disableQueryCheck = true;

foreach (Table::getInitializers($this->schema_version, Db::$db->title) as $query) {
foreach (Table::getInitializers($this->schema_version) as $query) {
Db::$db->query($query, [
'security_override' => true,
]);
Expand Down
11 changes: 10 additions & 1 deletion Sources/Maintenance/Tools/ToolsBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ public function checkAndHandleTimeout(): void
Maintenance::setQueryString();
}

Maintenance::exit();
Maintenance::exit(Maintenance::isJson());

throw new \Exception('Zombies!');
}
Expand All @@ -585,6 +585,15 @@ public function updateSettingsFile(array $config_vars, ?bool $keep_quotes = null
$this->logProgress(Lang::getTxt('log_settings_file_save', ['setting_names' => Lang::sentenceList(array_keys($config_vars))], file: 'Maintenance'), true);
}

if ($rebuild) {
// Remove all the existing comments to make the rebuild nice and clean.
Config::safeFileWrite(
file: SMF_SETTINGS_FILE,
data: Config::stripPhpComments(file_get_contents(SMF_SETTINGS_FILE)),
mtime: time(),
);
}

if (!Config::updateSettingsFile($config_vars, $keep_quotes, $rebuild)) {
$this->logProgress(Lang::getTxt('log_failed_with_error', ['error' => Lang::getTxt('settings_error', file: 'Maintenance')], file: 'Maintenance'));

Expand Down
Loading
Loading