Skip to content

[3.0] Adds migration and cleanup steps for upgrading from old versions#9120

Open
Sesquipedalian wants to merge 21 commits intoSimpleMachines:release-3.0from
Sesquipedalian:3.0/upgrade_old_versions
Open

[3.0] Adds migration and cleanup steps for upgrading from old versions#9120
Sesquipedalian wants to merge 21 commits intoSimpleMachines:release-3.0from
Sesquipedalian:3.0/upgrade_old_versions

Conversation

@Sesquipedalian
Copy link
Member

I've had this sitting around nearly completed for months now, so I thought I'd take a few hours to finish it off and submit it.

This PR adds all the necessary table schemas, migration steps, and cleanup steps required to allow the upgrader to upgrade any version of SMF or even YaBB SE.

@jdarwood007, I know that we had previously discussed writing converters for old SMF versions rather than upgrader steps. However, when I sat down to do it, writing upgrader steps turned out to be much easier, because I could base them on the old upgrade scripts rather than trying to come up with entirely new logic. And since everything in the new SMF\Maintenance paradigm is so clearly defined and reliable, I don't think these new upgrader steps are going to be a burden to maintain the way the old upgrade scripts were.

@sbulen, is there any chance you could fire up that marvellous test suite of yours to make sure that these all work? Overall, I expect that it go well, but there are doubtless some copy-paste errors and typos hiding in there someplace.

@Sesquipedalian Sesquipedalian added this to the 3.0 Alpha 5 milestone Feb 22, 2026
@Sesquipedalian Sesquipedalian changed the title Adds migration and cleanup steps for upgrading from old versions [3.0] Adds migration and cleanup steps for upgrading from old versions Feb 22, 2026
@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch from a2e1ec0 to ff9e7b5 Compare February 22, 2026 05:48
@sbulen
Copy link
Contributor

sbulen commented Feb 26, 2026

First attempt at running an upgrade with this PR tonight. First test was a 2.1 to 3.0 upgrade.

I had to do a composer update to get this to work. I actually wonder if the /vendor contents & composer lock should have been in the commits??? Not sure what the proper protocol should be. I was able to get past that by committing the update to composer.lock locally in my test branch for this PR.

Next problem is that the 'Continue' buttons are all associated with 3 second countdowns. I don't think there should be any countdown at all... That wasn't even enough time to type in my credentials. And it shouldn't continue until I say so...

I got past that by updating the upgrade template countdown to 30. And reading and typing quickly.

Next problem is that it appears to have automatically started trying to do a postgresql upgrade on my mysql database. Screenshot below. I wasn't able to get past this.

Not sure why it's attempting pg, I didn't even have pg started. And the 2.1 style Settings.php file specifies $db_type = 'mysql';

Note that with this screen, it's still doing an auto-continue every 3 seconds:
image

Mysql 8.4.4
PHP 8.4.5

@Sesquipedalian
Copy link
Member Author

Well, that's a bunch of weird surprises. I didn't touch any of those things in this PR. But I guess we'll need to fix them so you can test.

@Sesquipedalian
Copy link
Member Author

I had to do a composer update to get this to work. I actually wonder if the /vendor contents & composer lock should have been in the commits??? Not sure what the proper protocol should be. I was able to get past that by committing the update to composer.lock locally in my test branch for this PR.

I'm not sure why you had to do that. There are no differences between this branch and the release-3.0 branch for composer.lock, nor any for composer.json.

Next problem is that the 'Continue' buttons are all associated with 3 second countdowns. I don't think there should be any countdown at all... That wasn't even enough time to type in my credentials. And it shouldn't continue until I say so...

I got past that by updating the upgrade template countdown to 30. And reading and typing quickly.

I could have sworn that I removed that in an earlier PR.

Next problem is that it appears to have automatically started trying to do a postgresql upgrade on my mysql database. Screenshot below. I wasn't able to get past this.

Not sure why it's attempting pg, I didn't even have pg started. And the 2.1 style Settings.php file specifies $db_type = 'mysql';

That's extremely weird. More investigation is needed before I can even begin to guess why you saw that.

@Sesquipedalian
Copy link
Member Author

Next problem is that the 'Continue' buttons are all associated with 3 second countdowns. I don't think there should be any countdown at all... That wasn't even enough time to type in my credentials. And it shouldn't continue until I say so...

I got past that by updating the upgrade template countdown to 30. And reading and typing quickly.

