2024.12.09 08:44:47

This commit is contained in:
Christian Moser 2024-12-09 08:44:47 +01:00
parent ff0ab7be47
commit 0e20ab95a7
58 changed files with 2569 additions and 1585 deletions

View File

@ -1 +0,0 @@
de

View File

@ -1,16 +0,0 @@
../src/Constants/const.php
../src/Controller/MainController.php
../src/Controller/SecurityController.php
../src/Controller/SetupController.php
../src/Controller/WebrootController.php
../src/Controller/WebrootSetupController.php
../src/Entity/WebrootFile.php
../src/Entity/WebrootFilePermission.php
../src/Entity/WebrootRole.php
../src/Entity/WebrootUser.php
../src/Kernel.php
../src/Repository/WebrootFilePermissionRepository.php
../src/Repository/WebrootFileRepository.php
../src/Repository/WebrootRoleRepository.php
../src/Repository/WebrootUserRepository.php
../src/Utility/i18n.php

View File

@ -1,10 +0,0 @@
../src/MyDevel/Webroot/Controller/WebrootController.php
../src/MyDevel/Webroot/Controller/WebrootSetupController.php
../src/MyDevel/Webroot/Entity/WebrootFile.php
../src/MyDevel/Webroot/Entity/WebrootFilePermission.php
../src/MyDevel/Webroot/Entity/WebrootRole.php
../src/MyDevel/Webroot/Entity/WebrootUser.php
../src/MyDevel/Webroot/Repository/WebrootFilePermissionRepository.php
../src/MyDevel/Webroot/Repository/WebrootFileRepository.php
../src/MyDevel/Webroot/Repository/WebrootRoleRepository.php
../src/MyDevel/Webroot/Repository/WebrootUserRepository.php

View File

@ -1 +0,0 @@

View File

@ -1,19 +0,0 @@
#!/bin/sh
SELF="$(realpath "$0")"
PO_DIR="$(dirname "$SELF")"
PROJECT_ROOT="$(dirname "$PO_DIR")"
cd "$PO_DIR"
for i in `cat LINGUAS`; do
msgdir="$PROJECT_ROOT/translations/$i/LC_MESSAGES";
if [ ! -d "$msgdir" ]; then
mkdir -p "$msgdir"
fi
if [ -f $i.po ]; then
msgfmt -o "$msgdir/mydevel-webroot.mo" $i.po
fi
done

151
PO/de.po
View File

@ -1,151 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-27 19:03+0100\n"
"PO-Revision-Date: 2024-11-27 19:12+0100\n"
"Last-Translator: Christian Moser <christian@cmoser.eu>\n"
"Language-Team: \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.5\n"
#: ../src/Controller/SetupController.php:45
msgid "Email Settings"
msgstr "Email Einstellungen"
#: ../src/Controller/SetupController.php:46
msgid "Site Settings"
msgstr "Site Einstellungen"
#: ../src/Controller/SetupController.php:47
msgid "Database Settings"
msgstr "Datenbank Einstellungen"
#: ../src/Controller/SetupController.php:48
msgid "User Settings"
msgstr "Nutzer Einstellungen"
#: ../src/Controller/SetupController.php:56
msgid "Enter the site name that should be displayed in titles."
msgstr "Gib den Setiennamen and, der in Titeln angezeigt werden soll."
#: ../src/Controller/SetupController.php:57
msgid "Site name"
msgstr "Name der Site"
#: ../src/Controller/SetupController.php:61
msgid "Site root directory"
msgstr "Wurzelverzeichnis"
#: ../src/Controller/SetupController.php:65
msgid "Contact email"
msgstr "Kontakt Email"
#: ../src/Controller/SetupController.php:69
msgid "Username"
msgstr "Nutzername"
#: ../src/Controller/SetupController.php:73
msgid "Email"
msgstr "Email"
#: ../src/Controller/SetupController.php:77
#: ../src/Controller/SetupController.php:117
#: ../src/Controller/SetupController.php:144
msgid "Password"
msgstr "Passwort"
#: ../src/Controller/SetupController.php:81
msgid "Confirm Password"
msgstr "Passwort bestätigen"
#: ../src/Controller/SetupController.php:84
msgid "Run Migrations?"
msgstr "Migrationen anwenden?"
#: ../src/Controller/SetupController.php:93
msgid "SQLite3"
msgstr "SQLite3"
#: ../src/Controller/SetupController.php:94
msgid "MySQL/MariaDB"
msgstr "MySQL/MariaDB"
#: ../src/Controller/SetupController.php:95
msgid "PostgreSQL"
msgstr "PostgreSQL"
#: ../src/Controller/SetupController.php:96
#: ../src/Controller/SetupController.php:120
msgid "Database URL"
msgstr "Datenbank URL"
#: ../src/Controller/SetupController.php:101
msgid "Database"
msgstr "Datenbank"
#: ../src/Controller/SetupController.php:105
msgid "Host"
msgstr "Host"
#: ../src/Controller/SetupController.php:109
msgid "Port"
msgstr "Port"
#: ../src/Controller/SetupController.php:113
#: ../src/Controller/SetupController.php:140
msgid "User"
msgstr "Nutzer"
#: ../src/Controller/SetupController.php:124
msgid "Backend"
msgstr "Backend"
#: ../src/Controller/SetupController.php:127
msgid "No email support"
msgstr "Keine Emailunterstützung"
#: ../src/Controller/SetupController.php:128
msgid "SMTP"
msgstr "SMTP"
#: ../src/Controller/SetupController.php:129
msgid "Sendmail"
msgstr "Sendmail"
#: ../src/Controller/SetupController.php:130
msgid "Naitve"
msgstr "Nativ"
#: ../src/Controller/SetupController.php:131
msgid "User DSN"
msgstr "Nutzer DSN"
#: ../src/Controller/SetupController.php:136
msgid "Email Path"
msgstr "Email-Pfad"
#: ../src/Controller/SetupController.php:148
msgid "SMTP Host"
msgstr "SMTP Host"
#: ../src/Controller/SetupController.php:152
msgid "SMTP Port"
msgstr "SMTP Port"
#: ../src/Controller/SetupController.php:156
msgid "DSN"
msgstr "DSN"
#: ../src/Controller/SetupController.php:160
msgid "Sender address"
msgstr "Adresse des Senders"

View File

@ -1,35 +0,0 @@
#!/bin/sh
SELF="$(realpath "$0")"
PODIR="$(dirname "$SELF")"
PROJECT_ROOT="$(dirname "$PODIR")"
cd "$PODIR"
echo "Creating POTFILES"
rm -v POTFILES
for i in $(find ../src | grep '\.*php$'); do
echo $i >> POTFILES
done
if [ -f messages.pot ]; then
JOIN="--join-existing"
else
JOIN=""
fi
echo "extracting messages"
xgettext -f POTFILES -d mydevel.webroot -L PHP $JOIN --force-po -o messages.pot
if [ -z "$JOIN" ]; then
sed -i s/charset=CHARSET/charset=UTF-8/g messages.pot
fi
for i in `cat LINGUAS`; do
if [ ! -f $i.po ]; then
cp messages.pot $i.po
else
msgmerge $i.po messages.pot
fi
done

View File

@ -1,150 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-27 19:03+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../src/Controller/SetupController.php:45
msgid "Email Settings"
msgstr ""
#: ../src/Controller/SetupController.php:46
msgid "Site Settings"
msgstr ""
#: ../src/Controller/SetupController.php:47
msgid "Database Settings"
msgstr ""
#: ../src/Controller/SetupController.php:48
msgid "User Settings"
msgstr ""
#: ../src/Controller/SetupController.php:56
msgid "Enter the site name that should be displayed in titles."
msgstr ""
#: ../src/Controller/SetupController.php:57
msgid "Site name"
msgstr ""
#: ../src/Controller/SetupController.php:61
msgid "Site root directory"
msgstr ""
#: ../src/Controller/SetupController.php:65
msgid "Contact email"
msgstr ""
#: ../src/Controller/SetupController.php:69
msgid "Username"
msgstr ""
#: ../src/Controller/SetupController.php:73
msgid "Email"
msgstr ""
#: ../src/Controller/SetupController.php:77
#: ../src/Controller/SetupController.php:117
#: ../src/Controller/SetupController.php:144
msgid "Password"
msgstr ""
#: ../src/Controller/SetupController.php:81
msgid "Confirm Password"
msgstr ""
#: ../src/Controller/SetupController.php:84
msgid "Run Migrations?"
msgstr ""
#: ../src/Controller/SetupController.php:93
msgid "SQLite3"
msgstr ""
#: ../src/Controller/SetupController.php:94
msgid "MySQL/MariaDB"
msgstr ""
#: ../src/Controller/SetupController.php:95
msgid "PostgreSQL"
msgstr ""
#: ../src/Controller/SetupController.php:96
#: ../src/Controller/SetupController.php:120
msgid "Database URL"
msgstr ""
#: ../src/Controller/SetupController.php:101
msgid "Database"
msgstr ""
#: ../src/Controller/SetupController.php:105
msgid "Host"
msgstr ""
#: ../src/Controller/SetupController.php:109
msgid "Port"
msgstr ""
#: ../src/Controller/SetupController.php:113
#: ../src/Controller/SetupController.php:140
msgid "User"
msgstr ""
#: ../src/Controller/SetupController.php:124
msgid "Backend"
msgstr ""
#: ../src/Controller/SetupController.php:127
msgid "No email support"
msgstr ""
#: ../src/Controller/SetupController.php:128
msgid "SMTP"
msgstr ""
#: ../src/Controller/SetupController.php:129
msgid "Sendmail"
msgstr ""
#: ../src/Controller/SetupController.php:130
msgid "Naitve"
msgstr ""
#: ../src/Controller/SetupController.php:131
msgid "User DSN"
msgstr ""
#: ../src/Controller/SetupController.php:136
msgid "Email Path"
msgstr ""
#: ../src/Controller/SetupController.php:148
msgid "SMTP Host"
msgstr ""
#: ../src/Controller/SetupController.php:152
msgid "SMTP Port"
msgstr ""
#: ../src/Controller/SetupController.php:156
msgid "DSN"
msgstr ""
#: ../src/Controller/SetupController.php:160
msgid "Sender address"
msgstr ""

