Feature migrations up/down (#270)

* Migrations up down

* Add forum model

* Syntactic sugar for db structure changes

* Refactor migrations with $up & $down

* Fix migrations upgrade and downgrade

+ Add option to disable auto migrate

* Add migrate:to command

Usage: php aac migrate:to x (x - database version)

* Show error when mail is not enabled

* Fixes regarding to init.php

* Add migrate command to manually upgrade db, incase auto migrate is disabled

* Fixed rest of the migrations

* Limit max version of database

* Don't allow minus number

* Option to clear specified plugin settings by name

* Version is required

* Fix PHPStan errors

* Unset $up after migration, to prevent executing same migration twice

* Add database version to output

* This is not needed

* Update 5.php

* Set database_auto_migrate on install

* Set blank & color only if current db version supports it

* Fix duplicate function declaration
This commit is contained in:
Slawomir Boczek
2024-11-22 15:29:23 +01:00
committed by GitHub
parent 79636280a7
commit 3f6ff3a332
72 changed files with 1268 additions and 396 deletions

View File

@@ -16,6 +16,8 @@ class CacheClearCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
if (!clearCache()) {

View File

@@ -2,12 +2,8 @@
namespace MyAAC\Commands;
use MyAAC\Hooks;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
class Command extends SymfonyCommand
{
public function __construct() {
parent::__construct();
}
}

View File

@@ -16,10 +16,11 @@ class CronjobCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
// Create a new scheduler
$scheduler = new Scheduler();
global $hooks;
$hooks->trigger(HOOK_CRONJOB, ['scheduler' => $scheduler]);
// Let the scheduler execute jobs which are due.

View File

@@ -17,6 +17,8 @@ class CronjobInstallCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
if (MYAAC_OS !== 'LINUX') {

View File

@@ -21,8 +21,15 @@ class MailSendCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
if (!setting('core.mail_enabled')) {
$io->error('Mailing is not enabled on this server');
return Command::FAILURE;
}
$email_account_name = $input->getArgument('recipient');
$subject = $input->getOption('subject');
if (!$subject) {

View File

@@ -0,0 +1,28 @@
<?php
namespace MyAAC\Commands;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class MigrateCommand extends Command
{
protected function configure(): void
{
$this->setName('migrate')
->setDescription('This command updates the AAC to latest migration');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
require SYSTEM . 'migrate.php';
$io->success('Migrated to latest version (' . DATABASE_VERSION . ')');
return Command::SUCCESS;
}
}

View File

@@ -21,6 +21,8 @@ class MigrateRunCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
$ids = $input->getArgument('id');

View File

@@ -0,0 +1,108 @@
<?php
namespace MyAAC\Commands;
use MyAAC\Models\Config;
use POT;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class MigrateToCommand extends Command
{
protected function configure(): void
{
$this->setName('migrate:to')
->setDescription('This command migrates to specific version of database')
->addArgument('version',
InputArgument::REQUIRED,
'Version number'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$versionDest = $input->getArgument('version');
if (!$versionDest || $versionDest > DATABASE_VERSION || $versionDest < 1) {
$io->error('Please enter a valid version number');
return Command::FAILURE;
}
$this->initEnv();
$currentVersion = Config::where('name', 'database_version')->first()->value;
if ($currentVersion > $versionDest) {
// downgrade
for ($i = $currentVersion; $i > $versionDest; $i--) {
echo $i . ' ';
$this->executeMigration($i, false);
}
}
else if ($currentVersion < $versionDest) {
// upgrade
for ($i = $currentVersion + 1; $i <= $versionDest; $i++) {
echo $i . ' ';
$this->executeMigration($i, true);
}
}
else {
$io->success('Nothing to be done');
return Command::SUCCESS;
}
$upgrade = ($currentVersion < $versionDest ? 'Upgrade' : 'Downgrade');
$io->success("Migration ({$upgrade}) to version {$versionDest} has been completed");
return Command::SUCCESS;
}
private function executeMigration($id, $_up): void
{
global $db;
$db->revalidateCache();
require SYSTEM . 'migrations/' . $id . '.php';
if ($_up) {
if (isset($up)) {
$up();
}
}
else {
if (isset($down)) {
$down();
}
}
updateDatabaseConfig('database_version', ($_up ? $id : $id - 1));
}
private function initEnv()
{
global $config;
if (!isset($config['installed']) || !$config['installed']) {
throw new \RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.');
}
if(empty($config['server_path'])) {
throw new \RuntimeException('Server Path has been not set. Go to config.php and set it.');
}
// take care of trailing slash at the end
if($config['server_path'][strlen($config['server_path']) - 1] !== '/')
$config['server_path'] .= '/';
$config['lua'] = load_config_lua($config['server_path'] . 'config.lua');
// POT
require_once SYSTEM . 'libs/pot/OTS.php';
$ots = POT::getInstance();
$eloquentConnection = null;
require_once SYSTEM . 'database.php';
}
}

View File

@@ -19,6 +19,8 @@ class PluginInstallCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
$pathToFile = $input->getArgument('pathToPluginZip');

View File

@@ -19,6 +19,8 @@ class PluginInstallInstallCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
$pluginName = $input->getArgument('plugin');

View File

@@ -14,23 +14,43 @@ class SettingsResetCommand extends Command
protected function configure(): void
{
$this->setName('settings:reset')
->setDescription('Removes all settings in database');
->setDescription('Removes settings in database')
->addArgument('name',
InputArgument::OPTIONAL,
'Name of the plugin'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
if (!$io->confirm('Are you sure you want to reset all settings in database?', false)) {
$name = $input->getArgument('name');
$all = !$name ? 'all' : $name;
if (!$io->confirm("Are you sure you want to reset {$all} settings in database?", false)) {
return Command::FAILURE;
}
SettingsModel::truncate();
if (!$name) {
SettingsModel::truncate();
}
else {
$settingsModel = SettingsModel::where('name', $name)->first();
if (!$settingsModel) {
$io->warning('No settings for this plugin saved in database');
return Command::SUCCESS;
}
SettingsModel::where('name', $name)->delete();
}
$settings = Settings::getInstance();
$settings->clearCache();
$io->success('Setting cleared successfully');
$io->success('Settings cleared successfully');
return Command::SUCCESS;
}
}

View File

@@ -27,6 +27,8 @@ class SettingsSetCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
require SYSTEM . 'init.php';
$io = new SymfonyStyle($input, $output);
$key = $input->getArgument('key');

View File

@@ -0,0 +1,30 @@
<?php
namespace MyAAC\Models;
use Illuminate\Database\Eloquent\Model;
class Forum extends Model
{
protected $table = TABLE_PREFIX . 'forum';
public $timestamps = false;
protected $fillable = ['first_post', 'last_post', 'section', 'replies', 'views', 'author_aid', 'author_guid', 'post_text', 'post_topic', 'post_smile', 'post_html', 'post_date', 'last_edit_aid', 'edit_date', 'post_ip', 'sticked', 'closed'];
protected $casts = [
'first_post' => 'integer',
'last_post' => 'integer',
'section' => 'integer',
'replies' => 'integer',
'views' => 'integer',
'author_aid' => 'integer',
'author_guid' => 'integer',
'post_smile' => 'boolean',
'post_html' => 'boolean',
'post_date' => 'integer',
'last_edit_aid' => 'integer',
'edit_date' => 'integer',
'sticked' => 'boolean',
'closed' => 'boolean'
];
}

View File

@@ -770,6 +770,8 @@ class Plugins {
*/
public static function installMenus($templateName, $menus, $clearOld = false)
{
global $db;
if ($clearOld) {
Menu::where('template', $templateName)->delete();
}
@@ -804,10 +806,14 @@ class Plugins {
'link' => $link,
'category' => $category,
'ordering' => $i++,
'blank' => $blank,
'color' => $color,
];
// support for color and blank attributes
if($db->hasColumn(TABLE_PREFIX . 'menu', 'blank') && $db->hasColumn(TABLE_PREFIX . 'menu', 'color')) {
$insert_array['blank'] = $blank;
$insert_array['color'] = $color;
}
Menu::create($insert_array);
}
}