I believe the latest commit has fixed that one. It appears to have been introduced in #9090.

@Sesquipedalian
Copy link
Member Author

Sesquipedalian commented Feb 26, 2026

Next problem is that it appears to have automatically started trying to do a postgresql upgrade on my mysql database. Screenshot below. I wasn't able to get past this.

Not sure why it's attempting pg, I didn't even have pg started. And the 2.1 style Settings.php file specifies $db_type = 'mysql';

Note that with this screen, it's still doing an auto-continue every 3 seconds:

Oh, now I see. The screenshot doesn't indicate that the upgrader thought your database was PostgreSQL. It is just showing that the upgrader was walking through some of the early migration substeps that apply only to PostgreSQL (and skipping them because they weren't applicable) when it encountered an error with the message "TypeError: NetworkError when attempting to fetch resource."

The question is, why did it encounter that error? Based on a quick web search, it indicates a CORS problem while making an HTTP request, which is rather odd in this context.

To help us figure where the heck that error is coming from, please check:

  1. Your browser's console log to see what it might reveal about this error.
  2. The contents of the ./logs/install.log file that SMF 3.0 creates to log installation progress.
  3. Your server's logs.

@sbulen
Copy link
Contributor

sbulen commented Feb 26, 2026

Good news: I refreshed the code from the latest this morning, & the 2.1 => 3.0 upgrade went well!

I found that the composer.lock was simply because I'd run the composer update & there is a new version of fixer available.

Side note: I haven't had a successful 3.0 installer run in a very long time (I'd logged several issues early 2025). I still cannot get it to run, with or without this PR. Logged #9131

I just attempted a 2.0 (latin1) => 3.0 upgrade & got the following error. All files are there, I believe, & I double-checked the files used in the file check, and the versions look good. Haven't dug much deeper; I see the code looks for some classes also, it might be failing there.

A 2.0 (utf8) => 3.0 upgrade attempt has the same result.

image

@sbulen
Copy link
Contributor

sbulen commented Feb 26, 2026

I attempted a 1.1 => 3.0 upgrade & got a WSOD with the following:

Warning: Undefined array key "custom_avatar_url" in D:\wamp64\www\van1121\Sources\QueryString.php on line 778

Fatal error: Uncaught TypeError: strtr(): Argument # 1 ($string) must be of type string, null given in D:\wamp64\www\van1121\Sources\QueryString.php:778 Stack trace: #0 D:\wamp64\www\van1121\Sources\QueryString.php(778): strtr(NULL, Array) # 1 D:\wamp64\www\van1121\Sources\QueryString.php(295): SMF\QueryString::fixUrl() # 2 D:\wamp64\www\van1121\Sources\Maintenance\Tools\Upgrade.php(549): SMF\QueryString::cleanRequest() # 3 D:\wamp64\www\van1121\Sources\Maintenance\Maintenance.php(255): SMF\Maintenance\Tools\Upgrade->__construct() # 4 D:\wamp64\www\van1121\upgrade.php(24): SMF\Maintenance\Maintenance->execute(2) # 5 {main} thrown in D:\wamp64\www\van1121\Sources\QueryString.php on line 778

Pretty sure custom avatars didn't exist before 2.0?

@sbulen
Copy link
Contributor

sbulen commented Feb 26, 2026

I attempted a 1.0 => 3.0 upgrade & got a different WSOD:

Fatal error: Cannot redeclare class SMF\Maintenance\Migration\v1_0\BoardsAndCategories (previously declared in D:\wamp64\www\van1023\Sources\Maintenance\Migration\v1_0\BoardsAndCategories.php:22) in D:\wamp64\www\van1023\Sources\Maintenance\Migration\v1_0\AnnouncementPermissions.php on line 22

@sbulen
Copy link
Contributor

sbulen commented Feb 26, 2026

yabbse:

Fatal error: Uncaught TypeError: is_dir(): Argument # 1 ($filename) must be of type string, false given in D:\wamp64\www\yabbse\Sources\Config.php:1009 Stack trace: # 0 D:\wamp64\www\yabbse\Sources\Config.php(1009): is_dir(false) # 1 D:\wamp64\www\yabbse\index.php(127): SMF\Config::set(Array) # 2 D:\wamp64\www\yabbse\index.php(140): {closure:D:\wamp64\www\yabbse\index.php:102}() # 3 D:\wamp64\www\yabbse\upgrade.php(20): require_once('D:\wamp64\www\y...') # 4 {main} thrown in D:\wamp64\www\yabbse\Sources\Config.php on line 1009

