Compare commits

...

38 Commits

Author SHA1 Message Date
Jeroen De Meerleer f25a81daf1
Updated dependencies 2024-01-11 11:42:12 +01:00
Jeroen De Meerleer d1951ee3ed
Updated dependencies 2023-12-07 14:50:37 +01:00
Jeroen De Meerleer 97a0c316cc
Removed deprecated bootstrap-dark-5 2023-12-07 14:44:24 +01:00
Jeroen De Meerleer 9264dce266
Update composer.json and composer.lock
- Updated doctrine/orm from version ^2.15 to ^2.16
- Updated guzzlehttp/guzzle from version ^7.7 to ^7.8
- Updated nelmio/security-bundle from version ^3.0 to ^v3.0
- Updated symfony/flex from version ^2.3 to ^v2.4
- Updated symfony/webpack-encore-bundle from version "^v2.0" to "^v2.1"
- Updated laminas/laminas-code from version 4.12.0 to 4.13.0
- Updated doctrine/annotations from version "^2.0" to "^2.0.1"
- Updated laminas/laminas-coding-standard from version "^2.
2023-11-12 18:59:21 +01:00
Jeroen De Meerleer 9265c02ffb
Updated dependencies 2023-10-17 09:14:57 +02:00
Jeroen De Meerleer e7fc6f707b
Update timepicker restrictions and add a minimum date
- Update timepicker options to display inline and side by side
- Remove the previous restriction on minimum date
- Add a new restriction for maximum date
- Add a new restriction for minimum date, set to current date
2023-09-11 08:17:54 +02:00
Jeroen De Meerleer f42530ea46
Updated dependencies 2023-09-07 17:07:11 +02:00
Jeroen De Meerleer 90d0cfd1c8
Refactor run button initialization and timepicker usage
- Removed the `initTimepicker()` function call from `document.addEventListener` block
- Added a new line to initialize `selecttimedatepicker` if it is undefined in the event listener for `.run` buttons
- Moved the modal show code before initializing `selecttimedatepicker`
- Removed duplicate modal show code from `initRunButtons()` function
2023-09-03 12:53:44 +02:00
Jeroen De Meerleer 7ac00a622e
Update composer.lock with new versions of packages
- Update composer/ca-bundle from version 1.3.6 to 1.3.7
- Update doctrine/collections from version 2.1.2 to 2.1.3
- Update vimeo/psalm from version ^4.22 to ^5.11
- Update doctrine/dbal from version 3.6.4 to 3.6.6
- Update phpunit/phpunit from version 9.6.7 to 9.6.9
- Update jetbrains/phpstorm-stubs from version 2022.3 to 2023.
- Update slevomat/coding-standard from version 8..13..1 to
   - Version: "8..13..1"
   - Version: "8..13..1"
   - Version: "8..13..1"
   - Version: "8..13..1"
   - Version: "8..13..1"
   - Version: "8...
2023-08-31 15:02:36 +02:00
Jeroen De Meerleer 8caed4d025
Updated dependencies 2023-07-16 09:55:15 +02:00
Jeroen De Meerleer 449af1be8e
feat: Add nelmio/security-bundle
This commit adds the nelmio/security-bundle to the composer.json file. The bundle provides extra security-related features for Symfony, such as signed/encrypted cookies, HTTPS/SSL/HSTS handling, and cookie session storage.
2023-07-13 14:11:46 +02:00
Jeroen De Meerleer 06c6f0a659
Add login route to UserController
- Added a new route '/{_locale}/login' to the UserController class.
- This route allows users to access the login page.
- The loginAction method now handles requests for this new route.
2023-07-11 17:06:48 +02:00
Jeroen De Meerleer 7b899a01ef
Fix exit condition in DaemonCommand.php
The code change fixes the exit condition in the DaemonCommand.php file. Previously, the code would exit if `$pid` was equal to 0, but now it will only exit if `$pid` is set and equal to 0. This ensures that the correct condition is checked before exiting.
2023-07-11 17:02:45 +02:00
Jeroen De Meerleer 0d0b3b2e94
Update job edit template to increment key value for variables
The code changes in this commit update the job edit template. Specifically, it adds a line of code to increment the key value for variables. This change allows for proper indexing and handling of variables in the template.
2023-07-11 16:37:49 +02:00
Jeroen De Meerleer 08d35ad70a
Updated dependencies 2023-07-09 00:51:23 +02:00
Jeroen De Meerleer 9977a93874
Refactor JobRepository to decrypt secret values in commands
This commit modifies the JobRepository class to decrypt secret values in commands. It replaces placeholders with decrypted values using the Secret::decrypt() function. This change ensures that sensitive information is not exposed in plain text within the commands.
2023-07-09 00:44:21 +02:00
Jeroen De Meerleer f7a2228f26
Refactor JobRepository's addToken method for generating hook tokens
This commit refactors the addToken method in JobRepository to generate a random string of 32 characters using alphanumeric characters. The new implementation replaces the previous code that generated the token.
2023-06-27 14:43:26 +02:00
Jeroen De Meerleer 2b1a7939e3
Add hooktoken generation if it's missing
This commit adds a new feature to generate a random string of 32 characters as the hooktoken for jobs that don't have one. This is done in the JobRepository class.
2023-06-27 14:41:21 +02:00
Jeroen De Meerleer ec1b3313a2
Update framework to version 6.3 2023-06-27 14:40:24 +02:00
Jeroen De Meerleer de9b9380c2
Updated dependencies 2023-05-22 11:46:49 +02:00
Jeroen De Meerleer f361a71f73
BUGFIX: scheduling multiple jobs after each other with the same time did not schedule correct 2023-05-22 11:46:40 +02:00
Jeroen De Meerleer fed13e6ffb
UPDATED DEPENDENCIES 2023-04-25 15:15:16 +02:00
Jeroen De Meerleer 0aa982968f
Updated dependencies 2023-04-12 11:26:23 +02:00
Jeroen De Meerleer c7aaf102f4
BUGFIX: don't call functions twice 2023-03-15 12:36:21 +01:00
Jeroen De Meerleer c354f093c1
BUGFIX: updated dependencies 2023-03-15 12:23:13 +01:00
Jeroen De Meerleer e874a94fec
BUGFIX: only triggering event listener once 2023-02-21 14:41:41 +01:00
Jeroen De Meerleer cf9d2373c2
Updated dependencies 2023-02-07 10:29:24 +01:00
Jeroen De Meerleer 8e737d80cb
ENHANCEMENT: added visualisation of triggered run 2023-01-17 14:10:17 +01:00
Jeroen De Meerleer 2a7c2a5ca3
NEW FEATURE: added webhooks 2023-01-16 12:45:27 +01:00
Jeroen De Meerleer 12205ad18e
BUGFIX: reboot jobs stopped after triggering initial command 2023-01-11 13:46:19 +01:00
Jeroen De Meerleer c59a84a34f
BUGFIX: deletetempvar without name argument did not remove tempvars 2023-01-11 11:53:51 +01:00
Jeroen De Meerleer ff7044c567
BUGFIX: styling of timepicker to schedule was incorrect 2023-01-11 09:26:39 +01:00
Jeroen De Meerleer a6ce9afa18
Updated dependencies 2023-01-11 09:14:15 +01:00
Jeroen De Meerleer 45ba3c6ce2
BUGFIX: Scheduled run always started 1 second too late 2023-01-11 09:11:23 +01:00
Jeroen De Meerleer 21f65e2480
Added timed scheduled runs 2023-01-10 17:21:49 +01:00
Jeroen De Meerleer f77d487ab2
UPDATED DEPENDENCIES 2023-01-04 11:24:14 +01:00
Jeroen De Meerleer b933b2bad0
BUGFIX: Memory Limit could be too low when running daemon script 2022-12-19 11:01:21 +01:00
Jeroen De Meerleer 83f995c177
UPDATED CHANGELOG 2022-12-12 15:01:34 +01:00
51 changed files with 5057 additions and 9960 deletions

View File

@ -1,4 +1,7 @@
# Changelog
## Version 1.2
### New
* Added timed scheduled runs
## Version 1.1
@ -13,6 +16,7 @@
* User command now has update action
* Mail-failed-runs now takes an argument with recipients. Eliminating the need for a user account
* Docker images are build for amd64, arm and arm64
* Symfony framework has been updated to version 6.2
### Fixed
* Some flashes were not translated

View File

@ -9,4 +9,28 @@ Utils.initTags = () => {
})
}
Utils.timepickerOptions = {
localization:{
locale: 'nl',
format: 'dd/MM/yyyy HH:mm:ss'
},
display: {
icons: {
time: 'icon-clock-o',
date: 'icon-calendar',
up: 'icon-arrow-up',
down: 'icon-arrow-down',
previous: 'icon-chevron-left',
next: 'icon-chevron-right',
today: 'icon-calendar-check-o',
clear: 'icon-delete',
close: 'icon-x',
},
components: {
seconds: true,
useTwentyfourHour: true
}
},
}
export default Utils;

View File

