Add settings-driven theme and version metadata
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"type": "project",
|
||||
"version": "25.00.1",
|
||||
"license": "proprietary",
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
@@ -79,6 +80,9 @@
|
||||
"symfony/symfony": "*"
|
||||
},
|
||||
"extra": {
|
||||
"speedbb": {
|
||||
"build": 3
|
||||
},
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "8.0.*"
|
||||
|
||||
27
api/migrations/Version20251224184500.php
Normal file
27
api/migrations/Version20251224184500.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20251224184500 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add settings table with version and build metadata.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE settings (id INT AUTO_INCREMENT NOT NULL, version VARCHAR(20) NOT NULL, build INT NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql("INSERT INTO settings (id, version, build) VALUES (1, '25.00.1', 3)");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP TABLE settings');
|
||||
}
|
||||
}
|
||||
33
api/migrations/Version20251224191500.php
Normal file
33
api/migrations/Version20251224191500.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20251224191500 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Convert settings table to key/value rows for version/build.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE settings_new (id INT AUTO_INCREMENT NOT NULL, `key` VARCHAR(100) NOT NULL, value LONGTEXT NOT NULL, UNIQUE INDEX UNIQ_SETTINGS_KEY (`key`), PRIMARY KEY(id))');
|
||||
$this->addSql("INSERT INTO settings_new (`key`, value) SELECT 'version', version FROM settings LIMIT 1");
|
||||
$this->addSql("INSERT INTO settings_new (`key`, value) SELECT 'build', CAST(build AS CHAR) FROM settings LIMIT 1");
|
||||
$this->addSql('DROP TABLE settings');
|
||||
$this->addSql('RENAME TABLE settings_new TO settings');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE settings_old (id INT AUTO_INCREMENT NOT NULL, version VARCHAR(20) NOT NULL, build INT NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql("INSERT INTO settings_old (id, version, build) VALUES (1, (SELECT value FROM settings WHERE `key` = 'version' LIMIT 1), CAST((SELECT value FROM settings WHERE `key` = 'build' LIMIT 1) AS UNSIGNED))");
|
||||
$this->addSql('DROP TABLE settings');
|
||||
$this->addSql('RENAME TABLE settings_old TO settings');
|
||||
}
|
||||
}
|
||||
28
api/migrations/Version20251224193000.php
Normal file
28
api/migrations/Version20251224193000.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20251224193000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add indexes for forum parent ordering and type filters.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE INDEX idx_forum_parent_position ON forum (parent_id, position)');
|
||||
$this->addSql('CREATE INDEX idx_forum_type ON forum (type)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP INDEX idx_forum_parent_position ON forum');
|
||||
$this->addSql('DROP INDEX idx_forum_type ON forum');
|
||||
}
|
||||
}
|
||||
26
api/migrations/Version20251224194000.php
Normal file
26
api/migrations/Version20251224194000.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20251224194000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add accent color setting.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql("INSERT IGNORE INTO settings (`key`, value) VALUES ('accent_color', '#f29b3f')");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql("DELETE FROM settings WHERE `key` = 'accent_color'");
|
||||
}
|
||||
}
|
||||
24
api/src/Controller/VersionController.php
Normal file
24
api/src/Controller/VersionController.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Settings;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
final class VersionController
|
||||
{
|
||||
#[Route('/api/version', name: 'api_version', methods: ['GET'])]
|
||||
public function __invoke(EntityManagerInterface $entityManager): JsonResponse
|
||||
{
|
||||
$repository = $entityManager->getRepository(Settings::class);
|
||||
$version = $repository->findOneBy(['key' => 'version']);
|
||||
$build = $repository->findOneBy(['key' => 'build']);
|
||||
|
||||
return new JsonResponse([
|
||||
'version' => $version?->getValue(),
|
||||
'build' => $build ? (int) $build->getValue() : null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
#[ORM\Table(indexes: [
|
||||
new ORM\Index(name: 'idx_forum_parent_position', columns: ['parent_id', 'position']),
|
||||
new ORM\Index(name: 'idx_forum_type', columns: ['type']),
|
||||
])]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['parent' => 'exact', 'type' => 'exact'])]
|
||||
#[ApiFilter(ExistsFilter::class, properties: ['parent'])]
|
||||
#[ApiResource(
|
||||
|
||||
69
api/src/Entity/Settings.php
Normal file
69
api/src/Entity/Settings.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['key' => 'exact'])]
|
||||
#[ApiResource(
|
||||
operations : [
|
||||
new Get(),
|
||||
new GetCollection(),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')")
|
||||
],
|
||||
normalizationContext : ['groups' => ['settings:read']],
|
||||
denormalizationContext: ['groups' => ['settings:write']]
|
||||
)]
|
||||
class Settings
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['settings:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 100, unique: true)]
|
||||
#[Groups(['settings:read', 'settings:write'])]
|
||||
private string $key = '';
|
||||
|
||||
#[ORM\Column(type: 'text')]
|
||||
#[Groups(['settings:read', 'settings:write'])]
|
||||
private string $value = '';
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setKey(string $key): self
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(string $value): self
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,18 @@ msgstr "Abmelden"
|
||||
msgid "nav.language"
|
||||
msgstr "Sprache"
|
||||
|
||||
msgid "nav.theme"
|
||||
msgstr "Design"
|
||||
|
||||
msgid "nav.theme_auto"
|
||||
msgstr "Auto"
|
||||
|
||||
msgid "nav.theme_light"
|
||||
msgstr "Hell"
|
||||
|
||||
msgid "nav.theme_dark"
|
||||
msgstr "Dunkel"
|
||||
|
||||
msgid "home.hero_title"
|
||||
msgstr "Foren"
|
||||
|
||||
@@ -115,7 +127,7 @@ msgid "auth.register_hint"
|
||||
msgstr "Registriere dich mit E-Mail und einem eindeutigen Benutzernamen."
|
||||
|
||||
msgid "footer.copy"
|
||||
msgstr "speedBB Forum. Powered by API Platform und React-Bootstrap."
|
||||
msgstr "speedBB"
|
||||
|
||||
msgid "form.title"
|
||||
msgstr "Titel"
|
||||
|
||||
@@ -21,6 +21,18 @@ msgstr "Logout"
|
||||
msgid "nav.language"
|
||||
msgstr "Language"
|
||||
|
||||
msgid "nav.theme"
|
||||
msgstr "Theme"
|
||||
|
||||
msgid "nav.theme_auto"
|
||||
msgstr "Auto"
|
||||
|
||||
msgid "nav.theme_light"
|
||||
msgstr "Light"
|
||||
|
||||
msgid "nav.theme_dark"
|
||||
msgstr "Dark"
|
||||
|
||||
msgid "home.hero_title"
|
||||
msgstr "Forums"
|
||||
|
||||
@@ -115,7 +127,7 @@ msgid "auth.register_hint"
|
||||
msgstr "Register with an email and a unique username."
|
||||
|
||||
msgid "footer.copy"
|
||||
msgstr "speedBB forum. Powered by API Platform and React-Bootstrap."
|
||||
msgstr "speedBB"
|
||||
|
||||
msgid "form.title"
|
||||
msgstr "Title"
|
||||
|
||||
Reference in New Issue
Block a user