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
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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 dashmachine.paths import dashmachine_folder, images_folder
|
||||
from dashmachine.main.models import Apps
|
||||
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
|
||||
|
||||
|
||||
@ -77,3 +83,27 @@ def read_config():
|
||||
def public_route(decorated_function):
|
||||
decorated_function.is_public = True
|
||||
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")
|
||||
|
||||
icons_images_folder = os.path.join(images_folder, "icons")
|
||||
|
||||
cache_folder = os.path.join(static_folder, "cache")
|
||||
|
||||
user_images_folder = os.path.join(images_folder, "user")
|
||||
|
@ -6,16 +6,16 @@ from dashmachine.version import tcmachine_version
|
||||
|
||||
class GetVersion(Resource):
|
||||
def get(self):
|
||||
return {'Version': tcmachine_version}
|
||||
return {"Version": tcmachine_version}
|
||||
|
||||
|
||||
class ServerShutdown(Resource):
|
||||
def get(self):
|
||||
os.system('shutdown now')
|
||||
return {'Done'}
|
||||
os.system("shutdown now")
|
||||
return {"Done"}
|
||||
|
||||
|
||||
class ServerReboot(Resource):
|
||||
def get(self):
|
||||
os.system('reboot')
|
||||
return {'Done'}
|
||||
os.system("reboot")
|
||||
return {"Done"}
|
||||
|
@ -1,9 +1,10 @@
|
||||
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.user_system.forms import UserForm
|
||||
from dashmachine.main.utils import read_config
|
||||
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
|
||||
|
||||
settings_system = Blueprint("settings_system", __name__)
|
||||
@ -12,11 +13,12 @@ settings_system = Blueprint("settings_system", __name__)
|
||||
@settings_system.route("/settings", methods=["GET"])
|
||||
def settings():
|
||||
config_form = ConfigForm()
|
||||
user_form = UserForm()
|
||||
with open("dashmachine/user_data/config.ini", "r") as config_file:
|
||||
config_form.config.data = config_file.read()
|
||||
files_html = load_files_html()
|
||||
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"])
|
||||
def add_images():
|
||||
if request.form.get("folder") == "apps":
|
||||
dest_folder = apps_images_folder
|
||||
if request.form.get("folder") == "icons":
|
||||
dest_folder = icons_images_folder
|
||||
elif request.form.get("folder") == "backgrounds":
|
||||
dest_folder = backgrounds_images_folder
|
||||
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 os import listdir
|
||||
|
||||
|
||||
def load_files_html():
|
||||
background_images = listdir(backgrounds_images_folder)
|
||||
apps_images = listdir(apps_images_folder)
|
||||
backgrounds = listdir(backgrounds_images_folder)
|
||||
icons = listdir(icons_images_folder)
|
||||
return render_template(
|
||||
"settings_system/files.html",
|
||||
background_images=background_images,
|
||||
apps_images=apps_images,
|
||||
backgrounds=backgrounds,
|
||||
icons=icons,
|
||||
)
|
||||
|
@ -766,11 +766,6 @@ span.badge.new {
|
||||
position: relative;
|
||||
right: 10px;
|
||||
}
|
||||
.copy-btn {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/*FAB*/
|
||||
.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(){
|
||||
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) {
|
||||
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");
|
||||
copy_input.val(target_text);
|
||||
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() {
|
||||
$("#main-sidenav").addClass('hide');
|
||||
$("#main.main-full").css('padding-left', 0);
|
||||
@ -361,7 +303,6 @@ $(document).ready(function () {
|
||||
init_timepicker();
|
||||
initCleave();
|
||||
init_tooltips();
|
||||
init_copy_btn();
|
||||
init_select();
|
||||
|
||||
if (localStorage.getItem('sidenav_hidden') === 'true'){
|
||||
|
@ -63,7 +63,9 @@
|
||||
|
||||
{% block sidenav %}{% endblock sidenav %}
|
||||
|
||||
{% block content %}{% endblock content %}
|
||||
{% block content %}
|
||||
<input id="copy-input" class="hide">
|
||||
{% endblock content %}
|
||||
|
||||
{% block footer %}{% endblock footer %}
|
||||
|
||||
|
@ -27,27 +27,40 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
{% for app in 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 }}">
|
||||
{% elif app.open_in == 'this_tab' %}
|
||||
<a href="{{ app.prefix }}{{ app.url }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
||||
{% elif app.open_in == "new_tab" %}
|
||||
<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>
|
||||
<div class="card-action center-align">
|
||||
<h5>{{ app.name }}</h5>
|
||||
<span class="theme-secondary-text">{{ app.description }}</span>
|
||||
{% if apps %}
|
||||
|
||||
{% for app in 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 }}">
|
||||
{% elif app.open_in == 'this_tab' %}
|
||||
<a href="{{ app.prefix }}{{ app.url }}" class="app-a" data-name="{{ app.name }}" data-description="{{ app.description }}">
|
||||
{% elif app.open_in == "new_tab" %}
|
||||
<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>
|
||||
<div class="card-action center-align">
|
||||
<h5>{{ app.name }}</h5>
|
||||
<span class="theme-secondary-text">{{ app.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</a>
|
||||
{% 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>
|
||||
|
@ -1,15 +1,29 @@
|
||||
<style>
|
||||
.file-title {
|
||||
position: relative;
|
||||
top: .6em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<ul class="collection with-header">
|
||||
<li class="collection-header theme-primary white-text">Backgrounds</li>
|
||||
{% for background_image in background_images %}
|
||||
<li class="collection-item pt-2 pb-2"><span class="selectable-all">static/images/backgrounds/{{ background_image }}</span>
|
||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
|
||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">filter_none</i></span>
|
||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">visibility</i></span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if backgrounds %}
|
||||
{% for background in backgrounds %}
|
||||
<li class="collection-item pt-2 pb-2 avatar">
|
||||
<a href="static/images/backgrounds/{{ background }}" target="_blank">
|
||||
<img src="static/images/backgrounds/{{ background }}" alt="" class="circle">
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -17,14 +31,27 @@
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<ul class="collection with-header">
|
||||
<li class="collection-header theme-primary white-text">Apps</li>
|
||||
{% for apps_image in apps_images %}
|
||||
<li class="collection-item pt-2 pb-2"><span class="selectable-all">static/images/apps/{{ apps_image }}</span>
|
||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">close</i></span>
|
||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">filter_none</i></span>
|
||||
<span class="secondary-content"><i class="material-icons-outlined icon-btn">visibility</i></span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="collection-header theme-primary white-text">Icons</li>
|
||||
{% if icons %}
|
||||
{% for icon in icons %}
|
||||
<li class="collection-item pt-2 pb-2 avatar">
|
||||
<a href="static/images/icons/{{ icon }}" target="_blank">
|
||||
<img src="static/images/icons/{{ icon }}" alt="" class="circle">
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
init_copy_btn(".collection-item");
|
||||
});
|
||||
</script>
|
@ -48,32 +48,42 @@
|
||||
[Settings]<br>
|
||||
theme = dark<br>
|
||||
accent = orange<br>
|
||||
accent = static/images/backgrounds/background.png<br>
|
||||
background = static/images/backgrounds/background.png<br>
|
||||
</code>
|
||||
|
||||
<table class="mt-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
<th>Required</th>
|
||||
<th>Description</th>
|
||||
<th>Options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>[Settings]</td>
|
||||
<td>config section name. required.</td>
|
||||
<td>Yes</td>
|
||||
<td>Config section name.</td>
|
||||
<td>string</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>theme</td>
|
||||
<td>UI theme, options are light, dark</td>
|
||||
<td>Yes</td>
|
||||
<td>UI theme</td>
|
||||
<td>light, dark</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -93,37 +103,53 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
<th>Required</th>
|
||||
<th>Description</th>
|
||||
<th>Options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>[App Name]</td>
|
||||
<td>Yes</td>
|
||||
<td>The name of your app.</td>
|
||||
<td>string</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -146,20 +172,74 @@
|
||||
<div class="col s12 m12 l6">
|
||||
<div class="card scrollbar" style="max-height: calc(100vh - 30px)">
|
||||
<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="col s12">
|
||||
<h5>Images</h5>
|
||||
<form id="add-images-form">
|
||||
<div class="input-field col s12 mt-4">
|
||||
<select name="folder">
|
||||
<option value="apps">Apps</option>
|
||||
<option value="icons">Icons</option>
|
||||
<option value="backgrounds">Backgrounds</option>
|
||||
</select>
|
||||
<label>Folder</label>
|
||||
</div>
|
||||
<input name="files" id="add-images-input" class="hide">
|
||||
</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')}) }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -49,9 +49,9 @@
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="input-field col s12">
|
||||
<i class="material-icons-outlined prefix">mail_outline</i>
|
||||
{{ form.email(class="validate", id="email", type="text") }}
|
||||
<label for="email" class="center-align">Email</label>
|
||||
<i class="material-icons-outlined prefix">person</i>
|
||||
{{ form.username(class="validate", id="username", type="text") }}
|
||||
<label for="username" class="center-align">Username</label>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s12">
|
||||
|
@ -1,68 +1,17 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_login import current_user
|
||||
from wtforms import (
|
||||
StringField,
|
||||
PasswordField,
|
||||
SubmitField,
|
||||
BooleanField,
|
||||
SelectField,
|
||||
FileField,
|
||||
)
|
||||
from wtforms.validators import DataRequired, EqualTo, Email, Length, ValidationError
|
||||
from dashmachine.user_system.models import User
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class PasswordForm(FlaskForm):
|
||||
password = PasswordField(
|
||||
"Password",
|
||||
validators=[
|
||||
DataRequired(),
|
||||
Length(min=8, message="Password must be at least 8 characters."),
|
||||
EqualTo("confirm_password", message="Passwords must match."),
|
||||
],
|
||||
)
|
||||
class UserForm(FlaskForm):
|
||||
username = StringField(validators=[DataRequired()])
|
||||
|
||||
confirm_password = PasswordField("Confirm Password", validators=[DataRequired()])
|
||||
password = PasswordField(validators=[DataRequired()])
|
||||
|
||||
confirm_password = PasswordField()
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
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")
|
||||
remember = BooleanField()
|
||||
|
@ -1,7 +1,8 @@
|
||||
from flask import render_template, url_for, redirect, Blueprint
|
||||
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.utils import add_edit_user
|
||||
from dashmachine import bcrypt
|
||||
from dashmachine.main.utils import public_route
|
||||
|
||||
@ -20,10 +21,10 @@ def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("main.home"))
|
||||
|
||||
form = LoginForm()
|
||||
form = UserForm()
|
||||
|
||||
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):
|
||||
login_user(user, remember=form.remember.data)
|
||||
@ -43,3 +44,13 @@ def logout():
|
||||
|
||||
logout_user()
|
||||
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 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)
|
||||
|
||||
manager = Manager(app)
|
||||
manager.add_command('db', MigrateCommand)
|
||||
manager.add_command("db", MigrateCommand)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
manager.run()
|
||||
|
@ -15,17 +15,19 @@ config = context.config
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
logger = logging.getLogger('alembic.env')
|
||||
logger = logging.getLogger("alembic.env")
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
from flask import current_app
|
||||
|
||||
config.set_main_option(
|
||||
'sqlalchemy.url', current_app.config.get(
|
||||
'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
|
||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||
"sqlalchemy.url",
|
||||
current_app.config.get("SQLALCHEMY_DATABASE_URI").replace("%", "%%"),
|
||||
)
|
||||
target_metadata = current_app.extensions["migrate"].db.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
@ -46,9 +48,7 @@ def run_migrations_offline():
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url, target_metadata=target_metadata, literal_binds=True
|
||||
)
|
||||
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
@ -66,15 +66,15 @@ def run_migrations_online():
|
||||
# when there are no changes to the schema
|
||||
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||
def process_revision_directives(context, revision, directives):
|
||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||
if getattr(config.cmd_opts, "autogenerate", False):
|
||||
script = directives[0]
|
||||
if script.upgrade_ops.is_empty():
|
||||
directives[:] = []
|
||||
logger.info('No changes in schema detected.')
|
||||
logger.info("No changes in schema detected.")
|
||||
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix='sqlalchemy.',
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
@ -83,7 +83,7 @@ def run_migrations_online():
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
process_revision_directives=process_revision_directives,
|
||||
**current_app.extensions['migrate'].configure_args
|
||||
**current_app.extensions["migrate"].configure_args
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
|
Loading…
x
Reference in New Issue
Block a user