1
0
Fork 0
mirror of https://github.com/YunoHost/apps.git synced 2024-09-03 20:06:07 +02:00

Merge branch 'master' into patch-8

This commit is contained in:
OniriCorpe 2024-05-07 21:28:50 +02:00 committed by GitHub
commit aafacb17da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
157 changed files with 8182 additions and 2535 deletions

29
.github/diff_pot_files.py vendored Normal file
View file

@ -0,0 +1,29 @@
#! /usr/bin/env python3
"""
Super small script for github action to detect if 2 .pot files have changed for
github/workflows/auto_messages_pot.yml
"""
import sys
from babel.messages.pofile import PoFileParser
def load_pot_file(file_path):
poparser = PoFileParser({})
poparser.parse(open(file_path))
return poparser.catalog
def main():
file_1 = load_pot_file(sys.argv[1])
file_2 = load_pot_file(sys.argv[2])
if [x for x in file_1.keys() if x] == [x for x in file_2.keys() if x]:
print("false")
else:
print("true")
if __name__ == "__main__":
main()

View file

@ -12,6 +12,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.YUNOHOST_BOT_TOKEN }}
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@ -30,3 +31,6 @@ jobs:
with:
commit_message: "Automatically add dates to the catalog files"
file_pattern: 'apps.toml wishlist.toml graveyard.toml'
commit_user_name: yunohost-bot
commit_user_email: yunohost-bot@users.noreply.github.com
commit_author: 'yunohost-bot <yunohost-bot@users.noreply.github.com>'

View file

@ -18,17 +18,18 @@ jobs:
run: |
cd tools/readme_generator/
pip install -r requirements.txt
- name: Save old messges.pot for diffing later
run: |
cd tools/readme_generator/
cp messages.pot messages.pot.old
- name: Try to generate messages.pot
run: |
cd tools/readme_generator/
pip install -r requirements.txt
pybabel extract --ignore-dirs venv -F babel.cfg -o messages.pot .
- shell: pwsh
id: check_files_changed
run: |
# Diff HEAD with the previous commit
$diff = git diff
$HasDiff = $diff.Length -gt 0
$HasDiff = python .github/diff_pot_files.py tools/readme_generator/messages.pot.old tools/readme_generator/messages.pot
Write-Host "::set-output name=files_changed::$HasDiff"
- name: Create Pull Request
if: steps.check_files_changed.outputs.files_changed == 'true'

View file

