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

Merge branch 'update_app_levels' of https://github.com/YunoHost/apps into update_app_levels

This commit is contained in:
Éric Gaspar 2024-05-17 20:19:08 +02:00
commit 2c7ca48e05
56 changed files with 431 additions and 123 deletions

View file

@ -150,7 +150,7 @@ url = "https://github.com/YunoHost-Apps/archivebox_ynh"
[archivist]
added_date = 1674232499 # 2023/01/20
category = "system_tools"
level = 6
level = 7
state = "working"
subtags = [ "backup" ]
url = "https://github.com/YunoHost-Apps/archivist_ynh"
@ -205,7 +205,7 @@ url = "https://github.com/YunoHost-Apps/baikal_ynh"
[bazarr]
added_date = 1674232499 # 2023/01/20
category = "multimedia"
level = 6
level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/bazarr_ynh"
@ -440,7 +440,7 @@ url = "https://github.com/YunoHost-Apps/civicrm_drupal7_ynh"
[cjdns]
added_date = 1703094732 # 2023/12/20
category = "system_tools"
level = 0
level = 6
state = "working"
subtags = [ "network" ]
url = "https://github.com/YunoHost-Apps/cjdns_ynh"
@ -778,7 +778,7 @@ url = "https://github.com/YunoHost-Apps/django-fmd_ynh"
[django-for-runners]
added_date = 1674232499 # 2023/01/20
category = "small_utilities"
level = 6
level = 3
state = "working"
url = "https://github.com/YunoHost-Apps/django-for-runners_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 = 3
level = 8
state = "working"
subtags = [ "programming" ]
url = "https://github.com/YunoHost-Apps/django_example_ynh"
@ -900,7 +900,7 @@ url = "https://github.com/YunoHost-Apps/elabftw_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "not-totally-free-upstream" ]
category = "dev"
level = 6
level = 8
state = "working"
subtags = [ "programming" ]
url = "https://github.com/YunoHost-Apps/elasticsearch7_ynh"
@ -1142,7 +1142,7 @@ url = "https://github.com/YunoHost-Apps/flarum_ynh"
added_date = 1714137502 # 2024/04/26
antifeatures = [ "alpha-software", "arbitrary-limitations" ]
category = "publishing"
level = 6
level = 7
potential_alternative_to = [ "ClassifiedAds.com", "kleinanzeigen.de" ]
state = "working"
subtags = [ "ecommerce" ]
@ -1471,7 +1471,7 @@ url = "https://github.com/YunoHost-Apps/guacamole_ynh"
[h5ai]
added_date = 1674232499 # 2023/01/20
category = "small_utilities"
level = 0
level = 6
state = "working"
url = "https://github.com/YunoHost-Apps/h5ai_ynh"
@ -2268,7 +2268,7 @@ url = "https://github.com/YunoHost-Apps/mautrix_facebook_ynh"
[mautrix_signal]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 0
level = 3
potential_alternative_to = [ "Signal" ]
state = "working"
subtags = [ "chat" ]
@ -2277,7 +2277,7 @@ url = "https://github.com/YunoHost-Apps/mautrix_signal_ynh"
[mautrix_telegram]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 0
level = 8
potential_alternative_to = [ "Telegram" ]
state = "working"
subtags = [ "chat" ]
@ -2609,7 +2609,7 @@ url = "https://github.com/YunoHost-Apps/nocodb_ynh"
[nodebb]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 8
level = 6
state = "working"
subtags = [ "forum" ]
url = "https://github.com/YunoHost-Apps/nodebb_ynh"
@ -2856,7 +2856,7 @@ url = "https://github.com/YunoHost-Apps/peertube_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "not-totally-free-upstream" ]
category = "social_media"
level = 7
level = 1
state = "working"
subtags = [ "videos" ]
url = "https://github.com/YunoHost-Apps/peertube-search-index_ynh"
@ -3090,7 +3090,7 @@ url = "https://github.com/YunoHost-Apps/plateau_ynh"
[pleroma]
added_date = 1674232499 # 2023/01/20
category = "social_media"
level = 6
level = 8
potential_alternative_to = [ "X" ]
state = "working"
subtags = [ "microblogging" ]
@ -3132,7 +3132,7 @@ url = "https://github.com/YunoHost-Apps/prestashop_ynh"
[prettynoemiecms]
added_date = 1674232499 # 2023/01/20
category = "publishing"
level = 8
level = 2
potential_alternative_to = [ "Wix" ]
state = "working"
subtags = [ "website" ]
@ -3368,7 +3368,7 @@ url = "https://github.com/YunoHost-Apps/rocketchat_ynh"
[roundcube]
added_date = 1674232499 # 2023/01/20
category = "communication"
level = 8
level = 2
potential_alternative_to = [ "GMail", "Hotmail", "Microsoft Outlook", "Yahoo! Mail" ]
state = "working"
subtags = [ "email" ]
@ -3430,7 +3430,7 @@ url = "https://github.com/YunoHost-Apps/satdress_ynh"
[scovie]
added_date = 1685183203 # 2023/05/27
category = "publishing"
level = 8
level = 3
state = "working"
subtags = [ "website" ]
url = "https://github.com/YunoHost-Apps/scovie_ynh"
@ -3479,7 +3479,7 @@ url = "https://github.com/YunoHost-Apps/searx_ynh"
[searxng]
added_date = 1678310393 # 2023/03/08
category = "small_utilities"
level = 8
level = 6
potential_alternative_to = [ "Bing", "DuckDuckGo", "Google", "SearX", "Yahoo" ]
state = "working"
url = "https://github.com/YunoHost-Apps/searxng_ynh"
@ -4283,6 +4283,14 @@ level = 8
state = "working"
url = "https://github.com/YunoHost-Apps/yacy_ynh"
[yarr]
added_date = 1715771420 # 2024/05/15
category = "reading"
level = 7
state = "working"
subtags = [ "rssreader" ]
url = "https://github.com/YunoHost-Apps/yarr_ynh"
[yellow]
added_date = 1674232499 # 2023/01/20
category = "publishing"
@ -4375,7 +4383,7 @@ url = "https://github.com/YunoHost-Apps/zeronet_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "non-free-network", "not-totally-free-upstream" ]
category = "system_tools"
level = 8
level = 0
state = "working"
subtags = [ "network" ]
url = "https://github.com/YunoHost-Apps/zerotier_ynh"
@ -4402,7 +4410,7 @@ url = "https://github.com/YunoHost-Apps/zipline_ynh"
added_date = 1674232499 # 2023/01/20
antifeatures = [ "non-free-dependencies" ]
category = "system_tools"
level = 6
level = 8
state = "working"
subtags = [ "network" ]
url = "https://github.com/YunoHost-Apps/ztncui_ynh"