View File

@ -1,18 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-26 20:44+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@ -1,18 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-26 20:44+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@ -7,44 +7,45 @@
"php": ">=8.1", "php": ">=8.1",
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*", "ext-iconv": "*",
"doctrine/dbal": "^3", "doctrine/dbal": "^3.9.3",
"doctrine/doctrine-bundle": "^2.13", "doctrine/doctrine-bundle": "^2.13.1",
"doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/doctrine-migrations-bundle": "^3.3.1",
"doctrine/orm": "^3.3", "doctrine/orm": "^3.3",
"easycorp/easyadmin-bundle": "^4.18", "easycorp/easyadmin-bundle": "4.x-dev",
"phpdocumentor/reflection-docblock": "^5.6", "phpdocumentor/reflection-docblock": "^5.6",
"phpstan/phpdoc-parser": "^2.0", "phpstan/phpdoc-parser": "^2.0",
"symfony/asset": "6.4.*", "symfony/asset": "7.2.*",
"symfony/asset-mapper": "6.4.*", "symfony/asset-mapper": "7.2.*",
"symfony/console": "6.4.*", "symfony/console": "7.2.*",
"symfony/doctrine-messenger": "6.4.*", "symfony/doctrine-messenger": "7.2.*",
"symfony/dotenv": "6.4.*", "symfony/dotenv": "7.2.*",
"symfony/expression-language": "6.4.*", "symfony/expression-language": "7.2.*",
"symfony/flex": "^2", "symfony/flex": "^2.4",
"symfony/form": "6.4.*", "symfony/form": "7.2.*",
"symfony/framework-bundle": "6.4.*", "symfony/framework-bundle": "7.2.*",
"symfony/http-client": "6.4.*", "symfony/http-client": "7.2.*",
"symfony/intl": "6.4.*", "symfony/intl": "7.2.*",
"symfony/mailer": "6.4.*", "symfony/mailer": "7.2.*",
"symfony/mime": "6.4.*", "symfony/mime": "7.2.*",
"symfony/monolog-bundle": "^3.10", "symfony/monolog-bundle": "^3.10",
"symfony/notifier": "6.4.*", "symfony/notifier": "7.2.*",
"symfony/process": "6.4.*", "symfony/process": "7.2.*",
"symfony/property-access": "6.4.*", "symfony/property-access": "7.2.*",
"symfony/property-info": "6.4.*", "symfony/property-info": "7.2.*",
"symfony/runtime": "6.4.*", "symfony/runtime": "^7.2",
"symfony/security-bundle": "6.4.*", "symfony/security-bundle": "7.2.*",
"symfony/serializer": "6.4.*", "symfony/serializer": "7.2.*",
"symfony/stimulus-bundle": "^2.21", "symfony/stimulus-bundle": "^2.22",
"symfony/string": "6.4.*", "symfony/string": "7.2.*",
"symfony/translation": "6.4.*", "symfony/translation": "7.2.*",
"symfony/twig-bundle": "6.4.*", "symfony/twig-bundle": "7.2.*",
"symfony/ux-turbo": "^2.21", "symfony/ux-turbo": "^2.22",
"symfony/validator": "6.4.*", "symfony/validator": "7.2.*",
"symfony/web-link": "6.4.*", "symfony/web-link": "7.2.*",
"symfony/yaml": "6.4.*", "symfony/yaml": "7.2.*",
"twig/extra-bundle": "^2.12|^3.0", "symfonycasts/reset-password-bundle": "^1.23",
"twig/twig": "^2.12|^3.0" "twig/extra-bundle": "^2.12|^3.16",
"twig/twig": "^2.12|^3.16"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {
@ -92,17 +93,17 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "6.4.*" "require": "7.2.*"
} }
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.6.21",
"symfony/browser-kit": "6.4.*", "symfony/browser-kit": "7.2.*",
"symfony/css-selector": "6.4.*", "symfony/css-selector": "7.2.*",
"symfony/debug-bundle": "6.4.*", "symfony/debug-bundle": "7.2.*",
"symfony/maker-bundle": "^1.0", "symfony/maker-bundle": "^1.61",
"symfony/phpunit-bridge": "^7.1", "symfony/phpunit-bridge": "^7.2",
"symfony/stopwatch": "6.4.*", "symfony/stopwatch": "7.2.*",
"symfony/web-profiler-bundle": "6.4.*" "symfony/web-profiler-bundle": "7.2.*"
} }
} }

2133
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -15,4 +15,5 @@ return [
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true], Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
SymfonyCasts\Bundle\ResetPassword\SymfonyCastsResetPasswordBundle::class => ['all' => true],
]; ];

11
config/packages/csrf.yaml Normal file
View File

@ -0,0 +1,11 @@
# Enable stateless CSRF protection for forms and logins/logouts
framework:
form:
csrf_protection:
token_id: submit
csrf_protection:
stateless_token_ids:
- submit
- authenticate
- logout

View File

@ -1,3 +1,4 @@
framework: framework:
mailer: mailer:
dsn: '%env(MAILER_DSN)%' dsn: '%env(MAILER_DSN)%'
message_bus: false

View File

@ -0,0 +1,4 @@
symfonycasts_reset_password:
request_password_repository: App\Repository\ResetPasswordRequestRepository
lifetime: 3600
throttle_limit: 3600

View File

@ -1,4 +0,0 @@
framework:
uid:
default_uuid_version: 7
time_based_uuid_version: 7

View File

@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241201211313 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE rememberme_token (series VARCHAR(88) NOT NULL, value VARCHAR(88) NOT NULL, lastUsed DATETIME NOT NULL --(DC2Type:datetime_immutable)
, class VARCHAR(100) NOT NULL, username VARCHAR(200) NOT NULL, PRIMARY KEY(series))');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE rememberme_token');
}
}

View File

@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration;
/** /**
* Auto-generated Migration: Please modify to your needs! * Auto-generated Migration: Please modify to your needs!
*/ */
final class Version20241201030110 extends AbstractMigration final class Version20241204160251 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
{ {
@ -58,6 +58,18 @@ final class Version20241201030110 extends AbstractMigration
)'); )');
$this->addSql('CREATE UNIQUE INDEX UNIQ_A6BDD54BE7927C74 ON mydevel_webroot_user (email)'); $this->addSql('CREATE UNIQUE INDEX UNIQ_A6BDD54BE7927C74 ON mydevel_webroot_user (email)');
$this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_USERNAME ON mydevel_webroot_user (username)'); $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_USERNAME ON mydevel_webroot_user (username)');
$this->addSql('CREATE TABLE reset_password_request (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
user_id INTEGER NOT NULL,
selector VARCHAR(20) NOT NULL,
hashed_token VARCHAR(100) NOT NULL,
requested_at DATETIME NOT NULL --(DC2Type:datetime_immutable)
,
expires_at DATETIME NOT NULL --(DC2Type:datetime_immutable)
,
CONSTRAINT FK_7CE748AA76ED395 FOREIGN KEY (user_id) REFERENCES mydevel_webroot_user (id) NOT DEFERRABLE INITIALLY IMMEDIATE
)');
$this->addSql('CREATE INDEX IDX_7CE748AA76ED395 ON reset_password_request (user_id)');
$this->addSql('CREATE TABLE messenger_messages ( $this->addSql('CREATE TABLE messenger_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
body CLOB NOT NULL, body CLOB NOT NULL,
@ -72,6 +84,15 @@ final class Version20241201030110 extends AbstractMigration
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)'); $this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)');
$this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)'); $this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)');
$this->addSql('CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)'); $this->addSql('CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)');
$this->addSql('CREATE TABLE rememberme_token (
series VARCHAR(88) NOT NULL,
value VARCHAR(88) NOT NULL,
lastUsed DATETIME NOT NULL --(DC2Type:datetime_immutable)
,
class VARCHAR(100) NOT NULL,
username VARCHAR(200) NOT NULL,
PRIMARY KEY(series)
)');
} }
public function down(Schema $schema): void public function down(Schema $schema): void
@ -81,6 +102,8 @@ final class Version20241201030110 extends AbstractMigration
$this->addSql('DROP TABLE mydevel_webroot_file_permission'); $this->addSql('DROP TABLE mydevel_webroot_file_permission');
$this->addSql('DROP TABLE mydevel_webroot_role'); $this->addSql('DROP TABLE mydevel_webroot_role');
$this->addSql('DROP TABLE mydevel_webroot_user'); $this->addSql('DROP TABLE mydevel_webroot_user');
$this->addSql('DROP TABLE reset_password_request');
$this->addSql('DROP TABLE messenger_messages'); $this->addSql('DROP TABLE messenger_messages');
$this->addSql('DROP TABLE rememberme_token');
} }
} }

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241206014353 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TEMPORARY TABLE __temp__mydevel_webroot_file AS SELECT id, owner_id, url, abspath, description FROM mydevel_webroot_file');
$this->addSql('DROP TABLE mydevel_webroot_file');
$this->addSql('CREATE TABLE mydevel_webroot_file (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, owner_id INTEGER NOT NULL, url_path CLOB NOT NULL, abspath CLOB NOT NULL, description CLOB DEFAULT NULL, section VARCHAR(1024) DEFAULT \'webroot\' NOT NULL, CONSTRAINT FK_A7B135127E3C61F9 FOREIGN KEY (owner_id) REFERENCES mydevel_webroot_user (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
$this->addSql('INSERT INTO mydevel_webroot_file (id, owner_id, url_path, abspath, description) SELECT id, owner_id, url, abspath, description FROM __temp__mydevel_webroot_file');
$this->addSql('DROP TABLE __temp__mydevel_webroot_file');
$this->addSql('CREATE UNIQUE INDEX UNIQ_A7B135127E3C61F9 ON mydevel_webroot_file (owner_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TEMPORARY TABLE __temp__mydevel_webroot_file AS SELECT id, owner_id, url_path, abspath, description FROM mydevel_webroot_file');
$this->addSql('DROP TABLE mydevel_webroot_file');
$this->addSql('CREATE TABLE mydevel_webroot_file (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, owner_id INTEGER NOT NULL, url CLOB NOT NULL, abspath CLOB NOT NULL, description CLOB DEFAULT NULL, CONSTRAINT FK_A7B135127E3C61F9 FOREIGN KEY (owner_id) REFERENCES mydevel_webroot_user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
$this->addSql('INSERT INTO mydevel_webroot_file (id, owner_id, url, abspath, description) SELECT id, owner_id, url_path, abspath, description FROM __temp__mydevel_webroot_file');
$this->addSql('DROP TABLE __temp__mydevel_webroot_file');
$this->addSql('CREATE UNIQUE INDEX UNIQ_A7B135127E3C61F9 ON mydevel_webroot_file (owner_id)');
}
}