@ -31,16 +31,17 @@ them such as their category or maintenance state. This file is regularly read by
To add your application to the catalog:
* Fork [this repository](https://github.com/YunoHost/apps)
* Edit the [`apps.toml`](/apps.toml) file
* Add your app's ID and git information at the right alphabetical place
* Indicate the app's functioning state: `notworking`, `inprogress`, or `working`
* Indicate the app category, which you can pick from `categories.toml`
* Indicate any anti-feature that your app may be subject to, see `antifeatures.toml` (or remove the `antifeatures` key if there's none)
* Indicate if your app can be thought of as an alternative to popular proprietary services (or remove the `potential_alternative_to` key if there's none)
* *Do not* add the `level` entry by yourself. Our automatic test suite ("the CI") will handle it.
* Commit and push your modifications to your repository
* Create a [Pull Request](https://github.com/YunoHost/apps/pulls/)
- Fork [this repository](https://github.com/YunoHost/apps)
- Edit the [`apps.toml`](/apps.toml) file
- Add your app's ID and git information at the right alphabetical place
- Indicate the app's functioning state: `notworking`, `inprogress`, or `working`
- Indicate the app category, which you can pick from `categories.toml`
- Indicate any anti-feature that your app may be subject to, see `antifeatures.toml` (or remove the `antifeatures` key if there's none)
- Indicate if your app can be thought of as an alternative to popular proprietary services (or remove the `potential_alternative_to` key if there's none)
- *Do not* add the `level` entry by yourself. Our automatic test suite ("the CI") will handle it.
- Add the app's logo inside the `logos` folder. Please keep this logo as small as possible. It also must be square (or almost square). The filename must be the name of the app in lower case.
- Commit and push your modifications to your repository
- Create a [Pull Request](https://github.com/YunoHost/apps/pulls/)
App example addition:

View file

@ -8,7 +8,7 @@ description.it = "Ti traccia e/o riporta la tua attività a chi mantiene il codi
icon = "user-secret"
title.en = "Tracking"
title.eu = "Jarraipena"
title.fr = "Pistage"
title.fr = "Pistage "
title.it = "Tracciamento"
[non-free-network]
@ -19,7 +19,7 @@ description.it = "Promuove o dipende interamente da servizi di rete non liberi."
icon = "sitemap"
title.en = "Non-free Network Services"
title.eu = "Libreak ez diren sareko zerbitzuak"
title.fr = "Services réseau non libres"
title.fr = "Services réseau non libres "
title.it = "Servizi di rete non liberi"
[non-free-addons]
@ -30,7 +30,7 @@ description.it = "Promoove altre applicazioni o plugin non liberi"
icon = "puzzle-piece"
title.en = "Non-free Addons"
title.eu = "Libreak ez diren gehigarriak"
title.fr = "Extensions non libres"
title.fr = "Extensions non libres "
title.it = "Estensioni non libere"
[non-free-dependencies]
@ -41,7 +41,7 @@ description.it = "Per funzionare, si basa su dipendenze software non libere."
icon = "book"
title.en = "Non-free dependencies"
title.eu = "Libreak ez diren dependentziak"
title.fr = "Dépendances non libres"
title.fr = "Dépendances non libres "
title.it = "Dipendenze non libere"
[non-free-assets]
@ -52,7 +52,7 @@ description.it = "Contiene ed utilizza risorse mediatiche non libere. Il caso pi
icon = "file-image-o"
title.en = "Non-free assets"
title.eu = "Libreak ez diren baliabideak"
title.fr = "Ressources non libres"
title.fr = "Ressources non libres "
title.it = "Risorse non libere"
[bad-security-reputation]
@ -63,7 +63,7 @@ description.it = "Ha una cattiva reputazione in termini di sicurezza (per esempi
icon = "bug"
title.en = "Bad security reputation"
title.eu = "Segurtasun txarreko ospea"
title.fr = "Mauvaise réputation en matière de sécurité"
title.fr = "Mauvaise réputation en matière de sécurité "
title.it = "Cattiva reputazione di sicurezza"
[deprecated-software]
@ -74,7 +74,7 @@ description.it = "Questo software non è più mantenuto. Ci si può aspettare ch
icon = "trash-o"
title.en = "Upstream not maintained"
title.eu = "Jatorrizko garapena utzita"
title.fr = "Application non maintenue"
title.fr = "Application non maintenue "
title.it = "Applicazione non mantenuta"
[package-not-maintained]
@ -85,7 +85,7 @@ description.it = "Questo pacchetto di YunoHost non è più mantenuto e necessita
icon = "user-times"
title.en = "Package not maintained"
title.eu = "Mantendu gabeko paketea"
title.fr = "Paquet non maintenu"
title.fr = "Paquet non maintenu "
title.it = "Pacchetto non mantenuto"
[paid-content]
@ -96,7 +96,7 @@ description.it = "Promuove o dipende, interamente o parzialmente, da un servizio
icon = "money"
title.en = "Paid content"
title.eu = "Ordainpeko edukia"
title.fr = "Contenu payant"
title.fr = "Contenu payant "
title.it = "Contenuti a pagamento"
[arbitrary-limitations]
@ -107,7 +107,7 @@ description.it = "Contiene limitazioni arbitrarie. Fare riferimento al file “R
icon = "star-half-empty"
title.en = "Arbitrary limitations"
title.eu = "Muga arbitrarioak"
title.fr = "Limitations arbitraires"
title.fr = "Limitations arbitraires "
title.it = "Limitazioni arbitrarie"
[replaced-by-another-app]
@ -118,7 +118,7 @@ description.it = "Questapp è stata sostituita da unaltra app. Fare riferi
icon = "repeat"
title.en = "Replaced by another app"
title.eu = "Beste aplikazio batek ordeztu du"
title.fr = "Remplacé par une autre application"
title.fr = "Remplacé par une autre application "
title.it = "Sostituita da unaltra app"
[alpha-software]
@ -129,25 +129,25 @@ description.it = "Questo software è allinizio della sua fase di sviluppo. Po
icon = "flask"
title.en = "Alpha software"
title.eu = "Alfa softwarea"
title.fr = "Logiciel en version alpha"
title.fr = "Logiciel en version alpha "
title.it = "Software in versione alpha"
[not-totally-free-upstream]
description.en = "The packaged app is under an overall free licence, but with clauses that restrict its use."
description.en = "The packaged app is under an overall free license, but with clauses that may restrict its use."
description.eu = "Aplikazioak lizentzia librea du orokorrean, baina bere erabilera mugatzen duten klausulekin."
description.fr = "L'application packagée est sous une licence globalement libre, mais avec des clauses qui pourraient restreindre son utilisation."
description.it = "Questapplicazione è protetta da licenza generalmente libera, ma con delle clausole che potrebbero limitare il suo utilizzo."
icon = "lock"
title.en = "Not totally free upstream"
title.eu = "Jatorrizkoa ez da erabat librea"
title.fr = "Application sous licence libre restreinte"
title.fr = "Application sous licence libre restreinte "
title.it = "Applicazione con licenza parzialmente libera"
[not-totally-free-package]
description.en = "The YunoHost package of this app is under an overall free licence, but with clauses that restrict its use."
description.en = "The YunoHost package of this app is under an overall free license, but with clauses that may restrict its use."
description.eu = "Aplikazio honen YunoHost paketeak lizentzia librea du orokorrean, baina bere erabilera mugatzen duten klausulekin."
description.fr = "Le package YunoHost de cette application est sous une licence globalement libre, mais avec des clauses qui pourraient restreindre son utilisation."
icon = "archive"
title.en = "Not totally free package"
title.eu = "Paketea ez da erabat librea"
title.fr = "Package sous licence libre restreinte"
title.fr = "Package sous licence libre restreinte "

194
apps.toml
View file

@ -49,7 +49,7 @@ url = "https://github.com/YunoHost-Apps/acropolis_ynh"
[actual]
added_date = 1685962455 # 2023/06/05
category = "productivity_and_management"
level = 7
level = 8
potential_alternative_to = [ "Bankin", "Budgea", "Linxo", "Microsoft Money", "Mint", "You Need A Budget" ]
state = "working"
subtags = [ "accounting" ]
@ -173,7 +173,7 @@ url = "https://github.com/YunoHost-Apps/audiobookshelf_ynh"
[autobrr]
added_date = 1681591997 # 2023/04/15
category = "multimedia"
level = 7
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/autobrr_ynh"
@ -358,7 +358,7 @@ url = "https://github.com/YunoHost-Apps/castopod_ynh"
[cesium]
added_date = 1674232499 # 2023/01/20
category = "wat"
level = 6
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/cesium_ynh"
@ -366,7 +366,7 @@ url = "https://github.com/YunoHost-Apps/cesium_ynh"
added_date = 1684063245 # 2023/05/14
antifeatures = [ "non-free-network" ]
category = "small_utilities"
level = 7
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/chatgpt-web_ynh"
@ -448,7 +448,7 @@ url = "https://github.com/YunoHost-Apps/cjdns_ynh"
[cloudlog]
added_date = 1685981922 # 2023/06/05
category = "small_utilities"
level = 7
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/cloudlog_ynh"
@ -488,7 +488,7 @@ url = "https://github.com/YunoHost-Apps/coin_ynh"
[collabora]
added_date = 1674232499 # 2023/01/20
category = "office"
level = 6
level = 8
potential_alternative_to = [ "Apple Pages", "G Suite", "Google Docs", "Microsoft Excel", "Microsoft Office", "Microsoft Word" ]
state = "working"
subtags = [ "impress", "spreadsheet", "text" ]
@ -582,7 +582,7 @@ url = "https://github.com/YunoHost-Apps/cowyo_ynh"
[crabfit]
added_date = 1710114839 # 2024/03/10
category = "productivity_and_management"
level = 6
level = 7
potential_alternative_to = [ "Doodle", "OpenSondage" ]
state = "working"
subtags = [ "poll" ]
@ -754,7 +754,7 @@ added_date = 1674232499 # 2023/01/20
antifeatures = [ "deprecated-software" ]
category = "communication"
deprecated_date = 1709075769 # 2024/02/27
level = 8
level = 7
state = "working"
subtags = [ "chat" ]
url = "https://github.com/YunoHost-Apps/dispatch_ynh"
@ -785,7 +785,7 @@ url = "https://github.com/YunoHost-Apps/django-for-runners_ynh"
[django-fritzconnection]
added_date = 1674232499 # 2023/01/20
category = "system_tools"
level = 6
level = 8
state = "working"
subtags = [ "network" ]
url = "https://github.com/YunoHost-Apps/django-fritzconnection_ynh"
@ -793,7 +793,7 @@ url = "https://github.com/YunoHost-Apps/django-fritzconnection_ynh"
[django_example]
added_date = 1674232499 # 2023/01/20
category = "dev"
level = 8
level = 3
state = "working"
subtags = [ "programming" ]
url = "https://github.com/YunoHost-Apps/django_example_ynh"
@ -926,7 +926,7 @@ url = "https://github.com/YunoHost-Apps/element_ynh"
[element-call]
added_date = 1697570605 # 2023/10/17
category = "communication"
level = 6
level = 7
potential_alternative_to = [ "Skype", "Zoom" ]
state = "working"
subtags = [ "meeting" ]
@ -935,7 +935,7 @@ url = "https://github.com/YunoHost-Apps/element-call_ynh"
[eleventy]
added_date = 1683960849 # 2023/05/13
category = "publishing"
level = 7
level = 8
potential_alternative_to = [ "Blogger", "Blogspot", "Wix" ]
state = "working"
subtags = [ "blog", "website" ]
@ -952,7 +952,7 @@ url = "https://github.com/YunoHost-Apps/emailpoubelle_ynh"
[emoncms]
added_date = 1674232499 # 2023/01/20
category = "iot"
level = 6
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/emoncms_ynh"
@ -1065,7 +1065,7 @@ url = "https://github.com/YunoHost-Apps/fastapi_ynh"
[ffsync]
added_date = 1674232499 # 2023/01/20
antifeatures = [ "deprecated-software" ]
antifeatures = [ "deprecated-software", "replaced-by-another-app" ]
category = "synchronization"
deprecated_date = 1660842060 # 2022/08/18
level = 6
@ -1099,7 +1099,7 @@ url = "https://github.com/YunoHost-Apps/filepizza_ynh"
[firefish]
added_date = 1691055044 # 2023/08/03
category = "social_media"
level = 7
level = 0
potential_alternative_to = [ "Calckey", "Mastodon", "Misskey", "Pleroma", "Threads", "X" ]
state = "working"
url = "https://github.com/YunoHost-Apps/firefish_ynh"
@ -1138,6 +1138,16 @@ state = "working"
subtags = [ "forum" ]
url = "https://github.com/YunoHost-Apps/flarum_ynh"
[flohmarkt]
added_date = 1714137502 # 2024/04/26
antifeatures = [ "alpha-software", "arbitrary-limitations" ]
category = "publishing"
level = 6
potential_alternative_to = [ "ClassifiedAds.com", "kleinanzeigen.de" ]
state = "working"
subtags = [ "ecommerce" ]
url = "https://github.com/YunoHost-Apps/flohmarkt_ynh"
[flood]
added_date = 1674232499 # 2023/01/20
category = "multimedia"
@ -1184,7 +1194,7 @@ url = "https://github.com/YunoHost-Apps/forgejo_ynh"
[framaforms]
added_date = 1674232499 # 2023/01/20
category = "productivity_and_management"
level = 0
level = 8
potential_alternative_to = [ "Google Forms" ]
state = "working"
subtags = [ "poll" ]
@ -1246,7 +1256,7 @@ url = "https://github.com/YunoHost-Apps/galene_ynh"
[galette]
added_date = 1674232499 # 2023/01/20
category = "productivity_and_management"
level = 8
level = 6
state = "working"
subtags = [ "business_and_ngos" ]
url = "https://github.com/YunoHost-Apps/galette_ynh"
@ -1289,7 +1299,7 @@ url = "https://github.com/YunoHost-Apps/garradin_ynh"
[gemserv]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 6
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/gemserv_ynh"
@ -1339,7 +1349,7 @@ url = "https://github.com/YunoHost-Apps/gitlab-runner_ynh"
[gitlist]
added_date = 1675002183 # 2023/01/29
category = "dev"
level = 7
level = 8
state = "working"
subtags = [ "forge" ]
url = "https://github.com/YunoHost-Apps/gitlist_ynh"
@ -1405,7 +1415,7 @@ url = "https://github.com/YunoHost-Apps/gotosocial_ynh"
[grafana]
added_date = 1674232499 # 2023/01/20
category = "system_tools"
level = 8
level = 6
state = "working"
subtags = [ "monitoring" ]
url = "https://github.com/YunoHost-Apps/grafana_ynh"
@ -1460,7 +1470,7 @@ url = "https://github.com/YunoHost-Apps/guacamole_ynh"
[h5ai]
added_date = 1674232499 # 2023/01/20
category = "small_utilities"
level = 8
level = 0
state = "working"
url = "https://github.com/YunoHost-Apps/h5ai_ynh"
@ -1500,7 +1510,7 @@ url = "https://github.com/YunoHost-Apps/headphones_ynh"
[headscale]
added_date = 1686503631 # 2023/06/11
category = "system_tools"
level = 7
level = 8
potential_alternative_to = [ "Tailscale" ]
state = "working"
subtags = [ "network" ]
@ -1546,7 +1556,7 @@ url = "https://github.com/YunoHost-Apps/homeassistant_ynh"
[horde]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 4
level = 7
state = "working"
subtags = [ "email" ]
url = "https://github.com/YunoHost-Apps/horde_ynh"
@ -1569,7 +1579,7 @@ url = "https://github.com/Yunohost-Apps/httpsh_ynh"
[hubzilla]
added_date = 1674232499 # 2023/01/20
category = "social_media"
level = 6
level = 8
potential_alternative_to = [ "Facebook" ]
state = "working"
subtags = [ "microblogging" ]
@ -1638,6 +1648,13 @@ state = "working"
subtags = [ "accounting" ]
url = "https://github.com/YunoHost-Apps/ihatemoney_ynh"
[immich]
added_date = 1711921326 # 2024/03/31
category = "multimedia"
level = 7
state = "working"
url = "https://github.com/YunoHost-Apps/immich_ynh"
[incus]
added_date = 1710508401 # 2024/03/15
category = "system_tools"
@ -1666,7 +1683,7 @@ url = "https://github.com/YunoHost-Apps/influxdb_v2_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "non-free-network" ]
category = "social_media"
level = 8
level = 6
potential_alternative_to = [ "YouTube" ]
state = "working"
subtags = [ "videos" ]
@ -1713,8 +1730,9 @@ url = "https://github.com/YunoHost-Apps/jackett_ynh"
[jappix]
added_date = 1674232499 # 2023/01/20
antifeatures = [ "package-not-maintained" ]
antifeatures = [ "deprecated-software", "package-not-maintained" ]
category = "communication"
deprecated_date = 1714590228 # 2024/05/01
level = 7
state = "working"
subtags = [ "chat" ]
@ -1748,7 +1766,7 @@ url = "https://github.com/YunoHost-Apps/jellyfin-vue_ynh"
[jellyseerr]
added_date = 1683832079 # 2023/05/11
category = "multimedia"
level = 7
level = 8
potential_alternative_to = [ "Overseerr" ]
state = "working"
url = "https://github.com/YunoHost-Apps/jellyseerr_ynh"
@ -1763,7 +1781,7 @@ url = "https://github.com/YunoHost-Apps/jenkins_ynh"
[jirafeau]
added_date = 1674232499 # 2023/01/20
category = "synchronization"
level = 6
level = 8
potential_alternative_to = [ "ImageShack", "Imgur" ]
state = "working"
subtags = [ "files" ]
@ -1772,7 +1790,7 @@ url = "https://github.com/YunoHost-Apps/jirafeau_ynh"
[jitsi]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 6
level = 8
potential_alternative_to = [ "Google Hangouts", "Skype" ]
state = "working"
subtags = [ "meeting" ]
@ -1790,11 +1808,19 @@ url = "https://github.com/YunoHost-Apps/joomla_ynh"
[joplin]
added_date = 1702203874 # 2023/12/10
category = "office"
level = 0
state = "notworking"
level = 7
state = "working"
subtags = [ "text" ]
url = "https://github.com/YunoHost-Apps/joplin_ynh"
[jump]
added_date = 1714286036 # 2024/04/28
category = "productivity_and_management"
level = 0
potential_alternative_to = [ "Dashy", "Heimdall" ]
state = "notworking"
url = "https://github.com/YunoHost-Apps/jump_ynh"
[jupyterlab]
added_date = 1674232499 # 2023/01/20
category = "dev"
@ -1917,7 +1943,7 @@ url = "https://github.com/YunoHost-Apps/laverna_ynh"
[leantime]
added_date = 1683586765 # 2023/05/08
category = "productivity_and_management"
level = 7
level = 8
potential_alternative_to = [ "Asana", "ClickUp", "Notion" ]
state = "working"
subtags = [ "task" ]
@ -2035,14 +2061,14 @@ url = "https://github.com/YunoHost-Apps/limesurvey_ynh"
added_date = 1685604095 # 2023/06/01
antifeatures = [ "non-free-network" ]
category = "small_utilities"
level = 7
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/lingva_ynh"
[linkstack]
added_date = 1683443449 # 2023/05/07
category = "publishing"
level = 7
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/linkstack_ynh"
@ -2106,7 +2132,7 @@ url = "https://github.com/YunoHost-Apps/luckysheet_ynh"
[lufi]
added_date = 1674232499 # 2023/01/20
category = "synchronization"
level = 8
level = 6
potential_alternative_to = [ "WeTransfer" ]
state = "working"
subtags = [ "files" ]
@ -2138,7 +2164,7 @@ url = "https://github.com/YunoHost-Apps/lxd-dashboard_ynh"
[lychee]
added_date = 1674232499 # 2023/01/20
category = "multimedia"
level = 8
level = 6
potential_alternative_to = [ "Flickr", "Google Photos" ]
state = "working"
subtags = [ "pictures" ]
@ -2173,7 +2199,7 @@ url = "https://github.com/YunoHost-Apps/mastodon_ynh"
[matomo]
added_date = 1674232499 # 2023/01/20
category = "publishing"
level = 8
level = 6
potential_alternative_to = [ "Google Analytics", "Xiti" ]
state = "working"
subtags = [ "analytics" ]
@ -2218,7 +2244,7 @@ url = "https://github.com/YunoHost-Apps/mattermost_ynh"
[mautic]
added_date = 1674232499 # 2023/01/20
category = "productivity_and_management"
level = 6
level = 8
potential_alternative_to = [ "Marketo", "NetResult", "SalesFusion" ]
state = "working"
subtags = [ "business_and_ngos" ]
@ -2238,7 +2264,7 @@ added_date = 1674232499 # 2023/01/20
antifeatures = [ "deprecated-software" ]
category = "communication"
deprecated_date = 1709661665 # 2024/03/05
level = 6
level = 0
potential_alternative_to = [ "Facebook Messenger" ]
state = "working"
subtags = [ "chat" ]
@ -2247,7 +2273,7 @@ url = "https://github.com/YunoHost-Apps/mautrix_facebook_ynh"
[mautrix_signal]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 6
level = 0
potential_alternative_to = [ "Signal" ]
state = "working"
subtags = [ "chat" ]
@ -2256,7 +2282,7 @@ url = "https://github.com/YunoHost-Apps/mautrix_signal_ynh"
[mautrix_telegram]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 8
level = 0
potential_alternative_to = [ "Telegram" ]
state = "working"
subtags = [ "chat" ]
@ -2265,7 +2291,7 @@ url = "https://github.com/YunoHost-Apps/mautrix_telegram_ynh"
[mautrix_whatsapp]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 8
level = 0
potential_alternative_to = [ "Whatsapp" ]
state = "working"
subtags = [ "chat" ]
@ -2274,7 +2300,7 @@ url = "https://github.com/YunoHost-Apps/mautrix_whatsapp_ynh"
[mediawiki]
added_date = 1674232499 # 2023/01/20
category = "publishing"
level = 7
level = 8
state = "working"
subtags = [ "wiki" ]
url = "https://github.com/YunoHost-Apps/mediawiki_ynh"
@ -2295,6 +2321,14 @@ state = "working"
subtags = [ "monitoring" ]
url = "https://github.com/YunoHost-Apps/metabase_ynh"
[microbin]
added_date = 1712237414 # 2024/04/04
category = "small_utilities"
level = 7
state = "working"
subtags = [ "pastebin" ]
url = "https://github.com/YunoHost-Apps/microbin_ynh"
[minchat]
added_date = 1674232499 # 2023/01/20
category = "communication"
@ -2386,14 +2420,14 @@ url = "https://github.com/YunoHost-Apps/mongo-express_ynh"
[monica]
added_date = 1674232499 # 2023/01/20
category = "wat"
level = 6
level = 0
state = "working"
url = "https://github.com/YunoHost-Apps/monica_ynh"
[monitorix]
added_date = 1674232499 # 2023/01/20
category = "system_tools"
level = 6
level = 8
state = "working"
subtags = [ "monitoring" ]
url = "https://github.com/YunoHost-Apps/monitorix_ynh"
@ -2440,7 +2474,7 @@ url = "https://github.com/YunoHost-Apps/movim_ynh"
[mstream]
added_date = 1683054560 # 2023/05/02
category = "multimedia"
level = 7
level = 8
potential_alternative_to = [ "Anchor", "Deezer", "SoundCloud", "Spotify" ]
state = "working"
subtags = [ "music" ]
@ -2473,7 +2507,7 @@ url = "https://github.com/YunoHost-Apps/my-mind_ynh"
[my_capsule]
added_date = 1674232499 # 2023/01/20
category = "publishing"
level = 6
level = 7
state = "working"
url = "https://github.com/YunoHost-Apps/my_capsule_ynh"
@ -2504,7 +2538,7 @@ url = "https://github.com/YunoHost-Apps/mygpo_ynh"
[mytinytodo]
added_date = 1674232499 # 2023/01/20
category = "productivity_and_management"
level = 6
level = 8
state = "working"
subtags = [ "task" ]
url = "https://github.com/YunoHost-Apps/mytinytodo_ynh"
@ -2703,7 +2737,7 @@ url = "https://github.com/YunoHost-Apps/opensondage_ynh"
[opentracker]
added_date = 1674232499 # 2023/01/20
category = "wat"
level = 8
level = 0
state = "working"
url = "https://github.com/YunoHost-Apps/opentracker_ynh"
@ -2719,8 +2753,10 @@ url = "https://github.com/YunoHost-Apps/osada_ynh"
[osjs]
added_date = 1674232499 # 2023/01/20
antifeatures = [ "deprecated-software" ]
category = "wat"
level = 8
deprecated_date = 1712777448 # 2024/04/10
level = 7
state = "working"
url = "https://github.com/YunoHost-Apps/osjs_ynh"
@ -3016,7 +3052,7 @@ url = "https://github.com/YunoHost-Apps/piped_ynh"
[piwigo]
added_date = 1674232499 # 2023/01/20
category = "multimedia"
level = 6
level = 8
potential_alternative_to = [ "Google Photos", "Keepeek", "Koken", "Orkis Ajaris", "Orphéa" ]
state = "working"
subtags = [ "pictures" ]
@ -3143,7 +3179,7 @@ url = "https://github.com/YunoHost-Apps/prosody_ynh"
[prowlarr]
added_date = 1674232499 # 2023/01/20
category = "multimedia"
level = 6
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/prowlarr_ynh"
@ -3348,7 +3384,7 @@ url = "https://github.com/YunoHost-Apps/rocketchat_ynh"
[roundcube]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 6
level = 8
potential_alternative_to = [ "GMail", "Hotmail", "Microsoft Outlook", "Yahoo! Mail" ]
state = "working"
subtags = [ "email" ]
@ -3410,7 +3446,7 @@ url = "https://github.com/YunoHost-Apps/satdress_ynh"
[scovie]
added_date = 1685183203 # 2023/05/27
category = "publishing"
level = 7
level = 8
state = "working"
subtags = [ "website" ]
url = "https://github.com/YunoHost-Apps/scovie_ynh"
@ -3541,7 +3577,7 @@ url = "https://github.com/YunoHost-Apps/signaturepdf_ynh"
added_date = 1710170758 # 2024/03/11
category = "office"
level = 6
potential_alternative_to = [ "Joplin", "Logreq", "Microsoft OneNote", "Obisian" ]
potential_alternative_to = [ "Joplin", "Logseq", "Microsoft OneNote", "Obsidian" ]
state = "working"
subtags = [ "text" ]
url = "https://github.com/YunoHost-Apps/silverbullet_ynh"
@ -3549,7 +3585,7 @@ url = "https://github.com/YunoHost-Apps/silverbullet_ynh"
[simple-file-manager]
added_date = 1699776105 # 2023/11/12
category = "small_utilities"
level = 6
level = 7
state = "working"
url = "https://github.com/YunoHost-Apps/simple-file-manager_ynh"
@ -3582,7 +3618,7 @@ url = "https://github.com/YunoHost-Apps/simplex_ynh"
added_date = 1685875056 # 2023/06/04
antifeatures = [ "non-free-network" ]
category = "small_utilities"
level = 7
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/simplytranslate_ynh"
@ -3632,7 +3668,7 @@ url = "https://github.com/YunoHost-Apps/snserver_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "package-not-maintained" ]
category = "office"
level = 6
level = 7
state = "working"
subtags = [ "text" ]
url = "https://github.com/YunoHost-Apps/snweb_ynh"
@ -3655,7 +3691,7 @@ url = "https://github.com/YunoHost-Apps/sogo_ynh"
[sonarr]
added_date = 1674232499 # 2023/01/20
category = "multimedia"
level = 6
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/sonarr_ynh"
@ -3761,7 +3797,7 @@ url = "https://github.com/YunoHost-Apps/svgedit_ynh"
[synapse]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 6
level = 8
potential_alternative_to = [ "Discord", "Facebook Messenger", "Signal", "Skype", "Telegram", "Whatsapp" ]
state = "working"
subtags = [ "chat" ]
@ -3770,7 +3806,7 @@ url = "https://github.com/YunoHost-Apps/synapse_ynh"
[synapse-admin]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 8
level = 1
state = "working"
subtags = [ "chat" ]
url = "https://github.com/YunoHost-Apps/synapse-admin_ynh"
@ -3925,7 +3961,7 @@ url = "https://github.com/YunoHost-Apps/traccar_ynh"
[tracim]
added_date = 1674232499 # 2023/01/20
category = "office"
level = 7
level = 8
potential_alternative_to = [ "Dropbox", "Google Drive", "Slack", "Trello" ]
state = "working"
url = "https://github.com/YunoHost-Apps/tracim_ynh"
@ -4046,7 +4082,7 @@ url = "https://github.com/YunoHost-Apps/unattended_upgrades_ynh"
[uptime-kuma]
added_date = 1674232499 # 2023/01/20
category = "system_tools"
level = 6
level = 8
state = "working"
subtags = [ "monitoring" ]
url = "https://github.com/YunoHost-Apps/uptime-kuma_ynh"
@ -4054,7 +4090,7 @@ url = "https://github.com/YunoHost-Apps/uptime-kuma_ynh"
[vaultwarden]
added_date = 1674232499 # 2023/01/20
category = "synchronization"
level = 6
level = 8
potential_alternative_to = [ "1Password", "Dashlane", "Enpass", "LastPass" ]
state = "working"
subtags = [ "password" ]
@ -4112,7 +4148,7 @@ url = "https://github.com/YunoHost-Apps/watchdog_ynh"
[weblate]
added_date = 1674232499 # 2023/01/20
category = "dev"
level = 7
level = 6
potential_alternative_to = [ "Locize", "Transifex" ]
state = "working"
url = "https://github.com/YunoHost-Apps/weblate_ynh"
@ -4127,7 +4163,7 @@ url = "https://github.com/YunoHost-Apps/webmin_ynh"
[webtrees]
added_date = 1674232499 # 2023/01/20
category = "wat"
level = 6
level = 1
state = "working"
url = "https://github.com/YunoHost-Apps/webtrees_ynh"
@ -4207,12 +4243,20 @@ url = "https://github.com/YunoHost-Apps/woodpecker_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "non-free-addons", "paid-content" ]
category = "publishing"
level = 6
level = 8
potential_alternative_to = [ "Blogger", "Blogspot", "Wix" ]
state = "working"
subtags = [ "blog", "website" ]
url = "https://github.com/YunoHost-Apps/wordpress_ynh"
[workout-tracker]
added_date = 1712861274 # 2024/04/11
category = "small_utilities"
level = 7
potential_alternative_to = [ "Strava" ]
state = "working"
url = "https://github.com/YunoHost-Apps/workout-tracker_ynh"
[writefreely]
added_date = 1674232499 # 2023/01/20
category = "social_media"
@ -4258,7 +4302,7 @@ url = "https://github.com/YunoHost-Apps/yacy_ynh"
[yellow]
added_date = 1674232499 # 2023/01/20
category = "publishing"
level = 6
level = 8
state = "working"
subtags = [ "website" ]
url = "https://github.com/YunoHost-Apps/yellow_ynh"
@ -4282,8 +4326,10 @@ url = "https://github.com/YunoHost-Apps/yourls_ynh"
[yunomonitor]
added_date = 1674232499 # 2023/01/20
antifeatures = [ "deprecated-software" ]
category = "system_tools"
level = 8
deprecated_date = 1672947186 # 2023/01/05
level = 7
state = "working"
subtags = [ "monitoring" ]
url = "https://github.com/YunoHost-Apps/yunomonitor_ynh"
@ -4306,7 +4352,7 @@ url = "https://github.com/YunoHost-Apps/z-push_ynh"
[zabbix]
added_date = 1674232499 # 2023/01/20
category = "system_tools"
level = 6
level = 8
state = "working"
subtags = [ "monitoring" ]
url = "https://github.com/YunoHost-Apps/zabbix_ynh"
@ -4325,7 +4371,7 @@ url = "https://github.com/YunoHost-Apps/zap_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "replaced-by-another-app" ]
category = "small_utilities"
level = 6
level = 7
potential_alternative_to = [ "Pastebin" ]
state = "working"
subtags = [ "pastebin" ]
@ -4333,8 +4379,10 @@ url = "https://github.com/YunoHost-Apps/zerobin_ynh"
[zeronet]
added_date = 1674232499 # 2023/01/20
antifeatures = [ "deprecated-software" ]
category = "wat"
level = 6
deprecated_date = 1714840875 # 2024/05/04
level = 8
potential_alternative_to = [ "Mullvad" ]
state = "working"
url = "https://github.com/YunoHost-Apps/zeronet_ynh"
@ -4352,7 +4400,7 @@ url = "https://github.com/YunoHost-Apps/zerotier_ynh"
added_date = 1693093567 # 2023/08/26
antifeatures = [ "non-free-dependencies" ]
category = "system_tools"
level = 7
level = 0
state = "working"
subtags = [ "network" ]
url = "https://github.com/YunoHost-Apps/zeroui_ynh"

BIN
logos/flohmarkt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 29 KiB

BIN
logos/incus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
logos/microbin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -575,6 +575,14 @@
},
"force_version": {
"type": "string"
},
"allow_prereleases": {
"type": "boolean",
"description": "Allow prereleases when using strategy = latest_X_release"
},
"needs_manual_tweaks": {
"type": "boolean",
"description": "Inform the maintainer of manual steps to make autoupdate PRs work"
}
}
}

View file

@ -84,6 +84,14 @@ def localize(d):
return d["en"]
@app.context_processor
def utils():
return {
"user": session.get("user", {}),
"locale": get_locale(),
}
###############################################################################
@ -96,8 +104,6 @@ def favicon():
def index():
return render_template(
"index.html",
locale=get_locale(),
user=session.get("user", {}),
catalog=get_catalog(),
)
@ -106,12 +112,10 @@ def index():
def browse_catalog():
return render_template(
"catalog.html",
locale=get_locale(),
init_sort=request.args.get("sort"),
init_search=request.args.get("search"),
init_category=request.args.get("category"),
init_starsonly=request.args.get("starsonly"),
user=session.get("user", {}),
catalog=get_catalog(),
timestamp_now=int(time.time()),
stars=get_stars(),
@ -134,8 +138,6 @@ def app_info(app_id):
return render_template(
"app.html",
locale=get_locale(),
user=session.get("user", {}),
app_id=app_id,
infos=infos,
catalog=get_catalog(),
@ -184,11 +186,6 @@ def star_app(app_id, action):
def browse_wishlist():
return render_template(
"wishlist.html",
init_sort=request.args.get("sort"),
init_search=request.args.get("search"),
init_starsonly=request.args.get("starsonly"),
locale=get_locale(),
user=session.get("user", {}),
wishlist=get_wishlist(),
stars=get_stars(),
)
@ -208,8 +205,6 @@ def add_to_wishlist():
)
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=None,
successmsg=None,
errormsg=errormsg,
@ -220,8 +215,6 @@ def add_to_wishlist():
errormsg = _("Invalid CSRF token, please refresh the page and try again")
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=csrf_token,
successmsg=None,
errormsg=errormsg,
@ -256,7 +249,7 @@ def add_to_wishlist():
checks = [
(
check_wishlist_submit_ratelimit(session["user"]["username"]) is True
and session["user"]["bypass_ratelimit"] is False,
or session["user"]["bypass_ratelimit"] is True,
_(
"Proposing wishlist additions is limited to once every 15 days per user. Please try again in a few days."
),
@ -315,8 +308,6 @@ def add_to_wishlist():
if not check:
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=csrf_token,
successmsg=None,
errormsg=errormsg,
@ -337,8 +328,6 @@ def add_to_wishlist():
url = f"https://apps.yunohost.org/wishlist?search={slug}"
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=csrf_token,
successmsg=None,
errormsg=_(
@ -354,8 +343,6 @@ def add_to_wishlist():
url = f"https://apps.yunohost.org/app/{slug}"
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=csrf_token,
successmsg=None,
errormsg=_(
@ -389,8 +376,6 @@ def add_to_wishlist():
)
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=csrf_token,
successmsg=None,
errormsg=errormsg,
@ -444,8 +429,6 @@ Description: {description}
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
successmsg=successmsg,
)
else:
@ -454,8 +437,6 @@ Description: {description}
session["csrf_token"] = csrf_token
return render_template(
"wishlist_add.html",
locale=get_locale(),
user=session.get("user", {}),
csrf_token=csrf_token,
successmsg=None,
errormsg=None,

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -202,7 +202,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -121,7 +121,7 @@
{% endif %}
{% if infos["antifeatures"] %}
<h2 class="inline-block text-xl mb-2 font-semibold">{{ _("Anti-features") }}</h2>
<h2 class="inline-block text-xl mb-2 font-semibold">{{ _("Antifeatures") }}</h2>
<p class="inline-block text-sm">{{ _("(This app has features you may not like)") }}</p>
<div class="my-3 rounded-md bg-red-100 text-red-800 px-5 py-2">
<ul>

View file

@ -89,7 +89,7 @@
type="text"
id="search"
placeholder="{{ _('Search for…') }}"
{% if init_search %}value="{{ init_search }}"{% endif %}
{% if request.args.get("search") %}value="{{ request.args.get("search") }}"{% endif %}
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
>
@ -106,7 +106,7 @@
>
<option value="">{{ _("All apps") }}</option>
{% for id, category in catalog['categories'].items() %}
<option {% if id == init_category %}selected{% endif %} value="{{ id }}">{{ category['title']|localize }}</option>
<option {% if id == request.args.get("category") %}selected{% endif %} value="{{ id }}">{{ category['title']|localize }}</option>
{% endfor %}
</select>
</div>
@ -120,15 +120,15 @@
id="selectsort"
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<option {% if not init_sort or init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if init_sort == "newest" %}selected{% endif %} value="newest">{{ _("Newest") }}</option>
<option {% if init_sort == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</option>
<option {% if request.args.get("sort") in [None, "popularity"] %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if request.args.get("sort") == "newest" %}selected{% endif %} value="newest">{{ _("Newest") }}</option>
<option {% if request.args.get("sort") == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</option>
</select>
</div>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} {% if not user%}disabled{% endif %} >
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %} >
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span>

View file

@ -42,14 +42,14 @@
id="selectsort"
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<option {% if not init_sort or init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if init_sort == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</option>
<option {% if request.args.get("sort") in [None, "popularity"] %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if request.args.get("sort") == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</option>
</select>
</div>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} {% if not user%}disabled{% endif %}>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %}>
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span>

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:04+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ar <LL@li.org>\n"
@ -204,7 +204,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: bn_BD <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: br <LL@li.org>\n"
@ -207,7 +207,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-03-23 19:04+0000\n"
"Last-Translator: OniriCorpe <oniricorpe@disroot.org>\n"
"Language-Team: Catalan <https://translate.yunohost.org/projects/yunohost/"
@ -130,8 +130,8 @@ msgid ""
"An app with the name %(slug)s already exists in the catalog, <a "
"href='%(url)s'>you can see its page here</a>."
msgstr ""
"Una aplicació amb el nom %(slug)s ja existeix al catàleg, <a href='%(url)"
"s'>podeu veure la seva pàgina aquí</a>."
"Una aplicació amb el nom %(slug)s ja existeix al catàleg, <a "
"href='%(url)s'>podeu veure la seva pàgina aquí</a>."
#: app.py:386
#, python-format
@ -239,7 +239,9 @@ msgid "Important infos before installing"
msgstr "Informació important abans d'instal·lar"
#: templates/app.html:124
msgid "Anti-features"
#, fuzzy
#| msgid "Anti-features"
msgid "Antifeatures"
msgstr "Anticaracterístiques"
#: templates/app.html:125
@ -319,8 +321,8 @@ msgid ""
msgstr ""
"Fet amb <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> fent "
"servir <a class='text-blue-800' href='https://flask.palletsprojects."
"com'>Flask</a> i <a class='text-blue-800' href='https://tailwindcss.com/"
"'>TailwindCSS</a>"
"com'>Flask</a> i <a class='text-blue-800' href='https://tailwindcss."
"com/'>TailwindCSS</a>"
#: templates/base.html:198
msgid "Source"
@ -431,8 +433,8 @@ msgid ""
"to integrate it, and is merely a source of inspiration for packaging "
"volunteers."
msgstr ""
"La llista de desitjos és el lloc on les persones poden suggerir i votar col·"
"lectivament les aplicacions que els agradaria veure empaquetades i "
"La llista de desitjos és el lloc on les persones poden suggerir i votar "
"col·lectivament les aplicacions que els agradaria veure empaquetades i "
"disponibles al catàleg oficial d'aplicacions de YunoHost. No obstant això, "
"el fet que les aplicacions s'enumeren aquí no s'ha d'interpretar de cap "
"manera com un fet que el projecte YunoHost té previst integrar-lo, i és "

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ckb <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: cs <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: da <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-03-23 19:04+0000\n"
"Last-Translator: OniriCorpe <oniricorpe@disroot.org>\n"
"Language-Team: German <https://translate.yunohost.org/projects/yunohost/apps/"
@ -248,7 +248,9 @@ msgid "Important infos before installing"
msgstr "Wichtige Informationen vor der Installation"
#: templates/app.html:124
msgid "Anti-features"
#, fuzzy
#| msgid "Anti-features"
msgid "Antifeatures"
msgstr "Anti-Funktionen"
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: el <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: eo <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-03-23 00:54+0000\n"
"Last-Translator: OniriCorpe <oniricorpe@disroot.org>\n"
"Language-Team: Spanish <https://translate.yunohost.org/projects/yunohost/"
@ -205,7 +205,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-03-23 13:39+0000\n"
"Last-Translator: xabirequejo <xabi.rn@gmail.com>\n"
"Language-Team: Basque <https://translate.yunohost.org/projects/yunohost/apps/"
@ -133,8 +133,8 @@ msgid ""
"An app with the name %(slug)s already exists in the catalog, <a "
"href='%(url)s'>you can see its page here</a>."
msgstr ""
"Lehendik ere dago %(slug)s izena duen aplikazioa katalogoan, <a href='%(url)"
"s'>hemen ikus dezakezu</a>."
"Lehendik ere dago %(slug)s izena duen aplikazioa katalogoan, <a "
"href='%(url)s'>hemen ikus dezakezu</a>."
#: app.py:386
#, python-format
@ -236,7 +236,9 @@ msgid "Important infos before installing"
msgstr "Informazio garrantzitsua instalatu baino lehen"
#: templates/app.html:124
msgid "Anti-features"
#, fuzzy
#| msgid "Anti-features"
msgid "Antifeatures"
msgstr "Ezaugarri zalantzagarriak"
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: fa <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: fi <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-03-23 19:04+0000\n"
"Last-Translator: OniriCorpe <oniricorpe@disroot.org>\n"
"Language-Team: French <https://translate.yunohost.org/projects/yunohost/apps/"
@ -130,8 +130,8 @@ msgid ""
"An app with the name %(slug)s already exists in the catalog, <a "
"href='%(url)s'>you can see its page here</a>."
msgstr ""
"Une app nommée %(slug)s existe déjà dans le catalogue, <a href='%(url)"
"s'>vous pouvez voir sa page ici</a>."
"Une app nommée %(slug)s existe déjà dans le catalogue, <a "
"href='%(url)s'>vous pouvez voir sa page ici</a>."
#: app.py:386
#, python-format
@ -238,7 +238,9 @@ msgid "Important infos before installing"
msgstr "Informations importantes avant l'installation"
#: templates/app.html:124
msgid "Anti-features"
#, fuzzy
#| msgid "Anti-features"
msgid "Antifeatures"
msgstr "Anti-fonctionnalités"
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-03-23 19:04+0000\n"
"Last-Translator: OniriCorpe <oniricorpe@disroot.org>\n"
"Language-Team: Galician <https://translate.yunohost.org/projects/yunohost/"
@ -235,7 +235,9 @@ msgid "Important infos before installing"
msgstr "Información importante antes de instalar"
#: templates/app.html:124
msgid "Anti-features"
#, fuzzy
#| msgid "Anti-features"
msgid "Antifeatures"
msgstr "Características non desexables"
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: he <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: hi <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: hu <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: id <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: it <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ja <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: kab <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ko <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: lt <LL@li.org>\n"
@ -204,7 +204,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: mk <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: nb_NO <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ne <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: nl <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: oc <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pl <LL@li.org>\n"
@ -204,7 +204,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pt <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pt_BR <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ru <LL@li.org>\n"
@ -204,7 +204,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: sk <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: sl <LL@li.org>\n"
@ -204,7 +204,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: sv <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: te <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: tr <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: uk <LL@li.org>\n"
@ -204,7 +204,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-22 04:59+0100\n"
"POT-Creation-Date: 2024-04-01 00:57+0200\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: zh_Hans <LL@li.org>\n"
@ -203,7 +203,7 @@ msgid "Important infos before installing"
msgstr ""
#: templates/app.html:124
msgid "Anti-features"
msgid "Antifeatures"
msgstr ""
#: templates/app.html:125

View file

@ -77,7 +77,7 @@ def __app_cache_clone_or_update_mapped(data):
try:
app_cache_clone_or_update(name, info)
except Exception as err:
logging.error("Error while updating %s: %s", name, err)
logging.error("[App caches] Error while updating %s: %s", name, err)
def apps_cache_update_all(apps: dict[str, dict[str, Any]], parallel: int = 8) -> None:

View file

@ -0,0 +1,44 @@
# YunoHost app generator
This is a Flask app generating a draft .zip of a YunoHost application after filling a form
Official instance: https://appgenerator.yunohost.org/
## Developement
```bash
python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
# you need to manually download the assets to have access to the css and the javascript files
(cd assets && bash fetch_assets)
```
And then start the dev server:
```bash
source venv/bin/activate
FLASK_APP=app.py FLASK_ENV=development flask --debug run
```
## Translation
It's based on Flask-Babel : <https://python-babel.github.io/flask-babel/>
```bash
source venv/bin/activate
# Extract the english sentences from the code, needed if you modified it
pybabel extract --ignore-dirs venv -F babel.cfg -o messages.pot .
# If working on a new locale: initialize it (in this example: fr)
pybabel init -i messages.pot -d translations -l fr
# Otherwise, update the existing .po:
pybabel update -i messages.pot -d translations
# ... translate stuff in translations/<lang>/LC_MESSAGES/messages.po
# re-run the 'update' command to let Babel properly format the text
# then compile:
pybabel compile -d translations
```

812
tools/app_generator/app.py Normal file
View file

@ -0,0 +1,812 @@
#### Imports
import logging
from io import BytesIO
import re
import os
import jinja2 as j2
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.fields import HiddenField
# Translations
from flask_babel import Babel
from flask_babel import lazy_gettext as _
from flask import redirect, request, make_response # Language swap by redirecting
# Markdown to HTML - for debugging purposes
from misaka import Markdown, HtmlRenderer
# Managing zipfiles
import zipfile
from flask_cors import CORS
from urllib import parse
from secrets import token_urlsafe
logger = logging.getLogger()
#### GLOBAL VARIABLES
YOLOGEN_VERSION = "0.10"
GENERATOR_DICT = {"GENERATOR_VERSION": YOLOGEN_VERSION}
#### Create FLASK and Jinja Environments
app = Flask(__name__)
app.config["SECRET_KEY"] = token_urlsafe(16) # Necessary for the form CORS
cors = CORS(app)
environment = j2.Environment(loader=j2.FileSystemLoader("templates/"))
def is_hidden_field_filter(field):
return isinstance(field, HiddenField)
app.jinja_env.globals["bootstrap_is_hidden_field"] = is_hidden_field_filter
# Handle translations
BABEL_TRANSLATION_DIRECTORIES = "translations"
babel = Babel()
LANGUAGES = {"en": _("English"), "fr": _("French")}
@app.context_processor
def inject_conf_var():
return dict(AVAILABLE_LANGUAGES=LANGUAGES)
def configure(app):
babel.init_app(app, locale_selector=get_locale)
app.config["LANGUAGES"] = LANGUAGES
def get_locale():
print(request.accept_languages.best_match(app.config["LANGUAGES"].keys()))
print(request.cookies.get("lang", "en"))
# return 'en' # to test
# return 'fr'
if request.args.get("language"):
print(request.args.get("language"))
session["language"] = request.args.get("language")
return request.cookies.get("lang", "en")
# return request.accept_languages.best_match(app.config['LANGUAGES'].keys()) # The result is based on the Accept-Language header. For testing purposes, you can directly return a language code, for example: return de
configure(app)
#### Custom functions
# Define custom filter
@app.template_filter("render_markdown")
def render_markdown(text):
renderer = HtmlRenderer()
markdown = Markdown(renderer)
return markdown(text)
# Add custom filter
j2.filters.FILTERS["render_markdown"] = render_markdown
# Converting markdown to html
def markdown_file_to_html_string(file):
with open(file, "r") as file:
markdown_content = file.read()
# Convert content from Markdown to HTML
html_content = render_markdown(markdown_content)
# Return Markdown and HTML contents
return markdown_content, html_content
### Forms
# Language selector. Not used (in GeneratorForm) until it's fixed or superseeded.
# Use it in the HTML with {{ form_field(main_form.generator_language) }}
class Translations(FlaskForm):
generator_language = SelectField(
_("Select language"),
choices=[("none", "")] + [language for language in LANGUAGES.items()],
default=["en"],
id="selectLanguage",
)
class GeneralInfos(FlaskForm):
app_id = StringField(
Markup(_("Application identifier (id)")),
description=_("Small caps and without spaces"),
validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?<!_ynh)$")],
render_kw={
"placeholder": "my_super_app",
},
)
app_name = StringField(
_("App name"),
description=_("It's the application name, displayed in the user interface"),
validators=[DataRequired()],
render_kw={
"placeholder": "My super App",
},
)
description_en = StringField(
_("Short description (en)"),
description=_(
"Explain in a few words (10-15) why this app is useful or what it does (the goal is to give a broad idea for the user browsing an hundred apps long catalog"
),
validators=[DataRequired()],
)
description_fr = StringField(
_("Short description (fr)"),
description=_(
"Explain in a few words (10-15) why this app is useful or what it does (the goal is to give a broad idea for the user browsing an hundred apps long catalog"
),
validators=[DataRequired()],
)
class IntegrationInfos(FlaskForm):
# TODO : people shouldnt have to put the ~ynh1 ? This should be added automatically when rendering the app files ?
version = StringField(
_("Version"),
validators=[Regexp("\d{1,4}.\d{1,4}(.\d{1,4})?(.\d{1,4})?~ynh\d+")],
render_kw={"placeholder": "1.0~ynh1"},
)
maintainers = StringField(
_("Maintainer of the generated app"),
description=_("Usually you put your name here... If you're okay with it ;)"),
)
yunohost_required_version = StringField(
_("Minimal YunoHost version"),
description=_("Minimal YunoHost version for the application to work"),
render_kw={
"placeholder": "11.1.21",
},
)
architectures = SelectMultipleField(
_("Supported architectures"),
choices=[
("all", _("All architectures")),
("amd64", "amd64"),
("i386", "i386"),
("armhf", "armhf"),
("arm64", "arm64"),
],
default=["all"],
validators=[DataRequired()],
)
multi_instance = BooleanField(
_(
"The app can be installed multiple times at the same time on the same server"
),
default=True,
)
ldap = SelectField(
_("The app will be integrating LDAP"),
description=_(
"Which means it's possible to use Yunohost credentials to log into this app. 'LDAP' corresponds to the technology used by Yunohost to handle a centralised user base. Bridging the app and Yunohost's LDAP often requires to add the proper technical details in the app's configuration file"
),
choices=[
("false", _("No")),
("true", _("Yes")),
("not_relevant", _("Not relevant")),
],
default="not_relevant",
validators=[DataRequired()],
)
sso = SelectField(
_("The app will be integrated in Yunohost SSO (Single Sign On)"),
description=_(
"Which means that people will be logged in the app after logging in YunoHost's portal, without having to sign on specifically into this app."
),
choices=[
("false", _("Yes")),
("true", _("No")),
("not_relevant", _("Not relevant")),
],
default="not_relevant",
validators=[DataRequired()],
)
class UpstreamInfos(FlaskForm):
license = StringField(
_("Licence"),
description=_(
"You should check this on the upstream repository. The expected format is a SPDX id listed in https://spdx.org/licenses/"
),
validators=[DataRequired()],
)
website = StringField(
_("Official website"),
description=_("Leave empty if there is no official website"),
validators=[URL(), Optional()],
render_kw={
"placeholder": "https://awesome-app-website.com",
},
)
demo = StringField(
_("Official app demo"),
description=_("Leave empty if there is no official demo"),
validators=[URL(), Optional()],
render_kw={
"placeholder": "https://awesome-app-website.com/demo",
},
)
admindoc = StringField(
_("Admin documentation"),
description=_("Leave empty if there is no official admin doc"),
validators=[URL(), Optional()],
render_kw={
"placeholder": "https://awesome-app-website.com/doc/admin",
},
)
userdoc = StringField(
_("Usage documentation"),
description=_("Leave empty if there is no official user doc"),
validators=[URL(), Optional()],
render_kw={
"placeholder": "https://awesome-app-website.com/doc/user",
},
)
code = StringField(
_("Code repository"),
validators=[URL(), DataRequired()],
render_kw={
"placeholder": "https://some.git.forge/org/app",
},
)
class InstallQuestions(FlaskForm):
domain_and_path = SelectField(
_(
"Ask the URL where the app will be installed ('domain' and 'path' variables)"
),
default="true",
choices=[
("true", _("Ask domain+path")),
(
"full_domain",
_(
"Ask only the domain (the app requires to be installed at the root of a dedicated domain)"
),
),
("false", _("Do not ask (it isn't a webapp)")),
],
)
init_main_permission = BooleanField(
_("Ask who can access to the app"),
description=_(
"In the users groups : by default at least 'visitors', 'all_users' et 'admins' exists. (It was previously the private/public app concept)"
),
default=True,
)
init_admin_permission = BooleanField(
_("Ask who can access to the admin interface"),
description=_("In the case where the app has an admin interface"),
default=False,
)
language = SelectMultipleField(
_("Supported languages"),
choices=[
("_", _("None / not relevant")),
("en", _("English")),
("fr", _("French")),
("en", _("Spanish")),
("it", _("Italian")),
("de", _("German")),
("zh", _("Chinese")),
("jp", _("Japanese")),
("da", _("Danish")),
("pt", _("Portugese")),
("nl", _("Dutch")),
("ru", _("Russian")),
],
default=["_"],
validators=[DataRequired()],
)
# manifest
class Ressources(FlaskForm):
# Sources
source_url = StringField(
_("Application source code or executable"),
validators=[DataRequired(), URL()],
render_kw={
"placeholder": "https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz",
},
)
sha256sum = StringField(
_("Sources sha256 checksum"),
validators=[DataRequired(), Length(min=64, max=64)],
render_kw={
"placeholder": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
},
)
auto_update = SelectField(
_("Enable automatic update of sources (using a bot running every night)"),
description=_(
"If the upstream software is hosted in one of the handled sources and publishes proper releases or tags, the bot will create a pull request to update the sources URL and checksum"
),
default="none",
choices=[
("none", "Non"),
("latest_github_tag", "Github (tag)"),
("latest_github_release", "Github (release)"),
("latest_github_commit", "Github (commit)"),
("latest_gitlab_tag", "Gitlab (tag)"),
("latest_gitlab_release", "Gitlab (release)"),
("latest_gitlab_commit", "Gitlab (commit)"),
("latest_gitea_tag", "Gitea (tag)"),
("latest_gitea_release", "Gitea (release)"),
("latest_gitea_commit", "Gitea (commit)"),
("latest_forgejo_tag", "Forgejo (tag)"),
("latest_forgejo_release", "Forgejo (release)"),
("latest_forgejo_commit", "Forgejo (commit)"),
],
)
apt_dependencies = StringField(
_("Dependencies to be installed via apt (separated by comma and/or spaces)"),
render_kw={
"placeholder": "foo, bar2.1-ext, libwat",
},
)
database = SelectField(
_("Initialize an SQL database"),
choices=[
("false", "Non"),
("mysql", "MySQL/MariaDB"),
("postgresql", "PostgreSQL"),
],
default="false",
)
system_user = BooleanField(
_("Initialize a system user for this app"),
default=True,
)
install_dir = BooleanField(
_("Initialize an installation folder for this app"),
description=_("By default it's /var/www/$app"),
default=True,
)
data_dir = BooleanField(
_("Initialize a folder to store the app data"),
description=_("By default it's /var/yunohost.app/$app"),
default=False,
)
class SpecificTechnology(FlaskForm):
main_technology = SelectField(
_("App main technology"),
choices=[
("none", _("None / Static application")),
("php", "PHP"),
("nodejs", "NodeJS"),
("python", "Python"),
("ruby", "Ruby"),
("other", _("Other")),
],
default="none",
validators=[DataRequired()],
)
install_snippet = TextAreaField(
_("Installation specific commands"),
description=_(
"These commands are executed from the app installation folder (by default, /var/www/$app) after the sources have been deployed. This field uses by default a classic example based on the selected technology. You should probably compare and adapt it according to the app installation documentation"
),
validators=[Optional()],
render_kw={"spellcheck": "false"},
)
#
# PHP
#
use_composer = BooleanField(
_("Use composer"),
description=_("Composer is a PHP dependencies manager used by some apps"),
default=False,
)
#
# NodeJS
#
nodejs_version = StringField(
_("NodeJS version"),
description=_("For example: 16.4, 18, 18.2, 20, 20.1, ..."),
render_kw={
"placeholder": "20",
},
)
use_yarn = BooleanField(
_("Install and use Yarn"),
default=False,
)
# NodeJS / Python / Ruby / ...
systemd_execstart = StringField(
_("Command to start the app daemon (from systemd service)"),
description=_(
"Corresponds to 'ExecStart' statement in systemd. You can use '__INSTALL_DIR__' to refer to the install directory, or '__APP__' to refer to the app id"
),
render_kw={
"placeholder": "__INSTALL_DIR__/bin/app --some-option",
},
)
class AppConfig(FlaskForm):
use_custom_config_file = BooleanField(
_("The app uses a specific configuration file"),
description=_("Usually : .env, config.json, conf.ini, params.yml, ..."),
default=False,
)
custom_config_file = StringField(
_("Name or file path to use"),
validators=[Optional()],
render_kw={
"placeholder": "config.json",
},
)
custom_config_file_content = TextAreaField(
_("App configuration file pattern"),
description=_(
"In this pattern, you can use the syntax __FOO_BAR__ which will automatically replaced by the value of the variable $foo_bar"
),
validators=[Optional()],
render_kw={"spellcheck": "false"},
)
class Documentation(FlaskForm):
# TODO : # screenshot
description = TextAreaField(
Markup(
_(
"""doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly listing the main features, possible warnings and specific details on its functioning in Yunohost (e.g. warning about integration issues)."""
)
),
validators=[Optional()],
render_kw={
"spellcheck": "false",
},
)
pre_install = TextAreaField(
_(
"doc/PRE_INSTALL.md: important info to be shown to the admin before installing the app"
),
description=_("Leave empty if not relevant"),
validators=[Optional()],
render_kw={
"spellcheck": "false",
},
)
post_install = TextAreaField(
_(
"doc/POST_INSTALL.md: important info to be shown to the admin after installing the app"
),
description=_("Leave empty if not relevant"),
validators=[Optional()],
render_kw={
"spellcheck": "false",
},
)
pre_upgrade = TextAreaField(
_(
"doc/PRE_UPGRADE.md: important info to be shown to the admin before upgrading the app"
),
description=_("Leave empty if not relevant"),
validators=[Optional()],
render_kw={
"spellcheck": "false",
},
)
post_upgrade = TextAreaField(
_(
"doc/POST_UPGRADE.md: important info to be shown to the admin after upgrading the app"
),
description=_("Leave empty if not relevant"),
validators=[Optional()],
render_kw={
"spellcheck": "false",
},
)
admin = TextAreaField(
_("doc/ADMIN.md: general tips on how to administrate this app"),
description=_("Leave empty if not relevant"),
validators=[Optional()],
render_kw={
"spellcheck": "false",
},
)
class MoreAdvanced(FlaskForm):
enable_change_url = BooleanField(
_("Handle app install URL change (change_url script)"),
default=True,
render_kw={
"title": _("Should changing the app URL be allowed ? (change_url change)")
},
)
use_logrotate = BooleanField(
_("Use logrotate for the app logs"),
default=True,
render_kw={
"title": _(
"If the app generates logs, this option permit to handle their archival. Recommended."
)
},
)
# TODO : specify custom log file
# custom_log_file = "/var/log/$app/$app.log" "/var/log/nginx/${domain}-error.log"
use_fail2ban = BooleanField(
_("Protect the application against brute force attacks (via fail2ban)"),
default=False,
render_kw={
"title": _(
"If the app generates failed connexions logs, this option allows to automatically banish the related IP after a certain number of failed password tries. Recommended."
)
},
)
use_cron = BooleanField(
_("Add a CRON task for this application"),
description=_("Corresponds to some app periodic operations"),
default=False,
)
cron_config_file = TextAreaField(
_("Type the CRON file content"),
validators=[Optional()],
render_kw={
"class": "form-control",
"spellcheck": "false",
},
)
fail2ban_regex = StringField(
_("Regular expression for fail2ban"),
# Regex to match into the log for a failed login
validators=[Optional()],
render_kw={
"placeholder": _("A regular expression"),
"class": "form-control",
"title": _(
"Regular expression to check in the log file to activate failban (search for a line that indicates a credentials error)."
),
},
)
## Main form
class GeneratorForm(
GeneralInfos,
IntegrationInfos,
UpstreamInfos,
InstallQuestions,
Ressources,
SpecificTechnology,
AppConfig,
Documentation,
MoreAdvanced,
):
class Meta:
csrf = False
generator_mode = SelectField(
_("Generator mode"),
description=_(
"In tutorial version, the generated app will contain additionnal comments to ease the understanding. In steamlined version, the generated app will only contain the necessary minimum."
),
choices=[
("simple", _("Streamlined version")),
("tutorial", _("Tutorial version")),
],
default="true",
validators=[DataRequired()],
)
submit_preview = SubmitField(_("Previsualise"))
submit_download = SubmitField(_("Download the .zip"))
submit_demo = SubmitField(
_("Fill with demo values"),
render_kw={
"onclick": "fillFormWithDefaultValues()",
"title": _(
"Generate a complete and functionnal minimalistic app that you can iterate from"
),
},
)
#### Web pages
@app.route("/", methods=["GET", "POST"])
def main_form_route():
main_form = GeneratorForm()
app_files = []
if request.method == "POST":
if not main_form.validate_on_submit():
logging.error("Form not validated?")
logging.error(main_form.errors)
return render_template(
"index.html",
main_form=main_form,
generator_info=GENERATOR_DICT,
generated_files={},
)
if main_form.submit_preview.data:
submit_mode = "preview"
elif main_form.submit_demo.data:
submit_mode = "demo" # TODO : for now this always trigger a preview. Not sure if that's an issue
else:
submit_mode = "download"
class AppFile:
def __init__(self, id_, destination_path=None):
self.id = id_
self.destination_path = destination_path
self.content = None
app_files = [
AppFile("manifest", "manifest.toml"),
AppFile("tests", "tests.toml"), # TODO test this
AppFile("_common.sh", "scripts/_common.sh"),
AppFile("install", "scripts/install"),
AppFile("remove", "scripts/remove"),
AppFile("backup", "scripts/backup"),
AppFile("restore", "scripts/restore"),
AppFile("upgrade", "scripts/upgrade"),
AppFile("nginx", "conf/nginx.conf"),
]
if main_form.enable_change_url.data:
app_files.append(AppFile("change_url", "scripts/change_url"))
if main_form.main_technology.data not in ["none", "php"]:
app_files.append(AppFile("systemd", "conf/systemd.service"))
# TODO : buggy, tries to open php.j2
# if main_form.main_technology.data == "php":
# app_files.append(AppFile("php", "conf/extra_php-fpm.conf"))
if main_form.description.data:
app_files.append(AppFile("DESCRIPTION", "doc/DESCRIPTION.md"))
if main_form.pre_install.data:
app_files.append(AppFile("PRE_INSTALL", "doc/PRE_INSTALL.md"))
if main_form.post_install.data:
app_files.append(AppFile("POST_INSTALL", "doc/POST_INSTALL.md"))
if main_form.pre_upgrade.data:
app_files.append(AppFile("PRE_UPGRADE", "doc/PRE_UPGRADE.md"))
if main_form.post_upgrade.data:
app_files.append(AppFile("POST_UPGRADE", "doc/POST_UPGRADE.md"))
if main_form.admin.data:
app_files.append(AppFile("ADMIN", "doc/ADMIN.md"))
template_dir = os.path.dirname(__file__) + "/templates/"
for app_file in app_files:
template = open(template_dir + app_file.id + ".j2").read()
app_file.content = render_template_string(
template, data=dict(request.form | GENERATOR_DICT)
)
app_file.content = re.sub(r"\n\s+$", "\n", app_file.content, flags=re.M)
app_file.content = re.sub(r"\n{3,}", "\n\n", app_file.content, flags=re.M)
print(main_form.use_custom_config_file.data)
if main_form.use_custom_config_file.data:
app_files.append(
AppFile("appconf", "conf/" + main_form.custom_config_file.data)
)
app_files[-1].content = main_form.custom_config_file_content.data
print(main_form.custom_config_file.data)
print(main_form.custom_config_file_content.data)
# TODO : same for cron job
if submit_mode == "download":
# Generate the zip file
f = BytesIO()
with zipfile.ZipFile(f, "w") as zf:
print("Exporting zip archive for app: " + request.form["app_id"])
for app_file in app_files:
print(app_file.id)
zf.writestr(app_file.destination_path, app_file.content)
f.seek(0)
# Send the zip file to the user
return send_file(
f, as_attachment=True, download_name=request.form["app_id"] + ".zip"
)
return render_template(
"index.html",
main_form=main_form,
generator_info=GENERATOR_DICT,
generated_files=app_files,
)
# Localisation
@app.route("/language/<language>")
def set_language(language=None):
response = make_response(redirect(request.referrer or "/"))
response.set_cookie("lang", language)
return response
#### Running the web server
if __name__ == "__main__":
app.run(debug=True)

View file

@ -0,0 +1,2 @@
[python: *.py]
[jinja2: templates/**.html]

View file

@ -0,0 +1,14 @@
import os
install_dir = os.path.dirname(__file__)
command = f"{install_dir}/venv/bin/gunicorn"
pythonpath = install_dir
workers = 4
user = "appgenerator"
bind = f"unix:{install_dir}/sock"
pid = "/run/gunicorn/appgenerator-pid"
errorlog = "/var/log/appgenerator/error.log"
accesslog = "/var/log/appgenerator/access.log"
access_log_format = '%({X-Real-IP}i)s %({X-Forwarded-For}i)s %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
loglevel = "warning"
capture_output = True

View file

@ -0,0 +1,18 @@
blinker==1.6.3
cffi==1.16.0
click==8.1.7
dominate==2.8.0
Flask==3.0.0
flask_babel~=4.0.0
Flask-Cors==4.0.0
Flask-Misaka==1.0.0
Flask-WTF==1.2.1
itsdangerous==2.1.2
Jinja2==3.1.4
MarkupSafe==2.1.3
misaka==2.1.1
pycparser==2.21
visitor==0.1.3
Werkzeug==3.0.1
WTForms==3.0.1
gunicorn==22.0.0

View file

@ -0,0 +1,6 @@
# Download standalone tailwind to compile what we need
wget https://github.com/tailwindlabs/tailwindcss/releases/download/v3.3.3/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --minify

View file

@ -0,0 +1,109 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
body {
@apply text-gray-800;
}
h1 {
@apply text-2xl font-bold;
}
h2 {
@apply text-xl font-bold;
}
h3 {
@apply text-lg font-bold;
}
.hide {
display: none;
}
.btn {
@apply text-sm font-medium rounded-md px-4 py-2 transition bg-gray-200 m-1;
}
.btn-sm {
@apply text-xs font-medium rounded-md px-2 py-2 transition;
}
.btn-success {
@apply text-white bg-green-500 hover:bg-green-700;
}
.btn-primary {
@apply text-white bg-blue-500 hover:bg-blue-700;
}
.btn-link {
@apply bg-gray-400 hover:bg-gray-200;
}
.panel {
@apply block rounded-lg border border-gray-400 mb-2;
}
.panel-heading {
@apply text-white bg-blue-500 hover:bg-blue-700 p-2 font-bold;
}
.panel-body {
@apply p-2;
}
.alert-info {
@apply text-blue-900 border-blue-900 bg-blue-200 rounded-lg p-4;
}
.active, .collapse-button:hover {
background-color: #318ddc;
}
.collapse-title:after {
content: '\002B';
color: white;
font-weight: bold;
float: right;
margin-left: 5px;
}
.expanded .collapse-title::after {
content: "\2212";
}
.collapsed {
padding: 0px 15px 0px 15px;
}
.collapsible {
max-height: 0px;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
label {
@apply font-bold;
}
input {
@apply rounded-lg;
}
.form-group, .checkbox {
@apply px-2 py-4;
}
.form-control {
@apply block w-full rounded-md border-gray-300;
}
.help-block {
@apply text-gray-500 text-sm;
}
.tip {
@apply italic p-2;
}
.has-error {
@apply bg-red-200;
}
}

View file

@ -0,0 +1,17 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['../templates/*.html'],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
],
safelist: [
'safelisted',
{
pattern: /^(text-[a-z]+-600|border-[a-z]+-400)$/,
},
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,3 @@
This is a dummy admin doc for this app
The app install dir is `__INSTALL_DIR__`

View file

@ -0,0 +1 @@
This is a dummy description of this app features

View file

@ -0,0 +1,7 @@
This is a dummy disclaimer to display after the install
The app url is <https://__DOMAIN____PATH__>
The app install dir is `__INSTALL_DIR__`
The app id is `__ID__`

View file

@ -0,0 +1 @@
This is a dummy disclaimer to display after upgrades

View file

@ -0,0 +1 @@
This is a dummy disclaimer to display prior to the install

View file

@ -0,0 +1 @@
This is a dummy disclaimer to display prior to any upgrade

View file

@ -0,0 +1,14 @@
#!/bin/bash
#=================================================
# COMMON VARIABLES
#=================================================
{% if data.use_nodejs -%}
nodejs_version={{ data.nodejs_version }}
{% endif -%}
#=================================================
# PERSONAL HELPERS
#=================================================

View file

@ -0,0 +1,105 @@
#!/bin/bash
#### App file generated with YoloGen, the YunoHost app generator, version {{ data['GENERATOR_VERSION'] }}.
{% if data.generator_mode == 'tutorial' -%} # This is the tutorial version of the app.
# It contains extra commands to explain what should be done in case you want to adjust some part of the script.
# Once you are done, you may remove them.
{% endif %}
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
# Keep this path for calling _common.sh inside the execution's context of backup and restore scripts
source ../settings/scripts/_common.sh
source /usr/share/yunohost/helpers
#=================================================
# DECLARE DATA AND CONF FILES TO BACKUP
#=================================================
ynh_print_info --message="Declaring files to be backed up..."
{% if data.generator_mode == 'tutorial' -%}
### N.B. : the following 'ynh_backup' calls are only a *declaration* of what needs
### to be backuped and not an actual copy of any file. The actual backup that
### creates and fill the archive with the files happens in the core after this
### script is called. Hence ynh_backups calls takes basically 0 seconds to run.
{% endif %}
{% if data.data_dir -%}
#=================================================
# BACKUP THE DATA DIR
#=================================================
{% if data.generator_mode == 'tutorial' -%}
### Only relevant if there is a "data_dir" resource for this app
# The --is_big parameters ensure this folder is not included in the backup by default (taking less space), except if BACKUP_CORE_ONLY=0 is passed before the backup command. You might want to document that for your users.
# TODO : there should be an option for this
{% endif %}
ynh_backup --src_path="$data_dir" # --is_big
{% endif %}
#=================================================
# SYSTEM CONFIGURATION
#=================================================
# Backup the nginx configuration
ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf"
{% if data.main_technology == "php" -%}
# Backup the PHP-FPM configuration
ynh_backup --src_path="/etc/php/$phpversion/fpm/pool.d/$app.conf"
{% endif %}
{% if data.use_fail2ban -%}
# Backup the Fail2Ban config
ynh_backup --src_path="/etc/fail2ban/jail.d/$app.conf"
ynh_backup --src_path="/etc/fail2ban/filter.d/$app.conf"
{% endif %}
{% if data.use_logrotate -%}
# Backup the logrotate configuration
ynh_backup --src_path="/etc/logrotate.d/$app"
{% endif %}
{% if data.main_technology not in ["php", "none"] -%}
# Backup the systemd service unit
ynh_backup --src_path="/etc/systemd/system/$app.service"
{% endif %}
#=================================================
# BACKUP VARIOUS FILES
#=================================================
{% if data.use_cron -%}
# Backup the cron configuration
ynh_backup --src_path="/etc/cron.d/$app"
{% endif %}
{% if data.generator_mode == 'tutorial' -%}
### For apps with huge logs, you might want to pass --is_big,
### and in restore script, mkdir and pass --not_mandatory to ynh_restore_file.
{% endif %}
ynh_backup --src_path="/var/log/$app/" # TODO : add an option to specify log file
{% if data.database != 'false' -%}
#=================================================
# BACKUP THE DATABASE
#=================================================
ynh_print_info --message="Backing up the {{ data.database }} database..."
### (However, things like MySQL dumps *do* take some time to run, though the
### copy of the generated dump to the archive still happens later)
{% if data.use_db == 'mysql' -%}
ynh_mysql_dump_db --database="$db_name" > db.sql
{% elif data.use_db == 'postgresql' -%}
ynh_psql_dump_db --database="$db_name" > db.sql
{% endif %}
{% endif %}
#=================================================
# END OF SCRIPT
#=================================================
ynh_print_info --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)."

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>{{ gettext("YunoHost app generator") }}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='tailwind.css') }}">
</head>
<body>
<main class="my-2 mx-auto max-w-screen-md">
{% block main -%}
{%- endblock main %}
</main>
</body>
</html>

View file

@ -0,0 +1,54 @@
#!/bin/bash
### App file generated with YoloGen, the Yunohost app generator, version {{ data['GENERATOR_VERSION'] }}.
{% if data.generator_mode == 'tutorial' -%}
# This is the tutorial version of the app.
# It contains extra commands to explain what should be done in case you want to adjust some part of the script.
# Once you are done, you may remove them.
{% endif %}
#=================================================
# GENERIC STARTING
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
{% if data.main_technology not in ["php", "none"] -%}
#=================================================
# STOP SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Stopping a systemd service..."
ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log"
{% endif %}
#=================================================
# MODIFY URL IN NGINX CONF
#=================================================
ynh_script_progression --message="Updating NGINX web server configuration..."
ynh_change_url_nginx_config
#=================================================
# SPECIFIC MODIFICATIONS
#=================================================
# ...
#=================================================
{% if data.main_technology not in ["php", "none"] -%}
#=================================================
# START SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Starting a systemd service..."
ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log"
{% endif %}
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Change of URL completed for $app" --last

View file

@ -0,0 +1,361 @@
{% import "wtf.html" as wtf %}
{% macro form_field(field,
form_type="basic",
horizontal_columns=('lg', 2, 10),
button_map={}) %}
{% if field.widget.input_type == 'checkbox' %}
<div class="checkbox">
<label>
{{field()|safe}} {{field.label.text|safe}}
</label>
{%- if field.description -%}
<p class="help-block">{{field.description|safe}}</p>
{%- endif %}
</div>
{% else %}
{{ wtf.form_field(field, form_type, horizontal_columns, button_map) }}
{% endif %}
{% endmacro %}
{% extends "base.html" %}
{% block main %}
<div class="mx-auto w-full text-center p-8">
<img alt="YunoHost application logo" src="{{ url_for('static', filename='yunohost-package.png') }}" class="w-32 mx-auto">
<h1 class="text-2xl font-bold">
{{ _("Yunohost application generation form") }}
</h1>
<p>Version: {{ generator_info['GENERATOR_VERSION'] }}</p>
<script>
function changeLanguage(lang) {
var url = "{{ url_for('set_language', language=lang) }}" + lang;
window.location.href = url;
}
</script>
<div>
{% for lang in AVAILABLE_LANGUAGES.items() %}
<button class="btn btn-sm bg-gray-200" type="button" onclick="changeLanguage('{{ lang[0] }}')">{{ lang[1] }}</button>
{% endfor %}
</div>
</div>
<form method="POST" role="form">
{{ main_form.hidden_tag() }}
<div class="text-red-800">
{{ wtf.form_errors(main_form, hiddens="only") }}
</div>
{{ form_field(main_form.generator_mode) }}
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("1/9 - General information") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div>
{{ form_field(main_form.app_name) }}
{{ form_field(main_form.app_id) }}
{{ form_field(main_form.description_en) }}
{{ form_field(main_form.description_fr) }}
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("2/9 - Upstream information") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<p class="tip">{{ gettext("The word 'upstream' refers to the original project that develops and maintains the app") }}</p>
<div>
{{ form_field(main_form.license) }}
{{ form_field(main_form.website) }}
{{ form_field(main_form.demo) }}
{{ form_field(main_form.admindoc) }}
{{ form_field(main_form.userdoc) }}
{{ form_field(main_form.code) }}
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("3/9 - Integration in YunoHost") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div>
{{ form_field(main_form.version) }}
{{ form_field(main_form.maintainers) }}
{{ form_field(main_form.multi_instance) }}
{{ form_field(main_form.architectures) }}
{{ form_field(main_form.ldap) }}
{{ form_field(main_form.sso) }}
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("4/9 - Questions to ask during installation") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<p class="tip">
{{ gettext("This part is meant to indicate the questions that will be asked.") }}
<br/>
{{ gettext("NB: only standard questions are asked here, it might be required to complete it by hand using other questions as a guide.") }}
</p>
<div>
{{ form_field(main_form.domain_and_path) }}
{{ form_field(main_form.init_main_permission) }}
{{ form_field(main_form.init_admin_permission) }}
{{ form_field(main_form.language) }}
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("5/9 - Resources to initialize") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<p class="tip">
{{ gettext("Technical elements configured before launching the 'real' app install script. Usually : creating a system user, downloading app sources, initialiser le dossier d'install et de données, install apt dependencies, create a database, ...") }}
</p>
<h3>Sources du logiciel</h3>
<div class="pl-2">
{{ form_field(main_form.source_url) }}
{{ form_field(main_form.sha256sum) }}
{{ form_field(main_form.auto_update) }}
</div>
<div>
{{ form_field(main_form.system_user) }}
{{ form_field(main_form.install_dir) }}
{{ form_field(main_form.data_dir) }}
{{ form_field(main_form.apt_dependencies) }}
{{ form_field(main_form.database) }}
</div>
</div>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("6/9 - Specific technology") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
{{ form_field(main_form.main_technology) }}
<div id="php_options">
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("You probably want to make sure to have 'phpX.Y-fpm' and others 'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y being the php version you want to use)") }}</div>
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("The generated application draft will include an nginx configuration snippet that interfaces with PHP-FPM") }}</div>
{{ form_field(main_form.use_composer) }}
</div>
<div id="nodejs_options">
{{ form_field(main_form.nodejs_version) }}
{{ form_field(main_form.use_yarn) }}
</div>
<div id="python_options">
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("You probably want to make sure to have 'python3' and 'python3-venv' listed in the apt dependencies earlier. Other dependencies should be installed inside a venv (cf the proposed install snippet)") }}</div>
</div>
{{ form_field(main_form.install_snippet) }}
<div id="systemd_options">
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("The generated application draft will include an nginx configuration snippet that reverse-proxies to a systemd service using an internal port") }}</div>
{{ form_field(main_form.systemd_execstart) }}
</div>
</div>
<script>
let main_technology = document.getElementById("main_technology");
let install_snippet = document.getElementById("install_snippet");
let systemd_execstart = document.getElementById("systemd_execstart");
install_snippet.classList.add("font-mono");
systemd_execstart.classList.add("font-mono");
function update_main_technology()
{
document.getElementById("php_options").classList.add("hide");
document.getElementById("nodejs_options").classList.add("hide");
document.getElementById("python_options").classList.add("hide");
document.getElementById("systemd_options").classList.add("hide");
if (main_technology.value == "php") document.getElementById("php_options").classList.remove("hide");
if (main_technology.value == "nodejs") document.getElementById("nodejs_options").classList.remove("hide");
if (main_technology.value == "python") document.getElementById("python_options").classList.remove("hide");
if ((main_technology.value != "php") && (main_technology.value != "none")) document.getElementById("systemd_options").classList.remove("hide");
if (main_technology.value == "none")
{
install_snippet.value = "some_command --build";
}
else if (main_technology.value == "php")
{
if (document.getElementById("use_composer").checked)
{
install_snippet.value = "ynh_install_composer";
}
else
{
install_snippet.value = "some_command --build";
}
}
else if (main_technology.value == "nodejs")
{
if (document.getElementById("use_yarn").checked)
{
install_snippet.value = "ynh_exec_as $app $ynh_node_load_PATH yarn install --production --pure-lockfile\nynh_exec_as $app $ynh_node_load_PATH yarn build";
}
else
{
install_snippet.value = "ynh_exec_as $app $ynh_node_load_PATH $ynh_npm install --production --ignore-scripts\nynh_exec_as $app $ynh_node_load_PATH $ynh_npm run build"
}
}
else if (main_technology.value == "python")
{
install_snippet.value = "python3 -m venv venv\nvenv/bin/pip3 install -r requirements.txt";
}
else if (main_technology.value == "ruby")
{
install_snippet.value = "ynh_gem update --system --no-document\nynh_gem install bundler foreman --no-document\nbundle config set --local deployment 'true'\nbundle config set --local without 'development test'\nbundle install";
}
}
main_technology.addEventListener("change", update_main_technology);
document.getElementById("use_composer").addEventListener("change", update_main_technology);
document.getElementById("use_yarn").addEventListener("change", update_main_technology);
update_main_technology();
</script>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("7/9 - App configuration") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
{{ form_field(main_form.use_custom_config_file) }}
<!-- TODO : this show/hide the other fields -->
<div id="custom_config_file_options">
{{ form_field(main_form.custom_config_file) }}
{{ form_field(main_form.custom_config_file_content) }}
</div>
</div>
<script>
let use_custom_config_file = document.getElementById("use_custom_config_file");
function update_custom_config_file()
{
document.getElementById("custom_config_file_options").classList.add("hide");
if (use_custom_config_file.checked) document.getElementById("custom_config_file_options").classList.remove("hide");
}
use_custom_config_file.addEventListener("change", update_custom_config_file);
update_custom_config_file();
</script>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("8/9 - General and advanced documentation") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
{{ form_field(main_form.description) }}
{{ form_field(main_form.pre_install) }}
{{ form_field(main_form.post_install) }}
{{ form_field(main_form.pre_upgrade) }}
{{ form_field(main_form.post_upgrade) }}
{{ form_field(main_form.admin) }}
</div>
</div>
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("9/9 - Advanced options") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
{{ form_field(main_form.enable_change_url) }}
{{ form_field(main_form.use_logrotate) }}
{{ form_field(main_form.use_fail2ban) }}
{{ form_field(main_form.fail2ban_regex) }}
{{ form_field(main_form.use_cron) }}
{{ form_field(main_form.cron_config_file) }}
</div>
</div>
<!-- Submit button -->
<p class="text-center">
{{ main_form.submit_preview(class="btn btn-primary") }}
{{ main_form.submit_download(class="btn btn-primary") }}
<br/>
{{ main_form.submit_demo(class="btn") }}
</p>
<!-- Submit button for demo values-->
<script>
function fillFormWithDefaultValues() {
console.log("Filling the form with minimal dummy values")
document.getElementById('app_id').value = "awesome_app_demo"
document.getElementById('app_name').value = "Awesome demo app"
document.getElementById('description_en').value = "Dummy app to demonstrate Yunohost app generator"
document.getElementById('description_fr').value = "Application factice de démonstration du générateur d'application Yunohost"
document.getElementById('version').value = "1.0~ynh1"
document.getElementById('architectures').value = "all"
// document.getElementById('ldap').value = "not_relevant"
// document.getElementById('sso').value = "not_relevant"
document.getElementById('license').value = "AGPL-3.0"
document.getElementById('code').value = "https://github.com/YunoHost/example_ynh/"
document.getElementById('source_url').value = "https://github.com/YunoHost/example_ynh/archive/1235ae57a3a285a4024d028d860f56980fa5ed19.tar.gz"
document.getElementById('sha256sum').value = "471ff03ae251812d3d042fb7e9206c812de5bf07bd4ac4c95763278702eff30a" }
</script>
</form>
<hr class="my-2" />
{% if generated_files %}
<div>
<h2 class="text-center">Code généré</h2>
{% for file in generated_files %}
<div class="panel">
<div class="panel-heading collapse-button">
<h3 class="panel-title collapse-title">{{ file.destination_path }}</h3>
</div>
<div class="panel-body collapsible collapsed p-0">
<pre class="m-0">{{ file.content }}</pre>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<script>
var coll = document.getElementsByClassName("collapse-button");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
if (this.classList.contains('expanded')) {
this.classList.remove('expanded');
this.nextElementSibling.classList.add('collapsed');
} else {
this.classList.add('expanded');
this.nextElementSibling.classList.remove('collapsed');
}
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
</script>
{% endblock %}

View file

@ -0,0 +1,215 @@
#!/bin/bash
### App file generated with YoloGen, the Yunohost app generator, version {{ data['GENERATOR_VERSION'] }}.
{% if data.generator_mode == 'tutorial' -%}
# This is the tutorial version of the app.
# It contains extra commands to explain what should be done in case you want to adjust some part of the script.
# Once you are done, you may remove them.
{% endif %}
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
{% if data.generator_mode == 'tutorial' -%}
# Install parameters are automatically saved as settings
#
# Settings are automatically loaded as bash variables
# in every app script context, therefore typically these will exist:
# - $domain
# - $path
# - $language
# ... etc
#
# Resources defined in the manifest are provisioned prior to this script
# and corresponding settings are also available, such as:
# - $install_dir
# - $port
# - $db_name
# ...
#
# $app is the app id (i.e. 'example' for first install,
# or 'example__2', '__3', ... for multi-instance installs)
#
{% endif %}
#=================================================
# INSTALL DEPENDENCIES
#=================================================
{% if data.main_technology == "nodejs" -%}
ynh_script_progression --message="Installing NodeJS..." --weight=10
# Install Nodejs
ynh_exec_warn_less ynh_install_nodejs --nodejs_version=$nodejs_version
ynh_use_nodejs
{% endif %}
#=================================================
# APP "BUILD" (DEPLOYING SOURCES, VENV, COMPILING ETC)
#=================================================
# DOWNLOAD, CHECK AND UNPACK SOURCE
#=================================================
ynh_script_progression --message="Setting up source files..."
{% if data.generator_mode == 'tutorial' -%}
### `ynh_setup_source` is used to install an app from a zip or tar.gz file,
### downloaded from an upstream source, as defined in the manifest.toml
{% endif %}
ynh_setup_source --dest_dir="$install_dir"
{% if data.generator_mode == 'tutorial' -%}
# $install_dir will automatically be initialized with some decent
# permission by default ... however, you may need to recursively reapply
# ownership to all files such as after the ynh_setup_source step
{% endif %}
chown -R $app:www-data "$install_dir"
#=================================================
# SYSTEM CONFIGURATION
#=================================================
ynh_script_progression --message="Adding system configurations related to $app..."
{% if data.main_technology == "php" -%}
# Create a dedicated PHP-FPM config
# conf/extra_php-fpm.conf will be appended to the auto-generated config, which will go in /etc/php/X.Y/fpm/pool.d/
ynh_add_fpm_config
{% endif %}
# Create a dedicated NGINX config using the conf/nginx.conf template
ynh_add_nginx_config
{% if data.main_technology not in ["php", "none"] -%}
{% if data.generator_mode == 'tutorial' -%}
### `ynh_systemd_config` is used to configure a systemd script for an app, using the conf/systemd.service template
{% endif %}
# Create a dedicated systemd config
ynh_add_systemd_config
{% if data.generator_mode == 'tutorial' -%}
### `yunohost service add` integrates a service in YunoHost. It then gets
### displayed in the admin interface and through the others `yunohost service` commands.
{% endif %}
yunohost service add $app --log="/var/log/$app/$app.log"
{% if data.generator_mode == 'tutorial' -%}
### Additional options starting with 3.8:
###
### --needs_exposed_ports "$port" a list of ports that needs to be publicly exposed
### which will then be checked by YunoHost's diagnosis system
### (N.B. DO NOT USE THIS is the port is only internal!!!)
###
### --test_status "some command" a custom command to check the status of the service
### (only relevant if 'systemctl status' doesn't do a good job)
###
### --test_conf "some command" some command similar to "nginx -t" that validates the conf of the service
###
### Re-calling 'yunohost service add' during the upgrade script is the right way
### to proceed if you later realize that you need to enable some flags that
### weren't enabled on old installs (be careful it'll override the existing
### service though so you should re-provide all relevant flags when doing so)
{% endif -%}
{% endif %}
{% if data.use_logrotate %}
# Use logrotate to manage application logfile(s)
ynh_use_logrotate
{% endif %}
{% if data.use_fail2ban -%}
# Create a dedicated Fail2Ban config
ynh_add_fail2ban_config --logpath="/var/log/nginx/${domain}-error.log" --failregex="{{ data.fail2ban_regex }}"
{% endif %}
{% if data.use_cron -%}
# Add cron job
cron_path="/etc/cron.d/$app"
ynh_add_config --template="../conf/task.cron" --destination="$cron_path"
chown root: "$cron_path"
chmod 644 "$cron_path"
{% endif %}
#=================================================
# APP INITIAL CONFIGURATION
#=================================================
# ADD A CONFIGURATION
#=================================================
ynh_script_progression --message="Adding app's configuration file..."
{% if data.generator_mode == 'tutorial' -%}
### You can add specific configuration files.
###
### Typically, put your template conf file in ../conf/your_config_file
### The template may contain strings such as __FOO__ or __FOO_BAR__,
### which will automatically be replaced by the values of $foo and $foo_bar
###
### ynh_add_config will also keep track of the config file's checksum,
### which later during upgrade may allow to automatically backup the config file
### if it's found that the file was manually modified
###
### Check the documentation of `ynh_add_config` for more info.
{% endif %}
ynh_add_config --template="{{ data.custom_config_file }}" --destination="$install_dir/{{ data.custom_config_file }}"
# FIXME: this should be handled by the core in the future
# You may need to use chmod 600 instead of 400,
# for example if the app is expected to be able to modify its own config
chmod 400 "$install_dir/{{ data.custom_config_file }}"
chown $app:$app "$install_dir/{{ data.custom_config_file }}"
{% if data.generator_mode == 'tutorial' -%}
### For more complex cases where you want to replace stuff using regexes,
### you shoud rely on ynh_replace_string (which is basically a wrapper for sed)
### When doing so, you also need to manually call ynh_store_file_checksum
###
### ynh_replace_string --match_string="match_string" --replace_string="replace_string" --target_file="$install_dir/some_config_file"
### ynh_store_file_checksum --file="$install_dir/some_config_file"
{% endif %}
{% if data.install_snippet -%}
#=================================================
# INSTALL APP
#=================================================
ynh_script_progression --message="Installing app..." --weight=5
pushd $install_dir
{{ data.install_snippet }}
popd
{% endif %}
#=================================================
# FINALIZE APP INSTALL WITH CURL
#=================================================
{% if data.generator_mode == 'tutorial' -%}
### Use these lines only if the app installation needs to be finalized through
### web forms. We generally don't want to ask the final user,
### so we're going to use curl to automatically fill the fields and submit the
### forms.
{% endif %}
# REMOVEME? ynh_script_progression --message="Finalizing installation..."
# REMOVEME? ynh_local_curl "/INSTALL_PATH" "key1=value1" "key2=value2" "key3=value3"
{% if data.main_technology not in ["php", "none"] -%}
#=================================================
# START SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Starting app's systemd service..."
# Start a systemd service
ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log"
{% endif %}
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Installation of $app completed" --last

View file

@ -0,0 +1,178 @@
#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json
packaging_format = 2
id = "{{ data.app_id }}"
name = "{{ data.app_name }}"
description.en = "{{ data.description_en }}"
description.fr = "{{ data.description_fr }}"
version = "{{ data.version }}"
{% if data.maintainers -%}
maintainers = ["{{ data.maintainers }}"]
{%- endif %}
[upstream]
license = "{{ data.license }}"
{% if data.website -%}website = "{{ data.website }}"{%- endif %}
{% if data.demo -%}demo = "{{ data.demo }}"{%- endif %}
{% if data.admindoc -%}admindoc = "{{ data.admindoc }}"{%- endif %}
{% if data.userdoc -%}userdoc = "{{ data.userdoc }}"{%- endif %}
{% if data.code -%}code = "{{ data.code }}"{%- endif %}
[integration]
yunohost = '>= {{ data.yunohost_required_version or '11.2'}}'
{% if data.generator_mode == "tutorial" -%}
# List of supported archs using the dpkg --print-architecture nomenclature (amd64/i386/armhf/arm64), for example: ["amd64", "i386']
{% endif -%}
architectures = "{{ data.architectures }}" # TODO : handle the "all" option (no ["all"])
multi_instance = {{ "true" if data.multi_instance else "false" }}
ldap = "{{ data.ldap }}" # TODO : fixme, use actual booleans + handle the "not_relevant" value
sso = "{{ data.sso }}"
# FIXME: replace with an **estimate** minimum disk and RAM requirements. e.g. 20M, 400M, 1G... You may have have a look at CI results
disk = "50M"
ram.build = "50M"
ram.runtime = "50M"
[install]
{% if data.domain_and_path != "false" -%}
[install.domain]
{% if data.generator_mode == "tutorial" -%}
# this is a generic question - ask strings are automatically handled by YunoHost's core
{% endif -%}
type = "domain"
{% if data.domain_and_path != "full_domain" -%}
[install.path]
{% if data.generator_mode == "tutorial" -%}
# this is a generic question - ask strings are automatically handled by YunoHost's core
{% endif -%}
type = "path"
default = "/{{ data.app_id }}"
{% endif -%}
{% endif %}
{% if data.init_main_permission -%}
[install.init_main_permission]
{% if data.generator_mode == "tutorial" -%}
# this is a generic question - ask strings are automatically handled by YunoHost's core
# This won't be saved as setting and will instead be used to initialize the SSOwat permission
{% endif -%}
type = "group"
default = "visitors"
{% endif %}
{% if data.language != "_" -%}
[install.language]
ask.en = "Choose the application language"
ask.fr = "Choisissez la langue de l'application"
type = "select"
choices = {{ data.language |safe }}
{% endif %}
{% if data.init_admin_permission -%}
[install.init_admin_permission]
{% if data.generator_mode == "tutorial" -%}
# this is a generic question - ask strings are automatically handled by YunoHost's core
# This won't be saved as setting and will instead be used to initialize the SSOwat permission
{% endif -%}
type = "group"
default = "admins"
{% endif %}
[resources]
{% if data.generator_mode == "tutorial" -%}
# See the packaging documentation for the full set
# of explanation regarding the behavior and properties for each of those
# https://yunohost.org/packaging_apps_resources
{% endif %}
[resources.sources]
[resources.sources.main]
# This will pre-fetch the asset which can then be deployed during the install/upgrade scripts with :
# ynh_setup_source --dest_dir="$install_dir"
# You can also define other assets than "main" and add --source_id="foobar" in the previous command
url = "{{data.source_url}}"
sha256 = "{{data.sha256sum}}"
# These infos are used by https://github.com/YunoHost/apps/blob/master/tools/autoupdate_app_sources/autoupdate_app_sources.py
# to auto-update the previous asset urls and sha256sum + manifest version
# assuming the upstream's code repo is on github and relies on tags or releases
# See the 'sources' resource documentation for more details
{% if data.auto_update != "none" -%}
autoupdate.strategy = "{{ data.auto_update }}"
{% else -%}
{% if data.generator_mode == "tutorial" -%}
# You might want to add an "autoupdate strategy" setting to that your app receive automated pull request with each new update, which also trigger automated testing.
# autoupdate.strategy = ""
{%- endif -%}
{% endif %}
{% if data.system_user -%}
[resources.system_user]
{% if data.generator_mode == "tutorial" -%}
# This will provision/deprovision a unix system user
{%- endif -%}
{%- endif %}
{% if data.install_dir -%}
[resources.install_dir]
{% if data.generator_mode == "tutorial" -%}
# This will create/remove the install dir as /var/www/$app
# and store the corresponding setting $install_dir
{%- endif -%}
{%- endif %}
{% if data.data_dir -%}
[resources.data_dir]
{% if data.generator_mode == "tutorial" -%}
# This will create/remove the data dir as /home/yunohost.app/$app
# and store the corresponding setting $data_dir
{%- endif -%}
{%- endif %}
[resources.permissions]
{% if data.generator_mode == "tutorial" -%}
# This will configure SSOwat permission for $domain/$path/
# The initial allowed group of user is configured via the init_main_permission question (public=visitors, private=all_users)
{% endif -%}
main.url = "/"
{% if data.main_technology not in ['none', 'php'] -%}
[resources.ports]
{% if data.generator_mode == "tutorial" -%}
# This will pick a random port for reverse-proxying and store it as the $port setting
{% endif -%}
{%- endif %}
{%- if data.apt_dependencies or data.use_yarn -%}
[resources.apt]
{% if data.generator_mode == "tutorial" -%}
# This will automatically install/uninstall the following apt packages
# and implicitly define the $phpversion setting as 8.0 (if phpX.Y-foobar dependencies are listed)
{% endif -%}
packages = "{{ data.apt_dependencies }} {% if data.database == 'mysql' -%} mariadb-server {% elif data.database == 'postgresql' %} postgresql {% endif -%} {% if data.main_technology == 'python' -%} python3 python3-venv {% endif -%}"
{%- endif %}
{%- if data.use_nodejs_needs_yarn -%}
{% if data.generator_mode == "tutorial" -%}
# This will configure an extra repository to install yarn dependency
{% endif -%}
extras.yarn.repo = "deb https://dl.yarnpkg.com/debian/ stable main"
extras.yarn.key = "https://dl.yarnpkg.com/debian/pubkey.gpg"
extras.yarn.packages = "yarn"
{%- endif %}
{%- if data.database != 'false' -%}
[resources.database]
{% if data.generator_mode == "tutorial" -%}
# This will automatically provision/deprovison a database and store the corresponding credentials in settings $db_user, $db_name, $db_pwd
{% endif -%}
type = "{{ data.database }}"
{%- endif -%}

View file

@ -0,0 +1,35 @@
#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent;
location __PATH__/ {
{% if data.main_technology in ["none", "php"] %}
# Path to source
alias __INSTALL_DIR__/;
{% endif %}
client_max_body_size 10M;
{% if data.main_technology == "php" %}
try_files $uri $uri/ index.php;
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_pass unix:/var/run/php/php__PHPVERSION__-fpm-__NAME__.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
{% elif data.main_technology not in ["php", "none"] %}
proxy_pass http://127.0.0.1:__PORT__;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# preserve client IP
proxy_set_header X-Forwarded-For $remote_addr;
{% endif %}
# Include SSOWAT user panel's shortcut tile.
include conf.d/yunohost_panel.conf.inc;
}

View file

@ -0,0 +1,79 @@
#!/bin/bash
### App file generated with YoloGen, the Yunohost app generator, version {{ data['GENERATOR_VERSION'] }}.
{% if data.generator_mode == 'tutorial' -%} # This is the tutorial version of the app.
# It contains extra commands to explain what should be done in case you want to adjust some part of the script.
# Once you are done, you may remove them.
{% endif %}
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
{% if data.generator_mode == 'tutorial' -%}
# Settings are automatically loaded as bash variables
# in every app script context, therefore typically these will exist:
# - $domain
# - $path
# - $language
# - $install_dir
# - $port
# ...
# For remove operations :
# - the core will deprovision every resource defined in the manifest **after** this script is ran
# this includes removing the install directory, and data directory (if --purge was used)
{% endif %}
#=================================================
# REMOVE SYSTEM CONFIGURATIONS
#=================================================
# REMOVE SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Removing system configurations related to $app..."
{% if data.generator_mode == 'tutorial' -%}
# This should be a symetric version of what happens in the install script
{% endif %}
{% if data.main_technology not in ["php", "none"] -%}
# Remove the service from the list of services known by YunoHost (added from `yunohost service add`)
if ynh_exec_warn_less yunohost service status $app >/dev/null
then
ynh_script_progression --message="Removing $app service integration..."
yunohost service remove $app
fi
ynh_remove_systemd_config
{% endif %}
ynh_remove_nginx_config
{% if data.main_technology == "php" -%}
ynh_remove_fpm_config
{% endif %}
{% if data.use_logrotate -%}
ynh_remove_logrotate
{% endif %}
{% if data.use_fail2ban -%}
ynh_remove_fail2ban_config
{% endif %}
# Remove other various files specific to the app... such as :
{% if data.use_cron -%}
ynh_secure_remove --file="/etc/cron.d/$app"
{% endif %}
ynh_secure_remove --file="/var/log/$app"
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Removal of $app completed" --last

View file

@ -0,0 +1,132 @@
#!/bin/bash
### App file generated with YoloGen, the Yunohost app generator, version {{ data['GENERATOR_VERSION'] }}.
{% if data.generator_mode == 'tutorial' -%} # This is the tutorial version of the app.
# It contains extra commands to explain what should be done in case you want to adjust some part of the script.
# Once you are done, you may remove them.
{% endif %}
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
# Keep this path for calling _common.sh inside the execution's context of backup and restore scripts
source ../settings/scripts/_common.sh
source /usr/share/yunohost/helpers
{% if data.install_dir %}
#=================================================
# RESTORE THE APP MAIN DIR
#=================================================
ynh_script_progression --message="Restoring the app main directory..." --weight=1
ynh_restore_file --origin_path="$install_dir"
{% if data.generator_mode == 'tutorial' -%}
# $install_dir will automatically be initialized with some decent
# permission by default ... however, you may need to recursively reapply
# ownership to all files such as after the ynh_setup_source step
{% endif %}
chown -R $app:www-data "$install_dir"
{% endif %}
{% if data.data_dir -%}
#=================================================
# RESTORE THE DATA DIRECTORY
#=================================================
ynh_script_progression --message="Restoring the data directory..." --weight=1
ynh_restore_file --origin_path="$data_dir" --not_mandatory
# (Same as for install dir)
chown -R $app:www-data "$data_dir"
{% endif %}
{% if data.database -%}
#=================================================
# RESTORE THE MYSQL DATABASE
#=================================================
{% if data.datase == 'mysql' -%}
ynh_script_progression --message="Restoring the MySQL database..." --weight=1
ynh_mysql_connect_as --user=$db_user --password=$db_pwd --database=$db_name < ./db.sql
{% endif %}
{% if data.database == 'postgresql' -%}
ynh_script_progression --message="Restoring the PostgreSQL database..." --weight=1
ynh_psql_connect_as --user=$db_user --password=$db_pwd --database=$db_name < ./db.sql
{% endif %}
{% endif %}
#=================================================
# RESTORE SYSTEM CONFIGURATIONS
#=================================================
ynh_script_progression --message="Restoring system configurations related to $app..." --weight=1
# This should be a symetric version of what happens in the install script
{% if data.main_technology == "php" -%}
ynh_restore_file --origin_path="/etc/php/$phpversion/fpm/pool.d/$app.conf"
{% endif %}
ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf"
{% if data.main_technology not in ["php", "none"] -%}
ynh_restore_file --origin_path="/etc/systemd/system/$app.service"
systemctl enable $app.service --quiet
yunohost service add $app --log="/var/log/$app/$app.log"
{% endif %}
{% if data.use_logrotate -%}
ynh_restore_file --origin_path="/etc/logrotate.d/$app"
{% endif %}
{% if data.use_fail2ban -%}
ynh_restore_file --origin_path="/etc/fail2ban/jail.d/$app.conf"
ynh_restore_file --origin_path="/etc/fail2ban/filter.d/$app.conf"
ynh_systemd_action --action=restart --service_name=fail2ban
{% endif %}
#=================================================
# RESTORE VARIOUS FILES
#=================================================
{% if data.use_cron -%}
ynh_restore_file --origin_path="/etc/cron.d/$app"
{% endif %}
{% if data.generator_mode == 'tutorial' -%}
### For apps with huge logs, you might want to not backup logs every time:
### The mkdir call is just here in case the log directory was not backed up.
### mkdir -p "/var/log/$app"
### chown $app:www-data "/var/log/$app"
### ynh_restore_file --src_path="/var/log/$app/" --not_mandatory
###
### For other apps, the simple way is better:
{% endif %}
ynh_restore_file --origin_path="/var/log/$app/" # TODO : add custom log file option
#=================================================
# RELOAD NGINX AND PHP-FPM OR THE APP SERVICE
#=================================================
ynh_script_progression --message="Reloading NGINX web server and $app's service..." --weight=1
# Typically you only have either $app or php-fpm but not both at the same time...
{% if data.main_technology not in ["php", "none"] -%}
ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log"
{% endif %}
{% if data.main_technology == "php" -%}
ynh_systemd_action --service_name=php$phpversion-fpm --action=reload
{% endif %}
ynh_systemd_action --service_name=nginx --action=reload
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Restoration completed for $app" --last

View file

@ -0,0 +1,54 @@
[Unit]
Description=Service for {{ data.app_name }} (__APP__)
After=network.target
[Service]
Type=simple
User=__APP__
Group=__APP__
{% if data.custom_config_file == ".env" %}
EnvironmentFile=__INSTALL_DIR__/.env
{% endif %}
WorkingDirectory=__INSTALL_DIR__/
ExecStart={{ data.systemd_execstart }}
StandardOutput=append:/var/log/__APP__/__APP__.log
StandardError=inherit
Restart=on-failure
RestartSec=10
# Sandboxing options to harden security
# Depending on specificities of your service/app, you may need to tweak these
# .. but this should be a good baseline
# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
DevicePolicy=closed
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
ProtectSystem=full
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
LockPersonality=yes
SystemCallArchitectures=native
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap @cpu-emulation @privileged
# Denying access to capabilities that should not be relevant for webapps
# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html
CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT
CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM
CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE
CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,82 @@
#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/tests.v1.schema.json
test_format = 1.0
[default]
# ------------
# Tests to run
# ------------
{% if data.generator_mode == "tutorial" -%}
# -------------------------------------------------------------------------------
# EVERYTHING PAST THIS POINT IS OPTIONAL AND MOST LIKELY UNNECESSARY FOR NEW APPS
#--------------------------------------------------------------------------------
## Conventions in this sample:
## <- An actual comment
# <- uncommenting this should be a valid entry in 'tests.toml'
## NB: the tests to run are automatically deduced by the CI script according to the
## content of the app's manifest. The declarations below allow to customize which
## tests are ran, possibly add special test suite to test special args, or
## declare which commits to test upgrade from.
##
## You can also decide (though this is discouraged!) to ban/ignore some tests,
## The test IDs to be used in only/exclude statements are:
## * install.root
## * install.subdir
## * install.nourl
## * install.multi
## * backup_restore
## * upgrade
## * upgrade.someCommitId
## * change_url
## NB: you should NOT need this except if you really have a good reason...
# exclude = ["install.private", "install.multi"]
## For special usecases, sometimes you need to setup other things on the machine
## prior to installing the app (such as installing another app)
## (Remove this key entirely if not needed)
# preinstall = """
# sudo yunohost app install foobar
# sudo yunohost user list
# """
# -------------------------------
# Default args to use for install
# -------------------------------
## By default, the CI will automagically fill the 'standard' args
## such as domain, path, admin, is_public and password with relevant values
## and also install args with a "default" provided in the manifest..
## It should only make sense to declare custom args here for args with no default values
## NB: you should NOT need those lines unless for custom questions with no obvious/default value
# args.language = "fr_FR"
# args.multisite = 0
# -------------------------------
# Commits to test upgrade from
# -------------------------------
## 00a1a6e7 is part of commit SHA, preferrably from 'master' branch
## that points to valid install of previous version
# test_upgrade_from.00a1a6e7.name = "Upgrade from 5.4"
# test_upgrade_from.00a1a6e7.args.foo = "bar"
## This is an additional test suite
# [some_additional_testsuite]
## On additional tests suites, you can decide to run only specific tests
# only = ["install.subdir"]
# args.language = "en_GB"
# args.multisite = 1
{% endif -%}

View file

@ -0,0 +1,185 @@
#!/bin/bash
### App file generated with YoloGen, the Yunohost app generator, version {{ data['GENERATOR_VERSION'] }}.
{% if data.generator_mode == 'tutorial' -%} # This is the tutorial version of the app.
# It contains extra commands to explain what should be done in case you want to adjust some part of the script.
# Once you are done, you may remove them.
{% endif %}
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
{% if data.generator_mode == 'tutorial' -%}
# Settings are automatically loaded as bash variables
# in every app script context, therefore typically these will exist:
# - $domain
# - $path
# - $language
# - $install_dir
# - $port
# ...
# In the context of upgrade,
# - resources are automatically provisioned / updated / deleted (depending on existing resources)
# - a safety backup is automatically created by the core and will be restored if the upgrade fails
### This helper will compare the version of the currently installed app and the version of the upstream package.
### $upgrade_type can have 2 different values
### - UPGRADE_APP if the upstream app version has changed
### - UPGRADE_PACKAGE if only the YunoHost package has changed
### ynh_check_app_version_changed will stop the upgrade if the app is up to date.
### UPGRADE_APP should be used to upgrade the core app only if there's an upgrade to do.
{% endif %}
upgrade_type=$(ynh_check_app_version_changed)
#=================================================
# STANDARD UPGRADE STEPS
#=================================================
# ENSURE DOWNWARD COMPATIBILITY
#=================================================
{% if data.generator_mode == 'tutorial' -%}
#ynh_script_progression --message="Ensuring downward compatibility..."
#
# N.B. : the followings setting migrations snippets are provided as *EXAMPLES*
# of what you may want to do in some cases (e.g. a setting was not defined on
# some legacy installs and you therefore want to initialize stuff during upgrade)
#
# If db_name doesn't exist, create it
#if [ -z "${db_name:-}" ]; then
# db_name=$(ynh_sanitize_dbid --db_name=$app)
# ynh_app_setting_set --app=$app --key=db_name --value=$db_name
#fi
# If install_dir doesn't exist, create it
#if [ -z "${install_dir:-}" ]; then
# install_dir=/var/www/$app
# ynh_app_setting_set --app=$app --key=install_dir --value=$install_dir
#fi
{% endif %}
{% if data.main_technology not in ["php", "none"] -%}
#=================================================
# STOP SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Stopping a systemd service..."
ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log"
{% endif %}
#=================================================
# "REBUILD" THE APP (DEPLOY NEW SOURCES, RERUN NPM BUILD...)
#=================================================
# DOWNLOAD, CHECK AND UNPACK SOURCE
#=================================================
if [ "$upgrade_type" == "UPGRADE_APP" ]
then
ynh_script_progression --message="Upgrading source files..."
# Download, check integrity, uncompress and patch the source from app.src
ynh_setup_source --dest_dir="$install_dir" --full_replace=1 --keep=".env"
{{ data.custom_config_file }}
fi
{% if data.generator_mode == 'tutorial' -%}
# $install_dir will automatically be initialized with some decent
# permission by default ... however, you may need to recursively reapply
# ownership to all files such as after the ynh_setup_source step
{% endif %}
chown -R $app:www-data "$install_dir"
#=================================================
# REAPPLY SYSTEM CONFIGURATIONS
#=================================================
ynh_script_progression --message="Upgrading system configurations related to $app..."
{% if data.generator_mode == 'tutorial' -%}
# This should be a literal copypasta of what happened in the install's "System configuration" section
{% endif %}
{% if data.main_technology == "php" -%}
ynh_add_fpm_config
{% endif %}
ynh_add_nginx_config
{% if data.main_technology not in ["php", "none"] -%}
ynh_add_systemd_config
yunohost service add $app --log="/var/log/$app/$app.log"
{% endif %}
{% if data.use_logrotate -%}
ynh_use_logrotate --non-append
{% endif %}
{% if data.use_fail2ban -%}
# Create a dedicated Fail2Ban config
ynh_add_fail2ban_config --logpath="/var/log/nginx/${domain}-error.log" --failregex="{{ data.fail2ban_regex }}"
{% endif %}
{% if data.use_cron -%}
cron_path="/etc/cron.d/$app"
ynh_add_config --template="../conf/task.cron" --destination="$cron_path"
chown root: "$cron_path"
chmod 644 "$cron_path"
{% endif %}
{% if data.use_custom_config_file -%}
#=================================================
# RECONFIGURE THE APP (UPDATE CONF, APPLY MIGRATIONS...)
#=================================================
# UPDATE A CONFIG FILE
#=================================================
ynh_script_progression --message="Updating a configuration file..."
{% if data.generator_mode == 'tutorial' -%}
### Same as during install
###
### The file will automatically be backed-up if it's found to be manually modified (because
### ynh_add_config keeps track of the file's checksum)
{% endif %}
ynh_add_config --template="{{ data.custom_config_file }}" --destination="$install_dir/{{ data.custom_config_file }}"
{% if data.generator_mode == 'tutorial' -%}
# FIXME: this should be handled by the core in the future
# You may need to use chmod 600 instead of 400,
# for example if the app is expected to be able to modify its own config
{% endif %}
chmod 400 "$install_dir/{{ data.custom_config_file }}"
chown $app:$app "$install_dir/{{ data.custom_config_file }}"
{% if data.generator_mode == 'tutorial' -%}
### For more complex cases where you want to replace stuff using regexes,
### you shoud rely on ynh_replace_string (which is basically a wrapper for sed)
### When doing so, you also need to manually call ynh_store_file_checksum
###
### ynh_replace_string --match_string="match_string" --replace_string="replace_string" --target_file="$install_dir/{{ data.custom_config_file }}"
### ynh_store_file_checksum --file="$install_dir/{{ data.custom_config_file }}"
{% endif %}
{% endif %}
{% if data.main_technology not in ["php", "none"] -%}
#=================================================
# START SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Starting a systemd service..."
ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log"
{% endif %}
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Upgrade of $app completed" --last

View file

@ -0,0 +1,213 @@
{% macro form_errors(form, hiddens=True) %}
{%- if form.errors %}
{%- for fieldname, errors in form.errors.items() %}
{%- if bootstrap_is_hidden_field(form[fieldname]) and hiddens or
not bootstrap_is_hidden_field(form[fieldname]) and hiddens != 'only' %}
{%- for error in errors %}
<p class="error">{{error}}</p>
{%- endfor %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- endmacro %}
{% macro _hz_form_wrap(horizontal_columns, form_type, add_group=False, required=False) %}
{% if form_type == "horizontal" %}
{% if add_group %}<div class="form-group{% if required %} required{% endif %}">{% endif %}
<div class="col-{{horizontal_columns[0]}}-offset-{{horizontal_columns[1]}}
col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}
">
{% endif %}
{{caller()}}
{% if form_type == "horizontal" %}
{% if add_group %}</div>{% endif %}
</div>
{% endif %}
{% endmacro %}
{% macro form_field(field,
form_type="basic",
horizontal_columns=('lg', 2, 10),
button_map={}) %}
{# this is a workaround hack for the more straightforward-code of just passing required=required parameter. older versions of wtforms do not have
the necessary fix for required=False attributes, but will also not set the required flag in the first place. we skirt the issue using the code below #}
{% if field.flags.required and not required in kwargs %}
{% set kwargs = dict(required=True, **kwargs) %}
{% endif %}
{% if field.widget.input_type == 'checkbox' %}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
<div class="checkbox">
<label>
{{field()|safe}} {{field.label.text|safe}}
</label>
</div>
{% endcall %}
{%- elif field.type == 'RadioField' -%}
{# note: A cleaner solution would be rendering depending on the widget,
this is just a hack for now, until I can think of something better #}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
{% for item in field -%}
<div class="radio">
<label>
{{item|safe}} {{item.label.text|safe}}
</label>
</div>
{% endfor %}
{% endcall %}
{%- elif field.type == 'SubmitField' -%}
{# deal with jinja scoping issues? #}
{% set field_kwargs = kwargs %}
{# note: same issue as above - should check widget, not field type #}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
{{field(class='btn btn-%s' % button_map.get(field.name, 'default'),
**field_kwargs)}}
{% endcall %}
{%- elif field.type == 'FormField' -%}
{# note: FormFields are tricky to get right and complex setups requiring
these are probably beyond the scope of what this macro tries to do.
the code below ensures that things don't break horribly if we run into
one, but does not try too hard to get things pretty. #}
<fieldset>
<legend>{{field.label}}</legend>
{%- for subfield in field %}
{% if not bootstrap_is_hidden_field(subfield) -%}
{{ form_field(subfield,
form_type=form_type,
horizontal_columns=horizontal_columns,
button_map=button_map) }}
{%- endif %}
{%- endfor %}
</fieldset>
{% else -%}
<div class="form-group {% if field.errors %} has-error{% endif -%}
{%- if field.flags.required %} required{% endif -%}
">
{%- if form_type == "inline" %}
{{field.label(class="sr-only")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
{% elif form_type == "horizontal" %}
{{field.label(class="control-label " + (
" col-%s-%s" % horizontal_columns[0:2]
))|safe}}
<div class=" col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}">
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
</div>
{%- if field.errors %}
{%- for error in field.errors %}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{error}}</p>
{% endcall %}
{%- endfor %}
{%- elif field.description -%}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{field.description|safe}}</p>
{% endcall %}
{%- endif %}
{%- else -%}
{{field.label(class="control-label")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
{%- if field.errors %}
{%- for error in field.errors %}
<p class="help-block">{{error}}</p>
{%- endfor %}
{%- elif field.description -%}
<p class="help-block">{{field.description|safe}}</p>
{%- endif %}
{%- endif %}
</div>
{% endif %}
{% endmacro %}
{# valid form types are "basic", "inline" and "horizontal" #}
{% macro quick_form(form,
action="",
method="post",
extra_classes=None,
role="form",
form_type="basic",
horizontal_columns=('lg', 2, 10),
enctype=None,
button_map={},
id="",
novalidate=False) %}
{#-
action="" is what we want, from http://www.ietf.org/rfc/rfc2396.txt:
4.2. Same-document References
A URI reference that does not contain a URI is a reference to the
current document. In other words, an empty URI reference within a
document is interpreted as a reference to the start of that document,
and a reference containing only a fragment identifier is a reference
to the identified fragment of that document. Traversal of such a
reference should not result in an additional retrieval action.
However, if the URI reference occurs in a context that is always
intended to result in a new request, as in the case of HTML's FORM
element, then an empty URI reference represents the base URI of the
current document and should be replaced by that URI when transformed
into a request.
-#}
{#- if any file fields are inside the form and enctype is automatic, adjust
if file fields are found. could really use the equalto test of jinja2
here, but latter is not available until 2.8
warning: the code below is guaranteed to make you cry =(
#}
{%- set _enctype = [] %}
{%- if enctype is none -%}
{%- for field in form %}
{%- if field.type == 'FileField' %}
{#- for loops come with a fairly watertight scope, so this list-hack is
used to be able to set values outside of it #}
{%- set _ = _enctype.append('multipart/form-data') -%}
{%- endif %}
{%- endfor %}
{%- else %}
{% set _ = _enctype.append(enctype) %}
{%- endif %}
<form
{%- if action != None %} action="{{action}}"{% endif -%}
{%- if id %} id="{{id}}"{% endif -%}
{%- if method %} method="{{method}}"{% endif %}
class="form
{%- if extra_classes %} {{extra_classes}}{% endif -%}
{%- if form_type == "horizontal" %} form-horizontal
{%- elif form_type == "inline" %} form-inline
{%- endif -%}
"
{%- if _enctype[0] %} enctype="{{_enctype[0]}}"{% endif -%}
{%- if role %} role="{{role}}"{% endif -%}
{%- if novalidate %} novalidate{% endif -%}
>
{{ form.hidden_tag() }}
{{ form_errors(form, hiddens='only') }}
{%- for field in form %}
{% if not bootstrap_is_hidden_field(field) -%}
{{ form_field(field,
form_type=form_type,
horizontal_columns=horizontal_columns,
button_map=button_map) }}
{%- endif %}
{%- endfor %}
</form>
{%- endmacro %}

View file

@ -0,0 +1,760 @@
# English translations for PROJECT.
# Copyright (C) 2024 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-26 15:39+0200\n"
"PO-Revision-Date: 2024-03-31 20:23+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
#: app.py:68 app.py:334
msgid "English"
msgstr ""
#: app.py:68 app.py:335
msgid "French"
msgstr ""
#: app.py:127
msgid "Select language"
msgstr ""
#: app.py:137
msgid "Application identifier (id)"
msgstr ""
#: app.py:138
msgid "Small caps and without spaces"
msgstr ""
#: app.py:146
msgid "App name"
msgstr ""
#: app.py:147
msgid "It's the application name, displayed in the user interface"
msgstr ""
#: app.py:157
msgid "Short description (en)"
msgstr ""
#: app.py:158 app.py:165
msgid ""
"Explain in a few words (10-15) why this app is useful or what it does "
"(the goal is to give a broad idea for the user browsing an hundred apps "
"long catalog"
msgstr ""
#: app.py:164
msgid "Short description (fr)"
msgstr ""
#: app.py:176
msgid "Version"
msgstr ""
#: app.py:182
msgid "Maintainer of the generated app"
msgstr ""
#: app.py:183
msgid "Usually you put your name here... If you're okay with it ;)"
msgstr ""
#: app.py:189
msgid "Minimal YunoHost version"
msgstr ""
#: app.py:190
msgid "Minimal YunoHost version for the application to work"
msgstr ""
#: app.py:199
msgid "Supported architectures"
msgstr ""
#: app.py:201
msgid "All architectures"
msgstr ""
#: app.py:212
msgid ""
"The app can be installed multiple times at the same time on the same "
"server"
msgstr ""
#: app.py:219
msgid "The app will be integrating LDAP"
msgstr ""
#: app.py:220
msgid ""
"Which means it's possible to use Yunohost credentials to log into this "
"app. 'LDAP' corresponds to the technology used by Yunohost to handle a "
"centralised user base. Bridging the app and Yunohost's LDAP often "
"requires to add the proper technical details in the app's configuration "
"file"
msgstr ""
#: app.py:224 app.py:238
msgid "No"
msgstr ""
#: app.py:225 app.py:237
msgid "Yes"
msgstr ""
#: app.py:226 app.py:239
msgid "Not relevant"
msgstr ""
#: app.py:232
msgid "The app will be integrated in Yunohost SSO (Single Sign On)"
msgstr ""
#: app.py:233
msgid ""
"Which means that people will be logged in the app after logging in "
"YunoHost's portal, without having to sign on specifically into this app."
msgstr ""
#: app.py:249
msgid "Licence"
msgstr ""
#: app.py:250
msgid ""
"You should check this on the upstream repository. The expected format is "
"a SPDX id listed in https://spdx.org/licenses/"
msgstr ""
#: app.py:257
msgid "Official website"
msgstr ""
#: app.py:258
msgid "Leave empty if there is no official website"
msgstr ""
#: app.py:265
msgid "Official app demo"
msgstr ""
#: app.py:266
msgid "Leave empty if there is no official demo"
msgstr ""
#: app.py:273
msgid "Admin documentation"
msgstr ""
#: app.py:274
msgid "Leave empty if there is no official admin doc"
msgstr ""
#: app.py:281
msgid "Usage documentation"
msgstr ""
#: app.py:282
msgid "Leave empty if there is no official user doc"
msgstr ""
#: app.py:289
msgid "Code repository"
msgstr ""
#: app.py:300
msgid ""
"Ask the URL where the app will be installed ('domain' and 'path' "
"variables)"
msgstr ""
#: app.py:305
msgid "Ask domain+path"
msgstr ""
#: app.py:308
msgid ""
"Ask only the domain (the app requires to be installed at the root of a "
"dedicated domain)"
msgstr ""
#: app.py:312
msgid "Do not ask (it isn't a webapp)"
msgstr ""
#: app.py:317
msgid "Ask who can access to the app"
msgstr ""
#: app.py:318
msgid ""
"In the users groups : by default at least 'visitors', 'all_users' et "
"'admins' exists. (It was previously the private/public app concept)"
msgstr ""
#: app.py:325
msgid "Ask who can access to the admin interface"
msgstr ""
#: app.py:326
msgid "In the case where the app has an admin interface"
msgstr ""
#: app.py:331
msgid "Supported languages"
msgstr ""
#: app.py:333
msgid "None / not relevant"
msgstr ""
#: app.py:336
msgid "Spanish"
msgstr ""
#: app.py:337
msgid "Italian"
msgstr ""
#: app.py:338
msgid "German"
msgstr ""
#: app.py:339
msgid "Chinese"
msgstr ""
#: app.py:340
msgid "Japanese"
msgstr ""
#: app.py:341
msgid "Danish"
msgstr ""
#: app.py:342
msgid "Portugese"
msgstr ""
#: app.py:343
msgid "Dutch"
msgstr ""
#: app.py:344
msgid "Russian"
msgstr ""
#: app.py:356
msgid "Application source code or executable"
msgstr ""
#: app.py:363
msgid "Sources sha256 checksum"
msgstr ""
#: app.py:371
msgid "Enable automatic update of sources (using a bot running every night)"
msgstr ""
#: app.py:372
msgid ""
"If the upstream software is hosted in one of the handled sources and "
"publishes proper releases or tags, the bot will create a pull request to "
"update the sources URL and checksum"
msgstr ""
#: app.py:394
msgid "Dependencies to be installed via apt (separated by comma and/or spaces)"
msgstr ""
#: app.py:403
msgid "Initialize an SQL database"
msgstr ""
#: app.py:413
msgid "Initialize a system user for this app"
msgstr ""
#: app.py:418
msgid "Initialize an installation folder for this app"
msgstr ""
#: app.py:419
msgid "By default it's /var/www/$app"
msgstr ""
#: app.py:424
msgid "Initialize a folder to store the app data"
msgstr ""
#: app.py:425
msgid "By default it's /var/yunohost.app/$app"
msgstr ""
#: app.py:433
msgid "App main technology"
msgstr ""
#: app.py:435
msgid "None / Static application"
msgstr ""
#: app.py:440
msgid "Other"
msgstr ""
#: app.py:447
msgid "Installation specific commands"
msgstr ""
#: app.py:448
msgid ""
"These commands are executed from the app installation folder (by default,"
" /var/www/$app) after the sources have been deployed. This field uses by "
"default a classic example based on the selected technology. You should "
"probably compare and adapt it according to the app installation "
"documentation"
msgstr ""
#: app.py:460
msgid "Use composer"
msgstr ""
#: app.py:461
msgid "Composer is a PHP dependencies manager used by some apps"
msgstr ""
#: app.py:472
msgid "NodeJS version"
msgstr ""
#: app.py:473
msgid "For example: 16.4, 18, 18.2, 20, 20.1, ..."
msgstr ""
#: app.py:480
msgid "Install and use Yarn"
msgstr ""
#: app.py:487
msgid "Command to start the app daemon (from systemd service)"
msgstr ""
#: app.py:488
msgid ""
"Corresponds to 'ExecStart' statement in systemd. You can use "
"'__INSTALL_DIR__' to refer to the install directory, or '__APP__' to "
"refer to the app id"
msgstr ""
#: app.py:500
msgid "The app uses a specific configuration file"
msgstr ""
#: app.py:501
msgid "Usually : .env, config.json, conf.ini, params.yml, ..."
msgstr ""
#: app.py:508
msgid "Name or file path to use"
msgstr ""
#: app.py:516
msgid "App configuration file pattern"
msgstr ""
#: app.py:517
msgid ""
"In this pattern, you can use the syntax __FOO_BAR__ which will "
"automatically replaced by the value of the variable $foo_bar"
msgstr ""
#: app.py:529
msgid ""
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly "
"listing the main features, possible warnings and specific details on its "
"functioning in Yunohost (e.g. warning about integration issues)."
msgstr ""
#: app.py:539
msgid ""
"doc/PRE_INSTALL.md: important info to be shown to the admin before "
"installing the app"
msgstr ""
#: app.py:540 app.py:548 app.py:556 app.py:564 app.py:572
msgid "Leave empty if not relevant"
msgstr ""
#: app.py:547
msgid ""
"doc/POST_INSTALL.md: important info to be shown to the admin after "
"installing the app"
msgstr ""
#: app.py:555
msgid ""
"doc/PRE_UPGRADE.md: important info to be shown to the admin before "
"upgrading the app"
msgstr ""
#: app.py:563
msgid ""
"doc/POST_UPGRADE.md: important info to be shown to the admin after "
"upgrading the app"
msgstr ""
#: app.py:571
msgid "doc/ADMIN.md: general tips on how to administrate this app"
msgstr ""
#: app.py:583
msgid "Handle app install URL change (change_url script)"
msgstr ""
#: app.py:586
msgid "Should changing the app URL be allowed ? (change_url change)"
msgstr ""
#: app.py:593
msgid "Use logrotate for the app logs"
msgstr ""
#: app.py:596
msgid ""
"If the app generates logs, this option permit to handle their archival. "
"Recommended."
msgstr ""
#: app.py:604
msgid "Protect the application against brute force attacks (via fail2ban)"
msgstr ""
#: app.py:609
msgid ""
"If the app generates failed connexions logs, this option allows to "
"automatically banish the related IP after a certain number of failed "
"password tries. Recommended."
msgstr ""
#: app.py:615
msgid "Add a CRON task for this application"
msgstr ""
#: app.py:616
msgid "Corresponds to some app periodic operations"
msgstr ""
#: app.py:620
msgid "Type the CRON file content"
msgstr ""
#: app.py:629
msgid "Regular expression for fail2ban"
msgstr ""
#: app.py:633
msgid "A regular expression"
msgstr ""
#: app.py:635
msgid ""
"Regular expression to check in the log file to activate failban (search "
"for a line that indicates a credentials error)."
msgstr ""
#: app.py:659
msgid "Generator mode"
msgstr ""
#: app.py:660
msgid ""
"In tutorial version, the generated app will contain additionnal comments "
"to ease the understanding. In steamlined version, the generated app will "
"only contain the necessary minimum."
msgstr ""
#: app.py:664
msgid "Streamlined version"
msgstr ""
#: app.py:665
msgid "Tutorial version"
msgstr ""
#: app.py:671
msgid "Previsualise"
msgstr ""
#: app.py:672
msgid "Download the .zip"
msgstr ""
#: app.py:674
msgid "Fill with demo values"
msgstr ""
#: app.py:677
msgid ""
"Generate a complete and functionnal minimalistic app that you can iterate"
" from"
msgstr ""
#: templates/base.html:5
msgid "YunoHost app generator"
msgstr ""
#: templates/index.html:28
msgid "Yunohost application generation form"
msgstr ""
#: templates/index.html:55
msgid "1/9 - General information"
msgstr ""
#: templates/index.html:70
msgid "2/9 - Upstream information"
msgstr ""
#: templates/index.html:73
msgid ""
"The word 'upstream' refers to the original project that develops and "
"maintains the app"
msgstr ""
#: templates/index.html:88
msgid "3/9 - Integration in YunoHost"
msgstr ""
#: templates/index.html:104
msgid "4/9 - Questions to ask during installation"
msgstr ""
#: templates/index.html:108
msgid "This part is meant to indicate the questions that will be asked."
msgstr ""
#: templates/index.html:110
msgid ""
"NB: only standard questions are asked here, it might be required to "
"complete it by hand using other questions as a guide."
msgstr ""
#: templates/index.html:124
msgid "5/9 - Resources to initialize"
msgstr ""
#: templates/index.html:128
msgid ""
"Technical elements configured before launching the 'real' app install "
"script. Usually : creating a system user, downloading app sources, "
"initialiser le dossier d'install et de données, install apt dependencies,"
" create a database, ..."
msgstr ""
#: templates/index.html:151
msgid "6/9 - Specific technology"
msgstr ""
#: templates/index.html:156
msgid ""
"You probably want to make sure to have 'phpX.Y-fpm' and others "
"'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y "
"being the php version you want to use)"
msgstr ""
#: templates/index.html:157
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that interfaces with PHP-FPM"
msgstr ""
#: templates/index.html:165
msgid ""
"You probably want to make sure to have 'python3' and 'python3-venv' "
"listed in the apt dependencies earlier. Other dependencies should be "
"installed inside a venv (cf the proposed install snippet)"
msgstr ""
#: templates/index.html:169
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that reverse-proxies to a systemd service using an internal port"
msgstr ""
#: templates/index.html:237
msgid "7/9 - App configuration"
msgstr ""
#: templates/index.html:261
msgid "8/9 - General and advanced documentation"
msgstr ""
#: templates/index.html:275
msgid "9/9 - Advanced options"
msgstr ""
#~ msgid "Maintener of the generated app"
#~ msgstr ""
#~ msgid "Commonly you put your name here... If you agree with it ;)"
#~ msgstr ""
#~ msgid ""
#~ "Which means it's possible to use "
#~ "Yunohost credential to connect. 'LDAP' "
#~ "corresponds to the technology used by"
#~ " Yunohost to handle a centralised "
#~ "user base. Bridging the APP and "
#~ "Yunohost LDAP often requires to fill "
#~ "some parameters in the app configuration"
#~ msgstr ""
#~ msgid ""
#~ "Which means that one connexion to "
#~ "Yunohost unlock the connexion to the "
#~ "software, without having to sign on "
#~ "specificaly into it. One only has "
#~ "to connect once (Single Sign On)"
#~ msgstr ""
#~ msgid "Activate the automated source update bot"
#~ msgstr ""
#~ msgid ""
#~ "If the software is available in "
#~ "one of the handled sources and "
#~ "publish releases or tags for its "
#~ "new updates, or for each new "
#~ "commit, a bot will provide an "
#~ "update with updated URL and checksum"
#~ msgstr ""
#~ msgid ""
#~ "Dependances to be installed via apt "
#~ "(separated by a quote and/or spaces)"
#~ msgstr ""
#~ msgid "Initialise a SQL database"
#~ msgstr ""
#~ msgid "Initialise a system user for this app"
#~ msgstr ""
#~ msgid "Initialise an installation folder for this app"
#~ msgstr ""
#~ msgid "Initialise a folder to store the app data"
#~ msgstr ""
#~ msgid ""
#~ "Type the content of DESCRIPTION.md file."
#~ " <br> Do not give the software "
#~ "name at the beginning, as it will"
#~ " be integrated an 'Overview' subpart"
#~ msgstr ""
#~ msgid ""
#~ "Type the DISCLAIMER.md file content, "
#~ "which list warnings and attention "
#~ "points."
#~ msgstr ""
#~ msgid "Type the PRE_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the POST_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the PRE_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the POST_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the ADMIN.md file content"
#~ msgstr ""
#~ msgid "2/9 - Informations about the application"
#~ msgstr ""
#~ msgid "The word upstream refers to the original project that maintains the app"
#~ msgstr ""
#~ msgid "5/9 - Ressources to initialise"
#~ msgstr ""
#~ msgid ""
#~ "A more complete presentation that the"
#~ " summary completed above, explaining to "
#~ "what correspond the software, the "
#~ "eventual warnings and specific details "
#~ "on its functioning in Yunohost (it's "
#~ "the place where one can warn about"
#~ " integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "Indications to show at key steps "
#~ "to manage the package : installation,"
#~ " update, message to the admin "
#~ "account. You usually don't have to "
#~ "fill them."
#~ msgstr ""
#~ msgid "Description courte (fr)"
#~ msgstr ""
#~ msgid "Français"
#~ msgstr ""
#~ msgid ""
#~ "doc/DESCRIPTION.md : A comprehensive "
#~ "presentation of the app, possibly "
#~ "listing the main features, possible "
#~ "warnings and specific details on its "
#~ "functioning in Yunohost (e.g. warning "
#~ "about integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_INSTALL.md : important info to "
#~ "be shown to the admin before "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_INSTALL.md : important info to "
#~ "be shown to the admin after "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_UPGRADE.md : important info to "
#~ "be shown to the admin before "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_UPGRADE.md : important info to "
#~ "be shown to the admin after "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid "doc/ADMIN.md : general tips on how to administrate this app"
#~ msgstr ""

View file

@ -0,0 +1,760 @@
# Spanish translations for PROJECT.
# Copyright (C) 2024 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-26 15:39+0200\n"
"PO-Revision-Date: 2024-03-31 20:02+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es\n"
"Language-Team: es <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
#: app.py:68 app.py:334
msgid "English"
msgstr ""
#: app.py:68 app.py:335
msgid "French"
msgstr ""
#: app.py:127
msgid "Select language"
msgstr ""
#: app.py:137
msgid "Application identifier (id)"
msgstr ""
#: app.py:138
msgid "Small caps and without spaces"
msgstr ""
#: app.py:146
msgid "App name"
msgstr ""
#: app.py:147
msgid "It's the application name, displayed in the user interface"
msgstr ""
#: app.py:157
msgid "Short description (en)"
msgstr ""
#: app.py:158 app.py:165
msgid ""
"Explain in a few words (10-15) why this app is useful or what it does "
"(the goal is to give a broad idea for the user browsing an hundred apps "
"long catalog"
msgstr ""
#: app.py:164
msgid "Short description (fr)"
msgstr ""
#: app.py:176
msgid "Version"
msgstr ""
#: app.py:182
msgid "Maintainer of the generated app"
msgstr ""
#: app.py:183
msgid "Usually you put your name here... If you're okay with it ;)"
msgstr ""
#: app.py:189
msgid "Minimal YunoHost version"
msgstr ""
#: app.py:190
msgid "Minimal YunoHost version for the application to work"
msgstr ""
#: app.py:199
msgid "Supported architectures"
msgstr ""
#: app.py:201
msgid "All architectures"
msgstr ""
#: app.py:212
msgid ""
"The app can be installed multiple times at the same time on the same "
"server"
msgstr ""
#: app.py:219
msgid "The app will be integrating LDAP"
msgstr ""
#: app.py:220
msgid ""
"Which means it's possible to use Yunohost credentials to log into this "
"app. 'LDAP' corresponds to the technology used by Yunohost to handle a "
"centralised user base. Bridging the app and Yunohost's LDAP often "
"requires to add the proper technical details in the app's configuration "
"file"
msgstr ""
#: app.py:224 app.py:238
msgid "No"
msgstr ""
#: app.py:225 app.py:237
msgid "Yes"
msgstr ""
#: app.py:226 app.py:239
msgid "Not relevant"
msgstr ""
#: app.py:232
msgid "The app will be integrated in Yunohost SSO (Single Sign On)"
msgstr ""
#: app.py:233
msgid ""
"Which means that people will be logged in the app after logging in "
"YunoHost's portal, without having to sign on specifically into this app."
msgstr ""
#: app.py:249
msgid "Licence"
msgstr ""
#: app.py:250
msgid ""
"You should check this on the upstream repository. The expected format is "
"a SPDX id listed in https://spdx.org/licenses/"
msgstr ""
#: app.py:257
msgid "Official website"
msgstr ""
#: app.py:258
msgid "Leave empty if there is no official website"
msgstr ""
#: app.py:265
msgid "Official app demo"
msgstr ""
#: app.py:266
msgid "Leave empty if there is no official demo"
msgstr ""
#: app.py:273
msgid "Admin documentation"
msgstr ""
#: app.py:274
msgid "Leave empty if there is no official admin doc"
msgstr ""
#: app.py:281
msgid "Usage documentation"
msgstr ""
#: app.py:282
msgid "Leave empty if there is no official user doc"
msgstr ""
#: app.py:289
msgid "Code repository"
msgstr ""
#: app.py:300
msgid ""
"Ask the URL where the app will be installed ('domain' and 'path' "
"variables)"
msgstr ""
#: app.py:305
msgid "Ask domain+path"
msgstr ""
#: app.py:308
msgid ""
"Ask only the domain (the app requires to be installed at the root of a "
"dedicated domain)"
msgstr ""
#: app.py:312
msgid "Do not ask (it isn't a webapp)"
msgstr ""
#: app.py:317
msgid "Ask who can access to the app"
msgstr ""
#: app.py:318
msgid ""
"In the users groups : by default at least 'visitors', 'all_users' et "
"'admins' exists. (It was previously the private/public app concept)"
msgstr ""
#: app.py:325
msgid "Ask who can access to the admin interface"
msgstr ""
#: app.py:326
msgid "In the case where the app has an admin interface"
msgstr ""
#: app.py:331
msgid "Supported languages"
msgstr ""
#: app.py:333
msgid "None / not relevant"
msgstr ""
#: app.py:336
msgid "Spanish"
msgstr ""
#: app.py:337
msgid "Italian"
msgstr ""
#: app.py:338
msgid "German"
msgstr ""
#: app.py:339
msgid "Chinese"
msgstr ""
#: app.py:340
msgid "Japanese"
msgstr ""
#: app.py:341
msgid "Danish"
msgstr ""
#: app.py:342
msgid "Portugese"
msgstr ""
#: app.py:343
msgid "Dutch"
msgstr ""
#: app.py:344
msgid "Russian"
msgstr ""
#: app.py:356
msgid "Application source code or executable"
msgstr ""
#: app.py:363
msgid "Sources sha256 checksum"
msgstr ""
#: app.py:371
msgid "Enable automatic update of sources (using a bot running every night)"
msgstr ""
#: app.py:372
msgid ""
"If the upstream software is hosted in one of the handled sources and "
"publishes proper releases or tags, the bot will create a pull request to "
"update the sources URL and checksum"
msgstr ""
#: app.py:394
msgid "Dependencies to be installed via apt (separated by comma and/or spaces)"
msgstr ""
#: app.py:403
msgid "Initialize an SQL database"
msgstr ""
#: app.py:413
msgid "Initialize a system user for this app"
msgstr ""
#: app.py:418
msgid "Initialize an installation folder for this app"
msgstr ""
#: app.py:419
msgid "By default it's /var/www/$app"
msgstr ""
#: app.py:424
msgid "Initialize a folder to store the app data"
msgstr ""
#: app.py:425
msgid "By default it's /var/yunohost.app/$app"
msgstr ""
#: app.py:433
msgid "App main technology"
msgstr ""
#: app.py:435
msgid "None / Static application"
msgstr ""
#: app.py:440
msgid "Other"
msgstr ""
#: app.py:447
msgid "Installation specific commands"
msgstr ""
#: app.py:448
msgid ""
"These commands are executed from the app installation folder (by default,"
" /var/www/$app) after the sources have been deployed. This field uses by "
"default a classic example based on the selected technology. You should "
"probably compare and adapt it according to the app installation "
"documentation"
msgstr ""
#: app.py:460
msgid "Use composer"
msgstr ""
#: app.py:461
msgid "Composer is a PHP dependencies manager used by some apps"
msgstr ""
#: app.py:472
msgid "NodeJS version"
msgstr ""
#: app.py:473
msgid "For example: 16.4, 18, 18.2, 20, 20.1, ..."
msgstr ""
#: app.py:480
msgid "Install and use Yarn"
msgstr ""
#: app.py:487
msgid "Command to start the app daemon (from systemd service)"
msgstr ""
#: app.py:488
msgid ""
"Corresponds to 'ExecStart' statement in systemd. You can use "
"'__INSTALL_DIR__' to refer to the install directory, or '__APP__' to "
"refer to the app id"
msgstr ""
#: app.py:500
msgid "The app uses a specific configuration file"
msgstr ""
#: app.py:501
msgid "Usually : .env, config.json, conf.ini, params.yml, ..."
msgstr ""
#: app.py:508
msgid "Name or file path to use"
msgstr ""
#: app.py:516
msgid "App configuration file pattern"
msgstr ""
#: app.py:517
msgid ""
"In this pattern, you can use the syntax __FOO_BAR__ which will "
"automatically replaced by the value of the variable $foo_bar"
msgstr ""
#: app.py:529
msgid ""
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly "
"listing the main features, possible warnings and specific details on its "
"functioning in Yunohost (e.g. warning about integration issues)."
msgstr ""
#: app.py:539
msgid ""
"doc/PRE_INSTALL.md: important info to be shown to the admin before "
"installing the app"
msgstr ""
#: app.py:540 app.py:548 app.py:556 app.py:564 app.py:572
msgid "Leave empty if not relevant"
msgstr ""
#: app.py:547
msgid ""
"doc/POST_INSTALL.md: important info to be shown to the admin after "
"installing the app"
msgstr ""
#: app.py:555
msgid ""
"doc/PRE_UPGRADE.md: important info to be shown to the admin before "
"upgrading the app"
msgstr ""
#: app.py:563
msgid ""
"doc/POST_UPGRADE.md: important info to be shown to the admin after "
"upgrading the app"
msgstr ""
#: app.py:571
msgid "doc/ADMIN.md: general tips on how to administrate this app"
msgstr ""
#: app.py:583
msgid "Handle app install URL change (change_url script)"
msgstr ""
#: app.py:586
msgid "Should changing the app URL be allowed ? (change_url change)"
msgstr ""
#: app.py:593
msgid "Use logrotate for the app logs"
msgstr ""
#: app.py:596
msgid ""
"If the app generates logs, this option permit to handle their archival. "
"Recommended."
msgstr ""
#: app.py:604
msgid "Protect the application against brute force attacks (via fail2ban)"
msgstr ""
#: app.py:609
msgid ""
"If the app generates failed connexions logs, this option allows to "
"automatically banish the related IP after a certain number of failed "
"password tries. Recommended."
msgstr ""
#: app.py:615
msgid "Add a CRON task for this application"
msgstr ""
#: app.py:616
msgid "Corresponds to some app periodic operations"
msgstr ""
#: app.py:620
msgid "Type the CRON file content"
msgstr ""
#: app.py:629
msgid "Regular expression for fail2ban"
msgstr ""
#: app.py:633
msgid "A regular expression"
msgstr ""
#: app.py:635
msgid ""
"Regular expression to check in the log file to activate failban (search "
"for a line that indicates a credentials error)."
msgstr ""
#: app.py:659
msgid "Generator mode"
msgstr ""
#: app.py:660
msgid ""
"In tutorial version, the generated app will contain additionnal comments "
"to ease the understanding. In steamlined version, the generated app will "
"only contain the necessary minimum."
msgstr ""
#: app.py:664
msgid "Streamlined version"
msgstr ""
#: app.py:665
msgid "Tutorial version"
msgstr ""
#: app.py:671
msgid "Previsualise"
msgstr ""
#: app.py:672
msgid "Download the .zip"
msgstr ""
#: app.py:674
msgid "Fill with demo values"
msgstr ""
#: app.py:677
msgid ""
"Generate a complete and functionnal minimalistic app that you can iterate"
" from"
msgstr ""
#: templates/base.html:5
msgid "YunoHost app generator"
msgstr ""
#: templates/index.html:28
msgid "Yunohost application generation form"
msgstr ""
#: templates/index.html:55
msgid "1/9 - General information"
msgstr ""
#: templates/index.html:70
msgid "2/9 - Upstream information"
msgstr ""
#: templates/index.html:73
msgid ""
"The word 'upstream' refers to the original project that develops and "
"maintains the app"
msgstr ""
#: templates/index.html:88
msgid "3/9 - Integration in YunoHost"
msgstr ""
#: templates/index.html:104
msgid "4/9 - Questions to ask during installation"
msgstr ""
#: templates/index.html:108
msgid "This part is meant to indicate the questions that will be asked."
msgstr ""
#: templates/index.html:110
msgid ""
"NB: only standard questions are asked here, it might be required to "
"complete it by hand using other questions as a guide."
msgstr ""
#: templates/index.html:124
msgid "5/9 - Resources to initialize"
msgstr ""
#: templates/index.html:128
msgid ""
"Technical elements configured before launching the 'real' app install "
"script. Usually : creating a system user, downloading app sources, "
"initialiser le dossier d'install et de données, install apt dependencies,"
" create a database, ..."
msgstr ""
#: templates/index.html:151
msgid "6/9 - Specific technology"
msgstr ""
#: templates/index.html:156
msgid ""
"You probably want to make sure to have 'phpX.Y-fpm' and others "
"'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y "
"being the php version you want to use)"
msgstr ""
#: templates/index.html:157
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that interfaces with PHP-FPM"
msgstr ""
#: templates/index.html:165
msgid ""
"You probably want to make sure to have 'python3' and 'python3-venv' "
"listed in the apt dependencies earlier. Other dependencies should be "
"installed inside a venv (cf the proposed install snippet)"
msgstr ""
#: templates/index.html:169
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that reverse-proxies to a systemd service using an internal port"
msgstr ""
#: templates/index.html:237
msgid "7/9 - App configuration"
msgstr ""
#: templates/index.html:261
msgid "8/9 - General and advanced documentation"
msgstr ""
#: templates/index.html:275
msgid "9/9 - Advanced options"
msgstr ""
#~ msgid "Maintener of the generated app"
#~ msgstr ""
#~ msgid "Commonly you put your name here... If you agree with it ;)"
#~ msgstr ""
#~ msgid ""
#~ "Which means it's possible to use "
#~ "Yunohost credential to connect. 'LDAP' "
#~ "corresponds to the technology used by"
#~ " Yunohost to handle a centralised "
#~ "user base. Bridging the APP and "
#~ "Yunohost LDAP often requires to fill "
#~ "some parameters in the app configuration"
#~ msgstr ""
#~ msgid ""
#~ "Which means that one connexion to "
#~ "Yunohost unlock the connexion to the "
#~ "software, without having to sign on "
#~ "specificaly into it. One only has "
#~ "to connect once (Single Sign On)"
#~ msgstr ""
#~ msgid "Activate the automated source update bot"
#~ msgstr ""
#~ msgid ""
#~ "If the software is available in "
#~ "one of the handled sources and "
#~ "publish releases or tags for its "
#~ "new updates, or for each new "
#~ "commit, a bot will provide an "
#~ "update with updated URL and checksum"
#~ msgstr ""
#~ msgid ""
#~ "Dependances to be installed via apt "
#~ "(separated by a quote and/or spaces)"
#~ msgstr ""
#~ msgid "Initialise a SQL database"
#~ msgstr ""
#~ msgid "Initialise a system user for this app"
#~ msgstr ""
#~ msgid "Initialise an installation folder for this app"
#~ msgstr ""
#~ msgid "Initialise a folder to store the app data"
#~ msgstr ""
#~ msgid ""
#~ "Type the content of DESCRIPTION.md file."
#~ " <br> Do not give the software "
#~ "name at the beginning, as it will"
#~ " be integrated an 'Overview' subpart"
#~ msgstr ""
#~ msgid ""
#~ "Type the DISCLAIMER.md file content, "
#~ "which list warnings and attention "
#~ "points."
#~ msgstr ""
#~ msgid "Type the PRE_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the POST_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the PRE_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the POST_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the ADMIN.md file content"
#~ msgstr ""
#~ msgid "2/9 - Informations about the application"
#~ msgstr ""
#~ msgid "The word upstream refers to the original project that maintains the app"
#~ msgstr ""
#~ msgid "5/9 - Ressources to initialise"
#~ msgstr ""
#~ msgid ""
#~ "A more complete presentation that the"
#~ " summary completed above, explaining to "
#~ "what correspond the software, the "
#~ "eventual warnings and specific details "
#~ "on its functioning in Yunohost (it's "
#~ "the place where one can warn about"
#~ " integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "Indications to show at key steps "
#~ "to manage the package : installation,"
#~ " update, message to the admin "
#~ "account. You usually don't have to "
#~ "fill them."
#~ msgstr ""
#~ msgid "Description courte (fr)"
#~ msgstr ""
#~ msgid "Français"
#~ msgstr ""
#~ msgid ""
#~ "doc/DESCRIPTION.md : A comprehensive "
#~ "presentation of the app, possibly "
#~ "listing the main features, possible "
#~ "warnings and specific details on its "
#~ "functioning in Yunohost (e.g. warning "
#~ "about integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_INSTALL.md : important info to "
#~ "be shown to the admin before "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_INSTALL.md : important info to "
#~ "be shown to the admin after "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_UPGRADE.md : important info to "
#~ "be shown to the admin before "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_UPGRADE.md : important info to "
#~ "be shown to the admin after "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid "doc/ADMIN.md : general tips on how to administrate this app"
#~ msgstr ""

View file

@ -0,0 +1,699 @@
# French translations for PROJECT.
# Copyright (C) 2024 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-26 15:39+0200\n"
"PO-Revision-Date: 2024-03-31 20:23+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
#: app.py:68 app.py:334
msgid "English"
msgstr "Anglais"
#: app.py:68 app.py:335
msgid "French"
msgstr "Français"
#: app.py:127
msgid "Select language"
msgstr "Langues supportées"
#: app.py:137
msgid "Application identifier (id)"
msgstr "Identifiant (id) de l'application"
#: app.py:138
msgid "Small caps and without spaces"
msgstr "En minuscule et sans espace"
#: app.py:146
msgid "App name"
msgstr "Nom de l'application"
#: app.py:147
msgid "It's the application name, displayed in the user interface"
msgstr ""
"Il s'agit du nom l'application, affiché dans les interfaces "
"utilisateur·ice·s"
#: app.py:157
msgid "Short description (en)"
msgstr "Description courte (en)"
#: app.py:158 app.py:165
msgid ""
"Explain in a few words (10-15) why this app is useful or what it does "
"(the goal is to give a broad idea for the user browsing an hundred apps "
"long catalog"
msgstr ""
"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)"
#: app.py:164
msgid "Short description (fr)"
msgstr "Description courte (fr)"
#: app.py:176
msgid "Version"
msgstr "Version"
#: app.py:182
msgid "Maintainer of the generated app"
msgstr "Mainteneur·euse de l'app YunoHost créée"
#: app.py:183
msgid "Usually you put your name here... If you're okay with it ;)"
msgstr "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)"
#: app.py:189
msgid "Minimal YunoHost version"
msgstr "Version YunoHost minimale"
#: app.py:190
msgid "Minimal YunoHost version for the application to work"
msgstr "Version minimale de Yunohost pour que l'application fonctionne."
#: app.py:199
msgid "Supported architectures"
msgstr "Architectures supportées"
#: app.py:201
msgid "All architectures"
msgstr "Toutes les architectures"
#: app.py:212
msgid ""
"The app can be installed multiple times at the same time on the same "
"server"
msgstr ""
"L'app pourra être installée simultanément plusieurs fois sur la même "
"machine"
#: app.py:219
msgid "The app will be integrating LDAP"
msgstr "L'app s'intègrera avec le LDAP"
#: app.py:220
msgid ""
"Which means it's possible to use Yunohost credentials to log into this "
"app. 'LDAP' corresponds to the technology used by Yunohost to handle a "
"centralised user base. Bridging the app and Yunohost's LDAP often "
"requires to add the proper technical details in the app's configuration "
"file"
msgstr ""
"C'est-à-dire pouvoir se connecter en utilisant ses identifiants YunoHost."
" 'LDAP' corresponds à la technologie utilisée par YunoHost comme base de "
"compte utilisateurs centralisée. L'interface entre l'app et le LDAP de "
"YunoHost nécessite le plus souvent de remplir des paramètres dans la "
"configuration de l'app"
#: app.py:224 app.py:238
msgid "No"
msgstr "Non"
#: app.py:225 app.py:237
msgid "Yes"
msgstr "Oui"
#: app.py:226 app.py:239
msgid "Not relevant"
msgstr "Non pertinent"
#: app.py:232
msgid "The app will be integrated in Yunohost SSO (Single Sign On)"
msgstr "L'app s'intègrera avec le SSO (Single Sign On) de YunoHost"
#: app.py:233
msgid ""
"Which means that people will be logged in the app after logging in "
"YunoHost's portal, without having to sign on specifically into this app."
msgstr ""
"Ce qui signifie que les personnes seront logguées dans l'app après s'être"
" connectées au portail YunoHost, sans avoir à se connecter spécifiquement"
" dans"
#: app.py:249
msgid "Licence"
msgstr "License"
#: app.py:250
msgid ""
"You should check this on the upstream repository. The expected format is "
"a SPDX id listed in https://spdx.org/licenses/"
msgstr ""
"Vous devriez chercher cela dans le dépôt du logiciel. Le format attendu "
"est un identifiant SPDX listé dans https://spdx.org/licenses/"
#: app.py:257
msgid "Official website"
msgstr "Site web officiel"
#: app.py:258
msgid "Leave empty if there is no official website"
msgstr "Laisser vide s'il n'y a pas de site officiel"
#: app.py:265
msgid "Official app demo"
msgstr "Démo officielle de l'app"
#: app.py:266
msgid "Leave empty if there is no official demo"
msgstr "Laisser vide s'il n'y a pas de démo officielle"
#: app.py:273
msgid "Admin documentation"
msgstr "Documentation d'administration"
#: app.py:274
msgid "Leave empty if there is no official admin doc"
msgstr "Laisser vide s'il n'y a pas de documentation d'administration officielle"
#: app.py:281
msgid "Usage documentation"
msgstr "Documentation d'utilisation"
#: app.py:282
msgid "Leave empty if there is no official user doc"
msgstr "Laisser vide s'il n'y a pas de documentation d'utilisation officielle"
#: app.py:289
msgid "Code repository"
msgstr "Dépôt de code"
#: app.py:300
msgid ""
"Ask the URL where the app will be installed ('domain' and 'path' "
"variables)"
msgstr ""
"Demander l'URL sur laquelle sera installée l'app (variables 'domain' et "
"'path')"
#: app.py:305
msgid "Ask domain+path"
msgstr "Demander le domaine+chemin"
#: app.py:308
msgid ""
"Ask only the domain (the app requires to be installed at the root of a "
"dedicated domain)"
msgstr ""
"Demander le domaine uniquement (l'app nécessite d'être installée à la "
"racine d'un domaine dédié à cette app)"
#: app.py:312
msgid "Do not ask (it isn't a webapp)"
msgstr "Ne pas demander (l'app n'est pas une webapp)"
#: app.py:317
msgid "Ask who can access to the app"
msgstr "Demander qui pourra accéder à l'app"
#: app.py:318
msgid ""
"In the users groups : by default at least 'visitors', 'all_users' et "
"'admins' exists. (It was previously the private/public app concept)"
msgstr ""
"Parmis les groupes d'utilisateurs : par défaut au moins 'visitors', "
"'all_users' et 'admins' existent. (Corresponds anciennement à la notion "
"d'app privée/publique)"
#: app.py:325
msgid "Ask who can access to the admin interface"
msgstr "Demander qui pourra accéder à l'interface d'admin"
#: app.py:326
msgid "In the case where the app has an admin interface"
msgstr "Ceci suppose a priori que l'app dispose d'une interface d'admin"
#: app.py:331
msgid "Supported languages"
msgstr "Langues supportées"
#: app.py:333
msgid "None / not relevant"
msgstr "Aucune / non pertinent"
#: app.py:336
msgid "Spanish"
msgstr "Espagnol"
#: app.py:337
msgid "Italian"
msgstr "Italien"
#: app.py:338
msgid "German"
msgstr "Allemand"
#: app.py:339
msgid "Chinese"
msgstr "Chinois"
#: app.py:340
msgid "Japanese"
msgstr "Japonais"
#: app.py:341
msgid "Danish"
msgstr "Danois"
#: app.py:342
msgid "Portugese"
msgstr "Portugais"
#: app.py:343
msgid "Dutch"
msgstr "Néerlandais"
#: app.py:344
msgid "Russian"
msgstr "Russe"
#: app.py:356
msgid "Application source code or executable"
msgstr "Code source ou exécutable de l'application"
#: app.py:363
msgid "Sources sha256 checksum"
msgstr "Empreinte sha256 des sources"
#: app.py:371
msgid "Enable automatic update of sources (using a bot running every night)"
msgstr ""
"Activer le robot de mise à jour automatique des sources (via un robot "
"chaque nuit)"
#: app.py:372
msgid ""
"If the upstream software is hosted in one of the handled sources and "
"publishes proper releases or tags, the bot will create a pull request to "
"update the sources URL and checksum"
msgstr ""
"Si le logiciel est disponible sur une des sources prises en charge et "
"publie des releases ou des tags pour ses nouvelles versions, un robot "
"proposera automatiquement des mises à jour de l'URL et de la checksum."
#: app.py:394
msgid "Dependencies to be installed via apt (separated by comma and/or spaces)"
msgstr "Dépendances à installer via apt (séparées par des virgules et/ou espaces)"
#: app.py:403
msgid "Initialize an SQL database"
msgstr "Initialiser une base de données SQL"
#: app.py:413
msgid "Initialize a system user for this app"
msgstr "Initialiser un utilisateur système pour cet app"
#: app.py:418
msgid "Initialize an installation folder for this app"
msgstr "Initialiser un dossier d'installation de l'app"
#: app.py:419
msgid "By default it's /var/www/$app"
msgstr "Par défaut il s'agit de /var/www/$app"
#: app.py:424
msgid "Initialize a folder to store the app data"
msgstr "Initialiser un dossier destiné à stocker les données de l'app"
#: app.py:425
msgid "By default it's /var/yunohost.app/$app"
msgstr "Par défaut il s'agit de /home/yunohost.app/$app"
#: app.py:433
msgid "App main technology"
msgstr "Technologie principale de l'app"
#: app.py:435
msgid "None / Static application"
msgstr "Aucune / application statique"
#: app.py:440
msgid "Other"
msgstr "Autre"
#: app.py:447
msgid "Installation specific commands"
msgstr "Commandes spécifiques d'installation"
#: app.py:448
msgid ""
"These commands are executed from the app installation folder (by default,"
" /var/www/$app) after the sources have been deployed. This field uses by "
"default a classic example based on the selected technology. You should "
"probably compare and adapt it according to the app installation "
"documentation"
msgstr ""
"Ces commandes seront éxécutées depuis le répertoire d'installation de "
"l'app (par défaut, /var/www/$app) après que les sources aient été "
"déployées. Le champ est pré-rempli avec un exemple classique basé sur la "
"technologie sélectionnée. Vous devriez sans-doute le comparer et "
"l'adapter en fonction de la documentation d'installation de l'app."
#: app.py:460
msgid "Use composer"
msgstr "Utiliser composer"
#: app.py:461
msgid "Composer is a PHP dependencies manager used by some apps"
msgstr "Composer est un gestionnaire de dépendance PHP utilisé par certaines apps"
#: app.py:472
msgid "NodeJS version"
msgstr "Version de NodeJS"
#: app.py:473
msgid "For example: 16.4, 18, 18.2, 20, 20.1, ..."
msgstr "Par exemple: 16.4, 18, 18.2, 20, 20.1, ..."
#: app.py:480
msgid "Install and use Yarn"
msgstr "Installer et utiliser Yarn"
#: app.py:487
msgid "Command to start the app daemon (from systemd service)"
msgstr "Commande pour lancer le daemon de l'app (depuis le service systemd)"
#: app.py:488
msgid ""
"Corresponds to 'ExecStart' statement in systemd. You can use "
"'__INSTALL_DIR__' to refer to the install directory, or '__APP__' to "
"refer to the app id"
msgstr ""
"Correspond à l'intruction 'ExecStart' dans systemd. Vous pouvez utiliser "
"'__INSTALL_DIR__' pour faire référence directory, our '__APP__' pour "
"l'identifiant de l'application"
#: app.py:500
msgid "The app uses a specific configuration file"
msgstr "L'app utilise un fichier de configuration spécifique"
#: app.py:501
msgid "Usually : .env, config.json, conf.ini, params.yml, ..."
msgstr "Typiquement : .env, config.json, conf.ini, params.yml, ..."
#: app.py:508
msgid "Name or file path to use"
msgstr "Nom ou chemin du fichier à utiliser"
#: app.py:516
msgid "App configuration file pattern"
msgstr "Modèle de fichier de configuration de l'app"
#: app.py:517
msgid ""
"In this pattern, you can use the syntax __FOO_BAR__ which will "
"automatically replaced by the value of the variable $foo_bar"
msgstr ""
"Dans ce modèle, vous pouvez utilisez la syntaxe __FOO_BAR__ qui sera "
"automatiquement remplacé par la valeur de la variable $foo_bar"
#: app.py:529
msgid ""
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly "
"listing the main features, possible warnings and specific details on its "
"functioning in Yunohost (e.g. warning about integration issues)."
msgstr ""
"doc/DESCRIPTION.md : Une présentation plus complète que le résumé rempli "
"plus haut de ce à quoi correspond le logiciel, et les avertissements et "
"précisions éventuelles sur son fonctionnement dans Yunohost (c'est "
"l'endroit où l'on signale des problèmes d'intégrations)."
#: app.py:539
msgid ""
"doc/PRE_INSTALL.md: important info to be shown to the admin before "
"installing the app"
msgstr ""
"doc/PRE_INSTALL.md : info importantes à montrer aux admins avant "
"l'installation de l'app"
#: app.py:540 app.py:548 app.py:556 app.py:564 app.py:572
msgid "Leave empty if not relevant"
msgstr "Laisser vide si pas pertinent"
#: app.py:547
msgid ""
"doc/POST_INSTALL.md: important info to be shown to the admin after "
"installing the app"
msgstr ""
"doc/POST_INSTALL.md : infos importantes à montrer aux admins après "
"l'installation de l'app"
#: app.py:555
msgid ""
"doc/PRE_UPGRADE.md: important info to be shown to the admin before "
"upgrading the app"
msgstr ""
"doc/PRE_UPGRADE.md : infos importantes à montrer aux admins avant la mise"
" à jour de l'app"
#: app.py:563
msgid ""
"doc/POST_UPGRADE.md: important info to be shown to the admin after "
"upgrading the app"
msgstr ""
"doc/POST_UPGRADE.md : infos importantes à montrer aux admins après la "
"mise à jour de l'app"
#: app.py:571
msgid "doc/ADMIN.md: general tips on how to administrate this app"
msgstr "doc/ADMIN.md : indications générales pour administrer l'app"
#: app.py:583
msgid "Handle app install URL change (change_url script)"
msgstr "Gérer le changement d'URL d'installation (script change_url)"
#: app.py:586
msgid "Should changing the app URL be allowed ? (change_url change)"
msgstr ""
"Faut-il permettre le changement d'URL pour l'application ? (fichier "
"change_url)"
#: app.py:593
msgid "Use logrotate for the app logs"
msgstr "Utiliser logrotate pour les journaux de l'app"
#: app.py:596
msgid ""
"If the app generates logs, this option permit to handle their archival. "
"Recommended."
msgstr ""
"Si l'application genère des journaux (log), cette option permet d'en "
"gérer l'archivage. Recommandé."
#: app.py:604
msgid "Protect the application against brute force attacks (via fail2ban)"
msgstr "Protéger l'application des attaques par force brute (via fail2ban)"
#: app.py:609
msgid ""
"If the app generates failed connexions logs, this option allows to "
"automatically banish the related IP after a certain number of failed "
"password tries. Recommended."
msgstr ""
"Si l'application genère des journaux (log) d'erreurs de connexion, cette "
"option permet de bannir automatiquement les IP au bout d'un certain "
"nombre d'essais de mot de passe. Recommandé."
#: app.py:615
msgid "Add a CRON task for this application"
msgstr "Ajouter une tâche CRON pour cette application"
#: app.py:616
msgid "Corresponds to some app periodic operations"
msgstr "Corresponds à des opérations périodiques de l'application"
#: app.py:620
msgid "Type the CRON file content"
msgstr "Saisissez le contenu du fichier CRON"
#: app.py:629
msgid "Regular expression for fail2ban"
msgstr "Expression régulière pour fail2ban"
#: app.py:633
msgid "A regular expression"
msgstr "Une expression régulière"
#: app.py:635
msgid ""
"Regular expression to check in the log file to activate failban (search "
"for a line that indicates a credentials error)."
msgstr ""
"Expression régulière à vérifier dans le journal pour que fail2ban "
"s'active (cherchez une ligne qui indique une erreur d'identifiants de "
"connexion)."
#: app.py:659
msgid "Generator mode"
msgstr "Mode du générateur"
#: app.py:660
msgid ""
"In tutorial version, the generated app will contain additionnal comments "
"to ease the understanding. In steamlined version, the generated app will "
"only contain the necessary minimum."
msgstr ""
"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."
#: app.py:664
msgid "Streamlined version"
msgstr "Version épurée"
#: app.py:665
msgid "Tutorial version"
msgstr "Version tutoriel"
#: app.py:671
msgid "Previsualise"
msgstr "Prévisualiser"
#: app.py:672
msgid "Download the .zip"
msgstr "Télécharger le .zip"
#: app.py:674
msgid "Fill with demo values"
msgstr "Remplir avec des valeurs de démonstration"
#: app.py:677
msgid ""
"Generate a complete and functionnal minimalistic app that you can iterate"
" from"
msgstr ""
"Générer une application minimaliste complète et fonctionnelle à partir de"
" laquelle itérer"
#: templates/base.html:5
msgid "YunoHost app generator"
msgstr "Générateur d'app YunoHost"
#: templates/index.html:28
msgid "Yunohost application generation form"
msgstr "Formulaire de génération d'une application Yunohost"
#: templates/index.html:55
msgid "1/9 - General information"
msgstr "1/9 - Informations générales"
#: templates/index.html:70
msgid "2/9 - Upstream information"
msgstr "2/9 - Informations sur l'upstream"
#: templates/index.html:73
msgid ""
"The word 'upstream' refers to the original project that develops and "
"maintains the app"
msgstr ""
"Le terme 'upstream' désigne le projet original qui développe et maintient"
" l'app"
#: templates/index.html:88
msgid "3/9 - Integration in YunoHost"
msgstr "3/9 - Intégration dans YunoHost"
#: templates/index.html:104
msgid "4/9 - Questions to ask during installation"
msgstr "4/9 - Questions à poser pendant l'installation"
#: templates/index.html:108
msgid "This part is meant to indicate the questions that will be asked."
msgstr "Cette partie sert à indiquer les questions qui devront être posées."
#: templates/index.html:110
msgid ""
"NB: only standard questions are asked here, it might be required to "
"complete it by hand using other questions as a guide."
msgstr ""
"NB: seules des questions standard sont proposées ici, il faudra "
"éventuellement compléter à la main en suivant le modèle des autres "
"questions."
#: templates/index.html:124
msgid "5/9 - Resources to initialize"
msgstr "5/9 - Ressources à initialiser"
#: templates/index.html:128
msgid ""
"Technical elements configured before launching the 'real' app install "
"script. Usually : creating a system user, downloading app sources, "
"initialiser le dossier d'install et de données, install apt dependencies,"
" create a database, ..."
msgstr ""
"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ées, "
"..."
#: templates/index.html:151
msgid "6/9 - Specific technology"
msgstr "6/9 - Technologie spécifique"
#: templates/index.html:156
msgid ""
"You probably want to make sure to have 'phpX.Y-fpm' and others "
"'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y "
"being the php version you want to use)"
msgstr ""
"Vous voulez probablement vous assurer d'avoir 'phpX.Y-fpm' and autres "
"librairies 'phpX.Y-foobar' listées dans les dépendances apt (X.Y étant la"
" version de PHP que vous voulez utiliser)"
#: templates/index.html:157
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that interfaces with PHP-FPM"
msgstr ""
"Le brouillon de l'application inclura une configuration nginx qui "
"s'interface avec PHP-FPM"
#: templates/index.html:165
msgid ""
"You probably want to make sure to have 'python3' and 'python3-venv' "
"listed in the apt dependencies earlier. Other dependencies should be "
"installed inside a venv (cf the proposed install snippet)"
msgstr ""
"Vous voulez probablement vous assurer d'avoir 'python3' et 'python3-venv'"
" listés dans les dépendances apt. Les autres dépendences devront être "
"installées dans un venv"
#: templates/index.html:169
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that reverse-proxies to a systemd service using an internal port"
msgstr ""
"Le brouillon de l'application inclura une configuration nginx qui "
"redirige vers le service systemd en utilisant un port interne"
#: templates/index.html:237
msgid "7/9 - App configuration"
msgstr "7/9 - Configuration de l'app"
#: templates/index.html:261
msgid "8/9 - General and advanced documentation"
msgstr "8/9 - Documentation générale et avancée"
#: templates/index.html:275
msgid "9/9 - Advanced options"
msgstr "9/9 - Options avancées"
#~ msgid "Français"
#~ msgstr "French"

View file

@ -34,6 +34,10 @@ def notify(message: str, channel: str, markdown: bool = False) -> None:
try:
subprocess.call(command, stdout=subprocess.DEVNULL)
except FileNotFoundError:
logging.warning(
"The logging sender tool /var/www/webhooks/matrix-commander does not exist."
)
except subprocess.CalledProcessError as e:
logging.warning(
f"""Could not send a notification on {channel}.
@ -48,7 +52,7 @@ class LogSenderHandler(logging.Handler):
self.is_logging = False
def emit(self, record: logging.LogRecord) -> None:
msg = f"[Apps tools error] {record.msg}"
msg = f"[Apps tools error] {record.message}"
notify(msg, "dev")
@classmethod

View file

@ -8,7 +8,6 @@ from enum import Enum
from typing import Any, Optional, Union
import re
import sys
import textwrap
from pathlib import Path
from functools import cache
from datetime import datetime
@ -227,7 +226,8 @@ class AppAutoUpdater:
# Default message
pr_title = commit_msg = "Upgrade sources"
branch_name = "ci-auto-update-sources"
date = datetime.now().strftime("%y%m%d")
branch_name = f"ci-auto-update-sources-{date}"
for source, infos in self.sources.items():
update = self.get_source_update(source, infos)
@ -447,6 +447,7 @@ class AppAutoUpdater:
) -> Optional[tuple[str, Union[str, dict[str, str]], str]]:
upstream = autoupdate.get("upstream", self.main_upstream).strip("/")
version_re = autoupdate.get("version_regex", None)
allow_prereleases = autoupdate.get("allow_prereleases", False)
_, remote_type, revision_type = strategy.split("_")
api: Union[GithubAPI, GitlabAPI, GiteaForgejoAPI]
@ -462,10 +463,16 @@ class AppAutoUpdater:
if revision_type == "release":
releases: dict[str, dict[str, Any]] = {
release["tag_name"]: release
for release in api.releases()
if not release["draft"] and not release["prerelease"]
release["tag_name"]: release for release in api.releases()
}
if not allow_prereleases:
releases = {
name: info
for name, info in releases.items()
if not info["draft"] and not info["prerelease"]
}
latest_version_orig, latest_version = self.relevant_versions(
list(releases.keys()), self.app_id, version_re
)
@ -718,7 +725,7 @@ def main() -> None:
if apps_failed:
paste_message += f"\n{'=' * 80}\nApps failed:"
matrix_message += f"\n- {len(apps_failed)} failed apps updates: {', '.join(str(app) for app in apps_failed.keys())}"
matrix_message += f"\n- {len(apps_failed)} failed apps updates: {', '.join(str(app) for app in apps_failed.keys())}\n"
for app, logs in apps_failed.items():
paste_message += f"\n{'='*40}\n{app}\n{'-'*40}\n{logs[0]}\n{logs[1]}\n\n"

View file

@ -1,16 +0,0 @@
# Auto-README generation
Browses all repositories in YunoHost-Apps organization, and updates `updater.yml` with latest actions versions.
### Initial install
```
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
This script requires the following files:
- `.github_token` containing a token with `public.repo` and `workflow` permission
- `.github_login` containing the author's username
- `.github_email` containing the author's email address

View file

@ -1,139 +0,0 @@
#!/usr/bin/env python3
import json
import os
import re
import sys
import time
import urllib.request
import github
from github import Github
# Debug
from rich.traceback import install
install(width=150, show_locals=True, locals_max_length=None, locals_max_string=None)
#####
#
# CONFIG
#
#####
# API token for yunohost-bot, need public.repo permission
g = Github(open(".github_token").read().strip())
# Path to the file to be updated
path = ".github/workflows/updater.yml"
# Title of the PR
title = "[autopatch] Upgrade auto-updater"
# Body of the PR message
body = """
Auto-updater actions need upgrading to continue working:
- actions/checkout@v3
- peter-evans/create-pull-request@v4
"""
# Author of the commit
author = github.InputGitAuthor(
open(".github_login").read().strip(), open(".github_email").read().strip()
)
# Name of the branch created for the PR
new_branch = "upgrade-auto-updater"
#####
#
# CACHE
#
#####
with open("processed.txt") as f:
processed = f.read().splitlines()
#####
#
# CRAWL REPOSITORIES
#
#####
u = g.get_user("yunohost-bot")
org = g.get_organization("yunohost-apps")
# For each repositories belonging to the bot (user `u`)
i = 0
for repo in org.get_repos():
if repo.full_name not in processed:
# Determine base branch, either `testing` or default branch
try:
base_branch = repo.get_branch("testing").name
except:
base_branch = repo.default_branch
# Make sure the repository has an auto-updater
try:
repo.get_contents(path, ref="refs/heads/" + base_branch)
except:
with open("processed.txt", "a") as pfile:
pfile.write(repo.full_name + "\n")
time.sleep(1.5)
continue
# Process the repo
print("Processing " + repo.full_name)
try:
# Get the commit base for the new branch, and create it
commit_sha = repo.get_branch(base_branch).commit.sha
new_branch_ref = repo.create_git_ref(
ref="refs/heads/" + new_branch, sha=commit_sha
)
except:
new_branch_ref = repo.get_git_ref(ref="heads/" + new_branch)
# Get current file contents
contents = repo.get_contents(path, ref=new_branch_ref.ref)
# Update the file
updater_yml = contents.decoded_content.decode("unicode_escape")
updater_yml = re.sub(
r"(?m)uses: actions/checkout@v[\d]+",
"uses: actions/checkout@v3",
updater_yml,
)
updater_yml = re.sub(
r"(?m)uses: peter-evans/create-pull-request@v[\d]+",
"uses: peter-evans/create-pull-request@v4",
updater_yml,
)
updated = repo.update_file(
contents.path,
message=title,
content=updater_yml,
sha=contents.sha,
branch=new_branch,
author=author,
)
# Wait a bit to preserve the API rate limit
time.sleep(1.5)
# Open the PR
pr = repo.create_pull(
title="Upgrade auto-updater", body=body, head=new_branch, base=base_branch
)
print(repo.full_name + " updated with PR #" + str(pr.id))
i = i + 1
# Wait a bit to preserve the API rate limit
time.sleep(1.5)
with open("processed.txt", "a") as pfile:
pfile.write(repo.full_name + "\n")
print("Done. " + str(i) + " repos processed")

View file

@ -1,3 +0,0 @@
rich
PyGithub

View file

@ -65,7 +65,7 @@ def __build_app_dict(data) -> Optional[tuple[str, dict[str, Any]]]:
try:
return name, build_app_dict(name, info)
except Exception as err:
logging.error("Error while updating %s: %s", name, err)
logging.error("[List builder] Error while updating %s: %s", name, err)
def build_base_catalog(nproc: int):

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-25 01:09+0100\n"
"POT-Creation-Date: 2024-03-31 19:33+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -30,7 +30,7 @@ msgstr ""
msgid ""
"Copy this app before working on it, using the ['Use this "
"template'](https://github.com/new?template_name=example_ynh&template_owner=YunoHost)"
" button on the Github repo."
" button on the Github repo"
msgstr ""
#: templates/README.md.j2:5
@ -40,13 +40,13 @@ msgstr ""
#: templates/README.md.j2:6
msgid ""
"Edit the `install`, `upgrade`, `remove`, `backup` and `restore` scripts, "
"and any relevant conf files in `conf/`."
"and any relevant conf files in `conf/`"
msgstr ""
#: templates/README.md.j2:7
msgid ""
"Using the [script helpers "
"documentation.](https://yunohost.org/packaging_apps_helpers)"
"documentation](https://yunohost.org/packaging_apps_helpers)"
msgstr ""
#: templates/README.md.j2:8
@ -56,142 +56,153 @@ msgid ""
msgstr ""
#: templates/README.md.j2:9
msgid ""
"Add a `LICENSE` file for the package. NB: this LICENSE file is not meant "
"to necessarily be the LICENSE of the upstream app - it is only the "
"LICENSE you want this package's code to published with ;). We recommend "
"to use [the AGPL-3](https://www.gnu.org/licenses/agpl-3.0.txt)."
msgid "Add a `LICENSE` file for the package."
msgstr ""
#: templates/README.md.j2:10
msgid "Edit files under the `doc/` directory"
msgid ""
"NB: this `LICENSE` file is not meant to necessarily be the same LICENSE "
"as the upstream app - it is only the LICENSE you want this package's code"
" to published with and you can choose it freely! (If you don't know which"
" to choose, we recommend [the "
"AGPL-3](https://www.gnu.org/licenses/agpl-3.0.txt))"
msgstr ""
#: templates/README.md.j2:11
msgid ""
"Edit files under the `doc/` directory ([see the page about documenting "
"packages](https://yunohost.org/packaging_app_doc))"
msgstr ""
#: templates/README.md.j2:12
msgid ""
"The `README.md` files are to be automatically generated by "
"<https://github.com/YunoHost/apps/tree/master/tools/readme_generator>"
msgstr ""
#: templates/README.md.j2:17
#: templates/README.md.j2:18
msgid ""
"N.B.: This README was automatically generated by "
"<https://github.com/YunoHost/apps/tree/master/tools/readme_generator>\n"
"It shall NOT be edited by hand."
msgstr ""
#: templates/README.md.j2:21
#: templates/README.md.j2:22
#, python-format
msgid "%(application_name)s for YunoHost"
msgstr ""
#: templates/README.md.j2:23
#: templates/README.md.j2:24
msgid "Integration level"
msgstr ""
#: templates/README.md.j2:23
#: templates/README.md.j2:24
msgid "Working status"
msgstr ""
#: templates/README.md.j2:23
#: templates/README.md.j2:24
msgid "Maintenance status"
msgstr ""
#: templates/README.md.j2:25
#: templates/README.md.j2:26
#, python-format
msgid "Install %(application_name)s with YunoHost"
msgstr ""
#: templates/README.md.j2:27
msgid "Read this README is other languages."
#: templates/README.md.j2:28
msgid "Read this README in other languages."
msgstr ""
#: templates/README.md.j2:29
#: templates/README.md.j2:30
#, python-format
msgid ""
"This package allows you to install %(application_name)s quickly and "
"simply on a YunoHost server.\n"
"If you don't have YunoHost, please consult [the "
"guide](https://yunohost.org/#/install) to learn how to install it."
"simply on a YunoHost server."
msgstr ""
#: templates/README.md.j2:32
#: templates/README.md.j2:31
msgid ""
"If you don't have YunoHost, please consult [the "
"guide](https://yunohost.org/install) to learn how to install it."
msgstr ""
#: templates/README.md.j2:33
msgid "Overview"
msgstr ""
#: templates/README.md.j2:35
#: templates/README.md.j2:37
msgid "Shipped version:"
msgstr ""
#: templates/README.md.j2:39
#: templates/README.md.j2:41
msgid "Demo:"
msgstr ""
#: templates/README.md.j2:43
#: templates/README.md.j2:45
msgid "Screenshots"
msgstr ""
#: templates/README.md.j2:46
#: templates/README.md.j2:48
#, python-format
msgid "Screenshot of %(application_name)s"
msgstr ""
#: templates/README.md.j2:51
#: templates/README.md.j2:53
msgid "Disclaimers / important information"
msgstr ""
#: templates/README.md.j2:57
#: templates/README.md.j2:59
msgid "Antifeatures"
msgstr ""
#: templates/README.md.j2:64
#: templates/README.md.j2:66
msgid "Documentation and resources"
msgstr ""
#: templates/README.md.j2:66
#: templates/README.md.j2:68
msgid "Official app website:"
msgstr ""
#: templates/README.md.j2:68
#: templates/README.md.j2:70
msgid "Official user documentation:"
msgstr ""
#: templates/README.md.j2:70
#: templates/README.md.j2:72
msgid "Official admin documentation:"
msgstr ""
#: templates/README.md.j2:72
#: templates/README.md.j2:74
msgid "Upstream app code repository:"
msgstr ""
#: templates/README.md.j2:74
#: templates/README.md.j2:76
msgid "YunoHost Store:"
msgstr ""
#: templates/README.md.j2:75
#: templates/README.md.j2:77
msgid "Report a bug:"
msgstr ""
#: templates/README.md.j2:77
#: templates/README.md.j2:79
msgid "Developer info"
msgstr ""
#: templates/README.md.j2:79
#: templates/README.md.j2:81
#, python-format
msgid ""
"Please send your pull request to the [testing "
"branch](%(testing_branch_url)s)"
"Please send your pull request to the [`testing` "
"branch](%(testing_branch_url)s)."
msgstr ""
#: templates/README.md.j2:82
msgid "To try the testing branch, please proceed like that:"
#: templates/README.md.j2:83
msgid "To try the `testing` branch, please proceed like that:"
msgstr ""
#: templates/README.md.j2:86
#: templates/README.md.j2:87
msgid "or"
msgstr ""
#: templates/README.md.j2:90
#: templates/README.md.j2:91
msgid "More info regarding app packaging:"
msgstr ""

Some files were not shown because too many files have changed in this diff Show more