more v0.5 features

- users can now override global settings
- added update message
This commit is contained in:
Ross Mountjoy 2020-03-21 12:18:07 -04:00
parent 621a22b670
commit 83c19616cc
16 changed files with 237 additions and 95 deletions

View File

@ -1,11 +1,7 @@
#### Config.ini Readme #### Config.ini Readme
##### Settings ##### Settings
This is the configuration entry for DashMachine's settings. DashMachine will not work if This is the configuration entry for DashMachine's settings. DashMachine will not work if this is missing. As for all config entries, [Settings] can only appear once in the config. If you change the config.ini file, you either have to restart the container (or python script) or click the save button in the config section of settings for the config to be applied.
this is missing. As for all config entries, [Settings] can only appear once in the config.
If you change the config.ini file, you either have to restart the container
(or python script) or click the save button in the config section of settings for the
config to be applied.
```ini ```ini
[Settings] [Settings]
theme = light theme = light
@ -14,7 +10,9 @@ background = None
roles = admin,user,public_user roles = admin,user,public_user
home_access_groups = admin_only home_access_groups = admin_only
settings_access_groups = admin_only settings_access_groups = admin_only
home_view_mode = grid
custom_app_title = DashMachine custom_app_title = DashMachine
sidebar_default = open
``` ```
| Variable | Required | Description | Options | | Variable | Required | Description | Options |
@ -26,36 +24,32 @@ custom_app_title = DashMachine
| roles | No | User roles for access groups. | comma separated string, if not defined, this is set to 'admin,user,public_user'. Note: admin, user, public_user roles are required and will be added automatically if omitted. | | roles | No | User roles for access groups. | comma separated string, if not defined, this is set to 'admin,user,public_user'. Note: admin, user, public_user roles are required and will be added automatically if omitted. |
| home_access_groups | No | Define which access groups can access the /home page | Groups defined in your config. If not defined, default is admin_only | | home_access_groups | No | Define which access groups can access the /home page | Groups defined in your config. If not defined, default is admin_only |
| settings_access_groups | No | Define which access groups can access the /settings page | Groups defined in your config. If not defined, default is admin_only | | settings_access_groups | No | Define which access groups can access the /settings page | Groups defined in your config. If not defined, default is admin_only |
| home_view_mode | No | Choose the default for view mode on /home | grid, list |
| custom_app_title | No | Change the title of the app for browser tabs | string | | custom_app_title | No | Change the title of the app for browser tabs | string |
| sidebar_default | No | Select the default state for the sidebar | open, closed, no_sidebar |
##### Users ##### Users
Each user requires a config entry, and there must be at least one user in the config Each user requires a config entry, and there must be at least one user in the config (otherwise the default user is added). Each user has a username, a role for configuring access groups, and a password. By default there is one user, named 'admin', with role 'admin' and password 'admin'. To change this user's name, password or role, just modify the config entry's variables and press save. To add a new user, add another user config entry UNDER all existing user config entries. A user with role 'admin' must appear first in the config. Do not change the order of users in the config once they have been defined, otherwise their passwords will not match the next time the config is applied. When users are removed from the config, they are deleted and their cached password is also deleted when the config is applied.
(otherwise the default user is added). Each user has a username, a role for configuring
access groups, and a password. By default there is one user, named 'admin', with role
'admin' and password 'admin'. To change this user's name, password or role,
just modify the config entry's variables and press save. To add a new user, add another
user config entry UNDER all existing user config entries. A user with role 'admin' must
appear first in the config. Do not change the order of users in the config
once they have been defined, otherwise their passwords will not match the next time the
config is applied. When users are removed from the config, they are deleted and their
cached password is also deleted when the config is applied.
```ini ```ini
[Username] [admin]
role = admin role = admin
password = admin password = admin
confirm_password = admin confirm_password = admin
``` ```
| Variable | Required | Description | Options | | Variable | Required | Description | Options |
|------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| |------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
| [Username] | Yes | The user's name for logging in | [Username] | | [Username] | Yes | The user's name for logging in | [Username] |
| role | Yes | The user's role. This is used for access groups and controlling who can view /home and /settings. There must be at least one 'admin' user, and it must be defined first in the config. Otherwise, the first user will be set to admin. | string | | role | Yes | The user's role. This is used for access groups and controlling who can view /home and /settings. There must be at least one 'admin' user, and it must be defined first in the config. Otherwise, the first user will be set to admin. | string |
| password | No | Add a password to this variable to change the password for this user. The password will be hashed, cached and removed from the config. When adding a new user, specify the password, otherwise 'admin' will be used. | string | | password | No | Add a password to this variable to change the password for this user. The password will be hashed, cached and removed from the config. When adding a new user, specify the password, otherwise 'admin' will be used. | string |
| confirm_password | No | When adding a new user or changing an existing user's password you must confirm the password in this variable | string | | confirm_password | No | When adding a new user or changing an existing user's password you must confirm the password in this variable | string |
| theme | No | Override the theme from Settings for this user | same as Settings |
| accent | No | Override the accent from Settings for this user | same as Settings |
| sidebar_default | No | Override the sidebar_default from Settings for this user | same as Settings |
| home_view_mode | No | Override the home_view_mode from Settings for this user | same as Settings |
##### Apps ##### Apps
These entries are the cards that you see one the home page, as well as the sidenav. Entries These entries are the cards that you see one the home page, as well as the sidenav. Entries must be unique. They are displayed in the order that they appear in config.ini
must be unique. They are displayed in the order that they appear in config.ini
```ini ```ini
[App Name] [App Name]
prefix = https:// prefix = https://
@ -83,10 +77,7 @@ groups = admin_only
| groups | No | Optionally the access groups that can see this app. | comma separated string | | groups | No | Optionally the access groups that can see this app. | comma separated string |
##### Access Groups ##### Access Groups
You can create access groups to control what user roles can access parts of the ui. Access groups are just a collection of roles, and each user has an attribute 'role'. Each You can create access groups to control what user roles can access parts of the ui. Access groups are just a collection of roles, and each user has an attribute 'role'. Each application can have an access group, if the user's role is not in the group, the app will be hidden. Also, in the settings entry you can specify `home_access_groups` and `settings_access_groups` to control which groups can access /home and /settings
application can have an access group, if the user's role is not in the group, the app will be hidden.
Also, in the settings entry you can specify `home_access_groups` and `settings_access_groups` to control
which groups can access /home and /settings
```ini ```ini
[admin_only] [admin_only]
roles = admin roles = admin
@ -132,12 +123,7 @@ roles = admin
>Dashmachine will automatically add `admin,user,public_user`, so really you would have 4 roles: `my_people,admin,user,public_user`. Also, the `admin_only` group is required and added by default if omitted. >Dashmachine will automatically add `admin,user,public_user`, so really you would have 4 roles: `my_people,admin,user,public_user`. Also, the `admin_only` group is required and added by default if omitted.
#### Data Source Platforms #### Data Source Platforms
DashMachine includes several different 'platforms' for displaying data on your dash applications. DashMachine includes several different 'platforms' for displaying data on your dash applications. Platforms are essentially plugins. All data source config entries require the `platform` variable, which tells DashMachine which platform file in the platform folder to load. **Note:** you are able to load your own platform files by placing them in the platform folder and referencing them in the config. However currently they will be deleted if you update the application, if you would like to make them permanent, submit a pull request for it to be added by default!
Platforms are essentially plugins. All data source config entries require the `platform` variable,
which tells DashMachine which platform file in the platform folder to load. **Note:** you are able to
load your own platform files by placing them in the platform folder and referencing them in the config.
However currently they will be deleted if you update the application, if you would like to make them
permanent, submit a pull request for it to be added by default!
> To add a data source to your app, add a data source config entry from one of the samples below > To add a data source to your app, add a data source config entry from one of the samples below
**above** the application entry in config.ini, then add the following to your app config entry: **above** the application entry in config.ini, then add the following to your app config entry:

