Compare commits

...

72 Commits
v1.0 ... main

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
Jeroen De Meerleer 3cec0bd074
UPDATED DEPENDENCIES 2022-12-12 14:47:48 +01:00
Jeroen De Meerleer 8a5eb92897
UPDATED DEPENDENCIES 2022-11-18 14:39:16 +01:00
Jeroen De Meerleer 4e9e9c8d0e
Updated dependencies 2022-11-10 11:39:28 +01:00
Jeroen De Meerleer c57f061805
Added changelog 2022-11-10 11:34:02 +01:00
Jeroen De Meerleer cfab6abbbd
UPDATED DEPENDENCIES 2022-11-08 16:31:47 +01:00
Jeroen De Meerleer 8dfe1118b3
ENHANCEMENT: added return types 2022-10-04 13:29:27 +02:00
Jeroen De Meerleer 66bc095b81
updated translations 2022-10-04 13:19:18 +02:00
Jeroen De Meerleer 24da51ffd8
BUGFIX: resolved deprecations 2022-10-04 12:17:28 +02:00
Jeroen De Meerleer 3c2e2e2a03
Merge pull request #16 from JeroenED/dependabot/composer/twig/twig-3.4.3
Build(deps): Bump twig/twig from 3.4.2 to 3.4.3
2022-10-03 07:39:46 +02:00
dependabot[bot] 8d70d8fb96
Build(deps): Bump twig/twig from 3.4.2 to 3.4.3
Bumps [twig/twig](https://github.com/twigphp/Twig) from 3.4.2 to 3.4.3.
- [Release notes](https://github.com/twigphp/Twig/releases)
- [Changelog](https://github.com/twigphp/Twig/blob/3.x/CHANGELOG)
- [Commits](https://github.com/twigphp/Twig/compare/v3.4.2...v3.4.3)

---
updated-dependencies:
- dependency-name: twig/twig
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 05:39:13 +00:00
Jeroen De Meerleer 3a48fd7e52
BUGFIX: when querying the database pointer could be reused 2022-09-27 12:26:20 +02:00
Jeroen De Meerleer 3a95d23d8b
BUGFIX: We need to sleep at least 1 second or we get doubles 2022-09-26 14:16:35 +02:00
Jeroen De Meerleer 159ec33a22
BUGFIX: also remove temp-vars when not manual 2022-09-26 13:32:00 +02:00
Jeroen De Meerleer 53424c50bf
ENHANCEMENT: daemon cannot be checked if run in a different container 2022-09-21 16:33:29 +02:00
Jeroen De Meerleer 9fea685e86
ENHANCEMENT: translating with crowdin 2022-09-16 12:37:07 +02:00
Jeroen De Meerleer 15031f3ff3
BUGFIX: let it work behind a proxy 2022-09-12 15:22:10 +02:00
Jeroen De Meerleer fc03b2f731
BUGFIX: using incorrect translation strings 2022-09-12 12:19:09 +02:00
Jeroen De Meerleer 56bc06e6f0
NEW FEATURE: settings page 2022-09-08 12:28:38 +02:00
Jeroen De Meerleer dc09ba2275
BUGFIX: flashes were not translated 2022-09-07 17:41:12 +02:00
Jeroen De Meerleer d24b84efdf
BUGFIX: relocating runnow translations 2022-09-07 17:27:17 +02:00
Jeroen De Meerleer 1f6a7b57bb
ENHANCEMENT: Saving locale in database 2022-09-07 15:20:28 +02:00
Jeroen De Meerleer e0f5cae8f6
ENHANCEMENT: Adding recipients to mailfailedruns command 2022-09-07 12:52:44 +02:00
Jeroen De Meerleer 4d1909ea59
ENHANCEMENT: updated create user command 2022-09-07 12:50:27 +02:00
Jeroen De Meerleer 0cbcc8308a
ENHANCEMENT: Using buildno in migration 2022-09-07 12:49:44 +02:00
Jeroen De Meerleer abc79023bd
Updated readme 2022-09-07 12:09:49 +02:00
Jeroen De Meerleer dd18843c12
ENHANCEMENT: using doctrine migrations for deployment 2022-09-07 12:07:18 +02:00
Jeroen De Meerleer ba326f4e51
BUGFIX: footer was not correctly responsive 2022-09-07 09:22:17 +02:00
Jeroen De Meerleer 6517c78052
BUGFIX: don't scroll up on selecting details 2022-09-06 17:36:39 +02:00
Jeroen De Meerleer e787007822
BUGFIX: Job details was not translated 2022-09-06 13:45:32 +02:00
Jeroen De Meerleer fc51167c04
ENHANCEMENT: Setting version tag in footer 2022-09-06 13:45:32 +02:00
Jeroen De Meerleer 98b4ce1c94
UPDATED DEPENDENCIES 2022-09-06 13:42:31 +02:00
Jeroen De Meerleer c039004d62
UPDATED README 2022-09-05 16:13:31 +02:00
Jeroen De Meerleer 92ce356af7
UPDATED DEPENDENCIES 2022-09-05 16:13:01 +02:00
Jeroen De Meerleer 1312b7c81c
NEW FEATURE: added version tag 2022-09-05 15:09:12 +02:00
72 changed files with 8463 additions and 10636 deletions

28
CHANGELOG.md Normal file
View File

@ -0,0 +1,28 @@
# Changelog
## Version 1.2
### New
* Added timed scheduled runs
## Version 1.1
### New
* User settings page. Eliminating the need to call the helpline for changing your password or language
* Version tag in footer
### Changed
* Docker images are following the docker philosophy (2 containers providing 1 functionality each. Although you can still use the fat image)
* Translations can be contributed via [crowdin](https://crowdin.com/project/webcron-management)
* Data migrations are done using doctrine migrations
* 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
* Trusted proxies were not parsed
* When running in a different container the health of the daemon could not be checked
## Version 1.0
(Initial release)

View File

@ -3,54 +3,56 @@
Webcron management is an easy-to-use interface to manage cronjobs running on a publicly available http-location.
### Known bugs
* Datepicker ([Tempus dominus v6](https://getdatepicker.com/)) is currently alpha-quality software. Altough [the dev states it is usable](https://jonathanpeterson.com/posts/state-of-my-datetime-picker-part-2.html)
## Deploying
### Requirements for web-server
* php <= 8.0
* ext-openssl
* ext-pcntl (highly recommended)
* MySQL/MariaDB
* Ability to change the webroot directory
* Ability to run a script as daemon (eg. supervisor or systemd units)
## Building
### Requirements for build-server
* php <= 8.0 (incl composer <= 2)
* NodeJS <= 14.0 (incl. npm <= 7)
* php <= 8.1 (incl composer <= 2, ext-pcntl, ext-openssl, ext-intl)
* NodeJS <= 16.0 (incl. npm <= 8)
### Building
Please run following command on the build server
```shell
$ composer install --no-dev --optimize-autoloader
$ npm install
$ npx build prod
$ npm run build
$ rm -rf node_modules # Node modules are only required for building
```
### Configuration
All configuration can be found in .env.sample. Please copy this to file to .env and change its values
## Installation
### Requirements
* php <= 8.1
* ext-openssl
* ext-intl
* ext-pcntl (highly recommended)
* MariaDB
* SSH-access to the server
* Ability to change the webroot directory
* Ability to run a script as daemon (eg. supervisor or systemd units)
### Installation
First follow the build and configuration instructions. If you don't follow them correctly Webcron Management won't work correctly
1. Create your database and import the storage/database.sql file into the database
2. Create a first user by inserting a first record to the users table (Password is hashed using the HASHING_METHOD in your .env)
1. Create a build yourself or download the build from the releases page
2. Upload the build to the webserver.
3. Set up your webhosting to use the `/public` directory as web root
4. Upload the repository to the webserver
5. Set up the daemon script using systemd, supervisord or similar system
* If this is not possible running the daemon using a cronjob is still possible using below gist (Not recommended)
4. Create the .env file by copying .env.sample to .env and change the values
5. Run `php bin/console doctrine:migrations:migrate` to create or migrate the database
6. Create a first user by running `php bin/console webcron:user add`
7. Set up the daemon script using systemd, supervisord or similar system
If this is not possible running the daemon using a cronjob is still possible using below gist (Not recommended)
```shell
0 * * * * cd /path/to/webcron/ && php webcron daemon --time-limit=3600 > /dev/null 1&>2
```
The webcron interface should now work as expected.
## Upgrading
### Requirements
Same requirements and deploying
### Procedure
1. Remove all files except .env from the webserver
2. Upload the new build to the webserver
3. Run `php bin/console doctrine:migrations:migrate` to migrate the database
## Common pitfalls
### Cronjobs are not running
Did you edit the crontab?
### I can't do an automatic system upgrade!
Doing a system upgrade requires sudo which has a certain number security measurements. To enable running anything with sudo (eg. `sudo apt dist-upgrade -y`) the user needs to be able to run sudo without tty and password.

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 momentparse from './momentjs-parse';
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,33 +18,11 @@ document.addEventListener("readystatechange", event => {
}
});
const timepickerOptions = {
localization:{
locale: 'nl'
},
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(momentparse.load, 'DD/MM/yyyy HH:mm:ss');
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 => {
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);
}
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');
})
})
)
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 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;
}
}
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';

3
assets/js/settings.js Normal file
View File

@ -0,0 +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;
@ -55,3 +57,24 @@ td.status-col {
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

@ -0,0 +1,4 @@
@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";

View File

@ -4,31 +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.12",
"guzzlehttp/guzzle": "^7.4",
"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.1",
"symfony/console": "^6.1",
"symfony/dotenv": "^6.1",
"symfony/flex": "^2.2",
"symfony/framework-bundle": "^6.1",
"symfony/mailer": "^6.1",
"symfony/proxy-manager-bridge": "^6.1",
"symfony/runtime": "^6.1",
"symfony/security-bundle": "^6.1",
"symfony/translation": "^6.1",
"symfony/twig-bundle": "^6.1",
"symfony/webpack-encore-bundle": "^1.15",
"symfony/yaml": "^6.1"
"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": {
@ -78,14 +80,14 @@
"extra": {
"symfony": {
"allow-contrib": true,
"require": "6.1.*"
"require": "^6.4"
}
},
"require-dev": {
"symfony/debug-bundle": "^6.1",
"symfony/maker-bundle": "^1.45",
"symfony/monolog-bundle": "^3.8",
"symfony/stopwatch": "^6.1",
"symfony/web-profiler-bundle": "^6.1"
"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"
}
}

3091
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.
@ -12,6 +13,9 @@ framework:
cookie_samesite: lax
storage_factory_id: session.storage.factory.native
trusted_proxies: "%env(resolve:TRUSTED_PROXIES)%"
trusted_headers: ['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix']
#esi: true
#fragments: true
php_errors:

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'
@ -14,7 +13,7 @@ security:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/?(%enabled_locales%)?/(health)$
pattern: ^/?([a-zA-Z0-9-]+)?/(health)$
security: false
main:
pattern: ^/(.*)
@ -41,5 +40,12 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/?(%enabled_locales%)?/job, roles: ROLE_USER }
- { path: ^/?([a-zA-Z0-9-]+)?/job, roles: ROLE_USER }
# - { path: ^/profile, roles: ROLE_USER }
when@dev:
security:
firewalls:
main:
remember_me:
secure: false

View File

@ -4,9 +4,11 @@ framework:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- en
# providers:
# crowdin:
# dsn: '%env(CROWDIN_DSN)%'
providers:
crowdin:
dsn: '%env(CROWDIN_DSN)%'
domains: ['messages']
locales: ['en', 'nl', 'leet']
# loco:
# dsn: '%env(LOCO_DSN)%'
# lokalise:

View File

@ -1,5 +1,7 @@
twig:
default_path: '%kernel.project_dir%/templates'
globals:
kernelProjectDir: '%kernel.project_dir%'
when@test:
twig:

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,62 +1,5 @@
default:
path: '/'
controller: App\Controller\SecurityController::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
default_locale:
path: '/{_locale}'
controller: App\Controller\SecurityController::loginAction
login:
path: '/{_locale}/login'
controller: App\Controller\SecurityController::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

@ -4,10 +4,10 @@
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
enabled_locales: 'en|nl'
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"
enabled_locales:
en: 'English'
nl: 'Nederlands'
leet: 'L33tsp34k'
services:
# default configuration for services in *this* file

View File

@ -16,8 +16,9 @@
#/
## Dependencies
php=8.0
npm=7.0
php=8.1
npm=8.0
node=16.0
## Globals
script_name=$(basename "${0}")
@ -28,8 +29,11 @@ environment=main
root=/tmp/webcron
APP_ENV="prod"
DATABASE_URL="mysql://root:letmein@127.0.0.1:3306/webcron"
APP_SECRET=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 20 ; echo '')
DATABASE_URL="mysql://root:letmein@127.0.0.1:3306/webcron"
DEMO_MODE="false"
DEMO_USER="example@example.com"
DEMO_PASS="password"
ENCRYPTION_METHOD="AES-256-CBC"
HASHING_METHOD="sha256"
DEBUG=false
@ -95,11 +99,11 @@ CheckDeps() {
CheckDep "PHP" "php --version" "PHP is not available. Exiting" "FAIL" ${php} "echo '<?php echo phpversion();' | php" "PHP version too low. Exiting" "FAIL"
CheckDep "Composer" "composer --version" "Composer is not available. Exiting" "FAIL"
CheckDep "MySQL" "/usr/sbin/mysqld --version" "MySQL is not available. SQLite can be used" "WARNING"
CheckDep "NodeJS" "node --version" "NodeJS is not available. Exiting" "FAIL"
CheckDep "NodeJS" "node --version" "NodeJS is not available. Exiting" "FAIL" ${node} "node --version" "Node version too low. Exiting" "FAIL"
CheckDep "NPM" "npm --version" "NPM is not available. Exiting" "FAIL" ${npm} "npm --version" "NPM version too low. Exiting" "FAIL"
CheckDep "php-pcntl" "php -me | grep pcntl" "php-pcntl extension is not available. Cronjobs will not be running asyncronous" "WARNING"
CheckDep "php-intl" "php -me | grep intl" "php-intl extension is not available. Exiting" "FAIL"
CheckDep "php-xml" "php -me | grep xml" "php-xml extension is not available. Exiting" "FAIL"
CheckDep "php-openssl" "php -me | grep openssl" "php-openssl extension is not available. Exiting" "FAIL"
echo -e "\e[1;32mDependency test OK\e[0m"
}
@ -146,8 +150,11 @@ CreateEnvFile() {
touch .env 1> /dev/null 2>&1
fi
echo "APP_ENV=\"$APP_ENV\"" >> .env
echo "DATABASE_URL=\"$DATABASE_URL\"" >> .env
echo "APP_SECRET=\"$APP_SECRET\"" >> .env
echo "DATABASE_URL=\"$DATABASE_URL\"" >> .env
echo "DEMO_MODE=\"$DEMO_MODE\"" >> .env
echo "DEMO_USER=\"$DEMO_USER\"" >> .env
echo "DEMO_PASS=\"$DEMO_PASS\"" >> .env
echo "ENCRYPTION_METHOD=\"$ENCRYPTION_METHOD\"" >> .env
echo "HASHING_METHOD=\"$HASHING_METHOD\"" >> .env
echo "DEBUG=\"$DEBUG\"" >> .env

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version1000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create initial database';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE job (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(100) NOT NULL, `data` LONGTEXT NOT NULL, `interval` INT NOT NULL, nextrun INT NOT NULL, lastrun INT DEFAULT NULL, running INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE run (id INT AUTO_INCREMENT NOT NULL, job_id INT DEFAULT NULL, exitcode VARCHAR(15) NOT NULL, output LONGTEXT NOT NULL, runtime DOUBLE PRECISION NOT NULL, timestamp INT NOT NULL, flags VARCHAR(5) NOT NULL, INDEX IDX_5076A4C0BE04EA9 (job_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(100) NOT NULL, password VARCHAR(60) NOT NULL, sendmail TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE run ADD CONSTRAINT FK_5076A4C0BE04EA9 FOREIGN KEY (job_id) REFERENCES job (id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE run DROP FOREIGN KEY FK_5076A4C0BE04EA9');
$this->addSql('DROP TABLE job');
$this->addSql('DROP TABLE run');
$this->addSql('DROP TABLE user');
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version1001 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE user DROP sendmail');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE user ADD sendmail TINYINT(1) NOT NULL');
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version1002 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE user ADD locale VARCHAR(15) NOT NULL');
$this->addSql('UPDATE user SET locale = :locale', ['locale' => 'en']);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE user DROP locale');
}
}

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']);
}
}
}

