Dynamic app pages
This commit is contained in:
parent
a1b15aca5a
commit
ff61dd6e7b
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html lang="en">
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
{% include 'includes/head.html' %}
|
||||
</head>
|
||||
<body>
|
||||
{% include 'includes/logo_header.html' %}
|
||||
<div class="mynode_back_div">
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_back" href="/"><span class="ui-icon ui-icon-home"></span>home </a>
|
||||
</div>
|
||||
|
||||
<div class="main_header">{{app.name}} (custom page - remove)</div>
|
||||
<br/>
|
||||
|
||||
<div class="app_tile_row">
|
||||
<div class="info_tile">
|
||||
<div class="info_tile_header">Status</div>
|
||||
<div class="info_tile_contents">
|
||||
<table class="info_table">
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>{{app_status}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Actions</th>
|
||||
<td>
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_button_small" style="width: 100px;" href="#">Open</a>
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_button_small" style="width: 100px;" href="#">Button A</a>
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_button_small" style="width: 100px;" href="#">Button B</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="instructions">
|
||||
<div class="instructions-header">Instructions</div>
|
||||
<ol class="instructions-steps">
|
||||
<li>Custom instructions?</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
3
rootfs/standard/var/www/mynode/app/README
Normal file
3
rootfs/standard/var/www/mynode/app/README
Normal file
|
@ -0,0 +1,3 @@
|
|||
FOLDERS AND FILES IN THIS FOLDER ARE DYNAMICALLY ADDED
|
||||
|
||||
Do not edit files within this folder. They will be overwritten during app install process.
|
32
rootfs/standard/var/www/mynode/generic_app.py
Normal file
32
rootfs/standard/var/www/mynode/generic_app.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
from flask import Blueprint, render_template, redirect, request, url_for
|
||||
from flask import current_app
|
||||
from user_management import check_logged_in
|
||||
from device_info import *
|
||||
from application_info import *
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
|
||||
mynode_generic_app = Blueprint('mynode_generic_app',__name__)
|
||||
|
||||
# This is the generic app page handler. Specific ones can override this.
|
||||
@mynode_generic_app.route('/app/<name>/info')
|
||||
def app_generic_info_page(name):
|
||||
check_logged_in()
|
||||
|
||||
app = get_application(name)
|
||||
if not is_application_valid(name) or app == None:
|
||||
flash("Application is invalid", category="error")
|
||||
return redirect("/apps")
|
||||
|
||||
app_status = get_application_status(name)
|
||||
|
||||
# Load page
|
||||
templateData = {
|
||||
"title": "myNode - " + app["name"],
|
||||
"ui_settings": read_ui_settings(),
|
||||
"app_status": app_status,
|
||||
"app": app
|
||||
}
|
||||
return render_template('/app/generic_app.html', **templateData)
|
58
rootfs/standard/var/www/mynode/marketplace.py
Normal file
58
rootfs/standard/var/www/mynode/marketplace.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
|
||||
from flask import Blueprint, render_template, redirect, request
|
||||
from user_management import check_logged_in
|
||||
from device_info import *
|
||||
from application_info import *
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
|
||||
mynode_marketplace = Blueprint('mynode_marketplace',__name__)
|
||||
|
||||
|
||||
### Page functions
|
||||
@mynode_marketplace.route("/marketplace")
|
||||
def marketplace_page():
|
||||
check_logged_in()
|
||||
|
||||
t1 = get_system_time_in_ms()
|
||||
apps = get_all_applications(order_by="alphabetic")
|
||||
t2 = get_system_time_in_ms()
|
||||
|
||||
categories = [{"name": "bitcoin_app", "title": "Bitcoin Apps"},
|
||||
{"name": "lightning_app", "title": "Lightning Apps"},
|
||||
{"name": "uncategorized", "title": "Uncategorized"}
|
||||
]
|
||||
|
||||
# Load page
|
||||
templateData = {
|
||||
"title": "myNode Marketplace",
|
||||
"ui_settings": read_ui_settings(),
|
||||
"load_time": t2-t1,
|
||||
"product_key_skipped": skipped_product_key(),
|
||||
"categories": categories,
|
||||
"apps": apps,
|
||||
"has_customized_app_versions": has_customized_app_versions(),
|
||||
}
|
||||
return render_template('marketplace.html', **templateData)
|
||||
|
||||
@mynode_marketplace.route("/marketplace/<app_name>")
|
||||
def marketplace_app_page(app_name):
|
||||
check_logged_in()
|
||||
|
||||
app = get_application(app_name)
|
||||
if not is_application_valid(app_name) or app == None:
|
||||
flash("Application is invalid", category="error")
|
||||
return redirect("/marketplace")
|
||||
|
||||
app_status = get_application_status(app_name)
|
||||
|
||||
# Load page
|
||||
templateData = {
|
||||
"title": "myNode - " + app["name"],
|
||||
"ui_settings": read_ui_settings(),
|
||||
"product_key_skipped": skipped_product_key(),
|
||||
"app_status": app_status,
|
||||
"app": app
|
||||
}
|
||||
return render_template('/marketplace_app.html', **templateData)
|
9
rootfs/standard/var/www/mynode/static/css/hes-gallery.min.css
vendored
Normal file
9
rootfs/standard/var/www/mynode/static/css/hes-gallery.min.css
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*!
|
||||
|
||||
HesGallery v1.5.1
|
||||
|
||||
Copyright (c) 2018-2019 Artur Medrygal <medrygal.artur@gmail.com>
|
||||
|
||||
Product under MIT licence
|
||||
|
||||
*/#hgallery{display:block;content:'';position:fixed;top:0;left:0;width:100%;height:100vh;background-color:rgba(0,0,0,.9);visibility:hidden;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;opacity:0;-webkit-transition:.3s;transition:.3s;z-index:99999}.hg-disable-scrolling{overflow:hidden!important}#hg-bg{position:absolute;top:0;left:0;width:100%;height:100vh;z-index:1}#hg-bg::after{content:'';position:absolute;display:block;top:20px;right:20px;width:30px;height:30px;background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+CiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+Cjwvc3ZnPg==);background-position:center;background-size:contain;cursor:pointer;opacity:.8}#hg-bg::after:hover{background-color:#fff}#hgallery.open{visibility:visible!important;opacity:1}#hg-pic-cont{max-width:calc(70% - 40px);max-height:90vh;cursor:default;z-index:12;position:relative;background-color:#fff;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:scale(1);transform:scale(1)}#hg-pic-cont.hg-transition{-webkit-transform:scale(.1);transform:scale(.1)}#hg-subtext{color:#ddd;font-size:14px;position:absolute;display:block;left:5px;top:calc(100% + 6px)}#hg-howmany{color:#aaa;font-size:14px;position:absolute;display:block;right:5px;bottom:-20px}#hg-pic{width:auto;height:auto;min-height:100px;min-width:100px;max-width:100%;max-height:90vh;-webkit-box-sizing:border-box;box-sizing:border-box;display:block;cursor:default;-o-object-fit:contain;object-fit:contain;margin:0}#hg-pic:hover{-webkit-transform:none;transform:none;-webkit-box-shadow:none;box-shadow:none}#hgallery button{position:absolute;display:block;margin:auto 0;width:60px;height:60px;z-index:11;cursor:pointer;background-color:transparent;border:0;outline:0;opacity:0;-webkit-transition:opacity .3s,visibility .3s;transition:opacity .3s,visibility .3s;visibility:hidden}#hgallery button img{width:100%;height:100%;-o-object-fit:contain;object-fit:contain}#hgallery.open button{visibility:visible;opacity:.7}#hgallery button:hover{opacity:1}#hgallery button#hg-prev{left:10px;-webkit-transform:rotate(180deg);transform:rotate(180deg)}#hgallery button#hg-prev:active{left:7px}#hgallery button#hg-next{right:10px}#hgallery button#hg-next:active{right:7px}#hgallery #hg-next-onpic,#hgallery #hg-prev-onpic{position:absolute;top:0;left:0;width:34%;height:100%;cursor:pointer}#hgallery #hg-next-onpic{right:0;left:auto;width:66%}.hg-unvisible{opacity:0!important;visibility:hidden}@media (max-width:1100px){#hg-pic-cont{max-width:calc(100% - 40px)}}
|
10
rootfs/standard/var/www/mynode/static/js/hes-gallery.min.js
vendored
Normal file
10
rootfs/standard/var/www/mynode/static/js/hes-gallery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
98
rootfs/standard/var/www/mynode/static/js/manage_apps.js
Normal file
98
rootfs/standard/var/www/mynode/static/js/manage_apps.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// These functions are used both by the manage_apps page and individual app pages
|
||||
//
|
||||
|
||||
// ==========================================
|
||||
// Manage running apps
|
||||
// ==========================================
|
||||
function restart(name, short_name) {
|
||||
if ( confirm("Are you sure you want to restart "+name+"?\n\nRestarting services like Bitcoin or LND may have side effects. If so, restart the device.") ) {
|
||||
$('#loading_spinner_message').html("Restarting...");
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href='/apps/restart-app?app='+short_name;
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Manage app installations
|
||||
// ==========================================
|
||||
function upgrade(name, short_name) {
|
||||
if ( confirm("Are you sure you want to upgrade "+name+"? This will reboot your device.") ) {
|
||||
$('#loading_spinner_message').html("Upgrading...");
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href='/settings/reinstall-app?app='+short_name;
|
||||
}
|
||||
}
|
||||
|
||||
function reinstall(name, short_name) {
|
||||
if ( confirm("Are you sure you want to re-install "+name+"? This will reboot your device.") ) {
|
||||
$('#loading_spinner_message').html("Re-installing...");
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href='/settings/reinstall-app?app='+short_name;
|
||||
}
|
||||
}
|
||||
|
||||
function install(name, short_name) {
|
||||
if ( confirm("Are you sure you want to install "+name+"? This will reboot your device.") ) {
|
||||
$('#loading_spinner_message').html("Installing...");
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href='/settings/reinstall-app?app='+short_name;
|
||||
}
|
||||
}
|
||||
|
||||
function uninstall(name, short_name) {
|
||||
if ( confirm("Are you sure you want to uninstall "+name+"? ") ) {
|
||||
$('#loading_spinner_message').html("Uninstalling...");
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href='/settings/uninstall-app?app='+short_name;
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Toggle enable/disable functions
|
||||
// ==========================================
|
||||
function get_custom_enable_message(short_name) {
|
||||
message = "";
|
||||
if (short_name == "electrs") {
|
||||
message = "Enabling Electrum Server will take several days to fully sync for \
|
||||
the first time. Your myNode may run slowly during this period.";
|
||||
} else if (short_name == "vpn") {
|
||||
message = "Enabling VPN will set your IP to a static IP rather than a dynamic one via DHCP. \
|
||||
The initial setup may take about an hour.";
|
||||
} else if (short_name == "dojo") {
|
||||
message = "Enabling Dojo for the first time will reboot your device and install Dojo.";
|
||||
}
|
||||
if (message != "") {
|
||||
message += "<br/><br/>";
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
function toggleEnabled(short_name, full_name, is_enabled) {
|
||||
//enabled = application_data[short_name]["is_enabled"];
|
||||
//full_name = application_data[short_name]["name"];
|
||||
|
||||
if ( is_enabled ) {
|
||||
// Disabling
|
||||
openConfirmDialog("confirm-dialog",
|
||||
"Disable "+full_name,
|
||||
"Are you sure you want to disable "+full_name+"?",
|
||||
function(){
|
||||
$( this ).dialog( "close" );
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href="/toggle-enabled?app="+short_name
|
||||
});
|
||||
} else {
|
||||
custom_message = "";
|
||||
// Enabling
|
||||
openConfirmDialog("confirm-dialog",
|
||||
"Enable "+full_name,
|
||||
get_custom_enable_message(short_name) +
|
||||
"Are you sure you want to enable "+full_name+"?",
|
||||
function(){
|
||||
$( this ).dialog( "close" );
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
window.location.href="/toggle-enabled?app="+short_name
|
||||
});
|
||||
}
|
||||
}
|
3
rootfs/standard/var/www/mynode/templates/app/README
Normal file
3
rootfs/standard/var/www/mynode/templates/app/README
Normal file
|
@ -0,0 +1,3 @@
|
|||
FOLDERS AND FILES IN THIS FOLDER ARE DYNAMICALLY ADDED
|
||||
|
||||
Do not edit files within this folder. They will be overwritten during app install process.
|
255
rootfs/standard/var/www/mynode/templates/app/generic_app.html
Normal file
255
rootfs/standard/var/www/mynode/templates/app/generic_app.html
Normal file
|
@ -0,0 +1,255 @@
|
|||
<!DOCTYPE html lang="en">
|
||||
<head>
|
||||
<title>myNode - {{app.name}}</title>
|
||||
{% include 'includes/head.html' %}
|
||||
|
||||
<script src="{{ url_for('static', filename='js/manage_apps.js')}}"></script>
|
||||
|
||||
<style>
|
||||
.hes-gallery {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-gap: 10px;
|
||||
padding: 10px;
|
||||
columns: 4;
|
||||
}
|
||||
.hes-gallery img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hes-gallery img:hover {
|
||||
transform: scale(1.04);
|
||||
box-shadow: 2px 2px 6px #555;
|
||||
}
|
||||
|
||||
|
||||
.app_page_container {
|
||||
margin: auto;
|
||||
width: 1000px;
|
||||
}
|
||||
.app_page_block_header {
|
||||
width: 1000px;
|
||||
height: 12px;
|
||||
padding: 0px 20px 0px 20px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
background-color: orange;
|
||||
}
|
||||
.app_page_block_contents {
|
||||
width: 1000px;
|
||||
padding: 20px 20px 20px 20px;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
background: #f0f0f0;
|
||||
overflow: auto;
|
||||
}
|
||||
.app_page_block_contents_left {
|
||||
width: 150px;
|
||||
float: left;
|
||||
}
|
||||
.app_page_block_contents_right {
|
||||
width: 820px;
|
||||
float: right;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.app_page_icon {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 150px;
|
||||
}
|
||||
.app_page_button {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 150px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
.app_page_block_contents_heading {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.app_page_block_contents_text {
|
||||
font-size: 14px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Hes Gallery -->
|
||||
<link href="{{ url_for('static', filename='css/hes-gallery.min.css')}}" rel="stylesheet">
|
||||
<script src="{{ url_for('static', filename='js/hes-gallery.min.js')}}"></script>
|
||||
|
||||
<script>
|
||||
function restart_app_via_api(name, short_name) {
|
||||
if ( confirm("Are you sure you want to restart "+name+"?") ) {
|
||||
$('#loading_spinner_message').html("Restarting...");
|
||||
$('#loading_spinner_overlay').fadeIn();
|
||||
$.getJSON('/api/restart_app?app='+short_name, function( data ) {
|
||||
alert(data)
|
||||
if (data != "OK") {
|
||||
alert("Error restarting app: "+data)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
$(document).ready(function() {
|
||||
HesGallery.init({
|
||||
disableScrolling: false,
|
||||
wrapAround: true,
|
||||
animations: true,
|
||||
keyboardControl: true,
|
||||
showImageCount: true,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'includes/logo_header.html' %}
|
||||
<div class="mynode_back_div">
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_back" href="/"><span class="ui-icon ui-icon-home"></span>home </a>
|
||||
</div>
|
||||
|
||||
<div class="main_header">{{app.name}}</div>
|
||||
<br/>
|
||||
|
||||
<div class="app_page_container">
|
||||
<div class="app_page_block_header"> </div>
|
||||
<div class="app_page_block_contents">
|
||||
|
||||
<div class="app_page_block_contents_left">
|
||||
<img class="app_page_icon" src="{{ url_for('static', filename="images/app_icons/")}}{{app.short_name}}.png"/>
|
||||
<p style="font-size: 14px; text-align: center;">{{app_status}}</p>
|
||||
|
||||
<br/>
|
||||
|
||||
{% if not app.is_installed %}
|
||||
<!-- Install -->
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button install_button" onclick="install('{{ app.name }}', '{{ app.short_name }}');">Install</button>
|
||||
{% else %}
|
||||
|
||||
<!-- Open, Enable / Disable-->
|
||||
<!-- TODO: ADD ENABLE / DISABLE BUTTON -->
|
||||
{% if app.is_enabled %}
|
||||
{% if app.http_port != "" or app.https_port != "" %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="open_app_in_new_tab('{{app.http_port}}', '{{app.https_port}}', false, '{APP_TOR_ADDRESS}')">Open</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- Manage App: Restart, Reset Data, Etc... -->
|
||||
{% if app.is_enabled %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="restart_app_via_api('{{ app.name }}', '{{ app.short_name }}');">Restart</button>
|
||||
|
||||
{% for btn in app.app_page_additional_buttons %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button"
|
||||
{% if btn.href is defined and btn.href != "" %}
|
||||
onclick="window.location='{{btn.href}}'"
|
||||
{% elif btn.onclick is defined and btn.onclick != "" %}
|
||||
onclick="{{btn.onclick|safe}}"
|
||||
{% endif %}
|
||||
>{{btn.title}}</button>
|
||||
{% endfor %}
|
||||
|
||||
<div class="divider"></div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
<!-- Upgrade / Re-install / Uninstall -->
|
||||
{% if app.current_version != app.latest_version %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="upgrade('{{ app.name }}', '{{ app.short_name }}');">Upgrade</button>
|
||||
{% endif %}
|
||||
{% if app.can_reinstall %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="reinstall('{{ app.name }}', '{{ app.short_name }}');">Reinstall</button>
|
||||
{% endif %}
|
||||
{% if app.can_uninstall %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button uninstall_button" onclick="uninstall('{{ app.name }}', '{{ app.short_name }}');">Uninstall</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="app_page_block_contents_right">
|
||||
<div class="app_page_block_contents_heading">
|
||||
<div class="info-page-block">Info</div>
|
||||
</div>
|
||||
<div class="app_page_block_contents_text">
|
||||
<table class="info_table" style="font-size: 12px; margin-left: 10px">
|
||||
<tr>
|
||||
<th>Installed Version</th>
|
||||
<td>
|
||||
{% if app.is_installed %}
|
||||
{{app.current_version}}
|
||||
{% else %}
|
||||
Not Installed
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Version</th>
|
||||
<td>{{app.latest_version}}</td>
|
||||
</tr>
|
||||
{% if app.author.name is defined %}
|
||||
<tr>
|
||||
<th>Author</th>
|
||||
<td>
|
||||
{% if app.author.link is defined and app.author.link != "" %}
|
||||
<a href="{{app.author.link}}" target="_blank">{{app.author.name}}</a>
|
||||
{% else %}
|
||||
{{app.author.name}}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if app.website.name is defined and app.website.link is defined %}
|
||||
<tr>
|
||||
<th>Website</th>
|
||||
<td>
|
||||
<a href="{{app.website.link}}" target="_blank">{{app.website.name}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="app_page_block_contents_heading">
|
||||
<div class="info-page-block">Description</div>
|
||||
</div>
|
||||
<div class="app_page_block_contents_text">
|
||||
{% if app.description is defined and app.description|length > 0 %}
|
||||
{% for parapraph in app.description %}
|
||||
<p>{{parapraph}}</p>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
No description available.
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if app.screenshots is defined and app.screenshots|length > 0 %}
|
||||
<div class="app_page_block_contents_heading">
|
||||
<div class="info-page-block">Screenshots</div>
|
||||
</div>
|
||||
<div class="app_page_block_contents_text">
|
||||
<div class="hes-gallery">
|
||||
{% for screenshot in app.screenshots %}
|
||||
<img src="{{ url_for('static', filename="images/screenshots/")}}{{app.short_name}}/{{screenshot}}" alt="{{app.name}} Image" data-subtext=""/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="loading_spinner_overlay" class="loading_spinner_overlay" style="display:none;">
|
||||
<img id="loading_spinner" class="loading_image" src="{{ url_for('static', filename="images/loading.gif")}}"/>
|
||||
<br/>
|
||||
<span id="loading_spinner_message">Loading...</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
148
rootfs/standard/var/www/mynode/templates/marketplace.html
Normal file
148
rootfs/standard/var/www/mynode/templates/marketplace.html
Normal file
|
@ -0,0 +1,148 @@
|
|||
<!DOCTYPE html lang="en">
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
{% include 'includes/head.html' %}
|
||||
|
||||
<script src="{{ url_for('static', filename='js/manage_apps.js')}}"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$(".marketplace_app_tile").on("click", function() {
|
||||
shortname = $(this).data("shortname");
|
||||
window.location = "/marketplace/" + shortname;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.marketplace_category_header {
|
||||
color: #555555;
|
||||
text-align: center;
|
||||
font-size: 26px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.marketplace_category_container {
|
||||
width: 1020px;
|
||||
display: flex;
|
||||
margin: auto;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap:wrap;
|
||||
flex-direction: row;
|
||||
}
|
||||
.marketplace_app_tile {
|
||||
width: 320px;
|
||||
height: 80px;
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
.marketplace_app_tile:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 8px 0 rgba(180, 117, 0, 0.2), 0 6px 20px 0 rgba(180, 117, 0, 0.19);
|
||||
}
|
||||
.marketplace_app_tile_left {
|
||||
float: left;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.marketplace_app_tile_icon {
|
||||
float: left;
|
||||
width: 60px;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.marketplace_app_tile_right {
|
||||
float: right;
|
||||
width: 230px;
|
||||
height: 80px;
|
||||
font-size: 11px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.marketplace_app_tile_name {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin-bottom: 4px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
.marketplace_app_tile_description {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.marketplace_app_tile_version {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
.marketplace_app_tile_installed {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'includes/logo_header.html' %}
|
||||
<div class="mynode_back_div">
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_back" href="/"><span class="ui-icon ui-icon-home"></span>home </a>
|
||||
</div>
|
||||
|
||||
<div class="main_header">Marketplace</div>
|
||||
|
||||
{% include 'includes/message_display.html' %}
|
||||
|
||||
</br>
|
||||
|
||||
<!-- <br/>{{ load_time }} ms -->
|
||||
|
||||
<br/><br/>
|
||||
{% for category in categories %}
|
||||
<div class="marketplace_category_header">{{category.title}}</div>
|
||||
|
||||
<div class="marketplace_category_container">
|
||||
{% for app in apps %}
|
||||
{% if app.show_on_application_page and app.category == category.name %}
|
||||
<div class="marketplace_app_tile" data-shortname="{{app.short_name}}">
|
||||
<div class="marketplace_app_tile_left">
|
||||
<img class="marketplace_app_tile_icon" src="{{ url_for('static', filename="images/app_icons/")}}{{app.short_name}}.png"/>
|
||||
</div>
|
||||
<div class="marketplace_app_tile_right">
|
||||
<div class="marketplace_app_tile_name">{{app.name}}</div>
|
||||
<div class="marketplace_app_tile_description">{{app.short_description}}</div>
|
||||
|
||||
{% if not product_key_skipped or product_key_skipped and not app.is_premium %}
|
||||
<div class="marketplace_app_tile_version">Version {{app.latest_version}}</div>
|
||||
{% else %}
|
||||
<div class="marketplace_app_tile_version"><i>Premium Feature</i></div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.is_installed %}
|
||||
<img class="marketplace_app_tile_installed" title="Installed" src="{{ url_for('static', filename="images/")}}app_installed.png"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
<div id="loading_spinner_overlay" class="loading_spinner_overlay" style="display:none;">
|
||||
<img id="loading_spinner" class="loading_image" src="{{ url_for('static', filename="images/loading.gif")}}"/>
|
||||
<br/>
|
||||
<span id="loading_spinner_message">Loading...</span>
|
||||
</div>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
{% include 'includes/footer.html' %}
|
||||
</body>
|
||||
</html>
|
220
rootfs/standard/var/www/mynode/templates/marketplace_app.html
Normal file
220
rootfs/standard/var/www/mynode/templates/marketplace_app.html
Normal file
|
@ -0,0 +1,220 @@
|
|||
<!DOCTYPE html lang="en">
|
||||
<head>
|
||||
<title>myNode - {{app.name}}</title>
|
||||
{% include 'includes/head.html' %}
|
||||
|
||||
<script src="{{ url_for('static', filename='js/manage_apps.js')}}"></script>
|
||||
|
||||
<style>
|
||||
.hes-gallery {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-gap: 10px;
|
||||
padding: 10px;
|
||||
columns: 4;
|
||||
}
|
||||
.hes-gallery img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hes-gallery img:hover {
|
||||
transform: scale(1.04);
|
||||
box-shadow: 2px 2px 6px #555;
|
||||
}
|
||||
|
||||
|
||||
.app_page_container {
|
||||
margin: auto;
|
||||
width: 1000px;
|
||||
}
|
||||
.app_page_block_header {
|
||||
width: 1000px;
|
||||
height: 12px;
|
||||
padding: 0px 20px 0px 20px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
|
||||
}
|
||||
.app_page_block_contents {
|
||||
width: 1000px;
|
||||
padding: 20px 20px 20px 20px;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
overflow: auto;
|
||||
}
|
||||
.app_page_block_contents_left {
|
||||
width: 150px;
|
||||
float: left;
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
.app_page_block_contents_right {
|
||||
width: 760px;
|
||||
float: right;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.app_page_icon {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 150px;
|
||||
}
|
||||
.app_page_button {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 150px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
.app_page_block_contents_heading {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.app_page_block_contents_text {
|
||||
font-size: 14px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Hes Gallery -->
|
||||
<link href="{{ url_for('static', filename='css/hes-gallery.min.css')}}" rel="stylesheet">
|
||||
<script src="{{ url_for('static', filename='js/hes-gallery.min.js')}}"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
HesGallery.init({
|
||||
disableScrolling: false,
|
||||
wrapAround: true,
|
||||
animations: true,
|
||||
keyboardControl: true,
|
||||
showImageCount: true,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'includes/logo_header.html' %}
|
||||
<div class="mynode_back_div">
|
||||
<a class="ui-button ui-widget ui-corner-all mynode_back" href="/marketplace"><span class="ui-icon ui-icon-arrowthick-1-w"></span>back </a>
|
||||
</div>
|
||||
|
||||
<div class="main_header">{{app.name}}</div>
|
||||
<br/>
|
||||
|
||||
<div class="app_page_container">
|
||||
<div class="app_page_block_header"> </div>
|
||||
<div class="app_page_block_contents">
|
||||
|
||||
<div class="app_page_block_contents_left">
|
||||
<img class="app_page_icon" src="{{ url_for('static', filename="images/app_icons/")}}{{app.short_name}}.png"/>
|
||||
<p style="font-size: 14px; text-align: center;">{{app.short_description}}</p>
|
||||
<br/>
|
||||
|
||||
{% if not product_key_skipped or product_key_skipped and not app.is_premium %}
|
||||
<!-- On Marketplace, only show install / uninstall (app pages show others) -->
|
||||
{% if not app.is_installed %}
|
||||
<!-- Install -->
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button install_button" onclick="install('{{ app.name }}', '{{ app.short_name }}');">Install</button>
|
||||
{% else %}
|
||||
<!-- Upgrade / Re-install / Uninstall -->
|
||||
{% if app.current_version != app.latest_version %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button install_button" onclick="upgrade('{{ app.name }}', '{{ app.short_name }}');">Upgrade</button>
|
||||
{% endif %}
|
||||
{% if app.can_reinstall %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="reinstall('{{ app.name }}', '{{ app.short_name }}');">Reinstall</button>
|
||||
{% endif %}
|
||||
{% if app.can_uninstall %}
|
||||
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button uninstall_button" onclick="uninstall('{{ app.name }}', '{{ app.short_name }}');">Uninstall</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p style="font-size: 14px; text-align: center;"><i>Premium Feature</i></p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="app_page_block_contents_right">
|
||||
<div class="app_page_block_contents_heading">
|
||||
<div class="info-page-block">Info</div>
|
||||
</div>
|
||||
<div class="app_page_block_contents_text">
|
||||
<table class="info_table" style="font-size: 12px; margin-left: 10px">
|
||||
<tr>
|
||||
<th>Installed Version</th>
|
||||
<td>
|
||||
{% if app.is_installed %}
|
||||
{{app.current_version}}
|
||||
{% else %}
|
||||
Not Installed
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Latest Version</th>
|
||||
<td>{{app.latest_version}}</td>
|
||||
</tr>
|
||||
{% if app.author.name is defined %}
|
||||
<tr>
|
||||
<th>Author</th>
|
||||
<td>
|
||||
{% if app.author.link is defined and app.author.link != "" %}
|
||||
<a href="{{app.author.link}}" target="_blank">{{app.author.name}}</a>
|
||||
{% else %}
|
||||
{{app.author.name}}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if app.website.name is defined and app.website.link is defined %}
|
||||
<tr>
|
||||
<th>Website</th>
|
||||
<td>
|
||||
<a href="{{app.website.link}}" target="_blank">{{app.website.name}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="app_page_block_contents_heading">
|
||||
<div class="info-page-block">Description</div>
|
||||
</div>
|
||||
<div class="app_page_block_contents_text">
|
||||
{% if app.description is defined and app.description|length > 0 %}
|
||||
{% for parapraph in app.description %}
|
||||
<p>{{parapraph}}</p>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
No description available.
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if app.screenshots is defined and app.screenshots|length > 0 %}
|
||||
<div class="app_page_block_contents_heading">
|
||||
<div class="info-page-block">Screenshots</div>
|
||||
</div>
|
||||
<div class="app_page_block_contents_text">
|
||||
<div class="hes-gallery">
|
||||
{% for screenshot in app.screenshots %}
|
||||
<img src="{{ url_for('static', filename="images/screenshots/")}}{{app.short_name}}/{{screenshot}}" alt="{{app.name}} Image" data-subtext=""/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="loading_spinner_overlay" class="loading_spinner_overlay" style="display:none;">
|
||||
<img id="loading_spinner" class="loading_image" src="{{ url_for('static', filename="images/loading.gif")}}"/>
|
||||
<br/>
|
||||
<span id="loading_spinner_message">Loading...</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user