View File

@ -1 +0,0 @@
$2b$12$fB4kz89m4U7Dc9vpXLhDQ.47iNO4.XuIwqdD63lp8tMWIDgMN4y.a

View File

@ -74,6 +74,8 @@ def read_config():
"custom_app_title", "DashMachine" "custom_app_title", "DashMachine"
) )
settings.sidebar_default = config["Settings"].get("sidebar_default", "open")
db.session.add(settings) db.session.add(settings)
db.session.commit() db.session.commit()
@ -82,6 +84,10 @@ def read_config():
user = User() user = User()
user.username = section user.username = section
user.role = config[section]["role"] user.role = config[section]["role"]
user.sidebar_default = config[section].get("sidebar_default", None)
user.home_view_mode = config[section].get("home_view_mode", "grid")
user.theme = config[section].get("theme", None)
user.accent = config[section].get("accent", None)
user.password = "" user.password = ""
if not User.query.filter_by(role="admin").first() and user.role != "admin": if not User.query.filter_by(role="admin").first() and user.role != "admin":
print( print(

View File

@ -5,13 +5,13 @@ from htmlmin.main import minify
from configparser import ConfigParser from configparser import ConfigParser
from flask import render_template, url_for, redirect, request, Blueprint, jsonify from flask import render_template, url_for, redirect, request, Blueprint, jsonify
from flask_login import current_user from flask_login import current_user
from dashmachine.main.models import Files, Apps, DataSources, Tags from dashmachine.main.models import Files, Apps, DataSources
from dashmachine.main.forms import TagsForm
from dashmachine.main.utils import ( from dashmachine.main.utils import (
public_route,
check_groups, check_groups,
get_data_source, get_data_source,
mark_update_message_read,
) )
from dashmachine.user_system.models import User
from dashmachine.settings_system.models import Settings from dashmachine.settings_system.models import Settings
from dashmachine.paths import cache_folder, user_data_folder from dashmachine.paths import cache_folder, user_data_folder
from dashmachine import app, db from dashmachine import app, db
@ -35,28 +35,9 @@ def response_minify(response):
return response return response
# blocks access to all pages (except public routes) unless the user is
# signed in.
@main.before_app_request
def check_valid_login():
if any(
[
request.endpoint.startswith("static"),
current_user.is_authenticated,
getattr(app.view_functions[request.endpoint], "is_public", False),
]
):
return
else:
return redirect(url_for("user_system.login"))
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# /home # /home
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@public_route
@main.route("/") @main.route("/")
@main.route("/home", methods=["GET"]) @main.route("/home", methods=["GET"])
def home(): def home():
@ -66,7 +47,6 @@ def home():
return render_template("main/home.html") return render_template("main/home.html")
@public_route
@main.route("/app_view?<app_id>", methods=["GET"]) @main.route("/app_view?<app_id>", methods=["GET"])
def app_view(app_id): def app_view(app_id):
settings = Settings.query.first() settings = Settings.query.first()
@ -85,20 +65,25 @@ def load_data_source():
return data return data
@public_route @main.route("/change_home_view_mode?<mode>%<user_id>", methods=["GET"])
@main.route("/change_home_view_mode?<mode>", methods=["GET"]) def change_home_view_mode(mode, user_id):
def change_home_view_mode(mode): user = User.query.filter_by(id=user_id).first()
config = ConfigParser() config = ConfigParser()
config.read(os.path.join(user_data_folder, "config.ini")) config.read(os.path.join(user_data_folder, "config.ini"))
config.set("Settings", "home_view_mode", mode) config.set(user.username, "home_view_mode", mode)
config.write(open(os.path.join(user_data_folder, "config.ini"), "w")) config.write(open(os.path.join(user_data_folder, "config.ini"), "w"))
settings = Settings.query.first() user.home_view_mode = mode
settings.home_view_mode = mode db.session.merge(user)
db.session.merge(settings)
db.session.commit() db.session.commit()
return redirect(url_for("main.home")) return redirect(url_for("main.home"))
@main.route("/update_message_read", methods=["GET"])
def update_message_read():
mark_update_message_read()
return "ok"
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# TCDROP routes # TCDROP routes
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -2,9 +2,16 @@ import os
import importlib import importlib
from shutil import copyfile from shutil import copyfile
from PIL import Image from PIL import Image
from dashmachine.paths import dashmachine_folder, images_folder from markdown2 import markdown
from dashmachine.paths import (
dashmachine_folder,
images_folder,
root_folder,
user_data_folder,
)
from dashmachine.main.models import Groups from dashmachine.main.models import Groups
from dashmachine.main.read_config import read_config from dashmachine.main.read_config import read_config
from dashmachine.version import version as dashmachine_version
from dashmachine import db from dashmachine import db
@ -94,3 +101,35 @@ def resize_template_app_images():
image = Image.open(fp) image = Image.open(fp)
image.thumbnail((64, 64)) image.thumbnail((64, 64))
image.save(fp) image.save(fp)
def get_update_message_html():
try:
with open(os.path.join(user_data_folder, ".has_read_update"), "r") as has_read:
has_read_version = has_read.read()
except FileNotFoundError:
has_read_version = None
if not has_read_version or has_read_version.strip() != dashmachine_version:
with open(
os.path.join(root_folder, "update_message.md"), "r"
) as update_message:
md = update_message.read()
config_html = markdown(
md,
extras=[
"tables",
"fenced-code-blocks",
"break-on-newline",
"header-ids",
"code-friendly",
],
)
return config_html
else:
return ""
def mark_update_message_read():
with open(os.path.join(user_data_folder, ".has_read_update"), "w") as has_read:
has_read.write(dashmachine_version)

View File

@ -13,14 +13,17 @@ root_folder = get_root_folder()
dashmachine_folder = os.path.join(root_folder, "dashmachine") dashmachine_folder = os.path.join(root_folder, "dashmachine")
auth_cache = os.path.join(dashmachine_folder, "auth_cache")
template_apps_folder = os.path.join(root_folder, "template_apps") template_apps_folder = os.path.join(root_folder, "template_apps")
platform_folder = os.path.join(dashmachine_folder, "platform") platform_folder = os.path.join(dashmachine_folder, "platform")
user_data_folder = os.path.join(dashmachine_folder, "user_data") user_data_folder = os.path.join(dashmachine_folder, "user_data")
auth_cache = os.path.join(user_data_folder, "auth_cache")
if not os.path.isdir(auth_cache):
os.mkdir(auth_cache)
static_folder = os.path.join(dashmachine_folder, "static") static_folder = os.path.join(dashmachine_folder, "static")
images_folder = os.path.join(static_folder, "images") images_folder = os.path.join(static_folder, "images")

View File

@ -11,3 +11,4 @@ class Settings(db.Model):
settings_access_groups = db.Column(db.String()) settings_access_groups = db.Column(db.String())
home_view_mode = db.Column(db.String()) home_view_mode = db.Column(db.String())
custom_app_title = db.Column(db.String()) custom_app_title = db.Column(db.String())
sidebar_default = db.Column(db.String())

View File

@ -4,7 +4,7 @@ from jsmin import jsmin
from flask_login import current_user from flask_login import current_user
from dashmachine import app from dashmachine import app
from dashmachine.main.models import Apps from dashmachine.main.models import Apps
from dashmachine.main.utils import check_groups from dashmachine.main.utils import check_groups, get_update_message_html
from dashmachine.main.forms import TagsForm from dashmachine.main.forms import TagsForm
from dashmachine.settings_system.models import Settings from dashmachine.settings_system.models import Settings
from dashmachine.paths import static_folder, backgrounds_images_folder from dashmachine.paths import static_folder, backgrounds_images_folder
@ -100,6 +100,7 @@ def context_processor():
f"static/images/backgrounds/" f"static/images/backgrounds/"
f"{random.choice(os.listdir(backgrounds_images_folder))}" f"{random.choice(os.listdir(backgrounds_images_folder))}"
) )
update_message = get_update_message_html()
return dict( return dict(
test_key="test", test_key="test",
process_js_sources=process_js_sources, process_js_sources=process_js_sources,
@ -107,4 +108,5 @@ def context_processor():
apps=apps, apps=apps,
settings=settings, settings=settings,
tags_form=tags_form, tags_form=tags_form,
update_message=update_message,
) )

