user edit, fixed app heights, added api tutorial ready for docker
This commit is contained in:
parent
9384ff7bc9
commit
b59974c0e4
@ -63,7 +63,7 @@ def app_view(url):
|
||||
@main.route("/load_rest_data", methods=["GET"])
|
||||
def load_rest_data():
|
||||
data_template = get_rest_data(request.args.get("template"))
|
||||
return data_template[:50]
|
||||
return data_template
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -164,6 +164,7 @@ def public_route(decorated_function):
|
||||
|
||||
|
||||
def dashmachine_init():
|
||||
read_config()
|
||||
read_template_apps()
|
||||
user_data_folder = os.path.join(dashmachine_folder, "user_data")
|
||||
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
from flask import render_template, request, Blueprint, jsonify
|
||||
from dashmachine.settings_system.forms import ConfigForm
|
||||
from dashmachine.user_system.forms import UserForm
|
||||
from dashmachine.user_system.utils import add_edit_user
|
||||
from dashmachine.main.utils import read_config, row2dict
|
||||
from dashmachine.main.models import Files, TemplateApps
|
||||
from dashmachine.paths import backgrounds_images_folder, icons_images_folder
|
||||
@ -73,3 +74,20 @@ def update():
|
||||
@settings_system.route("/settings/check_update", methods=["GET"])
|
||||
def check_update():
|
||||
return str(check_needed())
|
||||
|
||||
|
||||
@settings_system.route("/settings/edit_user", methods=["POST"])
|
||||
def edit_user():
|
||||
form = UserForm()
|
||||
if form.validate_on_submit():
|
||||
if form.password.data != form.confirm_password.data:
|
||||
return jsonify(data={"err": "Passwords don't match"})
|
||||
add_edit_user(form.username.data, form.password.data)
|
||||
else:
|
||||
err_str = ""
|
||||
for fieldName, errorMessages in form.errors.items():
|
||||
err_str += f"{fieldName}: "
|
||||
for err in errorMessages:
|
||||
err_str += f"{err} "
|
||||
return jsonify(data={"err": err_str})
|
||||
return jsonify(data={"err": "success"})
|
||||
|
@ -25,6 +25,10 @@ body
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
|
||||
}
|
||||
|
||||
#main
|
||||
{
|
||||
padding-left: 0;
|
||||
|
@ -74,10 +74,18 @@ function show_sidenav(){
|
||||
localStorage.setItem('sidenav_hidden', null);
|
||||
}
|
||||
|
||||
function apply_settings(settings_theme, settings_accent){
|
||||
localStorage.setItem('mode', settings_theme);
|
||||
document.documentElement.setAttribute('data-theme', settings_theme);
|
||||
localStorage.setItem('accent', settings_accent);
|
||||
document.documentElement.setAttribute('data-accent', settings_accent);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Document ready function
|
||||
//--------------------------------------------------------------------------------------
|
||||
$(document).ready(function () {
|
||||
apply_settings($("#settings-theme").val(), $("#settings-accent").val());
|
||||
"use strict";
|
||||
|
||||
// INITS
|
||||
|
30
dashmachine/static/js/main/home.js
Normal file
30
dashmachine/static/js/main/home.js
Normal file
@ -0,0 +1,30 @@
|
||||
var d = document.getElementById("dashboard-sidenav");
|
||||
d.className += " active theme-primary";
|
||||
|
||||
|
||||
$( document ).ready(function() {
|
||||
$("#apps-filter").on('keyup', function(e) {
|
||||
var value = $(this).val().toLowerCase();
|
||||
$(".app-a").each(function(i, e) {
|
||||
if ($(this).attr("data-name").toLowerCase().indexOf(value) > -1
|
||||
|| $(this).attr("data-description").toLowerCase().indexOf(value) > -1) {
|
||||
$(this).removeClass('hide');
|
||||
} else {
|
||||
$(this).addClass('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".data-template").each(function(e) {
|
||||
var el = $(this);
|
||||
$.ajax({
|
||||
url: el.attr('data-url'),
|
||||
type: 'GET',
|
||||
data: {template: el.text()},
|
||||
success: function(data){
|
||||
el.text(data);
|
||||
el.removeClass('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
@ -1,13 +1,6 @@
|
||||
var d = document.getElementById("settings-sidenav");
|
||||
d.className += " active theme-primary";
|
||||
|
||||
function apply_settings(settings_theme, settings_accent){
|
||||
localStorage.setItem('mode', settings_theme);
|
||||
document.documentElement.setAttribute('data-theme', settings_theme);
|
||||
localStorage.setItem('accent', settings_accent);
|
||||
document.documentElement.setAttribute('data-accent', settings_accent);
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
initTCdrop('#images-tcdrop');
|
||||
$("#config-wiki-modal").modal();
|
||||
@ -20,7 +13,6 @@ $( document ).ready(function() {
|
||||
success: function(data){
|
||||
if (data.data.msg === "success"){
|
||||
M.toast({html: 'Config applied successfully'});
|
||||
apply_settings(data.data.settings.theme, data.data.settings.accent);
|
||||
location.reload(true);
|
||||
} else {
|
||||
M.toast({html: data.data.msg, classes: "theme-warning"});
|
||||
@ -93,4 +85,21 @@ $( document ).ready(function() {
|
||||
});
|
||||
});
|
||||
|
||||
$("#edit-user-btn").on('click', function(e) {
|
||||
$.ajax({
|
||||
url: $(this).attr('data-url'),
|
||||
type: 'POST',
|
||||
data: $("#edit-user-form").serialize(),
|
||||
success: function(data){
|
||||
if (data.data.err !== 'success'){
|
||||
M.toast({html: data.data.err, classes: 'theme-warning'});
|
||||
} else {
|
||||
$("#user-form-password").val('');
|
||||
$("#user-form-confirm_password").val('');
|
||||
M.toast({html: 'User updated'});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -40,16 +40,25 @@
|
||||
<a href="{{ app.prefix }}{{ app.url }}" target="_blank" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
||||
{% endif %}
|
||||
<div class="col s12 m6 l3">
|
||||
<div class="card theme-surface-transparent">
|
||||
<div class="card-content center-align">
|
||||
<img src="{{ app.icon }}" height="64px">
|
||||
<div class="card theme-surface-transparent app-card">
|
||||
<div class="card-content center-align scrollbar" style="max-height: 118px; min-height: 118px;">
|
||||
{% if app.data_template %}
|
||||
<p class="data-template hide" data-url="{{ url_for('main.load_rest_data') }}">
|
||||
{{ app.data_template }}
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col s6 center-align">
|
||||
<img src="{{ app.icon }}" height="64px">
|
||||
</div>
|
||||
|
||||
<div class="col s6 left-align">
|
||||
<p class="data-template hide theme-text" data-url="{{ url_for('main.load_rest_data') }}" style="white-space: pre-line">
|
||||
{{ app.data_template|safe }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{{ app.icon }}" height="64px">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-action center-align">
|
||||
<div class="card-action center-align scrollbar" style="max-height: 127px; min-height: 127px;">
|
||||
<h5>{{ app.name }}</h5>
|
||||
<span class="theme-secondary-text">{{ app.description }}</span>
|
||||
</div>
|
||||
@ -77,35 +86,5 @@
|
||||
{% endblock page_vendor_js %}
|
||||
|
||||
{% block page_lvl_js %}
|
||||
<script type="text/javascript">
|
||||
var d = document.getElementById("dashboard-sidenav");
|
||||
d.className += " active theme-primary";
|
||||
|
||||
$( document ).ready(function() {
|
||||
$("#apps-filter").on('keyup', function(e) {
|
||||
var value = $(this).val().toLowerCase();
|
||||
$(".app-a").each(function(i, e) {
|
||||
if ($(this).attr("data-name").toLowerCase().indexOf(value) > -1
|
||||
|| $(this).attr("data-description").toLowerCase().indexOf(value) > -1) {
|
||||
$(this).removeClass('hide');
|
||||
} else {
|
||||
$(this).addClass('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".data-template").each(function(e) {
|
||||
var el = $(this);
|
||||
$.ajax({
|
||||
url: el.attr('data-url'),
|
||||
type: 'GET',
|
||||
data: {template: el.text()},
|
||||
success: function(data){
|
||||
el.text(data);
|
||||
el.removeClass('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{ process_js_sources(src="main/home.js")|safe }}
|
||||
{% endblock page_lvl_js %}
|
||||
|
@ -25,7 +25,7 @@
|
||||
<h4>Config.ini Readme</h4>
|
||||
</div>
|
||||
<div id="config-help-col" class="col s12 theme-surface-1 padding-2 border-radius-10">
|
||||
<h5>Settings Example</h5>
|
||||
<h5>Settings Reference</h5>
|
||||
<code class="selectable-all">
|
||||
[Settings]<br>
|
||||
theme = dark<br>
|
||||
@ -42,7 +42,7 @@
|
||||
<th>Options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="selectable">
|
||||
<tr>
|
||||
<td>[Settings]</td>
|
||||
<td>Yes</td>
|
||||
@ -70,7 +70,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>App Example</h5>
|
||||
<h5>App Reference</h5>
|
||||
<code class="selectable-all">
|
||||
[App Name]<br>
|
||||
prefix = https://<br>
|
||||
@ -91,7 +91,7 @@
|
||||
<th>Options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="selectable">
|
||||
<tr>
|
||||
<td>[App Name]</td>
|
||||
<td>Yes</td>
|
||||
@ -143,6 +143,100 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>Api Data Reference</h5>
|
||||
<code class="selectable-all">
|
||||
[variable_name]<br>
|
||||
platform = rest<br>
|
||||
resource = your-website.com<br>
|
||||
value_template = variable_name<br>
|
||||
</code>
|
||||
|
||||
<table class="mt-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Required</th>
|
||||
<th>Description</th>
|
||||
<th>Options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="selectable">
|
||||
<tr>
|
||||
<td>[variable_name]</td>
|
||||
<td>Yes</td>
|
||||
<td>The variable to be made available to apps.</td>
|
||||
<td>variable (python syntax)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>platform</td>
|
||||
<td>Yes</td>
|
||||
<td>Platform for data source</td>
|
||||
<td>rest</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>resource</td>
|
||||
<td>Yes</td>
|
||||
<td>The url for the api call.</td>
|
||||
<td>myapp.com/api/hello</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>value_template</td>
|
||||
<td>No</td>
|
||||
<td>Tranform the data returned by the api call (python syntax)</td>
|
||||
<td>variable_name[0]['info']</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>method</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>payload</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>authentication</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>username</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>password</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
<td>NOT IMPLEMENTED</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>Api Data Example</h5>
|
||||
<p>Say we wanted to display how many Pokemon there are using the PokeAPI, we would add the following to the config:</p>
|
||||
<code class="selectable-all">
|
||||
[num_pokemon]<br>
|
||||
platform = rest<br>
|
||||
resource = https://pokeapi.co/api/v2/pokemon<br>
|
||||
value_template = num_pokemon['count']<br>
|
||||
</code>
|
||||
|
||||
<p>Then in the config entry for the app you want to add this to, you would add:</p>
|
||||
|
||||
<code class="selectable-all">
|
||||
data_template = Pokemon: {{ '{{ num_pokemon }}' }}
|
||||
</code>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -175,7 +269,7 @@
|
||||
<form id="config-form">
|
||||
{{ input(
|
||||
size="s12",
|
||||
class="materialize-textarea",
|
||||
class="materialize-textarea code",
|
||||
form_obj=config_form.config,
|
||||
id="config-textarea"
|
||||
) }}
|
||||
@ -247,7 +341,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div id="template-div" class="selectable-all copy-target"></div>
|
||||
<div id="template-div" class="selectable-all code"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -255,34 +349,39 @@
|
||||
<div id="user" class="col s12">
|
||||
<div class="row">
|
||||
<h5>User</h5>
|
||||
{{ user_form.hidden_tag() }}
|
||||
|
||||
{{ input(
|
||||
label="Username",
|
||||
id="user-form-username",
|
||||
size="s12",
|
||||
form_obj=user_form.username,
|
||||
val=current_user.username
|
||||
) }}
|
||||
<form id="edit-user-form">
|
||||
{{ user_form.hidden_tag() }}
|
||||
|
||||
{{ input(
|
||||
label="Password",
|
||||
id="user-form-password",
|
||||
form_obj=user_form.password,
|
||||
size="s12"
|
||||
) }}
|
||||
{{ input(
|
||||
label="Username",
|
||||
id="user-form-username",
|
||||
size="s12",
|
||||
form_obj=user_form.username,
|
||||
val=current_user.username
|
||||
) }}
|
||||
|
||||
{{ input(
|
||||
label="Confirm Password",
|
||||
id="user-form-confirm_password",
|
||||
form_obj=user_form.confirm_password,
|
||||
required='required',
|
||||
size="s12"
|
||||
) }}
|
||||
{{ input(
|
||||
label="Password",
|
||||
id="user-form-password",
|
||||
form_obj=user_form.password,
|
||||
size="s12"
|
||||
) }}
|
||||
|
||||
{{ input(
|
||||
label="Confirm Password",
|
||||
id="user-form-confirm_password",
|
||||
form_obj=user_form.confirm_password,
|
||||
required='required',
|
||||
size="s12"
|
||||
) }}
|
||||
</form>
|
||||
|
||||
{{ button(
|
||||
icon="save",
|
||||
float="left",
|
||||
id="edit-user-btn",
|
||||
data={'url': url_for('settings_system.edit_user')},
|
||||
text="save"
|
||||
) }}
|
||||
</div>
|
||||
|
@ -4,13 +4,13 @@ from wtforms import (
|
||||
PasswordField,
|
||||
BooleanField,
|
||||
)
|
||||
from wtforms.validators import DataRequired
|
||||
from wtforms.validators import DataRequired, Length
|
||||
|
||||
|
||||
class UserForm(FlaskForm):
|
||||
username = StringField(validators=[DataRequired()])
|
||||
username = StringField(validators=[DataRequired(), Length(min=4, max=120)])
|
||||
|
||||
password = PasswordField(validators=[DataRequired()])
|
||||
password = PasswordField(validators=[DataRequired(), Length(min=8, max=120)])
|
||||
|
||||
confirm_password = PasswordField()
|
||||
|
||||
|
@ -3,7 +3,10 @@ from dashmachine.user_system.models import User
|
||||
|
||||
|
||||
def add_edit_user(username, password, user_id=None):
|
||||
user = User.query.filter_by(id=user_id).first()
|
||||
if user_id:
|
||||
user = User.query.filter_by(id=user_id).first()
|
||||
else:
|
||||
user = User.query.first()
|
||||
if not user:
|
||||
user = User()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user