12
cron
View file

@ -1,8 +1,14 @@
# Every 4 hours
0 */4 * * * root /bin/bash __BASEDIR__/rebuild.sh
0 */4 * * * root /bin/bash __BASEDIR__/maintenance.sh rebuild_catalog
# Everyday at 01:30 UTC
30 1 * * * root /bin/bash __BASEDIR__/sourcesautoupdate.sh
30 1 * * * root /bin/bash __BASEDIR__/maintenance.sh autoupdate_app_sources
# Every friday at 6 PM UTC
0 17 * * 5 root /usr/bin/python3 __BASEDIR__/tools/update_app_levels/update_app_levels.py
0 17 * * 5 root /bin/bash __BASEDIR__/maintenance.sh update_app_levels
# Every 6 hours
0 */6 * * * root /bin/bash __BASEDIR__/maintenance.sh fetch_main_dashboard
# Every day at 2AM
0 2 * * * root /bin/bash __BASEDIR__/maintenance.sh fetch_level_history

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
logos/yarr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

98
maintenance.sh Normal file
View file

@ -0,0 +1,98 @@
#!/usr/bin/env bash
workdir=$(realpath $(dirname "$0"))
cd $workdir
function git_pull_and_update_cron_and_restart_services_if_needed()
{
git pull &>/dev/null
# Cron
cat cron | sed "s@__BASEDIR__@$workdir@g" > /etc/cron.d/app_list
# App store
chown -R appstore store
pushd store >/dev/null
modified_after_service_start="$(find *.py translations/ templates/ assets/ -newermt "$(systemctl show --property=ActiveEnterTimestamp appstore | cut -d= -f2 | cut -d' ' -f2-3)")"
if [ -n "$modified_after_service_start" ]
then
pushd assets >/dev/null
./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --minify
popd >/dev/null
systemctl restart appstore
sleep 3
fi
popd >/dev/null
systemctl --quiet is-active appstore || sendxmpppy "[appstore] Uhoh, failed to (re)start the appstore service?"
# App generator
chown -R appgenerator tools/app_generator
pushd tools/app_generator >/dev/null
modified_after_service_start="$(find *.py translations/ templates/ static/ -newermt "$(systemctl show --property=ActiveEnterTimestamp appgenerator | cut -d= -f2 | cut -d' ' -f2-3)")"
if [ -n "$modified_after_service_start" ]
then
pushd assets >/dev/null
./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --minify
popd >/dev/null
systemctl restart appgenerator
sleep 3
fi
popd >/dev/null
systemctl --quiet is-active appgenerator || sendxmpppy "[appgenerator] Uhoh, failed to (re)start the appgenerator service?"
# Autoreadme
pushd tools/readme_generator >/dev/null
modified_after_service_start="$(find *.py translations/ templates/ -newermt "$(systemctl show --property=ActiveEnterTimestamp autoreadme | cut -d= -f2 | cut -d' ' -f2-3)")"
if [ -n "$modified_after_service_start" ]
then
systemctl restart autoreadme
sleep 3
fi
popd >/dev/null
systemctl --quiet is-active autoreadme || sendxmpppy "[autoreadme] Uhoh, failed to (re)start the autoreadme service?"
}
function rebuild_catalog()
{
log=$workdir/app_list_auto_update.log
date >> $log
git_pull_and_update_cron_and_restart_services_if_needed
./tools/list_builder.py &>> $log || sendxmpppy "[listbuilder] Rebuilding the application list failed miserably"
}
function autoupdate_app_sources()
{
log=$workdir/app_sources_auto_update.log
date >> $log
git_pull_and_update_cron_and_restart_services_if_needed
tools/autoupdate_app_sources/venv/bin/python3 tools/autoupdate_app_sources/autoupdate_app_sources.py \
--edit --commit --pr --paste -j1 \
&> $log || sendxmpppy "[appsourcesautoupdate] App sources auto-update failed miserably"
}
function update_app_levels()
{
pushd tools/update_app_levels >/dev/null
python3 update_app_levels.py
popd >/dev/null
}
function fetch_main_dashboard()
{
pushd store >/dev/null
venv/bin/python3 fetch_main_dashboard.py 2>&1 | grep -v 'Following Github server redirection'
popd >/dev/null
}
function fetch_level_history()
{
pushd store >/dev/null
venv/bin/python3 fetch_level_history.py
popd >/dev/null
}
$1

View file