View File

@ -47,6 +47,7 @@
-ms-user-select: all; -ms-user-select: all;
user-select: all; user-select: all;
cursor: text; cursor: text;
white-space: pre-wrap;
} }
#config-readme th { #config-readme th {
color: var(--theme-primary); color: var(--theme-primary);

View File

@ -67,6 +67,13 @@ function hide_sidenav() {
localStorage.setItem('sidenav_hidden', 'true'); localStorage.setItem('sidenav_hidden', 'true');
} }
function no_sidebar() {
$("#main-sidenav").remove();
$("#main.main-full").css('padding-left', 0);
$("#no-sidenav").removeClass('hide');
localStorage.setItem('sidenav_hidden', 'no_sidebar');
}
function show_sidenav(){ function show_sidenav(){
$("#main-sidenav").removeClass('hide'); $("#main-sidenav").removeClass('hide');
$("#main.main-full").css('padding-left', 64); $("#main.main-full").css('padding-left', 64);
@ -74,26 +81,80 @@ function show_sidenav(){
localStorage.setItem('sidenav_hidden', null); localStorage.setItem('sidenav_hidden', null);
} }
function apply_settings(settings_theme, settings_accent){ function apply_settings(settings){
localStorage.setItem('mode', settings_theme); // theme
document.documentElement.setAttribute('data-theme', settings_theme); if (settings['user_theme'] != "None" && settings['user_theme'].length > 1) {
localStorage.setItem('accent', settings_accent); console.log(settings['user_theme'].length)
document.documentElement.setAttribute('data-accent', settings_accent); localStorage.setItem('mode', settings['user_theme']);
document.documentElement.setAttribute('data-theme', settings['user_theme']);
} else {
localStorage.setItem('mode', settings['settings_theme']);
document.documentElement.setAttribute('data-theme', settings['settings_theme']);
}
// accent
if (settings['user_accent'] != "None" && settings['user_accent'].length > 1) {
localStorage.setItem('accent', settings['user_accent']);
document.documentElement.setAttribute('data-accent', settings['user_accent']);
} else {
localStorage.setItem('accent', settings['settings_accent']);
document.documentElement.setAttribute('data-accent', settings['settings_accent']);
}
if (settings['settings_sidebar_default'] == "closed"){
localStorage.setItem('sidenav_hidden', 'true');
} else if (settings['settings_sidebar_default'] == "open"){
localStorage.setItem('sidenav_hidden', 'false');
} else if (settings['settings_sidebar_default'] == "no_sidebar"){
localStorage.setItem('sidenav_hidden', 'no_sidebar');
}
if (settings['user_sidebar_default'] == "closed"){
localStorage.setItem('sidenav_hidden', 'true');
} else if (settings['user_sidebar_default'] == "open"){
localStorage.setItem('sidenav_hidden', 'false');
} else if (settings['user_sidebar_default'] == "no_sidebar"){
localStorage.setItem('sidenav_hidden', 'no_sidebar');
}
if (localStorage.getItem('sidenav_hidden') === 'true'){
hide_sidenav();
} else if (localStorage.getItem('sidenav_hidden') === 'no_sidebar'){
no_sidebar();
} else if (settings['user_name'].length < 1) {
no_sidebar();
}
} }
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
// Document ready function // Document ready function
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
$(document).ready(function () { $(document).ready(function () {
apply_settings($("#settings-theme").val(), $("#settings-accent").val());
"use strict"; "use strict";
apply_settings({
settings_theme: $("#settings-theme").val(),
settings_accent: $("#settings-accent").val(),
settings_sidebar_default: $("#settings-sidebar_default").val(),
user_name: $("#user-name").val(),
user_theme: $("#user-theme").val(),
user_accent: $("#user-accent").val(),
user_sidebar_default: $("#user-sidebar_default").val(),
});
// INITS // INITS
init_select(); init_select();
if (localStorage.getItem('sidenav_hidden') === 'true'){ $("#update-message-modal").modal({
hide_sidenav(); dismissible: false
});
if ($("#update-message-content").text().length > 1){
$("#update-message-modal").modal('open');
} }
$("#update-message-read-btn").on('click', function(e) {
$.ajax({
url: $(this).attr('data-url'),
type: 'GET',
success: function(data){
$("#update-message-modal").modal('close');
}
});
});
$("#hide-sidenav").on('click', function(e) { $("#hide-sidenav").on('click', function(e) {
hide_sidenav(); hide_sidenav();

View File

@ -1,3 +1,4 @@
{% from 'global_macros.html' import button %}
<!DOCTYPE html> <!DOCTYPE html>
<html id="html" class="loading" lang="en" data-textdirection="ltr"> <html id="html" class="loading" lang="en" data-textdirection="ltr">
@ -47,18 +48,44 @@
data-menu="vertical-dark-menu" data-col="2-columns"> data-menu="vertical-dark-menu" data-col="2-columns">
<input id="copy-input" style="white-space: pre" class="hide"> <input id="copy-input" style="white-space: pre" class="hide">
{# System settings from database #}
<input id="settings-theme" class="hide" value="{{ settings.theme }}"> <input id="settings-theme" class="hide" value="{{ settings.theme }}">
<input id="settings-accent" class="hide" value="{{ settings.accent }}"> <input id="settings-accent" class="hide" value="{{ settings.accent }}">
<input id="settings-sidebar_default" class="hide" value="{{ settings.sidebar_default }}">
{# User settings from database #}
<input id="user-name" class="hide" value="{{ current_user.username }}">
<input id="user-theme" class="hide" value="{{ current_user.theme }}">
<input id="user-accent" class="hide" value="{{ current_user.accent }}">
<input id="user-sidebar_default" class="hide" value="{{ current_user.sidebar_default }}">
<script src="static/js/vendors/jquery.min.js"></script> <script src="static/js/vendors/jquery.min.js"></script>
<div id="update-message-modal" class="modal">
<div id="update-message-content" class="modal-content">
{{ update_message|safe }}
</div>
<div class="modal-footer">
{{ button(
id="update-message-read-btn",
data={"url": url_for('main.update_message_read')},
icon="check_circle",
text="read"
) }}
{{ button(
class="modal-close",
icon="restore",
text="later"
) }}
</div>
</div>
{% block header %}{% endblock header %} {% block header %}{% endblock header %}
{% block sidenav %}{% endblock sidenav %} {% block sidenav %}{% endblock sidenav %}
{% block content %} {% block content %}{% endblock content %}
{% endblock content %}
{% block footer %}{% endblock footer %} {% block footer %}{% endblock footer %}
@ -86,13 +113,6 @@
</style> </style>
</noscript> </noscript>
<!-- GLOBAL DOCUMENT VIEWER -->
<div id="pdf-viewer-modal" class="modal full-height-modal" style="overflow: hidden;">
<div class="modal-content p-0">
<embed id="pdf-viewer-modal-embed" src="" style="width: 100%; min-height: 99.4vh;">
</div>
</div>
</body> </body>
</html> </html>

View File

@ -28,13 +28,13 @@
<i class="material-icons prefix card-search-icon">search</i> <i class="material-icons prefix card-search-icon">search</i>
<input type="text" id="apps-filter" class="card-filter theme-surface-transparent" placeholder="Search apps" autofocus> <input type="text" id="apps-filter" class="card-filter theme-surface-transparent" placeholder="Search apps" autofocus>
{% if current_user.role == "admin" %} {% if current_user.is_authenticated %}
{% if settings.home_view_mode == "list" %} {% if current_user.home_view_mode == "list" %}
<a href="{{ url_for('main.change_home_view_mode', mode="grid") }}"> <a href="{{ url_for('main.change_home_view_mode', mode="grid", user_id=current_user.id) }}">
<i class="material-icons right filter-action pointer">apps</i> <i class="material-icons right filter-action pointer">apps</i>
</a> </a>
{% else %} {% else %}
<a href="{{ url_for('main.change_home_view_mode', mode="list") }}"> <a href="{{ url_for('main.change_home_view_mode', mode="list", user_id=current_user.id) }}">
<i class="material-icons right filter-action pointer">list</i> <i class="material-icons right filter-action pointer">list</i>
</a> </a>
{% endif %} {% endif %}
@ -51,7 +51,7 @@
{% if apps %} {% if apps %}
{# If tags are enabled, render the apps like this #} {# If tags are enabled, render the apps like this #}
{% if tags_form.tags.choices|count > 1 %} {% if tags_form.tags.choices|count > 1 %}
{% if settings.home_view_mode == "list" %} {% if settings.home_view_mode == "list" or current_user.home_view_mode == "list" %}
<div class="col s12 m12 l8"> <div class="col s12 m12 l8">
<div id="list-view-collection" class="collection"> <div id="list-view-collection" class="collection">
@ -96,8 +96,7 @@
{% else %} {% else %}
{# otherwise, render the apps like this #} {# otherwise, render the apps like this #}
{% if settings.home_view_mode == "list" %} {% if settings.home_view_mode == "list" or current_user.home_view_mode == "list" %}
<div class="col s12 m12 l8"> <div class="col s12 m12 l8">
<div id="list-view-collection" class="collection"> <div id="list-view-collection" class="collection">
{% for app in apps %} {% for app in apps %}

View File

@ -31,13 +31,13 @@
<span class="menu-title" data-i18n="">Logout</span> <span class="menu-title" data-i18n="">Logout</span>
</a></li> </a></li>
{% else %} {% else %}
<li class="bold"><a id="logout-sidenav" class="waves-effect waves-cyan" href="{{ url_for('user_system.login') }}"> <li class="bold"><a id="login-sidenav" class="waves-effect waves-cyan" href="{{ url_for('user_system.login') }}">
<i class="material-icons-outlined">account_circle</i> <i class="material-icons-outlined">account_circle</i>
<span class="menu-title" data-i18n="">Login</span> <span class="menu-title" data-i18n="">Login</span>
</a></li> </a></li>
{% endif %} {% endif %}
<li class="bold"><a id="hide-sidenav" class="waves-effect waves-cyan" href="#"> <li class="bold"><a id="hide-sidenav" class="waves-effect waves-cyan hide-on-med-and-down" href="#">
<i class="material-icons-outlined">menu_open</i> <i class="material-icons-outlined">menu_open</i>
<span class="menu-title" data-i18n="">Hide Sidenav</span> <span class="menu-title" data-i18n="">Hide Sidenav</span>
</a></li> </a></li>
@ -62,6 +62,27 @@
{% endblock sidenav%} {% endblock sidenav%}
{% block footer %} {% block footer %}
<div id="no-sidenav" class="card theme-surface-transparent hide" style="position: fixed; bottom: 0; right: 10px;">
<div class="card-content p-1">
<span>
{% if current_user.is_authenticated %}
<a class="waves-effect waves-cyan" href="{{ url_for('user_system.logout') }}">
<i class="material-icons-outlined icon-btn">exit_to_app</i>
</a>
{% else %}
<a class="waves-effect waves-cyan" href="{{ url_for('user_system.login') }}">
<i class="material-icons-outlined icon-btn">account_circle</i>
</a>
{% endif %}
<a class="waves-effect waves-cyan" href="{{ url_for('settings_system.settings') }}">
<i class="material-icons-outlined icon-btn">settings</i>
</a>
<a class="waves-effect waves-cyan" href="{{ url_for('main.home') }}">
<i class="material-icons-outlined icon-btn">dashboard</i>
</a>
</span>
</div>
</div>
<!-- BEGIN: Footer--> <!-- BEGIN: Footer-->
<!-- <footer class="page-footer footer footer-static footer-light navbar-border navbar-shadow"> <!-- <footer class="page-footer footer footer-static footer-light navbar-border navbar-shadow">
<div class="footer-copyright"> <div class="footer-copyright">

View File

@ -12,3 +12,8 @@ class User(db.Model, UserMixin):
username = db.Column(db.String(120), unique=True, nullable=False) username = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False) password = db.Column(db.String(60), nullable=False)
role = db.Column(db.String()) role = db.Column(db.String())
theme = db.Column(db.String())
background = db.Column(db.String())
accent = db.Column(db.String())
home_view_mode = db.Column(db.String(), default="grid")
sidebar_default = db.Column(db.String())

View File

@ -5,7 +5,9 @@ background = None
roles = admin,user,public_user roles = admin,user,public_user
home_access_groups = admin_only home_access_groups = admin_only
settings_access_groups = admin_only settings_access_groups = admin_only
home_view_mode = grid
custom_app_title = DashMachine custom_app_title = DashMachine
sidebar_default = open
[admin] [admin]
role = admin role = admin

12
update_message.md Normal file
View File

@ -0,0 +1,12 @@
##### Updated to version 0.5!
> In this version, I removed the need for database migrations by making the database 100% dynamic. Meaning when you run dashmachine your old site.db file will be deleted and a new one will be created using the data from config.ini. This will make adding features in the future easier and updates will break things less. To accomplish this you will notice that user management has been moved to config.ini. If you are upgrading from a previous version, your user table was deleted. Please login with default user/pass (which is now 'admin' and 'admin') and take a look at the users section in the readme to add users.
**Changelog**
- ui fixes
- users are now managed through config.ini
- no more alembic, completely dynamic database, created on startup
- users can now override global settings
- added update message
- performance fixes
- added setting for hiding sidebar by default
- fixes #41