mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-12-22 17:10:11 +00:00
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.
This commit is contained in:
parent
52a11c3905
commit
eb8e04d5a2
@ -82,7 +82,7 @@
|
|||||||
"name": "Port",
|
"name": "Port",
|
||||||
"required": true,
|
"required": true,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "int",
|
"type": "number",
|
||||||
"value": 8056
|
"value": 8056
|
||||||
},
|
},
|
||||||
"jellyfin_login": {
|
"jellyfin_login": {
|
||||||
@ -261,7 +261,7 @@
|
|||||||
"description": "Address to send emails from"
|
"description": "Address to send emails from"
|
||||||
},
|
},
|
||||||
"from": {
|
"from": {
|
||||||
"Name": "Sent from (name)",
|
"name": "Sent from (name)",
|
||||||
"required": false,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": false,
|
||||||
"depends_true": "method",
|
"depends_true": "method",
|
||||||
@ -285,7 +285,7 @@
|
|||||||
},
|
},
|
||||||
"watch_directory": {
|
"watch_directory": {
|
||||||
"name": "Jellyfin directory",
|
"name": "Jellyfin directory",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"depends_true": "enabled",
|
"depends_true": "enabled",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@ -312,7 +312,7 @@
|
|||||||
},
|
},
|
||||||
"subject": {
|
"subject": {
|
||||||
"name": "Email subject",
|
"name": "Email subject",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": false,
|
||||||
"depends_true": "enabled",
|
"depends_true": "enabled",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@ -418,7 +418,7 @@
|
|||||||
"name": "Port",
|
"name": "Port",
|
||||||
"required": false,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": false,
|
||||||
"type": "int",
|
"type": "number",
|
||||||
"value": 465
|
"value": 465
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
|
@ -241,9 +241,6 @@ $("form#loginForm").submit(function() {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
document.getElementById('openSettings').onclick = function () {
|
|
||||||
$('#settingsMenu').modal('show');
|
|
||||||
}
|
|
||||||
document.getElementById('openDefaultsWizard').onclick = function () {
|
document.getElementById('openDefaultsWizard').onclick = function () {
|
||||||
this.disabled = true;
|
this.disabled = true;
|
||||||
this.innerHTML =
|
this.innerHTML =
|
||||||
@ -336,6 +333,7 @@ document.getElementById('storeDefaults').onclick = function () {
|
|||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
button.textContent = 'Failed';
|
button.textContent = 'Failed';
|
||||||
|
config_base_path = local_dir / "config-base.json"
|
||||||
button.classList.remove('btn-primary');
|
button.classList.remove('btn-primary');
|
||||||
button.classList.add('btn-danger');
|
button.classList.add('btn-danger');
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
@ -449,3 +447,210 @@ document.getElementById('openUsers').onclick = function () {
|
|||||||
};
|
};
|
||||||
generateInvites(empty = true);
|
generateInvites(empty = true);
|
||||||
$("#login").modal('show');
|
$("#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 = `
|
||||||
|
<div class="card card-body">
|
||||||
|
<small class="text-muted">${sectionDescription}</small>
|
||||||
|
<div class="${entryListID}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 += ' <sup class="text-danger">*</sup>';
|
||||||
|
required = true;
|
||||||
|
};
|
||||||
|
// if (config[section][entry]['requires_restart']) {
|
||||||
|
// entryName += ' <sup class="text-danger">R</sup>';
|
||||||
|
// };
|
||||||
|
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 = `
|
||||||
|
<input class="form-check-input" type="checkbox" value="" id="${section}_${entry}">
|
||||||
|
<label class="form-check-label" for="${section}_${entry}">${entryName}</label>
|
||||||
|
`;
|
||||||
|
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 = `
|
||||||
|
<label for="${section}_${entry}">${entryName}</label>
|
||||||
|
<input type="${entryType}" class="form-control" id="${section}_${entry}" aria-describedby="${entry}" value="${entryValue}">
|
||||||
|
`;
|
||||||
|
entryGroup.getElementsByClassName('form-control')[0].required = required;
|
||||||
|
} else if (entryType == 'select') {
|
||||||
|
entryGroup.classList.add('form-group');
|
||||||
|
var entryOptions = config[section][entry]['options'];
|
||||||
|
var innerGroup = `
|
||||||
|
<label for="${section}_${entry}">${entryName}</label>
|
||||||
|
<select class="form-control" id="${section}_${entry}">
|
||||||
|
`;
|
||||||
|
for (var i = 0; i < entryOptions.length; i++) {
|
||||||
|
if (entryOptions[i] == entryValue) {
|
||||||
|
var selected = 'selected';
|
||||||
|
} else {
|
||||||
|
var selected = '';
|
||||||
|
}
|
||||||
|
innerGroup += `
|
||||||
|
<option value="${entryOptions[i]}" ${selected}>${entryOptions[i]}</option>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
innerGroup += '</select>';
|
||||||
|
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');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,20 +93,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
<li class="list-group-item">
|
<p>Note: <sup class="text-danger">*</sup> Indicates required field.</p>
|
||||||
<button type="button" class="btn btn-secondary" id="openUsers">
|
<button type="button" class="list-group-item list-group-item-action" id="openUsers">
|
||||||
Users <i class="fa fa-user"></i>
|
Users <i class="fa fa-user"></i>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
<button type="button" class="list-group-item list-group-item-action" id="openDefaultsWizard">
|
||||||
<li class="list-group-item">
|
New account defaults
|
||||||
<button type="button" class="btn btn-secondary" id="openDefaultsWizard">
|
</button>
|
||||||
New account defaults
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="list-group list-group-flush" id="settingsList">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer" id="settingsFooter">
|
<div class="modal-footer" id="settingsFooter">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="settingsSave">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -153,6 +153,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal fade" id="restartModal" tabindex="-1" role="dialog" aria-labelledby"Restart Warning" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Warning</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>A restart is needed to apply settings. This must be done manually. Apply now?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="applyRestarts" data-dismiss="alert">Apply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="pageContainer">
|
<div class="pageContainer">
|
||||||
<h1>
|
<h1>
|
||||||
Accounts admin
|
Accounts admin
|
||||||
|
@ -344,9 +344,7 @@ def modifyConfig():
|
|||||||
log.debug(f"{section}/{item} does not exist in config")
|
log.debug(f"{section}/{item} does not exist in config")
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
temp_config.write(config_file)
|
temp_config.write(config_file)
|
||||||
log.info("Config written, reloading")
|
log.info("Config written. Restart is needed to load settings.")
|
||||||
config.read(config_path)
|
|
||||||
log.info("Config reloaded.")
|
|
||||||
return resp()
|
return resp()
|
||||||
|
|
||||||
|
|
||||||
@ -363,6 +361,7 @@ def getConfig():
|
|||||||
log.debug('Config requested')
|
log.debug('Config requested')
|
||||||
with open(config_base_path, "r") as f:
|
with open(config_base_path, "r") as f:
|
||||||
config_base = json.load(f)
|
config_base = json.load(f)
|
||||||
|
config.read(config_path)
|
||||||
response_config = config_base
|
response_config = config_base
|
||||||
for section in config_base:
|
for section in config_base:
|
||||||
for entry in config_base[section]:
|
for entry in config_base[section]:
|
||||||
|
Loading…
Reference in New Issue
Block a user