It's been a while since I've run these, always possible I'm grappling with environment issues.

@sbulen
Copy link
Contributor

sbulen commented Feb 27, 2026

I took a peek at the 2.0 issue. First time seeing this code, so take this with a grain of salt...

It finds all the files OK, Upgrade.php line 704.

When it starts looking for the 2.0 classes is where there is an issue, Upgrade.php, line 718. It finds & loads the first one fine (SMF\Maintenance\Migration\v2_0\PostgreSQLFunctions), and loadClass() is properly returning true. So far, so good...

But for some reason it still throws the exception on line 719. I believe it's falling back thru a closure & losing the "true" there.

So, well hidden under the covers, it is throwing the error: Exception->__construct($message = 'SMF\Maintenance\Migration\v2_0\PostgreSQLFunctions does not exist') D:\wamp64\www\van2021\Sources\Maintenance\Tools\Upgrade.php:719

When in fact it was loaded OK.

@sbulen
Copy link
Contributor

sbulen commented Feb 27, 2026

Note that loadClass actually does an include, which in turn, recursively includes MigrationBase & SubStepInterface immediately after PostgreSQLFunctions. It's possible there's an issue with one of those? Topic me, all appear to load ok.

@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch from 356c8c9 to ddf782e Compare February 28, 2026 19:16
@Sesquipedalian
Copy link
Member Author

I took a peek at the 2.0 issue. First time seeing this code, so take this with a grain of salt...

It finds all the files OK, Upgrade.php line 704.

When it starts looking for the 2.0 classes is where there is an issue, Upgrade.php, line 718. It finds & loads the first one fine (SMF\Maintenance\Migration\v2_0\PostgreSQLFunctions), and loadClass() is properly returning true. So far, so good...

But for some reason it still throws the exception on line 719. I believe it's falling back thru a closure & losing the "true" there.

So, well hidden under the covers, it is throwing the error: Exception->__construct($message = 'SMF\Maintenance\Migration\v2_0\PostgreSQLFunctions does not exist') D:\wamp64\www\van2021\Sources\Maintenance\Tools\Upgrade.php:719

When in fact it was loaded OK.

Note that loadClass actually does an include, which in turn, recursively includes MigrationBase & SubStepInterface immediately after PostgreSQLFunctions. It's possible there's an issue with one of those? Topic me, all appear to load ok.

Just trying to clarify here. When you write "loadClass", do you mean "class_exists"?

@sbulen
Copy link
Contributor

sbulen commented Feb 28, 2026

class_exists by default forces an autoload, which calls Composer's loadClass as well as many layers of other stuff... A fractal recursive maze of OOPsie.

check_exists loads everything. If that's not desired, I think you can pass false as 2nd param.

I'm not sure why that's failing though. Seems to be loading fine - including the layers of base classes & interfaces. Makes no sense.

@sbulen
Copy link
Contributor

sbulen commented Feb 28, 2026

I think I found my new forum signature: "A fractal recursive maze of OOPsie."

@sbulen
Copy link
Contributor

sbulen commented Feb 28, 2026

The last class loaded was SubStepInterface.

This might make sense if there were an issue with SubStepInterface, but I don't see one.

@Sesquipedalian
Copy link
Member Author

Gotcha. Hm, that's still odd. I don't have any ideas about it yet. I'll have to try again to see if I can reproduce the error locally.

@sbulen
Copy link
Contributor

sbulen commented Mar 1, 2026

OK, some progress... My version of Composer was a few months old, 2.8.8. Latest version is 2.9.5, and the error was clearly deep in the heart of Composer, so...

I did a composer self-update to 2.9.5, and now I'm getting further... But the error now is identical to the 1.1 error. And yes, I refreshed to get the most recent commits:

Warning: Undefined array key "custom_avatar_url" in D:\wamp64\www\van2021\Sources\QueryString.php on line 778

