From eb8e04d5a25eeab1a05636d9a9daf7d2550d506c Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Tue, 30 Jun 2020 16:17:40 +0100 Subject: [PATCH] Added settings menu to UI Currently all setting changes require a restart to apply, so there's a bit of commented out code that i implemented before i realized. Still needs tooltips for each setting. --- jellyfin_accounts/data/config-base.json | 10 +- jellyfin_accounts/data/static/admin.js | 211 +++++++++++++++++++- jellyfin_accounts/data/templates/admin.html | 36 +++- jellyfin_accounts/web_api.py | 5 +- 4 files changed, 241 insertions(+), 21 deletions(-) diff --git a/jellyfin_accounts/data/config-base.json b/jellyfin_accounts/data/config-base.json index a54cdc4..16227fe 100644 --- a/jellyfin_accounts/data/config-base.json +++ b/jellyfin_accounts/data/config-base.json @@ -82,7 +82,7 @@ "name": "Port", "required": true, "requires_restart": true, - "type": "int", + "type": "number", "value": 8056 }, "jellyfin_login": { @@ -261,7 +261,7 @@ "description": "Address to send emails from" }, "from": { - "Name": "Sent from (name)", + "name": "Sent from (name)", "required": false, "requires_restart": false, "depends_true": "method", @@ -285,7 +285,7 @@ }, "watch_directory": { "name": "Jellyfin directory", - "required": true, + "required": false, "requires_restart": true, "depends_true": "enabled", "type": "text", @@ -312,7 +312,7 @@ }, "subject": { "name": "Email subject", - "required": true, + "required": false, "requires_restart": false, "depends_true": "enabled", "type": "text", @@ -418,7 +418,7 @@ "name": "Port", "required": false, "requires_restart": false, - "type": "int", + "type": "number", "value": 465 }, "password": { diff --git a/jellyfin_accounts/data/static/admin.js b/jellyfin_accounts/data/static/admin.js index 8e99840..79f7433 100644 --- a/jellyfin_accounts/data/static/admin.js +++ b/jellyfin_accounts/data/static/admin.js @@ -241,9 +241,6 @@ $("form#loginForm").submit(function() { }); return false; }); -document.getElementById('openSettings').onclick = function () { - $('#settingsMenu').modal('show'); -} document.getElementById('openDefaultsWizard').onclick = function () { this.disabled = true; this.innerHTML = @@ -336,6 +333,7 @@ document.getElementById('storeDefaults').onclick = function () { }, error: function() { button.textContent = 'Failed'; + config_base_path = local_dir / "config-base.json" button.classList.remove('btn-primary'); button.classList.add('btn-danger'); setTimeout(function(){ @@ -449,3 +447,210 @@ document.getElementById('openUsers').onclick = function () { }; generateInvites(empty = true); $("#login").modal('show'); + +var config = {}; +var modifiedConfig = {}; + +document.getElementById('openSettings').onclick = function () { + restart_setting_changed = false; + $.ajax('getConfig', { + type : 'GET', + dataType : 'json', + contentType : 'json', + xhrFields : { + withCredentials: true + }, + beforeSend : function (xhr) { + xhr.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":")); + }, + complete : function(data) { + if (data['status'] == 200) { + var settingsList = document.getElementById('settingsList'); + settingsList.textContent = ''; + config = data['responseJSON']; + for (var section of Object.keys(config)) { + var sectionCollapse = document.createElement('div'); + sectionCollapse.classList.add('collapse'); + sectionCollapse.id = section; + + var sectionTitle = config[section]['meta']['name']; + var sectionDescription = config[section]['meta']['description']; + var entryListID = section + '_entryList'; + var sectionFooter = section + '_footer'; + + var innerCollapse = ` +
+ ${sectionDescription} +
+
+
+ `; + + sectionCollapse.innerHTML = innerCollapse; + + for (var entry of Object.keys(config[section])) { + if (entry != 'meta') { + var entryName = config[section][entry]['name']; + var required = false; + if (config[section][entry]['required']) { + entryName += ' *'; + required = true; + }; + // if (config[section][entry]['requires_restart']) { + // entryName += ' R'; + // }; + var entryValue = config[section][entry]['value']; + var entryType = config[section][entry]['type']; + var entryGroup = document.createElement('div'); + if (entryType == 'bool') { + entryGroup.classList.add('form-check'); + if (entryValue) { + var checked = true; + } else { + var checked = false; + }; + entryGroup.innerHTML = ` + + + `; + entryGroup.getElementsByClassName('form-check-input')[0].required = required; + entryGroup.getElementsByClassName('form-check-input')[0].checked = checked; + entryGroup.getElementsByClassName('form-check-input')[0].onclick = function() { + var state = this.checked; + for (var sect of Object.keys(config)) { + for (var ent of Object.keys(config[sect])) { + if ((sect + '_' + config[sect][ent]['depends_true']) == this.id) { + document.getElementById(sect + '_' + ent).disabled = !state; + } else if ((sect + '_' + config[sect][ent]['depends_false']) == this.id) { + document.getElementById(sect + '_' + ent).disabled = state; + }; + }; + }; + }; + } else if ((entryType == 'text') || (entryType == 'email') || (entryType == 'password') || (entryType == 'number')) { + entryGroup.classList.add('form-group'); + entryGroup.innerHTML = ` + + + `; + entryGroup.getElementsByClassName('form-control')[0].required = required; + } else if (entryType == 'select') { + entryGroup.classList.add('form-group'); + var entryOptions = config[section][entry]['options']; + var innerGroup = ` + + '; + entryGroup.innerHTML = innerGroup; + entryGroup.getElementsByClassName('form-control')[0].required = required; + + }; + sectionCollapse.getElementsByClassName(entryListID)[0].appendChild(entryGroup); + }; + }; + var sectionButton = document.createElement('button'); + sectionButton.setAttribute('type', 'button'); + sectionButton.classList.add('list-group-item', 'list-group-item-action'); + sectionButton.appendChild(document.createTextNode(sectionTitle)); + sectionButton.id = section + '_button'; + sectionButton.setAttribute('data-toggle', 'collapse'); + sectionButton.setAttribute('data-target', '#' + section); + settingsList.appendChild(sectionButton); + settingsList.appendChild(sectionCollapse); + }; + }; + }, + }); + $('#settingsMenu').modal('show'); +}; + +function sendConfig(modalId) { + var modal = document.getElementById(modalId); + var send = JSON.stringify(modifiedConfig); + $.ajax('/modifyConfig', { + data : send, + contentType : 'application/json', + type : 'POST', + xhrFields : { + withCredentials: true + }, + beforeSend : function (xhr) { + xhr.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":")); + }, + success: function() { + if (modalId != 'settingsMenu') { + $('#' + modalId).modal('hide'); + $('#settingsMenu').modal('hide'); + }; + }, + fail: function(xhr, textStatus, errorThrown) { + var footer = modal.getElementsByClassName('modal-dialog')[0].getElementsByClassName('modal-content')[0].getElementsByClassName('modal-footer')[0]; + var alert = document.createElement('div'); + alert.classList.add('alert', 'alert-danger'); + alert.setAttribute('role', 'alert'); + alert.appendChild(document.createTextNode('Error: ' + errorThrown)); + footer.appendChild(alert); + }, + }); + // placeholder +}; + +document.getElementById('settingsSave').onclick = function() { + modifiedConfig = {}; + // Live config changes have not yet been implemented, so restart always required. + // var restart_setting_changed = false; + var settings_changed = false; + + for (var section of Object.keys(config)) { + for (var entry of Object.keys(config[section])) { + if (entry != 'meta') { + var entryID = section + '_' + entry; + var el = document.getElementById(entryID); + if (el.type == 'checkbox') { + var value = el.checked.toString(); + } else { + var value = el.value.toString(); + }; + if (value != config[section][entry]['value'].toString()) { + if (!modifiedConfig.hasOwnProperty(section)) { + modifiedConfig[section] = {}; + }; + modifiedConfig[section][entry] = value; + settings_changed = true; + // if (config[section][entry]['requires_restart']) { + // restart_setting_changed = true; + // }; + }; + }; + }; + }; + // if (restart_setting_changed) { + if (settings_changed) { + document.getElementById('applyRestarts').onclick = function(){sendConfig('restartModal');}; + $('#settingsMenu').modal('hide'); + $('#restartModal').modal({ + backdrop: 'static', + show: true + }); + } else { + // sendConfig('settingsMenu'); + $('#settingsMenu').modal('hide'); + }; +}; + + + + + + diff --git a/jellyfin_accounts/data/templates/admin.html b/jellyfin_accounts/data/templates/admin.html index 4cf3464..2b2ce52 100644 --- a/jellyfin_accounts/data/templates/admin.html +++ b/jellyfin_accounts/data/templates/admin.html @@ -93,20 +93,20 @@ @@ -153,6 +153,22 @@ +

Accounts admin diff --git a/jellyfin_accounts/web_api.py b/jellyfin_accounts/web_api.py index cad8ac8..cc83060 100644 --- a/jellyfin_accounts/web_api.py +++ b/jellyfin_accounts/web_api.py @@ -344,9 +344,7 @@ def modifyConfig(): log.debug(f"{section}/{item} does not exist in config") with open(config_path, "w") as config_file: temp_config.write(config_file) - log.info("Config written, reloading") - config.read(config_path) - log.info("Config reloaded.") + log.info("Config written. Restart is needed to load settings.") return resp() @@ -363,6 +361,7 @@ def getConfig(): log.debug('Config requested') with open(config_base_path, "r") as f: config_base = json.load(f) + config.read(config_path) response_config = config_base for section in config_base: for entry in config_base[section]: