Compare commits

...

10 Commits

Author SHA1 Message Date
Jonathan Carter
ed2ddb0a77 list debian deps 2025-12-10 21:18:21 +02:00
Jonathan Carter
31bb032a4e Set text_select to false for new pywbview' 2025-10-25 21:52:17 +02:00
Jonathan Carter
22dcf36b14 cleanup mainapplet a bit 2025-08-31 18:31:51 +02:00
Jonathan Carter
088bf495f5 Bunch of ui stuff 2025-08-31 14:53:40 +02:00
Jonathan Carter
c0aa82241b Fix static scrolling 2025-08-28 12:26:13 +02:00
Jonathan Carter
9544463bd6 Minor changes 2025-08-26 16:07:34 +02:00
Jonathan Carter
60cd5140d2 Add systemd unit 2025-08-26 15:58:42 +02:00
Jonathan Carter
dcbc8dd062 Try to make header moving work 2025-08-26 15:58:32 +02:00
Jonathan Carter
08f232bf51 Rename to yasi, add icon 2025-08-26 15:22:26 +02:00
Jonathan Carter
75d068fef6 Exclude cd and loop devs 2025-08-25 20:58:26 +02:00
27 changed files with 316 additions and 277 deletions

View File

@@ -10,12 +10,18 @@ Dependencies:
- flask
- pytest
- coverage
- libxapp-gtk3-module
- gettext
Recommended:
- libjs-htmx (needed if the web front-end is to be used)
- sass-stylesheets-bulma (needed if the web front-end is to be used)
- libxapp-gtk3-module
On Debian-based systems, you should get everything with the following:
```
apt install python3-flask python3-command-runner python3-pytest python3-dmm gettext libjs-htmx sass-stylesheets-bulma fonts-fork-awesome
```
## Development

View File

@@ -1,20 +0,0 @@
#!/usr/bin/env python3
import webview
LOCATION = "http://localhost:8080/welcome"
def on_closed():
"""
Actions to take when System Installer is closed.
"""
print('pywebview window is closed')
window = webview.create_window('System Installer', LOCATION,
transparent=True, easy_drag=False, frameless=True,
width=1050, focus=True, zoomable=True,
confirm_close=True, resizable=True)
window.events.closed += on_closed
webview.start()

View File