@ -1,7 +1,7 @@
import 'bootstrap';
import moment from 'moment';
import * as tempusDominus from '@eonasdan/tempus-dominus/dist/js/tempus-dominus';
import customDateFormat from '@eonasdan/tempus-dominus/dist/plugins/customDateFormat'
import '../main'
import {TempusDominus,extend} from "@eonasdan/tempus-dominus";
import customDateFormat from '@eonasdan/tempus-dominus/dist/plugins/customDateFormat';
import Utils from "./Utils";
document.addEventListener("readystatechange", event => {
@ -18,34 +18,11 @@ document.addEventListener("readystatechange", event => {
}
});
const timepickerOptions = {
localization:{
locale: 'nl',
format: 'dd/MM/yyyy HH:mm:ss'
},
display: {
icons: {
time: 'icon-clock-o',
date: 'icon-calendar',
up: 'icon-arrow-up',
down: 'icon-arrow-down',
previous: 'icon-chevron-left',
next: 'icon-chevron-right',
today: 'icon-calendar-check-o',
clear: 'icon-delete',
close: 'icon-x',
},
components: {
seconds: true,
useTwentyfourHour: true
}
},
}
function initDatePickers()
{
tempusDominus.extend(customDateFormat);
new tempusDominus.TempusDominus(document.querySelector('#nextrunselector'), timepickerOptions);
new tempusDominus.TempusDominus(document.querySelector('#lastrunselector'), timepickerOptions);
extend(customDateFormat);
new TempusDominus(document.querySelector('#nextrunselector'), Utils.timepickerOptions);
new TempusDominus(document.querySelector('#lastrunselector'), Utils.timepickerOptions);
}
function initCronType()

View File

@ -1,12 +1,16 @@
import {Modal} from 'bootstrap';
import '../main'
import image from '/assets/images/ajax-loader.gif'
import '/assets/scss/job/index.scss';
import Utils from "./Utils";
import customDateFormat from '@eonasdan/tempus-dominus/dist/plugins/customDateFormat';
import {DateTime,TempusDominus,extend} from "@eonasdan/tempus-dominus";
document.addEventListener("readystatechange", event => {
if(event.target.readyState === 'complete') {
initDeleteButtons();
initRunNowButtons();
initRunButtons();
initTimepicker();
Utils.initTags();
}
});
@ -27,55 +31,109 @@ function initDeleteButtons() {
}));
}
function initRunNowButtons() {
document.querySelectorAll('.runnow').forEach(elem => elem.addEventListener("click", event => {
var selecttimedatepicker;
var datepickeroptions
function initTimepicker() {
extend(customDateFormat);
let modal = document.querySelector('#run_selecttime');
datepickeroptions = Utils.timepickerOptions;
datepickeroptions.display.inline = true;
datepickeroptions.display.sideBySide = true;
selecttimedatepicker = new TempusDominus(document.querySelector('#selecttime_datepicker'), datepickeroptions);
}
function initRunButtons() {
document.querySelectorAll('.run').forEach(elem => elem.addEventListener("click", event => {
let me = event.currentTarget;
let href = me.dataset.href;
let runnowCnt = document.querySelector('.runnow-content');
if(runnowCnt.querySelector('img') === null) {
let loaderImg = document.createElement('img');
loaderImg.src = image;
runnowCnt.appendChild(loaderImg);
let norun = me.closest('tr').classList.contains('norun')
let maxdate = new DateTime(me.dataset.nextrun)
if (maxdate < new DateTime() ) {
if (norun) {
maxdate = undefined;
} else {
console.error('You cannot have to be run jobs in the past');
return;
}
}
document.querySelector('.container-fluid').classList.add('blur');
document.querySelector('.runnow-overlay').classList.add('d-block');
document.querySelector('.runnow-overlay').classList.remove('d-none');
fetch(href, { method: 'GET' })
.then(response => response.json())
.then(data => {
let modal = document.querySelector('#runnow_result');
modal.querySelector('.modal-title').innerHTML = data.title;
if (data.status == 'deferred') {
modal.querySelector('.modal-body').innerHTML = data.message;
me.classList.add('disabled');
let td = me.closest('td');
td.querySelectorAll('.btn').forEach(btn => {
btn.classList.add('btn-outline-success');
btn.classList.remove('btn-outline-primary');
btn.classList.remove('btn-outline-danger');
})
let tr = me.closest('tr');
tr.classList.add('running');
tr.classList.add('text-success');
tr.classList.remove('norun');
tr.classList.remove('text-danger');
} else if (data.status == 'ran') {
let content = '<p>' + data.message + '</p>'
content += '<pre>' + data.output + '</pre>'
modal.querySelector('.modal-body').innerHTML = content;
}
var bsModal = new Modal('#runnow_result').show();
document.querySelector('.container-fluid').classList.remove('blur');
document.querySelector('.runnow-overlay').classList.remove('d-block');
document.querySelector('.runnow-overlay').classList.add('d-none');
})
if(selecttimedatepicker.dates.lastPicked > maxdate) {
selecttimedatepicker.dispose();
selecttimedatepicker = new TempusDominus(document.querySelector('#selecttime_datepicker'), datepickeroptions);
}
selecttimedatepicker.updateOptions({
restrictions: {
maxDate: maxdate,
minDate: new Date()
}
})
var bsModal = new Modal('#run_selecttime');
bsModal.show();
let schedulefn = event => {
bsModal.hide();
let time = Math.floor(selecttimedatepicker.dates.lastPicked / 1000);
run(me, time);
}
let runnowfn = event => {
bsModal.hide();
run(me);
}
let closebtnfn = event => {
bsModal.hide();
document.querySelectorAll('.schedule').forEach(elem => elem.removeEventListener("click", schedulefn));
document.querySelectorAll('.run-now').forEach(elem => elem.removeEventListener("click",runnowfn));
}
document.querySelectorAll('.schedule').forEach(elem => elem.addEventListener("click", schedulefn, { once: true } ));
document.querySelectorAll('.run-now').forEach(elem => elem.addEventListener("click", runnowfn, { once: true } ));
document.querySelectorAll('.btn-close').forEach(elem => elem.addEventListener("click", closebtnfn ));
} ));
}
function run(elem, time = 0) {
let href = elem.dataset.href;
if (time > 0) href = href + '/' + time.toString();
let runCnt = document.querySelector('.run-content');
if(runCnt.querySelector('img') === null) {
let loaderImg = document.createElement('img');
loaderImg.src = image;
runCnt.appendChild(loaderImg);
}
document.querySelector('.container-fluid').classList.add('blur');
document.querySelector('.run-overlay').classList.add('d-block');
document.querySelector('.run-overlay').classList.remove('d-none');
fetch(href, { method: 'GET' })
.then(response => response.json())
.then(data => {
let modal = document.querySelector('#run_result');
modal.querySelector('.modal-title').innerHTML = data.title;
if (data.status == 'deferred') {
modal.querySelector('.modal-body').innerHTML = data.message;
elem.classList.add('disabled');
let td = elem.closest('td');
td.querySelectorAll('.btn').forEach(btn => {
btn.classList.add('btn-outline-success');
btn.classList.remove('btn-outline-primary');
btn.classList.remove('btn-outline-danger');
})
let tr = elem.closest('tr');
tr.classList.add('running');
tr.classList.add('text-success');
tr.classList.remove('norun');
tr.classList.remove('text-danger');
} else if (data.status == 'ran') {
let content = '<p>' + data.message + '</p>'
content += '<pre>' + data.output + '</pre>'
modal.querySelector('.modal-body').innerHTML = content;
}
var runModal = new Modal('#run_result');
runModal.show();
document.querySelector('.container-fluid').classList.remove('blur');
document.querySelector('.run-overlay').classList.remove('d-block');
document.querySelector('.run-overlay').classList.add('d-none');
})
)
}

View File

@ -1,4 +1,5 @@
import 'bootstrap';
import '../main'
import '/assets/scss/job/view.scss';
import Utils from "./Utils";

9
assets/js/main.js Normal file
View File

@ -0,0 +1,9 @@
const colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
const setColorScheme = e => {
let newColorScheme = e.matches ? "dark" : "light";
document.querySelector('body').dataset.bsTheme = newColorScheme;
}
setColorScheme(colorSchemeQueryList);
colorSchemeQueryList.addEventListener('change', setColorScheme);

View File

@ -1,2 +1,3 @@
import 'bootstrap';
import '../main'
import '/assets/scss/security/login.scss';

View File

@ -1,2 +1,3 @@
import 'bootstrap';
import './main'
import '/assets/scss/settings.scss';

View File

@ -1,2 +1 @@
@import "/node_modules/bootstrap/dist/css/bootstrap.css";
@import "/node_modules/bootstrap-dark-5/dist/css/bootstrap-dark.css";

View File

@ -1,5 +1,7 @@
@import "/assets/scss/base";
@import "/assets/scss/icons";
@import "assets/scss/base";
@import "assets/scss/icons";
@import "/node_modules/@eonasdan/tempus-dominus/dist/css/tempus-dominus.css";
@import "assets/scss/tempus-dominus-dark";
tr.norun td {
background-color: #f8d7da;
@ -33,8 +35,8 @@ td.status-col {
filter: blur(3px);
}
.runnow-overlay {
.runnow-blur {
.run-overlay {
.run-blur {
bottom: 0;
left: 0;
position: fixed;
@ -43,7 +45,7 @@ td.status-col {
z-index: 1500;
}
.runnow-content {
.run-content {
font-size: 10px;
height: 50px;
position: absolute;
@ -54,4 +56,25 @@ td.status-col {
margin-left: -50px;
margin-top: -50px;
}
}
#run_selecttime {
.tempus-dominus-widget {
box-shadow: none;
&.timepicker-sbs {
width: 19em;
.td-row {
flex-direction: column;
.td-half {
width: 19em;
}
}
}
&.dark {
background-color: #2f2f2f;
}
}
}

View File

@ -4,32 +4,33 @@
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.1",
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"ext-intl": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-posix": "*",
"doctrine/doctrine-bundle": "^2.7",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.13",
"guzzlehttp/guzzle": "^7.5",
"doctrine/doctrine-bundle": "^2.11",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^2.17",
"guzzlehttp/guzzle": "^7.8",
"nelmio/security-bundle": "^v3.1",
"phpseclib/phpseclib": "^3.0",
"scienta/doctrine-json-functions": "^5.2",
"symfony/console": "^6.2",
"symfony/crowdin-translation-provider": "^6.2",
"symfony/dotenv": "^6.2",
"symfony/flex": "^2.2",
"symfony/framework-bundle": "^6.2",
"symfony/mailer": "^6.2",
"symfony/proxy-manager-bridge": "^6.2",
"symfony/runtime": "^6.2",
"symfony/security-bundle": "^6.2",
"symfony/translation": "^6.2",
"symfony/twig-bundle": "^6.2",
"symfony/webpack-encore-bundle": "^v1.16",
"symfony/yaml": "^6.2"
"scienta/doctrine-json-functions": "^5.3",
"symfony/console": "^6.4",
"symfony/crowdin-translation-provider": "^6.4",
"symfony/dotenv": "^6.4",
"symfony/flex": "^2.4",
"symfony/framework-bundle": "^6.4",
"symfony/mailer": "^6.4",
"symfony/proxy-manager-bridge": "^6.4",
"symfony/runtime": "^6.4",
"symfony/security-bundle": "^6.4",
"symfony/translation": "^6.4",
"symfony/twig-bundle": "^6.4",
"symfony/webpack-encore-bundle": "^2.1",
"symfony/yaml": "^6.4"
},
"config": {
"allow-plugins": {
@ -79,14 +80,14 @@
"extra": {
"symfony": {
"allow-contrib": true,
"require": "^6.1"
"require": "^6.4"
}
},
"require-dev": {
"symfony/debug-bundle": "^6.2",
"symfony/maker-bundle": "^v1.48.0",
"symfony/monolog-bundle": "^3.8",
"symfony/stopwatch": "^6.2",
"symfony/web-profiler-bundle": "^6.2"
"symfony/debug-bundle": "^6.4",
"symfony/maker-bundle": "^1.52",
"symfony/monolog-bundle": "^3.10",
"symfony/stopwatch": "^6.4",
"symfony/web-profiler-bundle": "^6.4"
}
}

2844
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,4 +11,5 @@ return [
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true],
];

View File

@ -4,9 +4,12 @@ doctrine:
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '13'
#server_version: '15'
orm:
auto_generate_proxy_classes: true
enable_lazy_ghost_objects: true
report_fields_where_declared: true
validate_xml_mapping: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
dql:
@ -29,6 +32,7 @@ when@prod:
doctrine:
orm:
auto_generate_proxy_classes: false
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool

View File

@ -3,4 +3,4 @@ doctrine_migrations:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: '%kernel.debug%'
enable_profiler: false

View File

@ -3,6 +3,7 @@ framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
http_method_override: false
handle_all_throwables: true
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.

View File

@ -0,0 +1,66 @@
nelmio_security:
# prevents framing of the entire site
clickjacking:
paths:
'^/.*': DENY
# disables content type sniffing for script resources
content_type:
nosniff: true
# forces Microsoft's XSS-Protection with
# its block mode
xss_protection:
enabled: true
mode_block: true
# Send a full URL in the `Referer` header when performing a same-origin request,
# only send the origin of the document to secure destination (HTTPS->HTTPS),
# and send no header to a less secure destination (HTTPS->HTTP).
# If `strict-origin-when-cross-origin` is not supported, use `no-referrer` policy,
# no referrer information is sent along with requests.
referrer_policy:
enabled: true
policies:
- 'no-referrer'
- 'strict-origin-when-cross-origin'
csp:
enabled: true
report_logger_service: logger
hosts: []
content_types: []
enforce:
# see full description below
level1_fallback: true
# only send directives supported by the browser, defaults to false
# this is a port of https://github.com/twitter/secureheaders/blob/83a564a235c8be1a8a3901373dbc769da32f6ed7/lib/secure_headers/headers/policy_management.rb#L97
browser_adaptive:
enabled: false
report-uri: '%router.request_context.base_url%/nelmio/csp/report'
default-src:
- 'none'
script-src:
- 'self'
font-src:
- 'self'
style-src:
- 'self'
img-src:
- 'self'
- 'data:'
connect-src:
- 'self'
base-uri:
- 'none'
block-all-mixed-content: true # defaults to false, blocks HTTP content over HTTPS transport
# upgrade-insecure-requests: true # defaults to false, upgrades HTTP requests to HTTPS transport
report:
# see full description below
level1_fallback: true
# only send directives supported by the browser, defaults to false
# this is a port of https://github.com/twitter/secureheaders/blob/83a564a235c8be1a8a3901373dbc769da32f6ed7/lib/secure_headers/headers/policy_management.rb#L97
browser_adaptive:
enabled: true
report-uri: '%router.request_context.base_url%/nelmio/csp/report'
script-src:
- 'self'

View File

@ -1,5 +1,4 @@
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

View File

@ -8,7 +8,7 @@ framework:
crowdin:
dsn: '%env(CROWDIN_DSN)%'
domains: ['messages']
locales: ['en', 'nl', 'leet', 'lol']
locales: ['en', 'nl', 'leet']
# loco:
# dsn: '%env(LOCO_DSN)%'
# lokalise:

View File

@ -4,7 +4,9 @@ when@dev:
intercept_redirects: false
framework:
profiler: { only_exceptions: false }
profiler:
only_exceptions: false
collect_serializer_data: true
when@test:
web_profiler:

View File

@ -25,14 +25,10 @@ webpack_encore:
# If you have multiple builds:
# builds:
# pass "frontend" as the 3rg arg to the Twig functions
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
# frontend: '%kernel.project_dir%/public/frontend/build'
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
# Put in config/packages/prod/webpack_encore.yaml
# cache: true
# pass the build name as the 3rd argument to the Twig functions
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
framework:
assets:

View File

@ -1,72 +1,5 @@
default:
path: '/'
controller: App\Controller\UserController::loginAction
login_check:
path: '/login_check'
logout:
path: '/logout'
health:
path: '/health'
controller: App\Controller\SiteController::healthAction
favicon:
path: '/favicon.ico'
controller: App\Controller\SiteController::faviconAction
settings:
path: '/{_locale}/settings'
methods: [ 'GET' ]
controller: App\Controller\UserController::settingsAction
settings_save:
path: '/{_locale}/settings'
methods: [ 'POST' ]
controller: App\Controller\UserController::settingsSaveAction
default_locale:
path: '/{_locale}'
controller: App\Controller\UserController::loginAction
login:
path: '/{_locale}/login'
controller: App\Controller\UserController::loginAction
job_index:
path: '/{_locale}/job'
controller: App\Controller\JobController::defaultAction
job_view:
path: '/{_locale}/job/{id}/{all}'
methods: [ 'GET' ]
controller: App\Controller\JobController::jobAction
defaults:
all: false
requirements:
id: \d+
all: (all|)
job_delete:
path: '/{_locale}/job/{id}'
methods: [ 'DELETE' ]
controller: App\Controller\JobController::jobAction
requirements:
id: \d+
job_edit:
path: '/{_locale}/job/{id}/edit'
controller: App\Controller\JobController::editAction
requirements:
id: \d+
job_runnow:
path: '/{_locale}/job/{id}/runnow'
controller: App\Controller\JobController::runNowAction
requirements:
id: \d+
job_add:
path: '/{_locale}/job/add'
controller: App\Controller\JobController::addAction
controllers:
resource:
path: ../src/Controller/
namespace: App\Controller
type: attribute

View File

@ -8,9 +8,6 @@ parameters:
en: 'English'
nl: 'Nederlands'
leet: 'L33tsp34k'
security:
csp_policy: "default-src 'none'; font-src 'self' data:; style-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self' data:; form-action 'self'; require-trusted-types-for 'script'; frame-ancestors 'none'; base-uri 'none'"
referer_policy: "same-origin"
services:
# default configuration for services in *this* file

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use App\Entity\Job;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version1003 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$allJobs = $this->connection->executeQuery('SELECT * FROM job')->fetchAllAssociative();
foreach($allJobs as $job) {
$data = json_decode($job['data'], true);
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
$length = 32;
for ($i = 0; $i < $length; $i++) {
$index = rand(0, strlen($characters) - 1);
$randomString .= $characters[$index];
}
$data['hooktoken'] = $randomString;
$this->addSql('UPDATE job SET data = "' . addSlashes(json_encode($data)) . '" WHERE id = ' . $job['id']);
}
}
public function down(Schema $schema): void
{
$allJobs = $this->connection->executeQuery('SELECT * FROM job')->fetchAllAssociative();
foreach($allJobs as $job) {
$data = json_decode($job['data'], true);
unset($data['hooktoken']);
$this->addSql('UPDATE job SET data = "' . addSlashes(json_encode($data)) . '" WHERE id = ' . $job['id']);
}
}
}

10157
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,21 +6,27 @@
"license": "AGPL-3.0-or-later",
"private": true,
"dependencies": {
"@eonasdan/tempus-dominus": "^6.2",
"@eonasdan/tempus-dominus": "^6.9",
"@popperjs/core": "^2.11",
"bootstrap": "^5.2",
"bootstrap-dark-5": "^1.1",
"moment": "^2.29"
"bootstrap": "^5.3",
"moment": "^2.30"
},
"devDependencies": {
"sass": "^1.56",
"sass-loader": "^13.2",
"@symfony/webpack-encore": "^4.1",
"core-js": "^3.26"
"@babel/core": "^7.23",
"@babel/preset-env": "^7.23",
"@symfony/webpack-encore": "^4.5",
"core-js": "^3.35",
"sass": "^1.69",
"sass-loader": "^13.3",
"regenerator-runtime": "^0.14",
"webpack": "^5.89",
"webpack-cli": "^5.1",
"webpack-notifier": "^1.15"
},
"scripts": {
"watch" : "encore dev --watch",
"build-dev": "encore dev",
"build": "encore prod"
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress"
}
}

View File

@ -37,6 +37,7 @@ class DaemonCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output) : int
{
ini_set('memory_limit', '4G');
$jobRepo = $this->doctrine->getRepository(Job::class);
$timelimit = $input->getOption('time-limit') ?? false;
$async = $input->getOption('async') ?? function_exists('pcntl_fork');
@ -55,14 +56,19 @@ class DaemonCommand extends Command
$jobsToRun = $jobRepo->getJobsDue();
if(!empty($jobsToRun)) {
foreach($jobsToRun as $key=>$job) {
if($job->getData('crontype') == 'reboot') {
$str = @file_get_contents('/proc/uptime');
$num = floatval($str);
if ($job->getData('crontype') == 'reboot') {
$str = @file_get_contents('/proc/uptime');
$num = floatval($str);
$rebootedself = ($num < $job->getData('reboot-duration') * 60);
$consolerun = $jobRepo->getTempVar($job, 'consolerun', false);
if($consolerun && !$rebootedself) continue;
if ($consolerun && !$rebootedself) continue;
}
$manual = ($job->getRunning() == 2);
$manual = '';
if($jobRepo->getTempVar($job, 'webhook', false)) {
$manual = 'Webhook';
} elseif($job->getRunning() > 1) {
$manual = 'Manual';
};
$jobRepo->setJobRunning($job, true);
$output->writeln('Running Job ' . $job->getId());
if($async) {
@ -73,13 +79,10 @@ class DaemonCommand extends Command
$jobRepo = $this->doctrine->getRepository(Job::class);
}
if(!$async || $pid == -1) {
$jobRepo->RunJob($job, $manual);
$jobRepo->setJobRunning($job, false);
} elseif ($pid == 0) {
$jobRepo->RunJob($job, $manual);
$jobRepo->setJobRunning($job, false);
exit;
if((!$async || $pid == -1) || $pid == 0) {
$result = $jobRepo->RunJob($job, $manual);
if ($result['status'] == 'ran') $jobRepo->setJobRunning($job, false);
if (isset($pid) && $pid == 0) exit;
}
unset($jobsToRun[$key]);
unset($job);

View File

@ -86,6 +86,7 @@ class DemoInstallCommand extends Command
false,
]
]);
$job1->addToken();
$job2 = $jobRepo->prepareJob([
'name' => '[Website] Update texts to latest version',
@ -111,6 +112,8 @@ class DemoInstallCommand extends Command
true,
]
]);
$job2->addToken();
$job3 = $jobRepo->prepareJob([
'name' => '[Server][Reboot] Monthly reboot',
'interval' => (60*60*24*30),
@ -136,6 +139,8 @@ class DemoInstallCommand extends Command
'var-issecret' => [
]
]);
$job3->addToken();
$em->persist($job1);
$em->persist($job2);
$em->persist($job3);
@ -220,6 +225,15 @@ rtt min/avg/max/mdev = 101.362/101.362/101.362/0.000 ms')
->setFlags(RunRepository::SUCCESS);
$em->persist($run);
$run = new Run();
$run->setExitcode(200)
->setJob($job2)
->setRuntime(rand(0, 10000) / 1000)
->setOutput(json_encode(['success' => true, 'message' => 'Texts are updated succesfully']))
->setTimestamp(7200 * ceil( time() / 7200) - (3542))
->setFlags(RunRepository::SUCCESS . RunRepository::TRIGGERED);
$em->persist($run);
$run = new Run();
$run->setExitcode(200)
->setJob($job2)

View File

@ -47,14 +47,14 @@ class RunCommand extends Command
}
$jobRepo->setJobRunning($job, true);
$jobRepo->setTempVar($job, 'consolerun', true);
$result = $jobRepo->runNow($job, true);
$result = $jobRepo->run($job, true);
if($job->getData('crontype') == 'reboot') {
$sleeping = true;
while($sleeping) {
if(time() >= $job->getRunning()) $sleeping = false;
sleep(1);
}
$result = $jobRepo->runNow($job, true);
$result = $jobRepo->run($job, true);
}
$jobRepo->setJobRunning($job, false);
$jobRepo->setTempVar($job, 'consolerun', false);

View File

@ -11,10 +11,12 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
class JobController extends AbstractController
{
#[Route('/{_locale}/job', name: 'job_index')]
public function defaultAction(ManagerRegistry $doctrine): Response
{
$jobRepo = $doctrine->getRepository(Job::class);
@ -22,23 +24,27 @@ class JobController extends AbstractController
return $this->render('job/index.html.twig', ['jobs' => $jobs]);
}
public function jobAction(Request $request, ManagerRegistry $doctrine, int $id, mixed $all = false): Response
#[Route('/{_locale}/job/{id}/{all}', name: 'job_view', methods: ['GET'], defaults: [ 'all' => false ], requirements: ['id' => '\d+', 'all' => '(all|)'])]
public function viewAction(Request $request, ManagerRegistry $doctrine, int $id, mixed $all = false): Response
{
$jobRepo = $doctrine->getRepository(Job::class);
$runRepo = $doctrine->getRepository(Run::class);
if($request->getMethod() == 'GET') {
$job = $jobRepo->find($id);
$runs = $runRepo->getRunsForJob($job, $all != 'all');
return $this->render('job/view.html.twig', ['job' => $job, 'runs' => $runs, 'allruns' => $all == 'all']);
} elseif($request->getMethod() == 'DELETE') {
$success = $jobRepo->deleteJob($id);
$this->addFlash('success', 'job.index.flashes.jobdeleted');
return new JsonResponse(['return_path' => $this->GenerateUrl('job_index')]);
}
return new JsonResponse(['success'=>false, 'message' => 'Your request is invalid'], Response::HTTP_BAD_REQUEST);
$job = $jobRepo->find($id);
$runs = $runRepo->getRunsForJob($job, $all != 'all');
return $this->render('job/view.html.twig', ['job' => $job, 'runs' => $runs, 'allruns' => $all == 'all']);
}
#[Route('/{_locale}/job/{id}', name: 'job_delete', methods: ['DELETE'], defaults: [ 'all' => false ], requirements: ['id' => '\d+', 'all' => '(all|)'])]
public function deleteAction(Request $request, ManagerRegistry $doctrine, int $id, mixed $all = false): Response
{
$jobRepo = $doctrine->getRepository(Job::class);
$success = $jobRepo->deleteJob($id);
$this->addFlash('success', 'job.index.flashes.jobdeleted');
return new JsonResponse(['return_path' => $this->GenerateUrl('job_index')]);
}
#[Route('/{_locale}/job/{id}/edit', name: 'job_edit', requirements: ['id' => '\d+'])]
public function editAction(Request $request, ManagerRegistry $doctrine, int $id): Response
{
if($request->getMethod() == 'GET') {
@ -61,6 +67,7 @@ class JobController extends AbstractController
return new JsonResponse(['success'=>false, 'message' => 'Your request is invalid'], Response::HTTP_BAD_REQUEST);
}
#[Route('/{_locale}/job/add', name: 'job_add')]
public function addAction(Request $request, ManagerRegistry $doctrine): Response
{
if($request->getMethod() == 'GET') {
@ -80,35 +87,48 @@ class JobController extends AbstractController
return new Response('Not implemented yet', Response::HTTP_TOO_EARLY);
}
}
public function runNowAction(Request $request, ManagerRegistry $doctrine, TranslatorInterface $translator, int $id): JsonResponse
#[Route('/{_locale}/job/{id}/run/{timestamp}', name: 'job_run', defaults: ['timestamp' => 0 ], requirements: ['id' => '\d+', 'timestamp' => '\d+'])]
public function runAction(Request $request, ManagerRegistry $doctrine, TranslatorInterface $translator, int $id, int $timestamp): JsonResponse
{
if($request->getMethod() == 'GET') {
$jobRepo = $doctrine->getRepository(Job::class);
$job = $jobRepo->find($id);
$runnowResult = $jobRepo->runNow($job);
if ($runnowResult['success'] === NULL) {
$runResult = $jobRepo->run($job, false, $timestamp);
if ($runResult['success'] === NULL) {
$return = [
'status' => 'deferred',
'success' => NULL,
'title' => $translator->trans('job.index.runnow.deferred.title'),
'message' => $translator->trans('job.index.runnow.deferred.message')
'title' => $translator->trans('job.index.run.deferred.title'),
'message' => $translator->trans('job.index.run.deferred.message')
];
} else {
$return = [
'status' => 'ran',
'success' => $runnowResult['success'],
'title' => $runnowResult['success'] ? $translator->trans('job.index.runnow.ran.title.success') : $translator->trans('job.index.runnow.ran.title.failed'),
'message' => $translator->trans('job.index.runnow.ran.message', [
'_runtime_' => number_format($runnowResult['runtime'], 3),
'_exitcode_' => $runnowResult['exitcode']
'success' => $runResult['success'],
'title' => $runResult['success'] ? $translator->trans('job.index.run.ran.title.success') : $translator->trans('job.index.run.ran.title.failed'),
'message' => $translator->trans('job.index.run.ran.message', [
'_runtime_' => number_format($runResult['runtime'], 3),
'_exitcode_' => $runResult['exitcode']
]),
'exitcode' => $runnowResult['exitcode'],
'output' => $runnowResult['output'],
'exitcode' => $runResult['exitcode'],
'output' => $runResult['output'],
];
}
return new JsonResponse($return);
}
return new JsonResponse(['success'=>false, 'message' => 'Your request is invalid'], Response::HTTP_BAD_REQUEST);
}
#[Route('/hook/{id}/{token}', name: 'webhook', requirements: ['id' => '\d+', 'token' => '[A-Za-z0-9]+'])]
public function hookAction(Request $request, ManagerRegistry $doctrine, int $id, string $token)
{
$jobRepo = $doctrine->getRepository(Job::class);
$job = $jobRepo->find($id);
if(!empty($job->getToken()) && $job->getToken() == $token && $job->getRunning() != 1) {
$jobRepo->setTempVar($job, 'webhook', true);
return new JsonResponse($jobRepo->run($job, false, time()));
}
return new JsonResponse(['success'=>false, 'message' => 'Your request is invalid'], Response::HTTP_BAD_REQUEST);
}
}

View File

@ -10,10 +10,12 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route;
class SiteController extends AbstractController
{
#[Route('/health', name: 'health')]
public function healthAction(Request $request, ManagerRegistry $doctrine, KernelInterface $kernel)
{
$em = $doctrine->getManager();
@ -28,6 +30,7 @@ class SiteController extends AbstractController
return new JsonResponse($return, $return['DaemonRunning'] ? 200 : 500);
}
#[Route('/favicon.ico', name: 'favicon')]
public function faviconAction(Request $request)
{
return new Response('', 200);

View File

@ -11,10 +11,14 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class UserController extends AbstractController
{
#[Route('/', name: 'default')]
#[Route('/{_locale}/login', name: 'login')]
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils): Response
{
if($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
@ -42,13 +46,14 @@ class UserController extends AbstractController
]);
}
#[Route('/{_locale}/settings', name: 'settings', methods: ['GET'])]
public function settingsAction(Request $request)
{
$params['locales'] = $this->getParameter('enabled_locales');
$params['user'] = $this->getUser();
return $this->render('settings.html.twig', $params);
}
#[Route('/{_locale}/settings', name: 'settings_save', methods: ['POST'])]
public function settingsSaveAction(Request $request, ManagerRegistry $em, UserPasswordHasherInterface $passwordHasher)
{
$session = $request->getSession();
@ -94,12 +99,14 @@ class UserController extends AbstractController
return $this->redirect($this->generateUrl($route, ['_locale' => $locale]));
}
#[Route('/logout', name: 'logout')]
public function logoutAction(): void
{
// controller can be blank: it will never be called!
throw new \Exception('Don\'t forget to activate logout in security.yaml');
}
#[Route('/login_check', name: 'login_check')]
public function loginCheckAction(): void
{

View File

@ -259,4 +259,29 @@ class Job
return $this;
}
public function getToken(): string
{
return $this->getData('hooktoken') ?? '';
}
public function deleteToken(): Job
{
$this->removeData('hooktoken');
return $this;
}
public function addToken(): Job
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
$length = 32;
for ($i = 0; $i < $length; $i++) {
$index = rand(0, strlen($characters) - 1);
$randomString .= $characters[$index];
}
$this->setData('hooktoken', $randomString);
return $this;
}
}

View File

@ -1,35 +0,0 @@
<?php
namespace App\EventSubscriber;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SecurityHeadersSubscriber implements EventSubscriberInterface
{
private $params;
public function __construct(ContainerBagInterface $params)
{
$this->params = $params;
}
public function onResponse(ResponseEvent $event)
{
$response = $event->getResponse();
$securitypolicy = $this->params->get('security');
$csp = $securitypolicy['csp_policy'];
$referer = $securitypolicy['referer_policy'];
$response->headers->set("Content-Security-Policy", $csp);
$response->headers->set("Referrer-Policy", $referer);
}
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => 'onResponse'
];
}
}

View File

@ -123,7 +123,7 @@ class JobRepository extends EntityRepository
->orWhere(
$qb->expr()->andX(
$qb->expr()->notIn('job.running', [0,1,2]),
$qb->expr()->lt('job.running', ':timestamp')
$qb->expr()->lte('job.running', ':timestamp')
)
)
->orWhere('job.running = 2')
@ -142,7 +142,7 @@ class JobRepository extends EntityRepository
{
$em = $this->getEntityManager();
if(in_array($job->getRunning(), [0,1,2])) $job->setRunning($status ? 1 : 0);
$job->setRunning($status ? 1 : 0);
$em->persist($job);
$em->flush();
@ -167,7 +167,7 @@ class JobRepository extends EntityRepository
*/
public function deleteTempVar(Job &$job, ?string $name = NULL ): void
{
$job->removeData('temp_vars.' . ($name !== NULL ? '.' . $name : ''));
$job->removeData('temp_vars' . ($name !== NULL ? '.' . $name : ''));
}
/**
@ -239,7 +239,7 @@ class JobRepository extends EntityRepository
$command = $job->getData('command');
if(!empty($job->getData('vars'))) {
foreach ($job->getData('vars') as $key => $var) {
$command = str_replace('{' . $key . '}', $var['value'], $job->getData('command'));
$command = str_replace('{' . $key . '}', ($var['issecret'] ? Secret::decrypt(base64_decode($var['value'])) : $var['value']), $job->getData('command'));
}
}
@ -317,10 +317,10 @@ class JobRepository extends EntityRepository
* @return array|string[]
* @throws \Doctrine\DBAL\Exception
*/
private function runRebootJob(Job &$job, float &$starttime, bool &$manual): array
private function runRebootJob(Job &$job, float &$starttime, string &$manual): array
{
$em = $this->getEntityManager();
if($job->getRunning() == 1) {
if($this->getTempVar($job, 'rebooting', false) === false) {
if(isset($_ENV['DEMO_MODE']) && $_ENV['DEMO_MODE'] == 'true') {
$job->setRunning(time() + $job->getData('reboot-delay-secs') + ($job->getData('reboot-duration') * 60));
$em->persist($job);
@ -336,11 +336,12 @@ class JobRepository extends EntityRepository
if (!empty($job->getData('vars'))) {
foreach ($job->getData('vars') as $key => $var) {
$rebootcommand = str_replace('{' . $key . '}', $var['value'], $rebootcommand);
$rebootcommand = str_replace('{' . $key . '}', ($var['issecret'] ? Secret::decrypt(base64_decode($var['value'])) : $var['value']), $rebootcommand);
}
}
$job->setRunning(time() + $job->getData('reboot-delay-secs') + ($job->getData('reboot-duration') * 60));
$this->setTempVar($job, 'rebooting', true);
$em->persist($job);
$em->flush();
@ -358,7 +359,7 @@ class JobRepository extends EntityRepository
}
return ['status' => 'deferred'];
} elseif($job->getRunning() != 0) {
} elseif($this->getTempVar($job, 'rebooting', false) === true) {
if($job->getRunning() > time()) {
return ['status' => 'deferred'];
}
@ -384,7 +385,7 @@ class JobRepository extends EntityRepository
$getservicescommand = $job->getData('getservices-command');
if (!empty($job->getData('vars'))) {
foreach ($job->getData('vars') as $key => $var) {
$getservicescommand = str_replace('{' . $key . '}', $var['value'], $job->getData('getservices-command'));
$getservicescommand = str_replace('{' . $key . '}', ($var['issecret'] ? Secret::decrypt(base64_decode($var['value'])) : $var['value']), $job->getData('getservices-command'));
}
}
try {
@ -411,11 +412,15 @@ class JobRepository extends EntityRepository
* @return array
* @throws \Doctrine\DBAL\Exception
*/
public function runNow(Job &$job, $console = false) {
public function run(Job &$job, $console = false, int $timestamp = 0)
{
$em = $this->getEntityManager();
$runRepo = $this->getEntityManager()->getRepository(Run::class);
if($console == false && ($runRepo->isSlowJob($job)) || count($job->getRuns()) == 0 || $job->getData('crontype') === 'reboot') {
if ($timestamp > 0) {
$job->setRunning($timestamp);
$em->persist($job);
$em->flush();
} elseif($console == false && ($runRepo->isSlowJob($job)) || count($job->getRuns()) == 0 || $job->getData('crontype') === 'reboot') {
if(in_array($job->getRunning(), [0,1,2])) {
$job->setRunning(2);
$em->persist($job);
@ -458,7 +463,7 @@ class JobRepository extends EntityRepository
* @return array|string[]
* @throws \Doctrine\DBAL\Exception
*/
public function runJob(Job &$job, bool $manual): array
public function runJob(Job &$job, string $manual): array
{
$em = $this->getEntityManager();
$starttime = microtime(true);
@ -481,8 +486,10 @@ class JobRepository extends EntityRepository
$flags[] = RunRepository::SUCCESS;
}
if ($manual === true) {
if ($manual == 'Manual') {
$flags[] = RunRepository::MANUAL;
} elseif ($manual == 'Webhook') {
$flags[] = RunRepository::TRIGGERED;
}
// Remove secrets from output
@ -506,13 +513,12 @@ class JobRepository extends EntityRepository
} while ($nextrun < time());
$job->setNextrun($nextrun);
}
$this->deleteTempVar($job);
$em->persist($job);
$em->flush();
return ['job_id' => $job->getId(), 'exitcode' => $result['exitcode'], 'timestamp' =>floor($starttime), 'runtime' => $runtime, 'output' => (string)$result['output'], 'flags' => implode("", $flags)];
return ['job_id' => $job->getId(), 'status' => 'ran', 'exitcode' => $result['exitcode'], 'timestamp' =>floor($starttime), 'runtime' => $runtime, 'output' => (string)$result['output'], 'flags' => implode("", $flags)];
}
/**
@ -734,6 +740,9 @@ class JobRepository extends EntityRepository
}
}
}
if(empty($job->getData('hooktoken'))) {
$job->addToken();
}
return $job;
}

View File

@ -14,6 +14,7 @@ class RunRepository extends EntityRepository
const FAILED = 'F';
const SUCCESS = 'S';
const MANUAL = 'M';
const TRIGGERED = 'T';
public function getRunsForJob(Job $job, bool $onlyfailed = false, int $maxage = NULL, bool $ordered = true): array
{

View File

@ -24,12 +24,12 @@
"version": "v0.5.3"
},
"doctrine/doctrine-bundle": {
"version": "2.6",
"version": "2.10",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.4",
"ref": "ddddd8249dd55bbda16fa7a45bb7499ef6f8e90e"
"branch": "main",
"version": "2.10",
"ref": "f0d8c9a4da17815830aac0d63e153a940ae176bb"
},
"files": [
"config/packages/doctrine.yaml",
@ -41,9 +41,9 @@
"version": "3.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"branch": "main",
"version": "3.1",
"ref": "ee609429c9ee23e22d6fa5728211768f51ed2818"
"ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
},
"files": [
"config/packages/doctrine_migrations.yaml",
@ -95,6 +95,18 @@
"monolog/monolog": {
"version": "2.5.0"
},
"nelmio/security-bundle": {
"version": "3.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.4",
"ref": "65726efb67ff51d89de38195bc0d230fa811f64d"
},
"files": [
"config/packages/nelmio_security.yaml"
]
},
"nikic/php-parser": {
"version": "v4.13.2"
},
@ -104,18 +116,6 @@
"paragonie/random_compat": {
"version": "v9.99.100"
},
"pentatrion/vite-bundle": {
"version": "1.2",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "main",
"version": "1.0",
"ref": "33d48a1c831b2d29641175c8320e66a9f1c9911f"
},
"files": [
"config/routes/dev/pentatrion_vite.yaml"
]
},
"phpseclib/phpseclib": {
"version": "3.0.14"
},
@ -168,12 +168,12 @@
]
},
"symfony/crowdin-translation-provider": {
"version": "6.1",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "8acdcff2acee247e79f16ed0b2abc8fd53cf0d30"
"ref": "9f5f1508bc80ed56c8a3ae7febc53a8aa982e424"
}
},
"symfony/debug-bundle": {
@ -216,24 +216,24 @@
"version": "v6.0.3"
},
"symfony/flex": {
"version": "2.1",
"version": "2.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"branch": "main",
"version": "1.0",
"ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
"ref": "146251ae39e06a95be0fe3d13c807bcf3938b172"
},
"files": [
".env"
]
},
"symfony/framework-bundle": {
"version": "6.0",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.4",
"ref": "3cd216a4d007b78d8554d44a5b1c0a446dab24fb"
"branch": "main",
"version": "6.2",
"ref": "af47254c5e4cd543e6af3e4508298ffebbdaddd3"
},
"files": [
"config/packages/cache.yaml",
@ -259,12 +259,12 @@
"version": "v6.0.7"
},
"symfony/mailer": {
"version": "6.0",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"branch": "main",
"version": "4.3",
"ref": "97a61eabb351d7f6cb7702039bcfe07fe9d7e03c"
"ref": "2bf89438209656b85b9a49238c4467bff1b1f939"
},
"files": [
"config/packages/mailer.yaml"
@ -325,12 +325,12 @@
"version": "v6.0.6"
},
"symfony/routing": {
"version": "6.0",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "6.0",
"ref": "eb3b377a4dc07006c4bdb2c773652cc9434f5246"
"branch": "main",
"version": "6.2",
"ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6"
},
"files": [
"config/packages/routing.yaml",
@ -341,12 +341,12 @@
"version": "v6.0.7"
},
"symfony/security-bundle": {
"version": "6.0",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.3",
"ref": "98f1f2b0d635908c2b40f3675da2d23b1a069d30"
"branch": "main",
"version": "6.0",
"ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48"
},
"files": [
"config/packages/security.yaml"
@ -390,12 +390,12 @@
"version": "v6.0.7"
},
"symfony/twig-bundle": {
"version": "6.0",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.4",
"ref": "bb2178c57eee79e6be0b297aa96fc0c0def81387"
"branch": "main",
"version": "6.3",
"ref": "b7772eb20e92f3fb4d4fe756e7505b4ba2ca1a2c"
},
"files": [
"config/packages/twig.yaml",
@ -409,12 +409,12 @@
"version": "v6.0.7"
},
"symfony/web-profiler-bundle": {
"version": "6.0",
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.3",
"ref": "24bbc3d84ef2f427f82104f766014e799eefcc3e"
"branch": "main",
"version": "6.1",
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
},
"files": [
"config/packages/web_profiler.yaml",
@ -422,18 +422,15 @@
]
},
"symfony/webpack-encore-bundle": {
"version": "1.15",
"version": "2.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.10",
"ref": "2e458cc7e6f1df1dad890eb104b81e4f302c9bd4"
"version": "2.0",
"ref": "13ebe04e25085e2ff0bcb0f9218b561d8b5089f3"
},
"files": [
"assets/app.js",
"assets/bootstrap.js",
"assets/controllers.json",
"assets/controllers/hello_controller.js",
"assets/styles/app.css",
"config/packages/webpack_encore.yaml",
"package.json",

View File

@ -1,8 +1,8 @@
<!doctype html>
<!DOCTYPE html>
<html class="h-100">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ "title" | trans }} :: {% block title %}{% endblock %}</title>
{% block styles %}{% endblock %}
@ -39,5 +39,4 @@
{% block extrahtml %}{% endblock %}
{{ include('footer.html.twig') }}
</body>
</html>
</html>

View File

@ -2,252 +2,252 @@
{% block title %}{{ "job.add.title" | trans }}{% endblock %}
{% block content %}
<h2>{{ "job.add.header" | trans }}</h2>
<form method="post" class="form-horizontal" enctype="multipart/form-data" action="{{ path('job_add') }}">
<form method="post" class="form-horizontal" enctype="multipart/form-data" action="{{ path('job_add') }}">
<h3>{{ "job.addedit.generalinfo.header" | trans }}</h3>
<h3>{{ "job.addedit.generalinfo.header" | trans }}</h3>
<div class="mb-3">
<label for="name">{{ "job.addedit.generalinfo.name.label" | trans }}</label>
<input type="text" name="name" class="form-control" id="name" placeholder="{{ "job.addedit.generalinfo.name.placeholder" | trans }}">
<small id="name-help" class="form-text text-muted">{{ "job.addedit.generalinfo.name.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="name">{{ "job.addedit.generalinfo.interval.label" | trans }}</label>
<div class="input-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="intervalButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.generalinfo.interval.patterns.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="intervalButton">
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="60">{{ "job.addedit.generalinfo.interval.patterns.minute" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="3600">{{ "job.addedit.generalinfo.interval.patterns.hour" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="86400">{{ "job.addedit.generalinfo.interval.patterns.day" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="604800">{{ "job.addedit.generalinfo.interval.patterns.week" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="2419200">{{ "job.addedit.generalinfo.interval.patterns.4week" | trans }}</a></li>
</ul>
<input type="number" class="form-control" id="interval" name="interval">
</div>
</div>
<div class="mb-3">
<label for="nextrun">{{ "job.addedit.generalinfo.nextrun.label" | trans }}</label>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" placeholder="{{ date() | date("d/m/Y H:i:s")}}" step="1" id="nextrunselector" class="form-control datetimepicker-input" data-target="#nextrunselector" data-bs-toggle="datetimepicker" name="nextrun">
</div>
<div class="mb-3">
<label for="lastrun">{{ "job.addedit.generalinfo.lastrun.label" | trans }}</label>
<div class="input-group">
<div class="input-group-text border-end-0">
<input type="checkbox" name="lastrun-eternal" class="lastrun-eternal" placeholder="value" value="true">
</div>
<span class="input-group-text border-start-0">{{ "job.addedit.generalinfo.lastrun.eternal.label" | trans }}</span>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" placeholder="{{ date() | date("d/m/Y H:i:s")}}" data-placeholder="{{ date() | date("d/m/Y H:i:s")}}" id="lastrunselector" class="form-control datetimepicker-input" data-target="#lastrunselector" data-bs-toggle="datetimepicker" name="lastrun">
</div>
</div>
<div class="mb-3">
<label for="retention">{{ "job.addedit.generalinfo.retention.label" | trans }}</label>
<input type="number" name="retention" class="form-control" id="retention" placeholder="{{ "job.addedit.generalinfo.retention.placeholder" | trans }}">
<small id="retention-help" class="form-text text-muted">{{ "job.addedit.generalinfo.retention.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="fail-pct">{{ "job.addedit.generalinfo.failpercentage.label" | trans }}</label>
<div class="input-group d-flex">
<div class="range-value range-value-fail-pct pe-1">50%</div>
<div class="range-input ps-1 flex-grow-1">
<input type="range" name="fail-pct" class="form-range range-input-fail-pct" id="fail-pct" max="100" step="5" value="50">
</div>
</div>
</div>
<div class="mb-3">
<label for="fail-days">{{ "job.addedit.generalinfo.faildays.label" | trans }}</label>
<input type="number" name="fail-days" class="form-control" id="fail-days" placeholder="{{ "job.addedit.generalinfo.faildays.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="retention">{{ "job.addedit.generalinfo.hostlabel.label" | trans }}</label>
<input type="text" name="hostlabel" class="form-control" id="hostlabel" placeholder="{{ "job.addedit.generalinfo.hostlabel.placeholder" | trans }}">
<small id="hostlabel-help" class="form-text text-muted">{{ "job.addedit.generalinfo.hostlabel.helptext" | trans }}</small>
</div>
<h3>{{ "job.addedit.jobdetails.header" | trans }}</h3>
<div class="mb-3 btn-group croncategory-selector">
<div class="dropdown croncategory-group crontype-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="crontypeButton" data-default-text="" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.crontype.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="crontypeButton">
<li><a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="command">{{ "job.addedit.crontype.command.label" | trans }}</a></li>
<li><a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="reboot">{{ "job.addedit.crontype.reboot.label" | trans }}</a></li>
<li><a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="http">{{ "job.addedit.crontype.http.label" | trans }}</a></li>
</ul>
</div>
<div class="dropdown croncategory-group d-none hosttype-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="hosttypeButton" data-default-text="" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.hosttype.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="hosttypeButton">
<li><a class="dropdown-item hosttype-item" href="javascript:void(0);" data-type="local">{{ "job.addedit.hosttype.local.label" | trans }}</a></li>
<li><a class="dropdown-item hosttype-item" href="javascript:void(0);" data-type="ssh">{{ "job.addedit.hosttype.ssh.label" | trans }}</a></li>
</ul>
</div>
<div class="dropdown croncategory-group d-none containertype-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="containertypeButton" data-default-text="" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.containertype.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="containertypeButton">
<li><a class="dropdown-item containertype-item" href="javascript:void(0);" data-type="none">{{ "job.addedit.containertype.none.label" | trans }}</a></li>
<li><a class="dropdown-item containertype-item" href="javascript:void(0);" data-type="docker">{{ "job.addedit.containertype.docker.label" | trans }}</a></li>
</ul>
</div>
</div>
<div class="crontype-command crontype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.crontype.command.label" | trans }}</h4>
<div class="mb-3">
<label for="command">{{ "job.addedit.crontype.command.command.label" | trans }}</label>
<input type="text" name="command" class="form-control" id="command" placeholder="{{ "job.addedit.crontype.command.command.placeholder" | trans }}">
<label for="name">{{ "job.addedit.generalinfo.name.label" | trans }}</label>
<input type="text" name="name" class="form-control" id="name" placeholder="{{ "job.addedit.generalinfo.name.placeholder" | trans }}">
<small id="name-help" class="form-text text-muted">{{ "job.addedit.generalinfo.name.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="response">{{ "job.addedit.crontype.command.response.label" | trans }}</label>
<input type="text" name="response" class="form-control" id="response" placeholder="{{ "job.addedit.crontype.command.response.placeholder" | trans }}">
</div>
</div>
<div class="crontype-reboot crontype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.crontype.reboot.header" | trans }}</h4>
<div class="mb-3">
<label for="reboot-command">{{ "job.addedit.crontype.reboot.reboot.command.label" | trans }}</label>
<input type="text" name="reboot-command" class="form-control" id="reboot-command" placeholder="{{ "job.addedit.crontype.reboot.reboot.command.placeholder" | trans }}">
<small id="reboot-command-help" class="form-text text-muted">{{ "job.addedit.crontype.reboot.reboot.command.placeholder" | trans }}</small>
</div>
<div class="mb-3">
<label for="getservices-command">{{ "job.addedit.crontype.reboot.getservices.command.label" | trans }}</label>
<input type="text" name="getservices-command" class="form-control" id="getservices-command" placeholder="{{ "job.addedit.crontype.reboot.getservices.command.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="getservices-response">{{ "job.addedit.crontype.reboot.getservices.response.label" | trans }}</label>
<input type="text" name="getservices-response" class="form-control" id="getservices-response" placeholder="{{ "job.addedit.crontype.reboot.getservices.response.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="reboot-delay">{{ "job.addedit.crontype.reboot.reboot.delay.label" | trans }}</label>
<input type="number" name="reboot-delay" class="form-control" placeholder="{{ "job.addedit.crontype.reboot.reboot.delay.placeholder" | trans }}">
<small id="reboot-delay-help" class="form-text text-muted">{{ "job.addedit.crontype.reboot.reboot.delay.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="reboot-duration">{{ "job.addedit.crontype.reboot.reboot.duration.label" | trans }}</label>
<input type="number" name="reboot-duration" class="form-control" placeholder="{{ "job.addedit.crontype.reboot.reboot.duration.placeholder" | trans }}">
<small id="reboot-duration-help" class="form-text text-muted">{{ "job.addedit.crontype.reboot.reboot.duration.helptext" | trans }}</small>
</div>
</div>
<div class="crontype-http crontype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.crontype.http.header" | trans }}</h4>
<div class="mb-3">
<label for="url">{{ "job.addedit.crontype.http.url.label" | trans }}</label>
<input type="text" name="url" class="form-control" id="url" placeholder="{{ "job.addedit.crontype.http.url.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="basicauth-username">{{ "job.addedit.crontype.http.basic-auth.username.label" | trans }}</label>
<input type="text" name="basicauth-username" class="form-control" id="basicauth-username" placeholder="{{ "job.addedit.crontype.http.basic-auth.username.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="basicauth-password">{{ "job.addedit.crontype.http.basic-auth.password.label" | trans }}</label>
<input type="password" name="basicauth-password" class="form-control" placeholder="{{ "job.addedit.crontype.http.basic-auth.password.placeholder" | trans }}">
<small id="basicauth-password-help" class="form-text text-muted">{{ "job.addedit.crontype.http.basic-auth.password.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="http-status">{{ "job.addedit.crontype.http.response.label" | trans }}</label>
<input type="text" name="http-status" class="form-control" id="http-status" placeholder="{{ "job.addedit.crontype.http.response.placeholder" | trans }}">
</div>
</div>
<div class="hosttype-local hosttype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.hosttype.local.header" | trans }}</h4>
<h5>{{ "job.addedit.hosttype.local.nodetails" | trans }}</h5>
</div>
<div class="hosttype-ssh hosttype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.hosttype.ssh.header" | trans }}</h4>
<div class="mb-3">
<label for="host">{{ "job.addedit.hosttype.ssh.hostname.label" | trans }}</label>
<input type="text" name="host" class="form-control" id="host" placeholder="{{ "job.addedit.hosttype.ssh.hostname.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="user">{{ "job.addedit.hosttype.ssh.username.label" | trans }}</label>
<input type="text" name="user" class="form-control" id="user" placeholder="{{ "job.addedit.hosttype.ssh.username.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="privkey">{{ "job.addedit.hosttype.ssh.privatekey.label" | trans }}</label>
<label for="name">{{ "job.addedit.generalinfo.interval.label" | trans }}</label>
<div class="input-group">
<input type="file" class="form-control" id="privkey" class="form-control" name="privkey">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="intervalButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.generalinfo.interval.patterns.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="intervalButton">
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="60">{{ "job.addedit.generalinfo.interval.patterns.minute" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="3600">{{ "job.addedit.generalinfo.interval.patterns.hour" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="86400">{{ "job.addedit.generalinfo.interval.patterns.day" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="604800">{{ "job.addedit.generalinfo.interval.patterns.week" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="javascript:void(0);" data-time="2419200">{{ "job.addedit.generalinfo.interval.patterns.4week" | trans }}</a></li>
</ul>
<input type="number" class="form-control" id="interval" name="interval">
</div>
<small id="custom-file-help" class="form-text text-muted">{{ "job.addedit.hosttype.ssh.privatekey.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="privkey-password">{{ "job.addedit.hosttype.ssh.passphrase.label" | trans }}</label>
<input type="password" name="privkey-password" class="form-control" placeholder="{{ "job.addedit.hosttype.ssh.passphrase.placeholder" | trans }}">
<small id="privkey-password-help" class="form-text text-muted">{{ "job.addedit.hosttype.ssh.passphrase.helptext" | trans }}</small>
</div>
</div>
<div class="containertype-none containertype-inputs croncategory-inputs d-none">
</div>
<div class="containertype-docker containertype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.containertype.docker.header" | trans }}</h4>
<div class="mb-3">
<label for="service">{{ "job.addedit.containertype.docker.service.label" | trans }}</label>
<input type="text" name="service" class="form-control" id="service" placeholder="{{ "job.addedit.containertype.docker.service.placeholder" | trans }}">
<label for="nextrun">{{ "job.addedit.generalinfo.nextrun.label" | trans }}</label>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" placeholder="{{ date() | date("d/m/Y H:i:s")}}" step="1" id="nextrunselector" class="form-control datetimepicker-input" data-target="#nextrunselector" data-bs-toggle="datetimepicker" name="nextrun">
</div>
<div class="mb-3">
<label for="user">{{ "job.addedit.containertype.docker.username.label" | trans }}</label>
<input type="text" name="container-user" class="form-control" id="container-user" placeholder="{{ "job.addedit.containertype.docker.username.placeholder" | trans }}">
<label for="lastrun">{{ "job.addedit.generalinfo.lastrun.label" | trans }}</label>
<div class="input-group">
<div class="input-group-text border-end-0">
<input type="checkbox" name="lastrun-eternal" class="lastrun-eternal" placeholder="value" value="true">
</div>
<span class="input-group-text border-start-0">{{ "job.addedit.generalinfo.lastrun.eternal.label" | trans }}</span>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" placeholder="{{ date() | date("d/m/Y H:i:s")}}" data-placeholder="{{ date() | date("d/m/Y H:i:s")}}" id="lastrunselector" class="form-control datetimepicker-input" data-target="#lastrunselector" data-bs-toggle="datetimepicker" name="lastrun">
</div>
</div>
</div>
<h3>{{ "job.addedit.variables.header" | trans }}</h3>
<div class="vars mb-3">
<div class="input-group var-group d-none">
<span class="input-group-text border-end-0">
<input type="checkbox" name="var-issecret[0]" class="var-issecret" placeholder="value" value="true">
</span>
<span class="input-group-text border-start-0">{{ "job.addedit.variables.secret.label" | trans }}</span>
<input type="text" name="var-id[0]" class="form-control var-id" placeholder="{{ "job.addedit.variables.name.placeholder" | trans }}">
<input type="text" name="var-value[0]" class="form-control var-value" placeholder="{{ "job.addedit.variables.value.placeholder" | trans }}">
<div class="mb-3">
<label for="retention">{{ "job.addedit.generalinfo.retention.label" | trans }}</label>
<input type="number" name="retention" class="form-control" id="retention" placeholder="{{ "job.addedit.generalinfo.retention.placeholder" | trans }}">
<small id="retention-help" class="form-text text-muted">{{ "job.addedit.generalinfo.retention.helptext" | trans }}</small>
</div>
</div>
<div class="vars-description mb-3 d-none">
<p>
{{ "job.addedit.variables.helptext" | trans }}
</p>
</div>
<div class="mb-3">
<a href="javascript:void(0);" class="btn btn-outline-primary addvar-btn">{{ "job.addedit.variables.add.label" | trans }}</a>
</div>
<div class="mb-3">
<label for="fail-pct">{{ "job.addedit.generalinfo.failpercentage.label" | trans }}</label>
<div class="input-group d-flex">
<div class="range-value range-value-fail-pct pe-1">50%</div>
<div class="range-input ps-1 flex-grow-1">
<input type="range" name="fail-pct" class="form-range range-input-fail-pct" id="fail-pct" max="100" step="5" value="50">
</div>
</div>
</div>
<input type="hidden" name="crontype" class="crontype" value="">
<input type="hidden" name="hosttype" class="hosttype" value="">
<input type="hidden" name="containertype" class="containertype" value="">
<button type="submit" class="btn btn-outline-primary">{{ "job.addedit.submit.label" | trans }}</button>
</form>
<div class="mb-3">
<label for="fail-days">{{ "job.addedit.generalinfo.faildays.label" | trans }}</label>
<input type="number" name="fail-days" class="form-control" id="fail-days" placeholder="{{ "job.addedit.generalinfo.faildays.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="retention">{{ "job.addedit.generalinfo.hostlabel.label" | trans }}</label>
<input type="text" name="hostlabel" class="form-control" id="hostlabel" placeholder="{{ "job.addedit.generalinfo.hostlabel.placeholder" | trans }}">
<small id="hostlabel-help" class="form-text text-muted">{{ "job.addedit.generalinfo.hostlabel.helptext" | trans }}</small>
</div>
<h3>{{ "job.addedit.jobdetails.header" | trans }}</h3>
<div class="mb-3 btn-group croncategory-selector">
<div class="dropdown croncategory-group crontype-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="crontypeButton" data-default-text="" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.crontype.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="crontypeButton">
<li><a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="command">{{ "job.addedit.crontype.command.label" | trans }}</a></li>
<li><a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="reboot">{{ "job.addedit.crontype.reboot.label" | trans }}</a></li>
<li><a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="http">{{ "job.addedit.crontype.http.label" | trans }}</a></li>
</ul>
</div>
<div class="dropdown croncategory-group d-none hosttype-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="hosttypeButton" data-default-text="" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.hosttype.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="hosttypeButton">
<li><a class="dropdown-item hosttype-item" href="javascript:void(0);" data-type="local">{{ "job.addedit.hosttype.local.label" | trans }}</a></li>
<li><a class="dropdown-item hosttype-item" href="javascript:void(0);" data-type="ssh">{{ "job.addedit.hosttype.ssh.label" | trans }}</a></li>
</ul>
</div>
<div class="dropdown croncategory-group d-none containertype-group">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="containertypeButton" data-default-text="" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ "job.addedit.containertype.label" | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="containertypeButton">
<li><a class="dropdown-item containertype-item" href="javascript:void(0);" data-type="none">{{ "job.addedit.containertype.none.label" | trans }}</a></li>
<li><a class="dropdown-item containertype-item" href="javascript:void(0);" data-type="docker">{{ "job.addedit.containertype.docker.label" | trans }}</a></li>
</ul>
</div>
</div>
<div class="crontype-command crontype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.crontype.command.label" | trans }}</h4>
<div class="mb-3">
<label for="command">{{ "job.addedit.crontype.command.command.label" | trans }}</label>
<input type="text" name="command" class="form-control" id="command" placeholder="{{ "job.addedit.crontype.command.command.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="response">{{ "job.addedit.crontype.command.response.label" | trans }}</label>
<input type="text" name="response" class="form-control" id="response" placeholder="{{ "job.addedit.crontype.command.response.placeholder" | trans }}">
</div>
</div>
<div class="crontype-reboot crontype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.crontype.reboot.header" | trans }}</h4>
<div class="mb-3">
<label for="reboot-command">{{ "job.addedit.crontype.reboot.reboot.command.label" | trans }}</label>
<input type="text" name="reboot-command" class="form-control" id="reboot-command" placeholder="{{ "job.addedit.crontype.reboot.reboot.command.placeholder" | trans }}">
<small id="reboot-command-help" class="form-text text-muted">{{ "job.addedit.crontype.reboot.reboot.command.placeholder" | trans }}</small>
</div>
<div class="mb-3">
<label for="getservices-command">{{ "job.addedit.crontype.reboot.getservices.command.label" | trans }}</label>
<input type="text" name="getservices-command" class="form-control" id="getservices-command" placeholder="{{ "job.addedit.crontype.reboot.getservices.command.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="getservices-response">{{ "job.addedit.crontype.reboot.getservices.response.label" | trans }}</label>
<input type="text" name="getservices-response" class="form-control" id="getservices-response" placeholder="{{ "job.addedit.crontype.reboot.getservices.response.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="reboot-delay">{{ "job.addedit.crontype.reboot.reboot.delay.label" | trans }}</label>
<input type="number" name="reboot-delay" class="form-control" placeholder="{{ "job.addedit.crontype.reboot.reboot.delay.placeholder" | trans }}">
<small id="reboot-delay-help" class="form-text text-muted">{{ "job.addedit.crontype.reboot.reboot.delay.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="reboot-duration">{{ "job.addedit.crontype.reboot.reboot.duration.label" | trans }}</label>
<input type="number" name="reboot-duration" class="form-control" placeholder="{{ "job.addedit.crontype.reboot.reboot.duration.placeholder" | trans }}">
<small id="reboot-duration-help" class="form-text text-muted">{{ "job.addedit.crontype.reboot.reboot.duration.helptext" | trans }}</small>
</div>
</div>
<div class="crontype-http crontype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.crontype.http.header" | trans }}</h4>
<div class="mb-3">
<label for="url">{{ "job.addedit.crontype.http.url.label" | trans }}</label>
<input type="text" name="url" class="form-control" id="url" placeholder="{{ "job.addedit.crontype.http.url.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="basicauth-username">{{ "job.addedit.crontype.http.basic-auth.username.label" | trans }}</label>
<input type="text" name="basicauth-username" class="form-control" id="basicauth-username" placeholder="{{ "job.addedit.crontype.http.basic-auth.username.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="basicauth-password">{{ "job.addedit.crontype.http.basic-auth.password.label" | trans }}</label>
<input type="password" name="basicauth-password" class="form-control" placeholder="{{ "job.addedit.crontype.http.basic-auth.password.placeholder" | trans }}">
<small id="basicauth-password-help" class="form-text text-muted">{{ "job.addedit.crontype.http.basic-auth.password.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="http-status">{{ "job.addedit.crontype.http.response.label" | trans }}</label>
<input type="text" name="http-status" class="form-control" id="http-status" placeholder="{{ "job.addedit.crontype.http.response.placeholder" | trans }}">
</div>
</div>
<div class="hosttype-local hosttype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.hosttype.local.header" | trans }}</h4>
<h5>{{ "job.addedit.hosttype.local.nodetails" | trans }}</h5>
</div>
<div class="hosttype-ssh hosttype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.hosttype.ssh.header" | trans }}</h4>
<div class="mb-3">
<label for="host">{{ "job.addedit.hosttype.ssh.hostname.label" | trans }}</label>
<input type="text" name="host" class="form-control" id="host" placeholder="{{ "job.addedit.hosttype.ssh.hostname.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="user">{{ "job.addedit.hosttype.ssh.username.label" | trans }}</label>
<input type="text" name="user" class="form-control" id="user" placeholder="{{ "job.addedit.hosttype.ssh.username.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="privkey">{{ "job.addedit.hosttype.ssh.privatekey.label" | trans }}</label>
<div class="input-group">
<input type="file" class="form-control" id="privkey" class="form-control" name="privkey">
</div>
<small id="custom-file-help" class="form-text text-muted">{{ "job.addedit.hosttype.ssh.privatekey.helptext" | trans }}</small>
</div>
<div class="mb-3">
<label for="privkey-password">{{ "job.addedit.hosttype.ssh.passphrase.label" | trans }}</label>
<input type="password" name="privkey-password" class="form-control" placeholder="{{ "job.addedit.hosttype.ssh.passphrase.placeholder" | trans }}">
<small id="privkey-password-help" class="form-text text-muted">{{ "job.addedit.hosttype.ssh.passphrase.helptext" | trans }}</small>
</div>
</div>
<div class="containertype-none containertype-inputs croncategory-inputs d-none">
</div>
<div class="containertype-docker containertype-inputs croncategory-inputs d-none">
<h4>{{ "job.addedit.containertype.docker.header" | trans }}</h4>
<div class="mb-3">
<label for="service">{{ "job.addedit.containertype.docker.service.label" | trans }}</label>
<input type="text" name="service" class="form-control" id="service" placeholder="{{ "job.addedit.containertype.docker.service.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="user">{{ "job.addedit.containertype.docker.username.label" | trans }}</label>
<input type="text" name="container-user" class="form-control" id="container-user" placeholder="{{ "job.addedit.containertype.docker.username.placeholder" | trans }}">
</div>
</div>
<h3>{{ "job.addedit.variables.header" | trans }}</h3>
<div class="vars mb-3">
<div class="input-group var-group d-none">
<span class="input-group-text border-end-0">
<input type="checkbox" name="var-issecret[0]" class="var-issecret" placeholder="value" value="true">
</span>
<span class="input-group-text border-start-0">{{ "job.addedit.variables.secret.label" | trans }}</span>
<input type="text" name="var-id[0]" class="form-control var-id" placeholder="{{ "job.addedit.variables.name.placeholder" | trans }}">
<input type="text" name="var-value[0]" class="form-control var-value" placeholder="{{ "job.addedit.variables.value.placeholder" | trans }}">
</div>
</div>
<div class="vars-description mb-3 d-none">
<p>
{{ "job.addedit.variables.helptext" | trans }}
</p>
</div>
<div class="mb-3">
<a href="javascript:void(0);" class="btn btn-outline-primary addvar-btn">{{ "job.addedit.variables.add.label" | trans }}</a>
</div>
<input type="hidden" name="crontype" class="crontype" value="">
<input type="hidden" name="hosttype" class="hosttype" value="">
<input type="hidden" name="containertype" class="containertype" value="">
<button type="submit" class="btn btn-outline-primary">{{ "job.addedit.submit.label" | trans }}</button>
</form>
{% endblock %}

View File

@ -267,6 +267,7 @@
<input type="text" name="var-id[{{ key }}]" class="form-control var-id" placeholder="{{ "job.addedit.variables.name.placeholder" | trans }}" value="{{ id }}">
<input type="{% if var.issecret %}password{% else %}text{% endif %}" name="var-value[{{ key }}]" class="form-control var-value" placeholder="{{ "job.addedit.variables.value.placeholder" | trans }}" value="{% if var.issecret %}{{ var.value | decryptsecret }}{% else %}{{ var.value }}{% endif %}">
</div>
{% set key = key + 1 %}
{% endfor %}
{% endif %}
</div>

View File

@ -25,7 +25,7 @@
<td class="d-block d-md-table-cell align-middle">{{ job.interval | interval }}</td>
<td class="d-block d-md-table-cell align-middle">{{ job.nextrun | date("d/m/Y H:i:s") }}</td>
<td class="text-md-end d-block d-md-table-cell align-middle">
<a href="javascript:void(0);" data-href="{{ path('job_runnow', {'id': job.id}) }}" class="runnow btn btn-outline-{% if job.data.running == true %}success{% elseif job.data.norun == true %}danger{% else %}primary{% endif %}{% if job.data.running == true %} disabled{% endif %}"><i class="icon icon-run" aria-hidden="true"></i></a>
<a href="javascript:void(0);" data-nextrun="{{ job.nextrun | date("Y/m/d H:i:s") }}" data-href="{{ path('job_run', {'id': job.id}) }}" class="run btn btn-outline-{% if job.data.running == true %}success{% elseif job.data.norun == true %}danger{% else %}primary{% endif %}{% if job.data.running == true %} disabled{% endif %}"><i class="icon icon-run" aria-hidden="true"></i></a>
<a href="{{ path('job_view', {'id': job.id}) }}" class="btn btn-outline-{% if job.data.running == true %}success{% elseif job.data.norun == true %}danger{% else %}primary{% endif %}"><i class="icon icon-view" aria-hidden="true"></i></a>
<a href="{{ path('job_edit', {'id': job.id}) }}" class="btn btn-outline-{% if job.data.running == true %}success{% elseif job.data.norun == true %}danger{% else %}primary{% endif %}"><i class="icon icon-edit" aria-hidden="true"></i></a>
<a href="javascript:void(0);" data-confirmation="Are you sure you want to delete this job?" data-href="{{ path('job_delete', {'id': job.id}) }}" class="delete-btn btn btn-outline-{% if job.data.running == true %}success{% elseif job.data.norun == true %}danger{% else %}primary{% endif %}"><i class="icon icon-delete" aria-hidden="true"></i></a>
@ -34,7 +34,31 @@
{% endfor %}
</tbody>
</table>
<div class="modal fade" id="runnow_result" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal fade" id="run_selecttime" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">{{ "job.index.run.selecttime.header" | trans }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true"></span>
</button>
</div>
<div class="modal-body">
<p>{{ "job.index.run.selecttime.description" | trans }}</p>
<div class="d-flex flex-column align-items-center selecttimepickers">
<div id="selecttime_datepicker">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-primary schedule" data-bs-dismiss="modal">{{ "job.index.run.selecttime.btnschedule.label" | trans }}</button>
<button type="button" class="btn btn-outline-success run-now" data-bs-dismiss="modal">{{ "job.index.run.selecttime.btnrunnow.label" | trans }}</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="run_result" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
@ -47,7 +71,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-primary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-outline-primary" data-bs-dismiss="modal">{{ "job.index.run.ran.btnclose.label" | trans }}</button>
</div>
</div>
</div>
@ -55,10 +79,10 @@
{% endblock %}
{% block extrahtml %}
<div class="runnow-overlay d-none">
<div class="runnow-content">
<div class="run-overlay d-none">
<div class="run-content">
</div>
<div class="runnow-blur"></div>
<div class="run-blur"></div>
</div>
{% endblock %}

View File

@ -1,7 +1,8 @@
{% extends "base.html.twig" %}
{% block title %}{{ 'job.title' | trans({ '_jobname_': job.name }) }}{% endblock %}
{% block title %}{{ 'job.view.title' | trans({ '_jobname_': job.name }) }}{% endblock %}
{% block content %}
<h2>{{ 'job.view.header' | trans({ '_jobname_': (job.name | parsetags) }) | raw }}</h2>
<p class="text-muted small">{{ 'job.view.webhookurl' | trans }}: {{ url('webhook', {id: job.id, token: job.data('hooktoken') }) }}</p>
<p>
<a href="{{ path('job_edit', { id: job.id }) }}">{{ 'job.view.edit' | trans }}</a>
{% if allruns %} | <a href="{{ path('job_view', { id: job.id })}}">{{ 'job.view.show.onlyfailed' | trans }}</a>
@ -18,6 +19,8 @@
<div class="d-md-inline d-block text-left">({{ "job.view.results.exitcode"| trans }}: {{ run.exitcode }} | {{ "job.view.results.runtime"| trans }}: {{ run.runtime | interval }})</div>
{% if 'M' in run.flags %}
<div class="d-md-inline d-block text-left">{{ "job.view.results.manual"| trans }}</div>
{% elseif 'T' in run.flags %}
<div class="d-md-inline d-block text-left">{{ "job.view.results.webhook"| trans }}</div>
{% endif %}
</div>
</button>

View File

@ -161,26 +161,6 @@
<source>job.index.table.headers.nextrun</source>
<target>Next run</target>
</trans-unit>
<trans-unit id="n9XSrh3" resname="job.index.runnow.deferred.title">
<source>job.index.runnow.deferred.title</source>
<target>Cronjob has been scheduled</target>
</trans-unit>
<trans-unit id="2QVNoEx" resname="job.index.runnow.deferred.message">
<source>job.index.runnow.deferred.message</source>
<target>Job was scheduled to be run. You will find the output soon in the job details</target>
</trans-unit>
<trans-unit id="_9vL1xO" resname="job.index.runnow.ran.title.success">
<source>job.index.runnow.ran.title.success</source>
<target>Cronjob succesfully ran</target>
</trans-unit>
<trans-unit id="SH3oarr" resname="job.index.runnow.ran.title.failed">
<source>job.index.runnow.ran.title.failed</source>
<target>Cronjob failed. Please check output below</target>
</trans-unit>
<trans-unit id="az3XJT4" resname="job.index.runnow.ran.message">
<source>job.index.runnow.ran.message</source>
<target>Cronjob ran in _runtime_ seconds with exit code _exitcode_</target>
</trans-unit>
<trans-unit id="mokjk0L" resname="job.index.flashes.jobdeleted">
<source>job.index.flashes.jobdeleted</source>
<target>Cronjob is successfully deleted</target>
@ -613,6 +593,54 @@
<source>footer.source</source>
<target>Source</target>
</trans-unit>
<trans-unit id="X.uZ4TL" resname="job.index.run.selecttime.header">
<source>job.index.run.selecttime.header</source>
<target>When to run this job?</target>
</trans-unit>
<trans-unit id="i4An5BC" resname="job.index.run.selecttime.description">
<source>job.index.run.selecttime.description</source>
<target>Please select the time to run this job</target>
</trans-unit>
<trans-unit id="tav8v0S" resname="job.index.run.selecttime.btnschedule.label">
<source>job.index.run.selecttime.btnschedule.label</source>
<target>Schedule</target>
</trans-unit>
<trans-unit id="St.ceTi" resname="job.index.run.selecttime.btnrunnow.label">
<source>job.index.run.selecttime.btnrunnow.label</source>
<target>Run now</target>
</trans-unit>
<trans-unit id="XAxpenS" resname="job.index.run.ran.btnclose.label">
<source>job.index.run.ran.btnclose.label</source>
<target>Close</target>
</trans-unit>
<trans-unit id="QH_iTBI" resname="job.index.run.deferred.title">
<source>job.index.run.deferred.title</source>
<target>Cronjob has been scheduled</target>
</trans-unit>
<trans-unit id="QqC.rDo" resname="job.index.run.deferred.message">
<source>job.index.run.deferred.message</source>
<target>Job was scheduled to be run. You will find the output soon in the job details</target>
</trans-unit>
<trans-unit id="0DypXnU" resname="job.index.run.ran.title.success">
<source>job.index.run.ran.title.success</source>
<target>Cronjob succesfully ran</target>
</trans-unit>
<trans-unit id="7wkhcjy" resname="job.index.run.ran.title.failed">
<source>job.index.run.ran.title.failed</source>
<target>Cronjob failed. Please check output below</target>
</trans-unit>
<trans-unit id="W_dAs4D" resname="job.index.run.ran.message">
<source>job.index.run.ran.message</source>
<target>Cronjob ran in _runtime_ seconds with exit code _exitcode_</target>
</trans-unit>
<trans-unit id="2mVlLNJ" resname="job.view.webhookurl">
<source>job.view.webhookurl</source>
<target>Webhook URL</target>
</trans-unit>
<trans-unit id="aoV2sV4" resname="job.view.results.webhook">
<source>job.view.results.webhook</source>
<target>Triggered run</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -161,26 +161,6 @@
<source>job.index.table.headers.nextrun</source>
<target state="final">N3xtrun</target>
</trans-unit>
<trans-unit id="n9XSrh3" resname="job.index.runnow.deferred.title">
<source>job.index.runnow.deferred.title</source>
<target state="final">Cr0nj0b h4z b33n sch3duL3d</target>
</trans-unit>
<trans-unit id="2QVNoEx" resname="job.index.runnow.deferred.message">
<source>job.index.runnow.deferred.message</source>
<target state="final">J0b wuz sch3duL3d 2 b run. j00 w1LL f1nd t3h 0utput s00n 1n t3h j0b d3t41Lz</target>
</trans-unit>
<trans-unit id="_9vL1xO" resname="job.index.runnow.ran.title.success">
<source>job.index.runnow.ran.title.success</source>
<target state="final">Cr0nj0b succ3sfuLLy r4n</target>
</trans-unit>
<trans-unit id="SH3oarr" resname="job.index.runnow.ran.title.failed">
<source>job.index.runnow.ran.title.failed</source>
<target state="final">Cr0nj0b f41L3d. PL34s3 ch3ck 0utput b3L0w</target>
</trans-unit>
<trans-unit id="az3XJT4" resname="job.index.runnow.ran.message">
<source>job.index.runnow.ran.message</source>
<target state="final">Cr0nj0b r4n 1n _runtime_ s3c0ndz w1th 3x1t c0d3 _exitcode_</target>
</trans-unit>
<trans-unit id="mokjk0L" resname="job.index.flashes.jobdeleted">
<source>job.index.flashes.jobdeleted</source>
<target state="final">Cr0nj0b b succ3ssfuLLy d3L3t3d</target>
@ -403,7 +383,7 @@
</trans-unit>
<trans-unit id="f38i.f6" resname="job.addedit.crontype.reboot.reboot.delay.placeholder">
<source>job.addedit.crontype.reboot.reboot.delay.placeholder</source>
<target state="final">5</target>
<target state="final">0</target>
</trans-unit>
<trans-unit id="T55_j9D" resname="job.addedit.crontype.reboot.reboot.delay.helptext">
<source>job.addedit.crontype.reboot.reboot.delay.helptext</source>
@ -459,7 +439,7 @@
</trans-unit>
<trans-unit id="9d3Trxr" resname="job.addedit.crontype.http.basic-auth.username.placeholder">
<source>job.addedit.crontype.http.basic-auth.username.placeholder</source>
<target state="final">www-data</target>
<target state="final">root</target>
</trans-unit>
<trans-unit id="27zbUzr" resname="job.addedit.crontype.http.basic-auth.password.label">
<source>job.addedit.crontype.http.basic-auth.password.label</source>
@ -613,6 +593,54 @@
<source>footer.source</source>
<target state="final">S0urc3</target>
</trans-unit>
<trans-unit id="X.uZ4TL" resname="job.index.run.selecttime.header">
<source>job.index.run.selecttime.header</source>
<target state="final">Wh3n 2 run d1z j0b?</target>
</trans-unit>
<trans-unit id="i4An5BC" resname="job.index.run.selecttime.description">
<source>job.index.run.selecttime.description</source>
<target state="final">PL34s3 s3L3ct t3h t1m3 2 run d1z j0b</target>
</trans-unit>
<trans-unit id="tav8v0S" resname="job.index.run.selecttime.btnschedule.label">
<source>job.index.run.selecttime.btnschedule.label</source>
<target state="final">Sch3duL3</target>
</trans-unit>
<trans-unit id="St.ceTi" resname="job.index.run.selecttime.btnrunnow.label">
<source>job.index.run.selecttime.btnrunnow.label</source>
<target state="final">Run n0w</target>
</trans-unit>
<trans-unit id="XAxpenS" resname="job.index.run.ran.btnclose.label">
<source>job.index.run.ran.btnclose.label</source>
<target state="final">CL0s3</target>
</trans-unit>
<trans-unit id="QH_iTBI" resname="job.index.run.deferred.title">
<source>job.index.run.deferred.title</source>
<target state="final">Cr0nj0b h4z b33n sch3duL3d</target>
</trans-unit>
<trans-unit id="QqC.rDo" resname="job.index.run.deferred.message">
<source>job.index.run.deferred.message</source>
<target state="final">J0b wuz sch3duL3d 2 b run. j00 w1LL f1nd t3h 0utput s00n 1n t3h j0b d3t41Lz</target>
</trans-unit>
<trans-unit id="0DypXnU" resname="job.index.run.ran.title.success">
<source>job.index.run.ran.title.success</source>
<target state="final">Cr0nj0b succ3sfuLLy r4n</target>
</trans-unit>
<trans-unit id="7wkhcjy" resname="job.index.run.ran.title.failed">
<source>job.index.run.ran.title.failed</source>
<target state="final">Cr0nj0b f41L3d. PL34s3 ch3ck 0utput b3L0w</target>
</trans-unit>
<trans-unit id="W_dAs4D" resname="job.index.run.ran.message">
<source>job.index.run.ran.message</source>
<target state="final">Cr0nj0b r4n 1n _runtime_ s3c0ndz w1th 3x1t c0d3 _exitcode_</target>
</trans-unit>
<trans-unit id="2mVlLNJ" resname="job.view.webhookurl">
<source>job.view.webhookurl</source>
<target state="final">W3bh00k URL</target>
</trans-unit>
<trans-unit id="aoV2sV4" resname="job.view.results.webhook">
<source>job.view.results.webhook</source>
<target state="final">Tr1gg3r3d run</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -1,138 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="lol" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="qvIyBkY" resname="title">
<source>title</source>
<target state="final">webcROn managemnt</target>
</trans-unit>
<trans-unit id="HgWEol2" resname="header">
<source>header</source>
<target state="final">webcROn managemnt</target>
</trans-unit>
<trans-unit id="Q54TzT5" resname="demomode.flashnotice">
<source>demomode.flashnotice</source>
<target state="final">tHIZ ApplicashuN R in deEMo mode?? changez iz persisTd in tEH dATabase but jobz iz not bean executd k?</target>
</trans-unit>
<trans-unit id="rpq61qN" resname="demomode.credentials.header">
<source>demomode.credentials.header</source>
<target state="final">loGIN fr DEEmo mode</target>
</trans-unit>
<trans-unit id="jUz9fVE" resname="demomode.credentials.username">
<source>demomode.credentials.username</source>
<target state="final">oH hi username</target>
</trans-unit>
<trans-unit id="QNu97w_" resname="demomode.credentials.password">
<source>demomode.credentials.password</source>
<target state="final">oh hi paswORD k?</target>
</trans-unit>
<trans-unit id="U8IBiON" resname="menu.overview">
<source>menu.overview</source>
<target state="final">ovrview plz?</target>
</trans-unit>
<trans-unit id="iGC7eCk" resname="menu.add">
<source>menu.add</source>
<target state="final">ADD new cRONJob plz?</target>
</trans-unit>
<trans-unit id="4UTsL.X" resname="menu.settings">
<source>menu.settings</source>
<target state="final">settingz</target>
</trans-unit>
<trans-unit id="wBTFKFR" resname="menu.logout">
<source>menu.logout</source>
<target state="final">oh hi logowt plz?</target>
</trans-unit>
<trans-unit id="4cxyGOO" resname="security.login.username.label">
<source>security.login.username.label</source>
<target state="final">oH hi username</target>
</trans-unit>
<trans-unit id="u2tKtI1" resname="security.login.username.placeholder">
<source>security.login.username.placeholder</source>
<target state="final">jeroen@example.com</target>
</trans-unit>
<trans-unit id="Niy_SZ2" resname="security.login.password.label">
<source>security.login.password.label</source>
<target state="final">oh hi paswORD k?</target>
</trans-unit>
<trans-unit id="3rU2_6v" resname="security.login.password.placeholder">
<source>security.login.password.placeholder</source>
<target state="final">burgerz plz?</target>
</trans-unit>
<trans-unit id="0MGdvgf" resname="security.login.remember.label">
<source>security.login.remember.label</source>
<target state="final">rememBr me</target>
</trans-unit>
<trans-unit id="4GvZndQ" resname="security.login.submit-btn.label">
<source>security.login.submit-btn.label</source>
<target state="final">login</target>
</trans-unit>
<trans-unit id="W.225Bj" resname="settings.title">
<source>settings.title</source>
<target state="final">settingz</target>
</trans-unit>
<trans-unit id="a1bm5YC" resname="settings.header">
<source>settings.header</source>
<target state="final">settingz</target>
</trans-unit>
<trans-unit id="qUrJMfo" resname="settings.flashes.inexistinglocale">
<source>settings.flashes.inexistinglocale</source>
<target state="final">oh hi Language dus not EXIs plz?</target>
</trans-unit>
<trans-unit id="XcSnV8X" resname="settings.flashes.localesaved">
<source>settings.flashes.localesaved</source>
<target state="final">LAnguage r savd k?</target>
</trans-unit>
<trans-unit id="kpbry92" resname="settings.flashes.repeatpasswordnotok">
<source>settings.flashes.repeatpasswordnotok</source>
<target state="final">oh hi PASwordz iz not Ekwel</target>
</trans-unit>
<trans-unit id="DpJMsln" resname="settings.flashes.currentpassnotok">
<source>settings.flashes.currentpassnotok</source>
<target state="final">paswordz r not correcT Plz?</target>
</trans-unit>
<trans-unit id="tB3o6UA" resname="settings.flashes.passwordsaved">
<source>settings.flashes.passwordsaved</source>
<target state="final">paSWORd r SAVD</target>
</trans-unit>
<trans-unit id="aAt4FLV" resname="settings.password.header">
<source>settings.password.header</source>
<target state="final">oh hi paswORD k?</target>
</trans-unit>
<trans-unit id="qqAzx9b" resname="settings.password.current.label">
<source>settings.password.current.label</source>
<target state="final">currnt paswoRD</target>
</trans-unit>
<trans-unit id="rr8bU5t" resname="settings.password.current.placeholder">
<source>settings.password.current.placeholder</source>
<target state="final">burgerz plz?</target>
</trans-unit>
<trans-unit id="YVhGwqr" resname="settings.password.password.label">
<source>settings.password.password.label</source>
<target state="final">new pasword</target>
</trans-unit>
<trans-unit id="OZpjE9T" resname="settings.password.password.placeholder">
<source>settings.password.password.placeholder</source>
<target state="final">MOAr paswoRD</target>
</trans-unit>
<trans-unit id="TNBtpD_" resname="settings.password.repeat.label">
<source>settings.password.repeat.label</source>
<target state="final">Repeet pasword plz?</target>
</trans-unit>
<trans-unit id="bYb7Tz." resname="settings.password.repeat.placeholder">
<source>settings.password.repeat.placeholder</source>
<target state="final">MOAr paswoRD</target>
</trans-unit>
<trans-unit id="_OGkxC8" resname="settings.other.header">
<source>settings.other.header</source>
<target state="final">othr settingz plz?</target>
</trans-unit>
<trans-unit id="78Yswpr" resname="settings.other.locale.label">
<source>settings.other.locale.label</source>
<target state="final">languagE</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -161,26 +161,6 @@
<source>job.index.table.headers.nextrun</source>
<target state="final">Volgende uitvoering</target>
</trans-unit>
<trans-unit id="n9XSrh3" resname="job.index.runnow.deferred.title">
<source>job.index.runnow.deferred.title</source>
<target state="final">Taak werd doorgestuurd naar de daemon</target>
</trans-unit>
<trans-unit id="2QVNoEx" resname="job.index.runnow.deferred.message">
<source>job.index.runnow.deferred.message</source>
<target state="final">Taak werd doorgestuurd naar de daemon. De output vind u binnenkort in de taakdetails</target>
</trans-unit>
<trans-unit id="_9vL1xO" resname="job.index.runnow.ran.title.success">
<source>job.index.runnow.ran.title.success</source>
<target state="final">Taak werd succesvol uitgevoerd</target>
</trans-unit>
<trans-unit id="SH3oarr" resname="job.index.runnow.ran.title.failed">
<source>job.index.runnow.ran.title.failed</source>
<target state="final">Taak faalde. Hieronder vind u de output ter controle</target>
</trans-unit>
<trans-unit id="az3XJT4" resname="job.index.runnow.ran.message">
<source>job.index.runnow.ran.message</source>
<target state="final">Taak werd in _runtime_ seconden uitgevoerd met resultaat _exitcode_</target>
</trans-unit>
<trans-unit id="mokjk0L" resname="job.index.flashes.jobdeleted">
<source>job.index.flashes.jobdeleted</source>
<target state="final">Taak werd succesvol verwijderd</target>
@ -613,6 +593,54 @@
<source>footer.source</source>
<target state="final">Broncode</target>
</trans-unit>
<trans-unit id="X.uZ4TL" resname="job.index.run.selecttime.header">
<source>job.index.run.selecttime.header</source>
<target state="final">Wanneer moet deze taak uitgevoerd worden?</target>
</trans-unit>
<trans-unit id="i4An5BC" resname="job.index.run.selecttime.description">
<source>job.index.run.selecttime.description</source>
<target state="final">Gelieve de datum en tijd te selecteren om deze taak uit te voeren</target>
</trans-unit>
<trans-unit id="tav8v0S" resname="job.index.run.selecttime.btnschedule.label">
<source>job.index.run.selecttime.btnschedule.label</source>
<target state="final">Plan</target>
</trans-unit>
<trans-unit id="St.ceTi" resname="job.index.run.selecttime.btnrunnow.label">
<source>job.index.run.selecttime.btnrunnow.label</source>
<target state="final">Nu uitvoeren</target>
</trans-unit>
<trans-unit id="XAxpenS" resname="job.index.run.ran.btnclose.label">
<source>job.index.run.ran.btnclose.label</source>
<target state="final">Sluiten</target>
</trans-unit>
<trans-unit id="QH_iTBI" resname="job.index.run.deferred.title">
<source>job.index.run.deferred.title</source>
<target state="final">Taak werd doorgestuurd naar de daemon</target>
</trans-unit>
<trans-unit id="QqC.rDo" resname="job.index.run.deferred.message">
<source>job.index.run.deferred.message</source>
<target state="final">Taak werd doorgestuurd naar de daemon. De output vind u binnenkort in de taakdetails</target>
</trans-unit>
<trans-unit id="0DypXnU" resname="job.index.run.ran.title.success">
<source>job.index.run.ran.title.success</source>
<target state="final">Taak werd succesvol uitgevoerd</target>
</trans-unit>
<trans-unit id="7wkhcjy" resname="job.index.run.ran.title.failed">
<source>job.index.run.ran.title.failed</source>
<target state="final">Taak faalde. Hieronder vind u de output ter controle</target>
</trans-unit>
<trans-unit id="W_dAs4D" resname="job.index.run.ran.message">
<source>job.index.run.ran.message</source>
<target state="final">Taak werd in _runtime_ seconden uitgevoerd met resultaat _exitcode_</target>
</trans-unit>
<trans-unit id="2mVlLNJ" resname="job.view.webhookurl">
<source>job.view.webhookurl</source>
<target state="final">Webhook-URL</target>
</trans-unit>
<trans-unit id="aoV2sV4" resname="job.view.results.webhook">
<source>job.view.results.webhook</source>
<target state="final">Getriggerde uitvoering</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="en" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="baI_ZxO" resname="An authentication exception occurred.">
<source>An authentication exception occurred.</source>
<target>An authentication exception occurred.</target>
</trans-unit>
<trans-unit id="OETylMq" resname="Authentication credentials could not be found.">
<source>Authentication credentials could not be found.</source>
<target>Authentication credentials could not be found.</target>
</trans-unit>
<trans-unit id="3RJINQ0" resname="Authentication request could not be processed due to a system problem.">
<source>Authentication request could not be processed due to a system problem.</source>
<target>Authentication request could not be processed due to a system problem.</target>
</trans-unit>
<trans-unit id="qr0aiUo" resname="Invalid credentials.">
<source>Invalid credentials.</source>
<target>Invalid credentials.</target>
</trans-unit>
<trans-unit id="zrJWK0F" resname="Cookie has already been used by someone else.">
<source>Cookie has already been used by someone else.</source>
<target>Cookie has already been used by someone else.</target>
</trans-unit>
<trans-unit id="blC0fXX" resname="Not privileged to request the resource.">
<source>Not privileged to request the resource.</source>
<target>Not privileged to request the resource.</target>
</trans-unit>
<trans-unit id="dLzMRPR" resname="Invalid CSRF token.">
<source>Invalid CSRF token.</source>
<target>Invalid CSRF token.</target>
</trans-unit>
<trans-unit id="PhhlLem" resname="No authentication provider found to support the authentication token.">
<source>No authentication provider found to support the authentication token.</source>
<target>No authentication provider found to support the authentication token.</target>
</trans-unit>
<trans-unit id="v_RS21A" resname="No session available, it either timed out or cookies are not enabled.">
<source>No session available, it either timed out or cookies are not enabled.</source>
<target>No session available, it either timed out or cookies are not enabled.</target>
</trans-unit>
<trans-unit id="EYCKpDH" resname="No token could be found.">
<source>No token could be found.</source>
<target>No token could be found.</target>
</trans-unit>
<trans-unit id="z3cOUZo" resname="Username could not be found.">
<source>Username could not be found.</source>
<target>Username could not be found.</target>
</trans-unit>
<trans-unit id="By5eLYM" resname="Account has expired.">
<source>Account has expired.</source>
<target>Account has expired.</target>
</trans-unit>
<trans-unit id="YfZhiuA" resname="Credentials have expired.">
<source>Credentials have expired.</source>
<target>Credentials have expired.</target>
</trans-unit>
<trans-unit id="NrSSfLs" resname="Account is disabled.">
<source>Account is disabled.</source>
<target>Account is disabled.</target>
</trans-unit>
<trans-unit id="O5ZyxHr" resname="Account is locked.">
<source>Account is locked.</source>
<target>Account is locked.</target>
</trans-unit>
<trans-unit id="gd.MOnZ" resname="Too many failed login attempts, please try again later.">
<source>Too many failed login attempts, please try again later.</source>
<target>Too many failed login attempts, please try again later.</target>
</trans-unit>
<trans-unit id="l9VYRj0" resname="Invalid or expired login link.">
<source>Invalid or expired login link.</source>
<target>Invalid or expired login link.</target>
</trans-unit>
<trans-unit id="9qGC3hG" resname="Too many failed login attempts, please try again in %minutes% minute.">
<source>Too many failed login attempts, please try again in %minutes% minute.</source>
<target>Too many failed login attempts, please try again in %minutes% minute.</target>
</trans-unit>
<trans-unit id="AJR3lMs" resname="Too many failed login attempts, please try again in %minutes% minutes.">
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
<target>Too many failed login attempts, please try again in %minutes% minutes.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="fr" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="baI_ZxO" resname="An authentication exception occurred.">
<source>An authentication exception occurred.</source>
<target>Une exception d'authentification s'est produite.</target>
</trans-unit>
<trans-unit id="OETylMq" resname="Authentication credentials could not be found.">
<source>Authentication credentials could not be found.</source>
<target>Les identifiants d'authentification n'ont pas pu être trouvés.</target>
</trans-unit>
<trans-unit id="3RJINQ0" resname="Authentication request could not be processed due to a system problem.">
<source>Authentication request could not be processed due to a system problem.</source>
<target>La requête d'authentification n'a pas pu être executée à cause d'un problème système.</target>
</trans-unit>
<trans-unit id="qr0aiUo" resname="Invalid credentials.">
<source>Invalid credentials.</source>
<target>Identifiants invalides.</target>
</trans-unit>
<trans-unit id="zrJWK0F" resname="Cookie has already been used by someone else.">
<source>Cookie has already been used by someone else.</source>
<target>Le cookie a déjà été utilisé par quelqu'un d'autre.</target>
</trans-unit>
<trans-unit id="blC0fXX" resname="Not privileged to request the resource.">
<source>Not privileged to request the resource.</source>
<target>Privilèges insuffisants pour accéder à la ressource.</target>
</trans-unit>
<trans-unit id="dLzMRPR" resname="Invalid CSRF token.">
<source>Invalid CSRF token.</source>
<target>Jeton CSRF invalide.</target>
</trans-unit>
<trans-unit id="PhhlLem" resname="No authentication provider found to support the authentication token.">
<source>No authentication provider found to support the authentication token.</source>
<target>Aucun fournisseur d'authentification n'a été trouvé pour supporter le jeton d'authentification.</target>
</trans-unit>
<trans-unit id="v_RS21A" resname="No session available, it either timed out or cookies are not enabled.">
<source>No session available, it either timed out or cookies are not enabled.</source>
<target>Aucune session disponible, celle-ci a expiré ou les cookies ne sont pas activés.</target>
</trans-unit>
<trans-unit id="EYCKpDH" resname="No token could be found.">
<source>No token could be found.</source>
<target>Aucun jeton n'a pu être trouvé.</target>
</trans-unit>
<trans-unit id="z3cOUZo" resname="Username could not be found.">
<source>Username could not be found.</source>
<target>Le nom d'utilisateur n'a pas pu être trouvé.</target>
</trans-unit>
<trans-unit id="By5eLYM" resname="Account has expired.">
<source>Account has expired.</source>
<target>Le compte a expiré.</target>
</trans-unit>
<trans-unit id="YfZhiuA" resname="Credentials have expired.">
<source>Credentials have expired.</source>
<target>Les identifiants ont expiré.</target>
</trans-unit>
<trans-unit id="NrSSfLs" resname="Account is disabled.">
<source>Account is disabled.</source>
<target>Le compte est désactivé.</target>
</trans-unit>
<trans-unit id="O5ZyxHr" resname="Account is locked.">
<source>Account is locked.</source>
<target>Le compte est bloqué.</target>
</trans-unit>
<trans-unit id="gd.MOnZ" resname="Too many failed login attempts, please try again later.">
<source>Too many failed login attempts, please try again later.</source>
<target>Plusieurs tentatives de connexion ont échoué, veuillez réessayer plus tard.</target>
</trans-unit>
<trans-unit id="l9VYRj0" resname="Invalid or expired login link.">
<source>Invalid or expired login link.</source>
<target>Lien de connexion invalide ou expiré.</target>
</trans-unit>
<trans-unit id="9qGC3hG" resname="Too many failed login attempts, please try again in %minutes% minute.">
<source>Too many failed login attempts, please try again in %minutes% minute.</source>
<target>Plusieurs tentatives de connexion ont échoué, veuillez réessayer dans %minutes% minute.</target>
</trans-unit>
<trans-unit id="AJR3lMs" resname="Too many failed login attempts, please try again in %minutes% minutes.">
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
<target>Plusieurs tentatives de connexion ont échoué, veuillez réessayer dans %minutes% minutes.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="nl" datatype="plaintext" original="file.ext">
<header>
<tool tool-id="symfony" tool-name="Symfony"/>
</header>
<body>
<trans-unit id="baI_ZxO" resname="An authentication exception occurred.">
<source>An authentication exception occurred.</source>
<target>Er heeft zich een authenticatieprobleem voorgedaan.</target>
</trans-unit>
<trans-unit id="OETylMq" resname="Authentication credentials could not be found.">
<source>Authentication credentials could not be found.</source>
<target>Authenticatiegegevens konden niet worden gevonden.</target>
</trans-unit>
<trans-unit id="3RJINQ0" resname="Authentication request could not be processed due to a system problem.">
<source>Authentication request could not be processed due to a system problem.</source>
<target>Authenticatieaanvraag kon niet worden verwerkt door een technisch probleem.</target>
</trans-unit>
<trans-unit id="qr0aiUo" resname="Invalid credentials.">
<source>Invalid credentials.</source>
<target>Ongeldige inloggegevens.</target>
</trans-unit>
<trans-unit id="zrJWK0F" resname="Cookie has already been used by someone else.">
<source>Cookie has already been used by someone else.</source>
<target>Cookie is al door een ander persoon gebruikt.</target>
</trans-unit>
<trans-unit id="blC0fXX" resname="Not privileged to request the resource.">
<source>Not privileged to request the resource.</source>
<target>Onvoldoende rechten om de aanvraag te verwerken.</target>
</trans-unit>
<trans-unit id="dLzMRPR" resname="Invalid CSRF token.">
<source>Invalid CSRF token.</source>
<target>CSRF-code is ongeldig.</target>
</trans-unit>
<trans-unit id="PhhlLem" resname="No authentication provider found to support the authentication token.">
<source>No authentication provider found to support the authentication token.</source>
<target>Geen authenticatieprovider gevonden die de authenticatietoken ondersteunt.</target>
</trans-unit>
<trans-unit id="v_RS21A" resname="No session available, it either timed out or cookies are not enabled.">
<source>No session available, it either timed out or cookies are not enabled.</source>
<target>Geen sessie beschikbaar, mogelijk is deze verlopen of cookies zijn uitgeschakeld.</target>
</trans-unit>
<trans-unit id="EYCKpDH" resname="No token could be found.">
<source>No token could be found.</source>
<target>Er kon geen authenticatietoken worden gevonden.</target>
</trans-unit>
<trans-unit id="z3cOUZo" resname="Username could not be found.">
<source>Username could not be found.</source>
<target>Gebruikersnaam kon niet worden gevonden.</target>
</trans-unit>
<trans-unit id="By5eLYM" resname="Account has expired.">
<source>Account has expired.</source>
<target>Account is verlopen.</target>
</trans-unit>
<trans-unit id="YfZhiuA" resname="Credentials have expired.">
<source>Credentials have expired.</source>
<target>Authenticatiegegevens zijn verlopen.</target>
</trans-unit>
<trans-unit id="NrSSfLs" resname="Account is disabled.">
<source>Account is disabled.</source>
<target>Account is gedeactiveerd.</target>
</trans-unit>
<trans-unit id="O5ZyxHr" resname="Account is locked.">
<source>Account is locked.</source>
<target>Account is geblokkeerd.</target>
</trans-unit>
<trans-unit id="gd.MOnZ" resname="Too many failed login attempts, please try again later.">
<source>Too many failed login attempts, please try again later.</source>
<target>Te veel onjuiste inlogpogingen, probeer het later nogmaals.</target>
</trans-unit>
<trans-unit id="l9VYRj0" resname="Invalid or expired login link.">
<source>Invalid or expired login link.</source>
<target>Ongeldige of verlopen inloglink.</target>
</trans-unit>
<trans-unit id="9qGC3hG" resname="Too many failed login attempts, please try again in %minutes% minute.">
<source>Too many failed login attempts, please try again in %minutes% minute.</source>
<target>Te veel onjuiste inlogpogingen, probeer het opnieuw over %minutes% minuut.</target>
</trans-unit>
<trans-unit id="AJR3lMs" resname="Too many failed login attempts, please try again in %minutes% minutes.">
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
<target>Te veel onjuiste inlogpogingen, probeer het opnieuw over %minutes% minuten.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -1 +1 @@
v1.1-dev
v1.2-dev

View File

@ -11,7 +11,7 @@ Encore
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
// only needed for CDN's or subdirectory deploy
//.setManifestKeyPrefix('build/')
/*
@ -47,14 +47,15 @@ Encore
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('@babel/plugin-proposal-class-properties');
})
// configure Babel
// .configureBabel((config) => {
// config.plugins.push('@babel/a-babel-plugin');
// })
// enables @babel/preset-env polyfills
// enables and configure @babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
config.corejs = '3.23';
})
// enables Sass/SCSS support