Fatal error: Uncaught TypeError: strtr(): Argument # 1 ($string) must be of type string, null given in D:\wamp64\www\van2021\Sources\QueryString.php:778 Stack trace: # 0 D:\wamp64\www\van2021\Sources\QueryString.php(778): strtr(NULL, Array) # 1 D:\wamp64\www\van2021\Sources\QueryString.php(295): SMF\QueryString::fixUrl() # 2 D:\wamp64\www\van2021\Sources\Maintenance\Tools\Upgrade.php(549): SMF\QueryString::cleanRequest() # 3 D:\wamp64\www\van2021\Sources\Maintenance\Maintenance.php(255): SMF\Maintenance\Tools\Upgrade->__construct() # 4 D:\wamp64\www\van2021\upgrade.php(24): SMF\Maintenance\Maintenance->execute(2) # 5 {main} thrown in D:\wamp64\www\van2021\Sources\QueryString.php on line 778

@sbulen
Copy link
Contributor

sbulen commented Mar 1, 2026

Odd the 2.1 => 3.0 upgrade was not impacted, but the 2.0 => 3.0 upgrade was...

@Sesquipedalian
Copy link
Member Author

custom_avatar_url

That should be an easy fix. I'll push a commit for that later tonight.

@Sesquipedalian
Copy link
Member Author

Does that latest commit allow installation to proceed, @sbulen?

@sbulen
Copy link
Contributor

sbulen commented Mar 4, 2026

Sorry, wasn't sure you wanted a retest yet.

Reran 2.1 => 3.0 - OK
Reran 2.0 (latin1) =>3.0 - Issue below.
Reran 2.0 (utf8) =>3.0 - Issue below.

Latin1:

Fatal error: Uncaught TypeError: SMF\Config::safeFileWrite(): Argument # 4 ($mtime) must be of type ?int, float given, called in D:\wamp64\www\van2021\Sources\Maintenance\Tools\ToolsBase.php on line 590 and defined in D:\wamp64\www\van2021\Sources\Config.php:2397 Stack trace: # 0 D:\wamp64\www\van2021\Sources\Maintenance\Tools\ToolsBase.php(590): SMF\Config::safeFileWrite('D:\wamp64\www\v...', '<?php\n\n\n\n\n$main...', NULL, 1772654716.5724) # 1 D:\wamp64\www\van2021\Sources\Maintenance\Tools\Upgrade.php(1141): SMF\Maintenance\Tools\ToolsBase->updateSettingsFile(Array, false, true) # 2 D:\wamp64\www\van2021\Sources\Maintenance\Maintenance.php(294): SMF\Maintenance\Tools\Upgrade->upgradeOptions() # 3 D:\wamp64\www\van2021\upgrade.php(24): SMF\Maintenance\Maintenance->execute(2) # 4 {main} thrown in D:\wamp64\www\van2021\Sources\Config.php on line 2397

utf8:

Fatal error: Uncaught TypeError: Cannot assign string to property SMF\Config::$db_port of type int in D:\wamp64\www\van20u8\Sources\Maintenance\Tools\Upgrade.php:1122 Stack trace: # 0 D:\wamp64\www\van20u8\Sources\Maintenance\Maintenance.php(294): SMF\Maintenance\Tools\Upgrade->upgradeOptions() # 1 D:\wamp64\www\van20u8\upgrade.php(24): SMF\Maintenance\Maintenance->execute(2) # 2 {main} thrown in D:\wamp64\www\van20u8\Sources\Maintenance\Tools\Upgrade.php on line 1122

@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch 2 times, most recently from 69a55d1 to d92af01 Compare March 6, 2026 18:26
@Sesquipedalian
Copy link
Member Author

I've now pushed up fixes for all issues reported so far. When you are able, @sbulen, please check out a new copy of this brach and try again.

@sbulen
Copy link
Contributor

sbulen commented Mar 13, 2026

Note I see this in the PHP log... Should have looked at this earlier...

This includes errors from all yesterday's tests...

