diff --git a/assets/js/job/index.js b/assets/js/job/index.js index 617ac10..6f74974 100644 --- a/assets/js/job/index.js +++ b/assets/js/job/index.js @@ -71,7 +71,7 @@ function initRunNowButtons() { tr.classList.remove('norun'); tr.classList.remove('text-danger'); } else if (data.status == 'ran') { - let content = '

Cronjob ran in ' + data.runtime.toFixed(3) + ' seconds with exit code ' + data.exitcode +'

' + let content = '

' + data.message + '

' content += '
' + data.output + '
' modal.querySelector('.modal-body').innerHTML = content; diff --git a/composer.json b/composer.json index a837b50..8ee28fb 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "symfony/proxy-manager-bridge": "6.0.*", "symfony/runtime": "6.0.*", "symfony/security-bundle": "6.0.*", + "symfony/translation": "6.0.*", "symfony/twig-bundle": "6.0.*", "symfony/yaml": "6.0.*" }, diff --git a/composer.lock b/composer.lock index 17ecf58..c44a611 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": "7f812eb1644c07f1064bc949657fc1f5", + "content-hash": "5b6d0f7b3419f7248fa3262cfb27697e", "packages": [ { "name": "doctrine/annotations", @@ -5883,6 +5883,101 @@ ], "time": "2022-04-22T08:18:02+00:00" }, + { + "name": "symfony/translation", + "version": "v6.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "3d38cf8f8834148c4457681d539bc204de701501" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/3d38cf8f8834148c4457681d539bc204de701501", + "reference": "3d38cf8f8834148c4457681d539bc204de701501", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.0.8" + }, + "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": "2022-04-22T08:18:02+00:00" + }, { "name": "symfony/translation-contracts", "version": "v3.0.1", diff --git a/config/packages/security.yaml b/config/packages/security.yaml index c433f1e..d81a66e 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -13,6 +13,9 @@ security: dev: pattern: ^/(_(profiler|wdt)|css|images|js|health)/ security: false + login: + pattern: ^/?(%enabled_locales%)/(login|login_check|health)(?=.*) + security: false main: pattern: ^\/(.*) provider: app_user_provider diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml new file mode 100644 index 0000000..abb76aa --- /dev/null +++ b/config/packages/translation.yaml @@ -0,0 +1,13 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en +# providers: +# crowdin: +# dsn: '%env(CROWDIN_DSN)%' +# loco: +# dsn: '%env(LOCO_DSN)%' +# lokalise: +# dsn: '%env(LOKALISE_DSN)%' diff --git a/config/routes.yaml b/config/routes.yaml index eaa1161..3997167 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,7 +1,7 @@ default: path: '/' defaults: - _controller: App\Controller\JobController::defaultAction + _controller: App\Controller\SecurityController::loginAction health: path: '/health' @@ -9,7 +9,7 @@ health: _controller: App\Controller\SiteController::healthAction login: - path: '/login' + path: '/{_locale}/login' defaults: _controller: App\Controller\SecurityController::loginAction @@ -20,12 +20,12 @@ logout: path: '/logout' job_index: - path: '/job' + path: '/{_locale}/job' defaults: _controller: App\Controller\JobController::defaultAction job_view: - path: '/job/{id}/{all}' + path: '/{_locale}/job/{id}/{all}' methods: [ 'GET' ] defaults: _controller: App\Controller\JobController::jobAction @@ -35,7 +35,7 @@ job_view: all: (all|) job_delete: - path: '/job/{id}' + path: '/{_locale}/job/{id}' methods: [ 'DELETE' ] defaults: _controller: App\Controller\JobController::jobAction @@ -43,20 +43,20 @@ job_delete: id: \d+ job_edit: - path: '/job/{id}/edit' + path: '/{_locale}/job/{id}/edit' defaults: _controller: App\Controller\JobController::editAction requirements: id: \d+ job_runnow: - path: '/job/{id}/runnow' + path: '/{_locale}/job/{id}/runnow' defaults: _controller: App\Controller\JobController::runNowAction requirements: id: \d+ job_add: - path: '/job/add' + path: '/{_locale}/job/add' defaults: _controller: App\Controller\JobController::addAction diff --git a/config/services.yaml b/config/services.yaml index 2d6a76f..b794f50 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,6 +4,7 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: + enabled_locales: 'en|nl' services: # default configuration for services in *this* file diff --git a/src/Command/DaemonCommand.php b/src/Command/DaemonCommand.php index b166c02..9184b50 100644 --- a/src/Command/DaemonCommand.php +++ b/src/Command/DaemonCommand.php @@ -64,6 +64,7 @@ class DaemonCommand extends Command $consolerun = $jobRepo->getTempVar($job, 'consolerun', false); if($consolerun && !$rebootedself) continue; } + $manual = ($job->getRunning() == 2); $jobRepo->setJobRunning($job, true); $output->writeln('Running Job ' . $job->getId()); if($async) { @@ -75,10 +76,10 @@ class DaemonCommand extends Command } if(!$async || $pid == -1) { - $jobRepo->RunJob($job, $job->getRunning() == 2); + $jobRepo->RunJob($job, $manual); $jobRepo->setJobRunning($job, false); } elseif ($pid == 0) { - $jobRepo->RunJob($job, $job->getRunning() == 2); + $jobRepo->RunJob($job, $manual); $jobRepo->setJobRunning($job, false); exit; } diff --git a/src/Controller/JobController.php b/src/Controller/JobController.php index e73358e..4130d90 100644 --- a/src/Controller/JobController.php +++ b/src/Controller/JobController.php @@ -11,6 +11,7 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\Translation\TranslatorInterface; class JobController extends AbstractController { @@ -80,12 +81,33 @@ class JobController extends AbstractController } } - public function runNowAction(Request $request, ManagerRegistry $doctrine, int $id): JsonResponse + public function runNowAction(Request $request, ManagerRegistry $doctrine, TranslatorInterface $translator, int $id): JsonResponse { if($request->getMethod() == 'GET') { $jobRepo = $doctrine->getRepository(Job::class); $job = $jobRepo->find($id); - return new JsonResponse($jobRepo->runNow($job)); + $runnowResult = $jobRepo->runNow($job); + if ($runnowResult['success'] === NULL) { + $return = [ + 'status' => 'deferred', + 'success' => NULL, + 'title' => $translator->trans('job.runnow.deferred.title'), + 'message' => $translator->trans('job.runnow.deferred.message') + ]; + } else { + $return = [ + 'status' => 'ran', + 'success' => $runnowResult['success'], + 'title' => $runnowResult['success'] ? $translator->trans('job.runnow.ran.title.success') : $translator->trans('job.runnow.ran.title.failed'), + 'message' => $translator->trans('job.runnow.ran.message', [ + '_runtime_' => number_format($runnowResult['runtime'], 3), + '_exitcode_' => $runnowResult['exitcode'] + ]), + 'exitcode' => $runnowResult['exitcode'], + 'output' => $runnowResult['output'], + ]; + } + return new JsonResponse($return); } return new JsonResponse(['success'=>false, 'message' => 'Your request is invalid'], Response::HTTP_BAD_REQUEST); } diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 8d1ca9c..4f117c4 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -4,13 +4,18 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; class SecurityController extends AbstractController { - public function loginAction(AuthenticationUtils $authenticationUtils): Response + public function loginAction(Request $request, AuthenticationUtils $authenticationUtils): Response { + if($this->isGranted('IS_AUTHENTICATED_FULLY')) { + return new RedirectResponse($this->generateUrl('job_index')); + } // get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); diff --git a/src/Repository/JobRepository.php b/src/Repository/JobRepository.php index 18dc193..ada0503 100644 --- a/src/Repository/JobRepository.php +++ b/src/Repository/JobRepository.php @@ -394,11 +394,13 @@ class JobRepository extends EntityRepository 'output' => ($console) ? $output['output'] : htmlentities($output['output']), 'exitcode' => $output['exitcode'], 'runtime' => (float)$output['runtime'], - 'title' => !str_contains($output['flags'], RunRepository::FAILED) ? 'Cronjob successfully ran' : 'Cronjob failed. Please check output below', 'success' => !str_contains($output['flags'], RunRepository::FAILED) ]; } - return ['success' => true, 'status' => 'deferred', 'title' => 'Cronjob has been scheduled', 'message' => 'Job was scheduled to be run. You will find the output soon in the job details']; + return [ + 'success' => NULL, + 'status' => 'deferred' + ]; } /** diff --git a/symfony.lock b/symfony.lock index b7ad142..daf3b68 100644 --- a/symfony.lock +++ b/symfony.lock @@ -361,6 +361,19 @@ "symfony/string": { "version": "v6.0.3" }, + "symfony/translation": { + "version": "6.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.3", + "ref": "da64f5a2b6d96f5dc24914517c0350a5f91dee43" + }, + "files": [ + "config/packages/translation.yaml", + "translations/.gitignore" + ] + }, "symfony/translation-contracts": { "version": "v3.0.1" }, diff --git a/templates/base.html.twig b/templates/base.html.twig index 3bc8ba4..9cc2a35 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -23,9 +23,9 @@
diff --git a/templates/job/index.html.twig b/templates/job/index.html.twig index 362fd9a..fd751c8 100644 --- a/templates/job/index.html.twig +++ b/templates/job/index.html.twig @@ -1,16 +1,16 @@ {% extends "base.html.twig" %} -{% block title %}Overview{% endblock %} +{% block title %}{{ 'index.title' | trans }}{% endblock %} {% block content %} -

Overview of your cronjobs

+

{{ 'index.header' | trans }}

- - - - + + + + diff --git a/templates/job/view.html.twig b/templates/job/view.html.twig index d6fc699..c714927 100644 --- a/templates/job/view.html.twig +++ b/templates/job/view.html.twig @@ -1,11 +1,11 @@ {% extends "base.html.twig" %} -{% block title %}Overview of run for {{ job.name }}{% endblock %} +{% block title %}{{ 'job.title' | trans({ '_jobname_': job.name }) }}{% endblock %} {% block content %} -

Overview of runs for {{ job.name | parsetags | raw }}

+

{{ 'job.header' | trans({ '_jobname_': (job.name | parsetags) }) | raw }}

- Edit job - {% if allruns %} | Only show failed runs - {% elseif not allruns %} | Show all runs + {{ 'job.edit' | trans }} + {% if allruns %} | {{ 'job.show.onlyfailed' | trans }} + {% elseif not allruns %} | {{ 'job.show.all' | trans }} {% endif %}

@@ -15,9 +15,9 @@ @@ -29,8 +29,8 @@
{% else %} -

No {% if not allruns %}failed {% endif %}runs found

-

Show all runs

+

{% if not allruns %}{{ "job.results.noresults.failed" | trans }}{% else %}{{ "job.results.noresults.all"| trans }}{% endif %}

+

{{ 'job.show.all' | trans }}

{% endfor %} {% endblock %} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index f27d688..35c5174 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -25,19 +25,19 @@
- - + +
- - + +
- +
- + diff --git a/translations/.gitignore b/translations/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml new file mode 100644 index 0000000..fd97fba --- /dev/null +++ b/translations/messages.en.yaml @@ -0,0 +1,48 @@ +menu: + overview: "Overview" + add: "Add a new cronjob" + logout: "Logout" +security: + login: + username: + label: "Username" + placeholder: "jeroen@example.com" + password: + label: "Password" + placeholder: "abc123" + remember: + label: "Remember, remember" + submit-btn: + label: "Login" +index: + title: "Overview" + header: "Overview of the cronjobs" + table: + headers: + name: "Hame" + host: "Host" + interval: "Interval" + nextrun: "Nextrun" +job: + title: "Overview of runs for _jobname_" + header: "Overview of runs for _jobname_" + edit: "Edit job" + runnow: + deferred: + title: "Cronjob has been scheduled" + message: "Job was scheduled to be run. You will find the output soon in the job details" + ran: + title: + success: "Cronjob succesfully ran" + failed: "Cronjob failed. Please check output below" + message: "Cronjob ran in _runtime_ seconds with exit code _exitcode_" + show: + onlyfailed: "Only show failed runs" + all: "Show all runs" + results: + exitcode: "Exit code" + runtime: "Runtime" + manual: "Manual run" + noresults: + failed: "No failed runs found" + all: "No runs found" \ No newline at end of file diff --git a/translations/messages.nl.yaml b/translations/messages.nl.yaml new file mode 100644 index 0000000..0490c6d --- /dev/null +++ b/translations/messages.nl.yaml @@ -0,0 +1,48 @@ +menu: + overview: "Overzicht" + add: "Taak toevoegen" + logout: "Afmelden" +security: + login: + username: + label: "Gebruikersnaam" + placeholder: "jeroen@example.com" + password: + label: "Wachtwoord" + placeholder: "abc123" + remember: + label: "Onthoud mij!" + submit-btn: + label: "Aanmelden" +index: + title: "Overzicht" + header: "Overzicht van de geplande taken" + table: + headers: + name: "Naam" + host: "Host" + interval: "Interval" + nextrun: "Volgende uitvoering" +job: + title: "Overzicht van uitvoeringen van _jobname_" + header: "Overzicht van uitvoeringen van _jobname_" + edit: "Bewerk taak" + runnow: + deferred: + title: "Taak werd doorgestuurd naar de daemon" + message: "Taak werd doorgestuurd naar de daemon. De output vind u binnenkort in de taakdetails" + ran: + title: + success: "Taak werd succesvol uitgevoerd" + failed: "Taak faalde. Hieronder vind u de output ter controle" + message: "Taak werd in _runtime_ seconden uitgevoerd met resultaat _exitcode_" + show: + onlyfailed: "Toon alleen gefaalde uitvoeringen" + all: "Toon alle uitvoeringen" + results: + exitcode: "Resultaat" + runtime: "Duurtijd" + manual: "Manuele uitvoering" + noresults: + failed: "Geen gefaalde uitvoeringen gevonden" + all: "Geen uitvoeringen gevonden" \ No newline at end of file
 NameHostDelayNext run{{ 'index.table.headers.name' | trans }}{{ 'index.table.headers.host' | trans }}{{ 'index.table.headers.interval' | trans }}{{ 'index.table.headers.nextrun' | trans }}