From 6d540a6c715b89c7cb5dacda4fd824b88fff7eea Mon Sep 17 00:00:00 2001 From: Christian Moser Date: Sat, 30 Nov 2024 01:19:49 +0100 Subject: [PATCH] 2024.11.30 01:19:49 --- .gitattributes | 11 + assets/styles/app.css | 58 ++- .../webroot/mydevel_webroot_setup_initial.js | 92 +++++ assets/webroot/webroot.setup.run.js | 0 composer.json | 1 + composer.lock | 334 +++++++++++++++++- config/bundles.php | 2 + config/packages/translation.yaml | 6 - config/packages/twig_component.yaml | 5 + config/packages/uid.yaml | 4 + config/services.yaml | 9 - src/Controller/SetupController.php | 160 ++++++--- src/Controller/WebrootSetupController.php | 214 ++++++++--- symfony.lock | 33 ++ templates/setup/initial-setup.html.twig | 47 +-- templates/setup/initial-setup.run.html.twig | 72 ++++ .../de/LC_MESSAGES/mydevel-webroot.mo | Bin 1876 -> 0 bytes translations/messages+intl-icu.de.yaml | 97 +++++ translations/messages+intl-icu.en.yaml | 92 +++++ translations/validators.de.yaml | 186 ---------- 20 files changed, 1103 insertions(+), 320 deletions(-) create mode 100644 .gitattributes create mode 100644 assets/webroot/mydevel_webroot_setup_initial.js create mode 100644 assets/webroot/webroot.setup.run.js create mode 100644 config/packages/twig_component.yaml create mode 100644 config/packages/uid.yaml create mode 100644 templates/setup/initial-setup.run.html.twig delete mode 100644 translations/de/LC_MESSAGES/mydevel-webroot.mo create mode 100644 translations/messages+intl-icu.de.yaml create mode 100644 translations/messages+intl-icu.en.yaml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..84ff2a7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto + +*.js text +*.php text +*.html text +*.htm text + +*.png binary +*.svg binary +*.ico binary + diff --git a/assets/styles/app.css b/assets/styles/app.css index ad8375f..b8c6cf9 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -83,6 +83,12 @@ --color-code: white; --color-link-visited: #E496E7; --color-link-normal: #4880E7; + --color-select-background:#EAE3C4; + --color-optional-input:#B8B8B8; + --color-button-background:#645822; + --color-button-background-disabled:#645822; + --color-button-border:#EAE3C2; + --color-button-text: #E7E0BA; /* fonts */ --font-family: "JetBrains"; @@ -252,17 +258,49 @@ label { input[type=email], input[type=password], -input[type=text] +input[type=text], +input[type=number] { --color-text: #E0D4A4; - --background-color: height: 20px; font-size: 16px; background-color: var(--color-text); color: var(--color-background); - border-radius: 2px; + border-radius: 3px; border-width: 0px; - width: 100%; +} + +input[type=submit], +button +{ + background-color: var(--color-button-background); + border: 1px solid var(--color-button-border); + border-radius: 3px; + padding:4px; + font-size: 16px; + color: var(--color-button-text); +} + +input[type=submit]:disabled, +input[type=submit]:invalid, +button:disabled, +button:invalid +{ + background-color: var(--color-button-background-disabled); +} + + +select { + height:20px; + font-size:16px; + background-color: var(--color-select-background); + color: var(--color-background); + border-radius:4px; + border-width: 0px; +} + +.optional-input { + background-color: var(--color-optional-input); } code { @@ -285,8 +323,16 @@ code { } fieldset { - border: 2px var(--color-text) solid; - border-radius: 4px; + border: 3px solid var(--color-text); + border-radius: 8px; margin-top: 5px; margin-bottom: 5px; } + +.margin-5 { + margin: 5px; +} + +.margin-10 { + margin: 10px; +} \ No newline at end of file diff --git a/assets/webroot/mydevel_webroot_setup_initial.js b/assets/webroot/mydevel_webroot_setup_initial.js new file mode 100644 index 0000000..b4a5a57 --- /dev/null +++ b/assets/webroot/mydevel_webroot_setup_initial.js @@ -0,0 +1,92 @@ + + +function setOptional(element) { + element.removeAttribute("required"); + element.style.backgroundColor = "var(--color-optional-input)";; +} +function setRequired(element) { + element.setAttribute("required",""); + element.style.backgroundColor = "var(--color-text)"; +} + +function onDatabaseBackendChanged() { + const backend = document.getElementById("form_db_backend").value; + let database = document.getElementById("form_db_database"); + let host = document.getElementById("form_db_host"); + let port = document.getElementById("form_db_port"); + let user = document.getElementById("form_db_user"); + let password = document.getElementById("form_db_password"); + let url = document.getElementById("form_db_url"); + + if (backend === "url") { + setOptional(database); + setOptional(host); + setOptional(port); + setOptional(user); + setOptional(password); + setRequired(url); + } else if (backend === "sqlite") { + setRequired(database); + setOptional(host); + setOptional(port); + setOptional(user); + setOptional(password); + setOptional(url); + } else if ((backend === "postgresql") || (backend === "mysql")) { + setRequired(database); + setRequired(host); + setRequired(port); + setRequired(user); + setRequired(password); + setOptional(url); + } +} + +function onEmailBackendChanged() { + const backend = document.getElementById("form_email_backend").value; + let user = document.getElementById("form_email_user"); + let passwd = document.getElementById("form_email_password"); + let host = document.getElementById("form_email_host"); + let port = document.getElementById("form_email_port"); + let path = document.getElementById("form_email_path"); + let dsn = document.getElementById("form_email_dsn"); + + if (backend === "dsn") { + setOptional(user); + setOptional(passwd); + setOptional(host); + setOptional(port); + setOptional(path); + setRequired(dsn); + } else if ((backend === "none") || (backend === "native")) { + setOptional(user); + setOptional(passwd); + setOptional(host); + setOptional(port); + setOptional(path); + setOptional(dsn); + } else if (backend === "smtp") { + setRequired(user); + setRequired(passwd); + setRequired(host); + setRequired(port); + setOptional(path); + setOptional(dsn); + } else if (backend === "sendmail") { + setOptional(user); + setOptional(passwd); + setOptional(host); + setOptional(port); + setRequired(path); + setOptional(dsn); + } +} + +function initializeWebrootSetupForm() { + document.getElementById("form_db_backend").onchange = onDatabaseBackendChanged; + onDatabaseBackendChanged(); + document.getElementById("form_email_backend").onchange = onEmailBackendChanged; + onEmailBackendChanged(); + + console.log("WebrootSetupForm initialized!"); +} diff --git a/assets/webroot/webroot.setup.run.js b/assets/webroot/webroot.setup.run.js new file mode 100644 index 0000000..e69de29 diff --git a/composer.json b/composer.json index c3ee750..5f29e91 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "doctrine/doctrine-bundle": "^2.13", "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.3", + "easycorp/easyadmin-bundle": "^4.18", "phpdocumentor/reflection-docblock": "^5.6", "phpstan/phpdoc-parser": "^2.0", "symfony/asset": "6.4.*", diff --git a/composer.lock b/composer.lock index 453eb2c..432b419 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "261833b7e06c09ff39b49b9b6f4128c8", + "content-hash": "bc953808482a0b93397e27bda2670bdc", "packages": [ { "name": "composer/semver", @@ -1313,6 +1313,102 @@ }, "time": "2024-10-21T18:21:57+00:00" }, + { + "name": "easycorp/easyadmin-bundle", + "version": "v4.18.0", + "source": { + "type": "git", + "url": "https://github.com/EasyCorp/EasyAdminBundle.git", + "reference": "c1b694c1890d6f20858802d201cd7199212dc42a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/c1b694c1890d6f20858802d201cd7199212dc42a", + "reference": "c1b694c1890d6f20858802d201cd7199212dc42a", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "^2.5", + "doctrine/orm": "^2.12|^3.0", + "ext-json": "*", + "php": ">=8.1", + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/deprecation-contracts": "^3.0", + "symfony/doctrine-bridge": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/security-bundle": "^5.4|^6.0|^7.0", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/ux-twig-component": "^2.21", + "symfony/validator": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "doctrine/doctrine-fixtures-bundle": "^3.4|3.5.x-dev", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-phpunit": "^1.2", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.2", + "psr/log": "^1.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/debug-bundle": "^5.4|^6.0|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^6.1|^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "EasyCorp\\Bundle\\EasyAdminBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Project Contributors", + "homepage": "https://github.com/EasyCorp/EasyAdminBundle/graphs/contributors" + } + ], + "description": "Admin generator for Symfony applications", + "homepage": "https://github.com/EasyCorp/EasyAdminBundle", + "keywords": [ + "admin", + "backend", + "generator" + ], + "support": { + "issues": "https://github.com/EasyCorp/EasyAdminBundle/issues", + "source": "https://github.com/EasyCorp/EasyAdminBundle/tree/v4.18.0" + }, + "funding": [ + { + "url": "https://github.com/javiereguiluz", + "type": "github" + } + ], + "time": "2024-11-28T19:54:15+00:00" + }, { "name": "egulias/email-validator", "version": "4.0.2", @@ -5271,6 +5367,85 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/process", "version": "v6.4.15", @@ -6772,6 +6947,80 @@ ], "time": "2024-09-25T14:18:03+00:00" }, + { + "name": "symfony/uid", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:18:03+00:00" + }, { "name": "symfony/ux-turbo", "version": "v2.21.0", @@ -6870,6 +7119,89 @@ ], "time": "2024-10-21T19:07:02+00:00" }, + { + "name": "symfony/ux-twig-component", + "version": "v2.21.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/ux-twig-component.git", + "reference": "5b60b239fffcb04fc8bdb2a5a4001d19442d575d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/5b60b239fffcb04fc8bdb2a5a4001d19442d575d", + "reference": "5b60b239fffcb04fc8bdb2a5a4001d19442d575d", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/deprecation-contracts": "^2.2|^3.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "twig/twig": "^3.8" + }, + "conflict": { + "symfony/config": "<5.4.0" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^6.0|^7.0", + "symfony/stimulus-bundle": "^2.9.1", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/webpack-encore-bundle": "^1.15" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "name": "symfony/ux", + "url": "https://github.com/symfony/ux" + } + }, + "autoload": { + "psr-4": { + "Symfony\\UX\\TwigComponent\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Twig components for Symfony", + "homepage": "https://symfony.com", + "keywords": [ + "components", + "symfony-ux", + "twig" + ], + "support": { + "source": "https://github.com/symfony/ux-twig-component/tree/v2.21.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-05T22:11:16+00:00" + }, { "name": "symfony/validator", "version": "v6.4.15", diff --git a/config/bundles.php b/config/bundles.php index 4e3a560..1737743 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -13,4 +13,6 @@ return [ Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true], + EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], ]; diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml index a1d4d3e..aab528a 100644 --- a/config/packages/translation.yaml +++ b/config/packages/translation.yaml @@ -4,10 +4,4 @@ framework: default_path: '%kernel.project_dir%/translations' fallbacks: - en - providers: - mo: - dsn: '%kernel.project_dir%/translations' - locales: [de] - domains: [mydevel_webroot,messages] - diff --git a/config/packages/twig_component.yaml b/config/packages/twig_component.yaml new file mode 100644 index 0000000..fd17ac6 --- /dev/null +++ b/config/packages/twig_component.yaml @@ -0,0 +1,5 @@ +twig_component: + anonymous_template_directory: 'components/' + defaults: + # Namespace & directory for components + App\Twig\Components\: 'components/' diff --git a/config/packages/uid.yaml b/config/packages/uid.yaml new file mode 100644 index 0000000..0152094 --- /dev/null +++ b/config/packages/uid.yaml @@ -0,0 +1,4 @@ +framework: + uid: + default_uuid_version: 7 + time_based_uuid_version: 7 diff --git a/config/services.yaml b/config/services.yaml index c675c86..32db85d 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -29,15 +29,6 @@ services: tags: ['controller.service_arguments'] - translation.loader.po: - class: Symfony\Component\Translation\Loader\PoFileLoader - tags: - - { name: translation.loader, alias: po } - tranlation.loader.mo: - class: Symfony\Component\Translation\Loader\MoFileLoader - tags: - - { name: translation.loader, alias: mo } - # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/Controller/SetupController.php b/src/Controller/SetupController.php index 8764eb0..d2c4bcf 100644 --- a/src/Controller/SetupController.php +++ b/src/Controller/SetupController.php @@ -5,6 +5,8 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; + use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Form\Extension\Core\Type as FormType; use Symfony\Component\Dotenv\Dotenv; @@ -19,8 +21,7 @@ use App\Entity\WebrootFilePermission; class SetupController extends WebrootSetupController -{ - +{ #[Route('/setup',name:'webroot.setup.initial')] public function initialSetup(Request $request, EntityManagerInterface $em, @@ -39,10 +40,11 @@ class SetupController extends WebrootSetupController $have_migration_output=false; $translations=[ - 'email'=>_("Email Settings"), - 'site' => _("Site Settings"), - 'db' => _("Database Settings"), - 'user' => _("User Settings"), + 'email'=>$this->trans("setupform.section.email"), + 'site' => $this->trans("setupform.section.site"), + 'db' => $this->trans("setupform.section.database"), + 'user' => $this->trans("setupform.section.user"), + 'title' => $this->trans("setupform.title"), ]; $form = $this->createSetupForm(); @@ -71,40 +73,10 @@ class SetupController extends WebrootSetupController } - $dotevnlocal = join(DIRECTORY_SEPARATOR,[$this->getProjectDir(),'.env.local']); - $file = fopen($dotevnlocal,"w"); - - $language=$form->get('language')->getNormData(); - if ($language || strlen($language) > 1) { - fwrite($file,"LANG=\"" . $language . "\"\n"); - } - fwrite($file,"APP_LOCALEDIR=\"%kernel.project_dir%/Translations\""); - - fwrite($file,"SITE_NAME=\"" . $form->get("site_name")->getNormData() . "\"\n"); - fwrite($file,"SITE_EMAIL=\"". $form->get('site_email')->getNormData() . "\"\n"); - - $db_backend=$form->get('db_backend')->getNormData(); - if ($db_backend === "sqlite") { - fwrite($file,"DATABASE_URL=\"sqlite://" . $form->get('db_database')->getNormData() . "\"\n"); - } elseif ($db_backend === "mysql") { - fwrite("DATABASE_URL=\"msysql://" - . urlencode($form.get('db_user')->getNormData()) - . ":" . urlencode($form->get('db_password')->getNormData()) - . "@" . urlencode($form->get('db_host')->getNormData()) - . ":" . $form.get('db_port')->getNormData() - . "/" . urlencode($form->get('db_database')->getNormData()) - . "?charset=utf8mb4\"\n"); - } elseif ($db_backend === "postgresql") { - fwrite("DATABASE_URL=\"postgresql://" - . urlencode($form->get('db_user')->getNormData()) - . ":" . urlencode($form->get('db_password')->getNormData()) - . "@" . urlencode($form->get('db_host')->getNormData()) - . ":" . $form->get('db_port')->getNormData() - . "/" . urlencode($form->get('db_database')->getNormData()) - . "?charset=utf8"); - } - fclose($file); + $this->setSetupDataCookie($form); (new Dotenv())->loadEnv(join(DIRECTORY_SEPARATOR,[$this->getProjectDir(),".env"])); + $this->redirectToRoute("webroot.setup.run"); + /* try { $have_migration_output=true; @@ -163,7 +135,7 @@ class SetupController extends WebrootSetupController throw $ex; } finally { - } + }*/ } return $this->render('setup/initial-setup.html.twig', [ @@ -176,7 +148,7 @@ class SetupController extends WebrootSetupController } #[Route('/setup', name: 'webroot.setup')] - public function index(): Response + public function setup(): Response { if (!$this->isInstalled()) { return redirectToRoute('webroot.setup.initial'); @@ -192,12 +164,114 @@ class SetupController extends WebrootSetupController ]); } - #[Route('/setup/login',name:'webroot.setup.login')] - public function login() + #[ROUTE('/setup/inital/run',name:'setup.initial.run')] + public function setupInitialRun() : Response { return $this->render('setup/controller.html.twig', [ 'controller_name' => "WebrootController", 'function' => __FUNCTION__, ]); } + + #[Route('/setup/run/make-migration',name:'setup.initial.run.mkmigrations')] + public function setupInitialRunMakeMigrations(): Response + { + if ($this->hasAdministrator()) { + $user = getUser(); + if (!$user.isAdmin()) { + return redirectToRoute("app_login"); + } + } elseif (!isset($_COOKIE["--webroot-setup--"])) { + throw AccessDeniedException("You do not seem to be the Administrator"); + } + + try { + $this->runMakeMigration(); + $result="SUCCESS"; + } catch (\Exception $ex) { + $result="FAILED"; + } + + $ret = "" + . "" + . "" + . "" + . "" + . "" + . "" . $result ."" + . "" + . ""; + return new Response($ret); + } + + #[Route('/setup/run/migrate',name:'webroot.setup.run.migrate')] + public function setupRunMigrate() + { + if ($this->hasAdministrator()) { + $user = getUser(); + if (!$user.isAdmin()) { + return redirectToRoute("app_login"); + } + } elseif (!isset($_COOKIE["--webroot-setup--"])) { + throw AccessDeniedException("You do not seem to be the Administrator"); + } + + try { + $this->runMigrate(); + $result = "SUCCESS"; + } catch (\Exception $ex) { + $result = "FAILED"; + } + + $ret = "" + . "" + . "" + . "" + . "" + . "" + . "" . $result ."" + . "" + . ""; + + return new Response($ret); + } + #[Route('/setup/run/createdb',name:'webroot.setup.run.createdb')] + public function createDatabase(): Response + { + if ($this->hasAdministrator()) { + $user = getUser(); + if (!$user.isAdmin()) { + return redirectToRoute("app_login"); + } + } elseif (!isset($_COOKIE["--webroot-setup--"])) { + throw AccessDeniedException("You do not seem to be the Administrator"); + } + + try { + $this->$this->runCreateDatabase();; + $retsult = "SUCCESS"; + } catch (\Exception $ex) { + $result = "FAILED"; + } + $ret = "" + . "" + . "" + . "" + . "" + . "" + . "" . $result ."" + . "" + . ""; + + return new Response($ret); + } + + #[Route('/setup/login',name:'webroot.setup.login')] + public function login(): Response + { + return $this->render('setup/controller.html.twig', [ + 'controller_name' => "WebrootController", + 'function' => __F0UNCTION__, + ]); + } } diff --git a/src/Controller/WebrootSetupController.php b/src/Controller/WebrootSetupController.php index 264e697..c22c012 100644 --- a/src/Controller/WebrootSetupController.php +++ b/src/Controller/WebrootSetupController.php @@ -7,6 +7,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +use Symfony\Contracts\Translation\TranslatorInterface; @@ -29,12 +30,16 @@ abstract class WebrootSetupController extends AbstractController private ?KernelInterface $kernel = null; private ?string $project_dir = null; private ?UserPasswordHasherInterface $password_hasher; + private ?TranslatorInterface $translator; - public function __construct(KernelInterface $kernel, UserPasswordHasherInterface $password_hasher) { + public function __construct(KernelInterface $kernel, + UserPasswordHasherInterface $password_hasher, + TranslatorInterface $translator) { $this->kernel = $kernel; $this->project_dir = $kernel->getProjectDir(); $this->password_hasher = $password_hasher; + $this->translator = $translator; Utility\gettext_lib_init( WEBROOT_GETTEXT_DOMAIN, join(DIRECTORY_SEPARATOR,[$this->project_dir,"translations"]), @@ -80,6 +85,26 @@ abstract class WebrootSetupController extends AbstractController return false; } + + public function getKernel(): Kernel + { + return $this->kernel; + } + + public function getTranslator(): TranslatorInterface + { + return $this->translator; + } + + public function trans(string $message,array $args=[],?string $locale=null,?string $domain=null) : string + { + return $this->translator->trans($message,$args,locale:$locale,domain:$domain); + } + + public function translate(string $message,array $args=[],?string $locale=null,?string $domain=null) :string + { + return $this->translator->trans($message,$args,locale:$locale,domain:$domain); + } /** * @brief Check if we are already set up. @@ -172,13 +197,27 @@ abstract class WebrootSetupController extends AbstractController return $content; } + public function getInitialRoles(): array { return [ - ["role"=>"ROLE_SUPERADMIN","name"=>"Super Administrator","description"=>"Super Administrator with all rights, always!"], - ["role"=>"ROLE_ADMIN","name"=>"Administrator","description"=>"Role with administrative rights."], - ["role"=>"ROLE_USER","name"=>"Common User","description"=>"Common user rights."], - ["role"=>"ROLE_PUBLIC","name"=>"Public Access","description"=>"Role for public access."], + [ + "role"=>"ROLE_SUPERADMIN", + "name"=>$this->trans("role.superadmin.name",locale:'en'), + "description"=>$this->trans("role.superadmin.descr",locale:'en'), + ],[ + "role"=>"ROLE_ADMIN", + "name"=>$this->trans("role.admin.name",locale:'en'), + "description"=>$this->trans("role.admin.descr"), + ],[ + "role"=>"ROLE_USER", + "name"=>$this->trans("role.user.name",locale:'en'), + "description"=>$this->trans("role.user.descr",locale:'en'), + ],[ + "role"=>"ROLE_PUBLIC", + "name"=>$this->trans("role.public.name",locale:'en'), + "description"=>$this->trans("role.public.descr",locale:'en'), + ], ]; } @@ -186,133 +225,164 @@ abstract class WebrootSetupController extends AbstractController { return $this->createFormBuilder() ->add('env', FormType\ChoiceType::class,[ - "label"=>"Environment", + "label"=>$this->translator->trans("setupform.env.label"), "mapped"=>false, "required"=>true, + "help"=>$this->trans("setupform.env.help"), "choices"=>[ - "Production" => "prod", - "Development" => "dev" + $this->translator->trans("setupform.env.choices.prod") => "prod", + $this->translator->trans("setupform.env.choices.dev") => "dev", ], + "attr"=>["class"=>"full-width"], "data" => "prod"]) ->add('locale',FormType\TextType::class,[ "mapped"=>false, - "required"=>false]) + "label"=>$this->trans("setupform.locale.label"), + "help"=>$this->trans("setupform.locale.help"), + "required"=>false, + "attr"=>["class"=>"full-width"]]) ->add('tempdir',FormType\TextType::class,[ "mapped"=>false, - "required"=>false]) + "label"=>$this->trans("setupform.tempdir.label"), + "required"=>false, + "attr"=>["class"=>"full-width"]]) ->add('site_name',FormType\TextType::class,[ "mapped"=>false, "required"=>true, - "help"=>_("Enter the site name that should be displayed in titles."), - "label" => _("Site name")]) + "help"=>$this->trans("setupform.site.name.help"), + "label" => $this->trans("setupform.site.name.label"), + "attr"=>["class"=>"full-width"]]) ->add('site_rootdir',FormType\TextType::class,[ "mapped"=>false, "required"=>true, - "label" => _("Site root directory")]) + "help"=>$this->trans("setupform.site.root.help"), + "label" => $this->trans("setupform.site.root.label"), + "attr"=>["class"=>"full-width"]]) ->add('site_email',FormType\EmailType::class,[ "mapped"=>false, "required"=>false, - "label" => _("Contact email")]) + "label" => $this->trans("setupform.site.email.label"), + "attr"=>["class"=>"full-width"]]) ->add('user_username',FormType\TextType::class,[ "mapped"=>false, "required"=>true, - "label"=>_("Username")]) + "label"=>$this->trans("setupform.user.username.label"), + "attr"=>["class"=>"full-width"]]) ->add('user_email',FormType\EmailType::class,[ "mapped"=>false, "required"=>true, - "label"=> _("Email")]) + "label"=> $this->trans("setupform.user.email.label"), + "attr"=>["class"=>"full-width"]]) ->add("user_password0", FormType\PasswordType::class,[ "mapped"=>false, "required"=>true, - "label"=>_("Password")]) + "label"=>$this->trans("setupform.user.password.label"), + "attr"=>["class"=>"full-width"]]) ->add("user_password1",FormType\PasswordType::class,[ "mapped"=>false, "required"=>true, - "label"=>"Confirm Password"]) + "label"=>$this->trans("setupform.user.confpasswd.label"), + "attr"=>["class"=>"full-width"]]) ->add('db_migrate', FormType\CheckboxType::class,[ "mapped"=>false, - "label"=>"Run Migrations?", + "label"=>$this->trans("setupform.db.migrate.label"), "attr"=>["checked"=>true], "required"=>false]) ->add('db_create', FormType\CheckboxType::class,[ "mapped"=>false, - "label"=>"Create Database", + "label"=>$this->trans("setupform.db.create.label"), "required"=>false, "attr"=>["checked"=>false]]) ->add('db_backend',FormType\ChoiceType::class,[ "mapped" => false, - "label" => "Backend", + "label" => $this->trans("setupform.db.backend.label"), "required" => true, "choices" => [ - _("SQLite3")=>"sqlite", - _("MySQL/MariaDB")=>"mysql", - _("PostgreSQL")=>"postgresql", - _("Database URL")=>"url", - ]]) + $this->trans("setupform.db.backend.choices.sqlite")=>"sqlite", + $this->trans("setupform.db.backend.choices.mysql")=>"mysql", + $this->trans("setupform.db.backend.choices.portgesql")=>"postgresql", + $this->trans("setupform.db.backend.choices.url")=>"url", + ], + "attr"=>["class"=>"full-width"]]) ->add('db_database',FormType\TextType::class,[ "mapped"=>false, "required"=>true, - "label"=>_("Database")]) + "label"=>$this->trans("setupform.db.database.label"), + "attr"=>["class"=>"full-width"]]) ->add('db_host', FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("Host")]) + "label"=>$this->trans("setupform.db.host.label"), + "attr"=>["class"=>"full-width"]]) ->add('db_port', FormType\IntegerType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("Port")]) + "label"=>$this->trans("setupform.db.port.label"), + "attr"=>["class"=>"full-width"]]) ->add('db_user',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("User")]) + "label"=>$this->trans("setupform.db.user.label"), + "attr"=>["class"=>"full-width"]]) ->add('db_password',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("Password")]) + "label"=>$this->trans("setupform.db.password.label"), + "attr"=>["class"=>"full-width"]]) ->add('db_url',FormType\TextType::class,[ "mapped"=>false, - "label"=>_("Database URL"), - "required"=>false]) + "label"=>$this->trans("setupform.db.url.label"), + "required"=>false, + "attr"=>["class"=>"full-width"]]) ->add('email_backend', FormType\ChoiceType::class, [ "mapped"=>false, - "label" => _("Backend"), + "label" => $this->trans("setupform.email.backend.label"), "required"=>true, "choices"=> [ - _("No email support")=>"none", - _("SMTP")=>"smtp", - _("Sendmail")=>"sendmail", - _("Naitve")=>"native", - _("User DSN")=>"dsn", - ]]) + $this->trans("setupform.email.backend.choices.none")=>"none", + $this->trans("setupform.email.backend.choices.smtp")=>"smtp", + $this->trans("setupform.email.backend.choices.sendmail")=>"sendmail", + $this->trans("setupform.email.backend.choices.native")=>"native", + $this->trans("setupform.email.backend.choices.dsn")=>"dsn", + ], + "attr"=>["class"=>"full-width"]]) ->add('email_path',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("Email Path")]) + "label"=>$this->trans("setupform.email.path.label"), + "attr"=>["class"=>"full-width"]]) ->add('email_user',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("User")]) + "label"=>$this->trans("setupform.email.user.label"), + "attr"=>["class"=>"full-width"]]) ->add('email_password', FormType\PasswordType::class,[ "mapped"=>false, "required"=>false, - "label"=> _("Password")]) + "label"=> $this->trans("setupform.email.password.label"), + "attr"=>["class"=>"full-width"]]) ->add('email_host',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("SMTP Host")]) + "label"=>$this->trans("setupform.email.host.label"), + "attr"=>["class"=>"full-width"]]) ->add('email_port',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("SMTP Port")]) + "label"=>$this->trans("setupform.email.smtp-port.label"), + "attr"=>["class"=>"full-width"]]) ->add('email_dsn',FormType\TextType::class,[ "mapped"=>false, "required"=>false, - "label"=>_("DSN")]) + "label"=>$this->trans("setupform.email.dsn.label"), + "attr"=>["class"=>"full-width"]]) ->add('email_address', FormType\EmailType::class,[ "mapped"=>false, "required"=>true, - "label"=>_("Sender address")]) - ->add('submit', FormType\SubmitType::class) + "label"=>$this->trans("setupform.email.sender.label"), + "attr"=>["class"=>"full-width"]]) + ->add('submit', FormType\SubmitType::class,[ + "label"=>$this->trans("setupform.submit")]) ->getForm(); } @@ -549,6 +619,18 @@ abstract class WebrootSetupController extends AbstractController $fclose($file); } + function generateRandomString($length = 10) : string + { + $x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $x_len=strlen($x); + $ret=""; + + for ($i=0; $i< $length; ++$i) { + $ret .= x[rand() % $x_len]; + } + return ret; + } + public function setSetupFormFromFile(Form $form,string $yaml_file) { $this->setSetupFormFromData($form,(new Yaml())->parseFile($yaml_file)); @@ -564,4 +646,40 @@ abstract class WebrootSetupController extends AbstractController $this->exportSetupFormToFile($form, $filename); setcookie("--webroot-setup--", $filename); } + + public function writePrivEnv(array $data,bool $generate_app_secret=true) { + $file = fopen(join(DIRECTORY_SEPARATOR,[$this->project_dir,".env.local"])); + fwrite($file,"ENV=" . $data["env"] . "\n"); + + if ($generate_app_secret) { + fwrite($file,"APP_SECRET=\"" . $this->generateRandomString(60) . "\"\n"); + } else { + fwrite($file,"APP_SECET=\"" . getenv("APP_SECRET") . "\"\n"); + } + fwrite($file,"SITE_NAME=\"" . $data['site']['site_name'] . "\"\n"); + fwrite($file,"SITE_EMAIL=\"". $data['site']['email'] . "\"\n"); + + $db_backend=$data['database']['backend']; + if ($db_backend === "sqlite") { + fwrite($file,"DATABASE_URL=\"sqlite://" . $data['database']['database'] . "\"\n"); + } elseif ($db_backend === "mysql") { + fwrite($file,"DATABASE_URL=\"msysql://" + . urlencode($data['database']['user']) + . ":" . urlencode($data['database']['password']) + . "@" . urlencode($data['database']['host']) + . ":" . $data['database']['port'] + . "/" . urlencode($data['database']['database']) + . "?charset=utf8mb4\"\n"); + } elseif ($db_backend === "postgresql") { + fwrite($file,"DATABASE_URL=\"postgresql://" + . urlencode($data['database']['user']) + . ":" . urlencode($data['database']['password']) + . "@" . urlencode($data['database']['host']) + . ":" . $data['database']['port'] + . "/" . urlencode($data['database']['database']) + . "?charset=utf8"); + } elseif ($db_backend === "url") { + fwrite($file,"DATABASE_URL=\"" . $data["database"]["url"] . "\"\n"); + } + } } diff --git a/symfony.lock b/symfony.lock index c53dbb6..76897ca 100644 --- a/symfony.lock +++ b/symfony.lock @@ -26,6 +26,15 @@ "./migrations/.gitignore" ] }, + "easycorp/easyadmin-bundle": { + "version": "4.18", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.0", + "ref": "b131e6cbfe1b898a508987851963fff485986285" + } + }, "phpunit/phpunit": { "version": "9.6", "recipe": { @@ -248,6 +257,18 @@ "./templates/base.html.twig" ] }, + "symfony/uid": { + "version": "6.4", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.2", + "ref": "d294ad4add3e15d7eb1bae0221588ca89b38e558" + }, + "files": [ + "./config/packages/uid.yaml" + ] + }, "symfony/ux-turbo": { "version": "2.21", "recipe": { @@ -257,6 +278,18 @@ "ref": "9dd2778a116b6e5e01e5e1582d03d5a9e82630de" } }, + "symfony/ux-twig-component": { + "version": "2.21", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.13", + "ref": "67814b5f9794798b885cec9d3f48631424449a01" + }, + "files": [ + "./config/packages/twig_component.yaml" + ] + }, "symfony/validator": { "version": "6.4", "recipe": { diff --git a/templates/setup/initial-setup.html.twig b/templates/setup/initial-setup.html.twig index ed5d5af..b44aa9f 100644 --- a/templates/setup/initial-setup.html.twig +++ b/templates/setup/initial-setup.html.twig @@ -1,7 +1,11 @@ {% extends 'base.html.twig' %} +{% block javascripts %} + {% block importmap %}{{ importmap('app') }}{% endblock %} + +{% endblock %} {% block body %} -