[12-Mar-2026 23:16:22 UTC] PHP Warning: Undefined array key 72 in D:\wamp64\www\van2130\Sources\Maintenance\Utf8ConverterStep.php on line 746
[12-Mar-2026 23:16:22 UTC] PHP Warning: Attempt to read property "name" on null in D:\wamp64\www\van2130\Sources\Maintenance\Utf8ConverterStep.php on line 746
[12-Mar-2026 23:22:17 UTC] PHP Warning: Undefined variable $name_changes in D:\wamp64\www\van2021\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387
[12-Mar-2026 23:22:17 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van2021\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387
[12-Mar-2026 23:24:34 UTC] PHP Warning: Undefined variable $name_changes in D:\wamp64\www\van20u8\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387
[12-Mar-2026 23:24:34 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van20u8\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387
[12-Mar-2026 23:29:19 UTC] PHP Warning: Undefined variable $to_drop in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68
[12-Mar-2026 23:29:19 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68
[12-Mar-2026 23:32:00 UTC] PHP Warning: Undefined variable $to_drop in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68
[12-Mar-2026 23:32:00 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68

@sbulen
Copy link
Contributor

sbulen commented Mar 13, 2026

OK today's tests!

Another timer nit... Timer looks better, but only increments after major steps are completed. This in the past was an indicator it was actually running, even with debug off. In fact, it said so on the screen... "The time elapsed increments from the server to show progress is being made!" That was helpful on large forum upgrades.

I'm asking for debug on on all these, since that seems to provide better info.

2.1:

image

*** Nothing in the php/mysql/apache logs...

This is the bottom of the upgrader log:

...
+++ Add any missing functions (PostgreSQL)... skipped.
+++ Converting MySQL tables to the InnoDB engine and dynamic rows... done.
+++ Language Upgrade... done.
+++ Setting default value for log_errors.session column... done.
+++ Adding support for edit history... done.
+++ Adding version information to posts and personal messages... done.
+++ Adding SMF version information to log_packages... done.
+++ Adding support for recurring events...

2.0 latin1:

image

PHP log:
[13-Mar-2026 17:12:58 UTC] PHP Warning: Undefined variable $name_changes in D:\wamp64\www\van2021\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387
[13-Mar-2026 17:12:58 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van2021\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387

2.0 utf8:

image

PHP log:
[13-Mar-2026 17:15:07 UTC] PHP Warning: Undefined variable $name_changes in D:\wamp64\www\van20u8\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387
[13-Mar-2026 17:15:07 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van20u8\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387

1.1:

image

PHP log:
[13-Mar-2026 17:16:35 UTC] PHP Warning: Undefined variable $to_drop in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68
[13-Mar-2026 17:16:35 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68

@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch from b1f4238 to d9dceb2 Compare March 17, 2026 19:45
@Sesquipedalian
Copy link
Member Author

Sesquipedalian commented Mar 17, 2026

2.0 latin1:

image PHP log: [13-Mar-2026 17:12:58 UTC] PHP Warning: Undefined variable $name_changes in D:\wamp64\www\van2021\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387 [13-Mar-2026 17:12:58 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van2021\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387

2.0 utf8:

image PHP log: [13-Mar-2026 17:15:07 UTC] PHP Warning: Undefined variable $name_changes in D:\wamp64\www\van20u8\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387 [13-Mar-2026 17:15:07 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van20u8\Sources\Maintenance\Migration\v2_0\RenameColumns.php on line 387

1.1:

image PHP log: [13-Mar-2026 17:16:35 UTC] PHP Warning: Undefined variable $to_drop in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68 [13-Mar-2026 17:16:35 UTC] PHP Warning: foreach() argument must be of type array|object, null given in D:\wamp64\www\van1121\Sources\Maintenance\Migration\v1_1\RemoveIndexes.php on line 68

These three should be fixed in the latest commits.

@Sesquipedalian
Copy link
Member Author

Sesquipedalian commented Mar 17, 2026

2.1:

image *** Nothing in the php/mysql/apache logs...

This is the bottom of the upgrader log:

...
+++ Add any missing functions (PostgreSQL)... skipped.
+++ Converting MySQL tables to the InnoDB engine and dynamic rows... done.
+++ Language Upgrade... done.
+++ Setting default value for log_errors.session column... done.
+++ Adding support for edit history... done.
+++ Adding version information to posts and personal messages... done.
+++ Adding SMF version information to log_packages... done.
+++ Adding support for recurring events...

Please temporarily replace the contents of ./Sources/Maintenance/Migrations/v3_0/RecurringEvents.php with the following. Like before, it will dump a mess into your install.log that should help me figure out the problem.

<?php

/**
 * Simple Machines Forum (SMF)
 *
 * @package SMF
 * @author Simple Machines https://www.simplemachines.org
 * @copyright 2026 Simple Machines and individual contributors
 * @license https://www.simplemachines.org/about/smf/license.php BSD
 *
 * @version 3.0 Alpha 4
 */

declare(strict_types=1);

namespace SMF\Maintenance\Migration\v3_0;

use SMF\Db\DatabaseApi as Db;
use SMF\Db\Schema;
use SMF\Maintenance\Migration\MigrationBase;

class RecurringEvents extends MigrationBase
{
	/*******************
	 * Public properties
	 *******************/

	/**
	 *
	 */
	public string $name = 'Adding support for recurring events';

	/****************
	 * Public methods
	 ****************/

	/**
	 *
	 */
	public function execute(): bool
	{
		Maintenance::$tool->logProgress('Getting calendar table definition', true);
		$table = new Schema\v3_0\Calendar();
		Maintenance::$tool->logProgress('Getting current calendar table structure', true);
		$existing_structure = $table->getCurrentStructure();

		Maintenance::$tool->logProgress('Adding missing columns', true);
		foreach ($table->columns as $column) {
			if (!isset($existing_structure['columns'][$column->name])) {
				Maintenance::$tool->logProgress($column->name, true);
				$table->addColumn($column);
			}

			$this->handleTimeout();
		}

		if (Db::$db->title === MYSQL_TITLE) {
			Maintenance::$tool->logProgress('Rearranging columns', true);
			Db::$db->query(
				'ALTER TABLE {db_prefix}calendar
				MODIFY COLUMN start_date DATE AFTER id_member',
				[],
			);

			Db::$db->query(
				'ALTER TABLE {db_prefix}calendar
				MODIFY COLUMN end_date DATE AFTER start_date',
				[],
			);

		}

		Maintenance::$tool->logProgress('Building updates', true);
		$updates = [];

		$request = Db::$db->query(
			'SELECT id_event, start_date, end_date, start_time, end_time, timezone
			FROM {db_prefix}calendar',
			[],
		);

		while ($row = Db::$db->fetch_assoc($request)) {
			try {
			$row = array_diff($row, array_filter($row, 'is_null'));

			$allday = (
				!isset($row['start_time'])
				|| !isset($row['end_time'])
				|| !isset($row['timezone'])
				|| !\in_array($row['timezone'], timezone_identifiers_list(\DateTimeZone::ALL_WITH_BC))
			);

			$start = new \DateTime($row['start_date'] . (!$allday ? ' ' . $row['start_time'] . ' ' . $row['timezone'] : ''));
			$end = new \DateTime($row['end_date'] . (!$allday ? ' ' . $row['end_time'] . ' ' . $row['timezone'] : ''));

			if ($allday) {
				$end->modify('+1 day');
			}

			$duration = date_diff($start, $end);

			$format = '';

			foreach (['y', 'm', 'd', 'h', 'i', 's'] as $part) {
				if ($part === 'h') {
					$format .= 'T';
				}

				if (!empty($duration->{$part})) {
					$format .= '%' . $part . ($part === 'i' ? 'M' : strtoupper($part));
				}
			}
			$format = rtrim('P' . $format, 'PT');

			// TODO: Fix it right.
			if ((int) $end->format('Y') > 9999) {
				$end->setDate(9999, (int) $end->format('m'), (int) $end->format('d'));
			}

			$updates[$row['id_event']] = [
				'id_event' => $row['id_event'],
				'duration' => $duration->format($format),
				'end_date' => $end->format('Y-m-d'),
				'rrule' => 'FREQ=YEARLY;COUNT=1',
			];
			} catch (\Throwable $e) {
				Maintenance::$tool->logProgress($e->getMessage(), true);
				throw $e;
			}
		}
		Db::$db->free_result($request);

		foreach ($updates as $id_event => $changes) {
			Maintenance::$tool->logProgress('Submitting update: ' . \SMF\Utils::normalizeSpaces(\SMF\Config::varExport($changes), true, true, ['nobreaks' => true]), true);
			Db::$db->query(
				'UPDATE {db_prefix}calendar
				SET duration = {string:duration}, end_date = {date:end_date}, rrule = {string:rrule}
				WHERE id_event = {int:id_event}',
				$changes,
			);
		}

		Maintenance::$tool->logProgress('Removing end_time column', true);
		Db::$db->remove_column('{db_prefix}calendar', 'end_time');

		Maintenance::$tool->logProgress('returning', true);
		return true;
	}
}

@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch from ccec6b6 to 5e5a473 Compare March 18, 2026 22:10
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch 2 times, most recently from e1172c5 to a1f0fab Compare March 19, 2026 06:36
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
@Sesquipedalian Sesquipedalian force-pushed the 3.0/upgrade_old_versions branch from a1f0fab to ac9f699 Compare March 19, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants