From 09edb1540ac779c1278496c8b7ec44c1d9dbac04 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 8 Nov 2023 17:40:01 +0100 Subject: [PATCH] appgenerator: simplify/rework the HTML/form structure using Flask-Bootstrap's macro --- tools/app_generator/app_generator.py | 151 ++++----- tools/app_generator/static/stylesheet.css | 319 +----------------- tools/app_generator/templates/index.html | 393 +++++++++------------- 3 files changed, 229 insertions(+), 634 deletions(-) diff --git a/tools/app_generator/app_generator.py b/tools/app_generator/app_generator.py index 98325aec..d2a520e2 100644 --- a/tools/app_generator/app_generator.py +++ b/tools/app_generator/app_generator.py @@ -13,6 +13,7 @@ from markupsafe import Markup # No longer imported from Flask # Form libraries from flask_wtf import FlaskForm +from flask_bootstrap import Bootstrap from wtforms import ( StringField, RadioField, @@ -42,6 +43,7 @@ from secrets import token_urlsafe #### Create FLASK and Jinja Environments app = Flask(__name__) +Bootstrap(app) app.config["SECRET_KEY"] = token_urlsafe(16) # Necessary for the form CORS cors = CORS(app) @@ -77,9 +79,10 @@ def markdown_file_to_html_string(file): ## PHP forms -class Form_PHP_Config(FlaskForm): +class Form_PHP(FlaskForm): + use_php = BooleanField("Nécessite PHP", default=False) php_config_file = SelectField( - "Type de fichier PHP :", + "Type de fichier PHP", choices=[ ( "php-fpm.conf", @@ -102,7 +105,7 @@ class Form_PHP_Config(FlaskForm): # 'title': "Choisir un fichier permettant un paramétrage d'options complémentaires. C'est généralement recommandé." php_config_file_content = TextAreaField( - "Saisissez le contenu du fichier de configuration PHP :", + "Saisissez le contenu du fichier de configuration PHP", validators=[Optional()], render_kw={ "class": "form-control", @@ -113,15 +116,11 @@ class Form_PHP_Config(FlaskForm): ) -class Form_PHP(Form_PHP_Config): - use_php = BooleanField("Nécessite PHP", default=False) - - ## NodeJS forms class Form_NodeJS(FlaskForm): use_nodejs = BooleanField("Nécessite NodeJS", default=False) use_nodejs_version = StringField( - "Version de NodeJS :", + "Version de NodeJS", render_kw={ "placeholder": "20", "class": "form-control", @@ -145,7 +144,7 @@ class Form_Python(FlaskForm): "Nécessite Python", default=False ) ## TODO -> python3, python3-pip, python3-ven dependencies by default python_dependencies_type = SelectField( - "Configuration des dépendances Python :", + "Configuration des dépendances Python", choices=[ ("requirements.txt", Markup("Fichier requirements.txt")), ("manual_list", "Liste manuelle"), @@ -154,7 +153,7 @@ class Form_Python(FlaskForm): validators=[DataRequired(), Optional()], ) python_requirements = TextAreaField( - "La liste de dépendances inclue dans le fichier requirements.txt :", + "La liste de dépendances inclue dans le fichier requirements.txt", render_kw={ "class": "form-control", "style": "width: 50%;height:5.5em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;", @@ -163,7 +162,7 @@ class Form_Python(FlaskForm): }, ) python_dependencies_list = StringField( - "Liste de dépendances python :", + "Liste de dépendances python", render_kw={ "placeholder": "tensorflow uvicorn fastapi", "class": "form-control", @@ -175,45 +174,33 @@ class GeneralInfos(FlaskForm): app_id = StringField( Markup( - """Identifiant (id) de l'application (en minuscule et sans espaces) :""" + "Identifiant (id) de l'application" ), + description="En minuscule et sans espace.", validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?.btn { - padding:.25rem .5rem; - //font-size:.7875rem; - line-height:1.5; - border-radius:.2rem; -} - - -.form-control { - #display: block; - #width: 100%; - #height: calc(1.5em + .75rem + 2px); - padding: .375rem .75rem; - padding-right: 0.5rem; - margin-left:0.75rem; - font-size: .9rem; - font-weight: 400; - line-height: 1.5; - color: var(--gray-700); - background-color: var(--white); - background-clip: padding-box; - border: 1px solid var(--gray-400); - border-radius: .25rem; - transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; -} -.form-control.is-invalid { - border-color: var(--red); - padding-right: calc(1.5em + .75rem) !important; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='var%28--red%29' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='var%28--red%29' stroke='none'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(.375em + .1875rem) center; - background-size: calc(.75em + .375rem) calc(.75em + .375rem); -} - - - -.custom-select { - - display: inline-block; - width: 100%; - height: calc(1.5em + .75rem + 2px); - padding: .375rem 1.75rem .375rem .75rem; - font-size: .9rem; - font-weight: 400; - line-height: 1.5; - color: var(--gray-700); - vertical-align: middle; - background: var(--white) url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='var%28--gray-800%29' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat; - border: 1px solid var(--gray-400); - border-radius: .25rem; - appearance: none; - -} - - -.nav { - display:flex; - flex-wrap:wrap; - padding-left:0; - margin-bottom:0; - list-style:none -} -.nav-tabs .nav-item.show .nav-link { - color:var(--gray-700); - background-color:var(--white); - border-color:var(--gray-300) var(--gray-300) var(--white) -} - -nav a { - margin-left: 50px; - // text-decoration: none; // removing links underline is a bad idea -} - -footer .nav-item:before { - /* content:"\2022"; */ - width:1rem; - display:inline-block; - margin-left:-1.15rem; - padding-top:1rem!important; - margin-top:auto!important; -} - - -.test { - color:red; -} \ No newline at end of file diff --git a/tools/app_generator/templates/index.html b/tools/app_generator/templates/index.html index bde208c4..5a231778 100644 --- a/tools/app_generator/templates/index.html +++ b/tools/app_generator/templates/index.html @@ -1,9 +1,17 @@ - - - - - +{% import "bootstrap/wtf.html" as wtf %} +{% extends "bootstrap/base.html" %} + +{% block title %} +YunoHost app generator +{% endblock %} + +{% block styles %} +{{super()}} + +{% endblock %} + +{% block content %} - YunoHost app generator - - -

Formulaire de génération d'une application Yunohost

-
+ {{ main_form.hidden_tag() }} + {{ wtf.form_errors(main_form, hiddens="only") }} + {{ wtf.form_field(main_form.generator_mode) }} - {{ main_form.generator_mode.label }} {{ main_form.generator_mode() }} - {# TODO : this doesn't work, should be changed on the fly. Is it worth the trouble ? - {% if main_form.generator_mode %} - L'application générée contiendra des commentaires additionnels pour faciliter la compréhension - {% else %} - L'application générée ne contiendra que le minimum nécessaire - {% endif %} -
- #} -
- En mode tutoriel, l'application générée contiendra des commentaires additionnels pour faciliter la compréhension. En version épurée, l'application générée ne contiendra que le minimum nécessaire. - - - -
- - - -

Informations générales

- -
- {{ main_form.app_name.label }} {{ main_form.app_name()}} -
- {{ main_form.app_id.label}} {{ main_form.app_id() }} -
- - -
- {{ main_form.description_en.label}} - {{ main_form.description_en(style="height: - 2.5em;min-height: 2em; max-height: 4em;flex-grow: 1;box-sizing: border-box;")}} +
+
+

1/6 - Informations générales

+
+
+
+ {{ wtf.form_field(main_form.app_name) }} + {{ wtf.form_field(main_form.app_id) }} + {{ wtf.form_field(main_form.description_en) }} + {{ wtf.form_field(main_form.description_fr) }} +
+
-
-
- {{ main_form.description_fr.label}} {{ main_form.description_fr(style=" - height: - 2.5em;min-height: 1em; max-height: 4em;flex-grow: 1;box-sizing: border-box;")}} + + +
+
+

2/6 - Informations sur l'upstream

+
+
+

Le terme upstream désigne le projet original qui maintient l'app

+
+ {{ wtf.form_field(main_form.license) }} + {{ wtf.form_field(main_form.website) }} + {{ wtf.form_field(main_form.demo) }} + {{ wtf.form_field(main_form.admindoc) }} + {{ wtf.form_field(main_form.userdoc) }} + {{ wtf.form_field(main_form.code) }} +
+
+ +
+
+

3/6 - Intégration dans YunoHost

+
+
+
+ {{ wtf.form_field(main_form.version) }} + {{ wtf.form_field(main_form.maintainers) }} + {{ wtf.form_field(main_form.multi_instance) }} + {{ wtf.form_field(main_form.architectures) }} + {{ wtf.form_field(main_form.ldap) }} + {{ wtf.form_field(main_form.sso) }} +
+
+
+ + +
+
+

4/6 - Questions à poser pendant l'installation

+
+
+

+ Cette partie sert à indiquer les questions qui devront être posées. +
+ NB: seules des questions standard sont proposées ici, il faudra éventuellement compléter à la main en + suivant le modèle des autres questions. +

+ +
+ {{ wtf.form_field(main_form.domain_and_path) }} + {{ wtf.form_field(main_form.init_main_permission) }} + {{ wtf.form_field(main_form.init_admin_permission) }} + {{ wtf.form_field(main_form.language) }} +
+
+
+ +
+
+

5/6 - Ressources à initialiser

+
+
+

+ Il s'agit d'éléments techniques configurés avant que le "vrai" script d'install de l'app ne soit lancé. Typiquement : créer un user système, télécharger les sources de l'app, initialiser le dossier d'install et de données, installer des dépendances avec apt, créer une base de donnée SQL, ... +

+ +

Sources du logiciel

+
+ {{ wtf.form_field(main_form.source_url) }} + {{ wtf.form_field(main_form.sha256sum) }} + {{ wtf.form_field(main_form.auto_update) }} +
+ + +
+ {{ wtf.form_field(main_form.system_user) }} + {{ wtf.form_field(main_form.install_dir) }} + {{ wtf.form_field(main_form.data_dir) }} + {{ wtf.form_field(main_form.apt_dependencies) }} + {{ wtf.form_field(main_form.ports) }} + {{ wtf.form_field(main_form.database) }} +
+
-
- - -

Informations sur l'upstream

- -
-

Le terme upstream désigne le projet original qui maintient l'app

- {{ main_form.license.label}} {{ main_form.license()}} -
- {{ main_form.website.label}} {{ main_form.website()}} -
- {{ main_form.demo.label}} {{ main_form.demo()}} -
- {{ main_form.admindoc.label}} {{ main_form.admindoc()}} -
- {{ main_form.userdoc.label}} {{ main_form.userdoc()}} -
- {{ main_form.code.label}} {{ main_form.code()}} -
- - - - -
- - -

Informations sur le paquet et l'intégration dans YunoHost

- -
- {{ main_form.version.label}} {{ main_form.version()}} -
- {{ main_form.maintainers.label}} {{ main_form.maintainers(style="width: 60%;")}} -
- {{ main_form.multi_instance.label}} {{ main_form.multi_instance()}} -
-
- {{ main_form.architectures.label}} {{ main_form.architectures()}} -
-
- {{ main_form.ldap.label}} {{ main_form.ldap()}} -
- {{ main_form.sso.label}} {{ main_form.sso()}} -
-
- - -
- - - -

Questions à poser pendant l'installation

- -
-

- Cette partie sert à indiquer les questions qui devront être posées. -
- NB: seules des questions standard sont proposées ici, il faudra éventuellement compléter à la main en - suivant le modèle des autres questions. -

- - {{ main_form.domain_and_path.label}} {{ main_form.domain_and_path()}} -
- - {{ main_form.init_main_permission.label}} {{ main_form.init_main_permission()}} -
- - {{ main_form.init_admin_permission.label}} {{ main_form.init_admin_permission()}} -
- - {{ main_form.language.label}} {{ main_form.language()}} -
- - -
- - -

Ressources à initialiser

- -
-

- Il s'agit d'éléments techniques configurés avant que le "vrai" script d'install de l'app ne soit lancé. Typiquement : créer un user système, télécharger les sources de l'app, initialiser le dossier d'install et de données, installer des dépendances avec apt, créer une base de donnée SQL, ... -

- - Sources du logiciel -
- {{ main_form.source_url.label}} {{ main_form.source_url()}} -
- {{ main_form.sha256sum.label}} {{ main_form.sha256sum()}} -
- {{ main_form.auto_update.label}} {{ main_form.auto_update()}} -
-
- - - {{ main_form.system_user.label}} {{ main_form.system_user()}} -
- - {{ main_form.install_dir.label}} {{ main_form.install_dir()}} -
- - {{ main_form.data_dir.label}} {{ main_form.data_dir()}} -
- - {{ main_form.apt_dependencies.label}} {{ main_form.apt_dependencies(style="width:40%;")}} -
- - {{ main_form.ports.label}} {{ main_form.ports()}} -
- - {{ main_form.database.label }} {{ main_form.database() }} -
- -
- - -

Technologies spécifiques (to be reworked)

+

6/6 - Options et technologies spécifiques (to be reworked)

{{ main_form.supports_change_url.label}} {{ main_form.supports_change_url()}}
@@ -476,30 +418,6 @@ - -

Utilisation de PHP

{{ main_form.use_php.label}} {{ main_form.use_php(onchange="showForm(this, 'PHP')") }}
@@ -870,6 +788,5 @@ End of the result part --> {% endif %} - - - +
+{% endblock %}