Webroot Setup

+

{{ translations.title }}

    @@ -12,48 +16,49 @@
{{ form_start(form) }} -
+
{{ translations.site }} - - - + + + - - - - + + + + - - + + - + +
{{ form_label(form.locale) }}{{ form_widget(form.locale) }} {{ form_label(form.env) }}{{ form_widget(form.env) }}{{ form_widget(form.env,{'attr':{'tabindex':1}}) }}{{ form_label(form.site_name) }}{{ form_widget(form.site_name,{'attr':{'tabindex':4}}) }}
{{ form_label(form.site_name) }}{{ form_widget(form.site_name) }}{{ form_label(form.site_email) }}{{ form_widget(form.site_email) }}{{ form_label(form.locale) }}{{ form_widget(form.locale,{'attr':{'tabindex':2}}) }}{{ form_label(form.site_rootdir) }}{{ form_widget(form.site_rootdir,{'attr':{'tabindex':5}}) }}
{{ form_label(form.site_rootdir) }}{{ form_widget(form.site_rootdir) }}{{ form_label(form.site_email) }}{{ form_widget(form.site_email,{'attr':{'tabindex':3}}) }} {{ form_label(form.tempdir) }}{{ form_widget(form.tempdir) }}{{ form_widget(form.tempdir,{'attr':{'tabindex':6}}) }}
-
+
{{ translations.user }} - + - + - + - +
{{ form_label(form.user_username) }}{{ form_widget(form.user_username) }}{{ form_widget(form.user_username,{'attr':{'tabindex':7}}) }} {{ form_label(form.user_password0) }}{{ form_widget(form.user_password0) }}{{ form_widget(form.user_password0,{'attr':{'tabindex':9}}) }}
{{ form_label(form.user_email) }}{{ form_widget(form.user_email) }}{{ form_widget(form.user_email,{'attr':{'tabindex':8}}) }} {{ form_label(form.user_password1) }}{{ form_widget(form.user_password1) }}{{ form_widget(form.user_password1,{'attr':{'tabindex':10}}) }}
-
+
{{ translations.db }} @@ -83,7 +88,7 @@
-
+
{{ translations.email }} @@ -114,8 +119,8 @@
- {{ form_widget(form.submit) }} +
{{ form_widget(form.submit) }}
{{ form_end(form) }}
- + {% endblock %} diff --git a/templates/setup/initial-setup.run.html.twig b/templates/setup/initial-setup.run.html.twig new file mode 100644 index 0000000..30ab797 --- /dev/null +++ b/templates/setup/initial-setup.run.html.twig @@ -0,0 +1,72 @@ +{% extends 'base.html.twig' %} +{% block body %} + +