1
security.yaml Normal file
View File

@ -0,0 +1 @@
# vim syn=yaml ts=2 sts=2 sw=2 smartindent expandtab

View File

@ -0,0 +1,46 @@
<?php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DashboardController extends AbstractDashboardController
{
#[Route('/admin', name: 'admin')]
public function index(): Response
{
return parent::index();
// Option 1. You can make your dashboard redirect to some common page of your backend
//
// $adminUrlGenerator = $this->container->get(AdminUrlGenerator::class);
// return $this->redirect($adminUrlGenerator->setController(OneOfYourCrudController::class)->generateUrl());
// Option 2. You can make your dashboard redirect to different pages depending on the user
//
// if ('jane' === $this->getUser()->getUsername()) {
// return $this->redirect('...');
// }
// Option 3. You can render some custom template to display a proper dashboard with widgets, etc.
// (tip: it's easier if your template extends from @EasyAdmin/page/content.html.twig)
//
// return $this->render('some/path/my-dashboard.html.twig');
}
public function configureDashboard(): Dashboard
{
return Dashboard::new()
->setTitle('Webroot');
}
public function configureMenuItems(): iterable
{
yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
// yield MenuItem::linkToCrud('The Label', 'fas fa-list', EntityClass::class);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin;
use App\Entity\WebrootFile;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
class WebrootFileCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return WebrootFile::class;
}
/*
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id'),
TextField::new('title'),
TextEditorField::new('description'),
];
}
*/
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin;
use App\Entity\WebrootFilePermission;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
class WebrootFilePermissionCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return WebrootFilePermission::class;
}
/*
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id'),
TextField::new('title'),
TextEditorField::new('description'),
];
}
*/
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin;
use App\Entity\WebrootRole;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
class WebrootRoleCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return WebrootRole::class;
}
/*
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id'),
TextField::new('title'),
TextEditorField::new('description'),
];
}
*/
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin;
use App\Entity\WebrootUser;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
class WebrootUserCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return WebrootUser::class;
}
/*
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id'),
TextField::new('title'),
TextEditorField::new('description'),
];
}
*/
}

View File

@ -6,6 +6,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use App\Controller\WebrootController; use App\Controller\WebrootController;
class MainController extends WebrootController class MainController extends WebrootController
@ -29,4 +31,43 @@ class MainController extends WebrootController
'user' => $user, 'user' => $user,
])); ]));
} }
#[Route('/webroot',name:"app_webroot")]
public function webroot(): Response
{
$is_allowed = false;
$user = $this->getUser();
if (!$is_allowed) {
throw new AccessDeniedHttpException();
}
$routeconfig = [
"parent"=>null,
"url"=> $this->generateUrl("app_webroot"),
"child"=>"app_webroot_target",
];
return new Response("<html><body><h1>it works</h1></body></html>");
}
#[Route('/webroot/{target}',name:"app_webroot_target")]
public function webrootTarget(?string $target): Response
{
$is_allowed = false;
$user = $this->getUser();
if ($user) {
if (in_array("ROLE_SUPERADMIN",$user->getRoles()) || in_array("ROLE_ADMIN",$user->getRoles())) {
$is_allowed = true;
}
}
if (!$is_allowed) {
throw new AccessDeniedHttpException();
}
return new Response("<html><body><h1>it works</h1></body></html>");
}
} }

View File

@ -0,0 +1,181 @@
<?php
namespace App\Controller;
use App\Entity\WebrootUser;
use App\Form\ChangePasswordFormType;
use App\Form\ResetPasswordRequestFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
use Symfony\Component\Mailer\Transport\TransportInterface;
#[Route('/reset-password')]
class ResetPasswordController extends AbstractController
{
use ResetPasswordControllerTrait;
public function __construct(
private ResetPasswordHelperInterface $resetPasswordHelper,
private EntityManagerInterface $entityManager
) {
}
/**
* Display & process form to request a password reset.
*/
#[Route('', name: 'app_forgot_password_request')]
public function request(Request $request, TransportInterface $mailer, TranslatorInterface $translator): Response
{
$form = $this->createForm(ResetPasswordRequestFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var string $email */
$email = $form->get('email')->getData();
return $this->processSendingPasswordResetEmail($email, $mailer, $translator
);
}
return $this->render('reset_password/request.html.twig', [
'requestForm' => $form,
]);
}
/**
* Confirmation page after a user has requested a password reset.
*/
#[Route('/check-email', name: 'app_check_email')]
public function checkEmail(): Response
{
// Generate a fake token if the user does not exist or someone hit this page directly.
// This prevents exposing whether or not a user was found with the given email address or not
if (null === ($resetToken = $this->getTokenObjectFromSession())) {
$resetToken = $this->resetPasswordHelper->generateFakeResetToken();
}
return $this->render('reset_password/check_email.html.twig', [
'resetToken' => $resetToken,
]);
}
/**
* Validates and process the reset URL that the user clicked in their email.
*/
#[Route('/reset/{token}', name: 'app_reset_password')]
public function reset(Request $request, UserPasswordHasherInterface $passwordHasher, TranslatorInterface $translator, ?string $token = null): Response
{
if ($token) {
// We store the token in session and remove it from the URL, to avoid the URL being
// loaded in a browser and potentially leaking the token to 3rd party JavaScript.
$this->storeTokenInSession($token);
return $this->redirectToRoute('app_reset_password');
}
$token = $this->getTokenFromSession();
if (null === $token) {
throw $this->createNotFoundException('No reset password token found in the URL or in the session.');
}
try {
/** @var WebrootUser $user */
$user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
} catch (ResetPasswordExceptionInterface $e) {
$this->addFlash('reset_password_error', sprintf(
'%s - %s',
$translator->trans(ResetPasswordExceptionInterface::MESSAGE_PROBLEM_VALIDATE, [], 'ResetPasswordBundle'),
$translator->trans($e->getReason(), [], 'ResetPasswordBundle')
));
return $this->redirectToRoute('app_forgot_password_request');
}
// The token is valid; allow the user to change their password.
$form = $this->createForm(ChangePasswordFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// A password reset token should be used only once, remove it.
$this->resetPasswordHelper->removeResetRequest($token);
/** @var string $plainPassword */
$plainPassword = $form->get('plainPassword')->getData();
// Encode(hash) the plain password, and set it.
$user->setPassword($passwordHasher->hashPassword($user, $plainPassword));
$this->entityManager->flush();
// The session is cleaned up after the password has been changed.
$this->cleanSessionAfterReset();
return $this->redirectToRoute('app_index');
}
return $this->render('reset_password/reset.html.twig', [
'resetForm' => $form,
]);
}
private function processSendingPasswordResetEmail(string $emailFormData, TransportInterface $mailer, TranslatorInterface $translator): RedirectResponse
{
$user = $this->entityManager->getRepository(WebrootUser::class)->findOneBy([
'email' => $emailFormData,
]);
// Do not reveal whether a user account was found or not.
if (!$user) {
return $this->redirectToRoute('app_check_email');
}
try {
$resetToken = $this->resetPasswordHelper->generateResetToken($user);
} catch (ResetPasswordExceptionInterface $e) {
// If you want to tell the user why a reset email was not sent, uncomment
// the lines below and change the redirect to 'app_forgot_password_request'.
// Caution: This may reveal if a user is registered or not.
//
/*
$this->addFlash('reset_password_error', sprintf(
'%s - %s',
$translator->trans(ResetPasswordExceptionInterface::MESSAGE_PROBLEM_HANDLE, [], 'ResetPasswordBundle'),
$translator->trans($e->getReason(), [], 'ResetPasswordBundle')
));
return $this->redirectToRoute('app_forgot_password_request');
*/
return $this->redirectToRoute('app_check_email');
}
$email = (new TemplatedEmail())
->from(new Address('noreply@mydevel.at',"MyDevel.at"))
->to((string) $user->getEmail())
->subject($translator->trans('Your password reset request',domain:'ResetPasswordBundle'))
->htmlTemplate($translator->trans('reset_password/email.html.twig',domain:'ResetPasswordBundle'))
->context([
'resetToken' => $resetToken,
'username' => $user->getUsername(),
])
;
$mailer->send($email);
// Store the token object in session for retrieval in check-email route.
$this->setTokenObjectInSession($resetToken);
return $this->redirectToRoute('app_check_email');
}
}

View File

@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Bundle\SecurityBundle\Security;
use App\Controller\WebrootController; use App\Controller\WebrootController;
@ -27,14 +28,20 @@ class SecurityController extends WebrootController
'login_username' => $this->trans("login.username",domain:"security"), 'login_username' => $this->trans("login.username",domain:"security"),
'login_password' => $this->trans("login.password",domain:"security"), 'login_password' => $this->trans("login.password",domain:"security"),
'login_remember_me' => $this->trans("login.remember_me",domain:"security"), 'login_remember_me' => $this->trans("login.remember_me",domain:"security"),
'login_forgotten_password' => $this->trans("login.forgotten_password",domain:"security"),
'last_username' => $lastUsername, 'last_username' => $lastUsername,
'error' => $error, 'error' => $error,
])); ]));
} }
#[Route(path: '/logout', name: 'app_logout')] #[Route('/logout',name:'app_logout')]
public function logout(): void public function logout(Security $security): Response
{ {
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); if ($this->getUser()) {
$response = $security->logout(false);
} else {
$response = $this->redirect("Login");
}
return $response;
} }
} }

View File

@ -122,6 +122,7 @@ class SetupController extends WebrootSetupController
$data= $this->getDataFromInitialSetupDataCookie($request); $data= $this->getDataFromInitialSetupDataCookie($request);
$commands=$data["__commands__"]; $commands=$data["__commands__"];
$error=null;
if ($step >= count($commands)) { if ($step >= count($commands)) {
return $this->redirectToRoute("app_login"); return $this->redirectToRoute("app_login");
@ -133,14 +134,26 @@ class SetupController extends WebrootSetupController
$output = $this->trans("command.createdb",domain:"mydevel.webroot.setup"); $output = $this->trans("command.createdb",domain:"mydevel.webroot.setup");
try { try {
$this->runCreateDatabase(); $this->runCreateDatabase();
} catch (\Throwable $ex) { $status="SUCCESS";
} catch (\Exception $ex) {
$status = "FAILED"; $status = "FAILED";
$status_message = $this->trans("command.failed",domain:"mydevel.webroot.setup"); $status_message = $this->translate("command.failed",domain:"mydevel.webroot.setup");
$error = $ex->getMessage();
} catch (\Throwable $ex) {
$status = "FAILED";
if (function_exists([$ex,'getMessage'])) {
$error=$ex->getMessage();
}
$status_message = $this->trans("command.failed",domain:"mydevel.webroot.setup");
} }
} elseif ($commands[$step] === "make-migrations") { } elseif ($commands[$step] === "make-migrations") {
$output = $this->trans("command.makemigrations",domain:"mydevel.webroot.setup"); $output = $this->trans("command.makemigrations",domain:"mydevel.webroot.setup");
try { try {
$this->runMakeMigration(); $this->runMakeMigration();
} catch (\Exception $ex) {
$status = "FAILED";
$status_message = $this->translate("command.failed",domain:"mydevel.webroot.setup");
$error = $ex->getMessage();
} catch (\Throwable $ex) { } catch (\Throwable $ex) {
$status = "FAILED"; $status = "FAILED";
$status_message = $this->trans("command.success",domain:"mydevel.webroot.setup"); $status_message = $this->trans("command.success",domain:"mydevel.webroot.setup");
@ -149,6 +162,10 @@ class SetupController extends WebrootSetupController
$output = $this->trans("command.migrate",domain:"mydevel.webroot.setup"); $output = $this->trans("command.migrate",domain:"mydevel.webroot.setup");
try { try {
$this->runMigrate(); $this->runMigrate();
} catch (\Exception $ex) {
$status = "FAILED";
$status_message = $this->translate("command.failed",domain:"mydevel.webroot.setup");
$error = $ex->getMessage();
} catch (\Throwable $ex) { } catch (\Throwable $ex) {
$status_message = $this->trans("command.failed",domain:"mydevel.webroot.setup"); $status_message = $this->trans("command.failed",domain:"mydevel.webroot.setup");
$status = "FAILED"; $status = "FAILED";
@ -161,9 +178,13 @@ class SetupController extends WebrootSetupController
$this->addRootDir($data); $this->addRootDir($data);
$status = "SUCCESS"; $status = "SUCCESS";
$status_message = $this->trans("command.success",domain:"mydevel.webroot.setup"); $status_message = $this->trans("command.success",domain:"mydevel.webroot.setup");
} catch (\Throwable $ex) { } catch (\Exception $ex) {
$status = "FAILED"; $status = "FAILED";
$status_message = $this->translate("command.failed",domain:"mydevel.webroot.setup"); $status_message = $this->translate("command.failed",domain:"mydevel.webroot.setup");
$error = $ex->getMessage();
} catch (\throwable $ex) {
$status_message = $this->trans("command.failed");
$status = "FAILED";
} }
} else { } else {
$output = "Unknown command \"" . $commands[$step] . "\""; $output = "Unknown command \"" . $commands[$step] . "\"";
@ -177,6 +198,7 @@ class SetupController extends WebrootSetupController
'status' => $status, 'status' => $status,
'status_message' => $status_message, 'status_message' => $status_message,
'output' => $output, 'output' => $output,
'error' => $error,
'failed_button' => $this->trans("button.return-setup",domain:"mydevel.webroot.setup"), 'failed_button' => $this->trans("button.return-setup",domain:"mydevel.webroot.setup"),
]); ]);
} }

View File

@ -4,24 +4,33 @@ namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\WebrootFile;
use App\Utility\NullTranslator; use App\Utility\NullTranslator;
abstract class WebrootController extends AbstractController abstract class WebrootController extends AbstractController
{ {
private ?string $project_dir = null; private ?string $project_dir = null;
private ?TranslatorInterface $tranlsator = null; private ?TranslatorInterface $tranlsator = null;
private ?EntityManager $entitymanager = null;
protected ?NullTranslator $nulltranslator = null; protected ?NullTranslator $nulltranslator = null;
public function __construct(KernelInterface $kernel,TranslatorInterface $translator) public function __construct(KernelInterface $kernel,TranslatorInterface $translator,EntityManagerInterface $entitymanager)
{ {
$this->entity_manager = $entitymanager;
$this->project_dir = $kernel->getProjectDir(); $this->project_dir = $kernel->getProjectDir();
$this->translator = $translator; $this->translator = $translator;
$this->nulltranslator = new NullTranslator(); $this->nulltranslator = new NullTranslator();
$this->entitymanager = $entitymanager;
} }
public function getHeaderTitleFiglet(): ?string public function getHeaderTitleFiglet(): ?string
@ -51,4 +60,133 @@ abstract class WebrootController extends AbstractController
{ {
return $this->translator->trans($message,$args,domain:$domain,locale:$locale); return $this->translator->trans($message,$args,domain:$domain,locale:$locale);
} }
protected function getFilePermissions($section,string $url_path): array
{
return $this->entitymanager->getRepositiory(WebrootFile::class)
->findFilePermissionsBySectionPath($this->getUser(),$section,$url_path);
}
protected function getFileResponse($abspath): Response
{
if (!is_file($abspath)) {
throw new FileException();
}
$response = new BinaryFileResponse();
$response->setFile($abspath);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE);
return response;
}
protected function getDirectoryList(string $abspath,string $section,string $url_path,array $routeconfig): array
{
if (!is_directory("abspath")) {
throw new FileException();
}
$file_repos = $this->entitymanager->getRepository(WebrootFile::class);
$user = $this->getUser();
$roles = $user->getRoles();
if (in_array('ROLE_SUPERADMIN', $roles) || in_array('ROLE_ADMIN',$roles)) {
$show_dotfiles = true;
} else {
$show_dotfiles = false;
}
$dirlist = [];
if (key_exists("child",$routeconfig)) {
$child_route=$routeconfig["child"];
} else {
$child_route=$routeconfig["route"];
}
if (key_exists("parent_href",$routeconfig)) {
$parent_href=$routeconfig["parent"];
} else {
$parent_href=null;
}
foreach (scandir($abspath) as $dirent) {
if ($dirent === '.' || $dirent === '..') {
continue;
}
if ($dirent[0] === '.' && ! $show_dotfiles) {
continue;
}
$file_urlpath = $url_path . '/' . $dirent;
$file_abspath = $abspath . DIRECTORY_SEPARATOR . $dirent;
$fileperm = $file_repos->findFilePermissionsBySectionPath($user,$section,$file_urlpath);
if (!$fileperm['read']) {
continue;
}
if (is_file($file_abspath)) {
$dirlist[] = [
"name" => $dirent,
"icon" => "icons/file.svg",
"href" => $this->generateUrl($child_route, ["path"=>"file_urlpath"]),
"size" => filesize($file_abspath),
"delete" => $fileperm['delete'],
"write" => $fileperm['write'],
"mtime" => filemtime($file_abspath),
"read" => true,
"dir" => false,
];
} elseif (is_dir($file_abspath)) {
$dirlist[] = [
"name" => $dirent,
"icon"=>"icons/dir.svg",
"href" => $this->generateUrl($child_route, ["path"=>"file_urlpath"]),
"size" => "-",
"delete" => $fileperm['delete'],
"write" => $fileperm['write'],
"mtime" => filemtime($file_abspath),
"read" => true,
"dir" => true,
];
}
}
return $dirlist;
}
protected function getDirectoryResponse($abspath,$fileperm,$section,$url_path) : Response
{
$dirlist = $this->getDirectoryList($abspath, $fileperm, $section, $url_path);
return new Response();
}
protected function getSectionPathResponse(string $section,string $url_path,array $routeconfig): Response
{
if (!getFilePermissions($section,$urlPath)) {
throw AccessDeniedHttpException();
}
$path = $this->buildPath($section,$url_path);
if (!$path || !file_exists($path)) {
throw NotFoundHttpException();
}
$file_repos = $this->entitymanager->getRepository(WebrootFile::class);
$abspath = $file_repos->findAbspathBySectionPath($section,$path);
if (!file_exists($abspath)) {
throw new NotFoundHttpException();
}
$fileperm = $file_repos->findFilePermissionsBySectionPath($this->getUser(),$section,$path);
if (!$fileperm['read']) {
throw AccessDeniedHttpException();
}
if (is_file($abspath)) {
return $this->getFileResponse($abspath);
}
if (is_dir($abspath)) {
return $this->getDirectoryListResponse($abspath,$section,$url_path,$routeconfig);
}
throw FileException();
}
} }