@@ -1,32 +0,0 @@
#!/usr/bin/env dmm-perform-recipe
module_path:
- local
settings:
language: en
devmode: True
apt_depends: util-linux adduser parted e2fsprogs debootstrap
apps: main welcome users disks software summary install
menu_order: welcome users disks software summary
recipe:
installer_dependencies:
description: Installing Dependencies
module: aptpkg
function: install
packagooes: apt
chrooti: /tmp
users:
module: users
function: add
users:
- username:
password:
fullname:
sudo: True
popcon:
module: software
function: popcon
enable_popcon: False

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Welcome to SID - The System Installer Daemon!
Welcome to YaSID - The The Yasi System Installer Daemon!
"""
from flask import Flask, request, session, redirect, \
@@ -10,10 +10,10 @@ import logging
import dmm.lsblk as lsblk
import importlib
import yaml
import sys
# configuration
#CONFIGFILE="templates/dmm-installer-template.yaml"
CONFIGFILE="templates/dmm-installer-vm-template.yaml"
CONFIGFILE="/etc/yasi/yasi.yaml"
# import config
global config
@@ -28,6 +28,7 @@ app.config.from_object(__name__)
app.config['CONFIG'] = config
app.app_context().push()
config['settings']['menu'] = {}
sys.path.append('/usr/share/yasi-daemon')
if DEV_MODE:
print("Note: Starting in devmode!")
@@ -53,8 +54,8 @@ if DEV_MODE == "1":
if __name__ == '__main__':
serve(app,
host='0.0.0.0',
port=8080,
host='127.0.0.1',
port=9274,
url_scheme='http',
expose_tracebacks=DEV_MODE)

35
src/yasi-webclient Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import webview
LOCATION = "http://localhost:9274/welcome"
def on_closed():
"""
Actions to take when System Installer is closed.
"""
print('pywebview window is closed')
window = webview.create_window('System Installer', LOCATION,
transparent=True, easy_drag=False, frameless=True,
width=1050, focus=True, zoomable=True,
confirm_close=True, resizable=True, shadow=True,
text_select=False, draggable=False
)
webview.settings = {
'ALLOW_DOWNLOADS': False,
'ALLOW_FILE_URLS': False,
'DRAG_REGION_SELECTOR': '.pywebview-drag-region',
'DRAG_REGION_DIRECT_TARGET_ONLY': True,
'OPEN_EXTERNAL_LINKS_IN_BROWSER': True,
'OPEN_DEVTOOLS_IN_DEBUG': True,
'IGNORE_SSL_ERRORS': False,
'REMOTE_DEBUGGING_PORT': None,
'SHOW_DEFAULT_MENUS': True
}
window.events.closed += on_closed
#webview.start(icon='/usr/share/icons/yasi.png')
webview.start()

View File

View File

@@ -9,8 +9,9 @@
<div class="radios">
<label class="radio">
<input type="radio" name="rsvp" />
Preconfigured partitioning &nbsp;
<input selected type="radio" name="rsvp" />
Preconfigured partitioning &nbsp;
<!-- Let's hide these babies for the initial release.
<label class="radio">
<input type="radio" name="rsvp" disabled />
Erase entire disk
@@ -23,10 +24,11 @@
<input type="radio" name="rsvp" disabled />
Edit existing partitions
</label>
-->
</div>
<div class="columns" width="100%">
<div class="column"><p> <br>Proposed layout: </p></div>
<div class="column"><p> <br>Current layout: </p></div>
</div>
<!--
@@ -64,10 +66,9 @@
-->
<!-- {% for disk in blockdevs["blockdevices"] if disk["name"] != "zram0"%} -->
{% for disk in blockdevs["blockdevices"] if disk["name"] != "zram0" and "loop" not in disk["name"] and disk["name"] != "sr0" %}
<!--
<h1> Raw data </h1>
{{ disk }}
@@ -137,17 +138,40 @@ fssize: {{ child['fssize'] }}
{% endfor %}
-->
<br>
{% endfor %}
<!-- Add Undetected Disk
Add Virtual Filesystem
Add Network Filesystem -->
<div class="columns" width="100%">
<div class="column"><p> <br>Proposed partitioning: </p></div>
</div>
<div style=""> <!-- partition bars -->
<div style="background-color: gray; padding: 4px 6px 7px 6px; border-radius: 10px;">
<p> <span style="width: 100%; border-radius: 8px 8px 0 0;" class="tag is-black">
<small><small> New GPT Partition Table</small></small>
<div class="columns">
{% for partition in partitions %}
{% if "mkpart" in partition['command'] %}
<a hx-get="/disks/partition/{{ partition['device'] }}" hx-swap="outerHTML"/ style="{% if "100%" in partition['command'] %}width: 100%; {% endif %}">
<div class="column is-narrow is-primary">
<div class="box" style="min-width: 220px; border-radius: 0 0 0px 5px; height: 60px; padding-top: 5px;">
<p class="is-tiny"> {{ partition['device'] }} </p>
<p class="title is-5"><span class="tag is-black"> {{ partition['mountpoint'] }} </span>
<span class="tag is-link"> {{ partition['fstype'] }} </span>
</p>
</div>
</div>
</a>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -21,14 +21,18 @@ def disks():
add_disks_button = '&nbsp; <button class="button iis-info"> <i class="fa fa-hdd-o" aria-hidden="true"></i> &nbsp; Add Disk or Filesystem </button>'
add_swap_button = '&nbsp; <button class="button iis-info"> <i class="fa fa-object-group" aria-hidden="true"></i> &nbsp; Swap Configuration </button>'
bottom_menu = add_disks_button + add_swap_button
# no bottom menu for this release
bottom_menu = ""
build_summary()
return render_template('disks.html', blockdevs=blockdevs,
menu=current_app.config['CONFIG']['settings']['menu'],
menu_order=current_app.config['CONFIG']['settings']['menu_order'].split(),
previous_step = previous_step_url,
next_step = next_step_url,
bottom_menu = bottom_menu)
next_step=next_step_url,
partitions=current_app.config['CONFIG']['recipe']['create_partitions']['command_set'],
format_partitions=current_app.config['CONFIG']['recipe']['format_partitions'],
bottom_menu=bottom_menu)
@bp.route('/disks/partition/<part>')

View File

@@ -4,7 +4,15 @@
<!-- drag region class: -->
<div id="titlebar" class="pywebview-drag-region" style="padding: 15px; margin: -15px; padding-bottom: 30px;">
<a hx-get="/main/menu" hx-swap="outerHTML"/> <button class="button is-link"></button </a>
<!--
You might be tempted to replace the unicode character by a fa-code as in the
alternate commend below. Don't do it, it causes all kinds of artifacts and
glitches as the page reloads (and makes it feel much more like an old website
than a proper webapp.
-->
<a hx-get="/main/menu" hx-swap="outerHTML"/> <button class="button is-link"></button> </a>
<!--
<a hx-get="/main/menu" hx-swap="outerHTML"/> <button class="button is-link"> <i class="fa fa-bars" aria-hidden="true"></i> </button> </a> -->
{% for item in menu_order %}
<a href="{{ menu[item][1]}}"> <button class="button is-{% if request.path == menu[item][1] %}info{% else %}dark{% endif %}"> {{ menu[item][0]}} </button></a>

View File

@@ -4,12 +4,18 @@
<div style="background-color: #424242; padding: 15px; border-radius: 15px; boders: none; color: white;">
<h1 class="is-size-4"> Help </h1>
<br/><p class="title is-6"><span class="tag is-danger">The help system is not yet implemented.</span>
<br>
<p>The help system is not yet implemented.</p>
<br>
<p>Some keyboard tips:<p>
<br>
<ul>
<li>ALT+n or CTRL+PGDn moves to the next page.</li>
<li>ALT+p or CTRL+PGUp moves to the previous page.</li>
</ul>
<br>
<div class="columns">
<a href="/welcome"><div class="column"><button class="button is-link">Continue</button></div></a>
</div>

View File

@@ -4,7 +4,18 @@ from flask import Flask, request, session, redirect, \
# we use this neat little trick to get config data from the main app
from flask import current_app
import gettext
import dmm.lsblk as lsblk
# Set up Gettext
def set_language(LANG):
"""
Sets language for this applet
"""
# TODO: unhardcode this path
translations = gettext.translation("welcome", '/usr/share/yasi-daemon/yasi_applets/welcome/locales',
fallback=True, languages=[LANG])
translations.install()
_ = translations.gettext
@bp.route('/')
def index():
@@ -16,16 +27,7 @@ def index():
"Version: Unavailable")
@bp.route('/api/v0')
def apihome():
"""
Not sure what this function should do, but have
a feeling that it should exist.
"""
return ("0")
@bp.route('/main/help',methods=['GET', 'POST', 'PUT'])
@bp.route('/main/help',methods=['GET'])
def main_welcome():
"""
Manages the main help system.
@@ -33,7 +35,7 @@ def main_welcome():
return render_template('help.html')
@bp.route('/main/quit',methods=['GET', 'POST', 'PUT'])
@bp.route('/main/quit',methods=['GET'])
def main_quit():
"""
Manages the quit dialog.
@@ -41,9 +43,20 @@ def main_quit():
return render_template('quit.html')
@bp.route('/main/menu',methods=['GET', 'POST', 'PUT'])
@bp.route('/main/menu',methods=['GET'])
def main_menu():
"""
Manages the main menu.
"""
return render_template('menu.html')
def build_stringlist():
"""
Return all the strings that is used in this applet.
"""
string_dict = {}
string_dict['next_text'] = _("Next")
string_dict['previous_text'] = _("Previous")
return string_dict

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg2"
sodipodi:docname="keyboard.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="52.375"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-width="1236"
inkscape:window-height="1041"
inkscape:window-x="662"
inkscape:window-y="199"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<g
font-weight="400"
fill="#474747"
id="g2"
style="fill:#c0bfbc">
<path
d="M3.702 1C2.17 1 .984 2.32.984 3.844v8.344c0 1.524 1.185 2.843 2.718 2.843h8.58c1.532 0 2.75-1.32 2.75-2.844V3.845c0-1.525-1.218-2.844-2.75-2.844zm.782 1.031c3.526.256 5.317.134 7.047 0 .754-.058 1.532.616 1.532 1.438v7.375c0 .665-.532 1.095-1.188 1.187-2.836.397-4.753.44-7.673 0-.655-.099-1.187-.522-1.187-1.187V3.5c0-.822.714-1.524 1.469-1.469z"
style="line-height:normal;-inkscape-font-specification:'Bitstream Vera Sans';text-indent:0;text-align:start;text-decoration-line:none;text-transform:none;marker:none;fill:#c0bfbc"
color="#bebebe"
font-family="Bitstream Vera Sans"
overflow="visible"
id="path1" />
<path
d="M10.564 5.977l-2.939.044.016 1 2.879-.043c.2.018.33.076.396.135.067.06.105.121.105.293l-.008.604H8.517l.037.002a1.457 1.457 0 00-1.164.43 1.558 1.558 0 00-.416 1.023c-.013.367.092.75.352 1.053.26.303.687.496 1.162.482h3.482l.051-3.59v-.004c0-.402-.16-.786-.435-1.035-.276-.249-.63-.364-.998-.393zM8.48 9.01h2.519l-.014.99H8.457c-.22.007-.302-.051-.371-.133a.553.553 0 01-.112-.367.587.587 0 01.141-.371c.079-.083.163-.134.363-.12zm2.862-5.985l-3 1 .316.95 3-1z"
style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;shape-padding:0;isolation:auto;mix-blend-mode:normal;marker:none;fill:#c0bfbc"
color="#000"
font-family="sans-serif"
overflow="visible"
id="path2" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M192 64C209.7 64 224 78.3 224 96L224 128L352 128C369.7 128 384 142.3 384 160C384 177.7 369.7 192 352 192L342.4 192L334 215.1C317.6 260.3 292.9 301.6 261.8 337.1C276 345.9 290.8 353.7 306.2 360.6L356.6 383L418.8 243C423.9 231.4 435.4 224 448 224C460.6 224 472.1 231.4 477.2 243L605.2 531C612.4 547.2 605.1 566.1 589 573.2C572.9 580.3 553.9 573.1 546.8 557L526.8 512L369.3 512L349.3 557C342.1 573.2 323.2 580.4 307.1 573.2C291 566 283.7 547.1 290.9 531L330.7 441.5L280.3 419.1C257.3 408.9 235.3 396.7 214.5 382.7C193.2 399.9 169.9 414.9 145 427.4L110.3 444.6C94.5 452.5 75.3 446.1 67.4 430.3C59.5 414.5 65.9 395.3 81.7 387.4L116.2 370.1C132.5 361.9 148 352.4 162.6 341.8C148.8 329.1 135.8 315.4 123.7 300.9L113.6 288.7C102.3 275.1 104.1 254.9 117.7 243.6C131.3 232.3 151.5 234.1 162.8 247.7L173 259.9C184.5 273.8 197.1 286.7 210.4 298.6C237.9 268.2 259.6 232.5 273.9 193.2L274.4 192L64.1 192C46.3 192 32 177.7 32 160C32 142.3 46.3 128 64 128L160 128L160 96C160 78.3 174.3 64 192 64zM448 334.8L397.7 448L498.3 448L448 334.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -10,6 +10,7 @@ body {
overflow: hidden;
min-width: 1050px;
min-height: 600px;
user-select: none;
}
html, body {margin: 0; height: 100%; overflow: hidden;}
@@ -23,19 +24,31 @@ html, body {margin: 0; height: 100%; overflow: hidden;}
position: absolute;
top: 0px;
bottom: 0px;
user-select: none;
}
.applet a:link a:hover a:active {
color: yellow;
}
.applet::-webkit-scrollbar {
display: none;
}
.applet {
width: 100%;
height: 442px;
height: 512px;
overflow-y: scroll;
overflow-x: hidden;
overflow: -moz-scrollbars-none;
}
.radio:hover {
color: #f1f1f1;
}
.pywebview-drag-region {
width: 100%;
height: 85px;
}

View File

@@ -1,36 +0,0 @@
<!-- Buttons on the left -->
<div style="position: absolute; bottom: 15px; left: 15px;">
{% if previous_step %}
<!-- Why two of these? So that we can use two different keyboard shortcuts for back. -->
<button style="display: none;" hx-get="/{{ previous_step }}" hx-target="body"
hx-trigger="click, keydown[altKey&&key=='b'] from:body"></button>
<a href="/{{ previous_step }}"><button hx-get="/{{ previous_step }}" hx-target="body"
hx-trigger="click, keydown[ctrlKey&&code=='PageUp'] from:body" class="button is-light">Back</button></a>
{% endif %}
{% if bottom_menu %}
{{ bottom_menu | safe }}
{% endif %}
</div> <!-- end buttons on the left -->
<!-- Buttons on the right -->
<div style="position: absolute; bottom: 15px; right: 15px;">
{% if next_step %}
{% if next_step == "install" %}
<a href="/{{ next_step }}"><button hx-get="/{{ next_step }}" hx-target="body"
hx-trigger="click, keyup[altKey&&key=='i'] from:body" class="button is-link">Install</button></a>
{% else %}
<!-- Why two of these? So that we can use two different keyboard shortcuts for next. -->
<a href="/{{ next_step }}"<button hx-get="/{{ next_step }}" hx-target="body"
hx-trigger="click, keydown[ctrlKey&&code=='PageDown'] from:body" class=""></button></a>
<a href="/{{ next_step }}"<button hx-get="/{{ next_step }}" hx-target="body"
hx-trigger="click, keydown[altKey&&key=='n'] from:body" class="button is-link">Next</button></a>
{% endif %}
{% endif %}
</div> <!-- end buttons on the right -->

View File

@@ -1,20 +0,0 @@
<!-- Header -->
<div id="titlebar" style="padding: 15px; margin: -15px; padding-bottom: 30px;">
<!-- drag region class: -->
<div id="titlebar" class="pywebview-drag-region" style="padding: 15px; margin: -15px; padding-bottom: 30px;">
<a hx-get="/main/menu" hx-swap="outerHTML"/> <button class="button is-link"></button </a>
{% for item in menu_order %}
<a href="{{ menu[item][1]}}"> <button class="button is-{% if request.path == menu[item][1] %}info{% else %}dark{% endif %}"> {{ menu[item][0]}} </button></a>
{% endfor %}
<div style="position: absolute; top: 15px; right: 15px;">
<a hx-get="/main/help" hx-swap="outerHTML"/> <button class="button is-light"> <b> ? </b> </button></a>
<a hx-get="/main/quit" hx-swap="outerHTML"/><button onclick="closeApp()" class="button is-danger" > 🗙 </button></a>
</div>
</div> <!-- end pywebview-drag-region -->
<!-- End Header -->

View File

@@ -1,27 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/main/static/bulma.css" rel="stylesheet">
<link href="/main/static/system-installer.css" rel="stylesheet">
<link rel="shortcut icon" href="/main/static/icons/blippie.png" type="image/x-icon">
<link rel="stylesheet" href="/main/static/fork-awesome.css">
<script src="/main/static/htmx.min.js" crossorigin="anonymous"></script>
</head>
<body>
<div class="frame">
{% include "header.html" %}
<div class="applet">
{% block body %}
{% endblock %}
</div>
{% include "footer.html" %}
</div> <!-- class "frame" -->
</body>
</html>

View File

@@ -19,6 +19,9 @@ def summary_index():
html = (str(current_app.config['CONFIG']['Summary']['welcome']) + "<br>" + str(current_app.config['CONFIG']['Summary']['software']))
summary = config['Summary']
print("Config is: ")
print(config)
return render_template('summary.html',
menu=current_app.config['CONFIG']['settings']['menu'],
menu_order=current_app.config['CONFIG']['settings']['menu_order'].split(),

View File

@@ -10,7 +10,7 @@ def set_language(LANG):
"""
Sets language for this applet
"""
translations = gettext.translation("users", './applets/users/locales',
translations = gettext.translation("users", '/usr/share/yasi-daemon/yasi_applets/users/locales',
fallback=True, languages=[LANG])
translations.install()
_ = translations.gettext
@@ -101,6 +101,8 @@ def build_stringlist():
string_dict['password_confirm'] = _("Password (confirm)")
string_dict['password_nomatch'] = _("These passwords do now match")
string_dict['password_tooshort'] = _("This password is too short")
string_dict['next_text'] = _("Next")
string_dict['back_text'] = _("Back")
return string_dict
@@ -109,7 +111,6 @@ def build_menu():
Define menu items and paths.
"""
current_app.config['CONFIG']['settings']['menu']['users'] = (build_stringlist()['menu_item'], "/users", 20)
print(current_app.config['CONFIG']['settings']['menu'])
def build_summary():
@@ -118,7 +119,6 @@ def build_summary():
"""
current_app.config['CONFIG']['Summary']['users'] = {}
current_app.config['CONFIG']['Summary']['users']['heading'] = "Users and Identity"
current_app.config['CONFIG']['Summary']['users']['bleh'] = current_app.config['CONFIG']['recipe']['popcon']['enable_popcon']
current_app.config['CONFIG']['Summary']['users']['settings'] = current_app.config['CONFIG']['recipe']['users']['users']
current_app.config['CONFIG']['Summary']['users']['text'] = "Add primary user with the username: " + str(current_app.config['CONFIG']['recipe']['users']['users'][0]['username'])
return("ok?")

View File

@@ -1,24 +0,0 @@
from applets.software import bp
from flask import Flask, request, session, redirect, \
url_for, render_template, flash, Blueprint
# we use this neat little trick to get config data from the main app
from flask import current_app
def build_stringlist():
"""
Return all the strings that is used in this applet.
"""
string_dict = {}
string_dict['initial_user_text'] = _("Let's set up an initial user.")
string_dict['full_name'] = _("Full Name")
string_dict['user_name'] = _("Username")
string_dict['user_name_available'] = _("This username is available")
string_dict['user_name_not_available'] = _("This username is not available")
string_dict['user_name_reserved'] = _("This username is reserved by the system")
string_dict['user_name_characters'] = _("The username must be one word, lowercase, with no special characters")
string_dict['password'] = _("Password")
string_dict['password_confirm'] = _("Password (confirm)")
string_dict['password_nomatch'] = _("These passwords do now match")
string_dict['password_tooshort'] = _("This password is too short")
return string_dict

View File

@@ -12,6 +12,33 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: UTF-8\n"
msgid "Welcome"
msgstr "Welkom"
msgid "Users"
msgstr "Gebruikers"
msgid "Disks"
msgstr "Skywe"
msgid "Software"
msgstr "Sagteware"
msgid "Summary"
msgstr "Opsomming"
msgid "Power"
msgstr "Krag"
msgid "Internet"
msgstr "Internet"
msgid "Back"
msgstr "Terug"
msgid "Next"
msgstr "Volgende"
#: routes.py:27
msgid "Welcome! This setup program will install Debian on to your system."
msgstr "Welkom! Hierdie program sal jou deur die proses lei om Debian te installeer na jou sisteem."

View File

@@ -6,13 +6,16 @@ from flask import current_app
import gettext
import dmm.lsblk as lsblk
import dmm.timezone as timezone
import subprocess
import socket
# Set up Gettext
def set_language(LANG):
"""
Sets language for this applet
"""
translations = gettext.translation("welcome", './applets/welcome/locales',
# TODO: unhardcode this path
translations = gettext.translation("welcome", '/usr/share/yasi-daemon/yasi_applets/welcome/locales',
fallback=True, languages=[LANG])
translations.install()
_ = translations.gettext
@@ -37,8 +40,10 @@ def welcome_index():
print(request.form["timezone"])
global lang
lang = current_app.config['CONFIG']['settings']['language']
blkid = lsblk.list_scsi_devices()
set_language(lang)
global string_dict
string_dict = build_stringlist()
blkid = lsblk.list_scsi_devices()
build_menu()
build_summary()
menu = current_app.config['CONFIG']['settings']['menu_order'].split(" ")
@@ -51,12 +56,11 @@ def welcome_index():
else:
previous_step_url = menu[menu.index("users")-1]
print("next is: " + next_step_url)
print("previous is: " , previous_step_url)
power_button = '<button class="button is-dark"> <i class="fa fa-check" aria-hidden="true"></i> &nbsp; Power: AC </button>'
internet_button = '&nbsp; <button class="button is-dark"> <i class="fa fa-check" aria-hidden="true"></i> &nbsp; Internet </button>'
power_button = '<button id="powerbutton" hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #ffffff;"> <i class="fa fa-question" aria-hidden="true"></i></span> &nbsp;</button>'
internet_button = '&nbsp; <button id="internetbutton" class="button is-dark" hx-get="/welcome/internet/button" hx-trigger="load, every 1s" hx-target="#internetbutton"><span style="color: #ffffff;"> <i class="fa fa-question" aria-hidden="true"></i></span> &nbsp;</button>'
bottom_menu = power_button + internet_button
return render_template('welcome.html',
string_dict=string_dict, selected_lang=lang,
menu=current_app.config['CONFIG']['settings']['menu'],
@@ -67,18 +71,87 @@ def welcome_index():
bottom_menu=bottom_menu)
@bp.route('/welcome/battery',methods=['GET', 'POST', 'PUT'])
def welcome_battery():
"""
Are we running on battery?
How much power do we have?
"""
if "yes" in str(subprocess.check_output("LANG=C upower -d | grep on-battery", shell=True)):
on_battery = True
else:
on_battery = False
if on_battery:
percentage = str(subprocess.check_output('LANG=C upower -d | grep '
'percentage | head -1 | cut -f 2 -d":" | '
'sed "s/ //g"', shell=True)).strip("'b").strip("%\\n")
else:
percentage = False
status = {}
status = {"on_battery": on_battery, "percentage": percentage}
return status
@bp.route('/welcome/battery/button',methods=['GET', 'POST', 'PUT'])
def welcome_battery_button():
"""
Returns the html power button.
"""
on_battery, percentage = welcome_battery()['on_battery'], welcome_battery()['percentage']
if on_battery:
power_button = '<button id="powerbutton hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #ffa45c;"> <i class="fa fa-warning" aria-hidden="true"></i></span> &nbsp; Power: Battery: ' + percentage + '%</button>'
else:
power_button = """<button id="powerbutton hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #a0ff98;"> <i class="fa fa-check" aria-hidden="true"></i></span> &nbsp;""" + string_dict["power_text"] + "</button>"""
return str(power_button)
@bp.route('/welcome/internet', methods=['GET'])
def has_internet():
"""
Very rudimentary internet check.
Check if we can resolve deb.debian.org.
"""
try:
socket.getaddrinfo("deb.debian.org", 80, proto=socket.IPPROTO_TCP)
canhas = True
except socket.gaierror:
canhas = False
return canhas
@bp.route('/welcome/internet/button', methods=['GET'])
def welcome_internet_button():
"""
Draw updated button depending on internet status.
"""
if has_internet():
return('&nbsp; <button id="internetbutton" class="button is-dark" hx-get="/welcome/internet/button" hx-trigger="load, every 1s" hx-target="#internetbutton"><span style="color: #a0ff98;"> <i class="fa fa-check" aria-hidden="true"></i></span> &nbsp; Internet </button>')
else:
return('&nbsp; <button id="internetbutton" class="button is-dark" hx-get="/welcome/internet/button" hx-trigger="load, every 1s" hx-target="#internetbutton"><span style="color: #ffa45c;"> <i class="fa fa-warning" aria-hidden="true"></i></span> &nbsp; Internet </button>')
def build_stringlist():
"""
Return all the strings that is used in this applet."
"""
string_dict = {}
string_dict['menu_item'] = _("Welcome")
# In the future, these will be loaded from their own modules
string_dict['menu_item_users'] = _("Users")
string_dict['menu_item_disks'] = _("Disks")
string_dict['menu_item_software'] = _("Software")
string_dict['menu_item_summary'] = _("Summary")
string_dict['welcome_text'] = _("Welcome! This setup program will install Debian on to your system.")
string_dict['confirm_text'] = _("Please confirm the following details:")
string_dict['language_text'] = _("Language:")
string_dict['keylayout_text'] = _("Keyboard Layout:")
string_dict['timezone_text'] = _("Time Zone:")
string_dict['basicsettings_text'] = _("Basic Settings:")
string_dict['power_text'] = _("Power")
string_dict['internet_text'] = _("Internet")
string_dict['next_text'] = _("Next")
string_dict['back_text'] = _("Back")
lang_dict = {}
lang_dict['af'] = _("Afrikaans")
lang_dict['en'] = _("English (International)")
@@ -88,12 +161,19 @@ def build_stringlist():
string_dict['lang_list'] = lang_dict
return string_dict
def build_menu():
"""
Define menu items and paths.
"""
current_app.config['CONFIG']['settings']['menu']['welcome'] = (build_stringlist()['menu_item'], "/welcome", 10)
#TODO: temporary hack: We need an automated way to translate all menu items
current_app.config['CONFIG']['settings']['menu']['users'] = (build_stringlist()['menu_item_users'], "/users", 20)
current_app.config['CONFIG']['settings']['menu']['disks'] = (build_stringlist()['menu_item_disks'], "/disks", 30)
current_app.config['CONFIG']['settings']['menu']['software'] = (build_stringlist()['menu_item_software'], "/software", 40)
current_app.config['CONFIG']['settings']['menu']['summary'] = (build_stringlist()['menu_item_summary'], "/summary", 50)
def build_summary():
"""

View File

@@ -5,34 +5,52 @@
src="/welcome/static/img/banner.png"
alt="Debian Image banner" />
<!-- This odd, yet very efficient mechanism ensures that translations,
settings and summaries for each page gets loaded -->
<div hx-get="/users" hx-target="#none" hx-trigger="load"></div>
<div hx-get="/disks" hx-target="#none" hx-trigger="load"></div>
<div hx-get="/software" hx-target="#none" hx-trigger="load"></div>
<div hx-get="/summary" hx-target="#none" hx-trigger="load"></div>
<!-- TODO: Add hook to fix next tab again after loading summary -->
<div style="display: none;" id="none"></div>
<p><b> {{ string_dict['welcome_text'] }} </b></p>
<p>{{ string_dict['confirm_text'] }} </p> <br />
<form action="/welcome" method="POST">
<i class="fa fa-language" aria-hidden="true"></i> {{ string_dict['language_text'] }}
<div class="control is-link" width="180px">
<div class="select">
<select hx-post="/welcome" hx-target="body" name="lang" style="width: 220px">
{% for lang in string_dict['lang_list'] %}
<option value="{{lang}}" {% if lang == selected_lang %} selected=selected {% endif %}>{{ string_dict['lang_list'][lang] }}</option>
{% endfor %}
</select>
<div style="left: 25px; display: flex; align-items: center; gap: 1rem;">
<img style="width: 80px;" src="/welcome/static/img/lang_w.svg" alt="Language icon" />
<form action="/welcome" method="POST" style="display: flex; flex-direction: column; gap: 0.5rem;">
<label for="lang">{{ string_dict['language_text'] }}</label>
<div class="control is-link">
<div class="select">
<select id="lang" name="lang" hx-post="/welcome" hx-target="body" style="width: 220px;">
{% for lang in string_dict['lang_list'] %}
<option value="{{ lang }}" {% if lang == selected_lang %} selected {% endif %}>
{{ string_dict['lang_list'][lang] }}
</option>
{% endfor %}
</select>
</div>
</div>
</div>
</form>
<br>
</div>
<form>
<i class="fa fa-keyboard-o" aria-hidden="true"></i> {{ string_dict['keylayout_text'] }}
<div class="control is-link">
<div class="select">
<select hx-post="/welcome" hx-target="body" name="keyboard" style="width: 220px">
<option>en-us</option>
<option>en-uk</option>
</select>
</div>
</div>
</form>
<br />
<div style="left: 25px; display: flex; align-items: center; gap: 1rem;">
<img style="width: 80px;" src="/welcome/static/img/keyboard.svg" alt="Language icon" />
<form action="/welcome" method="POST" style="display: flex; flex-direction: column; gap: 0.5rem;">
<label for="keyboard">{{ string_dict['keylayout_text'] }}</label>
<div class="control is-link">
<div class="select">
<select name="keyboard" hx-post="/welcome" hx-target="body" style="width: 220px;">
<option>en-us</option>
<option>en-uk</option>
</select>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Yasi Daemon
After=network.target
[Service]
ExecStart=yasi-daemon
Environment="FLASK_ENV=production"
User=yasi
Group=yasi
Restart=always
[Install]
WantedBy=multi-user.target