@ -1,11 +0,0 @@
#!/usr/bin/env bash
workdir=$(realpath $(dirname "$0"))
log=$workdir/app_list_auto_update.log
cd $workdir
date >> $log
git pull &>/dev/null
cat cron | sed "s@__BASEDIR__@$workdir@g" > /etc/cron.d/app_list
./tools/list_builder.py &>> $log || sendxmpppy "[listbuilder] Rebuilding the application list failed miserably"

View file

@ -1,13 +0,0 @@
#!/bin/bash
workdir=$(realpath $(dirname "$0"))
log=$workdir/app_sources_auto_update.log
cd $workdir
date >> $log
git pull &>/dev/null
cat cron | sed "s@__BASEDIR__@$workdir@g" > /etc/cron.d/app_list
python3 tools/autoupdate_app_sources/autoupdate_app_sources.py \
--edit --commit --pr --paste -j1 \
&> $log || sendxmpppy "[appsourcesautoupdate] App sources auto-update failed miserably"

View file

@ -32,6 +32,9 @@ And then start the dev server:
```bash
source venv/bin/activate
FLASK_APP=app.py FLASK_ENV=development flask --debug run
# In another term, launch the tailwindcss process to autorebuild css:
cd assets; ./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --watch
```
## Translation

View file

@ -11,6 +11,7 @@ import hmac
import string
import random
import urllib
from datetime import datetime
from slugify import slugify
from flask import (
Flask,
@ -19,6 +20,7 @@ from flask import (
session,
redirect,
request,
make_response,
)
from flask_babel import Babel
from flask_babel import gettext as _
@ -89,6 +91,13 @@ def days_ago(timestamp):
return int((time.time() - timestamp) / (60 * 60 * 24))
@app.template_filter("format_datetime")
def format_datetime(value, format="%d %b %Y %I:%M %p"):
if value is None:
return ""
return datetime.strptime(value, "%b %d %Y").strftime(format)
@app.context_processor
def utils():
d = {
@ -487,6 +496,72 @@ def charts():
)
@app.route("/news.rss")
def news_rss():
news_per_date = json.loads(open(".cache/news.json").read())
# Keepy only the last N entries
news_per_date = {
d: infos for d, infos in reversed(list(news_per_date.items())[-2:])
}
rss_xml = render_template(
"news_rss.xml", news_per_date=news_per_date, catalog=get_catalog()
)
response = make_response(rss_xml)
response.headers["Content-Type"] = "application/rss+xml"
response.headers["Content-Disposition"] = "inline; filename=news_rss.xml"
return response
# Badges
@app.route("/integration/<app>")
@app.route("/integration/<app>.svg")
@app.route("/badge/<type>/<app>")
@app.route("/badge/<type>/<app>.svg")
def badge(app, type="integration"):
data = get_dashboard_data()
catalog = get_catalog()["apps"]
catalog_level = catalog.get(app, {}).get("level")
main_ci_level = (
data.get(app, {}).get("ci_results", {}).get("main", {}).get("level", "?")
)
if type == "integration":
if app in catalog and main_ci_level:
badge = f"level{main_ci_level}"
else:
badge = "unknown"
elif type == "state":
if app not in catalog:
badge = "state-unknown"
else:
if catalog_level in [None, "?"]:
badge = "state-just-got-added-to-catalog"
elif catalog_level in [0, -1]:
badge = "state-broken"
else:
badge = "state-working"
elif type == "maintained":
if app in catalog and catalog.get(app, {}).get("maintained") is False:
badge = "unmaintained"
else:
badge = "empty"
else:
badge = "empty"
svg = open(f"assets/badges/{badge}.svg").read()
response = make_response(svg)
response.content_type = "image/svg+xml"
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
return response
###############################################################################
# Session / SSO using Discourse #
###############################################################################

View file

@ -0,0 +1,20 @@
wget -O - https://img.shields.io/badge/Integration-Level_9-blue.svg > level9.svg
wget -O - https://img.shields.io/badge/Integration-Level_8-brightgreen.svg > level8.svg
wget -O - https://img.shields.io/badge/Integration-Level_7-green.svg > level7.svg
wget -O - https://img.shields.io/badge/Integration-Level_6-yellowgreen.svg > level6.svg
wget -O - https://img.shields.io/badge/Integration-Level_5-yellowgreen.svg > level5.svg
wget -O - https://img.shields.io/badge/Integration-Level_4-yellow.svg > level4.svg
wget -O - https://img.shields.io/badge/Integration-Level_3-yellow.svg > level3.svg
wget -O - https://img.shields.io/badge/Integration-Level_2-orange.svg > level2.svg
wget -O - https://img.shields.io/badge/Integration-Level_1-orange.svg > level1.svg
wget -O - https://img.shields.io/badge/Integration-Level_0-red.svg > level0.svg
wget -O - https://img.shields.io/badge/Integration-Unknown-lightgrey.svg > unknown.svg
wget -O - https://upload.wikimedia.org/wikipedia/commons/1/1d/No_image.svg > empty.svg
wget -O - https://img.shields.io/badge/Status-Package%20not%20maintained-red.svg > unmaintained.svg
wget -O - https://img.shields.io/badge/Status-working-brightgreen.svg > state-working.svg
wget -O - https://img.shields.io/badge/Status-Just%20got%20added%20to%20catalog-yellowgreen.svg > state-just-got-added-to-catalog.svg
wget -O - https://img.shields.io/badge/Status-In%20progress-orange.svg > state-inprogress.svg
wget -O - https://img.shields.io/badge/Status-Not%20working-red.svg > state-notworking.svg
wget -O - https://img.shields.io/badge/Status-Broken-red.svg > state-broken.svg

View file

@ -1,11 +1,14 @@
# Production -> download standalone tailwind to compile only what we need
wget https://github.com/tailwindlabs/tailwindcss/releases/download/v3.3.3/tailwindcss-linux-x64
wget https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.3/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --minify
# Development -> we use the JS magic thingy
curl -L https://cdn.tailwindcss.com?plugins=forms > tailwind-css.js
#curl -L https://cdn.tailwindcss.com?plugins=forms > tailwind-css.js
# Dark theme stuff
git clone https://github.com/jjranalli/nightwind
# Canvasjs (for the chart page only)
curl -L https://cdn.canvasjs.com/ga/canvasjs.min.js > canvasjs.min.js

View file

@ -3,8 +3,8 @@
@tailwind utilities;
@layer utilities {
body {
@apply text-gray-800;
input, textarea, select {
@apply !rounded-md shadow-sm !border-gray-200 !bg-neutral-50;
}
.btn {
@apply text-sm font-medium rounded-md px-4 py-2 transition;

View file

@ -1,11 +1,10 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['../templates/*.html'],
theme: {
extend: {},
},
darkMode: 'selector',
plugins: [
require('@tailwindcss/forms'),
require('./nightwind/src/index.js'),
],
safelist: [
'safelisted',

View file

@ -87,8 +87,6 @@ for app, infos in catalog["apps"].items():
if infos["state"] != "working":
continue
print(app)
consolidated_infos[app] = {
"public_level": infos["level"],
"url": infos["git"]["url"],

View file

@ -82,7 +82,7 @@
<span class="inline sm:hidden">{{ _("Demo") }}</span>
</a>
{% endif %}
<a aria-label="{{ _('Install with YunoHost') }}" title="{{ _('Install with YunoHost') }}" class="h-9.5 inline-block rounded-md border p-0 bg-gray-900 text-white " href="https://install-app.yunohost.org/?app={{ app_id }}">
<a aria-label="{{ _('Install with YunoHost') }}" title="{{ _('Install with YunoHost') }}" class="h-9.5 inline-block rounded-md border border-gray-300 p-0 bg-gray-900 dark:bg-gray-50 text-white dark:text-black" href="https://install-app.yunohost.org/?app={{ app_id }}">
<span class="inline-block text-[11px] leading-3 text-center py-1.5 pl-2">Install<br/>with</span>
<span class="inline-block pr-2 pt-1"><img alt="YunoHost" src="{{ url_for('static', filename='horizontal-yunohost.svg') }}"></span>
</a>

View file

@ -12,17 +12,17 @@
<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') }}">
{% if config.DEBUG %}
<script src="{{ url_for('static', filename='tailwind-css.js') }}"></script>
<style type="text/tailwindcss">
{{ tailwind_local }}
</style>
{% else %}
<link rel="stylesheet" href="{{ url_for('static', filename='tailwind.css') }}">
{% endif %}
<script>
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
</head>
<body>
<body class="bg-neutral-50 text-gray-800">
<header class="pb-2 shadow-sm">
<div
class="flex h-12 items-center gap-8 pt-2 px-4 sm:px-6 lg:px-8"
@ -36,24 +36,24 @@
<nav class="hidden md:block">
<ul class="flex items-center gap-6 text-sm">
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('browse_catalog') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('browse_catalog') }}">
{{ _("Catalog") }}
</a>
</li>
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('browse_wishlist') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('browse_wishlist') }}">
{{ _("Wishlist") }}
</a>
</li>
{% if packaging_enabled %}
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('dash') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('dash') }}">
{{ _("Packaging dashboard") }}
</a>
</li>
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('charts') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('charts') }}">
{{ _("Charts & history") }}
</a>
</li>
@ -70,6 +70,15 @@
<i class="fa fa-external-link fa-fw" aria-hidden="true"></i>
{{ _("YunoHost documentation") }}
</a>
<a
class="inline-block"
role="button"
target="_blank"
id="toggleDarkMode"
>
<i class="fa fa-sun-o inline-block dark:hidden rounded-md border border-gray-300 px-3 py-2.5 hover:bg-gray-100" aria-hidden="true" title="{{ _('Toggle light/dark mode') }}"></i>
<i class="fa fa-moon-o hidden dark:inline-block rounded-md border border-gray-300 px-3 py-2.5 hover:bg-gray-100" aria-hidden="true" title="{{ _('Toggle light/dark mode') }}"></i>
</a>
{% if not user %}
<a
class="btn btn-primary inline-block"
@ -252,14 +261,25 @@
<footer class="h-5 my-5 text-center">
<p>
{{ _("Made with <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> using <a class='text-blue-800' href='https://flask.palletsprojects.com'>Flask</a> and <a class='text-blue-800' href='https://tailwindcss.com/'>TailwindCSS</a>") }}
<a class='text-blue-800' href='https://github.com/YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-hidden='true'></i> {{ _("Source") }}</a>
<a class='text-blue-800' href='https://yunohost.org/tos'>{{ _("Terms of Services") }}</a>
{{ _("Made with <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> using <a class='text-blue-800 dark:text-blue-400' href='https://flask.palletsprojects.com'>Flask</a> and <a class='text-blue-800 dark:text-blue-400' href='https://tailwindcss.com/'>TailwindCSS</a>") }}
<a class='text-blue-800 dark:text-blue-400' href='https://github.com/YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-hidden='true'></i> {{ _("Source") }}</a>
<a class='text-blue-800 dark:text-blue-400' href='https://yunohost.org/tos'>{{ _("Terms of Services") }}</a>
</p>
</footer>
</body>
<script type="text/javascript">
document.getElementById('toggleDarkMode').addEventListener('click', () => {
document.documentElement.classList.toggle('dark')
if (document.documentElement.classList.contains("dark") == true)
{
localStorage.theme = 'dark';
}
else
{
localStorage.theme = 'light';
}
});
{% if user %}
document.getElementById('toggleUserMenu').addEventListener('click', () => {
document.getElementById('userMenu').classList.toggle("hidden");
@ -269,5 +289,4 @@
document.getElementById('menu').classList.toggle("hidden");
});
</script>
</html>