View File

@ -208,6 +208,54 @@ abstract class WebrootSetupController extends AbstractController
return $content; return $content;
} }
/**
*
* @return string
*/
protected function runCacheClear(): string
{
$application = new Application($this->kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
"command" => "cache:clear",
"--no-interaction" => true,
"--quiet" => true,
]);
$output = new BufferedOutput();
$errcode = $application->run($input,$output);
if ($errcode) {
throw new \Exception("runMigrate failed!\n". $output->fetch());
}
$content = $output->fetch();
return $content;
}
protected function runDatabaseUpdate(): string
{
$application = new Application($this->kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
"command" => "doctrine:schema:update",
"--no-interaction" => true,
"--quiet" => true,
]);
$output = new BufferedOutput();
$errcode = $application->run($input,$output);
if ($errcode) {
throw new \Exception("runMigrate failed!\n". $output->fetch());
}
$content = $output->fetch();
return $content;
}
public function getInitialRoles(bool $translated=true): array public function getInitialRoles(bool $translated=true): array
{ {
@ -543,8 +591,9 @@ abstract class WebrootSetupController extends AbstractController
public function getDataFromSetupForm(Form $form) : array public function getDataFromSetupForm(Form $form) : array
{ {
$data=[]; $data=[
$data['env'] = $form->get('env')->getNormData(); 'env' => $form->get('env')->getNormData()
];
$locale = $form->get('locale')->getNormData(); $locale = $form->get('locale')->getNormData();
if ($locale && strlen($locale)) { if ($locale && strlen($locale)) {
@ -632,22 +681,23 @@ abstract class WebrootSetupController extends AbstractController
if ($email_path && strlen($email_path)) { if ($email_path && strlen($email_path)) {
$email["path"] = $email_path; $email["path"] = $email_path;
} }
$email_user = $form->get("email_user")->getNormData(); $email_user = $form->get("email_user")->getNormData();
if ($email_user && strlen($email_user)) { if ($email_user && strlen($email_user)) {
$email["user"] = $email_user; $email["user"] = $email_user;
} }
$email_password = $form->get("email_password")->getNormData();
if ($email_password && strlen($email_password)) {
$email["password"] = $email_password;
}
$email_host = $form->get('email_host')->getNormData(); $email_host = $form->get('email_host')->getNormData();
if ($email_host && strlen($email_host)) { if ($email_host && strlen($email_host)) {
$email["host"] = $email_host; $email["host"] = $email_host;
} }
$email_port = $form->get('email_port')->getNormData(); $email_port = $form->get('email_port')->getNormData();
if ($email_port && (int) $email_port) { if ($email_port && (int) $email_port) {
$email["port"] = $email_port; $email["port"] = $email_port;
} }
$email_dsn = $form->get('email_dsn')->getNormData(); $email_dsn = $form->get('email_dsn')->getNormData();
if ($email_dsn && strlen($email_dsn)) { if ($email_dsn && strlen($email_dsn)) {
$email["dsn"] = $email_dsn; $email["dsn"] = $email_dsn;
@ -749,7 +799,14 @@ abstract class WebrootSetupController extends AbstractController
$response->headers->removeCookie(WebrootSetupController::INITIAL_SETUP_COOKIE,"/setup/"); $response->headers->removeCookie(WebrootSetupController::INITIAL_SETUP_COOKIE,"/setup/");
} }
} }
public function envencode(string $str): string
{
return preg_replace('/%/','%%',urlencode($str));
}
public function writeDotEnvLocal(array $data,bool $generate_app_secret=true) { public function writeDotEnvLocal(array $data,bool $generate_app_secret=true) {
$file = fopen(join(DIRECTORY_SEPARATOR,[$this->project_dir,".env.local"]),"w"); $file = fopen(join(DIRECTORY_SEPARATOR,[$this->project_dir,".env.local"]),"w");
fwrite($file,"APP_ENV=" . $data["env"] . "\n"); fwrite($file,"APP_ENV=" . $data["env"] . "\n");
@ -764,7 +821,7 @@ abstract class WebrootSetupController extends AbstractController
$db_backend=$data['database']['backend']; $db_backend=$data['database']['backend'];
if ($db_backend === "sqlite") { if ($db_backend === "sqlite") {
fwrite($file,"DATABASE_URL=\"sqlite://" . $data['database']['database'] . "\"\n"); fwrite($file,"DATABASE_URL=\"sqlite:///" . $data['database']['database'] . "\"\n");
} elseif ($db_backend === "mysql") { } elseif ($db_backend === "mysql") {
fwrite($file,"DATABASE_URL=\"msysql://" fwrite($file,"DATABASE_URL=\"msysql://"
. urlencode($data['database']['user']) . urlencode($data['database']['user'])
@ -784,8 +841,30 @@ abstract class WebrootSetupController extends AbstractController
} elseif ($db_backend === "url") { } elseif ($db_backend === "url") {
fwrite($file,"DATABASE_URL=\"" . $data["database"]["url"] . "\"\n"); fwrite($file,"DATABASE_URL=\"" . $data["database"]["url"] . "\"\n");
} }
$mail_backend = $data["email"]["backend"];
if ($mail_backend === "none") {
$MAILER_DSN="null://null";
} elseif ($mail_backend === "smtp") {
$MAILER_DSN="smtp://"
. urlencode($data['email']['user'])
. ':' . urlencode($data['email']['password'])
. '@' . urlencode($data['email']['host'])
. ':' . $data['email']['port'];
} elseif ($mail_backend === "sendmail") {
$MAILER_DSN="sendmail://default";
} elseif ($mail_backend === "native") {
$MAILER_DSN="native://default";
} elseif ($mail_backend === "dsn") {
$MAILER_DSN=$data['email']['dsn'];
}
fwrite($file,"MAILER_DSN=\"" . $MAILER_DSN . "\"\n");
fclose($file);
} }
protected function addRoles() protected function addRoles()
{ {
foreach ($this->getInitialRoles(false) as $roledata) { foreach ($this->getInitialRoles(false) as $roledata) {
@ -804,6 +883,7 @@ abstract class WebrootSetupController extends AbstractController
$user->setUsername($setup_data["user"]["username"]); $user->setUsername($setup_data["user"]["username"]);
$user->setEmail($setup_data["user"]["email"]); $user->setEmail($setup_data["user"]["email"]);
$user->setAdmin(true); $user->setAdmin(true);
$user->setRoles(["ROLE_SUPERADMIN","ROLE_ADMIN","ROLE_USER"]);
$user->setPassword($setup_data["user"]["password"]); $user->setPassword($setup_data["user"]["password"]);
$this->entitymanager->persist($user); $this->entitymanager->persist($user);
$this->entitymanager->flush(); $this->entitymanager->flush();
@ -814,7 +894,7 @@ abstract class WebrootSetupController extends AbstractController
->getRepository(WebrootUser::class) ->getRepository(WebrootUser::class)
->findByUsername($setup_data['user']['username']); ->findByUsername($setup_data['user']['username']);
$wrf = new WebrootFile(); $wrf = new WebrootFile();
$wrf->setUrl('/root'); $wrf->setUrl('/webroot');
$wrf->setAbspath($setup_data["site"]["rootdir"]); $wrf->setAbspath($setup_data["site"]["rootdir"]);
$wrf->setOwner($admin); $wrf->setOwner($admin);
$this->entitymanager->persist($wrf); $this->entitymanager->persist($wrf);
@ -822,14 +902,26 @@ abstract class WebrootSetupController extends AbstractController
$superadmin_role = $this->entitymanager $superadmin_role = $this->entitymanager
->getRepository(WebrootRole::class) ->getRepository(WebrootRole::class)
->findByRolename("ROLE_SUPERADMIN"); ->findByRolename("ROLE_SUPERADMIN");
$rootperm = new WebrootFilePermission(); $rootperm = new WebrootFilePermission();
$rootperm->setWebrootFile($wrf); $rootperm->setWebrootFile($wrf);
$rootperm->setRole($superadmin_role); $rootperm->setRole($superadmin_role);
$rootperm->setDeleteable(true); $rootperm->setDeleteable(true);
$rootperm->setReadable(true); $rootperm->setReadable(true);
$rootperm->setWriteable(false); $rootperm->setWriteable(true);
$this->entitymanager->persist($rootperm); $this->entitymanager->persist($rootperm);
$admin_role = $this->entitymanager
->getRepository(WebrootRole::class)
->findByRolename("ROLE_ADMIN");
$adminperm = new WebrootFilePermission();
$adminperm->setWebrootFile($wrf);
$adminperm->setRole($admin_role);
$adminperm->setReadable(true);
$adminperm->setWriteable(false);
$adminperm->setDeleteable(false);
$this->entitymanager->persist($adminperm);
$this->entitymanager->flush(); $this->entitymanager->flush();
} }
} }

View File

@ -0,0 +1,39 @@
<?php
namespace App\Entity;
use App\Repository\ResetPasswordRequestRepository;
use Doctrine\ORM\Mapping as ORM;
use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestInterface;
use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestTrait;
#[ORM\Entity(repositoryClass: ResetPasswordRequestRepository::class)]
class ResetPasswordRequest implements ResetPasswordRequestInterface
{
use ResetPasswordRequestTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?WebrootUser $user = null;
public function __construct(WebrootUser $user, \DateTimeInterface $expiresAt, string $selector, string $hashedToken)
{
$this->user = $user;
$this->initialize($expiresAt, $selector, $hashedToken);
}
public function getId(): ?int
{
return $this->id;
}
public function getUser(): WebrootUser
{
return $this->user;
}
}

View File

@ -16,8 +16,13 @@ class WebrootFile
#[ORM\Column] #[ORM\Column]
private ?int $id = null; private ?int $id = null;
#[ORM\Column(length:1024,nullable:false,options:["default"=>"webroot"])]
private ?string $section = null;
#[ORM\Column(length: 65535,nullable:false)] #[ORM\Column(length: 65535,nullable:false)]
private ?string $url = null; private ?string $url_path = null;
#[ORM\Column(length: 65535,nullable:false)] #[ORM\Column(length: 65535,nullable:false)]
private ?string $abspath = null; private ?string $abspath = null;
@ -46,12 +51,12 @@ class WebrootFile
return $this->id; return $this->id;
} }
public function getUrl(): ?string public function getUrlPath(): ?string
{ {
return $this->url; return $this->url;
} }
public function setUrl(string $url): static public function setUrlPath(string $url): static
{ {
$this->url = $url; $this->url = $url;

View File

@ -75,6 +75,7 @@ class WebrootUser implements UserInterface, PasswordAuthenticatedUserInterface
$roles = $this->roles; $roles = $this->roles;
// guarantee every user at least has ROLE_USER // guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER'; $roles[] = 'ROLE_USER';
$roles[] = 'ROLE_PUBLIC';
return array_unique($roles); return array_unique($roles);
} }

View File

@ -0,0 +1,58 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotCompromisedPassword;
use Symfony\Component\Validator\Constraints\PasswordStrength;
class ChangePasswordFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'options' => [
'attr' => [
'autocomplete' => 'new-password',
],
],
'first_options' => [
'constraints' => [
new NotBlank([
'message' => 'Please enter a password',
]),
new Length([
'min' => 12,
'minMessage' => 'Your password should be at least {{ limit }} characters',
// max length allowed by Symfony for security reasons
'max' => 4096,
]),
new PasswordStrength(),
new NotCompromisedPassword(),
],
'label' => 'New password',
],
'second_options' => [
'label' => 'Repeat Password',
],
'invalid_message' => 'The password fields must match.',
// Instead of being set onto the object directly,
// this is read and encoded in the controller
'mapped' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([]);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
class ResetPasswordRequestFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class, [
'attr' => ['autocomplete' => 'email'],
'constraints' => [
new NotBlank([
'message' => 'Please enter your email',
]),
],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([]);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Repository;
use App\Entity\ResetPasswordRequest;
use App\Entity\WebrootUser;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestInterface;
use SymfonyCasts\Bundle\ResetPassword\Persistence\Repository\ResetPasswordRequestRepositoryTrait;
use SymfonyCasts\Bundle\ResetPassword\Persistence\ResetPasswordRequestRepositoryInterface;
/**
* @extends ServiceEntityRepository<ResetPasswordRequest>
*/
class ResetPasswordRequestRepository extends ServiceEntityRepository implements ResetPasswordRequestRepositoryInterface
{
use ResetPasswordRequestRepositoryTrait;
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ResetPasswordRequest::class);
}
/**
* @param WebrootUser $user
*/
public function createResetPasswordRequest(object $user, \DateTimeInterface $expiresAt, string $selector, string $hashedToken): ResetPasswordRequestInterface
{
return new ResetPasswordRequest($user, $expiresAt, $selector, $hashedToken);
}
}

View File

@ -3,9 +3,11 @@
namespace App\Repository; namespace App\Repository;
use App\Entity\WebrootFile; use App\Entity\WebrootFile;
use App\Entity\WebrootUser;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
/** /**
* @extends ServiceEntityRepository<WebrootFile> * @extends ServiceEntityRepository<WebrootFile>
*/ */
@ -16,6 +18,108 @@ class WebrootFileRepository extends ServiceEntityRepository
parent::__construct($registry, WebrootFile::class); parent::__construct($registry, WebrootFile::class);
} }
public function findFilePermissionsBySectionPath(?WebrootUser $user,string $section,string $url_path): array
{
$paths_array= explode('/', $url_path);
$query_builder = $this->createQueryBuilder('f')
->andWhere('f.section := sect')
->andWhere('f.url_path := path')
->setParameter('sect', $section);
if ($user) {
$roles = $user->getRoles();
if (in_array('ROLE_SUPERADMIN',$roles)) {
return ['read'=>true,'write'=>true,'delete'=>true];
}
} else {
$roles = ["ROLE_PUBLIC"];
}
$ret = [
'read'=>false,
'write'=>false,
'delete'=>false,
];
while (sizeof($paths_array) > 0) {
$result = $query_builder->setParameter('sect',$section)
->setParameter('path',join(DIRECTORY_SEPARATOR,$paths_array))
->getQuery()
->getOneOrNullResult();
if ($result) {
foreach($result->getPermissions() as $perm) {
if ($user && $result->getOwner()->getId() === $user->getId()) {
return [['read'=>true,'write'=>true,'delete'=>true]];
}
if (in_array($perm->getRole()->getRole(),$roles)) {
if ($perm->isReadable()) {
$ret['read'] = true;
}
if ($perm->isWriteable()) {
$ret['write'] = true;
}
if ($perm->isDeleteable()) {
$ret['delete'] = true;
}
}
}
break;
}
}
return $ret;
}
public function findBySectionPath(string $section,string $url_path): ?WebrootFile
{
return $this->createQueryBuilder('f')
->andWhere('f.section := sect')
->andWhere('f.url_path := path')
->setParameter('sect', $section)
->setParameter('path', $url_path)
->getQuery()
->getOneOrNullResult();
}
public function findAbspathBySectionPath(string $section, string $url_path): ?string
{
$query_builder = $this->createQueryBuilder('f')
->andWhere('f.section := sect')
->andWhere('f.path : path')
->setParameter('sect', $section);
$path_array = explode('/',$url_path);
$path_extend=[];
while (sizeof($path_array) > 0) {
$result = query_builder->setParameter('path',join('/',path_array))
->qetQuery()
->getOneOrNullResult();
if ($result) {
if (!sizeof($path_extend)) {
return $result->getAbspath();
} else {
$join_path = [$result->getAbaspath()];
foreach (array_reverse($path_extend) as $xpath) {
$join_path[] = $xpath;
}
return join(DIRECTORY_SEPARATOR,$join_path);
}
}
$path_extend[] = array_pop($path_array);
}
return null;
}
public function findByAbspath(string $abspath): array
{
return $this->createQueryBuilder('f')
->andWhere('f.absath := val')
->setParameter('val', $abspath)
->getQuery()
->getResult();
}
// /** // /**
// * @return WebrootFile[] Returns an array of WebrootFile objects // * @return WebrootFile[] Returns an array of WebrootFile objects
// */ // */

View File

@ -27,7 +27,7 @@
] ]
}, },
"easycorp/easyadmin-bundle": { "easycorp/easyadmin-bundle": {
"version": "4.18", "version": "4.9999999",
"recipe": { "recipe": {
"repo": "github.com/symfony/recipes", "repo": "github.com/symfony/recipes",
"branch": "main", "branch": "main",
@ -100,6 +100,18 @@
"./.env" "./.env"
] ]
}, },
"symfony/form": {
"version": "7.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "7.2",
"ref": "7d86a6723f4a623f59e2bf966b6aad2fc461d36b"
},
"files": [
"./config/packages/csrf.yaml"
]
},
"symfony/framework-bundle": { "symfony/framework-bundle": {
"version": "6.4", "version": "6.4",
"recipe": { "recipe": {
@ -258,16 +270,13 @@
] ]
}, },
"symfony/uid": { "symfony/uid": {
"version": "6.4", "version": "7.2",
"recipe": { "recipe": {
"repo": "github.com/symfony/recipes", "repo": "github.com/symfony/recipes",
"branch": "main", "branch": "main",
"version": "6.2", "version": "7.0",
"ref": "d294ad4add3e15d7eb1bae0221588ca89b38e558" "ref": "0df5844274d871b37fc3816c57a768ffc60a43a5"
}, }
"files": [
"./config/packages/uid.yaml"
]
}, },
"symfony/ux-turbo": { "symfony/ux-turbo": {
"version": "2.21", "version": "2.21",
@ -279,7 +288,7 @@
} }
}, },
"symfony/ux-twig-component": { "symfony/ux-twig-component": {
"version": "2.21", "version": "2.22",
"recipe": { "recipe": {
"repo": "github.com/symfony/recipes", "repo": "github.com/symfony/recipes",
"branch": "main", "branch": "main",
@ -315,6 +324,18 @@
"./config/routes/web_profiler.yaml" "./config/routes/web_profiler.yaml"
] ]
}, },
"symfonycasts/reset-password-bundle": {
"version": "1.23",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "97c1627c0384534997ae1047b93be517ca16de43"
},
"files": [
"./config/packages/reset_password.yaml"
]
},
"twig/extra-bundle": { "twig/extra-bundle": {
"version": "v3.15.0" "version": "v3.15.0"
} }

