t pMerge branch 'develop' of https://github.com/jr-k/obscreen into develop

This commit is contained in:
jr-k 2024-05-05 20:53:31 +02:00
commit 80b6b8335f
14 changed files with 127 additions and 18 deletions

13
data/www/css/flatpickr.min.css vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -516,10 +516,14 @@ form .form-group input[type=checkbox] {
flex: 0;
}
form .form-group input[type=checkbox].trigger {
form .form-group .trigger {
margin-right: 10px;
}
form .form-group select.trigger {
max-width: 120px;
}
form .form-group span {
margin-left: 10px;
}

2
data/www/js/flatpickr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,36 @@ jQuery(document).ready(function ($) {
const $tableInactive = $('table.inactive-slides');
const $modalsRoot = $('.modals');
const validateCronDateTime = function(cronExpression) {
const pattern = /^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+(\d+)$/;
return pattern.test(cronExpression);
};
const getCronDateTime = function(cronExpression) {
const [minutes, hours, day, month, _, year] = cronExpression.split(' ');
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;
};
const loadDateTimePicker = function($el) {
$el.val('');
const pickr = $el.flatpickr({
enableTime: true,
time_24hr: true,
allowInput: false,
allowInvalidPreload: false,
dateFormat: 'Y-m-d H:i',
onChange: function(selectedDates, dateStr, instance) {
const d = selectedDates[0];
const $target = $el.parents('.widget:eq(0)').find('.target');
$target.val(
d ? `${d.getMinutes()} ${d.getHours()} ${d.getDate()} ${(d.getMonth() + 1)} * ${d.getFullYear()}` : ''
);
}
});
$el.addClass('hidden');
};
const getId = function ($el) {
return $el.is('tr') ? $el.attr('data-level') : $el.parents('tr:eq(0)').attr('data-level');
};
@ -68,12 +98,21 @@ jQuery(document).ready(function ($) {
updateTable();
});
$(document).on('change', '.modal-slide input[type=checkbox]', function () {
const $target = $('#'+ $(this).attr('id').replace('-trigger', ''));
const hide = !$(this).is(':checked');
$target.toggleClass('hidden', hide);
$(document).on('change', '.modal-slide select.trigger', function () {
const $target = $(this).parents('.widget:eq(0)').find('.target');
const $datetimepicker = $(this).parents('.widget:eq(0)').find('.datetimepicker');
if (hide) {
const isDateTime = $(this).val() === 'datetime';
const isLoop = $(this).val() === 'loop';
const flushValue = isLoop;
const hideCronField = isLoop || isDateTime;
const hideDateTimeField = !isDateTime;
$target.toggleClass('hidden', hideCronField);
$datetimepicker.toggleClass('hidden', hideDateTimeField)
if (flushValue) {
$target.val('');
}
});
@ -99,20 +138,28 @@ jQuery(document).ready(function ($) {
$(document).on('click', '.slide-add', function () {
showModal('modal-slide-add');
loadDateTimePicker($('.modal-slide-add .datetimepicker'))
$('.modal-slide-add input:eq(0)').focus().select();
});
$(document).on('click', '.slide-edit', function () {
const slide = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
showModal('modal-slide-edit');
loadDateTimePicker($('.modal-slide-edit .datetimepicker'))
const hasCron = slide.cron_schedule && slide.cron_schedule.length > 0;
const hasDateTime = hasCron && validateCronDateTime(slide.cron_schedule);
$('.modal-slide-edit input:visible:eq(0)').focus().select();
$('#slide-edit-name').val(slide.name);
$('#slide-edit-type').val(slide.type);
$('#slide-edit-location').val(slide.location);
$('#slide-edit-duration').val(slide.duration);
$('#slide-edit-cron-schedule').val(slide.cron_schedule).toggleClass('hidden', !hasCron);
$('#slide-edit-cron-schedule-trigger').prop('checked', hasCron);
$('#slide-edit-cron-schedule').val(slide.cron_schedule).toggleClass('hidden', !hasCron || hasDateTime);
$('#slide-edit-cron-schedule-trigger').val(hasDateTime ? 'datetime' : (hasCron ? 'cron' : 'loop'));
$('#slide-edit-cron-schedule-datetimepicker').toggleClass('hidden', !hasDateTime).val(
hasDateTime ? getCronDateTime(slide.cron_schedule) : ''
);
$('#slide-edit-id').val(slide.id);
});
@ -137,4 +184,4 @@ jQuery(document).ready(function ($) {
});
main();
});
});

View File

@ -26,6 +26,10 @@
"slideshow_slide_form_label_duration": "Duration",
"slideshow_slide_form_label_duration_unit": "seconds",
"slideshow_slide_form_label_cron_scheduled": "Scheduled",
"slideshow_slide_form_label_cron_scheduled_loop": "In the loop",
"slideshow_slide_form_label_cron_scheduled_datetime": "Date & Time",
"slideshow_slide_form_label_cron_scheduled_datetime_placeholder": "Set a date and time",
"slideshow_slide_form_label_cron_scheduled_cron": "Cron",
"slideshow_slide_form_widget_cron_scheduled_placeholder": "Use crontab format: * * * * *",
"slideshow_slide_form_button_cancel": "Cancel",
"js_slideshow_slide_delete_confirmation": "Are you sure?",

View File

@ -26,6 +26,10 @@
"slideshow_slide_form_label_duration": "Durée",
"slideshow_slide_form_label_duration_unit": "secondes",
"slideshow_slide_form_label_cron_scheduled": "Programmer",
"slideshow_slide_form_label_cron_scheduled_loop": "Dans la boucle",
"slideshow_slide_form_label_cron_scheduled_datetime": "Date & Heure",
"slideshow_slide_form_label_cron_scheduled_datetime_placeholder": "Choisir une date et un heure",
"slideshow_slide_form_label_cron_scheduled_cron": "Cron",
"slideshow_slide_form_widget_cron_scheduled_placeholder": "Utiliser le format crontab: * * * * *",
"slideshow_slide_form_button_cancel": "Annuler",
"js_slideshow_slide_delete_confirmation": "Êtes-vous sûr ?",

View File

@ -9,7 +9,7 @@ from src.model.hook.HookRegistration import HookRegistration
from src.model.hook.StaticHookRegistration import StaticHookRegistration
from src.model.hook.FunctionalHookRegistration import FunctionalHookRegistration
from src.constant.WebDirConstant import WebDirConstant
from src.utils import get_safe_cron_descriptor
from src.utils import get_safe_cron_descriptor, is_validate_cron_date_time
class TemplateRenderer:
@ -29,7 +29,8 @@ class TemplateRenderer:
VERSION=self._model_store.config().map().get('version'),
LANG=self._model_store.variable().map().get('lang').as_string(),
HOOK=self._render_hook,
cron_descriptor=self.cron_descriptor
cron_descriptor=self.cron_descriptor,
is_validate_cron_date_time=is_validate_cron_date_time
)
for hook in HookType:

View File

@ -8,7 +8,22 @@ from cron_descriptor import ExpressionDescriptor
from cron_descriptor.Exception import FormatException, WrongArgumentException, MissingFieldException
def is_validate_cron_date_time(expression) -> bool:
pattern = re.compile(r'^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+(\d+)$')
return bool(pattern.match(expression))
def get_safe_cron_descriptor(expression: str, use_24hour_time_format=True, locale_code: Optional[str] = None) -> str:
if is_validate_cron_date_time(expression):
[minutes, hours, day, month, _, year] = expression.split(' ')
return "{}-{}-{} at {}:{}".format(
year,
month.zfill(2),
day.zfill(2),
hours.zfill(2),
minutes.zfill(2)
)
options = {
"expression": expression,
"use_24hour_time_format": use_24hour_time_format

View File

@ -6,6 +6,7 @@
</title>
<meta name="robots" content="noindex, nofollow">
<meta name="google" content="notranslate">
<link rel="shortcut icon" href="{{ STATIC_PREFIX }}/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="{{ STATIC_PREFIX }}favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="{{ STATIC_PREFIX }}favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="{{ STATIC_PREFIX }}favicon/apple-icon-72x72.png">

View File

@ -4,7 +4,10 @@
<title>Obscreen</title>
<meta name="robots" content="noindex, nofollow">
<meta name="google" content="notranslate">
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/animate.min.css" />
<link rel="shortcut icon" href="{{ STATIC_PREFIX }}/favicon.ico">
{% if slide_animation_enabled.eval() %}
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/animate.min.css" />
{% endif %}
<style>
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; background-color: white; display: flex; flex-direction: row; justify-content: center; align-items: center; }
.slide { display: flex; flex-direction: row; justify-content: center; align-items: center; background: black; }

View File

@ -47,7 +47,11 @@
{% if slide.cron_schedule %}
{% set cron_desc = cron_descriptor(slide.cron_schedule) %}
{% if cron_desc %}
{% if is_validate_cron_date_time(slide.cron_schedule) %}
📆 {{ cron_desc }}
{% else %}
⏳ {{ cron_desc }}
{% endif %}
{% else %}
<span class="error">⚠️ {{ l.slideshow_slide_panel_td_cron_scheduled_bad_cron }}</span>
{% endif %}

View File

@ -5,10 +5,12 @@
{% endblock %}
{% block add_css %}
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/flatpickr.min.css" />
{{ HOOK(H_SLIDESHOW_CSS) }}
{% endblock %}
{% block add_js %}
<script src="{{ STATIC_PREFIX }}js/flatpickr.min.js"></script>
<script src="{{ STATIC_PREFIX }}js/tablednd-fixed.js"></script>
<script src="{{ STATIC_PREFIX }}js/slideshow.js"></script>
<script src="{{ STATIC_PREFIX }}js/restart.js"></script>

View File

@ -25,8 +25,7 @@
<label for="slide-add-duration">{{ l.slideshow_slide_form_label_object }}</label>
<div class="widget">
<input type="text" name="object" id="slide-add-object-input-text" class="slide-add-object-input" />
<input type="file" name="object" id="slide-add-object-input-upload"
class="slide-add-object-input hidden" disabled="disabled" />
<input type="file" name="object" id="slide-add-object-input-upload" class="slide-add-object-input hidden" disabled="disabled" />
</div>
</div>
@ -41,8 +40,13 @@
<div class="form-group">
<label for="slide-add-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget">
<input type="checkbox" id="slide-add-cron-schedule-trigger" class="trigger" />
<input type="text" name="cron_schedule" id="slide-add-cron-schedule" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" class="hidden" />
<select id="slide-add-cron-schedule-trigger" class="trigger">
<option value="loop">{{ l.slideshow_slide_form_label_cron_scheduled_loop }}</option>
<option value="datetime">{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}</option>
<option value="cron">{{ l.slideshow_slide_form_label_cron_scheduled_cron }}</option>
</select>
<input type="text" id="slide-add-cron-schedule-datetimepicker" class="datetimepicker" value="" placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}" />
<input type="text" name="cron_schedule" id="slide-add-cron-schedule" class="target hidden" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" />
</div>
</div>

View File

@ -41,8 +41,13 @@
<div class="form-group">
<label for="slide-edit-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget">
<input type="checkbox" id="slide-edit-cron-schedule-trigger" class="trigger" />
<input type="text" name="cron_schedule" id="slide-edit-cron-schedule" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" class="hidden" />
<select id="slide-edit-cron-schedule-trigger" class="trigger">
<option value="loop">{{ l.slideshow_slide_form_label_cron_scheduled_loop }}</option>
<option value="datetime">{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}</option>
<option value="cron">{{ l.slideshow_slide_form_label_cron_scheduled_cron }}</option>
</select>
<input type="text" id="slide-edit-cron-schedule-datetimepicker" class="datetimepicker" value="" placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}" />
<input type="text" name="cron_schedule" id="slide-edit-cron-schedule" class="target hidden" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" />
</div>
</div>