user edit, fixed app heights, added api tutorial ready for docker

This commit is contained in:
Ross Mountjoy 2020-02-01 11:55:14 -05:00
parent 9384ff7bc9
commit b59974c0e4
11 changed files with 229 additions and 78 deletions

View File

@ -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
# ------------------------------------------------------------------------------

View File

@ -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")

View File

@ -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"})

View File

@ -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;

View File

@ -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

View 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');
}
});
});
});

View File

@ -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'});
}
}
});
});
});

View File

@ -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 %}

View File

@ -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>

View File

@ -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()

View File

@ -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()