Running Setup

+
+ +

Running commands...

+
+
+ +{% endblock %} diff --git a/translations/de/LC_MESSAGES/mydevel-webroot.mo b/translations/de/LC_MESSAGES/mydevel-webroot.mo deleted file mode 100644 index d94db20aa91a381e7e61f09d418d404e42fc4894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1876 zcma)*O=z4&7{{lvesujxYt>q0bpO0?&f@s3sQrErIlVtK3`gDDK3AjQataIt*R~>GwH^lv|8Q-}@lrd*0T z4|D%}FedMtH6Fp_9dI1OWIr6#*v??=doiEdF@St`YEWZ7j>+*PCQ_?XjztfwmpyS% zYpj=p?{E~8`ru%HyhHY-W=wBYT&gbQZpHdF-7+C;=DsjQ(~DZxjLp<+dr{4rXu2lI z?ibyb7_IAvv1NH_LCqB>iWTAFU}P9O&$%dG$ARczqiAQ=1UnOT805n&)m_o44eOcN zl2lu#qm7w_DfMa|qH1onUt6d)jkjiYMlBi}H$*MwqcU|EuCM1ls@*PH#SRDiUT{z? z4V`YhD z>q@dD80u*5DFy zmT|7PV|cl@M$428U&JxjADX@-9WiT2x3h2PR4UE+=G?f#`c$m~=|pizw6%^(qP!cc zcs%P~(7l22LE`!4&c)=+*pCIRh)?ERFHfPLhCb7^vGQheNu1zmSZOPl)JA=B>iNp9 zM$1=?P)La1eHv|FoVtwcW;N=O>Pnr8>l(L8S&qKxIqO1{EE_tW8Qb^hHEFGwwAAbE zM=seex_5jyD~>Pbv~lSP7(GSWfgUoN`m!yyAN8hq`iDHr{zJOaZm7#*os4Q9@^68@ zuElLsxg%j{d5?s5>yI0pqVF0ZmZUd)0h3 diff --git a/translations/messages+intl-icu.de.yaml b/translations/messages+intl-icu.de.yaml new file mode 100644 index 0000000..3f1da28 --- /dev/null +++ b/translations/messages+intl-icu.de.yaml @@ -0,0 +1,97 @@ +role: + superadmin: + name: Superadministrator + descr: 'Superadministrator hat alle Rechte.' + admin: + name: Administrator + descr: 'Rolle für Administrator rechte.' + user: + name: 'Common User' + descr: 'Standardnutzer Role' + public: + name: Public + descr: 'Role für öffentlichen Zugang' +setupform: + env: + label: Umgebung + help: 'Applikationsumgebung. Nutze Produktion in öffentlich zugänglichen Netzerken! "Entiwcklung" ist nur für die Entwicklung gedacht!!!' + choices: + prod: Produktion + dev: Entwicklung + locale: + label: Locale + help: 'Gibt die bevorzugte Sprache ein.' + tempdir: + label: 'Temp Ordner' + site: + name: + help: 'Gib den Namen der Site an, welche in den Titeln angezeigt werden soll.' + label: 'Name der Site' + root: + help: 'Das Stammverzeichnis der Site.' + label: Stammverzeichnis + email: + label: Kontaktemail + user: + username: + label: Nutzername + email: + label: Email + password: + label: Passwort + confpasswd: + label: 'Passwort bestätigen' + db: + migrate: + label: 'Migriere Datenbank' + backend: + choices: + sqlite: SQLite3 + mysql: MySQL/MariaDB + portgesql: PostgreSQL + url: URL/DSN + label: Backend + database: + label: Datenbank + host: + label: Host + port: + label: Port + user: + label: Nutzer + password: + label: Passwort + url: + label: URL/DSN + create: + label: 'Erstelle Datenbank' + email: + backend: + choices: + none: 'Keine Emailunterstützung' + smtp: SMTP + sendmail: Sendmail + native: Nativ + dsn: 'Eigener DSN' + label: Backend + path: + label: Pfad + user: + label: Nutzer + password: + label: Passwort + host: + label: 'SMTP Host' + smtp-port: + label: 'SMTP Port' + sender: + label: Senderemail + dsn: + label: DSN + section: + email: Emailunterstützung + site: Site + database: Datenbank + user: 'Neuer Administrator' + title: 'Webroot Installation' + submit: 'Einstellungen andwenden und Setup ausführen' diff --git a/translations/messages+intl-icu.en.yaml b/translations/messages+intl-icu.en.yaml new file mode 100644 index 0000000..d2fbe14 --- /dev/null +++ b/translations/messages+intl-icu.en.yaml @@ -0,0 +1,92 @@ +'Email Settings': '__Email Settings' +'Site Settings': '__Site Settings' +'Database Settings': '__Database Settings' +'User Settings': '__User Settings' +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 +setupform: + env: + label: __setupform.env.label + help: __setupform.env.help + choices: + prod: __setupform.env.choices.prod + dev: __setupform.env.choices.dev + locale: + label: __setupform.locale.label + help: __setupform.locale.help + tempdir: + label: __setupform.tempdir.label + site: + name: + help: __setupform.site.name.help + label: __setupform.site.name.label + root: + help: __setupform.site.root.help + label: __setupform.site.root.label + email: + label: __setupform.site.email.label + user: + username: + label: __setupform.user.username.label + email: + label: __setupform.user.email.label + password: + label: __setupform.user.password.label + confpasswd: + label: __setupform.user.confpasswd.label + db: + migrate: + label: __setupform.db.migrate.label + backend: + choices: + sqlite: __setupform.db.backend.choices.sqlite + mysql: __setupform.db.backend.choices.mysql + portgesql: __setupform.db.backend.choices.portgesql + url: __setupform.db.backend.choices.url + database: + label: __setupform.db.database.label + host: + label: __setupform.db.host.label + port: + label: __setupform.db.port.label + user: + label: __setupform.db.user.label + password: + label: __setupform.db.password.label + url: + label: __setupform.db.url.label + email: + backend: + choices: + none: __setupform.email.backend.choices.none + smtp: __setupform.email.backend.choices.smtp + sendmail: __setupform.email.backend.choices.sendmail + native: __setupform.email.backend.choices.native + dsn: __setupform.email.backend.choices.dsn + path: + label: __setupform.email.path.label + user: + label: __setupform.email.user.label + password: + label: __setupform.email.password.label + host: + label: __setupform.email.host.label + smtp-port: + label: __setupform.email.smtp-port.label + sender: + label: __setupform.email.sender.label +stupform: + db: + create: + label: __stupform.db.create.label diff --git a/translations/validators.de.yaml b/translations/validators.de.yaml index 98d35da..01d7ce6 100644 --- a/translations/validators.de.yaml +++ b/translations/validators.de.yaml @@ -1,187 +1 @@ -'This value should be false.': 'Dieser Wert sollte false sein.' -'This value should be true.': 'Dieser Wert sollte true sein.' -'This value should be of type {{ type }}.': 'Dieser Wert sollte vom Typ {{ type }} sein.' -'This value should be blank.': 'Dieser Wert sollte leer sein.' -'The value you selected is not a valid choice.': 'Sie haben einen ungültigen Wert ausgewählt.' -'You must select at least {{ limit }} choice': - '|You must select at least {{ limit }} choices.': 'Sie müssen mindestens {{ limit }} Möglichkeit wählen.|Sie müssen mindestens {{ limit }} Möglichkeiten wählen.' -'You must select at most {{ limit }} choice': - '|You must select at most {{ limit }} choices.': 'Sie dürfen höchstens {{ limit }} Möglichkeit wählen.|Sie dürfen höchstens {{ limit }} Möglichkeiten wählen.' -'One or more of the given values is invalid.': 'Einer oder mehrere der angegebenen Werte sind ungültig.' -'This field was not expected.': 'Dieses Feld wurde nicht erwartet.' -'This field is missing.': 'Dieses Feld fehlt.' -'This value is not a valid date.': 'Dieser Wert entspricht keiner gültigen Datumsangabe.' -'This value is not a valid datetime.': 'Dieser Wert entspricht keiner gültigen Datums- und Zeitangabe.' -'This value is not a valid email address.': 'Dieser Wert ist keine gültige E-Mail-Adresse.' -'The file could not be found.': 'Die Datei wurde nicht gefunden.' -'The file is not readable.': 'Die Datei ist nicht lesbar.' -'The file is too large ({{ size }} {{ suffix }})': - ' Allowed maximum size is {{ limit }} {{ suffix }}.': 'Die Datei ist zu groß ({{ size }} {{ suffix }}). Die maximal zulässige Größe beträgt {{ limit }} {{ suffix }}.' -'The mime type of the file is invalid ({{ type }})': - ' Allowed mime types are {{ types }}.': 'Der Dateityp ist ungültig ({{ type }}). Erlaubte Dateitypen sind {{ types }}.' -'This value should be {{ limit }} or less.': 'Dieser Wert sollte kleiner oder gleich {{ limit }} sein.' -'This value is too long': - ' It should have {{ limit }} character or less': - '|This value is too long': - ' It should have {{ limit }} characters or less.': 'Diese Zeichenkette ist zu lang. Sie sollte höchstens {{ limit }} Zeichen haben.|Diese Zeichenkette ist zu lang. Sie sollte höchstens {{ limit }} Zeichen haben.' - ' It should contain one word': - '|This value is too long': - ' It should contain {{ max }} words or less.': 'Dieser Wert ist zu lang. Er darf maximal aus einem Wort bestehen.|Dieser Wert ist zu lang. Er darf maximal {{ max }} Wörter enthalten.' -'This value should be {{ limit }} or more.': 'Dieser Wert sollte größer oder gleich {{ limit }} sein.' -'This value is too short': - ' It should have {{ limit }} character or more': - '|This value is too short': - ' It should have {{ limit }} characters or more.': 'Diese Zeichenkette ist zu kurz. Sie sollte mindestens {{ limit }} Zeichen haben.|Diese Zeichenkette ist zu kurz. Sie sollte mindestens {{ limit }} Zeichen haben.' - ' It should contain at least one word': - '|This value is too short': - ' It should contain at least {{ min }} words.': 'Dieser Wert ist zu kurz. Er muss aus mindestens einem Wort bestehen.|Dieser Wert ist zu kurz. Er muss mindestens {{ min }} Wörter enthalten.' -'This value should not be blank.': 'Dieser Wert sollte nicht leer sein.' -'This value should not be null.': 'Dieser Wert sollte nicht null sein.' -'This value should be null.': 'Dieser Wert sollte null sein.' -'This value is not valid.': 'Dieser Wert ist nicht gültig.' -'This value is not a valid time.': 'Dieser Wert entspricht keiner gültigen Zeitangabe.' -'This value is not a valid URL.': 'Dieser Wert ist keine gültige URL.' -'The two values should be equal.': 'Die beiden Werte sollten identisch sein.' -'The file is too large': - ' Allowed maximum size is {{ limit }} {{ suffix }}.': 'Die Datei ist zu groß. Die maximal zulässige Größe beträgt {{ limit }} {{ suffix }}.' -'The file is too large.': 'Die Datei ist zu groß.' -'The file could not be uploaded.': 'Die Datei konnte nicht hochgeladen werden.' -'This value should be a valid number.': 'Dieser Wert sollte eine gültige Zahl sein.' -'This file is not a valid image.': 'Diese Datei ist kein gültiges Bild.' -'This is not a valid IP address.': 'Dieser Wert ist keine gültige IP-Adresse.' -'This value is not a valid language.': 'Dieser Wert entspricht keiner gültigen Sprache.' -'This value is not a valid locale.': 'Dieser Wert entspricht keinem gültigen Gebietsschema.' -'This value is not a valid country.': 'Dieser Wert entspricht keinem gültigen Land.' -'This value is already used.': 'Dieser Wert wird bereits verwendet.' -'The size of the image could not be detected.': 'Die Größe des Bildes konnte nicht ermittelt werden.' -'The image width is too big ({{ width }}px)': - ' Allowed maximum width is {{ max_width }}px.': 'Die Bildbreite ist zu groß ({{ width }}px). Die maximal zulässige Breite beträgt {{ max_width }}px.' -'The image width is too small ({{ width }}px)': - ' Minimum width expected is {{ min_width }}px.': 'Die Bildbreite ist zu gering ({{ width }}px). Die erwartete Mindestbreite beträgt {{ min_width }}px.' -'The image height is too big ({{ height }}px)': - ' Allowed maximum height is {{ max_height }}px.': 'Die Bildhöhe ist zu groß ({{ height }}px). Die maximal zulässige Höhe beträgt {{ max_height }}px.' -'The image height is too small ({{ height }}px)': - ' Minimum height expected is {{ min_height }}px.': 'Die Bildhöhe ist zu gering ({{ height }}px). Die erwartete Mindesthöhe beträgt {{ min_height }}px.' -"This value should be the user's current password.": 'Dieser Wert sollte dem aktuellen Benutzerpasswort entsprechen.' -'This value should have exactly {{ limit }} character': - '|This value should have exactly {{ limit }} characters.': 'Dieser Wert sollte genau {{ limit }} Zeichen lang sein.|Dieser Wert sollte genau {{ limit }} Zeichen lang sein.' -'The file was only partially uploaded.': 'Die Datei wurde nur teilweise hochgeladen.' -'No file was uploaded.': 'Es wurde keine Datei hochgeladen.' -'No temporary folder was configured in php': - ini.: 'Es wurde kein temporärer Ordner in der php.ini konfiguriert oder der temporäre Ordner existiert nicht.' -'Cannot write temporary file to disk.': 'Kann die temporäre Datei nicht speichern.' -'A PHP extension caused the upload to fail.': 'Eine PHP-Erweiterung verhinderte den Upload.' -'This collection should contain {{ limit }} element or more': - '|This collection should contain {{ limit }} elements or more.': 'Diese Sammlung sollte {{ limit }} oder mehr Elemente beinhalten.|Diese Sammlung sollte {{ limit }} oder mehr Elemente beinhalten.' -'This collection should contain {{ limit }} element or less': - '|This collection should contain {{ limit }} elements or less.': 'Diese Sammlung sollte {{ limit }} oder weniger Elemente beinhalten.|Diese Sammlung sollte {{ limit }} oder weniger Elemente beinhalten.' -'This collection should contain exactly {{ limit }} element': - '|This collection should contain exactly {{ limit }} elements.': 'Diese Sammlung sollte genau {{ limit }} Element beinhalten.|Diese Sammlung sollte genau {{ limit }} Elemente beinhalten.' -'Invalid card number.': 'Ungültige Kartennummer.' -'Unsupported card type or invalid card number.': 'Nicht unterstützter Kartentyp oder ungültige Kartennummer.' -'This is not a valid International Bank Account Number (IBAN).': 'Dieser Wert ist keine gültige Internationale Bankkontonummer (IBAN).' -'This value is not a valid ISBN-10.': 'Dieser Wert entspricht keiner gültigen ISBN-10.' -'This value is not a valid ISBN-13.': 'Dieser Wert entspricht keiner gültigen ISBN-13.' -'This value is neither a valid ISBN-10 nor a valid ISBN-13.': 'Dieser Wert ist weder eine gültige ISBN-10 noch eine gültige ISBN-13.' -'This value is not a valid ISSN.': 'Dieser Wert ist keine gültige ISSN.' -'This value is not a valid currency.': 'Dieser Wert ist keine gültige Währung.' -'This value should be equal to {{ compared_value }}.': 'Dieser Wert sollte gleich {{ compared_value }} sein.' -'This value should be greater than {{ compared_value }}.': 'Dieser Wert sollte größer als {{ compared_value }} sein.' -'This value should be greater than or equal to {{ compared_value }}.': 'Dieser Wert sollte größer oder gleich {{ compared_value }} sein.' -'This value should be identical to {{ compared_value_type }} {{ compared_value }}.': 'Dieser Wert sollte identisch sein mit {{ compared_value_type }} {{ compared_value }}.' -'This value should be less than {{ compared_value }}.': 'Dieser Wert sollte kleiner als {{ compared_value }} sein.' -'This value should be less than or equal to {{ compared_value }}.': 'Dieser Wert sollte kleiner oder gleich {{ compared_value }} sein.' -'This value should not be equal to {{ compared_value }}.': 'Dieser Wert sollte nicht {{ compared_value }} sein.' -'This value should not be identical to {{ compared_value_type }} {{ compared_value }}.': 'Dieser Wert sollte nicht identisch sein mit {{ compared_value_type }} {{ compared_value }}.' -'The image ratio is too big ({{ ratio }})': - ' Allowed maximum ratio is {{ max_ratio }}.': 'Das Seitenverhältnis des Bildes ist zu groß ({{ ratio }}). Der erlaubte Maximalwert ist {{ max_ratio }}.' -'The image ratio is too small ({{ ratio }})': - ' Minimum ratio expected is {{ min_ratio }}.': 'Das Seitenverhältnis des Bildes ist zu klein ({{ ratio }}). Der erwartete Minimalwert ist {{ min_ratio }}.' -'The image is square ({{ width }}x{{ height }}px)': - ' Square images are not allowed.': 'Das Bild ist quadratisch ({{ width }}x{{ height }}px). Quadratische Bilder sind nicht erlaubt.' -'The image is landscape oriented ({{ width }}x{{ height }}px)': - ' Landscape oriented images are not allowed.': 'Das Bild ist im Querformat ({{ width }}x{{ height }}px). Bilder im Querformat sind nicht erlaubt.' -'The image is portrait oriented ({{ width }}x{{ height }}px)': - ' Portrait oriented images are not allowed.': 'Das Bild ist im Hochformat ({{ width }}x{{ height }}px). Bilder im Hochformat sind nicht erlaubt.' -'An empty file is not allowed.': 'Eine leere Datei ist nicht erlaubt.' -'The host could not be resolved.': 'Der Hostname konnte nicht aufgelöst werden.' -'This value does not match the expected {{ charset }} charset.': 'Dieser Wert entspricht nicht dem erwarteten Zeichensatz {{ charset }}.' -'This is not a valid Business Identifier Code (BIC).': 'Dieser Wert ist keine gültige internationale Bankleitzahl (BIC).' Error: Fehler -'This is not a valid UUID.': 'Dieser Wert ist keine gültige UUID.' -'This value should be a multiple of {{ compared_value }}.': 'Dieser Wert sollte ein Vielfaches von {{ compared_value }} sein.' -'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.': 'Diese internationale Bankleitzahl (BIC) ist nicht mit der IBAN {{ iban }} assoziiert.' -'This value should be valid JSON.': 'Dieser Wert sollte gültiges JSON sein.' -'This collection should contain only unique elements.': 'Diese Sammlung darf keine doppelten Elemente enthalten.' -'This value should be positive.': 'Diese Zahl sollte positiv sein.' -'This value should be either positive or zero.': 'Diese Zahl sollte entweder positiv oder 0 sein.' -'This value should be negative.': 'Diese Zahl sollte negativ sein.' -'This value should be either negative or zero.': 'Diese Zahl sollte entweder negativ oder 0 sein.' -'This value is not a valid timezone.': 'Dieser Wert ist keine gültige Zeitzone.' -'This password has been leaked in a data breach, it must not be used': - ' Please use another password.': 'Dieses Passwort ist Teil eines Datenlecks, es darf nicht verwendet werden.' -'This value should be between {{ min }} and {{ max }}.': 'Dieser Wert sollte zwischen {{ min }} und {{ max }} sein.' -'This value is not a valid hostname.': 'Dieser Wert ist kein gültiger Hostname.' -'The number of elements in this collection should be a multiple of {{ compared_value }}.': 'Die Anzahl an Elementen in dieser Sammlung sollte ein Vielfaches von {{ compared_value }} sein.' -'This value should satisfy at least one of the following constraints:': 'Dieser Wert sollte eine der folgenden Bedingungen erfüllen:' -'Each element of this collection should satisfy its own set of constraints.': 'Jedes Element dieser Sammlung sollte seine eigene Menge an Bedingungen erfüllen.' -'This value is not a valid International Securities Identification Number (ISIN).': 'Dieser Wert ist keine gültige Internationale Wertpapierkennnummer (ISIN).' -'This value should be a valid expression.': 'Dieser Wert sollte eine gültige Expression sein.' -'This value is not a valid CSS color.': 'Dieser Wert ist keine gültige CSS-Farbe.' -'This value is not a valid CIDR notation.': 'Dieser Wert entspricht nicht der CIDR-Notation.' -'The value of the netmask should be between {{ min }} and {{ max }}.': 'Der Wert der Subnetzmaske sollte zwischen {{ min }} und {{ max }} liegen.' -'The filename is too long': - ' It should have {{ filename_max_length }} character or less': - '|The filename is too long': - ' It should have {{ filename_max_length }} characters or less.': 'Der Dateiname ist zu lang. Er sollte nicht länger als {{ filename_max_length }} Zeichen sein.|Der Dateiname ist zu lang. Er sollte nicht länger als {{ filename_max_length }} Zeichen sein.' -'The password strength is too low': - ' Please use a stronger password.': 'Das Passwort ist zu schwach.' -'This value contains characters that are not allowed by the current restriction-level.': 'Der Wert enthält Zeichen, die auf der aktuellen Einschränkungsstufe nicht erlaubt sind.' -'Using invisible characters is not allowed.': 'Unsichtbare Zeichen sind nicht erlaubt.' -'Mixing numbers from different scripts is not allowed.': 'Das Mischen von Zahlen aus verschiedenen Skripten ist nicht erlaubt.' -'Using hidden overlay characters is not allowed.': 'Verstecke Overlay-Zeichen sind nicht erlaubt.' -'The extension of the file is invalid ({{ extension }})': - ' Allowed extensions are {{ extensions }}.': 'Die Dateiendung ist ungültig ({{ extension }}). Gültige Dateiendungen sind {{ extensions }}.' -'The detected character encoding is invalid ({{ detected }})': - ' Allowed encodings are {{ encodings }}.': 'Der erkannte Zeichensatz ist nicht gültig ({{ detected }}). Gültige Zeichensätze sind {{ encodings }}.' -'This value is not a valid MAC address.': 'Dieser Wert ist keine gültige MAC-Adresse.' -'This URL is missing a top-level domain.': 'Dieser URL fehlt eine Top-Level-Domain.' -'This value does not represent a valid week in the ISO 8601 format.': 'Dieser Wert ist keine Wochenangabe im ISO 8601-Format.' -'This value is not a valid week.': 'Dieser Wert ist keine gültige Woche.' -'This value should not be before week "{{ min }}".': 'Dieser Wert darf nicht vor der Woche "{{ min }}" sein.' -'This value should not be after week "{{ max }}".': 'Dieser Wert darf nicht nach der Woche "{{ max }}" sein.' -'This form should not contain extra fields.': 'Dieses Formular sollte keine zusätzlichen Felder enthalten.' -'The uploaded file was too large': - ' Please try to upload a smaller file.': 'Die hochgeladene Datei ist zu groß. Versuchen Sie bitte eine kleinere Datei hochzuladen.' -'The CSRF token is invalid': - ' Please try to resubmit the form.': 'Der CSRF-Token ist ungültig. Versuchen Sie bitte, das Formular erneut zu senden.' -'This value is not a valid HTML5 color.': 'Dieser Wert ist keine gültige HTML5 Farbe.' -'Please enter a valid birthdate.': 'Bitte geben Sie ein gültiges Geburtsdatum ein.' -'The selected choice is invalid.': 'Die Auswahl ist ungültig.' -'The collection is invalid.': 'Diese Gruppe von Feldern ist ungültig.' -'Please select a valid color.': 'Bitte geben Sie eine gültige Farbe ein.' -'Please select a valid country.': 'Bitte wählen Sie ein gültiges Land aus.' -'Please select a valid currency.': 'Bitte wählen Sie eine gültige Währung aus.' -'Please choose a valid date interval.': 'Bitte wählen Sie ein gültiges Datumsintervall.' -'Please enter a valid date and time.': 'Bitte geben Sie ein gültiges Datum samt Uhrzeit ein.' -'Please enter a valid date.': 'Bitte geben Sie ein gültiges Datum ein.' -'Please select a valid file.': 'Bitte wählen Sie eine gültige Datei.' -'The hidden field is invalid.': 'Das versteckte Feld ist ungültig.' -'Please enter an integer.': 'Bitte geben Sie eine ganze Zahl ein.' -'Please select a valid language.': 'Bitte wählen Sie eine gültige Sprache.' -'Please select a valid locale.': 'Bitte wählen Sie eine gültige Locale-Einstellung aus.' -'Please enter a valid money amount.': 'Bitte geben Sie einen gültigen Geldbetrag ein.' -'Please enter a number.': 'Bitte geben Sie eine gültige Zahl ein.' -'The password is invalid.': 'Das Kennwort ist ungültig.' -'Please enter a percentage value.': 'Bitte geben Sie einen gültigen Prozentwert ein.' -'The values do not match.': 'Die Werte stimmen nicht überein.' -'Please enter a valid time.': 'Bitte geben Sie eine gültige Uhrzeit ein.' -'Please select a valid timezone.': 'Bitte wählen Sie eine gültige Zeitzone.' -'Please enter a valid URL.': 'Bitte geben Sie eine gültige URL ein.' -'Please enter a valid search term.': 'Bitte geben Sie einen gültigen Suchbegriff ein.' -'Please provide a valid phone number.': 'Bitte geben Sie eine gültige Telefonnummer ein.' -'The checkbox has an invalid value.': 'Das Kontrollkästchen hat einen ungültigen Wert.' -'Please enter a valid email address.': 'Bitte geben Sie eine gültige E-Mail-Adresse ein.' -'Please select a valid option.': 'Bitte wählen Sie eine gültige Option.' -'Please select a valid range.': 'Bitte wählen Sie einen gültigen Bereich.' -'Please enter a valid week.': 'Bitte geben Sie eine gültige Woche ein.'