Merge pull request #47 from jr-k/develop

Release v1.12
This commit is contained in:
JRK 2024-05-12 22:26:01 +02:00 committed by GitHub
commit fb6f491784
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 56 additions and 37 deletions

View File

@ -85,13 +85,18 @@ jQuery(document).ready(function ($) {
$(document).on('click', '.user-delete', function () { $(document).on('click', '.user-delete', function () {
if (confirm(l.js_auth_user_delete_confirmation)) { if (confirm(l.js_auth_user_delete_confirmation)) {
const $tr = $(this).parents('tr:eq(0)'); const $tr = $(this).parents('tr:eq(0)');
$tr.remove();
updateTable(); updateTable();
$.ajax({ $.ajax({
method: 'DELETE', method: 'DELETE',
url: '/auth/user/delete', url: '/auth/user/delete',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
data: JSON.stringify({id: getId($(this))}), data: JSON.stringify({id: getId($(this))}),
success: function(data) {
$tr.remove();
},
error: function(data) {
$('.alert-error').html(data.responseJSON.message).removeClass('hidden');
}
}); });
} }
}); });

View File

@ -115,8 +115,11 @@ jQuery(document).ready(function ($) {
}); });
$(document).on('change', '.modal-slide select.trigger', function () { $(document).on('change', '.modal-slide select.trigger', function () {
const $modal = $(this).parents('.modal-slide:eq(0)');
const $target = $(this).parents('.widget:eq(0)').find('.target'); const $target = $(this).parents('.widget:eq(0)').find('.target');
const $datetimepicker = $(this).parents('.widget:eq(0)').find('.datetimepicker'); const $datetimepicker = $(this).parents('.widget:eq(0)').find('.datetimepicker');
const $durationGroup = $modal.find('.slide-duration-group');
const $scheduleEndGroup = $modal.find('.slide-schedule-end-group');
const isDateTime = $(this).val() === 'datetime'; const isDateTime = $(this).val() === 'datetime';
const isLoop = $(this).val() === 'loop'; const isLoop = $(this).val() === 'loop';
@ -126,7 +129,9 @@ jQuery(document).ready(function ($) {
const hideDateTimeField = !isDateTime; const hideDateTimeField = !isDateTime;
$target.toggleClass('hidden', hideCronField); $target.toggleClass('hidden', hideCronField);
$datetimepicker.toggleClass('hidden', hideDateTimeField) $datetimepicker.toggleClass('hidden', hideDateTimeField);
// $durationGroup.toggleClass('hidden', !isLoop);
// $scheduleEndGroup.toggleClass('hidden', isLoop);
if (flushValue) { if (flushValue) {
$target.val(''); $target.val('');

View File

@ -74,6 +74,7 @@
"auth_user_form_label_username": "Username", "auth_user_form_label_username": "Username",
"auth_user_form_label_password": "Password", "auth_user_form_label_password": "Password",
"auth_user_form_button_cancel": "Cancel", "auth_user_form_button_cancel": "Cancel",
"auth_user_delete_at_least_one_account": "You must have at least one active user while using authentication feature",
"js_auth_user_delete_confirmation": "Are you sure?", "js_auth_user_delete_confirmation": "Are you sure?",
"settings_page_title": "Settings", "settings_page_title": "Settings",

View File

@ -74,6 +74,7 @@
"auth_user_form_label_username": "Nom d'utilisateur", "auth_user_form_label_username": "Nom d'utilisateur",
"auth_user_form_label_password": "Mot de passe", "auth_user_form_label_password": "Mot de passe",
"auth_user_form_button_cancel": "Annuler", "auth_user_form_button_cancel": "Annuler",
"auth_user_delete_at_least_one_account": "Vous devez avoir au moins un utilisateur actif lorsque vous activez la gestion de l'authentification",
"js_auth_user_delete_confirmation": "Êtes-vous sûr ?", "js_auth_user_delete_confirmation": "Êtes-vous sûr ?",
"settings_page_title": "Paramètres", "settings_page_title": "Paramètres",

View File

@ -70,6 +70,8 @@ class AuthController(ObController):
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
def auth_user_delete(self): def auth_user_delete(self):
if self._model_store.user().count_all_enabled() == 1:
return jsonify({'status': 'error', 'message': self.t('auth_user_delete_at_least_one_account')}), 400
data = request.get_json() data = request.get_json()
self._model_store.user().delete(data.get('id')) self._model_store.user().delete(data.get('id'))
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})

View File

@ -21,8 +21,8 @@ class SettingsController(ObController):
def settings_variable_edit(self): def settings_variable_edit(self):
self._model_store.variable().update_form(request.form['id'], request.form['value']) self._model_store.variable().update_form(request.form['id'], request.form['value'])
self._post_update(request.form['id']) forward = self._post_update(request.form['id'])
return redirect(url_for('settings_variable_list')) return forward if forward is not None else redirect(url_for('settings_variable_list'))
def _post_update(self, id: str): def _post_update(self, id: str):
variable = self._model_store.variable().get(id) variable = self._model_store.variable().get(id)
@ -38,6 +38,8 @@ class SettingsController(ObController):
if variable.name == 'auth_enabled': if variable.name == 'auth_enabled':
self.reload_web_server() self.reload_web_server()
if variable.as_bool():
return redirect(url_for('logout'))
if variable.name == 'lang': if variable.name == 'lang':
self._model_store.lang().set_lang(variable.value) self._model_store.lang().set_lang(variable.value)

View File

@ -28,4 +28,7 @@ class ObController(abc.ABC):
return self._plugin return self._plugin
def reload_web_server(self): def reload_web_server(self):
self._web_server.reload() self._web_server.reload()
def t(self, token) -> Union[Dict, str]:
return self._model_store.lang().translate(token)

View File

@ -56,8 +56,8 @@ class UserManager(ModelManager):
def get_one_by_username(self, username: str, enabled: bool = None) -> Optional[User]: def get_one_by_username(self, username: str, enabled: bool = None) -> Optional[User]:
return self.get_one_by(query=lambda v: v['username'] == username and (enabled is None or v['enabled'] == enabled)) return self.get_one_by(query=lambda v: v['username'] == username and (enabled is None or v['enabled'] == enabled))
def count_all(self): def count_all_enabled(self):
return len(self.get_all()) return len(self.get_enabled_users())
def get_all(self, sort: bool = False) -> List[User]: def get_all(self, sort: bool = False) -> List[User]:
raw_users = self._db.get_all() raw_users = self._db.get_all()

View File

@ -22,6 +22,7 @@ class WebServer:
def __init__(self, project_dir: str, model_store: ModelStore, template_renderer: TemplateRenderer): def __init__(self, project_dir: str, model_store: ModelStore, template_renderer: TemplateRenderer):
self._app = None self._app = None
self._auth_enabled = False
self._login_manager = None self._login_manager = None
self._project_dir = project_dir self._project_dir = project_dir
self._model_store = model_store self._model_store = model_store
@ -40,6 +41,7 @@ class WebServer:
self.setup() self.setup()
def setup(self) -> None: def setup(self) -> None:
self._auth_enabled = self._model_store.variable().map().get('auth_enabled').as_bool()
self._setup_flask_app() self._setup_flask_app()
self._setup_web_globals() self._setup_web_globals()
self._setup_web_errors() self._setup_web_errors()
@ -69,29 +71,22 @@ class WebServer:
if self._debug: if self._debug:
self._app.config['TEMPLATES_AUTO_RELOAD'] = True self._app.config['TEMPLATES_AUTO_RELOAD'] = True
def _setup_flask_login(self) -> bool: def _setup_flask_login(self):
auth_module = self._model_store.variable().map().get('auth_enabled').as_bool()
if not auth_module:
return auth_module
self._app.config['SECRET_KEY'] = self._model_store.config().map().get('secret_key') self._app.config['SECRET_KEY'] = self._model_store.config().map().get('secret_key')
self._login_manager = LoginManager() self._login_manager = LoginManager()
self._login_manager.init_app(self._app) self._login_manager.init_app(self._app)
self._login_manager.login_view = 'login' self._login_manager.login_view = 'login'
if self._model_store.user().count_all() == 0: if self._auth_enabled and self._model_store.user().count_all_enabled() == 0:
self._model_store.user().add_form(User(username="admin", password="admin", enabled=True)) self._model_store.user().add_form(User(username="admin", password="admin", enabled=True))
@self._login_manager.user_loader @self._login_manager.user_loader
def load_user(user_id): def load_user(user_id):
return self._model_store.user().get(user_id) return self._model_store.user().get(user_id)
return auth_module
def _setup_web_controllers(self) -> None: def _setup_web_controllers(self) -> None:
def auth_required(f): def auth_required(f):
if not self._login_manager: if not self._auth_enabled:
return f return f
def decorated_function(*args, **kwargs): def decorated_function(*args, **kwargs):

View File

@ -23,6 +23,11 @@
{{ HOOK(H_AUTH_TOOLBAR_ACTIONS_END) }} {{ HOOK(H_AUTH_TOOLBAR_ACTIONS_END) }}
</div> </div>
</div> </div>
<div class="alert alert-error hidden">
</div>
<div class="panel"> <div class="panel">
<div class="panel-body"> <div class="panel-body">
<h3>{{ l.auth_user_panel_active }}</h3> <h3>{{ l.auth_user_panel_active }}</h3>

View File

@ -46,7 +46,8 @@
Obscreen Obscreen
</a> </a>
</h1> </h1>
{% if (current_user and current_user.is_authenticated) or not current_user %}
{% if not AUTH_ENABLED or (current_user and current_user.is_authenticated) %}
<nav> <nav>
<ul> <ul>
{{ HOOK(H_ROOT_NAV_ELEMENT_START) }} {{ HOOK(H_ROOT_NAV_ELEMENT_START) }}

View File

@ -41,14 +41,6 @@
{{ l.slideshow_slide_form_section_scheduling }} {{ l.slideshow_slide_form_section_scheduling }}
</h3> </h3>
<div class="form-group">
<label for="slide-add-duration">{{ l.slideshow_slide_form_label_duration }}</label>
<div class="widget">
<input type="number" name="duration" id="slide-add-duration" required="required" />
<span>{{ l.slideshow_slide_form_label_duration_unit }}</span>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="slide-add-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label> <label for="slide-add-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget"> <div class="widget">
@ -62,11 +54,10 @@
</div> </div>
</div> </div>
<div class="form-group hidden"> <div class="form-group slide-schedule-end-group hidden">
<label for="slide-add-cron-schedule-end">{{ l.slideshow_slide_form_label_cron_scheduled_end }}</label> <label for="slide-add-cron-schedule-end">{{ l.slideshow_slide_form_label_cron_scheduled_end }}</label>
<div class="widget"> <div class="widget">
<select id="slide-add-cron-schedule-end-trigger" class="trigger"> <select id="slide-add-cron-schedule-end-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="datetime">{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}</option>
<option value="cron">{{ l.slideshow_slide_form_label_cron_scheduled_cron }}</option> <option value="cron">{{ l.slideshow_slide_form_label_cron_scheduled_cron }}</option>
</select> </select>
@ -75,6 +66,14 @@
</div> </div>
</div> </div>
<div class="form-group slide-duration-group">
<label for="slide-add-duration">{{ l.slideshow_slide_form_label_duration }}</label>
<div class="widget">
<input type="number" name="duration" id="slide-add-duration" required="required" />
<span>{{ l.slideshow_slide_form_label_duration_unit }}</span>
</div>
</div>
<div class="actions"> <div class="actions">
<button type="button" class="modal-close"> <button type="button" class="modal-close">
{{ l.slideshow_slide_form_button_cancel }} {{ l.slideshow_slide_form_button_cancel }}

View File

@ -43,14 +43,6 @@
{{ l.slideshow_slide_form_section_scheduling }} {{ l.slideshow_slide_form_section_scheduling }}
</h3> </h3>
<div class="form-group">
<label for="slide-edit-duration">{{ l.slideshow_slide_form_label_duration }}</label>
<div class="widget">
<input type="number" name="duration" id="slide-edit-duration" required="required" />
<span>{{ l.slideshow_slide_form_label_duration_unit }}</span>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="slide-edit-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label> <label for="slide-edit-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget"> <div class="widget">
@ -64,7 +56,7 @@
</div> </div>
</div> </div>
<div class="form-group hidden"> <div class="form-group slide-schedule-end-group hidden">
<label for="slide-edit-cron-schedule-end">{{ l.slideshow_slide_form_label_cron_scheduled_end }}</label> <label for="slide-edit-cron-schedule-end">{{ l.slideshow_slide_form_label_cron_scheduled_end }}</label>
<div class="widget"> <div class="widget">
<select id="slide-edit-cron-schedule-end-trigger" class="trigger"> <select id="slide-edit-cron-schedule-end-trigger" class="trigger">
@ -77,6 +69,14 @@
</div> </div>
</div> </div>
<div class="form-group slide-duration-group">
<label for="slide-edit-duration">{{ l.slideshow_slide_form_label_duration }}</label>
<div class="widget">
<input type="number" name="duration" id="slide-edit-duration" required="required" />
<span>{{ l.slideshow_slide_form_label_duration_unit }}</span>
</div>
</div>
<div class="actions"> <div class="actions">
<button type="button" class="modal-close"> <button type="button" class="modal-close">
{{ l.slideshow_slide_form_button_cancel }} {{ l.slideshow_slide_form_button_cancel }}