mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-12-22 17:10:11 +00:00
Fix admin, convert invite form, change readme
This commit is contained in:
parent
d1cd83f5ff
commit
acad3b1853
@ -1,11 +1,13 @@
|
|||||||
# ![jellyfin-accounts](https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/jellyfin-accounts-banner-wide.svg)
|
# ![jellyfin-accounts](https://raw.githubusercontent.com/hrfee/jellyfin-accounts/master/images/jellyfin-accounts-banner-wide.svg)
|
||||||
|
|
||||||
|
**This branch uses the bootstrap 5 alpha, which works well enough for the most part, but can sometimes be a bit glitchy. Also no more jquery.**
|
||||||
|
|
||||||
A basic account management system for [Jellyfin](https://github.com/jellyfin/jellyfin).
|
A basic account management system for [Jellyfin](https://github.com/jellyfin/jellyfin).
|
||||||
* Provides a web interface for creating invite codes, and a simple account creation form
|
* Provides a web interface for creating invite codes, and a simple account creation form
|
||||||
* Sends out emails when a user requests a password reset
|
* Sends out emails when a user requests a password reset
|
||||||
* Uses a basic python jellyfin API client for communication with the server.
|
* Uses a basic python jellyfin API client for communication with the server.
|
||||||
* Uses [Flask](https://github.com/pallets/flask), [HTTPAuth](https://github.com/miguelgrinberg/Flask-HTTPAuth), [itsdangerous](https://github.com/pallets/itsdangerous), and [Waitress](https://github.com/Pylons/waitress)
|
* Uses [Flask](https://github.com/pallets/flask), [HTTPAuth](https://github.com/miguelgrinberg/Flask-HTTPAuth), [itsdangerous](https://github.com/pallets/itsdangerous), and [Waitress](https://github.com/Pylons/waitress)
|
||||||
* Frontend uses [Bootstrap](https://getbootstrap.com), [jQuery](https://jquery.com) and [jQuery-serialize-object](https://github.com/macek/jquery-serialize-object)
|
* Frontend uses [Bootstrap 5](https://v5.getbootstrap.com)
|
||||||
* Password resets are handled using smtplib, requests, and [jinja](https://github.com/pallets/jinja)
|
* Password resets are handled using smtplib, requests, and [jinja](https://github.com/pallets/jinja)
|
||||||
## Interface
|
## Interface
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -208,7 +208,7 @@
|
|||||||
"no_username": {
|
"no_username": {
|
||||||
"name": "Use email addresses as username",
|
"name": "Use email addresses as username",
|
||||||
"required": false,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": true,
|
||||||
"depends_true": "method",
|
"depends_true": "method",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"value": false,
|
"value": false,
|
||||||
|
@ -27,9 +27,9 @@ function addItem(invite) {
|
|||||||
listItem.id = invite[0]
|
listItem.id = invite[0]
|
||||||
listItem.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'd-inline-block');
|
listItem.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'd-inline-block');
|
||||||
var listCode = document.createElement('div');
|
var listCode = document.createElement('div');
|
||||||
listCode.classList.add('d-flex', 'align-items-center', 'text-monospace');
|
listCode.classList.add('d-flex', 'align-items-center', 'font-monospace');
|
||||||
var codeLink = document.createElement('a');
|
var codeLink = document.createElement('a');
|
||||||
codeLink.setAttribute('style', 'margin-right: 2%;');
|
codeLink.setAttribute('style', 'margin-right: 0.5rem;');
|
||||||
codeLink.appendChild(document.createTextNode(invite[0].replace(/-/g, '‑')));
|
codeLink.appendChild(document.createTextNode(invite[0].replace(/-/g, '‑')));
|
||||||
listCode.appendChild(codeLink);
|
listCode.appendChild(codeLink);
|
||||||
listItem.appendChild(listCode);
|
listItem.appendChild(listCode);
|
||||||
@ -39,7 +39,7 @@ function addItem(invite) {
|
|||||||
listText.appendChild(document.createTextNode(invite[1]));
|
listText.appendChild(document.createTextNode(invite[1]));
|
||||||
listRight.appendChild(listText);
|
listRight.appendChild(listText);
|
||||||
if (invite[2] == 0) {
|
if (invite[2] == 0) {
|
||||||
var inviteCode = window.location.href + 'invite/' + invite[0];
|
var inviteCode = window.location.href.replace('#', '') + 'invite/' + invite[0];
|
||||||
codeLink.href = inviteCode;
|
codeLink.href = inviteCode;
|
||||||
// listCode.appendChild(document.createTextNode(" "));
|
// listCode.appendChild(document.createTextNode(" "));
|
||||||
var codeCopy = document.createElement('i');
|
var codeCopy = document.createElement('i');
|
||||||
@ -162,31 +162,6 @@ function toClipboard(str) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function serializeForm(id) {
|
|
||||||
var form = document.getElementById(id);
|
|
||||||
var formData = {};
|
|
||||||
for (var i = 0; i < form.elements.length; i++) {
|
|
||||||
var el = form.elements[i];
|
|
||||||
if (el.type != 'submit') {
|
|
||||||
var name = el.name;
|
|
||||||
if (name == '') {
|
|
||||||
name = el.id;
|
|
||||||
};
|
|
||||||
switch (el.type) {
|
|
||||||
case 'checkbox':
|
|
||||||
formData[name] = el.checked;
|
|
||||||
break;
|
|
||||||
case 'text':
|
|
||||||
case 'password':
|
|
||||||
case 'select-one':
|
|
||||||
case 'email':
|
|
||||||
formData[name] = el.value;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return formData;
|
|
||||||
};
|
|
||||||
document.getElementById('inviteForm').onsubmit = function() {
|
document.getElementById('inviteForm').onsubmit = function() {
|
||||||
var button = document.getElementById('generateSubmit');
|
var button = document.getElementById('generateSubmit');
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
@ -623,6 +598,13 @@ document.getElementById('openSettings').onclick = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('settingsMenu').addEventListener('shown.bs.modal', function() {
|
document.getElementById('settingsMenu').addEventListener('shown.bs.modal', function() {
|
||||||
|
// Hack to ensure anything dependent on checkboxes are disabled if necessary
|
||||||
|
var checkboxes = document.getElementById('settingsMenu').querySelectorAll('input[type="checkbox"]');
|
||||||
|
for (var i = 0; i < checkboxes.length; i++) {
|
||||||
|
checkboxes[i].click();
|
||||||
|
checkboxes[i].click();
|
||||||
|
};
|
||||||
|
// Initialize tooltips
|
||||||
var to_trigger = [].slice.call(document.querySelectorAll('a[data-toggle="tooltip"]'));
|
var to_trigger = [].slice.call(document.querySelectorAll('a[data-toggle="tooltip"]'));
|
||||||
var tooltips = to_trigger.map(function(el) {
|
var tooltips = to_trigger.map(function(el) {
|
||||||
return new bootstrap.Tooltip(el);
|
return new bootstrap.Tooltip(el);
|
||||||
|
25
jellyfin_accounts/data/static/serialize.js
Normal file
25
jellyfin_accounts/data/static/serialize.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
function serializeForm(id) {
|
||||||
|
var form = document.getElementById(id);
|
||||||
|
var formData = {};
|
||||||
|
for (var i = 0; i < form.elements.length; i++) {
|
||||||
|
var el = form.elements[i];
|
||||||
|
if (el.type != 'submit') {
|
||||||
|
var name = el.name;
|
||||||
|
if (name == '') {
|
||||||
|
name = el.id;
|
||||||
|
};
|
||||||
|
switch (el.type) {
|
||||||
|
case 'checkbox':
|
||||||
|
formData[name] = el.checked;
|
||||||
|
break;
|
||||||
|
case 'text':
|
||||||
|
case 'password':
|
||||||
|
case 'select-one':
|
||||||
|
case 'email':
|
||||||
|
formData[name] = el.value;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return formData;
|
||||||
|
};
|
@ -197,16 +197,14 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="send_to_address">Send invite to address</label>
|
<label for="send_to_address">Send invite to address</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-prepend">
|
|
||||||
<div class="input-group-text">
|
<div class="input-group-text">
|
||||||
<input type="checkbox" onchange="document.getElementById('send_to_address').disabled = !this.checked;" aria-label="Checkbox to allow input of email address" id="send_to_address_enabled">
|
<input class="form-check-input" type="checkbox" onchange="document.getElementById('send_to_address').disabled = !this.checked;" aria-label="Checkbox to allow input of email address" id="send_to_address_enabled">
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<input type="email" class="form-control" placeholder="example@example.com" id="send_to_address" disabled>
|
<input type="email" class="form-control" placeholder="example@example.com" id="send_to_address" disabled>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="submit" id="generateSubmit" class="btn btn-primary">Generate</button>
|
<button type="submit" id="generateSubmit" class="btn btn-primary" style="margin-top: 1rem;">Generate</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -215,6 +213,7 @@
|
|||||||
<p>{{ contactMessage }}</p>
|
<p>{{ contactMessage }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="serialize.js"></script>
|
||||||
<script src="admin.js"></script>
|
<script src="admin.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -12,12 +12,11 @@
|
|||||||
<meta name="msapplication-TileColor" content="#603cba">
|
<meta name="msapplication-TileColor" content="#603cba">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ css_href }}" integrity="{{ css_integrity }}" crossorigin="{{ css_crossorigin }}">
|
<!-- Bootstrap CSS -->
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css" integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I" crossorigin="anonymous">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-serialize-object/2.5.0/jquery.serialize-object.min.js" integrity="sha256-E8KRdFk/LTaaCBoQIV/rFNc0s3ICQQiOHFT4Cioifa8=" crossorigin="anonymous"></script>
|
|
||||||
<style>
|
<style>
|
||||||
.pageContainer {
|
.pageContainer {
|
||||||
margin: 5% 20% 5% 20%;
|
margin: 5% 20% 5% 20%;
|
||||||
@ -61,11 +60,11 @@
|
|||||||
<p class="contactBox">{{ contactMessage }}</p>
|
<p class="contactBox">{{ contactMessage }}</p>
|
||||||
<div class="container" id="container">
|
<div class="container" id="container">
|
||||||
<div class="row" id="cardContainer">
|
<div class="row" id="cardContainer">
|
||||||
<div class="col-sm" id="accountForm">
|
<div class="col-sm">
|
||||||
<div class="card bg-light mb-3">
|
<div class="card bg-light mb-3">
|
||||||
<div class="card-header">Details</div>
|
<div class="card-header">Details</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="#" method="POST">
|
<form action="#" method="POST" id="accountForm">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputEmail">Email</label>
|
<label for="inputEmail">Email</label>
|
||||||
<input type="email" class="form-control" id="{% if username %}inputEmail{% else %}inputUsername{% endif %}" name="{% if username %}email{% else %}username{% endif %}" placeholder="Email" value="{{ email }}" required>
|
<input type="email" class="form-control" id="{% if username %}inputEmail{% else %}inputUsername{% endif %}" name="{% if username %}email{% else %}username{% endif %}" placeholder="Email" value="{{ email }}" required>
|
||||||
@ -80,7 +79,7 @@
|
|||||||
<label for="inputPassword">Password</label>
|
<label for="inputPassword">Password</label>
|
||||||
<input type="password" class="form-control" id="inputPassword" name="password" placeholder="Password" required>
|
<input type="password" class="form-control" id="inputPassword" name="password" placeholder="Password" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group" role="group" aria-label="Button & Error" id="errorBox">
|
<div class="btn-group" role="group" aria-label="Button & Error" id="errorBox" style="margin-top: 1rem;">
|
||||||
<button type="submit" class="btn btn-outline-primary" id="submitButton">
|
<button type="submit" class="btn btn-outline-primary" id="submitButton">
|
||||||
<span id="createAccount">Create Account</span>
|
<span id="createAccount">Create Account</span>
|
||||||
</button>
|
</button>
|
||||||
@ -108,7 +107,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="serialize.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
var successBox = new bootstrap.Modal(document.getElementById('successBox'));
|
||||||
var code = window.location.href.split('/').pop();
|
var code = window.location.href.split('/').pop();
|
||||||
function toggleSpinner () {
|
function toggleSpinner () {
|
||||||
var submitButton = document.getElementById('submitButton');
|
var submitButton = document.getElementById('submitButton');
|
||||||
@ -131,25 +132,24 @@
|
|||||||
}
|
}
|
||||||
submitButton.replaceChild(newSpan, oldSpan);
|
submitButton.replaceChild(newSpan, oldSpan);
|
||||||
};
|
};
|
||||||
$("form").submit(function() {
|
document.getElementById('accountForm').onsubmit = function() {
|
||||||
toggleSpinner();
|
toggleSpinner();
|
||||||
var send = $("form").serializeObject();
|
var send = serializeForm('accountForm');
|
||||||
send['code'] = code;
|
send['code'] = code;
|
||||||
{% if not username %}
|
{% if not username %}
|
||||||
send['email'] = send['username'];
|
send['email'] = send['username'];
|
||||||
{% endif %}
|
{% endif %}
|
||||||
send = JSON.stringify(send);
|
send = JSON.stringify(send);
|
||||||
$.ajax('/newUser', {
|
var req = new XMLHttpRequest();
|
||||||
data : send,
|
req.open("POST", "/newUser", true);
|
||||||
contentType : 'application/json',
|
req.responseType = 'json';
|
||||||
type : 'POST',
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
crossDomain : true,
|
req.onreadystatechange = function() {
|
||||||
complete : function(response){
|
if (this.readyState == 4) {
|
||||||
toggleSpinner();
|
toggleSpinner();
|
||||||
var data = response['responseJSON'];
|
var data = this.response;
|
||||||
if ('error' in data) {
|
if ('error' in data) {
|
||||||
var text = document.createTextNode(data['error']);
|
var text = document.createTextNode(data['error']);
|
||||||
// <div class="alert alert-danger" id="errorBox"></div>
|
|
||||||
var error = document.createElement('button');
|
var error = document.createElement('button');
|
||||||
error.classList.add('btn', 'btn-outline-danger');
|
error.classList.add('btn', 'btn-outline-danger');
|
||||||
error.setAttribute('disabled', '');
|
error.setAttribute('disabled', '');
|
||||||
@ -177,13 +177,14 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (valid == true) {
|
if (valid == true) {
|
||||||
$('#successBox').modal('show');
|
successBox.show();
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
});
|
};
|
||||||
|
req.send(send);
|
||||||
return false;
|
return false;
|
||||||
});
|
};
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user