View File

@ -3,4 +3,11 @@
<p> <p>
You are logged in as <em>{{ user.username }}</em>. You are logged in as <em>{{ user.username }}</em>.
</p> </p>
<p>
<ul>
{% for role in user.roles %}
<li>{{ role }}</li>
{% endfor %}
</ul>
</p>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html.twig' %}
{% block title %}Password Reset Email Sent{% endblock %}
{% block body %}
<p>
If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.
This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
</p>
<p>If you don't receive an email please check your spam folder or <a href="{{ path('app_forgot_password_request') }}">try again</a>.</p>
{% endblock %}

View File

@ -0,0 +1,9 @@
<h1>Hi!</h1>
<p>Um Dein Passwort zurückzusetzen, besuche folgenden Link</p>
<a href="{{ url('app_reset_password', {token: resetToken.token}) }}">{{ url('app_reset_password', {token: resetToken.token}) }}</a>
<p>Der Link läuft in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }} ab.</p>
<p>Dein Nutzername falls du ihn vergessen hast: <em>{{ username }}</em></p>
<p>Mit freundlichen Grüßen das <em>{{ app.request.server.get('SITE_NAME') }} Team</em>!</p>

View File

@ -0,0 +1,9 @@
<h1>Hi!</h1>
<p>To reset your password, please visit the following link</p>
<a href="{{ url('app_reset_password', {token: resetToken.token}) }}">{{ url('app_reset_password', {token: resetToken.token}) }}</a>
<p>This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.</p>
<p> Your username in case you have forgotten it: <em>{{ username }}</em></p>
<p>Cheers! Your <em>{{ app.request.server.get("SITE_NAME")}} team</em>.</p>

View File

@ -0,0 +1,22 @@
{% extends 'base.html.twig' %}
{% block title %}Reset your password{% endblock %}
{% block body %}
{% for flash_error in app.flashes('reset_password_error') %}
<div class="alert alert-danger" role="alert">{{ flash_error }}</div>
{% endfor %}
<h1>Reset your password</h1>
{{ form_start(requestForm) }}
{{ form_row(requestForm.email) }}
<div>
<small>
Enter your email address, and we will send you a
link to reset your password.
</small>
</div>
<button class="btn btn-primary">Send password reset email</button>
{{ form_end(requestForm) }}
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html.twig' %}
{% block title %}Reset your password{% endblock %}
{% block body %}
<h1>Reset your password</h1>
{{ form_start(resetForm) }}
{{ form_row(resetForm.plainPassword) }}
<button class="btn btn-primary">Reset password</button>
{{ form_end(resetForm) }}
{% endblock %}

View File

@ -41,9 +41,14 @@
</table> </table>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
<p class="right"><a href="{{ path('app_forgot_password_request') }}">{{ login_forgotten_password }}</a></p>
<button class="btn btn-lg btn-primary" type="submit" class="right">{{ login_button }}</button> </p>
<div class="full-width right">
<button class="btn btn-lg btn-primary" type="submit" class="right">{{ login_button }}</button>
</div>
</fieldset> </fieldset>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -12,9 +12,9 @@ function initializeSetup() {
let a = document.createElement("a"); let a = document.createElement("a");
a.setAttribute('href',"{{ setup_url }}"); a.setAttribute('href',"{{ setup_url }}");
a.setAttribute('class','button'); a.setAttribute('class','button');
a.innerText = "{{ failed_button }}" a.innerText = "{{ failed_button }}";
elem.appendChild(a); elem.appendChild(a);
console.log("LOADED") console.log("LOADED");
} }
} }
</script> </script>
@ -24,5 +24,8 @@ function initializeSetup() {
<h1>Running Setup</h1> <h1>Running Setup</h1>
<p>{{ output }} ... {{ status_message }}</p> <p>{{ output }} ... {{ status_message }}</p>
</div> </div>
{% if error %}
<p class="error-message"><pre>{{ error }}</pre></p>
{% endif %}
<script>initializeSetup();</script> <script>initializeSetup();</script>
{% endblock %} {% endblock %}

View File

@ -3,14 +3,12 @@
{% block body %} {% block body %}
<h3>Index für <em>{{ url_path }}</em></h3> <h3>Index für <em>{{ url_path }}</em></h3>
<div class="toolbar"> <div class="toolbar">
{% if create_dir %} {% if write %}
<span class="toolbar-item"> <span class="toolbar-item">
<a class="coolbar-button" href="{{ create_dir }}"> <a class="coolbar-button" href="{{ create_dir }}">
<img /> <img />
</a> </a>
</span> </span>
{% endif %}
{% if upload_file %}
<span class="toolbar-item"> <span class="toolbar-item">
<a class="toolbar-button" href="{{ upload_file }}"> <a class="toolbar-button" href="{{ upload_file }}">
<img /> <img />
@ -20,10 +18,10 @@
</div> </div>
<table><!-- Directory Index --> <table><!-- Directory Index -->
<tr> <tr>
<th class="td-color"><a href="{{ sort_standard_href }}"><img class="list-icon" alt="[icon]" width="32" height="32" src="/static/icons/empty.svg" /></a></th> <th class="td-color"><a href="{#{ sort_standard_href }#}"><img class="list-icon" alt="[icon]" width="32" height="32" src="/static/icons/empty.svg" /></a></th>
<th class="td-color"><a href="{{ sort_name_href }}">Name</a></th> <th class="td-color"><a href="{#{ sort_name_href }#}">Name</a></th>
<th class="td-color"><a href="{{ sort_size_href }}">Größe</a></th> <th class="td-color"><a href="{#{ sort_size_href }#}">Größe</a></th>
<th class="td-color"><a href="{{ sort_time_href }}">Zuletzt geändert</a></th> <th class="td-color"><a href="{#{ sort_time_href }#}">Zuletzt geändert</a></th>
<th class="td-color">Beschreibung</th> <th class="td-color">Beschreibung</th>
<th class="td-color"></th> <th class="td-color"></th>
</tr> </tr>

View File

@ -3,33 +3,30 @@
{% block body %} {% block body %}
<h3>Index for <em>{{ url_path }}</em></h3> <h3>Index for <em>{{ url_path }}</em></h3>
<div class="toolbar"> <div class="toolbar">
{% if create_dir %} {% if write %}
<span class="toolbar-item"> <span class="toolbar-item">
<a class="coolbar-button" href="{{ create_dir }}"> <a class="toolbar-button" href="{#{ 'create_dir' }#}">
<img /> <img src="{{ asset('icons/upload.svg') }}" alt="create-icon"/>
</a> </a>
</span> </span>
{% endif %}
{% if upload_file %}
<span class="toolbar-item"> <span class="toolbar-item">
<a class="toolbar-button" href="{{ upload_file }}"> <a class="toolbar-button" href="{#{ 'upload_file' }#}">
<img /> <img alt="upload-icon"/>
</a> </a>
</span> </span>
{% endif %} {% endif %}
</div> </div>
<table><!-- Directory Index --> <table><!-- Directory Index -->
<tr> <tr>
<th class="td-color"><a href="{{ sort_standard_href }}"><img class="list-icon" alt="[icon]" width="32" height="32" src="/static/icons/empty.svg" /></a></th> <th class="td-color"><a href="{#{ 'sort_standard_href' }#}"><img class="list-icon" alt="[icon]" width="32" height="32" src="/static/icons/empty.svg" /></a></th>
<th class="td-color"><a href="{{ sort_name_href }}">Size</a></th> <th class="td-color"><a href="{#{ 'sort_name_href' }#}">Size</a></th>
<th class="td-color"><a href="{{ sort_time_href }}">Last changed</a></th> <th class="td-color"><a href="{#{ 'sort_time_href' }#}">Last changed</a></th>
<th class="td-color">Description</th>
<th class="td-color"></th> <th class="td-color"></th>
</tr> </tr>
<tr><td colspan="6"><strong><hr></strong></td></tr> <tr><td colspan="5"><strong><hr></strong></td></tr>
{% if parent_url %} {% if parent_url %}
<tr> <tr>
<td><img class="list-icom" width="24" height="24" alt="[icon]" src="/static/icons/back.svg" /></td> <td><img class="list-icom" width="24" height="24" alt="[icon]" src="{{ asset('icons/back.svg') }}" /></td>
<td><a href="{{ parent_url }}">Zurück</a></td> <td><a href="{{ parent_url }}">Zurück</a></td>
<td>-</td> <td>-</td>
<td>-</td> <td>-</td>
@ -39,11 +36,10 @@
{% endif %} {% endif %}
{% for entry in dir_entries %} {% for entry in dir_entries %}
<tr> <tr>
<td>{% if entry.icon %}<img class="list-icon" alt="{{ entry.icon_alt }}" src="{{ entry.icon }}" width="24" height="24" />{% endif %}</td> <td>{% if entry.icon %}<img class="list-icon" alt="icon" src="{{ entry.icon }}" width="24" height="24" />{% endif %}</td>
<td><a href="{{ entry.href }}">{{ entry.name }}</a></td> <td><a href="{{ entry.href }}">{{ entry.name }}</a></td>
<td>{{ entry.display_size }}</td> <td>{{ entry.size }}</td>
<td>{{ entry.last_modified }}</td> <td>{{ entry.mtime }}</td>
<td>{{ entry.description }}</td>
<td> <td>
{% if entry.delete %} {% if entry.delete %}
<a href="{{ entry.delete }}"<img class="list-icon" alt="[delete]" width="24" height="24" src="/static/icons/delete.svg" /></a> <a href="{{ entry.delete }}"<img class="list-icon" alt="[delete]" width="24" height="24" src="/static/icons/delete.svg" /></a>
@ -53,7 +49,7 @@
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
<tr><td colspan="6"><strong><hr></strong></td></tr> <tr><td colspan="5"><strong><hr></strong></td></tr>
</table> </table>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,13 @@
'%count% year|%count% years': '%count% Jahr|%count% Jahren'
'%count% month|%count% months': '%count% Monat|%count% Monaten'
'%count% day|%count% days': '%count% Tag|%count% Tagen'
'%count% hour|%count% hours': '%count% Stunde|%count% Stunden'
'%count% minute|%count% minutes': '%count% Minute|%count% Minuten'
'There was a problem validating your password reset request': 'Es gab ein Problem bei der Validierung Ihrer Anfrage zum Zurücksetzen des Passworts'
'There was a problem handling your password reset request': 'Es gab ein Problem bei der Bearbeitung Ihrer Anfrage zum Zurücksetzen des Passworts'
'The link in your email is expired. Please try to reset your password again.': 'Der Link in Ihrer E-Mail ist abgelaufen. Bitte versuchen Sie erneut, Ihr Passwort zurückzusetzen.'
'Please update the request_password_repository configuration in config/packages/reset_password.yaml to point to your "request password repository" service.': 'Bitte aktualisieren Sie die request_password_repository-Konfiguration in config/packages/reset_password.yaml, um auf Ihren "request password repository"-Dienst zu verweisen.'
'The reset password link is invalid. Please try to reset your password again.': 'Der Link zum Zurücksetzen des Passworts ist ungültig. Bitte versuchen Sie erneut, Ihr Passwort zurückzusetzen.'
'You have already requested a reset password email. Please check your email or try again soon.': 'Sie haben bereits eine E-Mail mit einem neuen Passwort angefordert. Bitte überprüfen Sie Ihre E-Mail oder versuchen Sie es später erneut.'
'Your password reset request': 'Dein Passwort zurücksetzen Anfrage'
reset_password/email.html.twig: reset_password/de.email.html.twig

View File

@ -0,0 +1,13 @@
'%count% year|%count% years': '%count% year|%count% years'
'%count% month|%count% months': '%count% month|%count% months'
'%count% day|%count% days': '%count% day|%count% days'
'%count% hour|%count% hours': '%count% hour|%count% hours'
'%count% minute|%count% minutes': '%count% minute|%count% minutes'
'There was a problem validating your password reset request': 'There was a problem validating your password reset request'
'There was a problem handling your password reset request': 'There was a problem handling your password reset request'
'The link in your email is expired. Please try to reset your password again.': 'The link in your email is expired. Please try to reset your password again.'
'Please update the request_password_repository configuration in config/packages/reset_password.yaml to point to your "request password repository" service.': 'Please update the request_password_repository configuration in config/packages/reset_password.yaml to point to your "request password repository" service.'
'The reset password link is invalid. Please try to reset your password again.': 'The reset password link is invalid. Please try to reset your password again.'
'You have already requested a reset password email. Please check your email or try again soon.': 'You have already requested a reset password email. Please check your email or try again soon.'
'Your password reset request': 'Your password reset request'
reset_password/email.html.twig: reset_password/email.html.twig

View File

@ -1,13 +0,0 @@
role:
superadmin:
name: __role.superadmin.name
descr: __role.superadmin.descr
admin:
name: __role.admin.name
descr: __role.admin.descr
user:
name: __role.user.name
descr: __role.user.descr
public:
name: __role.public.name
descr: __role.public.descr

View File

@ -0,0 +1,35 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en de\n"
msgid "webroot-user.name"
msgstr "__webroot-user.name"
msgid "webroot-user.decr"
msgstr "__webroot-user.decr"
msgid "superadmin.name"
msgstr "__superadmin.name"
msgid "superadmin.descr"
msgstr "__superadmin.descr"
msgid "admin.name"
msgstr "__admin.name"
msgid "admin.descr"
msgstr "__admin.descr"
msgid "user.name"
msgstr "__user.name"
msgid "user.descr"
msgstr "__user.descr"
msgid "public.name"
msgstr "__public.name"
msgid "public.descr"
msgstr "__public.descr"

View File

@ -0,0 +1,173 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en de\n"
msgid "setupform.section.email"
msgstr "__setupform.section.email"
msgid "setupform.section.site"
msgstr "__setupform.section.site"
msgid "setupform.section.database"
msgstr "__setupform.section.database"
msgid "setupform.section.user"
msgstr "__setupform.section.user"
msgid "setupform.title"
msgstr "__setupform.title"
msgid "command.success"
msgstr "__command.success"
msgid "command.createdb"
msgstr "__command.createdb"
msgid "command.failed"
msgstr "__command.failed"
msgid "command.makemigrations"
msgstr "__command.makemigrations"
msgid "command.migrate"
msgstr "__command.migrate"
msgid "command.populatedb"
msgstr "__command.populatedb"
msgid "button.return-setup"
msgstr "__button.return-setup"
msgid "setupform.env.label"
msgstr "__setupform.env.label"
msgid "setupform.env.help"
msgstr "__setupform.env.help"
msgid "setupform.env.choices.prod"
msgstr "__setupform.env.choices.prod"
msgid "setupform.env.choices.dev"
msgstr "__setupform.env.choices.dev"
msgid "setupform.locale.label"
msgstr "__setupform.locale.label"
msgid "setupform.locale.help"
msgstr "__setupform.locale.help"
msgid "setupform.tempdir.label"
msgstr "__setupform.tempdir.label"
msgid "setupform.site.name.help"
msgstr "__setupform.site.name.help"
msgid "setupform.site.name.label"
msgstr "__setupform.site.name.label"
msgid "setupform.site.root.help"
msgstr "__setupform.site.root.help"
msgid "setupform.site.root.label"
msgstr "__setupform.site.root.label"
msgid "setupform.site.email.label"
msgstr "__setupform.site.email.label"
msgid "setupform.user.username.label"
msgstr "__setupform.user.username.label"
msgid "setupform.user.email.label"
msgstr "__setupform.user.email.label"
msgid "setupform.user.password.label"
msgstr "__setupform.user.password.label"
msgid "setupform.user.confpasswd.label"
msgstr "__setupform.user.confpasswd.label"
msgid "setupform.db.migrate.label"
msgstr "__setupform.db.migrate.label"
msgid "setupform.db.create.label"
msgstr "__setupform.db.create.label"
msgid "setupform.db.mkmigrations.label"
msgstr "__setupform.db.mkmigrations.label"
msgid "setupform.db.backend.label"
msgstr "__setupform.db.backend.label"
msgid "setupform.db.backend.choices.sqlite"
msgstr "__setupform.db.backend.choices.sqlite"
msgid "setupform.db.backend.choices.mysql"
msgstr "__setupform.db.backend.choices.mysql"
msgid "setupform.db.backend.choices.portgesql"
msgstr "__setupform.db.backend.choices.portgesql"
msgid "setupform.db.backend.choices.url"
msgstr "__setupform.db.backend.choices.url"
msgid "setupform.db.database.label"
msgstr "__setupform.db.database.label"
msgid "setupform.db.host.label"
msgstr "__setupform.db.host.label"
msgid "setupform.db.port.label"
msgstr "__setupform.db.port.label"
msgid "setupform.db.user.label"
msgstr "__setupform.db.user.label"
msgid "setupform.db.password.label"
msgstr "__setupform.db.password.label"
msgid "setupform.db.url.label"
msgstr "__setupform.db.url.label"
msgid "setupform.email.backend.label"
msgstr "__setupform.email.backend.label"
msgid "setupform.email.backend.choices.none"
msgstr "__setupform.email.backend.choices.none"
msgid "setupform.email.backend.choices.smtp"
msgstr "__setupform.email.backend.choices.smtp"
msgid "setupform.email.backend.choices.sendmail"
msgstr "__setupform.email.backend.choices.sendmail"
msgid "setupform.email.backend.choices.native"
msgstr "__setupform.email.backend.choices.native"
msgid "setupform.email.backend.choices.dsn"
msgstr "__setupform.email.backend.choices.dsn"
msgid "setupform.email.path.label"
msgstr "__setupform.email.path.label"
msgid "setupform.email.user.label"
msgstr "__setupform.email.user.label"
msgid "setupform.email.password.label"
msgstr "__setupform.email.password.label"
msgid "setupform.email.host.label"
msgstr "__setupform.email.host.label"
msgid "setupform.email.smtp-port.label"
msgstr "__setupform.email.smtp-port.label"
msgid "setupform.email.dsn.label"
msgstr "__setupform.email.dsn.label"
msgid "setupform.email.sender.label"
msgstr "__setupform.email.sender.label"
msgid "setupform.submit"
msgstr "__setupform.submit"

View File

@ -18,8 +18,9 @@
'Too many failed login attempts, please try again in %minutes% minute.': 'Zu viele fehlgeschlagene Anmeldeversuche, bitte versuchen Sie es in einer Minute noch einmal.' 'Too many failed login attempts, please try again in %minutes% minute.': 'Zu viele fehlgeschlagene Anmeldeversuche, bitte versuchen Sie es in einer Minute noch einmal.'
'Too many failed login attempts, please try again in %minutes% minutes.': 'Zu viele fehlgeschlagene Anmeldeversuche, bitte versuchen Sie es in %minutes% Minuten noch einmal.' 'Too many failed login attempts, please try again in %minutes% minutes.': 'Zu viele fehlgeschlagene Anmeldeversuche, bitte versuchen Sie es in %minutes% Minuten noch einmal.'
login: login:
title: "Bitte melde dich an" title: 'Bitte melde dich an'
button: "Anmelden" button: Anmelden
username: "Nutzername" username: Nutzername
password: "Passwort" password: Passwort
remember_me: "An mich erinnern" remember_me: 'An mich erinnern'
forgotten_password: 'Passwort vergessen?'

View File

@ -18,8 +18,9 @@
'Too many failed login attempts, please try again in %minutes% minute.': 'Too many failed login attempts, please try again in %minutes% minute.' 'Too many failed login attempts, please try again in %minutes% minute.': 'Too many failed login attempts, please try again in %minutes% minute.'
'Too many failed login attempts, please try again in %minutes% minutes.': 'Too many failed login attempts, please try again in %minutes% minutes.' 'Too many failed login attempts, please try again in %minutes% minutes.': 'Too many failed login attempts, please try again in %minutes% minutes.'
login: login:
title: "Please sign in" title: 'Please sign in'
button: "Log me in" button: 'Log me in'
username: "Username" username: Username
password: "Password" password: Password
remember_me: "Remember me" remember_me: 'Remember me'
forgotten_password: "Password forgotten?"

0
v7.2.0 Normal file
View File