From 32b1ebb83660ffd37be0f2029c1f66b3aa3345a9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Oct 2023 18:03:37 +0200 Subject: [PATCH] Apply black --- tools/yunopackage/YunohostAppGenerator.py | 964 +++++++++++++++------- 1 file changed, 643 insertions(+), 321 deletions(-) diff --git a/tools/yunopackage/YunohostAppGenerator.py b/tools/yunopackage/YunohostAppGenerator.py index cdd8536f..09dd8be4 100644 --- a/tools/yunopackage/YunohostAppGenerator.py +++ b/tools/yunopackage/YunohostAppGenerator.py @@ -1,13 +1,39 @@ #### Imports import jinja2 as j2 -from flask import Flask, render_template, render_template_string, request, redirect, flash, send_file +from flask import ( + Flask, + render_template, + render_template_string, + request, + redirect, + flash, + send_file, +) from markupsafe import Markup # No longer imported from Flask + # Form libraries from flask_wtf import FlaskForm -from wtforms import StringField, RadioField, SelectField, SubmitField, TextAreaField, BooleanField, SelectMultipleField -from wtforms.validators import DataRequired, InputRequired, Optional, Regexp, URL, Length +from wtforms import ( + StringField, + RadioField, + SelectField, + SubmitField, + TextAreaField, + BooleanField, + SelectMultipleField, +) +from wtforms.validators import ( + DataRequired, + InputRequired, + Optional, + Regexp, + URL, + Length, +) + # Markdown to HTML - for debugging purposes from misaka import Markdown, HtmlRenderer + # Managing zipfiles import zipfile from flask_cors import CORS @@ -15,16 +41,16 @@ from urllib import parse from secrets import token_urlsafe #### GLOBAL VARIABLES -YOLOGEN_VERSION = '0.7.5' -GENERATOR_DICT = {'GENERATOR_VERSION': YOLOGEN_VERSION} +YOLOGEN_VERSION = "0.7.5" +GENERATOR_DICT = {"GENERATOR_VERSION": YOLOGEN_VERSION} #### Create FLASK and Jinja Environments -url_prefix = '' +url_prefix = "" # url_prefix = '/yunohost-app-generator' # app = Flask(__name__) app = Flask(__name__) # Blueprint('main', __name__, url_prefix=url_prefix) -app.config['SECRET_KEY'] = token_urlsafe(16) # Necessary for the form CORS +app.config["SECRET_KEY"] = token_urlsafe(16) # Necessary for the form CORS cors = CORS(app) environment = j2.Environment(loader=j2.FileSystemLoader("templates/")) @@ -32,8 +58,9 @@ environment = j2.Environment(loader=j2.FileSystemLoader("templates/")) #### Custom functions + # Define custom filter -@app.template_filter('render_markdown') +@app.template_filter("render_markdown") def render_markdown(text): renderer = HtmlRenderer() markdown = Markdown(renderer) @@ -41,12 +68,12 @@ def render_markdown(text): # Add custom filter -j2.filters.FILTERS['render_markdown'] = render_markdown +j2.filters.FILTERS["render_markdown"] = render_markdown # Converting markdown to html def markdown_file_to_html_string(file): - with open(file, 'r') as file: + with open(file, "r") as file: markdown_content = file.read() # Convert content from Markdown to HTML html_content = render_markdown(markdown_content) @@ -56,65 +83,113 @@ def markdown_file_to_html_string(file): ### Forms + ## PHP forms class Form_PHP_Config(FlaskForm): - php_config_file = SelectField('Type de fichier PHP :', choices=[ - ('php-fpm.conf', Markup('Fichier de configuration PHP complet (php-fpm.conf)')), - ('php_extra-fpm.conf', - Markup('Fichier de configuration PHP particulier (extra_php-fpm.conf)'))], - default='php_extra-fpm.conf', validators=[DataRequired()]) + php_config_file = SelectField( + "Type de fichier PHP :", + choices=[ + ( + "php-fpm.conf", + Markup( + 'Fichier de configuration PHP complet (php-fpm.conf)' + ), + ), + ( + "php_extra-fpm.conf", + Markup( + 'Fichier de configuration PHP particulier (extra_php-fpm.conf)' + ), + ), + ], + default="php_extra-fpm.conf", + validators=[DataRequired()], + ) ## TODO : figure out how to include these comments/title values # 'title': 'Remplace la configuration générée par défaut par un fichier de configuration complet. À éviter si possible. # '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 :", - validators=[Optional()], - render_kw={"class": "form-control", - "style": "width: 50%;height:11em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;", - "title": "TODO", - "placeholder": "; Additional php.ini defines, specific to this pool of workers. \n\nphp_admin_value[upload_max_filesize] = 100M \nphp_admin_value[post_max_size] = 100M"}) + php_config_file_content = TextAreaField( + "Saisissez le contenu du fichier de configuration PHP :", + validators=[Optional()], + render_kw={ + "class": "form-control", + "style": "width: 50%;height:11em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;", + "title": "TODO", + "placeholder": "; Additional php.ini defines, specific to this pool of workers. \n\nphp_admin_value[upload_max_filesize] = 100M \nphp_admin_value[post_max_size] = 100M", + }, + ) class Form_PHP(Form_PHP_Config): - use_php = BooleanField('Nécessite PHP', default=False) + 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 :", - render_kw={"placeholder": "20", "class": "form-control", - "title": "Saisissez la version de NodeJS à installer. Cela peut-être une version majeure (ex: 20) ou plus précise (ex: 20.1)."}) # TODO : this should be validated using a regex, should be only numbers and any (≥0) number of dots in between - use_nodejs_needs_yarn = BooleanField('Nécessite Yarn', default=False, render_kw={ - "title": "Faut-il installer automatiquement Yarn ? Cela configurera les dépôts spécifiques à Yarn."}) + use_nodejs = BooleanField("Nécessite NodeJS", default=False) + use_nodejs_version = StringField( + "Version de NodeJS :", + render_kw={ + "placeholder": "20", + "class": "form-control", + "title": "Saisissez la version de NodeJS à installer. Cela peut-être une version majeure (ex: 20) ou plus précise (ex: 20.1).", + }, + ) # TODO : this should be validated using a regex, should be only numbers and any (≥0) number of dots in between + use_nodejs_needs_yarn = BooleanField( + "Nécessite Yarn", + default=False, + render_kw={ + "title": "Faut-il installer automatiquement Yarn ? Cela configurera les dépôts spécifiques à Yarn." + }, + ) ## Python forms + class Form_Python(FlaskForm): - use_python = BooleanField('Nécessite Python', - default=False) ## TODO -> python3, python3-pip, python3-ven dependencies by default - python_dependencies_type = SelectField('Configuration des dépendances Python :', choices=[ - ('requirements.txt', Markup('Fichier requirements.txt')), - ('manual_list', 'Liste manuelle')], - default='requirements.txt', validators=[DataRequired(), Optional()]) - python_requirements = TextAreaField("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;", - "title": "Lister les dépendances à installer, une par ligne, avec un numéro de version derrière.\nEx: 'dépendance==1.0'.", - "placeholder": "tensorflow==2.3.1 \nuvicorn==0.12.2 \nfastapi==0.63.0"}) - python_dependencies_list = StringField("Liste de dépendances python :", - render_kw={"placeholder": "tensorflow uvicorn fastapi", - "class": "form-control", - "title": "Lister les dépendances à installer, séparées d'un espace."}) + use_python = BooleanField( + "Nécessite Python", default=False + ) ## TODO -> python3, python3-pip, python3-ven dependencies by default + python_dependencies_type = SelectField( + "Configuration des dépendances Python :", + choices=[ + ("requirements.txt", Markup("Fichier requirements.txt")), + ("manual_list", "Liste manuelle"), + ], + default="requirements.txt", + validators=[DataRequired(), Optional()], + ) + python_requirements = TextAreaField( + "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;", + "title": "Lister les dépendances à installer, une par ligne, avec un numéro de version derrière.\nEx: 'dépendance==1.0'.", + "placeholder": "tensorflow==2.3.1 \nuvicorn==0.12.2 \nfastapi==0.63.0", + }, + ) + python_dependencies_list = StringField( + "Liste de dépendances python :", + render_kw={ + "placeholder": "tensorflow uvicorn fastapi", + "class": "form-control", + "title": "Lister les dépendances à installer, séparées d'un espace.", + }, + ) ## Manifest form # Dependencies form class DependenciesForm(FlaskForm): - auto_update = BooleanField("Activer le robot de mise à jour automatiques  :", default=False, - render_kw={ - "title": "Si le logiciel est disponible sur github et publie des releases ou des tags pour ses nouvelles versions, un robot proposera automatiquement des mises à jours."}) + auto_update = BooleanField( + "Activer le robot de mise à jour automatiques  :", + default=False, + render_kw={ + "title": "Si le logiciel est disponible sur github et publie des releases ou des tags pour ses nouvelles versions, un robot proposera automatiquement des mises à jours." + }, + ) ## TODO # These infos are used by https://github.com/YunoHost/apps/blob/master/tools/autoupdate_app_sources/autoupdate_app_sources.py @@ -124,230 +199,429 @@ class DependenciesForm(FlaskForm): # autoupdate.strategy = "latest_github_tag" - dependencies = StringField("Dépendances de l'application (liste des paquets apt) à installer :", - render_kw={"placeholder": "foo foo2.1-ext somerandomdep", "class": "form-control", - "title": "Lister les paquets dont dépend l'application, séparés par un espace."}) + dependencies = StringField( + "Dépendances de l'application (liste des paquets apt) à installer :", + render_kw={ + "placeholder": "foo foo2.1-ext somerandomdep", + "class": "form-control", + "title": "Lister les paquets dont dépend l'application, séparés par un espace.", + }, + ) - use_db = SelectField("Configurer une base de données :", choices=[ - ('false', "Non"), - ('mysql', "MySQL/MariaDB"), - ('postgresql', "PostgreSQL")], - default='false', - render_kw={"title": "L'application nécessite-t-elle une base de données ?"}) + use_db = SelectField( + "Configurer une base de données :", + choices=[ + ("false", "Non"), + ("mysql", "MySQL/MariaDB"), + ("postgresql", "PostgreSQL"), + ], + default="false", + render_kw={"title": "L'application nécessite-t-elle une base de données ?"}, + ) # manifest class manifestForm(DependenciesForm): - version = StringField('Version', validators=[Regexp('\d{1,4}.\d{1,4}(.\d{1,4})?(.\d{1,4})?~ynh\d+')], - render_kw={"class": "form-control", - "placeholder": "1.0~ynh1"}) - description_en = TextAreaField("Description en quelques lignes de l'application, en anglais :", - validators=[DataRequired()], - render_kw={"class": "form-control", "style": "resize: none;", - "title": "Explain in *a few (10~15) words* the purpose of the app \\" - "or what it actually does (it is meant to give a rough idea to users browsing a catalog of 100+ apps)"}) - description_fr = TextAreaField("Description en quelques lignes de l'application :", validators=[DataRequired()], - render_kw={"class": "form-control", "style": "resize: none;", - "title": "Expliquez en *quelques* (10~15) mots l'utilité de l'app \\" - "ou ce qu'elle fait (l'objectif est de donner une idée grossière pour des utilisateurs qui naviguent dans un catalogue de 100+ apps)"}) + version = StringField( + "Version", + validators=[Regexp("\d{1,4}.\d{1,4}(.\d{1,4})?(.\d{1,4})?~ynh\d+")], + render_kw={"class": "form-control", "placeholder": "1.0~ynh1"}, + ) + description_en = TextAreaField( + "Description en quelques lignes de l'application, en anglais :", + validators=[DataRequired()], + render_kw={ + "class": "form-control", + "style": "resize: none;", + "title": "Explain in *a few (10~15) words* the purpose of the app \\" + "or what it actually does (it is meant to give a rough idea to users browsing a catalog of 100+ apps)", + }, + ) + description_fr = TextAreaField( + "Description en quelques lignes de l'application :", + validators=[DataRequired()], + render_kw={ + "class": "form-control", + "style": "resize: none;", + "title": "Expliquez en *quelques* (10~15) mots l'utilité de l'app \\" + "ou ce qu'elle fait (l'objectif est de donner une idée grossière pour des utilisateurs qui naviguent dans un catalogue de 100+ apps)", + }, + ) # TODO : handle multiple names separated by commas (.split(',') ? - maintainers = StringField('Mainteneurs et mainteneuses', render_kw={"class": "form-control", - "placeholder": "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)"}) # TODO : Usually you put your name here… if you like ;) - architectures = SelectMultipleField('Architectures supportées :', choices=[ - ('all', 'Toutes les architectures'), - ('amd64', 'amd64'), - ('arm64', 'arm64'), - ('i386', 'i386'), - ('todo', 'TODO : list more architectures')], - default=['all'], validators=[DataRequired()]) - yunohost_required_version = StringField('Mainteneurs et mainteneuses', render_kw={"class": "form-control", - "placeholder": "11.1.21", - "title": "Version minimale de Yunohost pour que l'application fonctionne."}) + maintainers = StringField( + "Mainteneurs et mainteneuses", + render_kw={ + "class": "form-control", + "placeholder": "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)", + }, + ) # TODO : Usually you put your name here… if you like ;) + architectures = SelectMultipleField( + "Architectures supportées :", + choices=[ + ("all", "Toutes les architectures"), + ("amd64", "amd64"), + ("arm64", "arm64"), + ("i386", "i386"), + ("todo", "TODO : list more architectures"), + ], + default=["all"], + validators=[DataRequired()], + ) + yunohost_required_version = StringField( + "Mainteneurs et mainteneuses", + render_kw={ + "class": "form-control", + "placeholder": "11.1.21", + "title": "Version minimale de Yunohost pour que l'application fonctionne.", + }, + ) - multi_instance = BooleanField("Application multi-instance", default=False, - render_kw={"class": "", - "title": "Peux-t-on installer simultannément plusieurs fois l'application sur un même serveur ?"}) + multi_instance = BooleanField( + "Application multi-instance", + default=False, + render_kw={ + "class": "", + "title": "Peux-t-on installer simultannément plusieurs fois l'application sur un même serveur ?", + }, + ) - ldap = SelectField('Integrate with LDAP (user can login using Yunohost credentials :', choices=[ - ('false', 'False'), - ('true', 'True'), - ('not_relevant', 'Not relevant')], default='not_relevant', validators=[DataRequired()], render_kw={ - "title": """Not to confuse with the "sso" key: the "ldap" key corresponds to wether or not a user *can* login on the app using its YunoHost credentials."""}) - sso = SelectField('Integrate with Yunohost SingleSignOn (SSO) :', choices=[ - ('false', 'False'), - ('true', 'True'), - ('not_relevant', 'Not relevant')], default='not_relevant', validators=[DataRequired()], render_kw={ - "title": """Not to confuse with the "ldap" key: the "sso" key corresponds to wether or not a user is *automatically logged-in* on the app when logged-in on the YunoHost portal."""}) + ldap = SelectField( + "Integrate with LDAP (user can login using Yunohost credentials :", + choices=[ + ("false", "False"), + ("true", "True"), + ("not_relevant", "Not relevant"), + ], + default="not_relevant", + validators=[DataRequired()], + render_kw={ + "title": """Not to confuse with the "sso" key: the "ldap" key corresponds to wether or not a user *can* login on the app using its YunoHost credentials.""" + }, + ) + sso = SelectField( + "Integrate with Yunohost SingleSignOn (SSO) :", + choices=[ + ("false", "False"), + ("true", "True"), + ("not_relevant", "Not relevant"), + ], + default="not_relevant", + validators=[DataRequired()], + render_kw={ + "title": """Not to confuse with the "ldap" key: the "sso" key corresponds to wether or not a user is *automatically logged-in* on the app when logged-in on the YunoHost portal.""" + }, + ) - license = StringField('Licence', validators=[DataRequired()], render_kw={"class": "form-control", - "placeholder": "GPL"}) + license = StringField( + "Licence", + validators=[DataRequired()], + render_kw={"class": "form-control", "placeholder": "GPL"}, + ) - website = StringField('Site web', validators=[URL(), Optional()], render_kw={"class": "form-control", - "placeholder": "https://awesome-app-website.com"}) - demo = StringField('Site de démonstration', validators=[URL(), Optional()], render_kw={"class": "form-control", - "placeholder": "https://awesome-app-website.com/demo"}) - admindoc = StringField("Documentation d'aministration", validators=[URL(), Optional()], - render_kw={"class": "form-control", - "placeholder": "https://awesome-app-website.com/doc/admin"}) - userdoc = StringField("Documentation d'utilisation", validators=[URL(), Optional()], - render_kw={"class": "form-control", - "placeholder": "https://awesome-app-website.com/doc/user"}) - code = StringField('Dépôt de code', validators=[URL(), Optional()], render_kw={"class": "form-control", - "placeholder": "https://awesome-app-website.com/get-the-code"}) + website = StringField( + "Site web", + validators=[URL(), Optional()], + render_kw={ + "class": "form-control", + "placeholder": "https://awesome-app-website.com", + }, + ) + demo = StringField( + "Site de démonstration", + validators=[URL(), Optional()], + render_kw={ + "class": "form-control", + "placeholder": "https://awesome-app-website.com/demo", + }, + ) + admindoc = StringField( + "Documentation d'aministration", + validators=[URL(), Optional()], + render_kw={ + "class": "form-control", + "placeholder": "https://awesome-app-website.com/doc/admin", + }, + ) + userdoc = StringField( + "Documentation d'utilisation", + validators=[URL(), Optional()], + render_kw={ + "class": "form-control", + "placeholder": "https://awesome-app-website.com/doc/user", + }, + ) + code = StringField( + "Dépôt de code", + validators=[URL(), Optional()], + render_kw={ + "class": "form-control", + "placeholder": "https://awesome-app-website.com/get-the-code", + }, + ) - data_dir = BooleanField("L'application nécessite un répertoire dédié pour ses données", default=False, - render_kw={"title": "Faut-il créer un répertoire /home/yunohost.app/votreApplication ?"}) - data_subdirs = StringField('Si nécessaire, lister les sous-répertoires à configurer :', - validators=[Optional()], render_kw={"class": "form-control", - "placeholder": "data, uploads, themes"}) - use_whole_domain = BooleanField("L'application nécessite d'utiliser tout un domaine (installation à la racine) :", - default=False, - render_kw={ - "title": "Doit-on installer l'application à la racine du domaine ? Sinon, on pourra l'installer dans un sous-dossier, par exemple /mon_app."}) + data_dir = BooleanField( + "L'application nécessite un répertoire dédié pour ses données", + default=False, + render_kw={ + "title": "Faut-il créer un répertoire /home/yunohost.app/votreApplication ?" + }, + ) + data_subdirs = StringField( + "Si nécessaire, lister les sous-répertoires à configurer :", + validators=[Optional()], + render_kw={"class": "form-control", "placeholder": "data, uploads, themes"}, + ) + use_whole_domain = BooleanField( + "L'application nécessite d'utiliser tout un domaine (installation à la racine) :", + default=False, + render_kw={ + "title": "Doit-on installer l'application à la racine du domaine ? Sinon, on pourra l'installer dans un sous-dossier, par exemple /mon_app." + }, + ) supports_change_url = BooleanField( - "L'application autorise le changement d'adresse (changement de domaine ou de chemin)", default=True, - render_kw={"title": "Faut-il permettre le changement d'URL pour l'application ? (fichier change_url)"}) + "L'application autorise le changement d'adresse (changement de domaine ou de chemin)", + default=True, + render_kw={ + "title": "Faut-il permettre le changement d'URL pour l'application ? (fichier change_url)" + }, + ) - needs_admin = BooleanField("L'application nécessite de configurer un compte d'administration :", default=False, - render_kw={"class": "", - "title": "Faut-il configurer un compte admin à l'installation ?"}) + needs_admin = BooleanField( + "L'application nécessite de configurer un compte d'administration :", + default=False, + render_kw={ + "class": "", + "title": "Faut-il configurer un compte admin à l'installation ?", + }, + ) # admin_password_help_message = BooleanField("TODO  :", default=False, # render_kw={"class": "", # "title": "TODO"}) - language = SelectMultipleField('Langues supportées :', choices=[ - ('en', 'English'), - ('fr', 'Français'), - ('en', 'Spanish'), - ('it', 'Italian'), - ('de', 'German'), - ('zh', 'Chinese'), - ('jp', 'Japanese'), - ('da', 'Danish'), - ('pt', 'Portugese'), - ('nl', 'Dutch'), - ('ru', 'Russian')], - default=['en'], validators=[DataRequired()]) + language = SelectMultipleField( + "Langues supportées :", + choices=[ + ("en", "English"), + ("fr", "Français"), + ("en", "Spanish"), + ("it", "Italian"), + ("de", "German"), + ("zh", "Chinese"), + ("jp", "Japanese"), + ("da", "Danish"), + ("pt", "Portugese"), + ("nl", "Dutch"), + ("ru", "Russian"), + ], + default=["en"], + validators=[DataRequired()], + ) - default_language = SelectField('Langues par défaut :', choices=[ - ('en', 'English'), - ('fr', 'Français'), - ('en', 'Spanish'), - ('it', 'Italian'), - ('zh', 'Chinese'), - ('jp', 'Japanese'), - ('da', 'Danish'), - ('pt', 'Portugese'), - ('nl', 'Dutch'), - ('ru', 'Russian')], - default=['en']) + default_language = SelectField( + "Langues par défaut :", + choices=[ + ("en", "English"), + ("fr", "Français"), + ("en", "Spanish"), + ("it", "Italian"), + ("zh", "Chinese"), + ("jp", "Japanese"), + ("da", "Danish"), + ("pt", "Portugese"), + ("nl", "Dutch"), + ("ru", "Russian"), + ], + default=["en"], + ) - visibility = RadioField("Visibilité de l'application :", choices=[ - ('admin', "Administrateur/administratrice uniquement"), - ('all_users', "Personnes connectées"), - ('visitors', "Publique")], - default='all_users', validators=[DataRequired()]) + visibility = RadioField( + "Visibilité de l'application :", + choices=[ + ("admin", "Administrateur/administratrice uniquement"), + ("all_users", "Personnes connectées"), + ("visitors", "Publique"), + ], + default="all_users", + validators=[DataRequired()], + ) - source_url = StringField("Code source ou exécutable de l'application", validators=[DataRequired(), URL()], - render_kw={"class": "form-control", - "placeholder": "https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz"}) # Application source code URL - sha256sum = StringField('Empreinte du code source (format sha256sum)', - validators=[DataRequired(), Length(min=64, max=64)], - render_kw={"class": "form-control", - "placeholder": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", - "title": "Sha256sum of the archive. Should be 64 characters-long."}) # Source code hash (sha256sum format) + source_url = StringField( + "Code source ou exécutable de l'application", + validators=[DataRequired(), URL()], + render_kw={ + "class": "form-control", + "placeholder": "https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz", + }, + ) # Application source code URL + sha256sum = StringField( + "Empreinte du code source (format sha256sum)", + validators=[DataRequired(), Length(min=64, max=64)], + render_kw={ + "class": "form-control", + "placeholder": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "title": "Sha256sum of the archive. Should be 64 characters-long.", + }, + ) # Source code hash (sha256sum format) ## Main form -class appGeneratorForm(manifestForm, DependenciesForm, Form_PHP, Form_NodeJS, Form_Python): - app_name = StringField("Nom de l'application :", validators=[DataRequired()], - render_kw={"placeholder": "My Great App", "class": "form-control", - "title": "Définir le nom de l'application, affiché dans l'interface"}) + +class appGeneratorForm( + manifestForm, DependenciesForm, Form_PHP, Form_NodeJS, Form_Python +): + app_name = StringField( + "Nom de l'application :", + validators=[DataRequired()], + render_kw={ + "placeholder": "My Great App", + "class": "form-control", + "title": "Définir le nom de l'application, affiché dans l'interface", + }, + ) app_id = StringField( - Markup( - """Identifiant (id) de l'application (en minuscule et sans espaces) :"""), - validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?(en minuscule et sans espaces) :""" + ), + validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?