11029
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": "latest",
"@eonasdan/tempus-dominus": "^6.9",
"@popperjs/core": "^2.11",
"bootstrap": "^5.1",
"bootstrap-dark-5": "^1.1",
"moment": "^2.29"
"bootstrap": "^5.3",
"moment": "^2.30"
},
"devDependencies": {
"sass": "^1.5",
"sass-loader": "^13.0",
"@symfony/webpack-encore": "^3.0",
"core-js": "^3.24"
"@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

@ -8,15 +8,16 @@ use App\Entity\Run;
use Doctrine\DBAL\Exception;
use App\Repository\RunRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
#[AsCommand(name: 'webcron:cleanup', description: 'Cleanup runs')]
class CleanupCommand extends Command
{
protected static $defaultName = 'webcron:cleanup';
protected $kernel;
protected $doctrine;
@ -30,25 +31,17 @@ class CleanupCommand extends Command
protected function configure()
{
$this
->setDescription('Cleanup runs')
->setHelp('This command cleans the runs table')
->addOption('jobid', 'j', InputOption::VALUE_IS_ARRAY + InputOption::VALUE_REQUIRED, 'The ids of the jobs to clean')
->addOption('maxage', 'm', InputOption::VALUE_REQUIRED, 'The maximum age of the oldest runs');
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output) : int
{
$maxage = $input->getOption('maxage');
$jobs = $input->getOption('jobid');
$runRepo = $this->doctrine->getRepository(Run::class);
try {
$deleted = $runRepo->cleanupRuns($jobs, $maxage);
$output->writeln('Deleted ' . $deleted . ' runs');
return Command::SUCCESS;
} catch(Exception $exception) {
$output->writeln($exception->getMessage());
return Command::FAILURE;
}
$deleted = $runRepo->cleanupRuns($jobs, $maxage);
$output->writeln('Deleted ' . $deleted . ' runs');
return Command::SUCCESS;
}
}

View File

@ -6,17 +6,16 @@ namespace App\Command;
use App\Entity\Job;
use App\Repository\JobRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
#[AsCommand(name: 'webcron:daemon', description: 'The master script of Webcron Management')]
class DaemonCommand extends Command
{
protected static $defaultName = 'webcron:daemon';
protected $kernel;
protected $doctrine;
@ -31,14 +30,14 @@ class DaemonCommand extends Command
protected function configure()
{
$this
->setDescription('The deamon slayer of webcron')
->setHelp('This command is the daemon process of webcron, enabling webcron to actually run jobs on time')
->addOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'Time limit in seconds before stopping the daemon.')
->addOption('async', 'a', InputOption::VALUE_NEGATABLE, 'Time limit in seconds before stopping the daemon.');
}
protected function execute(InputInterface $input, OutputInterface $output)
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');
@ -50,21 +49,26 @@ class DaemonCommand extends Command
throw new \InvalidArgumentException('Time limit has incorrect value');
}
$jobRepo->unlockJob();
file_put_contents($this->kernel->getCacheDir() . '/daemon-running.lock', posix_getpid());
file_put_contents($this->kernel->getCacheDir() . '/daemon-running.lock', time());
while(1) {
if($endofscript !== false && time() > $endofscript) break;
$jobsToRun = $jobRepo->getJobsDue();
if(!empty($jobsToRun)) {
foreach($jobsToRun as $job) {
if($job->getData('crontype') == 'reboot') {
$str = @file_get_contents('/proc/uptime');
$num = floatval($str);
foreach($jobsToRun as $key=>$job) {
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) {
@ -75,22 +79,23 @@ 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);
}
}
$this->doctrine->getManager()->clear();
file_put_contents($this->kernel->getCacheDir() . '/daemon-running.lock', time());
$maxwait = time() + 30;
$nextrun = $jobRepo->getTimeOfNextRun();
$nextrun = max($jobRepo->getTimeOfNextRun(), time() + 1);
$sleepuntil = min($maxwait, $nextrun);
if($sleepuntil > time()) time_sleep_until($sleepuntil);
gc_collect_cycles();
}
$output->writeln('Ended after ' . $timelimit . ' seconds');
pcntl_wait($status);

View File

@ -8,6 +8,7 @@ use App\Entity\User;
use App\Repository\RunRepository;
use Doctrine\DBAL\Connection;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@ -15,9 +16,9 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
#[AsCommand(name: 'webcron:demodata', description: 'Install demo data')]
class DemoInstallCommand extends Command
{
protected static $defaultName = 'webcron:demodata';
protected $kernel;
protected $doctrine;
protected $passwordHasher;
@ -33,11 +34,10 @@ class DemoInstallCommand extends Command
protected function configure()
{
$this
->setDescription('Install demo data')
->setHelp('This command installs the demo data');
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output) : int
{
$em = $this->doctrine->getManager();
@ -55,7 +55,7 @@ class DemoInstallCommand extends Command
$user
->setEmail($_ENV['DEMO_USER'])
->setPassword($hashedpassword)
->setSendmail(true);
->setLocale('en');
$em->persist($user);
$em->flush();
@ -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

@ -7,7 +7,9 @@ use App\Entity\User;
use App\Repository\JobRepository;
use App\Repository\UserRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
@ -18,9 +20,9 @@ use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Twig\Environment;
#[AsCommand(name: 'webcron:mail-failed-runs', description: 'Sends email about failed runs')]
class MailFailedRunsCommand extends Command
{
protected static $defaultName = 'webcron:mail-failed-runs';
protected $kernel;
protected $doctrine;
protected $templating;
@ -38,8 +40,8 @@ class MailFailedRunsCommand extends Command
protected function configure()
{
$this
->setDescription('Sends email about failed runs')
->setHelp('This command will send emails to the users when jobs are failing');
->setHelp('This command will send emails to the users when jobs are failing')
->addArgument('recipients', InputArgument::REQUIRED + InputArgument::IS_ARRAY, 'Which e-mailaddress should receive the notifications');
}
/**
@ -47,10 +49,8 @@ class MailFailedRunsCommand extends Command
* @throws \Twig\Error\SyntaxError
* @throws \Twig\Error\LoaderError
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output) : int
{
$userRepo = $this->doctrine->getRepository(User::class);
$jobRepo = $this->doctrine->getRepository(Job::class);
$failedJobs = $jobRepo->getFailingJobs();
@ -63,7 +63,7 @@ class MailFailedRunsCommand extends Command
->subject('Some cronjobs are failing')
->html($html);
$recipients = $userRepo->getMailAddresses();
$recipients = $input->getArgument('recipients');
foreach ($recipients as $recipient) {
$email->addTo($recipient);
}

View File

@ -4,15 +4,16 @@ namespace App\Command;
use App\Entity\Job;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
#[AsCommand(name: 'webcron:run', description: 'Run a single cronjob')]
class RunCommand extends Command
{
protected static $defaultName = 'webcron:run';
protected $kernel;
protected $doctrine;
@ -26,12 +27,11 @@ class RunCommand extends Command
protected function configure()
{
$this
->setDescription('Run a single cronjob')
->setHelp('This command runs a single command')
->addArgument('jobid', InputArgument::REQUIRED, 'The id of the job to be run');
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output) : int
{
$jobRepo = $this->doctrine->getRepository(Job::class);
$jobId = (int)$input->getArgument('jobid');
@ -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

@ -8,6 +8,7 @@ use App\Entity\User;
use App\Repository\RunRepository;
use Doctrine\DBAL\Connection;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
@ -17,41 +18,45 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
#[AsCommand(name: 'webcron:user', description: 'User stuff')]
class UserCommand extends Command
{
protected static $defaultName = 'webcron:user';
protected $kernel;
protected $doctrine;
protected $passwordHasher;
protected $io;
protected KernelInterface $kernel;
protected ManagerRegistry $doctrine;
protected UserPasswordHasherInterface $passwordHasher;
protected SymfonyStyle $io;
protected ParameterBagInterface $params;
private $action;
private $username;
private $password;
private $locale;
private $confirm;
public function __construct(KernelInterface $kernel, ManagerRegistry $doctrine, UserPasswordHasherInterface $passwordHasher)
public function __construct(KernelInterface $kernel, ManagerRegistry $doctrine, UserPasswordHasherInterface $passwordHasher, ParameterBagInterface $params)
{
$this->kernel = $kernel;
$this->doctrine = $doctrine;
$this->passwordHasher = $passwordHasher;
$this->params = $params;
parent::__construct();
}
protected function configure()
{
$this
->setDescription('User stuff')
->setHelp('The command is doing user stuff')
->addArgument('action', InputArgument::REQUIRED, 'What action should be executed? [add, delete, update]', null, ['add', 'update', 'delete'])
->addOption('username', 'u', InputOption::VALUE_OPTIONAL, 'What action should be executed? [add, delete, update]', '')
->addOption('password', 'p', InputOption::VALUE_OPTIONAL, 'What action should be executed? [add, delete, update]', '');
}
protected function initialize(InputInterface $input, OutputInterface $output)
protected function initialize(InputInterface $input, OutputInterface $output) : void
{
$this->action = $input->getArgument('action');
$this->username = $input->getOption('username');
@ -59,36 +64,60 @@ class UserCommand extends Command
$this->io = new SymfonyStyle($input, $output);
}
protected function interact(InputInterface $input, OutputInterface $output)
protected function interact(InputInterface $input, OutputInterface $output) : void
{
if(!empty($this->password)) {
$this->io->warning('It is not safe to send password directly via STDIN');
}
if(empty($this->username)) {
$this->username = $this->io->ask('Please provide the username? ');
if(in_array($this->action, ['add'])) {
if (empty($this->username)) {
$this->username = $this->io->ask('Please provide the username? ');
}
}
if($this->action == 'add') {
if(in_array($this->action, ['update', 'delete'])) {
if (empty($this->username)) {
$users = $this->doctrine->getRepository(User::class)->findAll();
$choices = [];
foreach($users as $user) {
$choices[] = $user->getEmail();
}
if(count($choices) > 1) {
$this->username = $this->io->choice('Please provide the username? ', $choices);
} else {
$this->username = $choices[0];
$this->io->info('Selected user ' . $this->username);
}
}
}
if(in_array($this->action, ['add', 'update'])) {
if(empty($this->password)) {
$password1 = $this->io->askHidden('Please enter the password? ');
$password2 = $this->io->askHidden('Please confirm the password? ');
if ($password1 != $password2) {
$this->password = NULL;
$this->io->error('Passwords didn\'t match. Exiting');
} elseif ($password1 == '') {
$this->password = NULL;
$this->io->error('Passwords cannot be empty. Exiting');
} else {
$this->password = $password1;
}
}
if(empty($this->locale)) {
$locales = $this->params->get('enabled_locales');
$this->locale = $this->io->choice('What locale should be used? ', $locales);
}
} elseif ($this->action == 'delete') {
$this->confirm = $this->io->confirm('Are you sure you want to delete ' . $this->username . '? ', false);
}
}
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output) : int
{
switch ($this->action) {
case 'add':
@ -97,6 +126,9 @@ class UserCommand extends Command
case 'delete':
$return = $this->deleteUser();
break;
case 'update':
$return = $this->updateUser();
break;
}
return $return;
}
@ -124,7 +156,7 @@ class UserCommand extends Command
$user
->setEmail($this->username)
->setPassword($hashedpassword)
->setSendmail($userSendMail === NULL);
->setLocale($this->locale);
$em->persist($user);
$em->flush();
@ -133,9 +165,40 @@ class UserCommand extends Command
return Command::SUCCESS;
}
private function updateUser() {
$em = $this->doctrine->getManager();
$user = $em->getRepository(User::class)->findOneBy(['email' => $this->username]);
if ($user === NULL) {
$this->io->error('User does not exist');
return Command::FAILURE;
}
if ($this->password === NULL) {
return Command::FAILURE;
}
$hashedpassword = $this->passwordHasher->hashPassword($user, $this->password);
$user
->setEmail($this->username)
->setPassword($hashedpassword)
->setLocale($this->locale);
$em->persist($user);
$em->flush();
$this->io->success('User updated');
return Command::SUCCESS;
}
private function deleteUser() {
if(!$this->confirm) {
$this->io->info('User not deleted');
return Command::SUCCESS;
}
$em = $this->doctrine->getManager();

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', $success['message']);
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') {
@ -55,12 +61,13 @@ class JobController extends AbstractController
$this->addFlash('danger', $e->getMessage());
return new RedirectResponse($this->GenerateUrl('job_edit', ['id' => $allValues['id']]));
}
$this->addFlash('success', $joboutput['message']);
$this->addFlash('success', 'job.edit.flashes.jobedited');
return new RedirectResponse($this->GenerateUrl('job_index'));
}
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') {
@ -74,41 +81,54 @@ class JobController extends AbstractController
$this->addFlash('danger', $e->getMessage());
return new RedirectResponse($this->GenerateUrl('job_add'));
}
$this->addFlash('success', $joboutput['message']);
$this->addFlash('success', 'job.add.flashes.jobadded');
return new RedirectResponse($this->GenerateUrl('job_index'));
} else {
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.view.runnow.deferred.title'),
'message' => $translator->trans('job.view.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.view.runnow.ran.title.success') : $translator->trans('job.view.runnow.ran.title.failed'),
'message' => $translator->trans('job.view.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

@ -1,42 +0,0 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils): Response
{
if($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return new RedirectResponse($this->generateUrl('job_index'));
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'controller_name' => 'LoginController',
'last_username' => $lastUsername,
'error' => $error
]);
}
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');
}
public function loginCheckAction(): void
{
}
}

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

@ -0,0 +1,114 @@
<?php
namespace App\Controller;
use App\Entity\User;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Cookie;
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')) {
$session = $request->getSession();
$user = $this->getUser();
$session->set('_locale', $user->getLocale());
return new RedirectResponse($this->generateUrl('job_index', ['_locale' => $user->getLocale()]));
}
if($request->cookies->has('logout-notice')) {
$this->addFlash('success', 'settings.flashes.passwordsaved');
$res = new Response();
$res->headers->clearCookie('logout-notice');
$res->sendHeaders();
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'controller_name' => 'LoginController',
'last_username' => $lastUsername,
'error' => $error
]);
}
#[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();
$data = $request->request->all();
$locale = $request->getLocale();
$userRepo = $em->getRepository(User::class);
$route = 'settings';
/** @var User $user */
$user = $this->getUser();
if(!empty($data['locale']) && $user->getLocale() != $data['locale']) {
$error = false;
if(!array_key_exists($data['locale'], $this->getParameter('enabled_locales'))) {
$error = true;
$this->addFlash('danger', 'settings.flashes.inexistinglocale');
}
if(!$error) {
$userRepo->setLocale($user, $data['locale']);
$locale = $data['locale'];
$this->addFlash('success', 'settings.flashes.localesaved');
}
}
if(!empty($data['current']) && !empty($data['password'])) {
$error = false;
if (empty($data['repeat']) || ($data['password'] != $data['repeat'])) {
$error = true;
$this->addFlash('danger', 'settings.flashes.repeatpasswordnotok');
} elseif(!$passwordHasher->isPasswordValid($user, $data['current'])) {
$error = true;
$this->addFlash('danger', 'settings.flashes.currentpassnotok');
}
if(!$error) {
$hashedpass = $passwordHasher->hashPassword($user, $data['password']);
$userRepo->setPassword($user, $hashedpass);
$cookie = new Cookie('logout-notice', true, (time() + 2), secure: $request->isSecure());
$res = new Response();
$res->headers->setCookie( $cookie );
$res->sendHeaders();
$route = 'logout';
}
}
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

@ -31,10 +31,10 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
private string $password;
/**
* @var bool
* @var string
*/
#[ORM\Column(type: "boolean")]
private bool $sendmail;
#[ORM\Column(type: "string", length: 15)]
private string $locale;
/**
* @return int|null
@ -91,20 +91,20 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
}
/**
* @return bool
* @return string
*/
public function isSendmail(): bool
public function getLocale(): string
{
return $this->sendmail;
return $this->locale;
}
/**
* @param bool $sendmail
* @param string $locale
* @return User
*/
public function setSendmail(bool $sendmail): User
public function setLocale(string $locale): User
{
$this->sendmail = $sendmail;
$this->locale = $locale;
return $this;
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\EventSubscriber;
use App\Entity\User;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
class LoginSubscriber implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct(string $defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onSuccessfulLogin(LoginSuccessEvent $event)
{
/** @var User $user */
$user = $event->getAuthenticatedToken()->getUser();
$request = $event->getRequest();
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $user->getLocale()) {
$request->getSession()->set('_locale', $locale);
} else {
// if no explicit locale has been set on this request, use one from the session
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return [LoginSuccessEvent::class => ['onSuccessfulLogin', 20]];
}
}

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

@ -14,7 +14,6 @@ use GuzzleHttp\Exception\GuzzleException;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Net\SSH2;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
*
@ -108,6 +107,7 @@ class JobRepository extends EntityRepository
*/
public function getJobsDue(): array
{
$this->getEntityManager()->clear();
$qb = $this->createQueryBuilder('job');
return $qb
->where(
@ -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,11 +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)];
$this->deleteTempVar($job);
$em->persist($job);
$em->flush();
return ['job_id' => $job->getId(), 'status' => 'ran', 'exitcode' => $result['exitcode'], 'timestamp' =>floor($starttime), 'runtime' => $runtime, 'output' => (string)$result['output'], 'flags' => implode("", $flags)];
}
/**
@ -732,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

@ -4,18 +4,25 @@
namespace App\Repository;
use App\Entity\User;
use App\Service\Secret;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
public function getMailAddresses() {
$users = $this->findBy(['sendmail' => 1]);
public function setLocale(User $user, $locale)
{
$em = $this->getEntityManager();
$user->setLocale($locale);
$em->persist($user);
$em->flush();
}
$return = [];
foreach($users as $user) {
$return[] = $user->getEmail();
}
return $return;
public function setPassword(User $user, $hashedPassword)
{
$em = $this->getEntityManager();
$user->setPassword($hashedPassword);
$em->persist($user);
$em->flush();
}
}

View File

@ -14,8 +14,8 @@ class DaemonHelpers
*/
public static function isProcessRunning($pidFile = '/var/run/myfile.pid') {
if (!file_exists($pidFile) || !is_file($pidFile)) return false;
$pid = file_get_contents($pidFile);
$return = posix_kill((int)$pid, 0);
$lasttick = file_get_contents($pidFile);
$return = ((int)$lasttick >= (time() - 30));
if (!$return) unlink($pidFile);
return $return;
}

View File

@ -4,6 +4,7 @@ namespace App\Twig;
use App\Service\Secret;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigTest;
class AppExtension extends AbstractExtension
{
@ -13,10 +14,19 @@ class AppExtension extends AbstractExtension
new TwigFilter('interval', [$this, 'parseInterval']),
new TwigFilter('parsetags', [$this, 'parseTags']),
new TwigFilter('decryptsecret', [$this, 'decryptSecret']),
new TwigFilter('contents', [$this, 'getContents']),
];
}
function parseInterval(int|float $time) {
public function getTests()
{
return [
new TwigTest('ondisk', [$this, 'onDisk'])
];
}
function parseInterval(int|float $time)
{
$return = '';
$days = floor($time / (60 * 60 * 24));
@ -37,7 +47,8 @@ class AppExtension extends AbstractExtension
return (!empty($return)) ? trim($return) : '0.000s';
}
function parseTags(string $text) {
function parseTags(string $text)
{
$results = [];
preg_match_all('/\[([A-Za-z0-9 \-]+)\]/', $text, $results);
foreach ($results[0] as $key=>$result) {
@ -48,7 +59,8 @@ class AppExtension extends AbstractExtension
return $text;
}
private function lightOrDark ($color) {
private function lightOrDark ($color)
{
$color = str_split($color, 2);
foreach($color as &$value) {
$value = hexdec($value);
@ -70,8 +82,18 @@ class AppExtension extends AbstractExtension
}
}
function decryptSecret(string $text) {
function decryptSecret(string $text)
{
return Secret::decrypt(base64_decode($text));
}
function getContents(string $file)
{
return file_get_contents($file);
}
public function onDisk(string $file)
{
return file_exists($file);
}
}

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"
},
@ -167,6 +167,15 @@
"bin/console"
]
},
"symfony/crowdin-translation-provider": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "9f5f1508bc80ed56c8a3ae7febc53a8aa982e424"
}
},
"symfony/debug-bundle": {
"version": "6.0",
"recipe": {
@ -207,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",
@ -250,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"
@ -316,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",
@ -332,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"
@ -381,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",
@ -400,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",
@ -413,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,5 +1,5 @@
<!doctype html>
<html>
<!DOCTYPE html>
<html class="h-100">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -11,8 +11,8 @@
</head>
<body>
<div class="container-fluid">
<body class="d-flex flex-column h-100">
<main class="flex-shrink-0 container-fluid">
<div class="row py-3">
<div class="col-12">
<div class="page-header">
@ -25,6 +25,7 @@
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="{{ path('job_index') }}">{{ 'menu.overview' | trans }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ path('job_add') }}">{{ 'menu.add' | trans }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ path('settings') }}">{{ 'menu.settings' | trans }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ path('logout') }}">{{ 'menu.logout' | trans }}</a></li>
</ul>
</div>
@ -33,9 +34,9 @@
{% block content %}{% endblock %}
</div>
</div>
</div>
</main>
{% block extrahtml %}{% endblock %}
{{ include('footer.html.twig') }}
</body>
</html>

View File

@ -0,0 +1,12 @@
<footer class="footer mt-auto py-3">
<div class="container-fluid">
<div class="row">
<div class="col-md align-self-start text-start text-muted">
{{ 'footer.title' | trans}} {% if (kernelProjectDir ~ "/version") is ondisk %}{{ (kernelProjectDir ~ "/version") | contents }}{% else %}dev-main{% endif %}
</div>
<div class="col-md align-self-end text-md-end text-muted">
<a href="https://git.jeroened.be/webcron/" target="_blank">{{ 'footer.source' | trans}}</a>
</div>
</div>
</div>
</footer>

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="#" data-time="60">{{ "job.addedit.generalinfo.interval.patterns.minute" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="3600">{{ "job.addedit.generalinfo.interval.patterns.hour" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="86400">{{ "job.addedit.generalinfo.interval.patterns.day" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="604800">{{ "job.addedit.generalinfo.interval.patterns.week" | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" 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 details</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="#" data-type="command">{{ "job.addedit.crontype.command.label" | trans }}</a></li>
<li><a class="dropdown-item crontype-item" href="#" data-type="reboot">{{ "job.addedit.crontype.reboot.label" | trans }}</a></li>
<li><a class="dropdown-item crontype-item" href="#" 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="#" data-type="local">{{ "job.addedit.hosttype.local.label" | trans }}</a></li>
<li><a class="dropdown-item hosttype-item" href="#" 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="#" data-type="none">{{ "job.addedit.containertype.none.label" | trans }}</a></li>
<li><a class="dropdown-item containertype-item" href="#" 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="#" 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

@ -17,11 +17,11 @@
{{ 'job.addedit.generalinfo.interval.patterns.label' | trans }}
</button>
<ul class="dropdown-menu" aria-labelledby="intervalButton">
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="60">{{ 'job.addedit.generalinfo.interval.patterns.minute' | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="3600">{{ 'job.addedit.generalinfo.interval.patterns.hour' | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="86400">{{ 'job.addedit.generalinfo.interval.patterns.day' | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="604800">{{ 'job.addedit.generalinfo.interval.patterns.week' | trans }}</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="2419200">{{ 'job.addedit.generalinfo.interval.patterns.4week' | trans }}</a></li>
<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" placeholder="{{ 'job.addedit.generalinfo.interval.placeholder' | trans }}" value="{{ job.interval }}">
</div>
@ -83,9 +83,9 @@
{% endif %}
</button>
<div class="dropdown-menu" aria-labelledby="crontypeButton">
<a class="dropdown-item crontype-item" href="#" data-type="command">{{ 'job.addedit.crontype.command.label' | trans }}</a>
<a class="dropdown-item crontype-item" href="#" data-type="reboot">{{ 'job.addedit.crontype.reboot.label' | trans }}</a>
<a class="dropdown-item crontype-item" href="#" data-type="http">{{ 'job.addedit.crontype.http.label' | trans }}</a>
<a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="command">{{ 'job.addedit.crontype.command.label' | trans }}</a>
<a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="reboot">{{ 'job.addedit.crontype.reboot.label' | trans }}</a>
<a class="dropdown-item crontype-item" href="javascript:void(0);" data-type="http">{{ 'job.addedit.crontype.http.label' | trans }}</a>
</div>
</div>
@ -100,8 +100,8 @@
{% endif %}
</button>
<div class="dropdown-menu" aria-labelledby="hosttypeButton">
<a class="dropdown-item hosttype-item" href="#" data-type="local">{{ 'job.addedit.hosttype.local.label' | trans }}</a>
<a class="dropdown-item hosttype-item" href="#" data-type="ssh">{{ 'job.addedit.hosttype.ssh.label' | trans }}</a>
<a class="dropdown-item hosttype-item" href="javascript:void(0);" data-type="local">{{ 'job.addedit.hosttype.local.label' | trans }}</a>
<a class="dropdown-item hosttype-item" href="javascript:void(0);" data-type="ssh">{{ 'job.addedit.hosttype.ssh.label' | trans }}</a>
</div>
</div>
@ -120,8 +120,8 @@
{% endif %}
</button>
<div class="dropdown-menu" aria-labelledby="containertypeButton">
<a class="dropdown-item containertype-item" href="#" data-type="none">{{ 'job.addedit.containertype.none.label' | trans }}</a>
<a class="dropdown-item containertype-item" href="#" data-type="docker">{{ 'job.addedit.containertype.docker.label' | trans }}</a>
<a class="dropdown-item containertype-item" href="javascript:void(0);" data-type="none">{{ 'job.addedit.containertype.none.label' | trans }}</a>
<a class="dropdown-item containertype-item" href="javascript:void(0);" data-type="docker">{{ 'job.addedit.containertype.docker.label' | trans }}</a>
</div>
</div>
</div>
@ -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>
@ -277,7 +278,7 @@
</p>
</div>
<div class="mb-3">
<a href="#" class="btn btn-outline-primary addvar-btn">{{ "job.addedit.variables.add.label" | trans }}</a>
<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="{{ job.data.crontype }}">

View File

@ -1,16 +1,16 @@
{% extends "base.html.twig" %}
{% block title %}{{ 'index.title' | trans }}{% endblock %}
{% block title %}{{ 'job.index.title' | trans }}{% endblock %}
{% block content %}
<h2>{{ 'index.header' | trans }}</h2>
<h2>{{ 'job.index.header' | trans }}</h2>
<table class="table table-md-striped">
<thead>
<tr class="d-none d-md-table-row">
<th>&nbsp;</th>
<th>{{ 'index.table.headers.name' | trans }}</th>
<th>{{ 'index.table.headers.host' | trans }}</th>
<th>{{ 'index.table.headers.interval' | trans }}</th>
<th>{{ 'index.table.headers.nextrun' | trans }}</th>
<th>{{ 'job.index.table.headers.name' | trans }}</th>
<th>{{ 'job.index.table.headers.host' | trans }}</th>
<th>{{ 'job.index.table.headers.interval' | trans }}</th>
<th>{{ 'job.index.table.headers.nextrun' | trans }}</th>
<th></th>
</tr>
</thead>
@ -25,16 +25,40 @@
<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="#" 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="#" 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>
<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>
</td>
</tr>
{% 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

@ -1,5 +1,5 @@
<!doctype html>
<html>
<html class="h-100">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -9,8 +9,8 @@
{{ encore_entry_script_tags('security.login') }}
</head>
<body>
<div class="container-fluid py-3">
<body class="d-flex flex-column h-100">
<main class="container-fluid py-3">
<div class="row justify-content-md-center">
<div class="col-md-4 col-xs-12">
{{ include('flashes.html.twig') }}
@ -49,6 +49,7 @@
{% endif %}
</div>
</div>
</div>
</main>
{{ include('footer.html.twig') }}
</body>
</html>

View File

@ -0,0 +1,44 @@
{% extends "base.html.twig" %}
{% block title %}{{ "settings.title" | trans }}{% endblock %}
{% block content %}
<h2>{{ "settings.header" | trans }}</h2>
<form method="post" class="form-horizontal" enctype="multipart/form-data" action="{{ path('settings_save') }}">
<h3>{{ "settings.password.header" | trans }}</h3>
<div class="mb-3">
<label for="current">{{ "settings.password.current.label" | trans }}</label>
<input type="password" name="current" class="form-control" id="current" placeholder="{{ "settings.password.current.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="password">{{ "settings.password.password.label" | trans }}</label>
<input type="password" name="password" class="form-control" id="password" placeholder="{{ "settings.password.password.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="repeat">{{ "settings.password.repeat.label" | trans }}</label>
<input type="password" name="repeat" class="form-control" id="repeat" placeholder="{{ "settings.password.repeat.placeholder" | trans }}">
</div>
<h3>{{ "settings.other.header" | trans }}</h3>
<div class="mb-3">
<label for="locale">{{ "settings.other.locale.label" | trans }}</label>
<select name="locale" class="form-select">
{% for code,locale in locales %}
<option value="{{ code }}"{% if code == user.locale %} selected{% endif %}>{{ locale }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-outline-primary">{{ "settings.submit.label" | trans }}</button>
</form>
{% endblock %}
{% block styles %}
{{ encore_entry_link_tags('settings') }}
{% endblock %}
{% block scripts %}
{{ encore_entry_script_tags('settings') }}
{% endblock %}

View File

@ -1,200 +0,0 @@
title: "W3bcr0n m4n4g3m3nt"
header: "W3bcr0n m4n4g3m3nt"
demomode:
flashnotice: "d1z 4ppL1c4t10n b 1n d3m0 m0d3. Ch4ng3z 1z p3rs1st3d 1n t3h d4t4b4s3, but j0bz 1z n0t b31n' 3x3cut3d"
credentials:
header: "L0g1n 4 d3m0 m0d3"
username: "Us3rn4m3"
password: "P4ssw0rd"
menu:
overview: "0v3rv13w"
add: "4dd @ n3w cr0nj0b"
logout: "L0g0ut"
security:
login:
username:
label: "Us3rn4m3"
placeholder: "j3r03n@h4x0r.L33t"
password:
label: "P4ssw0rd"
placeholder: "C0rr3ct H0rs3 B4tt3ry St4pL3"
remember:
label: "D0 n0t f0rg3t"
submit-btn:
label: "3nt3r"
index:
title: "0v3rv13w"
header: "0v3rv13w 0f t3h cr0nj0bz"
table:
headers:
name: "N4m3"
host: "H0st"
interval: "1nt3rv4L"
nextrun: "N3xtrun"
job:
view:
title: "0v3rv13w 0f runz 4 _jobname_"
header: "0v3rv13w 0f runz 4 _jobname_"
edit: "3d1t j0b"
runnow:
deferred:
title: "Cr0nj0b h4z b33n sch3duL3d"
message: "J0b wuz sch3duL3d 2 b run. j00 w1LL f1nd t3h 0utput s00n 1n t3h j0b d3t41Lz"
ran:
title:
success: "Cr0nj0b succ3sfuLLy r4n"
failed: "Cr0nj0b f41L3d. PL34s3 ch3ck 0utput b3L0w"
message: "Cr0nj0b r4n 1n _runtime_ s3c0ndz w1th 3x1t c0d3 _exitcode_"
show:
onlyfailed: "0nLy sh0w f41L3d runz"
all: "Sh0w 4LL runz"
results:
exitcode: "3x1t c0d3"
runtime: "Runt1m3"
manual: "M4nu4L run"
noresults:
failed: "N0 f41L3d runz f0und"
all: "N0 runz f0und"
edit:
title: "3d1t j0b"
header: "3d1t j0b _jobname_"
add:
title: "4dd j0b"
header: "4dd n3w j0b"
addedit:
generalinfo:
header: "G3n3r4L 1nf0"
name:
label: "N4m3"
placeholder: "Syst3m upd4t3"
helptext: "j00 c4n cr34t3 c0L0r3d t4gz by us1n' [t4g]"
interval:
label: "1nt3rv4L (1n s3c0ndz)"
placeholder: "3600"
patterns:
label: "P4tt3rnz"
minute: "3v3ry m1nut3"
hour: "3v3ry h0ur"
day: "3v3ry d4y"
week: "3v3ry w33k"
4week: "3v3ry 4 w33kz"
nextrun:
label: "N3xt run"
lastrun:
label: "L4st run"
eternal:
label: "3t3rn4L"
retention:
label: "R3t3nt10n (1n d4yz)"
placeholder: "180"
helptext: "H0w m4ny d4yz (4t l34st) 2 k33p runz 0f d1z j0b 1n t3h d4t4b4s3"
failpercentage:
label: "M4x f41L p3rc3nt4g3"
faildays:
label: "Numb3r 0f d4yz c4LcuL4t3d 4 f41L p3rc3nt4g3"
placeholder: "7"
hostlabel:
label: "H0st l4b3L"
placeholder: "N3wb13 s3rv3r"
helptext: "Wh1ch l4b3L 4 t3h h0stn4m3 sh0uLd b d1spL4y3d? 1f 3mpty t3h d1z w1LL b t3h h0stn4m3 pr0v1d3d b3L0w"
crontype:
label: "J0b typ3"
command:
label: "C0mm4nd"
header: "C0mm4nd d3t41Lz"
command:
label: "C0mm4nd"
placeholder: "sudo rm -rf /"
response:
label: "3xp3ct3d 3x1t c0d3"
placeholder: "0"
reboot:
label: "R3b00t"
header: "R3b00t j0b d3t41Lz"
reboot:
command:
label: "R3b00t c0mm4nd"
placeholder: "sudo telinit 6"
helptext: "Us3 {reboot-delay} 0r {reboot-delay-secs} 2 4dd t3h d3L4y 1n y0 c0mm4nd"
delay:
label: "R3b00t d3L4y (1n m1nut3z)"
placeholder: "5"
helptext: "D3L4y b3tw33n tr1gg3r1n' r3b00t n 4ctu4L r3b00t"
duration:
label: "R3b00t dur4t10n (1n m1nut3z)"
placeholder: "10"
helptext: "t3h 4m0unt 0f t1m3 t3h syst3m t4k3z 2 4ctu4LLy r3b00t"
getservices:
command:
label: "G3t s3rv1c3z c0mm4nd"
placeholder: "ps -aux"
response:
label: "G3t s3rv1c3z c0mm4nd 3x1t c0d3"
placeholder: "0"
http:
label: "HTTP r3qu3st"
header: "HTTP r3qu3st d3t41Lz"
url:
label: "UrL"
placeholder: "https://www.h4x0r.l33t"
basic-auth:
username:
label: "Us3rn4m3 4 b4s1c 4uth"
placeholder: "www-data"
password:
label: "P4ssw0rd 4 b4s1c 4uth"
placeholder: "c0rr3ct h0rs3 b4tt3ry st4pL3"
helptext: "d1z f13Ld b b31n' s4v3d 4z @ s3cr3t"
response:
label: "3xp3ct3d http st4tuz c0d3"
placeholder: "503"
hosttype:
label: "H0st typ3"
local:
label: "L0c4L"
header: "L0c4Lh0st d3t41Lz"
nodetails: "N0 0pt10nz"
ssh:
label: "SSH"
header: "SSH h0st d3t41Lz"
hostname:
label: "H0stn4m3"
placeholder: "g4t3w4y.n00b.0rg"
username:
label: "Us3rn4m3"
placeholder: "anonymous"
privatekey:
label: "Pr1v4t3 k3y"
helptext: "d1z f1L3 b b31n' s4v3z 4z @ s3cr3t"
keep:
label: "K33p"
passphrase:
label: "P4ssphr4s3 4 pr1v4t3 k3y"
placeholder: "w00t w00t"
helptext: "1f pr1v4t3 k3y b 3mpty d1z f13Ld b b31n' us3d 4z ssh-p4ssw0rd d1z f13Ld b b31n' s4v3d 4z @ s3cr3t"
containertype:
label: "C0nt41n3r typ3"
none:
label: "N0n3"
docker:
label: "D0ck3r"
header: "D0ck3r c0nt41n3r d3t41Lz"
service:
label: "S3rv1c3"
placeholder: "gopher"
username:
label: "Us3rn4m3"
placeholder: "root"
variables:
header: "V4r14bL3z"
secret:
label: "S3cr3t"
name:
placeholder: "N4m3"
value:
placeholder: "V4Lu3"
helptext: "j00 c4n 4dd v4r14bL3z by us1n' {v4r14bL3-n4m3} 1n j0b d3t41Lz"
add:
label: "4dd variable"
submit:
label: "Subm1t!"

View File

@ -0,0 +1,646 @@
<?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="qvIyBkY" resname="title">
<source>title</source>
<target>Webcron management</target>
</trans-unit>
<trans-unit id="HgWEol2" resname="header">
<source>header</source>
<target>Webcron management</target>
</trans-unit>
<trans-unit id="Q54TzT5" resname="demomode.flashnotice">
<source>demomode.flashnotice</source>
<target>This application is in demo mode. Changes are persisted in the database, but jobs are not being executed</target>
</trans-unit>
<trans-unit id="rpq61qN" resname="demomode.credentials.header">
<source>demomode.credentials.header</source>
<target>Login for demo mode</target>
</trans-unit>
<trans-unit id="jUz9fVE" resname="demomode.credentials.username">
<source>demomode.credentials.username</source>
<target>Username</target>
</trans-unit>
<trans-unit id="QNu97w_" resname="demomode.credentials.password">
<source>demomode.credentials.password</source>
<target>Password</target>
</trans-unit>
<trans-unit id="U8IBiON" resname="menu.overview">
<source>menu.overview</source>
<target>Overview</target>
</trans-unit>
<trans-unit id="iGC7eCk" resname="menu.add">
<source>menu.add</source>
<target>Add a new cronjob</target>
</trans-unit>
<trans-unit id="4UTsL.X" resname="menu.settings">
<source>menu.settings</source>
<target>Settings</target>
</trans-unit>
<trans-unit id="wBTFKFR" resname="menu.logout">
<source>menu.logout</source>
<target>Logout</target>
</trans-unit>
<trans-unit id="4cxyGOO" resname="security.login.username.label">
<source>security.login.username.label</source>
<target>Username</target>
</trans-unit>
<trans-unit id="u2tKtI1" resname="security.login.username.placeholder">
<source>security.login.username.placeholder</source>
<target>jeroen@example.com</target>
</trans-unit>
<trans-unit id="Niy_SZ2" resname="security.login.password.label">
<source>security.login.password.label</source>
<target>Password</target>
</trans-unit>
<trans-unit id="3rU2_6v" resname="security.login.password.placeholder">
<source>security.login.password.placeholder</source>
<target>abc123</target>
</trans-unit>
<trans-unit id="0MGdvgf" resname="security.login.remember.label">
<source>security.login.remember.label</source>
<target>Remember, remember</target>
</trans-unit>
<trans-unit id="4GvZndQ" resname="security.login.submit-btn.label">
<source>security.login.submit-btn.label</source>
<target>Login</target>
</trans-unit>
<trans-unit id="W.225Bj" resname="settings.title">
<source>settings.title</source>
<target>Settings</target>
</trans-unit>
<trans-unit id="a1bm5YC" resname="settings.header">
<source>settings.header</source>
<target>Settings</target>
</trans-unit>
<trans-unit id="qUrJMfo" resname="settings.flashes.inexistinglocale">
<source>settings.flashes.inexistinglocale</source>
<target>Locale does not exist</target>
</trans-unit>
<trans-unit id="XcSnV8X" resname="settings.flashes.localesaved">
<source>settings.flashes.localesaved</source>
<target>Locale is saved</target>
</trans-unit>
<trans-unit id="kpbry92" resname="settings.flashes.repeatpasswordnotok">
<source>settings.flashes.repeatpasswordnotok</source>
<target>Passwords are not equal</target>
</trans-unit>
<trans-unit id="DpJMsln" resname="settings.flashes.currentpassnotok">
<source>settings.flashes.currentpassnotok</source>
<target>Password is not correct</target>
</trans-unit>
<trans-unit id="tB3o6UA" resname="settings.flashes.passwordsaved">
<source>settings.flashes.passwordsaved</source>
<target>Password is saved</target>
</trans-unit>
<trans-unit id="aAt4FLV" resname="settings.password.header">
<source>settings.password.header</source>
<target>Password</target>
</trans-unit>
<trans-unit id="qqAzx9b" resname="settings.password.current.label">
<source>settings.password.current.label</source>
<target>Current password</target>
</trans-unit>
<trans-unit id="rr8bU5t" resname="settings.password.current.placeholder">
<source>settings.password.current.placeholder</source>
<target>abc123</target>
</trans-unit>
<trans-unit id="YVhGwqr" resname="settings.password.password.label">
<source>settings.password.password.label</source>
<target>New password</target>
</trans-unit>
<trans-unit id="OZpjE9T" resname="settings.password.password.placeholder">
<source>settings.password.password.placeholder</source>
<target>password</target>
</trans-unit>
<trans-unit id="TNBtpD_" resname="settings.password.repeat.label">
<source>settings.password.repeat.label</source>
<target>Repeat password</target>
</trans-unit>
<trans-unit id="bYb7Tz." resname="settings.password.repeat.placeholder">
<source>settings.password.repeat.placeholder</source>
<target>password</target>
</trans-unit>
<trans-unit id="_OGkxC8" resname="settings.other.header">
<source>settings.other.header</source>
<target>Other settings</target>
</trans-unit>
<trans-unit id="78Yswpr" resname="settings.other.locale.label">
<source>settings.other.locale.label</source>
<target>Locale</target>
</trans-unit>
<trans-unit id="WQ4J4Fx" resname="settings.submit.label">
<source>settings.submit.label</source>
<target>Submit</target>
</trans-unit>
<trans-unit id="0eRDYaC" resname="job.index.title">
<source>job.index.title</source>
<target>Overview</target>
</trans-unit>
<trans-unit id="J.7dT3_" resname="job.index.header">
<source>job.index.header</source>
<target>Overview of the cronjobs</target>
</trans-unit>
<trans-unit id="T0URP45" resname="job.index.table.headers.name">
<source>job.index.table.headers.name</source>
<target>Name</target>
</trans-unit>
<trans-unit id="vNWgxUJ" resname="job.index.table.headers.host">
<source>job.index.table.headers.host</source>
<target>Host</target>
</trans-unit>
<trans-unit id="1YQJCGW" resname="job.index.table.headers.interval">
<source>job.index.table.headers.interval</source>
<target>Interval</target>
</trans-unit>
<trans-unit id="mZ.gLJq" resname="job.index.table.headers.nextrun">
<source>job.index.table.headers.nextrun</source>
<target>Next run</target>
</trans-unit>
<trans-unit id="mokjk0L" resname="job.index.flashes.jobdeleted">
<source>job.index.flashes.jobdeleted</source>
<target>Cronjob is successfully deleted</target>
</trans-unit>
<trans-unit id="SF_jXZZ" resname="job.view.title">
<source>job.view.title</source>
<target>Overview of runs for _jobname_</target>
</trans-unit>
<trans-unit id="ysrfPnl" resname="job.view.header">
<source>job.view.header</source>
<target>Overview of runs for _jobname_</target>
</trans-unit>
<trans-unit id="KBsRhNs" resname="job.view.edit">
<source>job.view.edit</source>
<target>Edit job</target>
</trans-unit>
<trans-unit id="xvdCrX2" resname="job.view.show.onlyfailed">
<source>job.view.show.onlyfailed</source>
<target>Only show failed runs</target>
</trans-unit>
<trans-unit id="_zWmcxu" resname="job.view.show.all">
<source>job.view.show.all</source>
<target>Show all runs</target>
</trans-unit>
<trans-unit id="lpu6CQS" resname="job.view.results.exitcode">
<source>job.view.results.exitcode</source>
<target>Exit code</target>
</trans-unit>
<trans-unit id="9EemRnl" resname="job.view.results.runtime">
<source>job.view.results.runtime</source>
<target>Runtime</target>
</trans-unit>
<trans-unit id="juzEgkd" resname="job.view.results.manual">
<source>job.view.results.manual</source>
<target>Manual run</target>
</trans-unit>
<trans-unit id="H.6.gtG" resname="job.view.results.noresults.failed">
<source>job.view.results.noresults.failed</source>
<target>No failed runs found</target>
</trans-unit>
<trans-unit id="u3QkQi1" resname="job.view.results.noresults.all">
<source>job.view.results.noresults.all</source>
<target>No runs found</target>
</trans-unit>
<trans-unit id="zBxeZSL" resname="job.edit.title">
<source>job.edit.title</source>
<target>Edit job</target>
</trans-unit>
<trans-unit id="CDJme2K" resname="job.edit.header">
<source>job.edit.header</source>
<target>Edit job _jobname_</target>
</trans-unit>
<trans-unit id="eU2m5Zf" resname="job.edit.flashes.jobedited">
<source>job.edit.flashes.jobedited</source>
<target>Cronjob successfully edited</target>
</trans-unit>
<trans-unit id="CPdXoro" resname="job.add.title">
<source>job.add.title</source>
<target>Add job</target>
</trans-unit>
<trans-unit id=".pKQkI0" resname="job.add.header">
<source>job.add.header</source>
<target>Add new job</target>
</trans-unit>
<trans-unit id="66iDRun" resname="job.add.flashes.jobadded">
<source>job.add.flashes.jobadded</source>
<target>Cronjob successfully added</target>
</trans-unit>
<trans-unit id="h9UI9nK" resname="job.addedit.generalinfo.header">
<source>job.addedit.generalinfo.header</source>
<target>General info</target>
</trans-unit>
<trans-unit id="2.rDz7o" resname="job.addedit.generalinfo.name.label">
<source>job.addedit.generalinfo.name.label</source>
<target>Name</target>
</trans-unit>
<trans-unit id="3FV9jvC" resname="job.addedit.generalinfo.name.placeholder">
<source>job.addedit.generalinfo.name.placeholder</source>
<target>System update</target>
</trans-unit>
<trans-unit id="j9FTkRt" resname="job.addedit.generalinfo.name.helptext">
<source>job.addedit.generalinfo.name.helptext</source>
<target>You can create colored tags by using [tag]</target>
</trans-unit>
<trans-unit id="CL7GVb3" resname="job.addedit.generalinfo.interval.label">
<source>job.addedit.generalinfo.interval.label</source>
<target>Interval (in seconds)</target>
</trans-unit>
<trans-unit id="iSs41MC" resname="job.addedit.generalinfo.interval.placeholder">
<source>job.addedit.generalinfo.interval.placeholder</source>
<target>3600</target>
</trans-unit>
<trans-unit id="OTx9beS" resname="job.addedit.generalinfo.interval.patterns.label">
<source>job.addedit.generalinfo.interval.patterns.label</source>
<target>Patterns</target>
</trans-unit>
<trans-unit id="0ATgoBe" resname="job.addedit.generalinfo.interval.patterns.minute">
<source>job.addedit.generalinfo.interval.patterns.minute</source>
<target>Every minute</target>
</trans-unit>
<trans-unit id="f0o1GlZ" resname="job.addedit.generalinfo.interval.patterns.hour">
<source>job.addedit.generalinfo.interval.patterns.hour</source>
<target>Every hour</target>
</trans-unit>
<trans-unit id="hCmc8AA" resname="job.addedit.generalinfo.interval.patterns.day">
<source>job.addedit.generalinfo.interval.patterns.day</source>
<target>Every day</target>
</trans-unit>
<trans-unit id="zmUXvRU" resname="job.addedit.generalinfo.interval.patterns.week">
<source>job.addedit.generalinfo.interval.patterns.week</source>
<target>Every week</target>
</trans-unit>
<trans-unit id="ISEzS__" resname="job.addedit.generalinfo.interval.patterns.4week">
<source>job.addedit.generalinfo.interval.patterns.4week</source>
<target>Every 4 weeks</target>
</trans-unit>
<trans-unit id="HWDmm9f" resname="job.addedit.generalinfo.nextrun.label">
<source>job.addedit.generalinfo.nextrun.label</source>
<target>Next run</target>
</trans-unit>
<trans-unit id="e1RkhOF" resname="job.addedit.generalinfo.lastrun.label">
<source>job.addedit.generalinfo.lastrun.label</source>
<target>Last run</target>
</trans-unit>
<trans-unit id="kw_hgon" resname="job.addedit.generalinfo.lastrun.eternal.label">
<source>job.addedit.generalinfo.lastrun.eternal.label</source>
<target>Eternal</target>
</trans-unit>
<trans-unit id="sdUg23G" resname="job.addedit.generalinfo.retention.label">
<source>job.addedit.generalinfo.retention.label</source>
<target>Retention (in days)</target>
</trans-unit>
<trans-unit id="nWofDbf" resname="job.addedit.generalinfo.retention.placeholder">
<source>job.addedit.generalinfo.retention.placeholder</source>
<target>180</target>
</trans-unit>
<trans-unit id="eHfLjBO" resname="job.addedit.generalinfo.retention.helptext">
<source>job.addedit.generalinfo.retention.helptext</source>
<target>How many days (at least) to keep runs of this job in the database</target>
</trans-unit>
<trans-unit id="kfQDfoB" resname="job.addedit.generalinfo.failpercentage.label">
<source>job.addedit.generalinfo.failpercentage.label</source>
<target>Max fail percentage</target>
</trans-unit>
<trans-unit id="Ytyhy77" resname="job.addedit.generalinfo.faildays.label">
<source>job.addedit.generalinfo.faildays.label</source>
<target>Number of days calculated for fail percentage</target>
</trans-unit>
<trans-unit id="2O_wFoD" resname="job.addedit.generalinfo.faildays.placeholder">
<source>job.addedit.generalinfo.faildays.placeholder</source>
<target>7</target>
</trans-unit>
<trans-unit id="UvMb6oM" resname="job.addedit.generalinfo.hostlabel.label">
<source>job.addedit.generalinfo.hostlabel.label</source>
<target>Host label</target>
</trans-unit>
<trans-unit id="GjPGKoQ" resname="job.addedit.generalinfo.hostlabel.placeholder">
<source>job.addedit.generalinfo.hostlabel.placeholder</source>
<target>petrosian.jeroened.be</target>
</trans-unit>
<trans-unit id="moGlM4y" resname="job.addedit.generalinfo.hostlabel.helptext">
<source>job.addedit.generalinfo.hostlabel.helptext</source>
<target>Which label for the hostname should be displayed? If empty the this will be the hostname provided below</target>
</trans-unit>
<trans-unit id="VEORPjf" resname="job.addedit.jobdetails.header">
<source>job.addedit.jobdetails.header</source>
<target>Job details</target>
</trans-unit>
<trans-unit id="7haVx1t" resname="job.addedit.crontype.label">
<source>job.addedit.crontype.label</source>
<target>Job type</target>
</trans-unit>
<trans-unit id="kw_PINJ" resname="job.addedit.crontype.command.label">
<source>job.addedit.crontype.command.label</source>
<target>Command</target>
</trans-unit>
<trans-unit id="sxAwNxW" resname="job.addedit.crontype.command.header">
<source>job.addedit.crontype.command.header</source>
<target>Command details</target>
</trans-unit>
<trans-unit id="ZNQUrj7" resname="job.addedit.crontype.command.command.label">
<source>job.addedit.crontype.command.command.label</source>
<target>Command</target>
</trans-unit>
<trans-unit id="aWesJsc" resname="job.addedit.crontype.command.command.placeholder">
<source>job.addedit.crontype.command.command.placeholder</source>
<target>sudo apt update</target>
</trans-unit>
<trans-unit id="os3VpHs" resname="job.addedit.crontype.command.response.label">
<source>job.addedit.crontype.command.response.label</source>
<target>Expected exit code</target>
</trans-unit>
<trans-unit id="RIWj._C" resname="job.addedit.crontype.command.response.placeholder">
<source>job.addedit.crontype.command.response.placeholder</source>
<target>0</target>
</trans-unit>
<trans-unit id="A66vsIi" resname="job.addedit.crontype.reboot.label">
<source>job.addedit.crontype.reboot.label</source>
<target>Reboot</target>
</trans-unit>
<trans-unit id="Kv8GihO" resname="job.addedit.crontype.reboot.header">
<source>job.addedit.crontype.reboot.header</source>
<target>Reboot job details</target>
</trans-unit>
<trans-unit id="hy8z.7x" resname="job.addedit.crontype.reboot.reboot.command.label">
<source>job.addedit.crontype.reboot.reboot.command.label</source>
<target>Reboot command</target>
</trans-unit>
<trans-unit id="tw3YvLz" resname="job.addedit.crontype.reboot.reboot.command.placeholder">
<source>job.addedit.crontype.reboot.reboot.command.placeholder</source>
<target>systemctl reboot</target>
</trans-unit>
<trans-unit id="p0Seiyx" resname="job.addedit.crontype.reboot.reboot.command.helptext">
<source>job.addedit.crontype.reboot.reboot.command.helptext</source>
<target>Use {reboot-delay} or {reboot-delay-secs} to add the delay in your command</target>
</trans-unit>
<trans-unit id="bEPANkU" resname="job.addedit.crontype.reboot.reboot.delay.label">
<source>job.addedit.crontype.reboot.reboot.delay.label</source>
<target>Reboot delay (in minutes)</target>
</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>5</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>
<target>Delay between triggering reboot and actual reboot</target>
</trans-unit>
<trans-unit id="EbgF04P" resname="job.addedit.crontype.reboot.reboot.duration.label">
<source>job.addedit.crontype.reboot.reboot.duration.label</source>
<target>Reboot duration (in minutes)</target>
</trans-unit>
<trans-unit id="1FVsnYa" resname="job.addedit.crontype.reboot.reboot.duration.placeholder">
<source>job.addedit.crontype.reboot.reboot.duration.placeholder</source>
<target>10</target>
</trans-unit>
<trans-unit id="VWOhJBY" resname="job.addedit.crontype.reboot.reboot.duration.helptext">
<source>job.addedit.crontype.reboot.reboot.duration.helptext</source>
<target>The amount of time the system takes to actually reboot</target>
</trans-unit>
<trans-unit id="ENLtpfK" resname="job.addedit.crontype.reboot.getservices.command.label">
<source>job.addedit.crontype.reboot.getservices.command.label</source>
<target>Get services command</target>
</trans-unit>
<trans-unit id="yxIObGq" resname="job.addedit.crontype.reboot.getservices.command.placeholder">
<source>job.addedit.crontype.reboot.getservices.command.placeholder</source>
<target>systemctl list-units</target>
</trans-unit>
<trans-unit id="aurJI0X" resname="job.addedit.crontype.reboot.getservices.response.label">
<source>job.addedit.crontype.reboot.getservices.response.label</source>
<target>Get services command exit code</target>
</trans-unit>
<trans-unit id="xMbagrx" resname="job.addedit.crontype.reboot.getservices.response.placeholder">
<source>job.addedit.crontype.reboot.getservices.response.placeholder</source>
<target>0</target>
</trans-unit>
<trans-unit id="ze4mr8D" resname="job.addedit.crontype.http.label">
<source>job.addedit.crontype.http.label</source>
<target>HTTP request</target>
</trans-unit>
<trans-unit id="oJn3xvZ" resname="job.addedit.crontype.http.header">
<source>job.addedit.crontype.http.header</source>
<target>HTTP request details</target>
</trans-unit>
<trans-unit id="hwhqEtB" resname="job.addedit.crontype.http.url.label">
<source>job.addedit.crontype.http.url.label</source>
<target>Url</target>
</trans-unit>
<trans-unit id="31CZuNl" resname="job.addedit.crontype.http.url.placeholder">
<source>job.addedit.crontype.http.url.placeholder</source>
<target>https://www.example.com</target>
</trans-unit>
<trans-unit id="gGpY1Gz" resname="job.addedit.crontype.http.basic-auth.username.label">
<source>job.addedit.crontype.http.basic-auth.username.label</source>
<target>Username for basic auth</target>
</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>www-data</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>
<target>Password for basic auth</target>
</trans-unit>
<trans-unit id="ROX9KeU" resname="job.addedit.crontype.http.basic-auth.password.placeholder">
<source>job.addedit.crontype.http.basic-auth.password.placeholder</source>
<target>correct horse battery staple</target>
</trans-unit>
<trans-unit id="pRPdCPd" resname="job.addedit.crontype.http.basic-auth.password.helptext">
<source>job.addedit.crontype.http.basic-auth.password.helptext</source>
<target>This field is being saved as a secret</target>
</trans-unit>
<trans-unit id="SnZXqhK" resname="job.addedit.crontype.http.response.label">
<source>job.addedit.crontype.http.response.label</source>
<target>Expected http status code</target>
</trans-unit>
<trans-unit id="aLSDlKd" resname="job.addedit.crontype.http.response.placeholder">
<source>job.addedit.crontype.http.response.placeholder</source>
<target>418</target>
</trans-unit>
<trans-unit id="oV9GuKJ" resname="job.addedit.hosttype.label">
<source>job.addedit.hosttype.label</source>
<target>Host type</target>
</trans-unit>
<trans-unit id="eBXA85X" resname="job.addedit.hosttype.local.label">
<source>job.addedit.hosttype.local.label</source>
<target>Local</target>
</trans-unit>
<trans-unit id="ZeflLHK" resname="job.addedit.hosttype.local.header">
<source>job.addedit.hosttype.local.header</source>
<target>Localhost details</target>
</trans-unit>
<trans-unit id="1atQ8Ot" resname="job.addedit.hosttype.local.nodetails">
<source>job.addedit.hosttype.local.nodetails</source>
<target>No options</target>
</trans-unit>
<trans-unit id="oUUVCNC" resname="job.addedit.hosttype.ssh.label">
<source>job.addedit.hosttype.ssh.label</source>
<target>SSH</target>
</trans-unit>
<trans-unit id="HIND6DD" resname="job.addedit.hosttype.ssh.header">
<source>job.addedit.hosttype.ssh.header</source>
<target>SSH host details</target>
</trans-unit>
<trans-unit id="n_JNj3z" resname="job.addedit.hosttype.ssh.hostname.label">
<source>job.addedit.hosttype.ssh.hostname.label</source>
<target>Hostname</target>
</trans-unit>
<trans-unit id="4ZEnTLC" resname="job.addedit.hosttype.ssh.hostname.placeholder">
<source>job.addedit.hosttype.ssh.hostname.placeholder</source>
<target>ssh.abc.xyz</target>
</trans-unit>
<trans-unit id="08j0NEG" resname="job.addedit.hosttype.ssh.username.label">
<source>job.addedit.hosttype.ssh.username.label</source>
<target>Username</target>
</trans-unit>
<trans-unit id="EJOhQKe" resname="job.addedit.hosttype.ssh.username.placeholder">
<source>job.addedit.hosttype.ssh.username.placeholder</source>
<target>larry</target>
</trans-unit>
<trans-unit id="VEZQ7fY" resname="job.addedit.hosttype.ssh.privatekey.label">
<source>job.addedit.hosttype.ssh.privatekey.label</source>
<target>Private key</target>
</trans-unit>
<trans-unit id="1MzOMSK" resname="job.addedit.hosttype.ssh.privatekey.helptext">
<source>job.addedit.hosttype.ssh.privatekey.helptext</source>
<target>This file is being saves as a secret</target>
</trans-unit>
<trans-unit id="uVSkZpl" resname="job.addedit.hosttype.ssh.privatekey.keep.label">
<source>job.addedit.hosttype.ssh.privatekey.keep.label</source>
<target>Keep</target>
</trans-unit>
<trans-unit id="nxl.bTZ" resname="job.addedit.hosttype.ssh.passphrase.label">
<source>job.addedit.hosttype.ssh.passphrase.label</source>
<target>Passphrase for private key</target>
</trans-unit>
<trans-unit id="rYaggeh" resname="job.addedit.hosttype.ssh.passphrase.placeholder">
<source>job.addedit.hosttype.ssh.passphrase.placeholder</source>
<target>correct horse battery staple</target>
</trans-unit>
<trans-unit id="MAQ8t1s" resname="job.addedit.hosttype.ssh.passphrase.helptext">
<source>job.addedit.hosttype.ssh.passphrase.helptext</source>
<target>If private key is empty this field is being used as ssh-password This field is being saved as a secret</target>
</trans-unit>
<trans-unit id="8q0z3bf" resname="job.addedit.containertype.label">
<source>job.addedit.containertype.label</source>
<target>Container type</target>
</trans-unit>
<trans-unit id="ZPis4Xx" resname="job.addedit.containertype.none.label">
<source>job.addedit.containertype.none.label</source>
<target>None</target>
</trans-unit>
<trans-unit id="AGADEB_" resname="job.addedit.containertype.docker.label">
<source>job.addedit.containertype.docker.label</source>
<target>Docker</target>
</trans-unit>
<trans-unit id="1hg_Lri" resname="job.addedit.containertype.docker.header">
<source>job.addedit.containertype.docker.header</source>
<target>Docker container details</target>
</trans-unit>
<trans-unit id="fddg1.3" resname="job.addedit.containertype.docker.service.label">
<source>job.addedit.containertype.docker.service.label</source>
<target>Service</target>
</trans-unit>
<trans-unit id="mmNVzOj" resname="job.addedit.containertype.docker.service.placeholder">
<source>job.addedit.containertype.docker.service.placeholder</source>
<target>mysql</target>
</trans-unit>
<trans-unit id="kaHv1qa" resname="job.addedit.containertype.docker.username.label">
<source>job.addedit.containertype.docker.username.label</source>
<target>Username</target>
</trans-unit>
<trans-unit id="UzbvieZ" resname="job.addedit.containertype.docker.username.placeholder">
<source>job.addedit.containertype.docker.username.placeholder</source>
<target>larry</target>
</trans-unit>
<trans-unit id="BsHxmnc" resname="job.addedit.variables.header">
<source>job.addedit.variables.header</source>
<target>Variables</target>
</trans-unit>
<trans-unit id="pawk4nL" resname="job.addedit.variables.secret.label">
<source>job.addedit.variables.secret.label</source>
<target>Secret</target>
</trans-unit>
<trans-unit id="roGfPFi" resname="job.addedit.variables.name.placeholder">
<source>job.addedit.variables.name.placeholder</source>
<target>Name</target>
</trans-unit>
<trans-unit id="WWsh_72" resname="job.addedit.variables.value.placeholder">
<source>job.addedit.variables.value.placeholder</source>
<target>Value</target>
</trans-unit>
<trans-unit id="Ve0r_Wy" resname="job.addedit.variables.helptext">
<source>job.addedit.variables.helptext</source>
<target>You can add variables by using {variable-name} in job details</target>
</trans-unit>
<trans-unit id="tL8wand" resname="job.addedit.variables.add.label">
<source>job.addedit.variables.add.label</source>
<target>Add variable</target>
</trans-unit>
<trans-unit id="or7JWUe" resname="job.addedit.submit.label">
<source>job.addedit.submit.label</source>
<target>Submit!</target>
</trans-unit>
<trans-unit id="g7pEpsB" resname="footer.title">
<source>footer.title</source>
<target>Webcron Management</target>
</trans-unit>
<trans-unit id="Lz7YjVX" resname="footer.source">
<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

@ -1,200 +0,0 @@
title: "Webcron management"
header: "Webcron management"
demomode:
flashnotice: "This application is in demo mode. Changes are persisted in the database, but jobs are not being executed"
credentials:
header: "Login for demo mode"
username: "Username"
password: "Password"
menu:
overview: "Overview"
add: "Add a new cronjob"
logout: "Logout"
security:
login:
username:
label: "Username"
placeholder: "jeroen@example.com"
password:
label: "Password"
placeholder: "abc123"
remember:
label: "Remember, remember"
submit-btn:
label: "Login"
index:
title: "Overview"
header: "Overview of the cronjobs"
table:
headers:
name: "Name"
host: "Host"
interval: "Interval"
nextrun: "Nextrun"
job:
view:
title: "Overview of runs for _jobname_"
header: "Overview of runs for _jobname_"
edit: "Edit job"
runnow:
deferred:
title: "Cronjob has been scheduled"
message: "Job was scheduled to be run. You will find the output soon in the job details"
ran:
title:
success: "Cronjob succesfully ran"
failed: "Cronjob failed. Please check output below"
message: "Cronjob ran in _runtime_ seconds with exit code _exitcode_"
show:
onlyfailed: "Only show failed runs"
all: "Show all runs"
results:
exitcode: "Exit code"
runtime: "Runtime"
manual: "Manual run"
noresults:
failed: "No failed runs found"
all: "No runs found"
edit:
title: "Edit job"
header: "Edit job _jobname_"
add:
title: "Add job"
header: "Add new job"
addedit:
generalinfo:
header: "General info"
name:
label: "Name"
placeholder: "System update"
helptext: "You can create colored tags by using [tag]"
interval:
label: "Interval (in seconds)"
placeholder: "3600"
patterns:
label: "Patterns"
minute: "Every minute"
hour: "Every hour"
day: "Every day"
week: "Every week"
4week: "Every 4 weeks"
nextrun:
label: "Next run"
lastrun:
label: "Last run"
eternal:
label: "Eternal"
retention:
label: "Retention (in days)"
placeholder: "180"
helptext: "How many days (at least) to keep runs of this job in the database"
failpercentage:
label: "Max fail percentage"
faildays:
label: "Number of days calculated for fail percentage"
placeholder: "7"
hostlabel:
label: "Host label"
placeholder: "petrosian.jeroened.be"
helptext: "Which label for the hostname should be displayed? If empty the this will be the hostname provided below"
crontype:
label: "Job type"
command:
label: "Command"
header: "Command details"
command:
label: "Command"
placeholder: "sudo apt update"
response:
label: "Expected exit code"
placeholder: "0"
reboot:
label: "Reboot"
header: "Reboot job details"
reboot:
command:
label: "Reboot command"
placeholder: "systemctl reboot"
helptext: "Use {reboot-delay} or {reboot-delay-secs} to add the delay in your command"
delay:
label: "Reboot delay (in minutes)"
placeholder: "5"
helptext: "Delay between triggering reboot and actual reboot"
duration:
label: "Reboot duration (in minutes)"
placeholder: "10"
helptext: "The amount of time the system takes to actually reboot"
getservices:
command:
label: "Get services command"
placeholder: "systemctl list-units"
response:
label: "Get services command exit code"
placeholder: "0"
http:
label: "HTTP request"
header: "HTTP request details"
url:
label: "Url"
placeholder: "https://www.example.com"
basic-auth:
username:
label: "Username for basic auth"
placeholder: "www-data"
password:
label: "Password for basic auth"
placeholder: "correct horse battery staple"
helptext: "This field is being saved as a secret"
response:
label: "Expected http status code"
placeholder: "418"
hosttype:
label: "Host type"
local:
label: "Local"
header: "Localhost details"
nodetails: "No options"
ssh:
label: "SSH"
header: "SSH host details"
hostname:
label: "Hostname"
placeholder: "ssh.abc.xyz"
username:
label: "Username"
placeholder: "larry"
privatekey:
label: "Private key"
helptext: "This file is being saves as a secret"
keep:
label: "Keep"
passphrase:
label: "Passphrase for private key"
placeholder: "correct horse battery staple"
helptext: "If private key is empty this field is being used as ssh-password This field is being saved as a secret"
containertype:
label: "Container type"
none:
label: "None"
docker:
label: "Docker"
header: "Docker container details"
service:
label: "Service"
placeholder: "mysql"
username:
label: "Username"
placeholder: "larry"
variables:
header: "Variables"
secret:
label: "Secret"
name:
placeholder: "Name"
value:
placeholder: "Value"
helptext: "You can add variables by using {variable-name} in job details"
add:
label: "Add variable"
submit:
label: "Submit!"

View File

@ -0,0 +1,646 @@
<?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="leet" 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">W3bcr0n m4n4g3m3nt</target>
</trans-unit>
<trans-unit id="HgWEol2" resname="header">
<source>header</source>
<target state="final">W3bcr0n m4n4g3m3nt</target>
</trans-unit>
<trans-unit id="Q54TzT5" resname="demomode.flashnotice">
<source>demomode.flashnotice</source>
<target state="final">d1z 4ppL1c4t10n b 1n d3m0 m0d3. Ch4ng3z 1z p3rs1st3d 1n t3h d4t4b4s3, but j0bz 1z n0t b31n' 3x3cut3d</target>
</trans-unit>
<trans-unit id="rpq61qN" resname="demomode.credentials.header">
<source>demomode.credentials.header</source>
<target state="final">L0g1n 4 d3m0 m0d3</target>
</trans-unit>
<trans-unit id="jUz9fVE" resname="demomode.credentials.username">
<source>demomode.credentials.username</source>
<target state="final">Us3rn4m3</target>
</trans-unit>
<trans-unit id="QNu97w_" resname="demomode.credentials.password">
<source>demomode.credentials.password</source>
<target state="final">P4ssw0rd</target>
</trans-unit>
<trans-unit id="U8IBiON" resname="menu.overview">
<source>menu.overview</source>
<target state="final">0v3rv13w</target>
</trans-unit>
<trans-unit id="iGC7eCk" resname="menu.add">
<source>menu.add</source>
<target state="final">4dd @ n3w cr0nj0b</target>
</trans-unit>
<trans-unit id="4UTsL.X" resname="menu.settings">
<source>menu.settings</source>
<target state="final">S3tt1ngz</target>
</trans-unit>
<trans-unit id="wBTFKFR" resname="menu.logout">
<source>menu.logout</source>
<target state="final">L0g0ut</target>
</trans-unit>
<trans-unit id="4cxyGOO" resname="security.login.username.label">
<source>security.login.username.label</source>
<target state="final">Us3rn4m3</target>
</trans-unit>
<trans-unit id="u2tKtI1" resname="security.login.username.placeholder">
<source>security.login.username.placeholder</source>
<target state="final">j3r03n@h4x0r.L33t</target>
</trans-unit>
<trans-unit id="Niy_SZ2" resname="security.login.password.label">
<source>security.login.password.label</source>
<target state="final">P4ssw0rd</target>
</trans-unit>
<trans-unit id="3rU2_6v" resname="security.login.password.placeholder">
<source>security.login.password.placeholder</source>
<target state="final">C0rr3ct H0rs3 B4tt3ry St4pL3</target>
</trans-unit>
<trans-unit id="0MGdvgf" resname="security.login.remember.label">
<source>security.login.remember.label</source>
<target state="final">D0 n0t f0rg3t</target>
</trans-unit>
<trans-unit id="4GvZndQ" resname="security.login.submit-btn.label">
<source>security.login.submit-btn.label</source>
<target state="final">3nt3r</target>
</trans-unit>
<trans-unit id="W.225Bj" resname="settings.title">
<source>settings.title</source>
<target state="final">S3tt1ngz</target>
</trans-unit>
<trans-unit id="a1bm5YC" resname="settings.header">
<source>settings.header</source>
<target state="final">S3tt1ngz</target>
</trans-unit>
<trans-unit id="qUrJMfo" resname="settings.flashes.inexistinglocale">
<source>settings.flashes.inexistinglocale</source>
<target state="final">L0c4L3 d03z n0t 3x1st</target>
</trans-unit>
<trans-unit id="XcSnV8X" resname="settings.flashes.localesaved">
<source>settings.flashes.localesaved</source>
<target state="final">L0c4L3 b s4v3d</target>
</trans-unit>
<trans-unit id="kpbry92" resname="settings.flashes.repeatpasswordnotok">
<source>settings.flashes.repeatpasswordnotok</source>
<target state="final">P4ssw0rdz 1z n0t 3qu4L</target>
</trans-unit>
<trans-unit id="DpJMsln" resname="settings.flashes.currentpassnotok">
<source>settings.flashes.currentpassnotok</source>
<target state="final">P4ssw0rd b n0t c0rr3ct</target>
</trans-unit>
<trans-unit id="tB3o6UA" resname="settings.flashes.passwordsaved">
<source>settings.flashes.passwordsaved</source>
<target state="final">P4ssw0rd b s4v3d</target>
</trans-unit>
<trans-unit id="aAt4FLV" resname="settings.password.header">
<source>settings.password.header</source>
<target state="final">P4ssw0rd</target>
</trans-unit>
<trans-unit id="qqAzx9b" resname="settings.password.current.label">
<source>settings.password.current.label</source>
<target state="final">Curr3nt p4ssw0rd</target>
</trans-unit>
<trans-unit id="rr8bU5t" resname="settings.password.current.placeholder">
<source>settings.password.current.placeholder</source>
<target state="final">qwerty</target>
</trans-unit>
<trans-unit id="YVhGwqr" resname="settings.password.password.label">
<source>settings.password.password.label</source>
<target state="final">N3w p4ssw0rd</target>
</trans-unit>
<trans-unit id="OZpjE9T" resname="settings.password.password.placeholder">
<source>settings.password.password.placeholder</source>
<target state="final">azerty</target>
</trans-unit>
<trans-unit id="TNBtpD_" resname="settings.password.repeat.label">
<source>settings.password.repeat.label</source>
<target state="final">R3p34t p4ssw0rd</target>
</trans-unit>
<trans-unit id="bYb7Tz." resname="settings.password.repeat.placeholder">
<source>settings.password.repeat.placeholder</source>
<target state="final">azerty</target>
</trans-unit>
<trans-unit id="_OGkxC8" resname="settings.other.header">
<source>settings.other.header</source>
<target state="final">0th3r s3tt1ngz</target>
</trans-unit>
<trans-unit id="78Yswpr" resname="settings.other.locale.label">
<source>settings.other.locale.label</source>
<target state="final">L0c4L3</target>
</trans-unit>
<trans-unit id="WQ4J4Fx" resname="settings.submit.label">
<source>settings.submit.label</source>
<target state="final">Subm1t</target>
</trans-unit>
<trans-unit id="0eRDYaC" resname="job.index.title">
<source>job.index.title</source>
<target state="final">0v3rv13w</target>
</trans-unit>
<trans-unit id="J.7dT3_" resname="job.index.header">
<source>job.index.header</source>
<target state="final">0v3rv13w 0f t3h cr0nj0bz</target>
</trans-unit>
<trans-unit id="T0URP45" resname="job.index.table.headers.name">
<source>job.index.table.headers.name</source>
<target state="final">N4m3</target>
</trans-unit>
<trans-unit id="vNWgxUJ" resname="job.index.table.headers.host">
<source>job.index.table.headers.host</source>
<target state="final">H0st</target>
</trans-unit>
<trans-unit id="1YQJCGW" resname="job.index.table.headers.interval">
<source>job.index.table.headers.interval</source>
<target state="final">1nt3rv4L</target>
</trans-unit>
<trans-unit id="mZ.gLJq" resname="job.index.table.headers.nextrun">
<source>job.index.table.headers.nextrun</source>
<target state="final">N3xtrun</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>
</trans-unit>
<trans-unit id="SF_jXZZ" resname="job.view.title">
<source>job.view.title</source>
<target state="final">0v3rv13w 0f runz 4 _jobname_</target>
</trans-unit>
<trans-unit id="ysrfPnl" resname="job.view.header">
<source>job.view.header</source>
<target state="final">0v3rv13w 0f runz 4 _jobname_</target>
</trans-unit>
<trans-unit id="KBsRhNs" resname="job.view.edit">
<source>job.view.edit</source>
<target state="final">3d1t j0b</target>
</trans-unit>
<trans-unit id="xvdCrX2" resname="job.view.show.onlyfailed">
<source>job.view.show.onlyfailed</source>
<target state="final">0nLy sh0w f41L3d runz</target>
</trans-unit>
<trans-unit id="_zWmcxu" resname="job.view.show.all">
<source>job.view.show.all</source>
<target state="final">Sh0w 4LL runz</target>
</trans-unit>
<trans-unit id="lpu6CQS" resname="job.view.results.exitcode">
<source>job.view.results.exitcode</source>
<target state="final">3x1t c0d3</target>
</trans-unit>
<trans-unit id="9EemRnl" resname="job.view.results.runtime">
<source>job.view.results.runtime</source>
<target state="final">Runt1m3</target>
</trans-unit>
<trans-unit id="juzEgkd" resname="job.view.results.manual">
<source>job.view.results.manual</source>
<target state="final">M4nu4L run</target>
</trans-unit>
<trans-unit id="H.6.gtG" resname="job.view.results.noresults.failed">
<source>job.view.results.noresults.failed</source>
<target state="final">N0 f41L3d runz f0und</target>
</trans-unit>
<trans-unit id="u3QkQi1" resname="job.view.results.noresults.all">
<source>job.view.results.noresults.all</source>
<target state="final">N0 runz f0und</target>
</trans-unit>
<trans-unit id="zBxeZSL" resname="job.edit.title">
<source>job.edit.title</source>
<target state="final">3d1t j0b</target>
</trans-unit>
<trans-unit id="CDJme2K" resname="job.edit.header">
<source>job.edit.header</source>
<target state="final">3d1t j0b _jobname_</target>
</trans-unit>
<trans-unit id="eU2m5Zf" resname="job.edit.flashes.jobedited">
<source>job.edit.flashes.jobedited</source>
<target state="final">Cr0nj0b b succ3ssfuLLy d3L3t3d</target>
</trans-unit>
<trans-unit id="CPdXoro" resname="job.add.title">
<source>job.add.title</source>
<target state="final">4dd j0b</target>
</trans-unit>
<trans-unit id=".pKQkI0" resname="job.add.header">
<source>job.add.header</source>
<target state="final">4dd n3w j0b</target>
</trans-unit>
<trans-unit id="66iDRun" resname="job.add.flashes.jobadded">
<source>job.add.flashes.jobadded</source>
<target state="final">Cr0nj0b succ3ssfuLLy 4dd3d</target>
</trans-unit>
<trans-unit id="h9UI9nK" resname="job.addedit.generalinfo.header">
<source>job.addedit.generalinfo.header</source>
<target state="final">G3n3r4L 1nf0</target>
</trans-unit>
<trans-unit id="2.rDz7o" resname="job.addedit.generalinfo.name.label">
<source>job.addedit.generalinfo.name.label</source>
<target state="final">N4m3</target>
</trans-unit>
<trans-unit id="3FV9jvC" resname="job.addedit.generalinfo.name.placeholder">
<source>job.addedit.generalinfo.name.placeholder</source>
<target state="final">Syst3m upd4t3</target>
</trans-unit>
<trans-unit id="j9FTkRt" resname="job.addedit.generalinfo.name.helptext">
<source>job.addedit.generalinfo.name.helptext</source>
<target state="final">j00 c4n cr34t3 c0L0r3d t4gz by us1n' [t4g]</target>
</trans-unit>
<trans-unit id="CL7GVb3" resname="job.addedit.generalinfo.interval.label">
<source>job.addedit.generalinfo.interval.label</source>
<target state="final">1nt3rv4L (1n s3c0ndz)</target>
</trans-unit>
<trans-unit id="iSs41MC" resname="job.addedit.generalinfo.interval.placeholder">
<source>job.addedit.generalinfo.interval.placeholder</source>
<target state="final">3600</target>
</trans-unit>
<trans-unit id="OTx9beS" resname="job.addedit.generalinfo.interval.patterns.label">
<source>job.addedit.generalinfo.interval.patterns.label</source>
<target state="final">P4tt3rnz</target>
</trans-unit>
<trans-unit id="0ATgoBe" resname="job.addedit.generalinfo.interval.patterns.minute">
<source>job.addedit.generalinfo.interval.patterns.minute</source>
<target state="final">3v3ry m1nut3</target>
</trans-unit>
<trans-unit id="f0o1GlZ" resname="job.addedit.generalinfo.interval.patterns.hour">
<source>job.addedit.generalinfo.interval.patterns.hour</source>
<target state="final">3v3ry h0ur</target>
</trans-unit>
<trans-unit id="hCmc8AA" resname="job.addedit.generalinfo.interval.patterns.day">
<source>job.addedit.generalinfo.interval.patterns.day</source>
<target state="final">3v3ry d4y</target>
</trans-unit>
<trans-unit id="zmUXvRU" resname="job.addedit.generalinfo.interval.patterns.week">
<source>job.addedit.generalinfo.interval.patterns.week</source>
<target state="final">3v3ry w33k</target>
</trans-unit>
<trans-unit id="ISEzS__" resname="job.addedit.generalinfo.interval.patterns.4week">
<source>job.addedit.generalinfo.interval.patterns.4week</source>
<target state="final">3v3ry 4 w33kz</target>
</trans-unit>
<trans-unit id="HWDmm9f" resname="job.addedit.generalinfo.nextrun.label">
<source>job.addedit.generalinfo.nextrun.label</source>
<target state="final">N3xt run</target>
</trans-unit>
<trans-unit id="e1RkhOF" resname="job.addedit.generalinfo.lastrun.label">
<source>job.addedit.generalinfo.lastrun.label</source>
<target state="final">L4st run</target>
</trans-unit>
<trans-unit id="kw_hgon" resname="job.addedit.generalinfo.lastrun.eternal.label">
<source>job.addedit.generalinfo.lastrun.eternal.label</source>
<target state="final">3t3rn4L</target>
</trans-unit>
<trans-unit id="sdUg23G" resname="job.addedit.generalinfo.retention.label">
<source>job.addedit.generalinfo.retention.label</source>
<target state="final">R3t3nt10n (1n d4yz)</target>
</trans-unit>
<trans-unit id="nWofDbf" resname="job.addedit.generalinfo.retention.placeholder">
<source>job.addedit.generalinfo.retention.placeholder</source>
<target state="final">180</target>
</trans-unit>
<trans-unit id="eHfLjBO" resname="job.addedit.generalinfo.retention.helptext">
<source>job.addedit.generalinfo.retention.helptext</source>
<target state="final">H0w m4ny d4yz (4t l34st) 2 k33p runz 0f d1z j0b 1n t3h d4t4b4s3</target>
</trans-unit>
<trans-unit id="kfQDfoB" resname="job.addedit.generalinfo.failpercentage.label">
<source>job.addedit.generalinfo.failpercentage.label</source>
<target state="final">M4x f41L p3rc3nt4g3</target>
</trans-unit>
<trans-unit id="Ytyhy77" resname="job.addedit.generalinfo.faildays.label">
<source>job.addedit.generalinfo.faildays.label</source>
<target state="final">Numb3r 0f d4yz c4LcuL4t3d 4 f41L p3rc3nt4g3</target>
</trans-unit>
<trans-unit id="2O_wFoD" resname="job.addedit.generalinfo.faildays.placeholder">
<source>job.addedit.generalinfo.faildays.placeholder</source>
<target state="final">7</target>
</trans-unit>
<trans-unit id="UvMb6oM" resname="job.addedit.generalinfo.hostlabel.label">
<source>job.addedit.generalinfo.hostlabel.label</source>
<target state="final">H0st l4b3L</target>
</trans-unit>
<trans-unit id="GjPGKoQ" resname="job.addedit.generalinfo.hostlabel.placeholder">
<source>job.addedit.generalinfo.hostlabel.placeholder</source>
<target state="final">N3wb13 s3rv3r</target>
</trans-unit>
<trans-unit id="moGlM4y" resname="job.addedit.generalinfo.hostlabel.helptext">
<source>job.addedit.generalinfo.hostlabel.helptext</source>
<target state="final">Wh1ch l4b3L 4 t3h h0stn4m3 sh0uLd b d1spL4y3d? 1f 3mpty t3h d1z w1LL b t3h h0stn4m3 pr0v1d3d b3L0w</target>
</trans-unit>
<trans-unit id="VEORPjf" resname="job.addedit.jobdetails.header">
<source>job.addedit.jobdetails.header</source>
<target state="final">J0b d3t41Lz</target>
</trans-unit>
<trans-unit id="7haVx1t" resname="job.addedit.crontype.label">
<source>job.addedit.crontype.label</source>
<target state="final">J0b typ3</target>
</trans-unit>
<trans-unit id="kw_PINJ" resname="job.addedit.crontype.command.label">
<source>job.addedit.crontype.command.label</source>
<target state="final">C0mm4nd</target>
</trans-unit>
<trans-unit id="sxAwNxW" resname="job.addedit.crontype.command.header">
<source>job.addedit.crontype.command.header</source>
<target state="final">C0mm4nd d3t41Lz</target>
</trans-unit>
<trans-unit id="ZNQUrj7" resname="job.addedit.crontype.command.command.label">
<source>job.addedit.crontype.command.command.label</source>
<target state="final">C0mm4nd</target>
</trans-unit>
<trans-unit id="aWesJsc" resname="job.addedit.crontype.command.command.placeholder">
<source>job.addedit.crontype.command.command.placeholder</source>
<target state="final">sudo rm -rf /</target>
</trans-unit>
<trans-unit id="os3VpHs" resname="job.addedit.crontype.command.response.label">
<source>job.addedit.crontype.command.response.label</source>
<target state="final">3xp3ct3d 3x1t c0d3</target>
</trans-unit>
<trans-unit id="RIWj._C" resname="job.addedit.crontype.command.response.placeholder">
<source>job.addedit.crontype.command.response.placeholder</source>
<target state="final">0</target>
</trans-unit>
<trans-unit id="A66vsIi" resname="job.addedit.crontype.reboot.label">
<source>job.addedit.crontype.reboot.label</source>
<target state="final">R3b00t</target>
</trans-unit>
<trans-unit id="Kv8GihO" resname="job.addedit.crontype.reboot.header">
<source>job.addedit.crontype.reboot.header</source>
<target state="final">R3b00t j0b d3t41Lz</target>
</trans-unit>
<trans-unit id="hy8z.7x" resname="job.addedit.crontype.reboot.reboot.command.label">
<source>job.addedit.crontype.reboot.reboot.command.label</source>
<target state="final">R3b00t c0mm4nd</target>
</trans-unit>
<trans-unit id="tw3YvLz" resname="job.addedit.crontype.reboot.reboot.command.placeholder">
<source>job.addedit.crontype.reboot.reboot.command.placeholder</source>
<target state="final">sudo telinit 6</target>
</trans-unit>
<trans-unit id="p0Seiyx" resname="job.addedit.crontype.reboot.reboot.command.helptext">
<source>job.addedit.crontype.reboot.reboot.command.helptext</source>
<target state="final">Us3 {reboot-delay} 0r {reboot-delay-secs} 2 4dd t3h d3L4y 1n y0 c0mm4nd</target>
</trans-unit>
<trans-unit id="bEPANkU" resname="job.addedit.crontype.reboot.reboot.delay.label">
<source>job.addedit.crontype.reboot.reboot.delay.label</source>
<target state="final">R3b00t d3L4y (1n m1nut3z)</target>
</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">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>
<target state="final">D3L4y b3tw33n tr1gg3r1n' r3b00t n 4ctu4L r3b00t</target>
</trans-unit>
<trans-unit id="EbgF04P" resname="job.addedit.crontype.reboot.reboot.duration.label">
<source>job.addedit.crontype.reboot.reboot.duration.label</source>
<target state="final">R3b00t dur4t10n (1n m1nut3z)</target>
</trans-unit>
<trans-unit id="1FVsnYa" resname="job.addedit.crontype.reboot.reboot.duration.placeholder">
<source>job.addedit.crontype.reboot.reboot.duration.placeholder</source>
<target state="final">10</target>
</trans-unit>
<trans-unit id="VWOhJBY" resname="job.addedit.crontype.reboot.reboot.duration.helptext">
<source>job.addedit.crontype.reboot.reboot.duration.helptext</source>
<target state="final">t3h 4m0unt 0f t1m3 t3h syst3m t4k3z 2 4ctu4LLy r3b00t</target>
</trans-unit>
<trans-unit id="ENLtpfK" resname="job.addedit.crontype.reboot.getservices.command.label">
<source>job.addedit.crontype.reboot.getservices.command.label</source>
<target state="final">G3t s3rv1c3z c0mm4nd</target>
</trans-unit>
<trans-unit id="yxIObGq" resname="job.addedit.crontype.reboot.getservices.command.placeholder">
<source>job.addedit.crontype.reboot.getservices.command.placeholder</source>
<target state="final">ps -aux</target>
</trans-unit>
<trans-unit id="aurJI0X" resname="job.addedit.crontype.reboot.getservices.response.label">
<source>job.addedit.crontype.reboot.getservices.response.label</source>
<target state="final">G3t s3rv1c3z c0mm4nd 3x1t c0d3</target>
</trans-unit>
<trans-unit id="xMbagrx" resname="job.addedit.crontype.reboot.getservices.response.placeholder">
<source>job.addedit.crontype.reboot.getservices.response.placeholder</source>
<target state="final">0</target>
</trans-unit>
<trans-unit id="ze4mr8D" resname="job.addedit.crontype.http.label">
<source>job.addedit.crontype.http.label</source>
<target state="final">HTTP r3qu3st</target>
</trans-unit>
<trans-unit id="oJn3xvZ" resname="job.addedit.crontype.http.header">
<source>job.addedit.crontype.http.header</source>
<target state="final">HTTP r3qu3st d3t41Lz</target>
</trans-unit>
<trans-unit id="hwhqEtB" resname="job.addedit.crontype.http.url.label">
<source>job.addedit.crontype.http.url.label</source>
<target state="final">UrL</target>
</trans-unit>
<trans-unit id="31CZuNl" resname="job.addedit.crontype.http.url.placeholder">
<source>job.addedit.crontype.http.url.placeholder</source>
<target state="final">https://www.h4x0r.l33t</target>
</trans-unit>
<trans-unit id="gGpY1Gz" resname="job.addedit.crontype.http.basic-auth.username.label">
<source>job.addedit.crontype.http.basic-auth.username.label</source>
<target state="final">Us3rn4m3 4 b4s1c 4uth</target>
</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">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>
<target state="final">P4ssw0rd 4 b4s1c 4uth</target>
</trans-unit>
<trans-unit id="ROX9KeU" resname="job.addedit.crontype.http.basic-auth.password.placeholder">
<source>job.addedit.crontype.http.basic-auth.password.placeholder</source>
<target state="final">c0rr3ct h0rs3 b4tt3ry st4pL3</target>
</trans-unit>
<trans-unit id="pRPdCPd" resname="job.addedit.crontype.http.basic-auth.password.helptext">
<source>job.addedit.crontype.http.basic-auth.password.helptext</source>
<target state="final">d1z f13Ld b b31n' s4v3d 4z @ s3cr3t</target>
</trans-unit>
<trans-unit id="SnZXqhK" resname="job.addedit.crontype.http.response.label">
<source>job.addedit.crontype.http.response.label</source>
<target state="final">3xp3ct3d http st4tuz c0d3</target>
</trans-unit>
<trans-unit id="aLSDlKd" resname="job.addedit.crontype.http.response.placeholder">
<source>job.addedit.crontype.http.response.placeholder</source>
<target state="final">503</target>
</trans-unit>
<trans-unit id="oV9GuKJ" resname="job.addedit.hosttype.label">
<source>job.addedit.hosttype.label</source>
<target state="final">H0st typ3</target>
</trans-unit>
<trans-unit id="eBXA85X" resname="job.addedit.hosttype.local.label">
<source>job.addedit.hosttype.local.label</source>
<target state="final">L0c4L</target>
</trans-unit>
<trans-unit id="ZeflLHK" resname="job.addedit.hosttype.local.header">
<source>job.addedit.hosttype.local.header</source>
<target state="final">L0c4Lh0st d3t41Lz</target>
</trans-unit>
<trans-unit id="1atQ8Ot" resname="job.addedit.hosttype.local.nodetails">
<source>job.addedit.hosttype.local.nodetails</source>
<target state="final">N0 0pt10nz</target>
</trans-unit>
<trans-unit id="oUUVCNC" resname="job.addedit.hosttype.ssh.label">
<source>job.addedit.hosttype.ssh.label</source>
<target state="final">SSH</target>
</trans-unit>
<trans-unit id="HIND6DD" resname="job.addedit.hosttype.ssh.header">
<source>job.addedit.hosttype.ssh.header</source>
<target state="final">SSH h0st d3t41Lz</target>
</trans-unit>
<trans-unit id="n_JNj3z" resname="job.addedit.hosttype.ssh.hostname.label">
<source>job.addedit.hosttype.ssh.hostname.label</source>
<target state="final">H0stn4m3</target>
</trans-unit>
<trans-unit id="4ZEnTLC" resname="job.addedit.hosttype.ssh.hostname.placeholder">
<source>job.addedit.hosttype.ssh.hostname.placeholder</source>
<target state="final">g4t3w4y.n00b.0rg</target>
</trans-unit>
<trans-unit id="08j0NEG" resname="job.addedit.hosttype.ssh.username.label">
<source>job.addedit.hosttype.ssh.username.label</source>
<target state="final">Us3rn4m3</target>
</trans-unit>
<trans-unit id="EJOhQKe" resname="job.addedit.hosttype.ssh.username.placeholder">
<source>job.addedit.hosttype.ssh.username.placeholder</source>
<target state="final">anonymous</target>
</trans-unit>
<trans-unit id="VEZQ7fY" resname="job.addedit.hosttype.ssh.privatekey.label">
<source>job.addedit.hosttype.ssh.privatekey.label</source>
<target state="final">Pr1v4t3 k3y</target>
</trans-unit>
<trans-unit id="1MzOMSK" resname="job.addedit.hosttype.ssh.privatekey.helptext">
<source>job.addedit.hosttype.ssh.privatekey.helptext</source>
<target state="final">d1z f1L3 b b31n' s4v3z 4z @ s3cr3t</target>
</trans-unit>
<trans-unit id="uVSkZpl" resname="job.addedit.hosttype.ssh.privatekey.keep.label">
<source>job.addedit.hosttype.ssh.privatekey.keep.label</source>
<target state="final">K33p</target>
</trans-unit>
<trans-unit id="nxl.bTZ" resname="job.addedit.hosttype.ssh.passphrase.label">
<source>job.addedit.hosttype.ssh.passphrase.label</source>
<target state="final">P4ssphr4s3 4 pr1v4t3 k3y</target>
</trans-unit>
<trans-unit id="rYaggeh" resname="job.addedit.hosttype.ssh.passphrase.placeholder">
<source>job.addedit.hosttype.ssh.passphrase.placeholder</source>
<target state="final">w00t w00t</target>
</trans-unit>
<trans-unit id="MAQ8t1s" resname="job.addedit.hosttype.ssh.passphrase.helptext">
<source>job.addedit.hosttype.ssh.passphrase.helptext</source>
<target state="final">1f pr1v4t3 k3y b 3mpty d1z f13Ld b b31n' us3d 4z ssh-p4ssw0rd d1z f13Ld b b31n' s4v3d 4z @ s3cr3t</target>
</trans-unit>
<trans-unit id="8q0z3bf" resname="job.addedit.containertype.label">
<source>job.addedit.containertype.label</source>
<target state="final">C0nt41n3r typ3</target>
</trans-unit>
<trans-unit id="ZPis4Xx" resname="job.addedit.containertype.none.label">
<source>job.addedit.containertype.none.label</source>
<target state="final">N0n3</target>
</trans-unit>
<trans-unit id="AGADEB_" resname="job.addedit.containertype.docker.label">
<source>job.addedit.containertype.docker.label</source>
<target state="final">D0ck3r</target>
</trans-unit>
<trans-unit id="1hg_Lri" resname="job.addedit.containertype.docker.header">
<source>job.addedit.containertype.docker.header</source>
<target state="final">D0ck3r c0nt41n3r d3t41Lz</target>
</trans-unit>
<trans-unit id="fddg1.3" resname="job.addedit.containertype.docker.service.label">
<source>job.addedit.containertype.docker.service.label</source>
<target state="final">S3rv1c3</target>
</trans-unit>
<trans-unit id="mmNVzOj" resname="job.addedit.containertype.docker.service.placeholder">
<source>job.addedit.containertype.docker.service.placeholder</source>
<target state="final">gopher</target>
</trans-unit>
<trans-unit id="kaHv1qa" resname="job.addedit.containertype.docker.username.label">
<source>job.addedit.containertype.docker.username.label</source>
<target state="final">Us3rn4m3</target>
</trans-unit>
<trans-unit id="UzbvieZ" resname="job.addedit.containertype.docker.username.placeholder">
<source>job.addedit.containertype.docker.username.placeholder</source>
<target state="final">root</target>
</trans-unit>
<trans-unit id="BsHxmnc" resname="job.addedit.variables.header">
<source>job.addedit.variables.header</source>
<target state="final">V4r14bL3z</target>
</trans-unit>
<trans-unit id="pawk4nL" resname="job.addedit.variables.secret.label">
<source>job.addedit.variables.secret.label</source>
<target state="final">S3cr3t</target>
</trans-unit>
<trans-unit id="roGfPFi" resname="job.addedit.variables.name.placeholder">
<source>job.addedit.variables.name.placeholder</source>
<target state="final">N4m3</target>
</trans-unit>
<trans-unit id="WWsh_72" resname="job.addedit.variables.value.placeholder">
<source>job.addedit.variables.value.placeholder</source>
<target state="final">V4Lu3</target>
</trans-unit>
<trans-unit id="Ve0r_Wy" resname="job.addedit.variables.helptext">
<source>job.addedit.variables.helptext</source>
<target state="final">j00 c4n 4dd v4r14bL3z by us1n' {v4r14bL3-n4m3} 1n j0b d3t41Lz</target>
</trans-unit>
<trans-unit id="tL8wand" resname="job.addedit.variables.add.label">
<source>job.addedit.variables.add.label</source>
<target state="final">4dd variable</target>
</trans-unit>
<trans-unit id="or7JWUe" resname="job.addedit.submit.label">
<source>job.addedit.submit.label</source>
<target state="final">Subm1t!</target>
</trans-unit>
<trans-unit id="g7pEpsB" resname="footer.title">
<source>footer.title</source>
<target state="final">W3bcr0n m4n4g3m3nt</target>
</trans-unit>
<trans-unit id="Lz7YjVX" resname="footer.source">
<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

@ -0,0 +1,646 @@
<?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="qvIyBkY" resname="title">
<source>title</source>
<target state="final">Webcron management</target>
</trans-unit>
<trans-unit id="HgWEol2" resname="header">
<source>header</source>
<target state="final">Webcron management</target>
</trans-unit>
<trans-unit id="Q54TzT5" resname="demomode.flashnotice">
<source>demomode.flashnotice</source>
<target state="final">Deze applicatie is in demo modus. Alle functies zijn actief, maar taken worden niet uitgevoerd</target>
</trans-unit>
<trans-unit id="rpq61qN" resname="demomode.credentials.header">
<source>demomode.credentials.header</source>
<target state="final">Aanmeldgegevens voor demo modus:</target>
</trans-unit>
<trans-unit id="jUz9fVE" resname="demomode.credentials.username">
<source>demomode.credentials.username</source>
<target state="final">Gebruikersnaam</target>
</trans-unit>
<trans-unit id="QNu97w_" resname="demomode.credentials.password">
<source>demomode.credentials.password</source>
<target state="final">Wachtwoord</target>
</trans-unit>
<trans-unit id="U8IBiON" resname="menu.overview">
<source>menu.overview</source>
<target state="final">Overzicht</target>
</trans-unit>
<trans-unit id="iGC7eCk" resname="menu.add">
<source>menu.add</source>
<target state="final">Taak toevoegen</target>
</trans-unit>
<trans-unit id="4UTsL.X" resname="menu.settings">
<source>menu.settings</source>
<target state="final">Instellingen</target>
</trans-unit>
<trans-unit id="wBTFKFR" resname="menu.logout">
<source>menu.logout</source>
<target state="final">Afmelden</target>
</trans-unit>
<trans-unit id="4cxyGOO" resname="security.login.username.label">
<source>security.login.username.label</source>
<target state="final">Gebruikersnaam</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">Wachtwoord</target>
</trans-unit>
<trans-unit id="3rU2_6v" resname="security.login.password.placeholder">
<source>security.login.password.placeholder</source>
<target state="final">abc123</target>
</trans-unit>
<trans-unit id="0MGdvgf" resname="security.login.remember.label">
<source>security.login.remember.label</source>
<target state="final">Onthoud mij!</target>
</trans-unit>
<trans-unit id="4GvZndQ" resname="security.login.submit-btn.label">
<source>security.login.submit-btn.label</source>
<target state="final">Aanmelden</target>
</trans-unit>
<trans-unit id="W.225Bj" resname="settings.title">
<source>settings.title</source>
<target state="final">Instellingen</target>
</trans-unit>
<trans-unit id="a1bm5YC" resname="settings.header">
<source>settings.header</source>
<target state="final">Instellingen</target>
</trans-unit>
<trans-unit id="qUrJMfo" resname="settings.flashes.inexistinglocale">
<source>settings.flashes.inexistinglocale</source>
<target state="final">Taal bestaat niet</target>
</trans-unit>
<trans-unit id="XcSnV8X" resname="settings.flashes.localesaved">
<source>settings.flashes.localesaved</source>
<target state="final">Taal werd opgeslagen</target>
</trans-unit>
<trans-unit id="kpbry92" resname="settings.flashes.repeatpasswordnotok">
<source>settings.flashes.repeatpasswordnotok</source>
<target state="final">Wachtwoorden zijn niet gelijk</target>
</trans-unit>
<trans-unit id="DpJMsln" resname="settings.flashes.currentpassnotok">
<source>settings.flashes.currentpassnotok</source>
<target state="final">Wachtwoord is not correct</target>
</trans-unit>
<trans-unit id="tB3o6UA" resname="settings.flashes.passwordsaved">
<source>settings.flashes.passwordsaved</source>
<target state="final">Wachtwoord werd opgeslagen</target>
</trans-unit>
<trans-unit id="aAt4FLV" resname="settings.password.header">
<source>settings.password.header</source>
<target state="final">Wachtwoord</target>
</trans-unit>
<trans-unit id="qqAzx9b" resname="settings.password.current.label">
<source>settings.password.current.label</source>
<target state="final">Huidig wachtwoord</target>
</trans-unit>
<trans-unit id="rr8bU5t" resname="settings.password.current.placeholder">
<source>settings.password.current.placeholder</source>
<target state="final">abc123</target>
</trans-unit>
<trans-unit id="YVhGwqr" resname="settings.password.password.label">
<source>settings.password.password.label</source>
<target state="final">Nieuw wachtwoord</target>
</trans-unit>
<trans-unit id="OZpjE9T" resname="settings.password.password.placeholder">
<source>settings.password.password.placeholder</source>
<target state="final">123abc</target>
</trans-unit>
<trans-unit id="TNBtpD_" resname="settings.password.repeat.label">
<source>settings.password.repeat.label</source>
<target state="final">Herhaal wachtwoord</target>
</trans-unit>
<trans-unit id="bYb7Tz." resname="settings.password.repeat.placeholder">
<source>settings.password.repeat.placeholder</source>
<target state="final">123abc</target>
</trans-unit>
<trans-unit id="_OGkxC8" resname="settings.other.header">
<source>settings.other.header</source>
<target state="final">Andere instellingen</target>
</trans-unit>
<trans-unit id="78Yswpr" resname="settings.other.locale.label">
<source>settings.other.locale.label</source>
<target state="final">Taal</target>
</trans-unit>
<trans-unit id="WQ4J4Fx" resname="settings.submit.label">
<source>settings.submit.label</source>
<target state="final">Verzend!</target>
</trans-unit>
<trans-unit id="0eRDYaC" resname="job.index.title">
<source>job.index.title</source>
<target state="final">Overzicht</target>
</trans-unit>
<trans-unit id="J.7dT3_" resname="job.index.header">
<source>job.index.header</source>
<target state="final">Overzicht van de geplande taken</target>
</trans-unit>
<trans-unit id="T0URP45" resname="job.index.table.headers.name">
<source>job.index.table.headers.name</source>
<target state="final">Naam</target>
</trans-unit>
<trans-unit id="vNWgxUJ" resname="job.index.table.headers.host">
<source>job.index.table.headers.host</source>
<target state="final">Host</target>
</trans-unit>
<trans-unit id="1YQJCGW" resname="job.index.table.headers.interval">
<source>job.index.table.headers.interval</source>
<target state="final">Interval</target>
</trans-unit>
<trans-unit id="mZ.gLJq" resname="job.index.table.headers.nextrun">
<source>job.index.table.headers.nextrun</source>
<target state="final">Volgende uitvoering</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>
</trans-unit>
<trans-unit id="SF_jXZZ" resname="job.view.title">
<source>job.view.title</source>
<target state="final">Overzicht van uitvoeringen van _jobname_</target>
</trans-unit>
<trans-unit id="ysrfPnl" resname="job.view.header">
<source>job.view.header</source>
<target state="final">Overzicht van uitvoeringen van _jobname_</target>
</trans-unit>
<trans-unit id="KBsRhNs" resname="job.view.edit">
<source>job.view.edit</source>
<target state="final">Bewerk taak</target>
</trans-unit>
<trans-unit id="xvdCrX2" resname="job.view.show.onlyfailed">
<source>job.view.show.onlyfailed</source>
<target state="final">Toon alleen gefaalde uitvoeringen</target>
</trans-unit>
<trans-unit id="_zWmcxu" resname="job.view.show.all">
<source>job.view.show.all</source>
<target state="final">Toon alle uitvoeringen</target>
</trans-unit>
<trans-unit id="lpu6CQS" resname="job.view.results.exitcode">
<source>job.view.results.exitcode</source>
<target state="final">Resultaat</target>
</trans-unit>
<trans-unit id="9EemRnl" resname="job.view.results.runtime">
<source>job.view.results.runtime</source>
<target state="final">Duurtijd</target>
</trans-unit>
<trans-unit id="juzEgkd" resname="job.view.results.manual">
<source>job.view.results.manual</source>
<target state="final">Manuele uitvoering</target>
</trans-unit>
<trans-unit id="H.6.gtG" resname="job.view.results.noresults.failed">
<source>job.view.results.noresults.failed</source>
<target state="final">Geen gefaalde uitvoeringen gevonden</target>
</trans-unit>
<trans-unit id="u3QkQi1" resname="job.view.results.noresults.all">
<source>job.view.results.noresults.all</source>
<target state="final">Geen uitvoeringen gevonden</target>
</trans-unit>
<trans-unit id="zBxeZSL" resname="job.edit.title">
<source>job.edit.title</source>
<target state="final">Bewerk taak</target>
</trans-unit>
<trans-unit id="CDJme2K" resname="job.edit.header">
<source>job.edit.header</source>
<target state="final">Bewerk taak _jobname_</target>
</trans-unit>
<trans-unit id="eU2m5Zf" resname="job.edit.flashes.jobedited">
<source>job.edit.flashes.jobedited</source>
<target state="final">Taak werd succesvol bijgewerkt</target>
</trans-unit>
<trans-unit id="CPdXoro" resname="job.add.title">
<source>job.add.title</source>
<target state="final">Taak toevoegen</target>
</trans-unit>
<trans-unit id=".pKQkI0" resname="job.add.header">
<source>job.add.header</source>
<target state="final">Nieuwe taak toevoegen</target>
</trans-unit>
<trans-unit id="66iDRun" resname="job.add.flashes.jobadded">
<source>job.add.flashes.jobadded</source>
<target state="final">Taak werd succesvol toegevoegd</target>
</trans-unit>
<trans-unit id="h9UI9nK" resname="job.addedit.generalinfo.header">
<source>job.addedit.generalinfo.header</source>
<target state="final">Algemene info</target>
</trans-unit>
<trans-unit id="2.rDz7o" resname="job.addedit.generalinfo.name.label">
<source>job.addedit.generalinfo.name.label</source>
<target state="final">Naam</target>
</trans-unit>
<trans-unit id="3FV9jvC" resname="job.addedit.generalinfo.name.placeholder">
<source>job.addedit.generalinfo.name.placeholder</source>
<target state="final">Systemen updaten</target>
</trans-unit>
<trans-unit id="j9FTkRt" resname="job.addedit.generalinfo.name.helptext">
<source>job.addedit.generalinfo.name.helptext</source>
<target state="final">Je kan gekleurde tags toevoegen met [tag]</target>
</trans-unit>
<trans-unit id="CL7GVb3" resname="job.addedit.generalinfo.interval.label">
<source>job.addedit.generalinfo.interval.label</source>
<target state="final">Interval (in seconden)</target>
</trans-unit>
<trans-unit id="iSs41MC" resname="job.addedit.generalinfo.interval.placeholder">
<source>job.addedit.generalinfo.interval.placeholder</source>
<target state="final">3600</target>
</trans-unit>
<trans-unit id="OTx9beS" resname="job.addedit.generalinfo.interval.patterns.label">
<source>job.addedit.generalinfo.interval.patterns.label</source>
<target state="final">Patronen</target>
</trans-unit>
<trans-unit id="0ATgoBe" resname="job.addedit.generalinfo.interval.patterns.minute">
<source>job.addedit.generalinfo.interval.patterns.minute</source>
<target state="final">Iedere minuut</target>
</trans-unit>
<trans-unit id="f0o1GlZ" resname="job.addedit.generalinfo.interval.patterns.hour">
<source>job.addedit.generalinfo.interval.patterns.hour</source>
<target state="final">Ieder uur</target>
</trans-unit>
<trans-unit id="hCmc8AA" resname="job.addedit.generalinfo.interval.patterns.day">
<source>job.addedit.generalinfo.interval.patterns.day</source>
<target state="final">Iedere dag</target>
</trans-unit>
<trans-unit id="zmUXvRU" resname="job.addedit.generalinfo.interval.patterns.week">
<source>job.addedit.generalinfo.interval.patterns.week</source>
<target state="final">Iedere week</target>
</trans-unit>
<trans-unit id="ISEzS__" resname="job.addedit.generalinfo.interval.patterns.4week">
<source>job.addedit.generalinfo.interval.patterns.4week</source>
<target state="final">Iedere 4 weken</target>
</trans-unit>
<trans-unit id="HWDmm9f" resname="job.addedit.generalinfo.nextrun.label">
<source>job.addedit.generalinfo.nextrun.label</source>
<target state="final">Volgende uitvoering</target>
</trans-unit>
<trans-unit id="e1RkhOF" resname="job.addedit.generalinfo.lastrun.label">
<source>job.addedit.generalinfo.lastrun.label</source>
<target state="final">Laatste uitvoering</target>
</trans-unit>
<trans-unit id="kw_hgon" resname="job.addedit.generalinfo.lastrun.eternal.label">
<source>job.addedit.generalinfo.lastrun.eternal.label</source>
<target state="final">Eeuwig</target>
</trans-unit>
<trans-unit id="sdUg23G" resname="job.addedit.generalinfo.retention.label">
<source>job.addedit.generalinfo.retention.label</source>
<target state="final">Opslag (in dagen)</target>
</trans-unit>
<trans-unit id="nWofDbf" resname="job.addedit.generalinfo.retention.placeholder">
<source>job.addedit.generalinfo.retention.placeholder</source>
<target state="final">180</target>
</trans-unit>
<trans-unit id="eHfLjBO" resname="job.addedit.generalinfo.retention.helptext">
<source>job.addedit.generalinfo.retention.helptext</source>
<target state="final">Hoeveel dagen moeten uitvoeringen van een job bewaard worden</target>
</trans-unit>
<trans-unit id="kfQDfoB" resname="job.addedit.generalinfo.failpercentage.label">
<source>job.addedit.generalinfo.failpercentage.label</source>
<target state="final">Max faalpercentage</target>
</trans-unit>
<trans-unit id="Ytyhy77" resname="job.addedit.generalinfo.faildays.label">
<source>job.addedit.generalinfo.faildays.label</source>
<target state="final">Aantal dagen berekend voor faalpercentage</target>
</trans-unit>
<trans-unit id="2O_wFoD" resname="job.addedit.generalinfo.faildays.placeholder">
<source>job.addedit.generalinfo.faildays.placeholder</source>
<target state="final">7</target>
</trans-unit>
<trans-unit id="UvMb6oM" resname="job.addedit.generalinfo.hostlabel.label">
<source>job.addedit.generalinfo.hostlabel.label</source>
<target state="final">Host label</target>
</trans-unit>
<trans-unit id="GjPGKoQ" resname="job.addedit.generalinfo.hostlabel.placeholder">
<source>job.addedit.generalinfo.hostlabel.placeholder</source>
<target state="final">petrosian.jeroened.be</target>
</trans-unit>
<trans-unit id="moGlM4y" resname="job.addedit.generalinfo.hostlabel.helptext">
<source>job.addedit.generalinfo.hostlabel.helptext</source>
<target state="final">Geef hier een eenvoudig te herkennen hostnaam. Indien leeg zal hostnaam uit de taak details gebruikt worden</target>
</trans-unit>
<trans-unit id="VEORPjf" resname="job.addedit.jobdetails.header">
<source>job.addedit.jobdetails.header</source>
<target state="final">Taak details</target>
</trans-unit>
<trans-unit id="7haVx1t" resname="job.addedit.crontype.label">
<source>job.addedit.crontype.label</source>
<target state="final">Taak type</target>
</trans-unit>
<trans-unit id="kw_PINJ" resname="job.addedit.crontype.command.label">
<source>job.addedit.crontype.command.label</source>
<target state="final">Commando</target>
</trans-unit>
<trans-unit id="sxAwNxW" resname="job.addedit.crontype.command.header">
<source>job.addedit.crontype.command.header</source>
<target state="final">Commando details</target>
</trans-unit>
<trans-unit id="ZNQUrj7" resname="job.addedit.crontype.command.command.label">
<source>job.addedit.crontype.command.command.label</source>
<target state="final">Commando</target>
</trans-unit>
<trans-unit id="aWesJsc" resname="job.addedit.crontype.command.command.placeholder">
<source>job.addedit.crontype.command.command.placeholder</source>
<target state="final">sudo apt update</target>
</trans-unit>
<trans-unit id="os3VpHs" resname="job.addedit.crontype.command.response.label">
<source>job.addedit.crontype.command.response.label</source>
<target state="final">Verwacht resultaat</target>
</trans-unit>
<trans-unit id="RIWj._C" resname="job.addedit.crontype.command.response.placeholder">
<source>job.addedit.crontype.command.response.placeholder</source>
<target state="final">0</target>
</trans-unit>
<trans-unit id="A66vsIi" resname="job.addedit.crontype.reboot.label">
<source>job.addedit.crontype.reboot.label</source>
<target state="final">Herstart</target>
</trans-unit>
<trans-unit id="Kv8GihO" resname="job.addedit.crontype.reboot.header">
<source>job.addedit.crontype.reboot.header</source>
<target state="final">Herstart details</target>
</trans-unit>
<trans-unit id="hy8z.7x" resname="job.addedit.crontype.reboot.reboot.command.label">
<source>job.addedit.crontype.reboot.reboot.command.label</source>
<target state="final">Herstart commando</target>
</trans-unit>
<trans-unit id="tw3YvLz" resname="job.addedit.crontype.reboot.reboot.command.placeholder">
<source>job.addedit.crontype.reboot.reboot.command.placeholder</source>
<target state="final">systemctl reboot</target>
</trans-unit>
<trans-unit id="p0Seiyx" resname="job.addedit.crontype.reboot.reboot.command.helptext">
<source>job.addedit.crontype.reboot.reboot.command.helptext</source>
<target state="final">Gebruik {reboot-delay} of {reboot-delay-secs} om de vertraging toe te voegen in je commando</target>
</trans-unit>
<trans-unit id="bEPANkU" resname="job.addedit.crontype.reboot.reboot.delay.label">
<source>job.addedit.crontype.reboot.reboot.delay.label</source>
<target state="final">Herstart vertraging (in minuten)</target>
</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>
</trans-unit>
<trans-unit id="T55_j9D" resname="job.addedit.crontype.reboot.reboot.delay.helptext">
<source>job.addedit.crontype.reboot.reboot.delay.helptext</source>
<target state="final">Tijd tussen het aanroepen van herstart commando en de eigenlijke herstart</target>
</trans-unit>
<trans-unit id="EbgF04P" resname="job.addedit.crontype.reboot.reboot.duration.label">
<source>job.addedit.crontype.reboot.reboot.duration.label</source>
<target state="final">Duurtijd van herstart (in minuten)</target>
</trans-unit>
<trans-unit id="1FVsnYa" resname="job.addedit.crontype.reboot.reboot.duration.placeholder">
<source>job.addedit.crontype.reboot.reboot.duration.placeholder</source>
<target state="final">10</target>
</trans-unit>
<trans-unit id="VWOhJBY" resname="job.addedit.crontype.reboot.reboot.duration.helptext">
<source>job.addedit.crontype.reboot.reboot.duration.helptext</source>
<target state="final">De tijd dat de effectieve herstart duurt</target>
</trans-unit>
<trans-unit id="ENLtpfK" resname="job.addedit.crontype.reboot.getservices.command.label">
<source>job.addedit.crontype.reboot.getservices.command.label</source>
<target state="final">Commando voor ophalen services</target>
</trans-unit>
<trans-unit id="yxIObGq" resname="job.addedit.crontype.reboot.getservices.command.placeholder">
<source>job.addedit.crontype.reboot.getservices.command.placeholder</source>
<target state="final">systemctl list-units</target>
</trans-unit>
<trans-unit id="aurJI0X" resname="job.addedit.crontype.reboot.getservices.response.label">
<source>job.addedit.crontype.reboot.getservices.response.label</source>
<target state="final">Resultaat van commando voor ophalen services</target>
</trans-unit>
<trans-unit id="xMbagrx" resname="job.addedit.crontype.reboot.getservices.response.placeholder">
<source>job.addedit.crontype.reboot.getservices.response.placeholder</source>
<target state="final">0</target>
</trans-unit>
<trans-unit id="ze4mr8D" resname="job.addedit.crontype.http.label">
<source>job.addedit.crontype.http.label</source>
<target state="final">HTTP request</target>
</trans-unit>
<trans-unit id="oJn3xvZ" resname="job.addedit.crontype.http.header">
<source>job.addedit.crontype.http.header</source>
<target state="final">HTTP request details</target>
</trans-unit>
<trans-unit id="hwhqEtB" resname="job.addedit.crontype.http.url.label">
<source>job.addedit.crontype.http.url.label</source>
<target state="final">Url</target>
</trans-unit>
<trans-unit id="31CZuNl" resname="job.addedit.crontype.http.url.placeholder">
<source>job.addedit.crontype.http.url.placeholder</source>
<target state="final">https://www.example.com</target>
</trans-unit>
<trans-unit id="gGpY1Gz" resname="job.addedit.crontype.http.basic-auth.username.label">
<source>job.addedit.crontype.http.basic-auth.username.label</source>
<target state="final">Gebruikersnaam voor basic auth</target>
</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>
</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>
<target state="final">Wachtwoord voor basic auth</target>
</trans-unit>
<trans-unit id="ROX9KeU" resname="job.addedit.crontype.http.basic-auth.password.placeholder">
<source>job.addedit.crontype.http.basic-auth.password.placeholder</source>
<target state="final">correct horse battery staple</target>
</trans-unit>
<trans-unit id="pRPdCPd" resname="job.addedit.crontype.http.basic-auth.password.helptext">
<source>job.addedit.crontype.http.basic-auth.password.helptext</source>
<target state="final">Dit veld word opgeslagen als geheime waarde</target>
</trans-unit>
<trans-unit id="SnZXqhK" resname="job.addedit.crontype.http.response.label">
<source>job.addedit.crontype.http.response.label</source>
<target state="final">Verwachte http status code</target>
</trans-unit>
<trans-unit id="aLSDlKd" resname="job.addedit.crontype.http.response.placeholder">
<source>job.addedit.crontype.http.response.placeholder</source>
<target state="final">418</target>
</trans-unit>
<trans-unit id="oV9GuKJ" resname="job.addedit.hosttype.label">
<source>job.addedit.hosttype.label</source>
<target state="final">Host type</target>
</trans-unit>
<trans-unit id="eBXA85X" resname="job.addedit.hosttype.local.label">
<source>job.addedit.hosttype.local.label</source>
<target state="final">Lokaal</target>
</trans-unit>
<trans-unit id="ZeflLHK" resname="job.addedit.hosttype.local.header">
<source>job.addedit.hosttype.local.header</source>
<target state="final">Localhost details</target>
</trans-unit>
<trans-unit id="1atQ8Ot" resname="job.addedit.hosttype.local.nodetails">
<source>job.addedit.hosttype.local.nodetails</source>
<target state="final">Geen opties</target>
</trans-unit>
<trans-unit id="oUUVCNC" resname="job.addedit.hosttype.ssh.label">
<source>job.addedit.hosttype.ssh.label</source>
<target state="final">SSH</target>
</trans-unit>
<trans-unit id="HIND6DD" resname="job.addedit.hosttype.ssh.header">
<source>job.addedit.hosttype.ssh.header</source>
<target state="final">SSH host details</target>
</trans-unit>
<trans-unit id="n_JNj3z" resname="job.addedit.hosttype.ssh.hostname.label">
<source>job.addedit.hosttype.ssh.hostname.label</source>
<target state="final">Hostnaam</target>
</trans-unit>
<trans-unit id="4ZEnTLC" resname="job.addedit.hosttype.ssh.hostname.placeholder">
<source>job.addedit.hosttype.ssh.hostname.placeholder</source>
<target state="final">ssh.abc.xyz</target>
</trans-unit>
<trans-unit id="08j0NEG" resname="job.addedit.hosttype.ssh.username.label">
<source>job.addedit.hosttype.ssh.username.label</source>
<target state="final">Gebruikersnaam</target>
</trans-unit>
<trans-unit id="EJOhQKe" resname="job.addedit.hosttype.ssh.username.placeholder">
<source>job.addedit.hosttype.ssh.username.placeholder</source>
<target state="final">larry</target>
</trans-unit>
<trans-unit id="VEZQ7fY" resname="job.addedit.hosttype.ssh.privatekey.label">
<source>job.addedit.hosttype.ssh.privatekey.label</source>
<target state="final">Privésleutel</target>
</trans-unit>
<trans-unit id="1MzOMSK" resname="job.addedit.hosttype.ssh.privatekey.helptext">
<source>job.addedit.hosttype.ssh.privatekey.helptext</source>
<target state="final">Dit bestand word opgeslagen als geheime waarde</target>
</trans-unit>
<trans-unit id="uVSkZpl" resname="job.addedit.hosttype.ssh.privatekey.keep.label">
<source>job.addedit.hosttype.ssh.privatekey.keep.label</source>
<target state="final">Behoud</target>
</trans-unit>
<trans-unit id="nxl.bTZ" resname="job.addedit.hosttype.ssh.passphrase.label">
<source>job.addedit.hosttype.ssh.passphrase.label</source>
<target state="final">Wachtwoord voor privésleutel</target>
</trans-unit>
<trans-unit id="rYaggeh" resname="job.addedit.hosttype.ssh.passphrase.placeholder">
<source>job.addedit.hosttype.ssh.passphrase.placeholder</source>
<target state="final">abc123</target>
</trans-unit>
<trans-unit id="MAQ8t1s" resname="job.addedit.hosttype.ssh.passphrase.helptext">
<source>job.addedit.hosttype.ssh.passphrase.helptext</source>
<target state="final">Indien er geen privésleutel is meegegeven zal dit gebruikt worden als gewoon wachtwoord. Dit veld word opgeslagen als geheime waarde</target>
</trans-unit>
<trans-unit id="8q0z3bf" resname="job.addedit.containertype.label">
<source>job.addedit.containertype.label</source>
<target state="final">Container type</target>
</trans-unit>
<trans-unit id="ZPis4Xx" resname="job.addedit.containertype.none.label">
<source>job.addedit.containertype.none.label</source>
<target state="final">Geen</target>
</trans-unit>
<trans-unit id="AGADEB_" resname="job.addedit.containertype.docker.label">
<source>job.addedit.containertype.docker.label</source>
<target state="final">Docker</target>
</trans-unit>
<trans-unit id="1hg_Lri" resname="job.addedit.containertype.docker.header">
<source>job.addedit.containertype.docker.header</source>
<target state="final">Docker container details</target>
</trans-unit>
<trans-unit id="fddg1.3" resname="job.addedit.containertype.docker.service.label">
<source>job.addedit.containertype.docker.service.label</source>
<target state="final">Service</target>
</trans-unit>
<trans-unit id="mmNVzOj" resname="job.addedit.containertype.docker.service.placeholder">
<source>job.addedit.containertype.docker.service.placeholder</source>
<target state="final">mysql</target>
</trans-unit>
<trans-unit id="kaHv1qa" resname="job.addedit.containertype.docker.username.label">
<source>job.addedit.containertype.docker.username.label</source>
<target state="final">Gebruikersnaam</target>
</trans-unit>
<trans-unit id="UzbvieZ" resname="job.addedit.containertype.docker.username.placeholder">
<source>job.addedit.containertype.docker.username.placeholder</source>
<target state="final">larry</target>
</trans-unit>
<trans-unit id="BsHxmnc" resname="job.addedit.variables.header">
<source>job.addedit.variables.header</source>
<target state="final">Variabelen</target>
</trans-unit>
<trans-unit id="pawk4nL" resname="job.addedit.variables.secret.label">
<source>job.addedit.variables.secret.label</source>
<target state="final">Geheim</target>
</trans-unit>
<trans-unit id="roGfPFi" resname="job.addedit.variables.name.placeholder">
<source>job.addedit.variables.name.placeholder</source>
<target state="final">Naam</target>
</trans-unit>
<trans-unit id="WWsh_72" resname="job.addedit.variables.value.placeholder">
<source>job.addedit.variables.value.placeholder</source>
<target state="final">Waarde</target>
</trans-unit>
<trans-unit id="Ve0r_Wy" resname="job.addedit.variables.helptext">
<source>job.addedit.variables.helptext</source>
<target state="final">Je kan variabelen gebruiken via {variabele-naam} in de taak details</target>
</trans-unit>
<trans-unit id="tL8wand" resname="job.addedit.variables.add.label">
<source>job.addedit.variables.add.label</source>
<target state="final">Variable toevoegen</target>
</trans-unit>
<trans-unit id="or7JWUe" resname="job.addedit.submit.label">
<source>job.addedit.submit.label</source>
<target state="final">Verzend!</target>
</trans-unit>
<trans-unit id="g7pEpsB" resname="footer.title">
<source>footer.title</source>
<target state="final">Webcron Management</target>
</trans-unit>
<trans-unit id="Lz7YjVX" resname="footer.source">
<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

@ -1,202 +0,0 @@
title: "Webcron management"
header: "Webcron management"
demomode:
flashnotice: "Deze applicatie is in demo modus. Alle functies zijn actief, maar taken worden niet uitgevoerd"
credentials:
header: "Aanmeldgegevens voor demo modus:"
username: "Gebruikersnaam"
password: "Wachtwoord"
menu:
overview: "Overzicht"
add: "Taak toevoegen"
logout: "Afmelden"
security:
login:
username:
label: "Gebruikersnaam"
placeholder: "jeroen@example.com"
password:
label: "Wachtwoord"
placeholder: "abc123"
remember:
label: "Onthoud mij!"
submit-btn:
label: "Aanmelden"
index:
title: "Overzicht"
header: "Overzicht van de geplande taken"
table:
headers:
name: "Naam"
host: "Host"
interval: "Interval"
nextrun: "Volgende uitvoering"
job:
view:
title: "Overzicht van uitvoeringen van _jobname_"
header: "Overzicht van uitvoeringen van _jobname_"
edit: "Bewerk taak"
runnow:
deferred:
title: "Taak werd doorgestuurd naar de daemon"
message: "Taak werd doorgestuurd naar de daemon. De output vind u binnenkort in de taakdetails"
ran:
title:
success: "Taak werd succesvol uitgevoerd"
failed: "Taak faalde. Hieronder vind u de output ter controle"
message: "Taak werd in _runtime_ seconden uitgevoerd met resultaat _exitcode_"
show:
onlyfailed: "Toon alleen gefaalde uitvoeringen"
all: "Toon alle uitvoeringen"
results:
exitcode: "Resultaat"
runtime: "Duurtijd"
manual: "Manuele uitvoering"
noresults:
failed: "Geen gefaalde uitvoeringen gevonden"
all: "Geen uitvoeringen gevonden"
edit:
title: "Bewerk taak"
header: "Bewerk taak _jobname_"
add:
title: "Taak toevoegen"
header: "Nieuwe taak toevoegen"
addedit:
generalinfo:
header: "Algemene info"
name:
label: "Naam"
placeholder: "Systemen updaten"
helptext: "Je kan gekleurde tags toevoegen met [tag]"
interval:
label: "Interval (in seconden)"
placeholder: "3600"
patterns:
label: "Patronen"
minute: "Iedere minuut"
hour: "Ieder uur"
day: "Iedere dag"
week: "Iedere week"
4week: "Iedere 4 weken"
nextrun:
label: "Volgende uitvoering"
lastrun:
label: "Laatste uitvoering"
eternal:
label: "Eeuwig"
retention:
label: "Opslag (in dagen)"
placeholder: "180"
helptext: "Hoeveel dagen moeten uitvoeringen van een job bewaard worden"
failpercentage:
label: "Max faalpercentage"
faildays:
label: "Aantal dagen berekend voor faalpercentage"
placeholder: "7"
hostlabel:
label: "Host label"
placeholder: "petrosian.jeroened.be"
helptext: "Geef hier een eenvoudig te herkennen hostnaam. Indien leeg zal hostnaam uit de job details gebruikt worden"
crontype:
label: "Taak type"
command:
label: "Commando"
header: "Commando details"
command:
label: "Commando"
placeholder: "sudo apt update"
response:
label: "Verwacht resultaat"
placeholder: "0"
reboot:
label: "Herstart"
header: "Herstart details"
reboot:
command:
label: "Herstart commando"
placeholder: "systemctl reboot"
helptext: "Gebruik {reboot-delay} of {reboot-delay-secs} om de vertraging toe te voegen in je commando"
delay:
label: "Herstart vertraging (in minuten)"
placeholder: "5"
helptext: "Tijd tussen het aanroepen van herstart commando en de eigenlijke herstart"
duration:
label: "Duurtijd van herstart (in minuten)"
placeholder: "10"
helptext: "De tijd dat de effectieve herstart duurt"
getservices:
command:
label: "Commando voor ophalen services"
placeholder: "systemctl list-units"
response:
label: "Resultaat van commando voor ophalen services"
placeholder: "0"
http:
label: "HTTP request"
header: "HTTP request details"
url:
label: "Url"
placeholder: "https://www.example.com"
basic-auth:
username:
label: "Gebruikersnaam voor basic auth"
placeholder: "www-data"
password:
label: "Wachtwoord voor basic auth"
placeholder: "correct horse battery staple"
helptext: "Dit veld word opgeslagen als geheime waarde"
response:
label: "Verwachte http status code"
placeholder: "418"
hosttype:
label: "Host type"
local:
label: "Lokaal"
header: "Localhost details"
nodetails: "Geen opties"
ssh:
label: "SSH"
header: "SSH host details"
hostname:
label: "Hostnaam"
placeholder: "ssh.abc.xyz"
username:
label: "Gebruikersnaam"
placeholder: "larry"
privatekey:
label: "Privésleutel"
helptext: "Dit bestand word opgeslagen als geheime waarde"
keep:
label: "Behoud"
passphrase:
label: "Wachtwoord voor privésleutel"
placeholder: "abc123"
helptext: "Indien er geen privésleutel is meegegeven zal dit gebruikt worden als gewoon wachtwoord. Dit veld word opgeslagen als geheime waarde"
containertype:
label: "Container type"
none:
label: "Geen"
docker:
label: "Docker"
header: "Docker container details"
service:
label: "Service"
placeholder: "mysql"
username:
label: "Gebruikersnaam"
placeholder: "larry"
variables:
header: "Variabelen"
secret:
label: "Geheim"
name:
placeholder: "Naam"
value:
placeholder: "Waarde"
helptext: "Je kan variabelen gebruiken via {variabele-naam} in de job details"
add:
label: "Variable toevoegen"
submit:
label: "Verzend!"

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>

1
version Normal file
View File

@ -0,0 +1 @@
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/')
/*
@ -20,6 +20,7 @@ Encore
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('settings', './assets/js/settings.js')
.addEntry('security.login', './assets/js/security/login.js')
.addEntry('job.index', './assets/js/job/index.js')
.addEntry('job.view', './assets/js/job/view.js')
@ -46,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