1
This commit is contained in:
parent
3a596c27ce
commit
47fe5c34cb
6
app_templates.ini
Normal file
6
app_templates.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[Nextcloud]
|
||||||
|
prefix = https://
|
||||||
|
url = your-website.com
|
||||||
|
icon = static/images/apps/nextcloud.png
|
||||||
|
description = A safe home for all your data – community-driven, free & open source
|
||||||
|
open_in = this_tab
|
@ -1,30 +0,0 @@
|
|||||||
import os
|
|
||||||
from shutil import copyfile
|
|
||||||
from dashmachine.paths import dashmachine_folder, images_folder
|
|
||||||
from dashmachine.main.utils import read_config
|
|
||||||
from dashmachine.user_system.models import User
|
|
||||||
from dashmachine.user_system.utils import add_edit_user
|
|
||||||
|
|
||||||
|
|
||||||
def dashmachine_init():
|
|
||||||
user_data_folder = os.path.join(dashmachine_folder, "user_data")
|
|
||||||
|
|
||||||
# create the user_data subdirectories, link them to static
|
|
||||||
user_backgrounds_folder = os.path.join(user_data_folder, "backgrounds")
|
|
||||||
if not os.path.isdir(user_backgrounds_folder):
|
|
||||||
os.mkdir(user_backgrounds_folder)
|
|
||||||
os.symlink(user_backgrounds_folder, os.path.join(images_folder, "backgrounds"))
|
|
||||||
|
|
||||||
user_icons_folder = os.path.join(user_data_folder, "user_icons")
|
|
||||||
if not os.path.isdir(user_icons_folder):
|
|
||||||
os.mkdir(user_icons_folder)
|
|
||||||
os.symlink(user_icons_folder, os.path.join(images_folder, "user_icons"))
|
|
||||||
|
|
||||||
config_file = os.path.join(user_data_folder, "config.ini")
|
|
||||||
if not os.path.exists(config_file):
|
|
||||||
copyfile("default_config.ini", config_file)
|
|
||||||
read_config()
|
|
||||||
|
|
||||||
user = User.query.first()
|
|
||||||
if not user:
|
|
||||||
add_edit_user(username="admin", password="admin")
|
|
@ -1,6 +1,6 @@
|
|||||||
from flask import Blueprint, render_template
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
error_pages = Blueprint('error_pages', __name__)
|
error_pages = Blueprint("error_pages", __name__)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@ -8,14 +8,14 @@ error_pages = Blueprint('error_pages', __name__)
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@error_pages.app_errorhandler(404)
|
@error_pages.app_errorhandler(404)
|
||||||
def error_404(error):
|
def error_404(error):
|
||||||
return render_template('/error_pages/404.html'), 404
|
return render_template("/error_pages/404.html"), 404
|
||||||
|
|
||||||
|
|
||||||
@error_pages.app_errorhandler(403)
|
@error_pages.app_errorhandler(403)
|
||||||
def error_403(error):
|
def error_403(error):
|
||||||
return render_template('/error_pages/403.html'), 403
|
return render_template("/error_pages/403.html"), 403
|
||||||
|
|
||||||
|
|
||||||
@error_pages.app_errorhandler(500)
|
@error_pages.app_errorhandler(500)
|
||||||
def error_500(error):
|
def error_500(error):
|
||||||
return render_template('/error_pages/500.html'), 500
|
return render_template("/error_pages/500.html"), 500
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
|
import os
|
||||||
|
from shutil import copyfile
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
|
from dashmachine.paths import dashmachine_folder, images_folder
|
||||||
from dashmachine.main.models import Apps
|
from dashmachine.main.models import Apps
|
||||||
from dashmachine.settings_system.models import Settings
|
from dashmachine.settings_system.models import Settings
|
||||||
|
from dashmachine.user_system.models import User
|
||||||
|
from dashmachine.user_system.utils import add_edit_user
|
||||||
from dashmachine import db
|
from dashmachine import db
|
||||||
|
|
||||||
|
|
||||||
@ -77,3 +83,27 @@ def read_config():
|
|||||||
def public_route(decorated_function):
|
def public_route(decorated_function):
|
||||||
decorated_function.is_public = True
|
decorated_function.is_public = True
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
def dashmachine_init():
|
||||||
|
user_data_folder = os.path.join(dashmachine_folder, "user_data")
|
||||||
|
|
||||||
|
# create the user_data subdirectories, link them to static
|
||||||
|
user_backgrounds_folder = os.path.join(user_data_folder, "backgrounds")
|
||||||
|
if not os.path.isdir(user_backgrounds_folder):
|
||||||
|
os.mkdir(user_backgrounds_folder)
|
||||||
|
os.symlink(user_backgrounds_folder, os.path.join(images_folder, "backgrounds"))
|
||||||
|
|
||||||
|
icons_folder = os.path.join(user_data_folder, "icons")
|
||||||
|
if not os.path.isdir(icons_folder):
|
||||||
|
os.mkdir(icons_folder)
|
||||||
|
os.symlink(icons_folder, os.path.join(images_folder, "icons"))
|
||||||
|
|
||||||
|
config_file = os.path.join(user_data_folder, "config.ini")
|
||||||
|
if not os.path.exists(config_file):
|
||||||
|
copyfile("default_config.ini", config_file)
|
||||||
|
read_config()
|
||||||
|
|
||||||
|
user = User.query.first()
|
||||||
|
if not user:
|
||||||
|
add_edit_user(username="admin", password="admin")
|
@ -21,6 +21,8 @@ apps_images_folder = os.path.join(images_folder, "apps")
|
|||||||
|
|
||||||
backgrounds_images_folder = os.path.join(images_folder, "backgrounds")
|
backgrounds_images_folder = os.path.join(images_folder, "backgrounds")
|
||||||
|
|
||||||
|
icons_images_folder = os.path.join(images_folder, "icons")
|
||||||
|
|
||||||
cache_folder = os.path.join(static_folder, "cache")
|
cache_folder = os.path.join(static_folder, "cache")
|
||||||
|
|
||||||
user_images_folder = os.path.join(images_folder, "user")
|
user_images_folder = os.path.join(images_folder, "user")
|
||||||
|
@ -6,16 +6,16 @@ from dashmachine.version import tcmachine_version
|
|||||||
|
|
||||||
class GetVersion(Resource):
|
class GetVersion(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
return {'Version': tcmachine_version}
|
return {"Version": tcmachine_version}
|
||||||
|
|
||||||
|
|
||||||
class ServerShutdown(Resource):
|
class ServerShutdown(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
os.system('shutdown now')
|
os.system("shutdown now")
|
||||||
return {'Done'}
|
return {"Done"}
|
||||||
|
|
||||||
|
|
||||||
class ServerReboot(Resource):
|
class ServerReboot(Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
os.system('reboot')
|
os.system("reboot")
|
||||||
return {'Done'}
|
return {"Done"}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
from flask import render_template, url_for, redirect, request, Blueprint, jsonify
|
from flask import render_template, request, Blueprint, jsonify
|
||||||
from dashmachine.settings_system.forms import ConfigForm
|
from dashmachine.settings_system.forms import ConfigForm
|
||||||
|
from dashmachine.user_system.forms import UserForm
|
||||||
from dashmachine.main.utils import read_config
|
from dashmachine.main.utils import read_config
|
||||||
from dashmachine.main.models import Files
|
from dashmachine.main.models import Files
|
||||||
from dashmachine.paths import backgrounds_images_folder, apps_images_folder
|
from dashmachine.paths import backgrounds_images_folder, icons_images_folder
|
||||||
from dashmachine.settings_system.utils import load_files_html
|
from dashmachine.settings_system.utils import load_files_html
|
||||||
|
|
||||||
settings_system = Blueprint("settings_system", __name__)
|
settings_system = Blueprint("settings_system", __name__)
|
||||||
@ -12,11 +13,12 @@ settings_system = Blueprint("settings_system", __name__)
|
|||||||
@settings_system.route("/settings", methods=["GET"])
|
@settings_system.route("/settings", methods=["GET"])
|
||||||
def settings():
|
def settings():
|
||||||
config_form = ConfigForm()
|
config_form = ConfigForm()
|
||||||
|
user_form = UserForm()
|
||||||
with open("dashmachine/user_data/config.ini", "r") as config_file:
|
with open("dashmachine/user_data/config.ini", "r") as config_file:
|
||||||
config_form.config.data = config_file.read()
|
config_form.config.data = config_file.read()
|
||||||
files_html = load_files_html()
|
files_html = load_files_html()
|
||||||
return render_template(
|
return render_template(
|
||||||
"settings_system/settings.html", config_form=config_form, files_html=files_html
|
"settings_system/settings.html", config_form=config_form, files_html=files_html, user_form=user_form
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -30,8 +32,8 @@ def save_config():
|
|||||||
|
|
||||||
@settings_system.route("/settings/add_images", methods=["POST"])
|
@settings_system.route("/settings/add_images", methods=["POST"])
|
||||||
def add_images():
|
def add_images():
|
||||||
if request.form.get("folder") == "apps":
|
if request.form.get("folder") == "icons":
|
||||||
dest_folder = apps_images_folder
|
dest_folder = icons_images_folder
|
||||||
elif request.form.get("folder") == "backgrounds":
|
elif request.form.get("folder") == "backgrounds":
|
||||||
dest_folder = backgrounds_images_folder
|
dest_folder = backgrounds_images_folder
|
||||||
for cached_file in request.form.get("files").split(","):
|
for cached_file in request.form.get("files").split(","):
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
from dashmachine.paths import backgrounds_images_folder, apps_images_folder
|
from dashmachine.paths import backgrounds_images_folder, icons_images_folder
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from os import listdir
|
from os import listdir
|
||||||
|
|
||||||
|
|
||||||
def load_files_html():
|
def load_files_html():
|
||||||
background_images = listdir(backgrounds_images_folder)
|
backgrounds = listdir(backgrounds_images_folder)
|
||||||
apps_images = listdir(apps_images_folder)
|
icons = listdir(icons_images_folder)
|
||||||
return render_template(
|
return render_template(
|
||||||
"settings_system/files.html",
|
"settings_system/files.html",
|
||||||
background_images=background_images,
|
backgrounds=backgrounds,
|
||||||
apps_images=apps_images,
|
icons=icons,
|
||||||
)
|
)
|
||||||
|
@ -766,11 +766,6 @@ span.badge.new {
|
|||||||
position: relative;
|
position: relative;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
}
|
}
|
||||||
.copy-btn {
|
|
||||||
position: relative;
|
|
||||||
top: 2px;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*FAB*/
|
/*FAB*/
|
||||||
.tap-target-wave::before, .tap-target-wave::after {
|
.tap-target-wave::before, .tap-target-wave::after {
|
||||||
|
1
dashmachine/static/images/icons
Symbolic link
1
dashmachine/static/images/icons
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/home/ross/Dev/DashMachineEnv/DashMachine/dashmachine/user_data/icons
|
@ -170,22 +170,6 @@ function init_autocomplete_chips(){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function init_send_doc_email_btns(el) {
|
|
||||||
el.on('click', function() {
|
|
||||||
let attachment_deal = $("#attachment-deal")
|
|
||||||
attachment_deal.val($(this).attr('data-deal_id'));
|
|
||||||
attachment_deal.trigger('change');
|
|
||||||
sleep(100).then(() => {
|
|
||||||
let deal_doc = $("#deal-doc")
|
|
||||||
deal_doc.val($(this).attr('data-file'));
|
|
||||||
deal_doc.trigger('change');
|
|
||||||
tinymce.get("email-panel-content").setContent($(this).attr('data-signature'));
|
|
||||||
$("#attachments").removeClass('hide');
|
|
||||||
$("#email-panel").removeClass('hide');
|
|
||||||
$("#chips-mailto input").trigger('focus');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleDarkMode(){
|
function toggleDarkMode(){
|
||||||
let mode = localStorage.getItem('mode');
|
let mode = localStorage.getItem('mode');
|
||||||
@ -212,9 +196,10 @@ function toggleTooltips(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init_copy_btn(){
|
function init_copy_btn(parent_class){
|
||||||
$(".copy-btn").on('click', function(e) {
|
$(".copy-btn").on('click', function(e) {
|
||||||
let target_text = $(this).closest('.col').find('.copy-target').text();
|
let target_text = $(this).closest(parent_class).find('.copy-target').text();
|
||||||
|
console.log(target_text)
|
||||||
let copy_input = $("#copy-input");
|
let copy_input = $("#copy-input");
|
||||||
copy_input.val(target_text);
|
copy_input.val(target_text);
|
||||||
copy_input.removeClass("hide");
|
copy_input.removeClass("hide");
|
||||||
@ -293,49 +278,6 @@ function initCleave(){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TinyMCE Editor
|
|
||||||
function initTinyMCE(el){
|
|
||||||
// Check TinyMCE initialized or not
|
|
||||||
if(tinyMCE.get(el)){
|
|
||||||
// Remove instance by id
|
|
||||||
tinymce.remove('#' + el);
|
|
||||||
}else{
|
|
||||||
let mode = localStorage.getItem('mode');
|
|
||||||
let theme = ""
|
|
||||||
if (mode === 'dark') {
|
|
||||||
theme = "dark"
|
|
||||||
} else {
|
|
||||||
theme = "light"
|
|
||||||
}
|
|
||||||
tinymce.init({
|
|
||||||
selector: '#' + el,
|
|
||||||
height: 200,
|
|
||||||
menubar: false,
|
|
||||||
removed_menuitems: 'undo, redo, anchor',
|
|
||||||
skin: theme,
|
|
||||||
statusbar: true,
|
|
||||||
branding: false,
|
|
||||||
paste_data_images: true,
|
|
||||||
force_br_newlines: true,
|
|
||||||
force_p_newlines: false,
|
|
||||||
forced_root_block: '',
|
|
||||||
content_style: "body {margin-top: 15px}",
|
|
||||||
visual_table_class: 'no-border',
|
|
||||||
mode: "exact",
|
|
||||||
plugins: [
|
|
||||||
'autolink lists link image charmap print preview anchor textcolor',
|
|
||||||
'searchreplace visualblocks code fullscreen',
|
|
||||||
'insertdatetime media table paste code help imagetools'
|
|
||||||
],
|
|
||||||
toolbar: 'formatselect | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent fullscreen code',
|
|
||||||
content_css: [
|
|
||||||
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
|
|
||||||
'//www.tiny.cloud/css/codepen.min.css'
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide_sidenav() {
|
function hide_sidenav() {
|
||||||
$("#main-sidenav").addClass('hide');
|
$("#main-sidenav").addClass('hide');
|
||||||
$("#main.main-full").css('padding-left', 0);
|
$("#main.main-full").css('padding-left', 0);
|
||||||
@ -361,7 +303,6 @@ $(document).ready(function () {
|
|||||||
init_timepicker();
|
init_timepicker();
|
||||||
initCleave();
|
initCleave();
|
||||||
init_tooltips();
|
init_tooltips();
|
||||||
init_copy_btn();
|
|
||||||
init_select();
|
init_select();
|
||||||
|
|
||||||
if (localStorage.getItem('sidenav_hidden') === 'true'){
|
if (localStorage.getItem('sidenav_hidden') === 'true'){
|
||||||
|
@ -63,7 +63,9 @@
|
|||||||
|
|
||||||
{% block sidenav %}{% endblock sidenav %}
|
{% block sidenav %}{% endblock sidenav %}
|
||||||
|
|
||||||
{% block content %}{% endblock content %}
|
{% block content %}
|
||||||
|
<input id="copy-input" class="hide">
|
||||||
|
{% endblock content %}
|
||||||
|
|
||||||
{% block footer %}{% endblock footer %}
|
{% block footer %}{% endblock footer %}
|
||||||
|
|
||||||
|
@ -27,27 +27,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for app in apps %}
|
{% if apps %}
|
||||||
{% if app.open_in == 'iframe' %}
|
|
||||||
<a href="{{ url_for('main.app_view', url=app.url) }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
{% for app in apps %}
|
||||||
{% elif app.open_in == 'this_tab' %}
|
{% if app.open_in == 'iframe' %}
|
||||||
<a href="{{ app.prefix }}{{ app.url }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
<a href="{{ url_for('main.app_view', url=app.url) }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
||||||
{% elif app.open_in == "new_tab" %}
|
{% elif app.open_in == 'this_tab' %}
|
||||||
<a href="{{ app.prefix }}{{ app.url }}" target="_blank" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
<a href="{{ app.prefix }}{{ app.url }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
||||||
{% endif %}
|
{% elif app.open_in == "new_tab" %}
|
||||||
<div class="col s12 m6 l3">
|
<a href="{{ app.prefix }}{{ app.url }}" target="_blank" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
||||||
<div class="card theme-surface-transparent">
|
{% endif %}
|
||||||
<div class="card-content center-align">
|
<div class="col s12 m6 l3">
|
||||||
<img src="{{ app.icon }}" height="64px">
|
<div class="card theme-surface-transparent">
|
||||||
</div>
|
<div class="card-content center-align">
|
||||||
<div class="card-action center-align">
|
<img src="{{ app.icon }}" height="64px">
|
||||||
<h5>{{ app.name }}</h5>
|
</div>
|
||||||
<span class="theme-secondary-text">{{ app.description }}</span>
|
<div class="card-action center-align">
|
||||||
|
<h5>{{ app.name }}</h5>
|
||||||
|
<span class="theme-secondary-text">{{ app.description }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
{% endfor %}
|
||||||
{% endfor %}
|
{% else %}
|
||||||
|
<a href="{{ url_for('settings_system.settings') }}">
|
||||||
|
<div class="col s12 m6 l3">
|
||||||
|
<div class="card theme-surface-transparent">
|
||||||
|
<div class="card-action center-align">
|
||||||
|
<h5>No apps yet, go to settings.</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
|
<style>
|
||||||
|
.file-title {
|
||||||
|
position: relative;
|
||||||
|
top: .6em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<ul class="collection with-header">
|
<ul class="collection with-header">
|
||||||
<li class="collection-header theme-primary white-text">Backgrounds</li>
|
<li class="collection-header theme-primary white-text">Backgrounds</li>
|
||||||
{% for background_image in background_images %}
|
{% if backgrounds %}
|
||||||
<li class="collection-item pt-2 pb-2"><span class="selectable-all">static/images/backgrounds/{{ background_image }}</span>
|
{% for background in backgrounds %}
|
||||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
|
<li class="collection-item pt-2 pb-2 avatar">
|
||||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">filter_none</i></span>
|
<a href="static/images/backgrounds/{{ background }}" target="_blank">
|
||||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">visibility</i></span>
|
<img src="static/images/backgrounds/{{ background }}" alt="" class="circle">
|
||||||
</li>
|
</a>
|
||||||
{% endfor %}
|
<span class="selectable-all copy-target file-title">static/images/backgrounds/{{ background }}</span>
|
||||||
|
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
|
||||||
|
<span class="secondary-content mr-4"><i class="material-icons-outlined icon-btn copy-btn">filter_none</i></span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<li class="collection-item">No files yet</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -17,14 +31,27 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<ul class="collection with-header">
|
<ul class="collection with-header">
|
||||||
<li class="collection-header theme-primary white-text">Apps</li>
|
<li class="collection-header theme-primary white-text">Icons</li>
|
||||||
{% for apps_image in apps_images %}
|
{% if icons %}
|
||||||
<li class="collection-item pt-2 pb-2"><span class="selectable-all">static/images/apps/{{ apps_image }}</span>
|
{% for icon in icons %}
|
||||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
|
<li class="collection-item pt-2 pb-2 avatar">
|
||||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">filter_none</i></span>
|
<a href="static/images/icons/{{ icon }}" target="_blank">
|
||||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">visibility</i></span>
|
<img src="static/images/icons/{{ icon }}" alt="" class="circle">
|
||||||
</li>
|
</a>
|
||||||
{% endfor %}
|
<span class="selectable-all copy-target file-title">static/images/icons/{{ icon }}</span>
|
||||||
|
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
|
||||||
|
<span class="secondary-content mr-4"><i class="material-icons-outlined icon-btn copy-btn">filter_none</i></span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<li class="collection-item">No files yet</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$( document ).ready(function() {
|
||||||
|
init_copy_btn(".collection-item");
|
||||||
|
});
|
||||||
|
</script>
|
@ -48,32 +48,42 @@
|
|||||||
[Settings]<br>
|
[Settings]<br>
|
||||||
theme = dark<br>
|
theme = dark<br>
|
||||||
accent = orange<br>
|
accent = orange<br>
|
||||||
accent = static/images/backgrounds/background.png<br>
|
background = static/images/backgrounds/background.png<br>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<table class="mt-4">
|
<table class="mt-4">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Variable</th>
|
<th>Variable</th>
|
||||||
<th>Value</th>
|
<th>Required</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Options</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>[Settings]</td>
|
<td>[Settings]</td>
|
||||||
<td>config section name. required.</td>
|
<td>Yes</td>
|
||||||
|
<td>Config section name.</td>
|
||||||
|
<td>string</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>theme</td>
|
<td>theme</td>
|
||||||
<td>UI theme, options are light, dark</td>
|
<td>Yes</td>
|
||||||
|
<td>UI theme</td>
|
||||||
|
<td>light, dark</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>accent</td>
|
<td>accent</td>
|
||||||
<td>UI accent, options are orange, green, blue, green, pink, grey</td>
|
<td>Yes</td>
|
||||||
|
<td>UI accent color</td>
|
||||||
|
<td>orange, green, blue, green, pink, grey</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>background</td>
|
<td>background</td>
|
||||||
<td>Background image. can be either a local link (prefixed by /static/images/backgrounds/) or an external link. Can also be set to random. If not set defaults to blank.</td>
|
<td>Yes</td>
|
||||||
|
<td>Background image for the UI</td>
|
||||||
|
<td>/static/images/backgrounds/yourpicture.png, external link to image, None, random</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -93,37 +103,53 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Variable</th>
|
<th>Variable</th>
|
||||||
<th>Value</th>
|
<th>Required</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Options</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>[App Name]</td>
|
<td>[App Name]</td>
|
||||||
|
<td>Yes</td>
|
||||||
<td>The name of your app.</td>
|
<td>The name of your app.</td>
|
||||||
|
<td>string</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>prefix</td>
|
<td>prefix</td>
|
||||||
<td>the prefix for the app's url, e.g. http:// or https://</td>
|
<td>Yes</td>
|
||||||
|
<td>The prefix for the app's url.</td>
|
||||||
|
<td>web prefix, e.g. http:// or https://</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>url</td>
|
<td>url</td>
|
||||||
<td>the url for your app, e.g. google.com</td>
|
<td>Yes</td>
|
||||||
|
<td>The url for your app.</td>
|
||||||
|
<td>web url, e.g. myapp.com</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>icon</td>
|
<td>icon</td>
|
||||||
<td>icon for the dashboard, can be either a local link (prefixed by /static/images/apps/), or an external link</td>
|
<td>No</td>
|
||||||
|
<td>Icon for the dashboard.</td>
|
||||||
|
<td>/static/images/icons/yourpicture.png, external link to image</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>sidebar_icon</td>
|
<td>sidebar_icon</td>
|
||||||
<td>icon for the sidebar, can be either a local link (prefixed by /static/images/apps/), or an external link</td>
|
<td>No</td>
|
||||||
|
<td>Icon for the sidenav.</td>
|
||||||
|
<td>/static/images/icons/yourpicture.png, external link to image</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>description</td>
|
<td>description</td>
|
||||||
<td>a short description for the app</td>
|
<td>No</td>
|
||||||
|
<td>A short description for the app.</td>
|
||||||
|
<td>string</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>open_in</td>
|
<td>open_in</td>
|
||||||
<td>open the app in the current tab, an iframe or a new tab, options are iframe, new_tab, or this_tab</td>
|
<td>Yes</td>
|
||||||
|
<td>open the app in the current tab, an iframe or a new tab</td>
|
||||||
|
<td>iframe, new_tab, this_tab</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -146,20 +172,74 @@
|
|||||||
<div class="col s12 m12 l6">
|
<div class="col s12 m12 l6">
|
||||||
<div class="card scrollbar" style="max-height: calc(100vh - 30px)">
|
<div class="card scrollbar" style="max-height: calc(100vh - 30px)">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<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
|
||||||
|
) }}
|
||||||
|
|
||||||
|
{{ 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"
|
||||||
|
) }}
|
||||||
|
|
||||||
|
{{ button(
|
||||||
|
icon="save",
|
||||||
|
float="left",
|
||||||
|
text="save"
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<h5>App Templates</h5>
|
||||||
|
<div class="row card-filter-container">
|
||||||
|
<div class="col s12 input-field">
|
||||||
|
<span>
|
||||||
|
<i class="material-icons prefix card-search-icon">search</i>
|
||||||
|
<input type="text" id="templates-filter" class="card-filter theme-surface-1" placeholder="Search for app (e.g. Nextcloud)">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<h5>Images</h5>
|
<h5>Images</h5>
|
||||||
<form id="add-images-form">
|
<form id="add-images-form">
|
||||||
<div class="input-field col s12 mt-4">
|
<div class="input-field col s12 mt-4">
|
||||||
<select name="folder">
|
<select name="folder">
|
||||||
<option value="apps">Apps</option>
|
<option value="icons">Icons</option>
|
||||||
<option value="backgrounds">Backgrounds</option>
|
<option value="backgrounds">Backgrounds</option>
|
||||||
</select>
|
</select>
|
||||||
<label>Folder</label>
|
<label>Folder</label>
|
||||||
</div>
|
</div>
|
||||||
<input name="files" id="add-images-input" class="hide">
|
<input name="files" id="add-images-input" class="hide">
|
||||||
</form>
|
</form>
|
||||||
{{ tcdrop(allowed_types='jpg,jpeg,png', id="images-tcdrop", max_files="30") }}
|
{{ tcdrop(allowed_types='jpg,jpeg,png,gif', id="images-tcdrop", max_files="30") }}
|
||||||
{{ button(text="save", icon="save", id="save-images-btn", float="left", data={"url": url_for('settings_system.add_images')}) }}
|
{{ button(text="save", icon="save", id="save-images-btn", float="left", data={"url": url_for('settings_system.add_images')}) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,9 +49,9 @@
|
|||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
<i class="material-icons-outlined prefix">mail_outline</i>
|
<i class="material-icons-outlined prefix">person</i>
|
||||||
{{ form.email(class="validate", id="email", type="text") }}
|
{{ form.username(class="validate", id="username", type="text") }}
|
||||||
<label for="email" class="center-align">Email</label>
|
<label for="username" class="center-align">Username</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
|
@ -1,68 +1,17 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask_login import current_user
|
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
StringField,
|
StringField,
|
||||||
PasswordField,
|
PasswordField,
|
||||||
SubmitField,
|
|
||||||
BooleanField,
|
BooleanField,
|
||||||
SelectField,
|
|
||||||
FileField,
|
|
||||||
)
|
)
|
||||||
from wtforms.validators import DataRequired, EqualTo, Email, Length, ValidationError
|
from wtforms.validators import DataRequired
|
||||||
from dashmachine.user_system.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordForm(FlaskForm):
|
class UserForm(FlaskForm):
|
||||||
password = PasswordField(
|
username = StringField(validators=[DataRequired()])
|
||||||
"Password",
|
|
||||||
validators=[
|
|
||||||
DataRequired(),
|
|
||||||
Length(min=8, message="Password must be at least 8 characters."),
|
|
||||||
EqualTo("confirm_password", message="Passwords must match."),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
confirm_password = PasswordField("Confirm Password", validators=[DataRequired()])
|
password = PasswordField(validators=[DataRequired()])
|
||||||
|
|
||||||
|
confirm_password = PasswordField()
|
||||||
|
|
||||||
class RegisterForm(FlaskForm):
|
remember = BooleanField()
|
||||||
email = StringField("Email", validators=[DataRequired(), Email()])
|
|
||||||
|
|
||||||
def validate_email(form, field):
|
|
||||||
if field.data == current_user.email:
|
|
||||||
email_in_db = None
|
|
||||||
else:
|
|
||||||
email_in_db = User.query.filter_by(email=field.data).first()
|
|
||||||
if email_in_db:
|
|
||||||
raise ValidationError("Email is already registered.")
|
|
||||||
|
|
||||||
fname = StringField("First Name", validators=[DataRequired()])
|
|
||||||
|
|
||||||
lname = StringField("Last Name", validators=[DataRequired()])
|
|
||||||
|
|
||||||
phone = StringField("Phone Number", validators=[DataRequired()])
|
|
||||||
|
|
||||||
company = StringField("Company/Team Name")
|
|
||||||
|
|
||||||
avatar = FileField()
|
|
||||||
|
|
||||||
password = PasswordField(
|
|
||||||
"Password",
|
|
||||||
validators=[
|
|
||||||
DataRequired(),
|
|
||||||
Length(min=8, message="Password must be at least 8 characters."),
|
|
||||||
EqualTo("confirm_password", message="Passwords must match."),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
confirm_password = PasswordField("Confirm Password", validators=[DataRequired()])
|
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(FlaskForm):
|
|
||||||
email = StringField("User Name", validators=[DataRequired()])
|
|
||||||
|
|
||||||
password = PasswordField("Password", validators=[DataRequired()])
|
|
||||||
|
|
||||||
submit = SubmitField()
|
|
||||||
|
|
||||||
remember = BooleanField("Remember Me")
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from flask import render_template, url_for, redirect, Blueprint
|
from flask import render_template, url_for, redirect, Blueprint
|
||||||
from flask_login import login_user, logout_user, current_user
|
from flask_login import login_user, logout_user, current_user
|
||||||
from dashmachine.user_system.forms import LoginForm
|
from dashmachine.user_system.forms import UserForm
|
||||||
from dashmachine.user_system.models import User
|
from dashmachine.user_system.models import User
|
||||||
|
from dashmachine.user_system.utils import add_edit_user
|
||||||
from dashmachine import bcrypt
|
from dashmachine import bcrypt
|
||||||
from dashmachine.main.utils import public_route
|
from dashmachine.main.utils import public_route
|
||||||
|
|
||||||
@ -20,10 +21,10 @@ def login():
|
|||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for("main.home"))
|
return redirect(url_for("main.home"))
|
||||||
|
|
||||||
form = LoginForm()
|
form = UserForm()
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
user = User.query.filter_by(username=form.email.data.lower()).first()
|
user = User.query.filter_by(username=form.username.data.lower()).first()
|
||||||
|
|
||||||
if user and bcrypt.check_password_hash(user.password, form.password.data):
|
if user and bcrypt.check_password_hash(user.password, form.password.data):
|
||||||
login_user(user, remember=form.remember.data)
|
login_user(user, remember=form.remember.data)
|
||||||
@ -43,3 +44,13 @@ def logout():
|
|||||||
|
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect(url_for("user_system.login"))
|
return redirect(url_for("user_system.login"))
|
||||||
|
|
||||||
|
|
||||||
|
@user_system.route("/edit_user", methods=["POST"])
|
||||||
|
def edit_user():
|
||||||
|
form = UserForm()
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
add_edit_user(username=form.username.data, password=form.password.data)
|
||||||
|
|
||||||
|
return 'ok'
|
@ -1 +1 @@
|
|||||||
tcmachine_version = 'v0.0'
|
tcmachine_version = "v0.0"
|
||||||
|
@ -4,12 +4,12 @@ from flask_script import Manager
|
|||||||
from flask_migrate import Migrate, MigrateCommand
|
from flask_migrate import Migrate, MigrateCommand
|
||||||
from dashmachine import app, db
|
from dashmachine import app, db
|
||||||
|
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///dashmachine/site.db'
|
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///dashmachine/site.db"
|
||||||
|
|
||||||
migrate = Migrate(app, db)
|
migrate = Migrate(app, db)
|
||||||
|
|
||||||
manager = Manager(app)
|
manager = Manager(app)
|
||||||
manager.add_command('db', MigrateCommand)
|
manager.add_command("db", MigrateCommand)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
manager.run()
|
manager.run()
|
||||||
|
@ -15,17 +15,19 @@ config = context.config
|
|||||||
# Interpret the config file for Python logging.
|
# Interpret the config file for Python logging.
|
||||||
# This line sets up loggers basically.
|
# This line sets up loggers basically.
|
||||||
fileConfig(config.config_file_name)
|
fileConfig(config.config_file_name)
|
||||||
logger = logging.getLogger('alembic.env')
|
logger = logging.getLogger("alembic.env")
|
||||||
|
|
||||||
# add your model's MetaData object here
|
# add your model's MetaData object here
|
||||||
# for 'autogenerate' support
|
# for 'autogenerate' support
|
||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
config.set_main_option(
|
config.set_main_option(
|
||||||
'sqlalchemy.url', current_app.config.get(
|
"sqlalchemy.url",
|
||||||
'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
|
current_app.config.get("SQLALCHEMY_DATABASE_URI").replace("%", "%%"),
|
||||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
)
|
||||||
|
target_metadata = current_app.extensions["migrate"].db.metadata
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
# can be acquired:
|
# can be acquired:
|
||||||
@ -46,9 +48,7 @@ def run_migrations_offline():
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
url = config.get_main_option("sqlalchemy.url")
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
context.configure(
|
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
|
||||||
url=url, target_metadata=target_metadata, literal_binds=True
|
|
||||||
)
|
|
||||||
|
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
context.run_migrations()
|
context.run_migrations()
|
||||||
@ -66,15 +66,15 @@ def run_migrations_online():
|
|||||||
# when there are no changes to the schema
|
# when there are no changes to the schema
|
||||||
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||||
def process_revision_directives(context, revision, directives):
|
def process_revision_directives(context, revision, directives):
|
||||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
if getattr(config.cmd_opts, "autogenerate", False):
|
||||||
script = directives[0]
|
script = directives[0]
|
||||||
if script.upgrade_ops.is_empty():
|
if script.upgrade_ops.is_empty():
|
||||||
directives[:] = []
|
directives[:] = []
|
||||||
logger.info('No changes in schema detected.')
|
logger.info("No changes in schema detected.")
|
||||||
|
|
||||||
connectable = engine_from_config(
|
connectable = engine_from_config(
|
||||||
config.get_section(config.config_ini_section),
|
config.get_section(config.config_ini_section),
|
||||||
prefix='sqlalchemy.',
|
prefix="sqlalchemy.",
|
||||||
poolclass=pool.NullPool,
|
poolclass=pool.NullPool,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ def run_migrations_online():
|
|||||||
connection=connection,
|
connection=connection,
|
||||||
target_metadata=target_metadata,
|
target_metadata=target_metadata,
|
||||||
process_revision_directives=process_revision_directives,
|
process_revision_directives=process_revision_directives,
|
||||||
**current_app.extensions['migrate'].configure_args
|
**current_app.extensions["migrate"].configure_args
|
||||||
)
|
)
|
||||||
|
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user