View file

@ -16,7 +16,7 @@
>
<a
href="{{ url_for('app_info', app_id=app) }}"
class="relative block overflow-hidden rounded-lg py-2 px-4 hover:bg-gray-100 mx-2 md:mx-0"
class="relative block overflow-hidden rounded-lg py-2 px-4 hover:bg-neutral-100 mx-2 md:mx-0"
>
<div class="flex justify-between gap-4">
<div class="shrink-0">
@ -27,7 +27,7 @@
src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
{% endif %}
loading="lazy"
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
class="h-12 w-12 rounded-lg object-cover shadow mt-1"
>
</div>
<div class="w-full">
@ -53,14 +53,16 @@
</span>
</span>
</span>
<p class="max-w-[40ch] text-xs text-gray-700">
<p class="max-w-[40ch] text-xs opacity-75">
{{ infos['manifest']['description']|localize }}
</p>
<div class="hidden">
{{ infos["potential_alternative_to"]|join(', ') }}
</div>
{% if infos['category'] %}
<span class="rounded-full px-2.5 py-0.5 text-[10px] border text-{{ catalog['categories'][infos['category']]['color'] }}-600 border-{{ catalog['categories'][infos['category']]['color'] }}-400 ">
<span class="rounded-full px-2.5 py-0.5 text-[10px] border
text-{{ catalog['categories'][infos['category']]['color'] }}-600
border-{{ catalog['categories'][infos['category']]['color'] }}-400 ">
{{ catalog['categories'][infos['category']]['title']|localize|lower }}
</span>
{% endif %}
@ -90,7 +92,7 @@
id="search"
placeholder="{{ _('Search for…') }}"
{% 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"
class="w-full sm:text-sm py-2 pe-10"
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
@ -102,7 +104,7 @@
<select
name="selectcategory"
id="selectcategory"
class="w-full rounded-md border-gray-200 shadow-sm sm:test-sm px-2 py-1.5"
class="w-full sm:text-sm px-2 py-1.5"
>
<option value="">{{ _("All apps") }}</option>
{% for id, category in catalog['categories'].items() %}
@ -118,14 +120,14 @@
<select
name="selectsort"
id="selectsort"
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
class="inline-block text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<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 %}>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}opacity-75{% 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 request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %} >

View file

@ -10,8 +10,13 @@
<div id="levelHistory" class="h-80"></div>
</div>
<div class="mx-auto w-80">
<h1 class="text-center font-medium text-2xl py-2">{{ _("History") }}</h1>
<div class="mx-auto w-80 py-2">
<h1 class="text-center font-medium text-2xl pt-4">
{{ _("History") }}
<link rel="alternate" type="application/rss+xml" title="YunoHost apps catalog news" href="{{ url_for('news_rss')}}" />
<a href="{{ url_for('news_rss')}}" class="btn btn-sm h-6 bg-orange-500 text-white !font-bold align-middle !px-2 !py-1"><i class="fa fa-rss-square" aria-hidden="true"></i> Suscribe via RSS</a>
</h1>
<div>
{% for date, news in news_per_date.items()|reverse %}
<h2 class="text-center font-medium text-xl py-4">{{ date }}</h2>
@ -54,6 +59,12 @@
<script>
window.onload = function () {
var theme = "light1";
if (document.documentElement.classList.contains("dark") == true)
{
theme = "dark1";
}
var colors_per_level = [
"#d9534f",
"#E26D4F",
@ -68,6 +79,8 @@ window.onload = function () {
];
new CanvasJS.Chart("levelRing", {
backgroundColor: 'transparent',
theme: theme,
animationEnabled: false,
data: [{
type: "doughnut",
@ -93,6 +106,8 @@ window.onload = function () {
new CanvasJS.Chart("levelHistory", {
backgroundColor: 'transparent',
theme: theme,
animationEnabled: false,
toolTip: {
reversed: true,

View file

@ -4,7 +4,7 @@
{% endblock %}
{% block main %}
<div class="mx-auto w-full text-center p-8">
<div class="mx-auto w-full text-center px-4 py-8">
<h1 class="text-2xl font-bold">
{{ _("App packaging dashboard") }}
</h1>
@ -61,13 +61,13 @@
</div>
<table id="appTable" class="mx-auto">
<tr class="h-20">
<th class="w-32">{{ _("App") }}</th>
<th class="-rotate-45 w-16 text-left">{{ _("Catalog") }}</th>
<th class="-rotate-45 w-16 text-left">{{ _("Main CI") }}</th>
<th class="-rotate-45 w-16 text-left">{{ _("Bookworm CI") }}</th>
<th class="-rotate-45 w-16 text-left">{{ _("Testing PR") }}</th>
<th class="-rotate-45 w-16 text-left">{{ _("Autoupdate PR") }}</th>
<tr class="h-40 md:h-20">
<th class="max-w-20 md:max-w-32">{{ _("App") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Catalog") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Main CI") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Bookworm CI") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Testing PR") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Autoupdate PR") }}</th>
<th></th>
</tr>
{% for app, infos in data.items() %}
@ -93,9 +93,9 @@
data-last-update-autoupdate-daysago="{% if infos["ci-auto-update"] %}{{ infos["ci-auto-update"]["timestamp_updated"] | days_ago }}{% endif %}"
data-packaging-format="{{ infos["packaging_format"] }}"
>
<td class="text-center text-blue-600 font-medium"><a href="{{ infos["url"] }}">{{ app }}</a></td>
<td class="max-w-40 md:max-w-64 text-center text-blue-600 font-medium"><a href="{{ infos["url"] }}">{{ app }}</a></td>
<td class="font-bold">
<a href="https://apps.yunohost.org/app/{{ app }}">
<a href="{{ url_for('app_info', app_id=app) }}">
{{ infos["public_level"] }}
{% if infos["public_level"] == "?" %}
{% elif infos["public_level"] == 0 %}
@ -106,8 +106,12 @@
</a>
</td>
<td>
<a class="{% if infos["ci_results"]["main"]["timestamp"] | days_ago > 30 %}opacity-50{% endif %}" href="https://ci-apps.yunohost.org/ci/apps/{{ app }}/">
{{ infos["ci_results"]["main"]["level"] }}
<a class="{% if infos["public_level"] == infos["ci_results"]["main"]["level"] or infos["ci_results"]["main"]["timestamp"] | days_ago > 30 %}opacity-50{% endif %}" href="https://ci-apps.yunohost.org/ci/apps/{{ app }}/">
{% if infos["public_level"] == infos["ci_results"]["main"]["level"] %}
=
{% else %}
{{ infos["ci_results"]["main"]["level"] }}
{% endif %}
{% if infos["ci_results"]["main"]["timestamp"] | days_ago > 30 %}
<i class="fa fa-hourglass-o" title="{{ _("Outdated test (%(days)s days ago)", days=infos["ci_results"]["main"]["timestamp"] | days_ago) }}"></i>
{% endif %}
@ -120,9 +124,13 @@
</a>
</td>
<td>
<a class="{% if infos["ci_results"]["nextdebian"] and infos["ci_results"]["nextdebian"]["timestamp"] | days_ago > 30 %}opacity-50{% endif %}" href="https://ci-apps-bookworm.yunohost.org/ci/apps/{{ app }}/">
<a class="{% if infos["ci_results"]["nextdebian"] and ((infos["public_level"] == infos["ci_results"]["nextdebian"]["level"]) or (infos["ci_results"]["nextdebian"]["timestamp"] | days_ago) > 30) %}opacity-50{% endif %}" href="https://ci-apps-bookworm.yunohost.org/ci/apps/{{ app }}/">
{% if infos["ci_results"]["nextdebian"] %}
{{ infos["ci_results"]["nextdebian"]["level"] }}
{% if infos["public_level"] == infos["ci_results"]["nextdebian"]["level"] %}
=
{% else %}
{{ infos["ci_results"]["nextdebian"]["level"] }}
{% endif %}
{% if infos["ci_results"]["nextdebian"]["timestamp"] | days_ago > 30 %}
<i class="fa fa-hourglass-o" title="{{ _("Outdated test (%(days)s days ago)", days=infos["ci_results"]["nextdebian"]["timestamp"] | days_ago) }}"></i>
{% endif %}
@ -143,7 +151,7 @@
<i class="fa fa-flask"></i>
{% for s in infos["testing"]["statuses"] %}
{% if s["context"] == "ci-apps-dev" %}
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-600{% endif %}"></i>
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-500{% endif %}"></i>
{% endif %}
{% endfor %}
{% if infos["testing"]["timestamp_updated"] | days_ago > 30 %}
@ -158,7 +166,7 @@
<i class="fa fa-arrow-up"></i>
{% for s in infos["ci-auto-update"]["statuses"] %}
{% if s["context"] == "ci-apps-dev" %}
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-600{% endif %}"></i>
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-500{% endif %}"></i>
{% endif %}
{% endfor %}
{% if infos["ci-auto-update"]["timestamp_updated"] | days_ago > 30 %}
@ -256,7 +264,7 @@
}
else if (filterName == "regressions_bookworm")
{
if ((entries[i].dataset.publicLevel >= 6) && (entries[i].dataset.nextdebianCiLevel < 6))
if ((entries[i].dataset.publicLevel >= 6) && (entries[i].dataset.nextdebianCiLevel < 6) && (entries[i].dataset.nextdebianCiLevel != entries[i].dataset.mainCiLevel))
{
entries[i].classList.remove("hidden");
nb_found++;

View file

@ -5,14 +5,14 @@
{% block main %}
<div class="mx-auto w-full text-center p-8">
<img alt="YunoHost logo" src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto">
<img alt="YunoHost logo" src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto dark:invert">
<h1 class="text-2xl font-bold">
{{ _("Application Store") }}
</h1>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 max-w-screen-lg mx-auto pt-5">
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0">
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0 border-gray-200">
<a
href="{{ url_for('browse_catalog') }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-12"
@ -23,7 +23,7 @@
</a>
</div>
{% for id, category in catalog['categories'].items() %}
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0">
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0 border-gray-200">
<a
href="{{ url_for('browse_catalog', category=id) }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-10"
@ -32,7 +32,7 @@
<i class="fa fa-{{ category['icon'] }}" aria-hidden="true"></i>
{{ category['title']|localize }}
</h2>
<p class="mx-auto max-w-[40ch] text-xs text-gray-500">
<p class="mx-auto max-w-[40ch] text-xs opacity-75">
{{ category['description']|localize }}
</p>
</a>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
<title>YunoHost apps catalog News</title>
<link>{{ url_for('news_rss', _external=True)}}</link>
<atom:link href="{{ url_for('news_rss', _external=True)}}" rel="self" type="application/rss+xml" />
<description>YunoHost apps catalog news</description>
{%- for date, news in news_per_date.items() %}
{%- for status in ("added", "repaired", "broke", "removed") %}
{%- for app, url in news[status] %}
{% set manifest = catalog["apps"].get(app, {}).get("manifest", {}) %}
<item>
<title>[{{ status|capitalize }}] {% if manifest %}{{ manifest['name'] }}{% else %}{{ app }}{% endif %}</title>
<link>{{ url_for('app_info', app_id=app, _external=True) }}</link>
<guid isPermaLink="false">{{ app }}#{{ date|format_datetime("%Y%m%d") }}</guid>
<pubDate>{{ date|format_datetime("%a, %d %b %Y %H:%M:%S +0000") }}</pubDate>
<description>{{ manifest.get("description", {}).get("en") }}</description>
<content:encoded>{{ manifest.get("description", {}).get("en") }}</content:encoded>
</item>
{% endfor -%}
{% endfor -%}
{% endfor %}
</channel>
</rss>

View file

@ -7,22 +7,22 @@
<h1 class="text-2xl font-bold">
{{ _("Application Wishlist") }}
</h1>
<p class="text-sm text-gray-700 mx-10 mt-2">{{ _("The wishlist is the place where people can collectively suggest and vote for apps that they would like to see packaged and made available in YunoHost's official apps catalog. Nevertheless, the fact that apps are listed here should by no mean be interpreted as a fact that the YunoHost project plans to integrate it, and is merely a source of inspiration for packaging volunteers.") }}</p>
<p class="text-sm opacity-75 mx-10 mt-2">{{ _("The wishlist is the place where people can collectively suggest and vote for apps that they would like to see packaged and made available in YunoHost's official apps catalog. Nevertheless, the fact that apps are listed here should by no mean be interpreted as a fact that the YunoHost project plans to integrate it, and is merely a source of inspiration for packaging volunteers.") }}</p>
</div>
<div class="max-w-screen-md mx-auto mt-3 mb-3">
<div class="flex flex-col md:flex-row items-center">
<div class="px-2 inline-block relative basis-2/3 text-gray-700">
<div class="px-2 inline-block relative basis-2/3">
<label for="search" class="sr-only"> {{ _("Search") }} </label>
<input
type="text"
id="search"
placeholder="{{ _('Search for…') }}"
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
class="w-full sm:text-sm py-2 pe-10"
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4 opacity-75">
<i class="fa fa-search" aria-hidden="true"></i>
</span>
</div>
@ -40,13 +40,13 @@
<select
name="selectsort"
id="selectsort"
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
class="inline-block text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<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 %}>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}opacity-75{% 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 request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %}>
@ -64,7 +64,7 @@
</div>
<div class="overflow-x-auto max-w-screen-lg mx-auto pt-5">
<table class="min-w-full divide-y-2 divide-gray-200 bg-white text-sm">
<table class="min-w-full divide-y-2 divide-gray-100 dark:divide-gray-700 text-sm">
<thead>
<tr>
<th class="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium">
@ -79,7 +79,7 @@
</tr>
</thead>
<tbody id="wishlist" class="divide-y divide-gray-200">
<tbody id="wishlist" class="divide-y divide-gray-100 dark:divide-gray-700">
{% for app, infos in wishlist.items() %}
{% set this_app_stars = stars.get(app, {})|length %}
{% if user %}
@ -95,7 +95,7 @@
<td class="inline-block sm:table-cell px-4 py-2 font-bold sm:max-w-[10em]">
{{ infos['name'] }}
</td>
<td class="block sm:table-cell px-4 py-0 sm:py-2 text-gray-700 max-w-md">{{ infos['description'] }}</td>
<td class="block sm:table-cell px-4 py-0 sm:py-2 opacity-75 max-w-md">{{ infos['description'] }}</td>
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0">
{% if infos['website'] %}
<a
@ -105,7 +105,7 @@
class="inline-block"
target="_blank"
>
<i class="fa fa-globe rounded-md border px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
<i class="fa fa-globe rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>
@ -118,7 +118,7 @@
class="inline-block"
target="_blank"
>
<i class="fa fa-code rounded-md border px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
<i class="fa fa-code rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>

View file

@ -59,21 +59,21 @@
<input name="csrf_token" type="text" class="hidden" value="{{ csrf_token }}" >
<label for="name" class="mt-5 block font-bold text-gray-700">{{ _("Name") }}</label>
<input name="name" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="30" required onkeyup="this.value = this.value.replace(/[^a-zA-Z0-9.-\\(\\)\\ ]/, '')" >
<input name="name" type="text" class="w-full mt-1" maxlength="30" required onkeyup="this.value = this.value.replace(/[^a-zA-Z0-9.-\\(\\)\\ ]/, '')" >
<label for="description" class="mt-5 block font-bold text-gray-700">{{ _("App's description") }}</label>
<textarea name="description" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" required rows='3' maxlength='100'></textarea>
<span class="text-xs text-gray-600"><span class="font-bold">{{ _("Please be concise and focus on what the app does.") }}</span> {{ _("No need to repeat '[App] is …'. No need to state that it is free/open-source or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid marketing stuff like 'the most', or vague properties like 'easy', 'simple', 'lightweight'.") }}</span>
<label for="upstream" class="mt-5 block font-bold text-gray-700">{{ _("Project code repository") }}</label>
<input name="upstream" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" required >
<input name="upstream" type="url" class="w-full mt-1" maxlength="150" required >
<label for="license" class="mt-5 block font-bold text-gray-700">{{ _("Link to the project's LICENSE") }}</label>
<input name="license" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" required maxlength="250"></input>
<input name="license" type="url" class="w-full mt-1" required maxlength="250"></input>
<span class="text-xs text-gray-600 font-bold">{{ _("The YunoHost project will only package free/open-source software (with possible case-by-case exceptions for apps which are not-totally-free)") }}</span>
<label for="website" class="mt-5 block font-bold text-gray-700">{{ _("Project website") }}</label>
<input name="website" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" >
<input name="website" type="url" class="w-full mt-1" maxlength="150" >
<span class="text-xs text-gray-600">{{ _("Please *do not* just copy-paste the code repository URL. If the project has no proper website, then leave the field empty.") }}</span>
<button

View file

@ -331,6 +331,14 @@ class AppAutoUpdater:
continue
tags_dict[tag_as_ints] = (tag, tag_clean)
if app_id == "focalboard":
# Stupid ad-hoc patch for focalboard where 7.11.4 doesn't have the proper asset
# because idk it was just a patch for mattermost or something
if "v7.11.4" in tags_dict:
del tags_dict["v7.11.4"]
if "7.11.4" in tags_dict:
del tags_dict["7.11.4"]
# sorted will sort by keys, tag_as_ints
# reverse=True will set the last release as first element
tags_dict = dict(sorted(tags_dict.items(), reverse=True))

View file

@ -37,7 +37,7 @@ class GithubAPI:
def releases(self) -> list[dict[str, Any]]:
"""Get a list of releases for project."""
return self.internal_api(f"repos/{self.upstream_repo}/releases")
return self.internal_api(f"repos/{self.upstream_repo}/releases?per_page=100")
def url_for_ref(self, ref: str, ref_type: RefType) -> str:
"""Get a URL for a ref."""

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-31 19:33+0200\n"
"PO-Revision-Date: 2024-05-10 15:59+0000\n"
"PO-Revision-Date: 2024-05-11 11:36+0000\n"
"Last-Translator: rosbeef andino <ra@3cmr.fr>\n"
"Language-Team: Spanish <https://translate.yunohost.org/projects/yunohost/"
"readme-generator/es/>\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.3.1\n"
"X-Generator: Weblate 5.4.3\n"
"Generated-By: Babel 2.14.0\n"
#: templates/ALL_README.md.j2:11
@ -226,7 +226,7 @@ msgstr ""
#: templates/README.md.j2:83
msgid "To try the `testing` branch, please proceed like that:"
msgstr ""
msgstr "Para probar la rama `testing`, sigue asÍ:"
#: templates/README.md.j2:87
msgid "or"

View file

@ -33,6 +33,7 @@ upstream = "https://gitlab.com/hanklank/amara-archive"
website = "https://amara.org"
added_date = 1695656621 # 2023/09/25
[amusewiki]
name = "Amusewiki"
description = "A library-oriented wiki engine and a powerful authoring, archiving and publishing platform."
@ -75,6 +76,14 @@ upstream = "https://github.com/archivesspace/archivesspace"
website = "https://archivesspace.org/"
added_date = 1695656621 # 2023/09/25
[argos-panopts]
name = "Argos Panopts"
description = "A monitoring and status board for your websites."
upstream = "https://framagit.org/framasoft/framaspace/argos/"
website = "https://framasoft.frama.io/framaspace/argos/"
added_date = 1715934490 # 2024/05/17
[asqatasun]
name = "Asqatasun"
description = "Website analyser for web accessibility and SEO"
@ -625,6 +634,14 @@ upstream = "https://github.com/forem/selfhost"
website = "https://www.forem.com/"
added_date = 1695656621 # 2023/09/25
[formbricks]
name = "Formbricks"
description = "Experience Management Suite / Survey Platform"
upstream = "https://github.com/formbricks/formbricks"
website = "https://formbricks.com/"
added_date = 1715531786 # 2024/05/12
[fractale]
name = "Fractale"
description = "Platform for self-organization."
@ -1286,6 +1303,14 @@ upstream = "https://github.com/samuelclay/NewsBlur"
website = "https://www.newsblur.com"
added_date = 1695656621 # 2023/09/25
[noe]
name = "NOE"
description = "Votre QG événementiel pour gérer votre planning de formations, votre bénévolat et vos participant.es"
upstream = "https://gitlab.com/alternatiba/noe"
website = "https://get.noe-app.io"
added_date = 1715531830 # 2024/05/12
[nostr]
name = "Nostr"
description = "Censorship-resistant alternative to Twitter"
@ -1514,6 +1539,13 @@ upstream = "https://github.com/pretix"
website = "https://pretix.eu/about/en/"
added_date = 1696008732 # 2023/09/29
[pritunl]
name = "pritunl"
description = "Distributed enterprise vpn server built using the OpenVPN protocol"
upstream = "https://github.com/pritunl/pritunl"
website = "https://pritunl.com/"
added_date = 1715531577 # 2024/05/12
[privategpt]
name = "PrivateGPT"
description = "AI project that allows you to ask questions to your documents using the power of LLMs."
@ -1751,7 +1783,6 @@ upstream = "https://github.com/simple-login/app"
website = "https://simplelogin.io"
added_date = 1695656621 # 2023/09/25
[slskd]
name = "Slskd"
description = "A modern client-server application for the Soulseek file sharing network."
@ -2164,6 +2195,14 @@ upstream = "https://github.com/yggdrasil-network/yggdrasil-go"
website = "https://yggdrasil-network.github.io/"
added_date = 1695656621 # 2023/09/25
[yolo-jukebox]
name = "Yolo jukebox"
description = "comeoninternet.ogg"
upstream = "https://framagit.org/framasoft/fun/yolo-cowboy"
website = ""
added_date = 1715698221 # 2024/05/14
[your-spotify]
name = "your_spotify"
description = "Spotify tracking dashboard"