Merge branch 'dev' of https://github.com/YunoHost/yunohost into enh-dns-autoconf

This commit is contained in:
Paco 2021-03-25 21:11:33 +01:00
commit 4a92f97b4e
64 changed files with 1266 additions and 877 deletions

View file

@ -1410,13 +1410,11 @@ dyndns:
### dyndns_installcron() ### dyndns_installcron()
installcron: installcron:
action_help: Install IP update cron deprecated: true
api: POST /dyndns/cron
### dyndns_removecron() ### dyndns_removecron()
removecron: removecron:
action_help: Remove IP update cron deprecated: true
api: DELETE /dyndns/cron
############################# #############################

View file

@ -47,10 +47,11 @@ ynh_wait_dpkg_free() {
# Check either a package is installed or not # Check either a package is installed or not
# #
# example: ynh_package_is_installed --package=yunohost && echo "ok" # example: ynh_package_is_installed --package=yunohost && echo "installed"
# #
# usage: ynh_package_is_installed --package=name # usage: ynh_package_is_installed --package=name
# | arg: -p, --package= - the package name to check # | arg: -p, --package= - the package name to check
# | ret: 0 if the package is installed, 1 else.
# #
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_package_is_installed() { ynh_package_is_installed() {
@ -216,7 +217,8 @@ ynh_package_install_from_equivs () {
# example : ynh_install_app_dependencies dep1 dep2 "dep3|dep4|dep5" # example : ynh_install_app_dependencies dep1 dep2 "dep3|dep4|dep5"
# #
# usage: ynh_install_app_dependencies dep [dep [...]] # usage: ynh_install_app_dependencies dep [dep [...]]
# | arg: dep - the package name to install in dependence. Writing "dep3|dep4|dep5" can be used to specify alternatives. For example : dep1 dep2 "dep3|dep4|dep5" will require to install dep1 and dep 2 and (dep3 or dep4 or dep5). # | arg: dep - the package name to install in dependence.
# | arg: "dep1|dep2|…" - You can specify alternatives. It will require to install (dep1 or dep2, etc).
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_install_app_dependencies () { ynh_install_app_dependencies () {
@ -224,13 +226,10 @@ ynh_install_app_dependencies () {
# Add a comma for each space between packages. But not add a comma if the space separate a version specification. (See below) # Add a comma for each space between packages. But not add a comma if the space separate a version specification. (See below)
dependencies="$(echo "$dependencies" | sed 's/\([^\<=\>]\)\ \([^(]\)/\1, \2/g')" dependencies="$(echo "$dependencies" | sed 's/\([^\<=\>]\)\ \([^(]\)/\1, \2/g')"
local dependencies=${dependencies//|/ | } local dependencies=${dependencies//|/ | }
local manifest_path="../manifest.json" local manifest_path="$YNH_APP_BASEDIR/manifest.json"
if [ ! -e "$manifest_path" ]; then
manifest_path="../settings/manifest.json" # Into the restore script, the manifest is not at the same place
fi
local version=$(grep '\"version\": ' "$manifest_path" | cut --delimiter='"' --fields=4) # Retrieve the version number in the manifest file. local version=$(jq -r '.version' "$manifest_path")
if [ ${#version} -eq 0 ]; then if [ -z "${version}" ] || [ "$version" == "null" ]; then
version="1.0" version="1.0"
fi fi
local dep_app=${app//_/-} # Replace all '_' by '-' local dep_app=${app//_/-} # Replace all '_' by '-'
@ -459,11 +458,11 @@ ynh_remove_extra_repo () {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
name="${name:-$app}" name="${name:-$app}"
ynh_secure_remove "/etc/apt/sources.list.d/$name.list" ynh_secure_remove --file="/etc/apt/sources.list.d/$name.list"
# Sury pinning is managed by the regenconf in the core... # Sury pinning is managed by the regenconf in the core...
[[ "$name" == "extra_php_version" ]] || ynh_secure_remove "/etc/apt/preferences.d/$name" [[ "$name" == "extra_php_version" ]] || ynh_secure_remove "/etc/apt/preferences.d/$name"
ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.gpg" > /dev/null ynh_secure_remove --file="/etc/apt/trusted.gpg.d/$name.gpg" > /dev/null
ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.asc" > /dev/null ynh_secure_remove --file="/etc/apt/trusted.gpg.d/$name.asc" > /dev/null
# Update the list of package to exclude the old repo # Update the list of package to exclude the old repo
ynh_package_update ynh_package_update

View file

@ -13,13 +13,13 @@ CAN_BIND=${CAN_BIND:-1}
# #
# This helper can be used both in a system backup hook, and in an app backup script # This helper can be used both in a system backup hook, and in an app backup script
# #
# Details: ynh_backup writes SRC and the relative DEST into a CSV file. And it # `ynh_backup` writes `src_path` and the relative `dest_path` into a CSV file, and it
# creates the parent destination directory # creates the parent destination directory
# #
# If DEST is ended by a slash it complete this path with the basename of SRC. # If `dest_path` is ended by a slash it complete this path with the basename of `src_path`.
#
# Example in the context of a wordpress app
# #
# Example in the context of a wordpress app :
# ```
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" # ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf"
# # => This line will be added into CSV file # # => This line will be added into CSV file
# # "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf" # # "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf"
@ -40,26 +40,28 @@ CAN_BIND=${CAN_BIND:-1}
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "/conf/" # ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "/conf/"
# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf" # # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
# #
# ```
# #
# How to use --is_big: # How to use `--is_big`:
# --is_big is used to specify that this part of the backup can be quite huge. #
# `--is_big` is used to specify that this part of the backup can be quite huge.
# So, you don't want that your package does backup that part during ynh_backup_before_upgrade. # So, you don't want that your package does backup that part during ynh_backup_before_upgrade.
# In the same way, an user may doesn't want to backup this big part of the app for # In the same way, an user may doesn't want to backup this big part of the app for
# each of his backup. And so handle that part differently. # each of his backup. And so handle that part differently.
# #
# As this part of your backup may not be done, your restore script has to handle it. # As this part of your backup may not be done, your restore script has to handle it.
# In your restore script, use --not_mandatory with ynh_restore_file # In your restore script, use `--not_mandatory` with `ynh_restore_file`
# As well in your remove script, you should not remove those data ! Or an user may end up with # As well in your remove script, you should not remove those data ! Or an user may end up with
# a failed upgrade restoring an app without data anymore ! # a failed upgrade restoring an app without data anymore !
# #
# To have the benefit of --is_big while doing a backup, you can whether set the environement # To have the benefit of `--is_big` while doing a backup, you can whether set the environement
# variable BACKUP_CORE_ONLY to 1 (BACKUP_CORE_ONLY=1) before the backup command. It will affect # variable `BACKUP_CORE_ONLY` to 1 (`BACKUP_CORE_ONLY=1`) before the backup command. It will affect
# only that backup command. # only that backup command.
# Or set the config do_not_backup_data to 1 into the settings.yml of the app. This will affect # Or set the config `do_not_backup_data` to 1 into the `settings.yml` of the app. This will affect
# all backups for this app until the setting is removed. # all backups for this app until the setting is removed.
# #
# Requires YunoHost version 2.4.0 or higher. # Requires YunoHost version 2.4.0 or higher.
# Requires YunoHost version 3.5.0 or higher for the argument --not_mandatory # Requires YunoHost version 3.5.0 or higher for the argument `--not_mandatory`
ynh_backup() { ynh_backup() {
# TODO find a way to avoid injection by file strange naming ! # TODO find a way to avoid injection by file strange naming !
@ -221,26 +223,25 @@ with open(sys.argv[1], 'r') as backup_file:
# Restore a file or a directory # Restore a file or a directory
# #
# Use the registered path in backup_list by ynh_backup to restore the file at
# the right place.
#
# usage: ynh_restore_file --origin_path=origin_path [--dest_path=dest_path] [--not_mandatory] # usage: ynh_restore_file --origin_path=origin_path [--dest_path=dest_path] [--not_mandatory]
# | arg: -o, --origin_path= - Path where was located the file or the directory before to be backuped or relative path to $YNH_CWD where it is located in the backup archive # | arg: -o, --origin_path= - Path where was located the file or the directory before to be backuped or relative path to $YNH_CWD where it is located in the backup archive
# | arg: -d, --dest_path= - Path where restore the file or the dir, if unspecified, the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in the archive, the destination will be searched into backup.csv # | arg: -d, --dest_path= - Path where restore the file or the dir. If unspecified, the destination will be `ORIGIN_PATH` or if the `ORIGIN_PATH` doesn't exist in the archive, the destination will be searched into `backup.csv`
# | arg: -m, --not_mandatory - Indicate that if the file is missing, the restore process can ignore it. # | arg: -m, --not_mandatory - Indicate that if the file is missing, the restore process can ignore it.
# #
# Use the registered path in backup_list by ynh_backup to restore the file at the right place.
#
# examples: # examples:
# ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf" # ynh_restore_file -o "/etc/nginx/conf.d/$domain.d/$app.conf"
# # You can also use relative paths: # # You can also use relative paths:
# ynh_restore_file "conf/nginx.conf" # ynh_restore_file -o "conf/nginx.conf"
# #
# If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in # If `DEST_PATH` already exists and is lighter than 500 Mo, a backup will be made in
# /home/yunohost.conf/backup/. Otherwise, the existing file is removed. # `/home/yunohost.conf/backup/`. Otherwise, the existing file is removed.
# #
# if apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf exists, restore it into # if `apps/$app/etc/nginx/conf.d/$domain.d/$app.conf` exists, restore it into
# /etc/nginx/conf.d/$domain.d/$app.conf # `/etc/nginx/conf.d/$domain.d/$app.conf`
# if no, search for a match in the csv (eg: conf/nginx.conf) and restore it into # if no, search for a match in the csv (eg: conf/nginx.conf) and restore it into
# /etc/nginx/conf.d/$domain.d/$app.conf # `/etc/nginx/conf.d/$domain.d/$app.conf`
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
# Requires YunoHost version 3.5.0 or higher for the argument --not_mandatory # Requires YunoHost version 3.5.0 or higher for the argument --not_mandatory
@ -346,13 +347,13 @@ ynh_store_file_checksum () {
# Verify the checksum and backup the file if it's different # Verify the checksum and backup the file if it's different
# #
# This helper is primarily meant to allow to easily backup personalised/manually
# modified config files.
#
# usage: ynh_backup_if_checksum_is_different --file=file # usage: ynh_backup_if_checksum_is_different --file=file
# | arg: -f, --file= - The file on which the checksum test will be perfomed. # | arg: -f, --file= - The file on which the checksum test will be perfomed.
# | ret: the name of a backup file, or nothing # | ret: the name of a backup file, or nothing
# #
# This helper is primarily meant to allow to easily backup personalised/manually
# modified config files.
#
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_backup_if_checksum_is_different () { ynh_backup_if_checksum_is_different () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -399,14 +400,27 @@ ynh_delete_file_checksum () {
ynh_app_setting_delete --app=$app --key=$checksum_setting_name ynh_app_setting_delete --app=$app --key=$checksum_setting_name
} }
# Checks a backup archive exists
#
# [internal]
#
ynh_backup_archive_exists () {
yunohost backup list --output-as json --quiet \
| jq -e --arg archive "$1" '.archives | index($archive)' >/dev/null
}
# Make a backup in case of failed upgrade # Make a backup in case of failed upgrade
# #
# usage: # usage: ynh_backup_before_upgrade
#
# Usage in a package script:
# ```
# ynh_backup_before_upgrade # ynh_backup_before_upgrade
# ynh_clean_setup () { # ynh_clean_setup () {
# ynh_restore_upgradebackup # ynh_restore_upgradebackup
# } # }
# ynh_abort_if_errors # ynh_abort_if_errors
# ```
# #
# Requires YunoHost version 2.7.2 or higher. # Requires YunoHost version 2.7.2 or higher.
ynh_backup_before_upgrade () { ynh_backup_before_upgrade () {
@ -423,7 +437,7 @@ ynh_backup_before_upgrade () {
if [ "$NO_BACKUP_UPGRADE" -eq 0 ] if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
then then
# Check if a backup already exists with the prefix 1 # Check if a backup already exists with the prefix 1
if yunohost backup list | grep --quiet $app_bck-pre-upgrade1 if ynh_backup_archive_exists "$app_bck-pre-upgrade1"
then then
# Prefix becomes 2 to preserve the previous backup # Prefix becomes 2 to preserve the previous backup
backup_number=2 backup_number=2
@ -435,7 +449,7 @@ ynh_backup_before_upgrade () {
if [ "$?" -eq 0 ] if [ "$?" -eq 0 ]
then then
# If the backup succeeded, remove the previous backup # If the backup succeeded, remove the previous backup
if yunohost backup list | grep --quiet $app_bck-pre-upgrade$old_backup_number if ynh_backup_archive_exists "$app_bck-pre-upgrade$old_backup_number"
then then
# Remove the previous backup only if it exists # Remove the previous backup only if it exists
yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null
@ -450,12 +464,16 @@ ynh_backup_before_upgrade () {
# Restore a previous backup if the upgrade process failed # Restore a previous backup if the upgrade process failed
# #
# usage: # usage: ynh_restore_upgradebackup
#
# Usage in a package script:
# ```
# ynh_backup_before_upgrade # ynh_backup_before_upgrade
# ynh_clean_setup () { # ynh_clean_setup () {
# ynh_restore_upgradebackup # ynh_restore_upgradebackup
# } # }
# ynh_abort_if_errors # ynh_abort_if_errors
# ```
# #
# Requires YunoHost version 2.7.2 or higher. # Requires YunoHost version 2.7.2 or higher.
ynh_restore_upgradebackup () { ynh_restore_upgradebackup () {
@ -467,7 +485,7 @@ ynh_restore_upgradebackup () {
if [ "$NO_BACKUP_UPGRADE" -eq 0 ] if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
then then
# Check if an existing backup can be found before removing and restoring the application. # Check if an existing backup can be found before removing and restoring the application.
if yunohost backup list | grep --quiet $app_bck-pre-upgrade$backup_number if ynh_backup_archive_exists "$app_bck-pre-upgrade$backup_number"
then then
# Remove the application then restore it # Remove the application then restore it
yunohost app remove $app yunohost app remove $app

View file

@ -12,15 +12,14 @@
# #
# usage 2: ynh_add_fail2ban_config --use_template [--others_var="list of others variables to replace"] # usage 2: ynh_add_fail2ban_config --use_template [--others_var="list of others variables to replace"]
# | arg: -t, --use_template - Use this helper in template mode # | arg: -t, --use_template - Use this helper in template mode
# | arg: -v, --others_var= - List of others variables to replace separeted by a space # | arg: -v, --others_var= - List of others variables to replace separeted by a space for example : 'var_1 var_2 ...'
# | for example : 'var_1 var_2 ...'
# #
# This will use a template in ../conf/f2b_jail.conf and ../conf/f2b_filter.conf # This will use a template in `../conf/f2b_jail.conf` and `../conf/f2b_filter.conf`
# See the documentation of ynh_add_config for a description of the template # See the documentation of `ynh_add_config` for a description of the template
# format and how placeholders are replaced with actual variables. # format and how placeholders are replaced with actual variables.
# #
# Generally your template will look like that by example (for synapse): # Generally your template will look like that by example (for synapse):
# # ```
# f2b_jail.conf: # f2b_jail.conf:
# [__APP__] # [__APP__]
# enabled = true # enabled = true
@ -28,7 +27,8 @@
# filter = __APP__ # filter = __APP__
# logpath = /var/log/__APP__/logfile.log # logpath = /var/log/__APP__/logfile.log
# maxretry = 3 # maxretry = 3
# # ```
# ```
# f2b_filter.conf: # f2b_filter.conf:
# [INCLUDES] # [INCLUDES]
# before = common.conf # before = common.conf
@ -41,14 +41,15 @@
# failregex = ^%(__synapse_start_line)s INFO \- POST\-(\d+)\- <HOST> \- \d+ \- Received request\: POST /_matrix/client/r0/login\??<SKIPLINES>%(__synapse_start_line)s INFO \- POST\-\1\- Got login request with identifier: \{u'type': u'm.id.user', u'user'\: u'(.+?)'\}, medium\: None, address: None, user\: u'\5'<SKIPLINES>%(__synapse_start_line)s WARNING \- \- (Attempted to login as @\5\:.+ but they do not exist|Failed password login for user @\5\:.+)$ # failregex = ^%(__synapse_start_line)s INFO \- POST\-(\d+)\- <HOST> \- \d+ \- Received request\: POST /_matrix/client/r0/login\??<SKIPLINES>%(__synapse_start_line)s INFO \- POST\-\1\- Got login request with identifier: \{u'type': u'm.id.user', u'user'\: u'(.+?)'\}, medium\: None, address: None, user\: u'\5'<SKIPLINES>%(__synapse_start_line)s WARNING \- \- (Attempted to login as @\5\:.+ but they do not exist|Failed password login for user @\5\:.+)$
# #
# ignoreregex = # ignoreregex =
# ```
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# #
# Note about the "failregex" option: # Note about the "failregex" option:
# regex to match the password failure messages in the logfile. The #
# host must be matched by a group named "host". The tag "<HOST>" can # regex to match the password failure messages in the logfile. The host must be
# be used for standard IP/hostname matching and is only an alias for # matched by a group named "`host`". The tag "`<HOST>`" can be used for standard
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+) # IP/hostname matching and is only an alias for `(?:::f{4,6}:)?(?P<host>[\w\-.^_]+)`
# #
# You can find some more explainations about how to make a regex here : # You can find some more explainations about how to make a regex here :
# https://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Filters # https://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Filters
@ -56,29 +57,35 @@
# Note that the logfile need to exist before to call this helper !! # Note that the logfile need to exist before to call this helper !!
# #
# To validate your regex you can test with this command: # To validate your regex you can test with this command:
# ```
# fail2ban-regex /var/log/YOUR_LOG_FILE_PATH /etc/fail2ban/filter.d/YOUR_APP.conf # fail2ban-regex /var/log/YOUR_LOG_FILE_PATH /etc/fail2ban/filter.d/YOUR_APP.conf
# ```
# #
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_add_fail2ban_config () { ynh_add_fail2ban_config () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=lrmpt local legacy_args=lrmptv
local -A args_array=( [l]=logpath= [r]=failregex= [m]=max_retry= [p]=ports= [t]=use_template) local -A args_array=( [l]=logpath= [r]=failregex= [m]=max_retry= [p]=ports= [t]=use_template [v]=others_var=)
local logpath local logpath
local failregex local failregex
local max_retry local max_retry
local ports local ports
local others_var
local use_template local use_template
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
max_retry=${max_retry:-3} max_retry=${max_retry:-3}
ports=${ports:-http,https} ports=${ports:-http,https}
others_var="${others_var:-}"
use_template="${use_template:-0}" use_template="${use_template:-0}"
[[ -z "$others_var" ]] || ynh_print_warn --message="Packagers: using --others_var is unecessary since Yunohost 4.2"
if [ $use_template -ne 1 ] if [ $use_template -ne 1 ]
then then
# Usage 1, no template. Build a config file from scratch. # Usage 1, no template. Build a config file from scratch.
test -n "$logpath" || ynh_die "ynh_add_fail2ban_config expects a logfile path as first argument and received nothing." test -n "$logpath" || ynh_die --message="ynh_add_fail2ban_config expects a logfile path as first argument and received nothing."
test -n "$failregex" || ynh_die "ynh_add_fail2ban_config expects a failure regex as second argument and received nothing." test -n "$failregex" || ynh_die --message="ynh_add_fail2ban_config expects a failure regex as second argument and received nothing."
echo " echo "
[__APP__] [__APP__]
@ -87,7 +94,7 @@ port = __PORTS__
filter = __APP__ filter = __APP__
logpath = __LOGPATH__ logpath = __LOGPATH__
maxretry = __MAX_RETRY__ maxretry = __MAX_RETRY__
" > ../conf/f2b_jail.conf " > $YNH_APP_BASEDIR/conf/f2b_jail.conf
echo " echo "
[INCLUDES] [INCLUDES]
@ -95,11 +102,11 @@ before = common.conf
[Definition] [Definition]
failregex = __FAILREGEX__ failregex = __FAILREGEX__
ignoreregex = ignoreregex =
" > ../conf/f2b_filter.conf " > $YNH_APP_BASEDIR/conf/f2b_filter.conf
fi fi
ynh_add_config --template="../conf/f2b_jail.conf" --destination="/etc/fail2ban/jail.d/$app.conf" ynh_add_config --template="$YNH_APP_BASEDIR/conf/f2b_jail.conf" --destination="/etc/fail2ban/jail.d/$app.conf"
ynh_add_config --template="../conf/f2b_filter.conf" --destination="/etc/fail2ban/filter.d/$app.conf" ynh_add_config --template="$YNH_APP_BASEDIR/conf/f2b_filter.conf" --destination="/etc/fail2ban/filter.d/$app.conf"
ynh_systemd_action --service_name=fail2ban --action=reload --line_match="(Started|Reloaded) Fail2Ban Service" --log_path=systemd ynh_systemd_action --service_name=fail2ban --action=reload --line_match="(Started|Reloaded) Fail2Ban Service" --log_path=systemd
@ -117,7 +124,7 @@ ignoreregex =
# #
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_remove_fail2ban_config () { ynh_remove_fail2ban_config () {
ynh_secure_remove "/etc/fail2ban/jail.d/$app.conf" ynh_secure_remove --file="/etc/fail2ban/jail.d/$app.conf"
ynh_secure_remove "/etc/fail2ban/filter.d/$app.conf" ynh_secure_remove --file="/etc/fail2ban/filter.d/$app.conf"
ynh_systemd_action --service_name=fail2ban --action=reload ynh_systemd_action --service_name=fail2ban --action=reload
} }

View file

@ -7,7 +7,7 @@
# | arg: -t, --total - Count total RAM+swap # | arg: -t, --total - Count total RAM+swap
# | arg: -s, --ignore_swap - Ignore swap, consider only real RAM # | arg: -s, --ignore_swap - Ignore swap, consider only real RAM
# | arg: -o, --only_swap - Ignore real RAM, consider only swap # | arg: -o, --only_swap - Ignore real RAM, consider only swap
# | ret: the amount of free ram # | ret: the amount of free ram, in MB (MegaBytes)
# #
# Requires YunoHost version 3.8.1 or higher. # Requires YunoHost version 3.8.1 or higher.
ynh_get_ram () { ynh_get_ram () {
@ -70,13 +70,13 @@ ynh_get_ram () {
# Return 0 or 1 depending if the system has a given amount of RAM+swap free or total # Return 0 or 1 depending if the system has a given amount of RAM+swap free or total
# #
# usage: ynh_require_ram --required=RAM required in Mb [--free|--total] [--ignore_swap|--only_swap] # usage: ynh_require_ram --required=RAM [--free|--total] [--ignore_swap|--only_swap]
# | arg: -r, --required= - The amount to require, in Mb # | arg: -r, --required= - The amount to require, in MB
# | arg: -f, --free - Count free RAM+swap # | arg: -f, --free - Count free RAM+swap
# | arg: -t, --total - Count total RAM+swap # | arg: -t, --total - Count total RAM+swap
# | arg: -s, --ignore_swap - Ignore swap, consider only real RAM # | arg: -s, --ignore_swap - Ignore swap, consider only real RAM
# | arg: -o, --only_swap - Ignore real RAM, consider only swap # | arg: -o, --only_swap - Ignore real RAM, consider only swap
# | exit: Return 1 if the ram is under the requirement, 0 otherwise. # | ret: 1 if the ram is under the requirement, 0 otherwise.
# #
# Requires YunoHost version 3.8.1 or higher. # Requires YunoHost version 3.8.1 or higher.
ynh_require_ram () { ynh_require_ram () {

View file

@ -102,8 +102,7 @@ ynh_print_err () {
# Execute a command and print the result as an error # Execute a command and print the result as an error
# #
# usage: ynh_exec_err your_command # usage: ynh_exec_err "your_command [ | other_command ]"
# usage: ynh_exec_err "your_command | other_command"
# | arg: command - command to execute # | arg: command - command to execute
# #
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe. # When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
@ -117,8 +116,7 @@ ynh_exec_err () {
# Execute a command and print the result as a warning # Execute a command and print the result as a warning
# #
# usage: ynh_exec_warn your_command # usage: ynh_exec_warn "your_command [ | other_command ]"
# usage: ynh_exec_warn "your_command | other_command"
# | arg: command - command to execute # | arg: command - command to execute
# #
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe. # When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
@ -132,8 +130,7 @@ ynh_exec_warn () {
# Execute a command and force the result to be printed on stdout # Execute a command and force the result to be printed on stdout
# #
# usage: ynh_exec_warn_less your_command # usage: ynh_exec_warn_less "your_command [ | other_command ]"
# usage: ynh_exec_warn_less "your_command | other_command"
# | arg: command - command to execute # | arg: command - command to execute
# #
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe. # When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
@ -147,8 +144,7 @@ ynh_exec_warn_less () {
# Execute a command and redirect stdout in /dev/null # Execute a command and redirect stdout in /dev/null
# #
# usage: ynh_exec_quiet your_command # usage: ynh_exec_quiet "your_command [ | other_command ]"
# usage: ynh_exec_quiet "your_command | other_command"
# | arg: command - command to execute # | arg: command - command to execute
# #
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe. # When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
@ -162,8 +158,7 @@ ynh_exec_quiet () {
# Execute a command and redirect stdout and stderr in /dev/null # Execute a command and redirect stdout and stderr in /dev/null
# #
# usage: ynh_exec_fully_quiet your_command # usage: ynh_exec_fully_quiet "your_command [ | other_command ]"
# usage: ynh_exec_fully_quiet "your_command | other_command"
# | arg: command - command to execute # | arg: command - command to execute
# #
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe. # When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
@ -363,8 +358,7 @@ ynh_debug () {
# Execute a command and print the result as debug # Execute a command and print the result as debug
# #
# usage: ynh_debug_exec your_command # usage: ynh_debug_exec "your_command [ | other_command ]"
# usage: ynh_debug_exec "your_command | other_command"
# | arg: command - command to execute # | arg: command - command to execute
# #
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe. # When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.

View file

@ -7,16 +7,14 @@
# | arg: -n, --nonappend - (optional) Replace the config file instead of appending this new config. # | arg: -n, --nonappend - (optional) Replace the config file instead of appending this new config.
# | arg: -u, --specific_user= - run logrotate as the specified user and group. If not specified logrotate is runned as root. # | arg: -u, --specific_user= - run logrotate as the specified user and group. If not specified logrotate is runned as root.
# #
# If no --logfile is provided, /var/log/${app} will be used as default. # If no `--logfile` is provided, `/var/log/$app` will be used as default.
# logfile can be just a directory, or a full path to a logfile : # `logfile` can point to a directory or a file.
# /parentdir/logdir
# /parentdir/logdir/logfile.log
# #
# It's possible to use this helper multiple times, each config will be added to # It's possible to use this helper multiple times, each config will be added to
# the same logrotate config file. Unless you use the option --non-append # the same logrotate config file. Unless you use the option `--non-append`
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
# Requires YunoHost version 3.2.0 or higher for the argument --specific_user # Requires YunoHost version 3.2.0 or higher for the argument `--specific_user`
ynh_use_logrotate () { ynh_use_logrotate () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=lnuya local legacy_args=lnuya

96
data/helpers.d/multimedia Normal file
View file

@ -0,0 +1,96 @@
#!/bin/bash
readonly MEDIA_GROUP=multimedia
readonly MEDIA_DIRECTORY=/home/yunohost.multimedia
# Initialize the multimedia directory system
#
# usage: ynh_multimedia_build_main_dir
ynh_multimedia_build_main_dir() {
## Création du groupe multimedia
groupadd -f $MEDIA_GROUP
## Création des dossiers génériques
mkdir -p "$MEDIA_DIRECTORY"
mkdir -p "$MEDIA_DIRECTORY/share"
mkdir -p "$MEDIA_DIRECTORY/share/Music"
mkdir -p "$MEDIA_DIRECTORY/share/Picture"
mkdir -p "$MEDIA_DIRECTORY/share/Video"
mkdir -p "$MEDIA_DIRECTORY/share/eBook"
## Création des dossiers utilisateurs
for user in $(yunohost user list --output-as json | jq -r '.users | keys[]')
do
mkdir -p "$MEDIA_DIRECTORY/$user"
mkdir -p "$MEDIA_DIRECTORY/$user/Music"
mkdir -p "$MEDIA_DIRECTORY/$user/Picture"
mkdir -p "$MEDIA_DIRECTORY/$user/Video"
mkdir -p "$MEDIA_DIRECTORY/$user/eBook"
ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share"
# Création du lien symbolique dans le home de l'utilisateur.
ln -sfn "$MEDIA_DIRECTORY/$user" "/home/$user/Multimedia"
# Propriétaires des dossiers utilisateurs.
chown -R $user "$MEDIA_DIRECTORY/$user"
done
# Default yunohost hooks for post_user_create,delete will take care
# of creating/deleting corresponding multimedia folders when users
# are created/deleted in the future...
## Application des droits étendus sur le dossier multimedia.
# Droit d'écriture pour le groupe et le groupe multimedia en acl et droit de lecture pour other:
setfacl -RnL -m g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$MEDIA_DIRECTORY"
# Application de la même règle que précédemment, mais par défaut pour les nouveaux fichiers.
setfacl -RnL -m d:g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$MEDIA_DIRECTORY"
# Réglage du masque par défaut. Qui garantie (en principe...) un droit maximal à rwx. Donc pas de restriction de droits par l'acl.
setfacl -RL -m m::rwx "$MEDIA_DIRECTORY"
}
# Add a directory in yunohost.multimedia
#
# usage: ynh_multimedia_addfolder --source_dir="source_dir" --dest_dir="dest_dir"
#
# | arg: -s, --source_dir= - Source directory - The real directory which contains your medias.
# | arg: -d, --dest_dir= - Destination directory - The name and the place of the symbolic link, relative to "/home/yunohost.multimedia"
#
# This "directory" will be a symbolic link to a existing directory.
#
ynh_multimedia_addfolder() {
# Declare an array to define the options of this helper.
local legacy_args=sd
local -A args_array=( [s]=source_dir= [d]=dest_dir= )
local source_dir
local dest_dir
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Ajout d'un lien symbolique vers le dossier à partager
ln -sfn "$source_dir" "$MEDIA_DIRECTORY/$dest_dir"
## Application des droits étendus sur le dossier ajouté
# Droit d'écriture pour le groupe et le groupe multimedia en acl et droit de lecture pour other:
setfacl -RnL -m g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$source_dir"
# Application de la même règle que précédemment, mais par défaut pour les nouveaux fichiers.
setfacl -RnL -m d:g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$source_dir"
# Réglage du masque par défaut. Qui garantie (en principe...) un droit maximal à rwx. Donc pas de restriction de droits par l'acl.
setfacl -RL -m m::rwx "$source_dir"
}
# Allow an user to have an write authorisation in multimedia directories
#
# usage: ynh_multimedia_addaccess user_name
#
# | arg: -u, --user_name= - The name of the user which gain this access.
#
ynh_multimedia_addaccess () {
# Declare an array to define the options of this helper.
local legacy_args=u
declare -Ar args_array=( [u]=user_name=)
local user_name
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
groupadd -f multimedia
usermod -a -G multimedia $user_name
}

View file

@ -1,17 +1,16 @@
#!/bin/bash #!/bin/bash
MYSQL_ROOT_PWD_FILE=/etc/yunohost/mysql
# Open a connection as a user # Open a connection as a user
# #
# example: ynh_mysql_connect_as --user="user" --password="pass" <<< "UPDATE ...;"
# example: ynh_mysql_connect_as --user="user" --password="pass" < /path/to/file.sql
#
# usage: ynh_mysql_connect_as --user=user --password=password [--database=database] # usage: ynh_mysql_connect_as --user=user --password=password [--database=database]
# | arg: -u, --user= - the user name to connect as # | arg: -u, --user= - the user name to connect as
# | arg: -p, --password= - the user password # | arg: -p, --password= - the user password
# | arg: -d, --database= - the database to connect to # | arg: -d, --database= - the database to connect to
# #
# examples:
# ynh_mysql_connect_as --user="user" --password="pass" <<< "UPDATE ...;"
# ynh_mysql_connect_as --user="user" --password="pass" < /path/to/file.sql
#
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_mysql_connect_as() { ynh_mysql_connect_as() {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -49,8 +48,7 @@ ynh_mysql_execute_as_root() {
database="--database=$database" database="--database=$database"
fi fi
ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \ mysql -B "$database" <<< "$sql"
$database <<< "$sql"
} }
# Execute a command from a file as root user # Execute a command from a file as root user
@ -75,9 +73,7 @@ ynh_mysql_execute_file_as_root() {
database="--database=$database" database="--database=$database"
fi fi
mysql -B "$database" < "$file"
ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \
$database < "$file"
} }
# Create a database and grant optionnaly privilegies to a user # Create a database and grant optionnaly privilegies to a user
@ -125,11 +121,11 @@ ynh_mysql_drop_db() {
# Dump a database # Dump a database
# #
# example: ynh_mysql_dump_db --database=roundcube > ./dump.sql
#
# usage: ynh_mysql_dump_db --database=database # usage: ynh_mysql_dump_db --database=database
# | arg: -d, --database= - the database name to dump # | arg: -d, --database= - the database name to dump
# | ret: the mysqldump output # | ret: The mysqldump output
#
# example: ynh_mysql_dump_db --database=roundcube > ./dump.sql
# #
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_mysql_dump_db() { ynh_mysql_dump_db() {
@ -140,7 +136,7 @@ ynh_mysql_dump_db() {
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
mysqldump --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" --single-transaction --skip-dump-date "$database" mysqldump --single-transaction --skip-dump-date "$database"
} }
# Create a user # Create a user
@ -161,7 +157,7 @@ ynh_mysql_create_user() {
# #
# usage: ynh_mysql_user_exists --user=user # usage: ynh_mysql_user_exists --user=user
# | arg: -u, --user= - the user for which to check existence # | arg: -u, --user= - the user for which to check existence
# | exit: Return 1 if the user doesn't exist, 0 otherwise. # | ret: 0 if the user exists, 1 otherwise.
# #
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_mysql_user_exists() ynh_mysql_user_exists()
@ -200,8 +196,8 @@ ynh_mysql_drop_user() {
# | arg: -n, --db_name= - Name of the database # | arg: -n, --db_name= - Name of the database
# | arg: -p, --db_pwd= - Password of the database. If not provided, a password will be generated # | arg: -p, --db_pwd= - Password of the database. If not provided, a password will be generated
# #
# After executing this helper, the password of the created database will be available in $db_pwd # After executing this helper, the password of the created database will be available in `$db_pwd`
# It will also be stored as "mysqlpwd" into the app settings. # It will also be stored as "`mysqlpwd`" into the app settings.
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_mysql_setup_db () { ynh_mysql_setup_db () {
@ -214,12 +210,13 @@ ynh_mysql_setup_db () {
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
local new_db_pwd=$(ynh_string_random) # Generate a random password # Generate a random password
local new_db_pwd=$(ynh_string_random)
# If $db_pwd is not provided, use new_db_pwd instead for db_pwd # If $db_pwd is not provided, use new_db_pwd instead for db_pwd
db_pwd="${db_pwd:-$new_db_pwd}" db_pwd="${db_pwd:-$new_db_pwd}"
ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd" # Create the database ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd"
ynh_app_setting_set --app=$app --key=mysqlpwd --value=$db_pwd # Store the password in the app's config ynh_app_setting_set --app=$app --key=mysqlpwd --value=$db_pwd
} }
# Remove a database if it exists, and the associated user # Remove a database if it exists, and the associated user
@ -232,16 +229,14 @@ ynh_mysql_setup_db () {
ynh_mysql_remove_db () { ynh_mysql_remove_db () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=un local legacy_args=un
local -A args_array=( [u]=db_user= [n]=db_name= ) local -Ar args_array=( [u]=db_user= [n]=db_name= )
local db_user local db_user
local db_name local db_name
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
local mysql_root_password=$(cat $MYSQL_ROOT_PWD_FILE) if mysqlshow | grep -q "^| $db_name "; then
if mysqlshow --user=root --password=$mysql_root_password | grep --quiet "^| $db_name" ynh_mysql_drop_db $db_name
then # Check if the database exists
ynh_mysql_drop_db $db_name # Remove the database
else else
ynh_print_warn --message="Database $db_name not found" ynh_print_warn --message="Database $db_name not found"
fi fi

View file

@ -2,12 +2,12 @@
# Find a free port and return it # Find a free port and return it
# #
# example: port=$(ynh_find_port --port=8080)
#
# usage: ynh_find_port --port=begin_port # usage: ynh_find_port --port=begin_port
# | arg: -p, --port= - port to start to search # | arg: -p, --port= - port to start to search
# | ret: the port number # | ret: the port number
# #
# example: port=$(ynh_find_port --port=8080)
#
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_find_port () { ynh_find_port () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -27,11 +27,11 @@ ynh_find_port () {
# Test if a port is available # Test if a port is available
# #
# example: ynh_port_available --port=1234 || ynh_die "Port 1234 is needs to be available for this app"
#
# usage: ynh_find_port --port=XYZ # usage: ynh_find_port --port=XYZ
# | arg: -p, --port= - port to check # | arg: -p, --port= - port to check
# | exit: Return 1 if the port is already used by another process. # | ret: 0 if the port is available, 1 if it is already used by another process.
#
# example: ynh_port_available --port=1234 || ynh_die --message="Port 1234 is needs to be available for this app"
# #
# Requires YunoHost version 3.8.0 or higher. # Requires YunoHost version 3.8.0 or higher.
ynh_port_available () { ynh_port_available () {
@ -89,12 +89,12 @@ EOF
# Validate an IPv4 address # Validate an IPv4 address
# #
# example: ynh_validate_ip4 111.222.333.444
#
# usage: ynh_validate_ip4 --ip_address=ip_address # usage: ynh_validate_ip4 --ip_address=ip_address
# | arg: -i, --ip_address= - the ipv4 address to check # | arg: -i, --ip_address= - the ipv4 address to check
# | ret: 0 for valid ipv4 addresses, 1 otherwise # | ret: 0 for valid ipv4 addresses, 1 otherwise
# #
# example: ynh_validate_ip4 111.222.333.444
#
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_validate_ip4() ynh_validate_ip4()
{ {
@ -111,12 +111,12 @@ ynh_validate_ip4()
# Validate an IPv6 address # Validate an IPv6 address
# #
# example: ynh_validate_ip6 2000:dead:beef::1
#
# usage: ynh_validate_ip6 --ip_address=ip_address # usage: ynh_validate_ip6 --ip_address=ip_address
# | arg: -i, --ip_address= - the ipv6 address to check # | arg: -i, --ip_address= - the ipv6 address to check
# | ret: 0 for valid ipv6 addresses, 1 otherwise # | ret: 0 for valid ipv6 addresses, 1 otherwise
# #
# example: ynh_validate_ip6 2000:dead:beef::1
#
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_validate_ip6() ynh_validate_ip6()
{ {

View file

@ -4,13 +4,13 @@
# #
# usage: ynh_add_nginx_config # usage: ynh_add_nginx_config
# #
# This will use a template in ../conf/nginx.conf # This will use a template in `../conf/nginx.conf`
# See the documentation of ynh_add_config for a description of the template # See the documentation of `ynh_add_config` for a description of the template
# format and how placeholders are replaced with actual variables. # format and how placeholders are replaced with actual variables.
# #
# Additionally, ynh_add_nginx_config will replace: # Additionally, ynh_add_nginx_config will replace:
# - #sub_path_only by empty string if path_url is not '/' # - `#sub_path_only` by empty string if `path_url` is not `'/'`
# - #root_path_only by empty string if path_url *is* '/' # - `#root_path_only` by empty string if `path_url` *is* `'/'`
# #
# This allows to enable/disable specific behaviors dependenging on the install # This allows to enable/disable specific behaviors dependenging on the install
# location # location
@ -22,12 +22,12 @@ ynh_add_nginx_config () {
if [ "${path_url:-}" != "/" ] if [ "${path_url:-}" != "/" ]
then then
ynh_replace_string --match_string="^#sub_path_only" --replace_string="" --target_file="../conf/nginx.conf" ynh_replace_string --match_string="^#sub_path_only" --replace_string="" --target_file="$YNH_APP_BASEDIR/conf/nginx.conf"
else else
ynh_replace_string --match_string="^#root_path_only" --replace_string="" --target_file="../conf/nginx.conf" ynh_replace_string --match_string="^#root_path_only" --replace_string="" --target_file="$YNH_APP_BASEDIR/conf/nginx.conf"
fi fi
ynh_add_config --template="../conf/nginx.conf" --destination="$finalnginxconf" ynh_add_config --template="$YNH_APP_BASEDIR/conf/nginx.conf" --destination="$finalnginxconf"
ynh_systemd_action --service_name=nginx --action=reload ynh_systemd_action --service_name=nginx --action=reload

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
n_version=7.0.0 n_version=7.0.2
n_install_dir="/opt/node_n" n_install_dir="/opt/node_n"
node_version_path="$n_install_dir/n/versions/node" node_version_path="$n_install_dir/n/versions/node"
# N_PREFIX is the directory of n, it needs to be loaded as a environment variable. # N_PREFIX is the directory of n, it needs to be loaded as a environment variable.
@ -16,9 +16,8 @@ export N_PREFIX="$n_install_dir"
ynh_install_n () { ynh_install_n () {
ynh_print_info --message="Installation of N - Node.js version management" ynh_print_info --message="Installation of N - Node.js version management"
# Build an app.src for n # Build an app.src for n
mkdir --parents "../conf"
echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz
SOURCE_SUM=2933855140f980fc6d1d6103ea07cd4d915b17dea5e17e43921330ea89978b5b" > "../conf/n.src" SOURCE_SUM=fa80a8685f0fb1b4187fc0a1228b44f0ea2f244e063fe8f443b8913ea595af89" > "$YNH_APP_BASEDIR/conf/n.src"
# Download and extract n # Download and extract n
ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n
# Install n # Install n
@ -28,11 +27,14 @@ SOURCE_SUM=2933855140f980fc6d1d6103ea07cd4d915b17dea5e17e43921330ea89978b5b" > "
# Load the version of node for an app, and set variables. # Load the version of node for an app, and set variables.
# #
# ynh_use_nodejs has to be used in any app scripts before using node for the first time. # usage: ynh_use_nodejs
#
# `ynh_use_nodejs` has to be used in any app scripts before using node for the first time.
# This helper will provide alias and variables to use in your scripts. # This helper will provide alias and variables to use in your scripts.
# #
# To use npm or node, use the alias `ynh_npm` and `ynh_node` # To use npm or node, use the alias `ynh_npm` and `ynh_node`.
# Those alias will use the correct version installed for the app #
# Those alias will use the correct version installed for the app.
# For example: use `ynh_npm install` instead of `npm install` # For example: use `ynh_npm install` instead of `npm install`
# #
# With `sudo` or `ynh_exec_as`, use instead the fallback variables `$ynh_npm` and `$ynh_node` # With `sudo` or `ynh_exec_as`, use instead the fallback variables `$ynh_npm` and `$ynh_node`
@ -40,7 +42,7 @@ SOURCE_SUM=2933855140f980fc6d1d6103ea07cd4d915b17dea5e17e43921330ea89978b5b" > "
# Exemple: `ynh_exec_as $app $ynh_node_load_PATH $ynh_npm install` # Exemple: `ynh_exec_as $app $ynh_node_load_PATH $ynh_npm install`
# #
# $PATH contains the path of the requested version of node. # $PATH contains the path of the requested version of node.
# However, $PATH is duplicated into $node_PATH to outlast any manipulation of $PATH # However, $PATH is duplicated into $node_PATH to outlast any manipulation of `$PATH`
# You can use the variable `$ynh_node_load_PATH` to quickly load your node version # You can use the variable `$ynh_node_load_PATH` to quickly load your node version
# in $PATH for an usage into a separate script. # in $PATH for an usage into a separate script.
# Exemple: $ynh_node_load_PATH $final_path/script_that_use_npm.sh` # Exemple: $ynh_node_load_PATH $final_path/script_that_use_npm.sh`
@ -48,13 +50,17 @@ SOURCE_SUM=2933855140f980fc6d1d6103ea07cd4d915b17dea5e17e43921330ea89978b5b" > "
# #
# Finally, to start a nodejs service with the correct version, 2 solutions # Finally, to start a nodejs service with the correct version, 2 solutions
# Either the app is dependent of node or npm, but does not called it directly. # Either the app is dependent of node or npm, but does not called it directly.
# In such situation, you need to load PATH # In such situation, you need to load PATH :
# `Environment="__NODE_ENV_PATH__"` # ```
# `ExecStart=__FINALPATH__/my_app` # Environment="__NODE_ENV_PATH__"
# You will replace __NODE_ENV_PATH__ with $ynh_node_load_PATH # ExecStart=__FINALPATH__/my_app
# ```
# You will replace __NODE_ENV_PATH__ with $ynh_node_load_PATH.
# #
# Or node start the app directly, then you don't need to load the PATH variable # Or node start the app directly, then you don't need to load the PATH variable
# `ExecStart=__YNH_NODE__ my_app run` # ```
# ExecStart=__YNH_NODE__ my_app run
# ```
# You will replace __YNH_NODE__ with $ynh_node # You will replace __YNH_NODE__ with $ynh_node
# #
# #
@ -62,8 +68,6 @@ SOURCE_SUM=2933855140f980fc6d1d6103ea07cd4d915b17dea5e17e43921330ea89978b5b" > "
# - $nodejs_path: The absolute path to node binaries for the chosen version. # - $nodejs_path: The absolute path to node binaries for the chosen version.
# - $nodejs_version: Just the version number of node for this app. Stored as 'nodejs_version' in settings.yml. # - $nodejs_version: Just the version number of node for this app. Stored as 'nodejs_version' in settings.yml.
# #
# usage: ynh_use_nodejs
#
# Requires YunoHost version 2.7.12 or higher. # Requires YunoHost version 2.7.12 or higher.
ynh_use_nodejs () { ynh_use_nodejs () {
nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version) nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version)
@ -97,10 +101,10 @@ ynh_use_nodejs () {
# usage: ynh_install_nodejs --nodejs_version=nodejs_version # usage: ynh_install_nodejs --nodejs_version=nodejs_version
# | arg: -n, --nodejs_version= - Version of node to install. When possible, your should prefer to use major version number (e.g. 8 instead of 8.10.0). The crontab will then handle the update of minor versions when needed. # | arg: -n, --nodejs_version= - Version of node to install. When possible, your should prefer to use major version number (e.g. 8 instead of 8.10.0). The crontab will then handle the update of minor versions when needed.
# #
# n (Node version management) uses the PATH variable to store the path of the version of node it is going to use. # `n` (Node version management) uses the `PATH` variable to store the path of the version of node it is going to use.
# That's how it changes the version # That's how it changes the version
# #
# Refer to ynh_use_nodejs for more information about available commands and variables # Refer to `ynh_use_nodejs` for more information about available commands and variables
# #
# Requires YunoHost version 2.7.12 or higher. # Requires YunoHost version 2.7.12 or higher.
ynh_install_nodejs () { ynh_install_nodejs () {
@ -177,12 +181,12 @@ ynh_install_nodejs () {
# Remove the version of node used by the app. # Remove the version of node used by the app.
# #
# This helper will check if another app uses the same version of node,
# if not, this version of node will be removed.
# If no other app uses node, n will be also removed.
#
# usage: ynh_remove_nodejs # usage: ynh_remove_nodejs
# #
# This helper will check if another app uses the same version of node.
# - If not, this version of node will be removed.
# - If no other app uses node, n will be also removed.
#
# Requires YunoHost version 2.7.12 or higher. # Requires YunoHost version 2.7.12 or higher.
ynh_remove_nodejs () { ynh_remove_nodejs () {
nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version) nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version)

View file

@ -2,15 +2,17 @@
# Create a new permission for the app # Create a new permission for the app
# #
# example 1: ynh_permission_create --permission=admin --url=/admin --additional_urls=domain.tld/admin /superadmin --allowed=alice bob \ # Example 1: `ynh_permission_create --permission=admin --url=/admin --additional_urls=domain.tld/admin /superadmin --allowed=alice bob \
# --label="My app admin" --show_tile=true # --label="My app admin" --show_tile=true`
# #
# This example will create a new permission permission with this following effect: # This example will create a new permission permission with this following effect:
# - A tile named "My app admin" in the SSO will be available for the users alice and bob. This tile will point to the relative url '/admin'. # - A tile named "My app admin" in the SSO will be available for the users alice and bob. This tile will point to the relative url '/admin'.
# - Only the user alice and bob will have the access to theses following url: /admin, domain.tld/admin, /superadmin # - Only the user alice and bob will have the access to theses following url: /admin, domain.tld/admin, /superadmin
# #
# #
# example 2: ynh_permission_create --permission=api --url=domain.tld/api --auth_header=false --allowed=visitors \ # Example 2:
#
# ynh_permission_create --permission=api --url=domain.tld/api --auth_header=false --allowed=visitors \
# --label="MyApp API" --protected=true # --label="MyApp API" --protected=true
# #
# This example will create a new protected permission. So the admin won't be able to add/remove the visitors group of this permission. # This example will create a new protected permission. So the admin won't be able to add/remove the visitors group of this permission.
@ -25,19 +27,14 @@
# usage: ynh_permission_create --permission="permission" [--url="url"] [--additional_urls="second-url" [ "third-url" ]] [--auth_header=true|false] # usage: ynh_permission_create --permission="permission" [--url="url"] [--additional_urls="second-url" [ "third-url" ]] [--auth_header=true|false]
# [--allowed=group1 [ group2 ]] [--label="label"] [--show_tile=true|false] # [--allowed=group1 [ group2 ]] [--label="label"] [--show_tile=true|false]
# [--protected=true|false] # [--protected=true|false]
# | arg: -p, permission= - the name for the permission (by default a permission named "main" already exist) # | arg: -p, --permission= - the name for the permission (by default a permission named "main" already exist)
# | arg: -u, url= - (optional) URL for which access will be allowed/forbidden. # | arg: -u, --url= - (optional) URL for which access will be allowed/forbidden. Note that if 'show_tile' is enabled, this URL will be the URL of the tile.
# | Not that if 'show_tile' is enabled, this URL will be the URL of the tile. # | arg: -A, --additional_urls= - (optional) List of additional URL for which access will be allowed/forbidden
# | arg: -A, additional_urls= - (optional) List of additional URL for which access will be allowed/forbidden # | arg: -h, --auth_header= - (optional) Define for the URL of this permission, if SSOwat pass the authentication header to the application. Default is true
# | arg: -h, auth_header= - (optional) Define for the URL of this permission, if SSOwat pass the authentication header to the application. Default is true # | arg: -a, --allowed= - (optional) A list of group/user to allow for the permission
# | arg: -a, allowed= - (optional) A list of group/user to allow for the permission # | arg: -l, --label= - (optional) Define a name for the permission. This label will be shown on the SSO and in the admin. Default is "APP_LABEL (permission name)".
# | arg: -l, label= - (optional) Define a name for the permission. This label will be shown on the SSO and in the admin. # | arg: -t, --show_tile= - (optional) Define if a tile will be shown in the SSO. If yes the name of the tile will be the 'label' parameter. Defaults to false for the permission different than 'main'.
# | Default is "APP_LABEL (permission name)". # | arg: -P, --protected= - (optional) Define if this permission is protected. If it is protected the administrator won't be able to add or remove the visitors group of this permission. Defaults to 'false'.
# | arg: -t, show_tile= - (optional) Define if a tile will be shown in the SSO. If yes the name of the tile will be the 'label' parameter.
# | Default is false (for the permission different than 'main').
# | arg: -P, protected= - (optional) Define if this permission is protected. If it is protected the administrator
# | won't be able to add or remove the visitors group of this permission.
# | By default it's 'false'
# #
# If provided, 'url' or 'additional_urls' is assumed to be relative to the app domain/path if they # If provided, 'url' or 'additional_urls' is assumed to be relative to the app domain/path if they
# start with '/'. For example: # start with '/'. For example:
@ -185,20 +182,20 @@ ynh_permission_exists() {
local permission local permission
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
yunohost user permission list --short | grep --word-regexp --quiet "$app.$permission" yunohost user permission list --output-as json --quiet \
| jq -e --arg perm "$app.$permission" '.permissions[$perm]' >/dev/null
} }
# Redefine the url associated to a permission # Redefine the url associated to a permission
# #
# usage: ynh_permission_url --permission "permission" [--url="url"] [--add_url="new-url" [ "other-new-url" ]] [--remove_url="old-url" [ "other-old-url" ]] # usage: ynh_permission_url --permission "permission" [--url="url"] [--add_url="new-url" [ "other-new-url" ]] [--remove_url="old-url" [ "other-old-url" ]]
# [--auth_header=true|false] [--clear_urls] # [--auth_header=true|false] [--clear_urls]
# | arg: -p, permission= - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) # | arg: -p, --permission= - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
# | arg: -u, url= - (optional) URL for which access will be allowed/forbidden. # | arg: -u, --url= - (optional) URL for which access will be allowed/forbidden. Note that if you want to remove url you can pass an empty sting as arguments ("").
# | Note that if you want to remove url you can pass an empty sting as arguments (""). # | arg: -a, --add_url= - (optional) List of additional url to add for which access will be allowed/forbidden.
# | arg: -a, add_url= - (optional) List of additional url to add for which access will be allowed/forbidden. # | arg: -r, --remove_url= - (optional) List of additional url to remove for which access will be allowed/forbidden
# | arg: -r, remove_url= - (optional) List of additional url to remove for which access will be allowed/forbidden # | arg: -h, --auth_header= - (optional) Define for the URL of this permission, if SSOwat pass the authentication header to the application
# | arg: -h, auth_header= - (optional) Define for the URL of this permission, if SSOwat pass the authentication header to the application # | arg: -c, --clear_urls - (optional) Clean all urls (url and additional_urls)
# | arg: -c, clear_urls - (optional) Clean all urls (url and additional_urls)
# #
# Requires YunoHost version 3.7.0 or higher. # Requires YunoHost version 3.7.0 or higher.
ynh_permission_url() { ynh_permission_url() {
@ -268,13 +265,12 @@ ynh_permission_url() {
# #
# usage: ynh_permission_update --permission "permission" [--add="group" ["group" ...]] [--remove="group" ["group" ...]] # usage: ynh_permission_update --permission "permission" [--add="group" ["group" ...]] [--remove="group" ["group" ...]]
# [--label="label"] [--show_tile=true|false] [--protected=true|false] # [--label="label"] [--show_tile=true|false] [--protected=true|false]
# | arg: -p, permission= - the name for the permission (by default a permission named "main" already exist) # | arg: -p, --permission= - the name for the permission (by default a permission named "main" already exist)
# | arg: -a, add= - the list of group or users to enable add to the permission # | arg: -a, --add= - the list of group or users to enable add to the permission
# | arg: -r, remove= - the list of group or users to remove from the permission # | arg: -r, --remove= - the list of group or users to remove from the permission
# | arg: -l, label= - (optional) Define a name for the permission. This label will be shown on the SSO and in the admin. # | arg: -l, --label= - (optional) Define a name for the permission. This label will be shown on the SSO and in the admin.
# | arg: -t, show_tile= - (optional) Define if a tile will be shown in the SSO # | arg: -t, --show_tile= - (optional) Define if a tile will be shown in the SSO
# | arg: -P, protected= - (optional) Define if this permission is protected. If it is protected the administrator # | arg: -P, --protected= - (optional) Define if this permission is protected. If it is protected the administrator won't be able to add or remove the visitors group of this permission.
# | won't be able to add or remove the visitors group of this permission.
# #
# Requires YunoHost version 3.7.0 or higher. # Requires YunoHost version 3.7.0 or higher.
ynh_permission_update() { ynh_permission_update() {
@ -366,7 +362,8 @@ ynh_permission_has_user() {
return 1 return 1
fi fi
yunohost user permission info "$app.$permission" | grep --word-regexp --quiet "$user" yunohost user permission info "$app.$permission" --output-as json --quiet \
| jq -e --arg user $user '.corresponding_users | index($user)' >/dev/null
} }
# Check if a legacy permissions exist # Check if a legacy permissions exist

View file

@ -153,10 +153,7 @@ ynh_add_fpm_config () {
if [ $use_template -eq 1 ] if [ $use_template -eq 1 ]
then then
# Usage 1, use the template in conf/php-fpm.conf # Usage 1, use the template in conf/php-fpm.conf
local phpfpm_path="../conf/php-fpm.conf" local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf"
if [ ! -e "$phpfpm_path" ]; then
phpfpm_path="../settings/conf/php-fpm.conf" # Into the restore script, the PHP-FPM template is not at the same place
fi
# Make sure now that the template indeed exists # Make sure now that the template indeed exists
[ -e "$phpfpm_path" ] || ynh_die --message="Unable to find template to configure PHP-FPM." [ -e "$phpfpm_path" ] || ynh_die --message="Unable to find template to configure PHP-FPM."
else else
@ -169,7 +166,7 @@ ynh_add_fpm_config () {
# Define the values to use for the configuration of PHP. # Define the values to use for the configuration of PHP.
ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint
local phpfpm_path="../conf/php-fpm.conf" local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf"
echo " echo "
[__APP__] [__APP__]
@ -204,18 +201,18 @@ pm.process_idle_timeout = 10s
fi fi
# Concatene the extra config. # Concatene the extra config.
if [ -e ../conf/extra_php-fpm.conf ]; then if [ -e $YNH_APP_BASEDIR/conf/extra_php-fpm.conf ]; then
cat ../conf/extra_php-fpm.conf >> "$phpfpm_path" cat $YNH_APP_BASEDIR/conf/extra_php-fpm.conf >> "$phpfpm_path"
fi fi
fi fi
local finalphpconf="$fpm_config_dir/pool.d/$app.conf" local finalphpconf="$fpm_config_dir/pool.d/$app.conf"
ynh_add_config --template="$phpfpm_path" --destination="$finalphpconf" ynh_add_config --template="$phpfpm_path" --destination="$finalphpconf"
if [ -e "../conf/php-fpm.ini" ] if [ -e "$YNH_APP_BASEDIR/conf/php-fpm.ini" ]
then then
ynh_print_warn --message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead." ynh_print_warn --message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead."
ynh_add_config --template="../conf/php-fpm.ini" --destination="$fpm_config_dir/conf.d/20-$app.ini" ynh_add_config --template="$YNH_APP_BASEDIR/conf/php-fpm.ini" --destination="$fpm_config_dir/conf.d/20-$app.ini"
fi fi
if [ $dedicated_service -eq 1 ] if [ $dedicated_service -eq 1 ]
@ -228,7 +225,7 @@ pid = /run/php/php__PHPVERSION__-fpm-__APP__.pid
error_log = /var/log/php/fpm-php.__APP__.log error_log = /var/log/php/fpm-php.__APP__.log
syslog.ident = php-fpm-__APP__ syslog.ident = php-fpm-__APP__
include = __FINALPHPCONF__ include = __FINALPHPCONF__
" > ../conf/php-fpm-$app.conf " > $YNH_APP_BASEDIR/conf/php-fpm-$app.conf
ynh_add_config --template="../config/php-fpm-$app.conf" --destination="$globalphpconf" ynh_add_config --template="../config/php-fpm-$app.conf" --destination="$globalphpconf"
@ -245,7 +242,7 @@ ExecReload=/bin/kill -USR2 \$MAINPID
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
" > ../conf/$fpm_service " > $YNH_APP_BASEDIR/conf/$fpm_service
# Create this dedicated PHP-FPM service # Create this dedicated PHP-FPM service
ynh_add_systemd_config --service=$fpm_service --template=$fpm_service ynh_add_systemd_config --service=$fpm_service --template=$fpm_service
@ -340,7 +337,7 @@ ynh_install_php () {
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ] if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]
then then
ynh_die "Do not use ynh_install_php to install php$YNH_DEFAULT_PHP_VERSION" ynh_die --message="Do not use ynh_install_php to install php$YNH_DEFAULT_PHP_VERSION"
fi fi
# Create the file if doesn't exist already # Create the file if doesn't exist already
@ -572,6 +569,7 @@ YNH_COMPOSER_VERSION=${YNH_COMPOSER_VERSION:-$YNH_DEFAULT_COMPOSER_VERSION}
# | arg: -v, --phpversion - PHP version to use with composer # | arg: -v, --phpversion - PHP version to use with composer
# | arg: -w, --workdir - The directory from where the command will be executed. Default $final_path. # | arg: -w, --workdir - The directory from where the command will be executed. Default $final_path.
# | arg: -c, --commands - Commands to execute. # | arg: -c, --commands - Commands to execute.
#
ynh_composer_exec () { ynh_composer_exec () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=vwc local legacy_args=vwc
@ -596,6 +594,7 @@ ynh_composer_exec () {
# | arg: -w, --workdir - The directory from where the command will be executed. Default $final_path. # | arg: -w, --workdir - The directory from where the command will be executed. Default $final_path.
# | arg: -a, --install_args - Additional arguments provided to the composer install. Argument --no-dev already include # | arg: -a, --install_args - Additional arguments provided to the composer install. Argument --no-dev already include
# | arg: -c, --composerversion - Composer version to install # | arg: -c, --composerversion - Composer version to install
#
ynh_install_composer () { ynh_install_composer () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=vwac local legacy_args=vwac
@ -614,9 +613,9 @@ ynh_install_composer () {
curl -sS https://getcomposer.org/installer \ curl -sS https://getcomposer.org/installer \
| COMPOSER_HOME="$workdir/.composer" \ | COMPOSER_HOME="$workdir/.composer" \
php${phpversion} -- --quiet --install-dir="$workdir" --version=$composerversion \ php${phpversion} -- --quiet --install-dir="$workdir" --version=$composerversion \
|| ynh_die "Unable to install Composer." || ynh_die --message="Unable to install Composer."
# install dependencies # install dependencies
ynh_composer_exec --phpversion="${phpversion}" --workdir="$workdir" --commands="install --no-dev $install_args" \ ynh_composer_exec --phpversion="${phpversion}" --workdir="$workdir" --commands="install --no-dev $install_args" \
|| ynh_die "Unable to install core dependencies with Composer." || ynh_die --message="Unable to install core dependencies with Composer."
} }

View file

@ -5,15 +5,15 @@ PSQL_VERSION=11
# Open a connection as a user # Open a connection as a user
# #
# examples:
# ynh_psql_connect_as 'user' 'pass' <<< "UPDATE ...;"
# ynh_psql_connect_as 'user' 'pass' < /path/to/file.sql
#
# usage: ynh_psql_connect_as --user=user --password=password [--database=database] # usage: ynh_psql_connect_as --user=user --password=password [--database=database]
# | arg: -u, --user= - the user name to connect as # | arg: -u, --user= - the user name to connect as
# | arg: -p, --password= - the user password # | arg: -p, --password= - the user password
# | arg: -d, --database= - the database to connect to # | arg: -d, --database= - the database to connect to
# #
# examples:
# ynh_psql_connect_as 'user' 'pass' <<< "UPDATE ...;"
# ynh_psql_connect_as 'user' 'pass' < /path/to/file.sql
#
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_psql_connect_as() { ynh_psql_connect_as() {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -127,12 +127,12 @@ ynh_psql_drop_db() {
# Dump a database # Dump a database
# #
# example: ynh_psql_dump_db 'roundcube' > ./dump.sql
#
# usage: ynh_psql_dump_db --database=database # usage: ynh_psql_dump_db --database=database
# | arg: -d, --database= - the database name to dump # | arg: -d, --database= - the database name to dump
# | ret: the psqldump output # | ret: the psqldump output
# #
# example: ynh_psql_dump_db 'roundcube' > ./dump.sql
#
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_psql_dump_db() { ynh_psql_dump_db() {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -286,19 +286,20 @@ ynh_psql_remove_db() {
} }
# Create a master password and set up global settings # Create a master password and set up global settings
# It also make sure that postgresql is installed and running
# Please always call this script in install and restore scripts
# #
# usage: ynh_psql_test_if_first_run # usage: ynh_psql_test_if_first_run
# #
# It also make sure that postgresql is installed and running
# Please always call this script in install and restore scripts
#
# Requires YunoHost version 2.7.13 or higher. # Requires YunoHost version 2.7.13 or higher.
ynh_psql_test_if_first_run() { ynh_psql_test_if_first_run() {
# Make sure postgresql is indeed installed # Make sure postgresql is indeed installed
dpkg --list | grep -q "ii postgresql-$PSQL_VERSION" || ynh_die "postgresql-$PSQL_VERSION is not installed !?" dpkg --list | grep -q "ii postgresql-$PSQL_VERSION" || ynh_die --message="postgresql-$PSQL_VERSION is not installed !?"
# Check for some weird issue where postgresql could be installed but etc folder would not exist ... # Check for some weird issue where postgresql could be installed but etc folder would not exist ...
[ -e "/etc/postgresql/$PSQL_VERSION" ] || ynh_die "It looks like postgresql was not properly configured ? /etc/postgresql/$PSQL_VERSION is missing ... Could be due to a locale issue, c.f.https://serverfault.com/questions/426989/postgresql-etc-postgresql-doesnt-exist" [ -e "/etc/postgresql/$PSQL_VERSION" ] || ynh_die --message="It looks like postgresql was not properly configured ? /etc/postgresql/$PSQL_VERSION is missing ... Could be due to a locale issue, c.f.https://serverfault.com/questions/426989/postgresql-etc-postgresql-doesnt-exist"
# Make sure postgresql is started and enabled # Make sure postgresql is started and enabled
# (N.B. : to check the active state, we check the cluster state because # (N.B. : to check the active state, we check the cluster state because

View file

@ -108,12 +108,12 @@ EOF
# Check availability of a web path # Check availability of a web path
# #
# example: ynh_webpath_available --domain=some.domain.tld --path_url=/coffee
#
# usage: ynh_webpath_available --domain=domain --path_url=path # usage: ynh_webpath_available --domain=domain --path_url=path
# | arg: -d, --domain= - the domain/host of the url # | arg: -d, --domain= - the domain/host of the url
# | arg: -p, --path_url= - the web path to check the availability of # | arg: -p, --path_url= - the web path to check the availability of
# #
# example: ynh_webpath_available --domain=some.domain.tld --path_url=/coffee
#
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_webpath_available () { ynh_webpath_available () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -129,13 +129,13 @@ ynh_webpath_available () {
# Register/book a web path for an app # Register/book a web path for an app
# #
# example: ynh_webpath_register --app=wordpress --domain=some.domain.tld --path_url=/coffee
#
# usage: ynh_webpath_register --app=app --domain=domain --path_url=path # usage: ynh_webpath_register --app=app --domain=domain --path_url=path
# | arg: -a, --app= - the app for which the domain should be registered # | arg: -a, --app= - the app for which the domain should be registered
# | arg: -d, --domain= - the domain/host of the web path # | arg: -d, --domain= - the domain/host of the web path
# | arg: -p, --path_url= - the web path to be registered # | arg: -p, --path_url= - the web path to be registered
# #
# example: ynh_webpath_register --app=wordpress --domain=some.domain.tld --path_url=/coffee
#
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_webpath_register () { ynh_webpath_register () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.

View file

@ -2,12 +2,12 @@
# Generate a random string # Generate a random string
# #
# example: pwd=$(ynh_string_random --length=8)
#
# usage: ynh_string_random [--length=string_length] # usage: ynh_string_random [--length=string_length]
# | arg: -l, --length= - the string length to generate (default: 24) # | arg: -l, --length= - the string length to generate (default: 24)
# | ret: the generated string # | ret: the generated string
# #
# example: pwd=$(ynh_string_random --length=8)
#
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_string_random() { ynh_string_random() {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
@ -30,9 +30,8 @@ ynh_string_random() {
# | arg: -r, --replace_string= - String that will replace matches # | arg: -r, --replace_string= - String that will replace matches
# | arg: -f, --target_file= - File in which the string will be replaced. # | arg: -f, --target_file= - File in which the string will be replaced.
# #
# As this helper is based on sed command, regular expressions and # As this helper is based on sed command, regular expressions and references to
# references to sub-expressions can be used # sub-expressions can be used (see sed manual page for more information)
# (see sed manual page for more information)
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_replace_string () { ynh_replace_string () {
@ -86,14 +85,15 @@ ynh_replace_special_string () {
} }
# Sanitize a string intended to be the name of a database # Sanitize a string intended to be the name of a database
# (More specifically : replace - and . by _)
#
# example: dbname=$(ynh_sanitize_dbid $app)
# #
# usage: ynh_sanitize_dbid --db_name=name # usage: ynh_sanitize_dbid --db_name=name
# | arg: -n, --db_name= - name to correct/sanitize # | arg: -n, --db_name= - name to correct/sanitize
# | ret: the corrected name # | ret: the corrected name
# #
# example: dbname=$(ynh_sanitize_dbid $app)
#
# Underscorify the string (replace - and . by _)
#
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_sanitize_dbid () { ynh_sanitize_dbid () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.

View file

@ -3,26 +3,31 @@
# Create a dedicated systemd config # Create a dedicated systemd config
# #
# usage: ynh_add_systemd_config [--service=service] [--template=template] # usage: ynh_add_systemd_config [--service=service] [--template=template]
# | arg: -s, --service= - Service name (optionnal, $app by default) # | arg: -s, --service= - Service name (optionnal, `$app` by default)
# | arg: -t, --template= - Name of template file (optionnal, this is 'systemd' by default, meaning ./conf/systemd.service will be used as template) # | arg: -t, --template= - Name of template file (optionnal, this is 'systemd' by default, meaning `../conf/systemd.service` will be used as template)
# #
# This will use the template ../conf/<templatename>.service # This will use the template `../conf/<templatename>.service`.
# See the documentation of ynh_add_config for a description of the template #
# See the documentation of `ynh_add_config` for a description of the template
# format and how placeholders are replaced with actual variables. # format and how placeholders are replaced with actual variables.
# #
# Requires YunoHost version 2.7.11 or higher. # Requires YunoHost version 2.7.11 or higher.
ynh_add_systemd_config () { ynh_add_systemd_config () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=st local legacy_args=stv
local -A args_array=( [s]=service= [t]=template=) local -A args_array=( [s]=service= [t]=template= [v]=others_var=)
local service local service
local template local template
local others_var
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
local service="${service:-$app}" service="${service:-$app}"
local template="${template:-systemd.service}" template="${template:-systemd.service}"
others_var="${others_var:-}"
ynh_add_config --template="../conf/$template" --destination="/etc/systemd/system/$service.service" [[ -z "$others_var" ]] || ynh_print_warn --message="Packagers: using --others_var is unecessary since Yunohost 4.2"
ynh_add_config --template="$YNH_APP_BASEDIR/conf/$template" --destination="/etc/systemd/system/$service.service"
systemctl enable $service --quiet systemctl enable $service --quiet
systemctl daemon-reload systemctl daemon-reload
@ -56,10 +61,10 @@ ynh_remove_systemd_config () {
# Start (or other actions) a service, print a log in case of failure and optionnaly wait until the service is completely started # Start (or other actions) a service, print a log in case of failure and optionnaly wait until the service is completely started
# #
# usage: ynh_systemd_action [--service_name=service_name] [--action=action] [ [--line_match="line to match"] [--log_path=log_path] [--timeout=300] [--length=20] ] # usage: ynh_systemd_action [--service_name=service_name] [--action=action] [ [--line_match="line to match"] [--log_path=log_path] [--timeout=300] [--length=20] ]
# | arg: -n, --service_name= - Name of the service to start. Default : $app # | arg: -n, --service_name= - Name of the service to start. Default : `$app`
# | arg: -a, --action= - Action to perform with systemctl. Default: start # | arg: -a, --action= - Action to perform with systemctl. Default: start
# | arg: -l, --line_match= - Line to match - The line to find in the log to attest the service have finished to boot. If not defined it don't wait until the service is completely started. WARNING: When using --line_match, you should always add `ynh_clean_check_starting` into your `ynh_clean_setup` at the beginning of the script. Otherwise, tail will not stop in case of failure of the script. The script will then hang forever. # | arg: -l, --line_match= - Line to match - The line to find in the log to attest the service have finished to boot. If not defined it don't wait until the service is completely started.
# | arg: -p, --log_path= - Log file - Path to the log file. Default : /var/log/$app/$app.log # | arg: -p, --log_path= - Log file - Path to the log file. Default : `/var/log/$app/$app.log`
# | arg: -t, --timeout= - Timeout - The maximum time to wait before ending the watching. Default : 300 seconds. # | arg: -t, --timeout= - Timeout - The maximum time to wait before ending the watching. Default : 300 seconds.
# | arg: -e, --length= - Length of the error log : Default : 20 # | arg: -e, --length= - Length of the error log : Default : 20
# #
@ -117,6 +122,7 @@ ynh_systemd_action() {
then then
ynh_exec_err tail --lines=$length "$log_path" ynh_exec_err tail --lines=$length "$log_path"
fi fi
ynh_clean_check_starting
return 1 return 1
fi fi
@ -161,20 +167,18 @@ ynh_systemd_action() {
} }
# Clean temporary process and file used by ynh_check_starting # Clean temporary process and file used by ynh_check_starting
# (usually used in ynh_clean_setup scripts)
# #
# usage: ynh_clean_check_starting # [internal]
# #
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_clean_check_starting () { ynh_clean_check_starting () {
if [ -n "$pid_tail" ] if [ -n "${pid_tail:-}" ]
then then
# Stop the execution of tail. # Stop the execution of tail.
kill -SIGTERM $pid_tail 2>&1 kill -SIGTERM $pid_tail 2>&1
fi fi
if [ -n "$templog" ] if [ -n "${templog:-}" ]
then then
ynh_secure_remove "$templog" 2>&1 ynh_secure_remove --file="$templog" 2>&1
fi fi
} }

View file

@ -2,11 +2,11 @@
# Check if a YunoHost user exists # Check if a YunoHost user exists
# #
# example: ynh_user_exists 'toto' || exit 1
#
# usage: ynh_user_exists --username=username # usage: ynh_user_exists --username=username
# | arg: -u, --username= - the username to check # | arg: -u, --username= - the username to check
# | exit: Return 1 if the user doesn't exist, 0 otherwise # | ret: 0 if the user exists, 1 otherwise.
#
# example: ynh_user_exists 'toto' || echo "User does not exist"
# #
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_user_exists() { ynh_user_exists() {
@ -17,17 +17,17 @@ ynh_user_exists() {
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
yunohost user list --output-as json | grep --quiet "\"username\": \"${username}\"" yunohost user list --output-as json --quiet | jq -e ".users.${username}" >/dev/null
} }
# Retrieve a YunoHost user information # Retrieve a YunoHost user information
# #
# example: mail=$(ynh_user_get_info 'toto' 'mail')
#
# usage: ynh_user_get_info --username=username --key=key # usage: ynh_user_get_info --username=username --key=key
# | arg: -u, --username= - the username to retrieve info from # | arg: -u, --username= - the username to retrieve info from
# | arg: -k, --key= - the key to retrieve # | arg: -k, --key= - the key to retrieve
# | ret: string - the key's value # | ret: the value associate to that key
#
# example: mail=$(ynh_user_get_info 'toto' 'mail')
# #
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_user_get_info() { ynh_user_get_info() {
@ -39,27 +39,26 @@ ynh_user_get_info() {
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
yunohost user info "$username" --output-as plain | ynh_get_plain_key "$key" yunohost user info "$username" --output-as json --quiet | jq -r ".$key"
} }
# Get the list of YunoHost users # Get the list of YunoHost users
# #
# example: for u in $(ynh_user_list); do ...
#
# usage: ynh_user_list # usage: ynh_user_list
# | ret: string - one username per line # | ret: one username per line as strings
#
# example: for u in $(ynh_user_list); do ... ; done
# #
# Requires YunoHost version 2.4.0 or higher. # Requires YunoHost version 2.4.0 or higher.
ynh_user_list() { ynh_user_list() {
yunohost user list --output-as plain --quiet \ yunohost user list --output-as json --quiet | jq -r ".users | keys[]"
| awk '/^##username$/{getline; print}'
} }
# Check if a user exists on the system # Check if a user exists on the system
# #
# usage: ynh_system_user_exists --username=username # usage: ynh_system_user_exists --username=username
# | arg: -u, --username= - the username to check # | arg: -u, --username= - the username to check
# | exit: Return 1 if the user doesn't exist, 0 otherwise # | ret: 0 if the user exists, 1 otherwise.
# #
# Requires YunoHost version 2.2.4 or higher. # Requires YunoHost version 2.2.4 or higher.
ynh_system_user_exists() { ynh_system_user_exists() {
@ -77,7 +76,7 @@ ynh_system_user_exists() {
# #
# usage: ynh_system_group_exists --group=group # usage: ynh_system_group_exists --group=group
# | arg: -g, --group= - the group to check # | arg: -g, --group= - the group to check
# | exit: Return 1 if the group doesn't exist, 0 otherwise # | ret: 0 if the group exists, 1 otherwise.
# #
# Requires YunoHost version 3.5.0.2 or higher. # Requires YunoHost version 3.5.0.2 or higher.
ynh_system_group_exists() { ynh_system_group_exists() {
@ -93,17 +92,20 @@ ynh_system_group_exists() {
# Create a system user # Create a system user
# #
# examples:
# # Create a nextcloud user with no home directory and /usr/sbin/nologin login shell (hence no login capability)
# ynh_system_user_create --username=nextcloud
# # Create a discourse user using /var/www/discourse as home directory and the default login shell
# ynh_system_user_create --username=discourse --home_dir=/var/www/discourse --use_shell
#
# usage: ynh_system_user_create --username=user_name [--home_dir=home_dir] [--use_shell] # usage: ynh_system_user_create --username=user_name [--home_dir=home_dir] [--use_shell]
# | arg: -u, --username= - Name of the system user that will be create # | arg: -u, --username= - Name of the system user that will be create
# | arg: -h, --home_dir= - Path of the home dir for the user. Usually the final path of the app. If this argument is omitted, the user will be created without home # | arg: -h, --home_dir= - Path of the home dir for the user. Usually the final path of the app. If this argument is omitted, the user will be created without home
# | arg: -s, --use_shell - Create a user using the default login shell if present. If this argument is omitted, the user will be created with /usr/sbin/nologin shell # | arg: -s, --use_shell - Create a user using the default login shell if present. If this argument is omitted, the user will be created with /usr/sbin/nologin shell
# #
# Create a nextcloud user with no home directory and /usr/sbin/nologin login shell (hence no login capability) :
# ```
# ynh_system_user_create --username=nextcloud
# ```
# Create a discourse user using /var/www/discourse as home directory and the default login shell :
# ```
# ynh_system_user_create --username=discourse --home_dir=/var/www/discourse --use_shell
# ```
#
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_system_user_create () { ynh_system_user_create () {
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.

View file

@ -1,5 +1,7 @@
#!/bin/bash #!/bin/bash
YNH_APP_BASEDIR=$([[ "$(basename $0)" =~ ^backup|restore$ ]] && echo '../settings' || echo '..')
# Handle script crashes / failures # Handle script crashes / failures
# #
# [internal] # [internal]
@ -19,6 +21,9 @@
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_exit_properly () { ynh_exit_properly () {
local exit_code=$? local exit_code=$?
rm -rf "/var/cache/yunohost/download/"
if [ "$exit_code" -eq 0 ]; then if [ "$exit_code" -eq 0 ]; then
exit 0 # Exit without error if the script ended correctly exit 0 # Exit without error if the script ended correctly
fi fi
@ -46,9 +51,8 @@ ynh_exit_properly () {
# usage: ynh_abort_if_errors # usage: ynh_abort_if_errors
# #
# This configure the rest of the script execution such that, if an error occurs # This configure the rest of the script execution such that, if an error occurs
# or if an empty variable is used, the execution of the script stops # or if an empty variable is used, the execution of the script stops immediately
# immediately and a call to `ynh_clean_setup` is triggered if it has been # and a call to `ynh_clean_setup` is triggered if it has been defined by your script.
# defined by your script.
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_abort_if_errors () { ynh_abort_if_errors () {
@ -61,45 +65,37 @@ ynh_abort_if_errors () {
# #
# usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id] # usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id]
# | arg: -d, --dest_dir= - Directory where to setup sources # | arg: -d, --dest_dir= - Directory where to setup sources
# | arg: -s, --source_id= - Name of the app, if the package contains more than one app # | arg: -s, --source_id= - Name of the source, defaults to `app`
# #
# The file conf/app.src need to contains: # This helper will read `conf/${source_id}.src`, download and install the sources.
# #
# The src file need to contains:
# ```
# SOURCE_URL=Address to download the app archive # SOURCE_URL=Address to download the app archive
# SOURCE_SUM=Control sum # SOURCE_SUM=Control sum
# # (Optional) Program to check the integrity (sha256sum, md5sum...) # # (Optional) Program to check the integrity (sha256sum, md5sum...). Default: sha256
# # default: sha256
# SOURCE_SUM_PRG=sha256 # SOURCE_SUM_PRG=sha256
# # (Optional) Archive format # # (Optional) Archive format. Default: tar.gz
# # default: tar.gz
# SOURCE_FORMAT=tar.gz # SOURCE_FORMAT=tar.gz
# # (Optional) Put false if sources are directly in the archive root # # (Optional) Put false if sources are directly in the archive root. Default: true
# # default: true # # Instead of true, SOURCE_IN_SUBDIR could be the number of sub directories to remove.
# # Instead of true, SOURCE_IN_SUBDIR could be the number of sub directories
# # to remove.
# SOURCE_IN_SUBDIR=false # SOURCE_IN_SUBDIR=false
# # (Optionnal) Name of the local archive (offline setup support) # # (Optionnal) Name of the local archive (offline setup support). Default: ${src_id}.${src_format}
# # default: ${src_id}.${src_format}
# SOURCE_FILENAME=example.tar.gz # SOURCE_FILENAME=example.tar.gz
# # (Optional) If it set as false don't extract the source. # # (Optional) If it set as false don't extract the source. Default: true
# # (Useful to get a debian package or a python wheel.) # # (Useful to get a debian package or a python wheel.)
# # default: true
# SOURCE_EXTRACT=(true|false) # SOURCE_EXTRACT=(true|false)
# ```
# #
# Details: # The helper will:
# This helper downloads sources from SOURCE_URL if there is no local source # - Check if there is a local source archive in `/opt/yunohost-apps-src/$APP_ID/$SOURCE_FILENAME`
# archive in /opt/yunohost-apps-src/APP_ID/SOURCE_FILENAME # - Download `$SOURCE_URL` if there is no local archive
# # - Check the integrity with `$SOURCE_SUM_PRG -c --status`
# Next, it checks the integrity with "SOURCE_SUM_PRG -c --status" command. # - Uncompress the archive to `$dest_dir`.
# # - If `$SOURCE_IN_SUBDIR` is true, the first level directory of the archive will be removed.
# If it's ok, the source archive will be uncompressed in $dest_dir. If the # - If `$SOURCE_IN_SUBDIR` is a numeric value, the N first level directories will be removed.
# SOURCE_IN_SUBDIR is true, the first level directory of the archive will be # - Patches named `sources/patches/${src_id}-*.patch` will be applied to `$dest_dir`
# removed. # - Extra files in `sources/extra_files/$src_id` will be copied to dest_dir
# If SOURCE_IN_SUBDIR is a numeric value, 2 for example, the 2 first level
# directories will be removed
#
# Finally, patches named sources/patches/${src_id}-*.patch and extra files in
# sources/extra_files/$src_id will be applied to dest_dir
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_setup_source () { ynh_setup_source () {
@ -112,12 +108,7 @@ ynh_setup_source () {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
source_id="${source_id:-app}" # If the argument is not given, source_id equals "app" source_id="${source_id:-app}" # If the argument is not given, source_id equals "app"
local src_file_path="$YNH_CWD/../conf/${source_id}.src" local src_file_path="$YNH_APP_BASEDIR/conf/${source_id}.src"
# In case of restore script the src file is in an other path.
# So try to use the restore path if the general path point to no file.
if [ ! -e "$src_file_path" ]; then
src_file_path="$YNH_CWD/../settings/conf/${source_id}.src"
fi
# Load value from configuration file (see above for a small doc about this file # Load value from configuration file (see above for a small doc about this file
# format) # format)
@ -138,12 +129,20 @@ ynh_setup_source () {
if [ "$src_filename" = "" ]; then if [ "$src_filename" = "" ]; then
src_filename="${source_id}.${src_format}" src_filename="${source_id}.${src_format}"
fi fi
# (Unused?) mecanism where one can have the file in a special local cache to not have to download it...
local local_src="/opt/yunohost-apps-src/${YNH_APP_ID}/${src_filename}" local local_src="/opt/yunohost-apps-src/${YNH_APP_ID}/${src_filename}"
mkdir -p /var/cache/yunohost/download/${YNH_APP_ID}/
src_filename="/var/cache/yunohost/download/${YNH_APP_ID}/${src_filename}"
if test -e "$local_src" if test -e "$local_src"
then # Use the local source file if it is present then
cp $local_src $src_filename cp $local_src $src_filename
else # If not, download the source else
[ -n "$src_url" ] || ynh_die "Couldn't parse SOURCE_URL from $src_file_path ?"
# NB. we have to declare the var as local first, # NB. we have to declare the var as local first,
# otherwise 'local foo=$(false) || echo 'pwet'" does'nt work # otherwise 'local foo=$(false) || echo 'pwet'" does'nt work
# because local always return 0 ... # because local always return 0 ...
@ -197,34 +196,34 @@ ynh_setup_source () {
fi fi
# Apply patches # Apply patches
if (( $(find $YNH_CWD/../sources/patches/ -type f -name "${source_id}-*.patch" 2> /dev/null | wc --lines) > "0" )) if (( $(find $YNH_APP_BASEDIR/sources/patches/ -type f -name "${source_id}-*.patch" 2> /dev/null | wc --lines) > "0" ))
then then
(cd "$dest_dir" (cd "$dest_dir"
for p in $YNH_CWD/../sources/patches/${source_id}-*.patch for p in $YNH_APP_BASEDIR/sources/patches/${source_id}-*.patch
do do
patch --strip=1 < $p patch --strip=1 < $p
done) || ynh_die --message="Unable to apply patches" done) || ynh_die --message="Unable to apply patches"
fi fi
# Add supplementary files # Add supplementary files
if test -e "$YNH_CWD/../sources/extra_files/${source_id}"; then if test -e "$YNH_APP_BASEDIR/sources/extra_files/${source_id}"; then
cp --archive $YNH_CWD/../sources/extra_files/$source_id/. "$dest_dir" cp --archive $YNH_APP_BASEDIR/sources/extra_files/$source_id/. "$dest_dir"
fi fi
} }
# Curl abstraction to help with POST requests to local pages (such as installation forms) # Curl abstraction to help with POST requests to local pages (such as installation forms)
# #
# example: ynh_local_curl "/install.php?installButton" "foo=$var1" "bar=$var2"
#
# usage: ynh_local_curl "page_uri" "key1=value1" "key2=value2" ... # usage: ynh_local_curl "page_uri" "key1=value1" "key2=value2" ...
# | arg: page_uri - Path (relative to $path_url) of the page where POST data will be sent # | arg: page_uri - Path (relative to `$path_url`) of the page where POST data will be sent
# | arg: key1=value1 - (Optionnal) POST key and corresponding value # | arg: key1=value1 - (Optionnal) POST key and corresponding value
# | arg: key2=value2 - (Optionnal) Another POST key and corresponding value # | arg: key2=value2 - (Optionnal) Another POST key and corresponding value
# | arg: ... - (Optionnal) More POST keys and values # | arg: ... - (Optionnal) More POST keys and values
# #
# example: ynh_local_curl "/install.php?installButton" "foo=$var1" "bar=$var2"
#
# For multiple calls, cookies are persisted between each call for the same app # For multiple calls, cookies are persisted between each call for the same app
# #
# $domain and $path_url should be defined externally (and correspond to the domain.tld and the /path (of the app?)) # `$domain` and `$path_url` should be defined externally (and correspond to the domain.tld and the /path (of the app?))
# #
# Requires YunoHost version 2.6.4 or higher. # Requires YunoHost version 2.6.4 or higher.
ynh_local_curl () { ynh_local_curl () {
@ -265,20 +264,22 @@ ynh_local_curl () {
# Create a dedicated config file from a template # Create a dedicated config file from a template
# #
# usage: ynh_add_config --template="template" --destination="destination"
# | arg: -t, --template= - Template config file to use
# | arg: -d, --destination= - Destination of the config file
#
# examples: # examples:
# ynh_add_config --template=".env" --destination="$final_path/.env" # ynh_add_config --template=".env" --destination="$final_path/.env"
# ynh_add_config --template="../conf/.env" --destination="$final_path/.env" # ynh_add_config --template="../conf/.env" --destination="$final_path/.env"
# ynh_add_config --template="/etc/nginx/sites-available/default" --destination="etc/nginx/sites-available/mydomain.conf" # ynh_add_config --template="/etc/nginx/sites-available/default" --destination="etc/nginx/sites-available/mydomain.conf"
# #
# usage: ynh_add_config --template="template" --destination="destination"
# | arg: -t, --template= - Template config file to use
# | arg: -d, --destination= - Destination of the config file
#
# The template can be by default the name of a file in the conf directory # The template can be by default the name of a file in the conf directory
# of a YunoHost Package, a relative path or an absolute path # of a YunoHost Package, a relative path or an absolute path.
# The helper will use the template $template to generate a config file #
# $destination by replacing the following keywords with global variables # The helper will use the template `template` to generate a config file
# `destination` by replacing the following keywords with global variables
# that should be defined before calling this helper : # that should be defined before calling this helper :
# ```
# __PATH__ by $path_url # __PATH__ by $path_url
# __NAME__ by $app # __NAME__ by $app
# __NAMETOCHANGE__ by $app # __NAMETOCHANGE__ by $app
@ -286,15 +287,18 @@ ynh_local_curl () {
# __FINALPATH__ by $final_path # __FINALPATH__ by $final_path
# __PHPVERSION__ by $YNH_PHP_VERSION # __PHPVERSION__ by $YNH_PHP_VERSION
# __YNH_NODE_LOAD_PATH__ by $ynh_node_load_PATH # __YNH_NODE_LOAD_PATH__ by $ynh_node_load_PATH
# # ```
# And any dynamic variables that should be defined before calling this helper like: # And any dynamic variables that should be defined before calling this helper like:
# ```
# __DOMAIN__ by $domain # __DOMAIN__ by $domain
# __APP__ by $app # __APP__ by $app
# __VAR_1__ by $var_1 # __VAR_1__ by $var_1
# __VAR_2__ by $var_2 # __VAR_2__ by $var_2
# ```
# #
# The helper will verify the checksum and backup the destination file # The helper will verify the checksum and backup the destination file
# if it's different before applying the new template. # if it's different before applying the new template.
#
# And it will calculate and store the destination file checksum # And it will calculate and store the destination file checksum
# into the app settings when configuration is done. # into the app settings when configuration is done.
# #
@ -309,10 +313,8 @@ ynh_add_config () {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
local template_path local template_path
if [ -f "../conf/$template" ]; then if [ -f "$YNH_APP_BASEDIR/conf/$template" ]; then
template_path="../conf/$template" template_path="$YNH_APP_BASEDIR/conf/$template"
elif [ -f "../settings/conf/$template" ]; then
template_path="../settings/conf/$template"
elif [ -f "$template" ]; then elif [ -f "$template" ]; then
template_path=$template template_path=$template
else else
@ -321,7 +323,7 @@ ynh_add_config () {
ynh_backup_if_checksum_is_different --file="$destination" ynh_backup_if_checksum_is_different --file="$destination"
cp "$template_path" "$destination" cp -f "$template_path" "$destination"
chown root: "$destination" chown root: "$destination"
ynh_replace_vars --file="$destination" ynh_replace_vars --file="$destination"
@ -387,20 +389,24 @@ ynh_replace_vars () {
# Replace others variables # Replace others variables
# List other unique (__ __) variables in $file # List other unique (__ __) variables in $file
local uniques_vars=( $(grep -o '__[A-Z0-9_]*__' $file | sort --unique | sed "s@__\([^.]*\)__@\L\1@g" )) local uniques_vars=( $(grep -oP '__[A-Z0-9]+?[A-Z0-9_]*?[A-Z0-9]*?__' $file | sort --unique | sed "s@__\([^.]*\)__@\L\1@g" ))
# Do the replacement # Do the replacement
local delimit=@ local delimit=@
for one_var in "${uniques_vars[@]}" for one_var in "${uniques_vars[@]}"
do do
# Validate that one_var is indeed defined # Validate that one_var is indeed defined
# Explanation for the weird '+x' syntax: https://stackoverflow.com/a/13864829 # -v checks if the variable is defined, for example:
test -n "${one_var+x}" || ynh_die --message="Variable \$$one_var wasn't initialized when trying to replace __${one_var^^}__ in $file" # -v FOO tests if $FOO is defined
# -v $FOO tests if ${!FOO} is defined
# More info: https://stackoverflow.com/questions/3601515/how-to-check-if-a-variable-is-set-in-bash/17538964#comment96392525_17538964
[[ -v "${one_var:-}" ]] || ynh_die --message="Variable \$$one_var wasn't initialized when trying to replace __${one_var^^}__ in $file"
# Escape delimiter in match/replace string # Escape delimiter in match/replace string
match_string="__${one_var^^}__" match_string="__${one_var^^}__"
match_string=${match_string//${delimit}/"\\${delimit}"} match_string=${match_string//${delimit}/"\\${delimit}"}
replace_string="${!one_var}" replace_string="${!one_var}"
replace_string=${replace_string//\\/\\\\}
replace_string=${replace_string//${delimit}/"\\${delimit}"} replace_string=${replace_string//${delimit}/"\\${delimit}"}
# Actually replace (sed is used instead of ynh_replace_string to avoid triggering an epic amount of debug logs) # Actually replace (sed is used instead of ynh_replace_string to avoid triggering an epic amount of debug logs)
@ -504,12 +510,7 @@ ynh_secure_remove () {
# #
# [internal] # [internal]
# #
# example: yunohost user info tata --output-as plain | ynh_get_plain_key mail # (Deprecated, use --output-as json and jq instead)
#
# usage: ynh_get_plain_key key [subkey [subsubkey ...]]
# | ret: string - the key's value
#
# Requires YunoHost version 2.2.4 or higher.
ynh_get_plain_key() { ynh_get_plain_key() {
local prefix="#" local prefix="#"
local founded=0 local founded=0
@ -556,22 +557,23 @@ ynh_read_manifest () {
if [ ! -e "$manifest" ]; then if [ ! -e "$manifest" ]; then
# If the manifest isn't found, try the common place for backup and restore script. # If the manifest isn't found, try the common place for backup and restore script.
manifest="../settings/manifest.json" manifest="$YNH_APP_BASEDIR/manifest.json"
fi fi
jq ".$manifest_key" "$manifest" --raw-output jq ".$manifest_key" "$manifest" --raw-output
} }
# Read the upstream version from the manifest, or from the env variable $YNH_APP_MANIFEST_VERSION if not given # Read the upstream version from the manifest or `$YNH_APP_MANIFEST_VERSION`
# #
# usage: ynh_app_upstream_version [--manifest="manifest.json"] # usage: ynh_app_upstream_version [--manifest="manifest.json"]
# | arg: -m, --manifest= - Path of the manifest to read # | arg: -m, --manifest= - Path of the manifest to read
# | ret: the version number of the upstream app # | ret: the version number of the upstream app
# #
# The version number in the manifest is defined by <upstreamversion>~ynh<packageversion> # If the `manifest` is not specified, the envvar `$YNH_APP_MANIFEST_VERSION` will be used.
# For example : 4.3-2~ynh3 #
# This include the number before ~ynh # The version number in the manifest is defined by `<upstreamversion>~ynh<packageversion>`.
# In the last example it return 4.3-2 #
# For example, if the manifest contains `4.3-2~ynh3` the function will return `4.3-2`
# #
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_app_upstream_version () { ynh_app_upstream_version () {
@ -599,10 +601,9 @@ ynh_app_upstream_version () {
# | arg: -m, --manifest= - Path of the manifest to read # | arg: -m, --manifest= - Path of the manifest to read
# | ret: the version number of the package # | ret: the version number of the package
# #
# The version number in the manifest is defined by <upstreamversion>~ynh<packageversion> # The version number in the manifest is defined by `<upstreamversion>~ynh<packageversion>`.
# For example : 4.3-2~ynh3 #
# This include the number after ~ynh # For example, if the manifest contains `4.3-2~ynh3` the function will return `3`
# In the last example it return 3
# #
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_app_package_version () { ynh_app_package_version () {
@ -619,18 +620,16 @@ ynh_app_package_version () {
# Checks the app version to upgrade with the existing app version and returns: # Checks the app version to upgrade with the existing app version and returns:
# #
# - UPGRADE_PACKAGE if only the YunoHost package has changed # usage: ynh_check_app_version_changed
# - UPGRADE_APP otherwise # | ret: `UPGRADE_APP` if the upstream version changed, `UPGRADE_PACKAGE` otherwise.
# #
# This helper should be used to avoid an upgrade of an app, or the upstream part # This helper should be used to avoid an upgrade of an app, or the upstream part
# of it, when it's not needed # of it, when it's not needed
# #
# To force an upgrade, even if the package is up to date, # You can force an upgrade, even if the package is up to date, with the `--force` (or `-F`) argument :
# you have to use the parameter --force (or -F). # ```
# example: sudo yunohost app upgrade MyApp --force # sudo yunohost app upgrade <appname> --force
# # ```
# usage: ynh_check_app_version_changed
#
# Requires YunoHost version 3.5.0 or higher. # Requires YunoHost version 3.5.0 or higher.
ynh_check_app_version_changed () { ynh_check_app_version_changed () {
local return_value=${YNH_APP_UPGRADE_TYPE} local return_value=${YNH_APP_UPGRADE_TYPE}
@ -644,24 +643,23 @@ ynh_check_app_version_changed () {
} }
# Compare the current package version against another version given as an argument. # Compare the current package version against another version given as an argument.
# This is really useful when we need to do some actions only for some old package versions. #
# usage: ynh_compare_current_package_version --comparison (lt|le|eq|ne|ge|gt) --version <X~ynhY>
# | arg: --comparison - Comparison type. Could be : `lt` (lower than), `le` (lower or equal), `eq` (equal), `ne` (not equal), `ge` (greater or equal), `gt` (greater than)
# | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like `2.3.1~ynh4`)
# | ret: 0 if the evaluation is true, 1 if false.
# #
# example: ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1 # example: ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1
# This example will check if the installed version is lower than (lt) the version 2.3.2~ynh1
# #
# Generally you might probably use it as follow in the upgrade script # This helper is usually used when we need to do some actions only for some old package versions.
# #
# Generally you might probably use it as follow in the upgrade script :
# ```
# if ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1 # if ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1
# then # then
# # Do something that is needed for the package version older than 2.3.2~ynh1 # # Do something that is needed for the package version older than 2.3.2~ynh1
# fi # fi
# # ```
# usage: ynh_compare_current_package_version --comparison lt|le|eq|ne|ge|gt
# | arg: --comparison - Comparison type. Could be : lt (lower than), le (lower or equal),
# | eq (equal), ne (not equal), ge (greater or equal), gt (greater than)
# | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4)
#
# Return 0 if the evaluation is true. 1 if false.
# #
# Requires YunoHost version 3.8.0 or higher. # Requires YunoHost version 3.8.0 or higher.
ynh_compare_current_package_version() { ynh_compare_current_package_version() {
@ -682,7 +680,7 @@ ynh_compare_current_package_version() {
# Check validity of the comparator # Check validity of the comparator
if [[ ! $comparison =~ (lt|le|eq|ne|ge|gt) ]]; then if [[ ! $comparison =~ (lt|le|eq|ne|ge|gt) ]]; then
ynh_die --message="Invialid comparator must be : lt, le, eq, ne, ge, gt" ynh_die --message="Invalid comparator must be : lt, le, eq, ne, ge, gt"
fi fi
# Return the return value of dpkg --compare-versions # Return the return value of dpkg --compare-versions

View file

@ -8,4 +8,3 @@ cd "$YNH_CWD"
# Backup the configuration # Backup the configuration
ynh_exec_warn_less ynh_backup --src_path="/etc/yunohost/dyndns" --not_mandatory ynh_exec_warn_less ynh_backup --src_path="/etc/yunohost/dyndns" --not_mandatory
ynh_exec_warn_less ynh_backup --src_path="/etc/cron.d/yunohost-dyndns" --not_mandatory

View file

@ -50,6 +50,8 @@ do_init_regen() {
chown root:root /etc/ssowat/conf.json.persistent chown root:root /etc/ssowat/conf.json.persistent
mkdir -p /var/cache/yunohost/repo mkdir -p /var/cache/yunohost/repo
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
} }
do_pre_regen() { do_pre_regen() {
@ -86,6 +88,17 @@ SHELL=/bin/bash
0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%1200)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably" 0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%1200)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably"
EOF EOF
# If we subscribed to a dyndns domain, add the corresponding cron
# - delay between 0 and 60 secs to spread the check over a 1 min window
# - do not run the command if some process already has the lock, to avoid queuing hundreds of commands...
if ls -l /etc/yunohost/dyndns/K*.private 2>/dev/null
then
cat > $pending_dir/etc/cron.d/yunohost-dyndns << EOF
SHELL=/bin/bash
*/10 * * * * root : YunoHost DynDNS update; sleep \$((RANDOM\\%60)); test -e /var/run/moulinette_yunohost.lock || yunohost dyndns update >> /dev/null
EOF
fi
# legacy stuff to avoid yunohost reporting etckeeper as manually modified # legacy stuff to avoid yunohost reporting etckeeper as manually modified
# (this make sure that the hash is null / file is flagged as to-delete) # (this make sure that the hash is null / file is flagged as to-delete)
mkdir -p $pending_dir/etc/etckeeper mkdir -p $pending_dir/etc/etckeeper
@ -117,6 +130,11 @@ do_post_regen() {
# Enfore permissions # # Enfore permissions #
###################### ######################
chmod 750 /home/yunohost.conf
chmod 750 /home/yunohost.backup
chmod 750 /home/yunohost.backup/archives
chown root:root /home/yunohost.conf
chown admin:root /home/yunohost.backup
chown admin:root /home/yunohost.backup/archives chown admin:root /home/yunohost.backup/archives
# Certs # Certs
@ -126,6 +144,9 @@ do_post_regen() {
find /etc/yunohost/certs/ -type f -exec chmod 640 {} \; find /etc/yunohost/certs/ -type f -exec chmod 640 {} \;
find /etc/yunohost/certs/ -type d -exec chmod 750 {} \; find /etc/yunohost/certs/ -type d -exec chmod 750 {} \;
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
# Misc configuration / state files # Misc configuration / state files
chown root:root $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null) chown root:root $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
chmod 600 $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null) chmod 600 $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)

View file

@ -1,7 +1,6 @@
#!/bin/bash #!/bin/bash
set -e set -e
MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')"
. /usr/share/yunohost/helpers . /usr/share/yunohost/helpers
do_pre_regen() { do_pre_regen() {
@ -15,6 +14,45 @@ do_pre_regen() {
do_post_regen() { do_post_regen() {
regen_conf_files=$1 regen_conf_files=$1
if [[ ! -d /var/lib/mysql/mysql ]]
then
# dpkg-reconfigure will initialize mysql (if it ain't already)
# It enabled auth_socket for root, so no need to define any root password...
# c.f. : cat /var/lib/dpkg/info/mariadb-server-10.3.postinst | grep install_db -C3
MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')"
dpkg-reconfigure -freadline -u "$MYSQL_PKG" 2>&1
systemctl -q is-active mariadb.service \
|| systemctl start mariadb
sleep 5
echo "" | mysql && echo "Can't connect to mysql using unix_socket auth ... something went wrong during initial configuration of mysql !?" >&2
fi
# Legacy code to get rid of /etc/yunohost/mysql ...
# Nowadays, we can simply run mysql while being run as root of unix_socket/auth_socket is enabled...
if [ -f /etc/yunohost/mysql ]; then
# This is a trick to check if we're able to use mysql without password
# Expect instances installed in stretch to already have unix_socket
#configured, but not old instances from the jessie/wheezy era
if ! echo "" | mysql
then
password="$(cat /etc/yunohost/mysql)"
# Enable plugin unix_socket for root on localhost
mysql -u root -p"$password" <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED WITH unix_socket WITH GRANT OPTION;"
fi
# If now we're able to login without password, drop the mysql password
if echo "" | mysql
then
rm /etc/yunohost/mysql
else
echo "Can't connect to mysql using unix_socket auth ... something went wrong while trying to get rid of mysql password !?" >&2
fi
fi
# mysql is supposed to be an alias to mariadb... but in some weird case is not # mysql is supposed to be an alias to mariadb... but in some weird case is not
# c.f. https://forum.yunohost.org/t/mysql-ne-fonctionne-pas/11661 # c.f. https://forum.yunohost.org/t/mysql-ne-fonctionne-pas/11661
# Playing with enable/disable allows to recreate the proper symlinks. # Playing with enable/disable allows to recreate the proper symlinks.
@ -27,44 +65,6 @@ do_post_regen() {
systemctl is-active mariadb -q || systemctl start mariadb systemctl is-active mariadb -q || systemctl start mariadb
fi fi
if [ ! -f /etc/yunohost/mysql ]; then
# ensure that mysql is running
systemctl -q is-active mysql.service \
|| service mysql start
# generate and set new root password
mysql_password=$(ynh_string_random 10)
mysqladmin -s -u root -pyunohost password "$mysql_password" || {
if [ $FORCE -eq 1 ]; then
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, and is going to reset the MySQL root password." \
"You can find this new password in /etc/yunohost/mysql." >&2
# set new password with debconf
debconf-set-selections << EOF
$MYSQL_PKG mysql-server/root_password password $mysql_password
$MYSQL_PKG mysql-server/root_password_again password $mysql_password
EOF
# reconfigure Debian package
dpkg-reconfigure -freadline -u "$MYSQL_PKG" 2>&1
else
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, but the MySQL root password is unknown." \
"You must either pass --force to reset the password or" \
"put the current one into the file /etc/yunohost/mysql." >&2
exit 1
fi
}
# store new root password
echo "$mysql_password" | tee /etc/yunohost/mysql
chmod 400 /etc/yunohost/mysql
fi
[[ -z "$regen_conf_files" ]] \ [[ -z "$regen_conf_files" ]] \
|| service mysql restart || service mysql restart
} }

View file

@ -43,7 +43,7 @@ class PortsDiagnoser(Diagnoser):
for ipversion in ipversions: for ipversion in ipversions:
try: try:
r = Diagnoser.remote_diagnosis( r = Diagnoser.remote_diagnosis(
"check-ports", data={"ports": ports.keys()}, ipversion=ipversion "check-ports", data={"ports": list(ports)}, ipversion=ipversion
) )
results[ipversion] = r["ports"] results[ipversion] = r["ports"]
except Exception as e: except Exception as e:

View file

@ -77,7 +77,7 @@ class SystemResourcesDiagnoser(Diagnoser):
# Ignore /dev/loop stuff which are ~virtual partitions ? (e.g. mounted to /snap/) # Ignore /dev/loop stuff which are ~virtual partitions ? (e.g. mounted to /snap/)
disk_partitions = [ disk_partitions = [
d for d in disk_partitions if not d.device.startswith("/dev/loop") d for d in disk_partitions if d.mountpoint in ["/", "/var"] or not d.device.startswith("/dev/loop")
] ]
for disk_partition in disk_partitions: for disk_partition in disk_partitions:
@ -139,7 +139,7 @@ class SystemResourcesDiagnoser(Diagnoser):
status="ERROR", status="ERROR",
summary="diagnosis_rootfstotalspace_critical", summary="diagnosis_rootfstotalspace_critical",
) )
if main_space < 14 * GB: elif main_space < 14 * GB:
yield dict( yield dict(
meta={"test": "rootfstotalspace"}, meta={"test": "rootfstotalspace"},
data={"space": human_size(main_space)}, data={"space": human_size(main_space)},

View file

@ -0,0 +1,28 @@
#!/bin/bash
user=$1
readonly MEDIA_GROUP=multimedia
readonly MEDIA_DIRECTORY=/home/yunohost.multimedia
# We only do this if multimedia directory is enabled (= the folder exists)
[ -e "$MEDIA_DIRECTORY" ] || exit 0
mkdir -p "$MEDIA_DIRECTORY/$user"
mkdir -p "$MEDIA_DIRECTORY/$user/Music"
mkdir -p "$MEDIA_DIRECTORY/$user/Picture"
mkdir -p "$MEDIA_DIRECTORY/$user/Video"
mkdir -p "$MEDIA_DIRECTORY/$user/eBook"
ln -sfn "$MEDIA_DIRECTORY/share" "$MEDIA_DIRECTORY/$user/Share"
# Création du lien symbolique dans le home de l'utilisateur.
ln -sfn "$MEDIA_DIRECTORY/$user" "/home/$user/Multimedia"
# Propriétaires des dossiers utilisateurs.
chown -R $user "$MEDIA_DIRECTORY/$user"
## Application des droits étendus sur le dossier multimedia.
# Droit d'écriture pour le groupe et le groupe multimedia en acl et droit de lecture pour other:
setfacl -RnL -m g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$MEDIA_DIRECTORY/$user"
# Application de la même règle que précédemment, mais par défaut pour les nouveaux fichiers.
setfacl -RnL -m d:g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$MEDIA_DIRECTORY/$user"
# Réglage du masque par défaut. Qui garantie (en principe...) un droit maximal à rwx. Donc pas de restriction de droits par l'acl.
setfacl -RL -m m::rwx "$MEDIA_DIRECTORY/$user"

View file

@ -0,0 +1,8 @@
#!/bin/bash
user=$1
MEDIA_DIRECTORY=/home/yunohost.multimedia
if [ -n "$user" ] && [ -e "$MEDIA_DIRECTORY/$user" ]; then
sudo rm -r "$MEDIA_DIRECTORY/$user"
fi

View file

@ -1,5 +0,0 @@
# We don't backup/restore mysql password anymore
# c.f. https://github.com/YunoHost/yunohost/pull/912
# This is a dummy empty file as a workaround for
# https://github.com/YunoHost/issues/issues/1553 until it is fixed

View file

@ -7,4 +7,3 @@ cd "$YNH_CWD"
# Restore file if exists # Restore file if exists
ynh_restore_file --origin_path="/etc/yunohost/dyndns" --not_mandatory ynh_restore_file --origin_path="/etc/yunohost/dyndns" --not_mandatory
ynh_restore_file --origin_path="/etc/cron.d/yunohost-dyndns" --not_mandatory

66
debian/changelog vendored
View file

@ -1,8 +1,68 @@
yunohost (4.2) unstable; urgency=low yunohost (4.2.0) testing; urgency=low
- Placeholder for 4.2 to satisfy CI / debian build during dev - [mod] Python2 -> Python3 ([#1116](https://github.com/yunohost/yunohost/pull/1116), a97a9df3, 1387dff4, b53859db, f5ab4443, f9478b93, dc6033c3)
- [mod] refactoring: Drop legacy-way of passing arguments in hook_exec, prevent exposing secrets in command line args ([#1096](https://github.com/yunohost/yunohost/pull/1096))
- [mod] refactoring: use regen_conf instead of service_regen_conf in settings.py (9c11fd58)
- [mod] refactoring: More consistent local CA management for simpler postinstall ([#1062](https://github.com/yunohost/yunohost/pull/1062))
- [mod] refactoring: init folders during .deb install instead of regen conf ([#1063](https://github.com/yunohost/yunohost/pull/1063))
- [mod] refactoring: init ldap before the postinstall ([#1064](https://github.com/yunohost/yunohost/pull/1064))
- [mod] refactoring: simpler and more consistent logging initialization ([#1119](https://github.com/yunohost/yunohost/pull/1119), 0884a0c1)
- [mod] code-quality: add CI job to auto-format code, fix linter errors ([#1142](https://github.com/yunohost/yunohost/pull/1142), [#1161](https://github.com/yunohost/yunohost/pull/1161), 97f26015, [#1162](https://github.com/yunohost/yunohost/pull/1162))
- [mod] misc: Prevent the installation of apache2 ... ([#1148](https://github.com/yunohost/yunohost/pull/1148))
- [mod] misc: Drop old cache rules for .ms files, not relevant anymore ([#1150](https://github.com/yunohost/yunohost/pull/1150))
- [fix] misc: Abort postinstall if /etc/yunohost/apps ain't empty ([#1147](https://github.com/yunohost/yunohost/pull/1147))
- [mod] misc: No need for mysql root password anymore ([#912](https://github.com/YunoHost/yunohost/pull/912))
- [fix] app operations: wait for services to finish reloading (4a19a60b)
- [enh] ux: Improve error semantic such that the webadmin can autoredirect to the proper log view ([#1077](https://github.com/yunohost/yunohost/pull/1077), [#1187](https://github.com/YunoHost/yunohost/pull/1187))
- [mod] cli/api: Misc command and routes renaming / aliasing ([#1146](https://github.com/yunohost/yunohost/pull/1146))
- [enh] cli: Add a new "yunohost app search" command ([#1070](https://github.com/yunohost/yunohost/pull/1070))
- [enh] cli: Add '--remove-apps' (and '--force') options to "yunohost domain remove" ([#1125](https://github.com/yunohost/yunohost/pull/1125))
- [enh] diagnosis: Report low total space for rootfs ([#1145](https://github.com/yunohost/yunohost/pull/1145))
- [fix] upnp: Handle port closing ([#1154](https://github.com/yunohost/yunohost/pull/1154))
- [fix] dyndns: clean old madness, improve update strategy, improve cron management, delete dyndns key upon domain removal ([#1149](https://github.com/yunohost/yunohost/pull/1149))
- [enh] helpers: Adding composer helper ([#1090](https://github.com/yunohost/yunohost/pull/1090))
- [enh] helpers: Upgrade n to v7.0.2 ([#1178](https://github.com/yunohost/yunohost/pull/1178))
- [enh] helpers: Add multimedia helpers and hooks ([#1129](https://github.com/yunohost/yunohost/pull/1129), 47420c62)
- [enh] helpers: Normalize conf template handling for nginx, php-fpm, systemd and fail2ban using ynh_add_config ([#1118](https://github.com/yunohost/yunohost/pull/1118))
- [fix] helpers, doc: Update template for the new doc (grav) ([#1167](https://github.com/yunohost/yunohost/pull/1167), [#1168](https://github.com/yunohost/yunohost/pull/1168), 59d3e387)
- [enh] helpers: Define YNH_APP_BASEDIR to be able to properly point to conf folder depending on the app script we're running ([#1172](https://github.com/yunohost/yunohost/pull/1172))
- [enh] helpers: Use jq / output-as json to get info from yunohost commands instead of scraping with grep ([#1160](https://github.com/yunohost/yunohost/pull/1160))
- [fix] helpers: Misc fixes/enh (b85d959d, db93b82b, ce04570b, 07f8d6d7)
- [fix] helpers: download ynh_setup_source stuff in /var/cache/yunohost to prevent situations where it ends up in /etc/yunohost/apps/ (d98ec6ce)
- [i18n] Translations updated for Catalan, Chinese (Simplified), Czech, Dutch, French, German, Italian, Occitan, Polish
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 20 Jan 2021 05:19:58 +0100 Thanks to all contributors <3 ! (Bram, Christian W., Daniel, Dave, Éric G., Félix P., Flavio C., Kay0u, Krzysztof N., ljf, Mathieu M., Miloš K., MrMorals, Nils V.Z., penguin321, ppr, Quentí, Radek S, Scapharnaum, Sébastien M., xaloc33, yalh76, Yifei D.)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 25 Mar 2021 01:00:00 +0100
yunohost (4.1.7.4) stable; urgency=low
- [fix] sec: Enforce permissions for /home/yunohost.backup and .conf (41b5a123)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 11 Mar 2021 03:08:10 +0100
yunohost (4.1.7.3) stable; urgency=low
- [fix] log: Some secrets were not redacted (0c172cd3)
- [fix] log: For some reason sometimes we were redacting 'empty string' which made everything explode (88b414c8)
- [fix] helpers: Various fixes for ynh_add_config / ynh_replace_vars (a43cd72c, 2728801d, 9bbc3b72, 2402a1db, 6ce02270)
- [fix] helpers: Fix permission helpers doc format (d12f403f)
- [fix] helpers: ynh_systemd_action did not properly clean the 'tail' process when service action failed (05969184)
- [fix] i18n: Translation typo in italian translation ... (bd8644a6)
Thanks to all contributors <3 ! (Kay0u, yalh76)
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 02 Mar 2021 02:03:35 +0100
yunohost (4.1.7.2) stable; urgency=low
- [fix] When migration legacy protected permissions, all users were allowed on the new perm (29bd3c4a)
- [fix] Mysql is a fucking joke (... trying to fix the mysql issue on RPi ...) (cd4fdb2b)
- [fix] Replace \t when converting legacy conf.json.persistent... (f398f463)
Thanks to all contributors <3 ! (ljf)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 21 Feb 2021 05:25:49 +0100
yunohost (4.1.7.1) stable; urgency=low yunohost (4.1.7.1) stable; urgency=low

3
debian/control vendored
View file

@ -26,7 +26,8 @@ Depends: ${python3:Depends}, ${misc:Depends}
, rspamd, opendkim-tools, postsrsd, procmail, mailutils , rspamd, opendkim-tools, postsrsd, procmail, mailutils
, redis-server , redis-server
, metronome (>=3.14.0) , metronome (>=3.14.0)
, git, curl, wget, cron, unzip, jq, bc , acl
, git, curl, wget, cron, unzip, jq, bc, at
, lsb-release, haveged, fake-hwclock, equivs, lsof, whois , lsb-release, haveged, fake-hwclock, equivs, lsof, whois
Recommends: yunohost-admin Recommends: yunohost-admin
, ntp, inetutils-ping | iputils-ping , ntp, inetutils-ping | iputils-ping

View file

@ -12,30 +12,31 @@ Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{{
{% for category, helpers in data.helpers %} {% for category, helpers in data.helpers %}
### {{ category.upper() }} ### {{ category.upper() }}
{% for h in helpers %} {% for h in helpers %}
**{{ h.name }}** **{{ h.name }}**<br/>
[details summary="<i>{{ h.brief }}</i>" class="helper-card-subtitle text-muted"] [details summary="<i>{{ h.brief }}</i>" class="helper-card-subtitle text-muted"]
<p></p> <p></p>
**Usage**: `{{ h.usage }}` **Usage**: `{{ h.usage }}`
{% if h.args %} {%- if h.args %}
**Arguments**: **Arguments**:
{% for infos in h.args %} {%- for infos in h.args %}
{% if infos|length == 2 %} {%- if infos|length == 2 %}
- `{{ infos[0] }}`: {{ infos[1] }} - `{{ infos[0] }}`: {{ infos[1] }}
{% else %} {%- else %}
- `{{ infos[0] }}`, `{{ infos[1] }}`: {{ infos[2] }} - `{{ infos[0] }}`, `{{ infos[1] }}`: {{ infos[2] }}
{% endif %} {%- endif %}
{% endfor %} {%- endfor %}
{% endif %} {%- endif %}
{% if h.ret %} {%- if h.ret %}
**Returns**: {{ h.ret }} **Returns**: {{ h.ret }}
{% endif %} {%- endif %}
{% if "example" in h.keys() %} {%- if "example" in h.keys() %}
**Example**: `{{ h.example }}` **Example**: `{{ h.example }}`
{% endif %} {%- endif %}
{% if "examples" in h.keys() %} {%- if "examples" in h.keys() %}
**Examples**: **Examples**:
{% for example in h.examples %} {% for example in h.examples %}
@ -45,12 +46,12 @@ Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{{
- `{{ example.strip("# ") }}` - `{{ example.strip("# ") }}`
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {%- endif %}
{% if h.details %} {%- if h.details %}
**Details**: **Details**:<br/>
{{ h.details.replace('\n', '</br>').replace('_', '\_') }} {{ h.details }}
{% endif %} {%- endif %}
[Dude, show me the code!](https://github.com/YunoHost/yunohost/blob/{{ current_commit }}/data/helpers.d/{{ category }}#L{{ h.line + 1 }}) [Dude, show me the code!](https://github.com/YunoHost/yunohost/blob/{{ current_commit }}/data/helpers.d/{{ category }}#L{{ h.line + 1 }})
[/details] [/details]

View file

@ -714,5 +714,8 @@
"additional_urls_already_removed": "URL addicional «{url:s}» ja ha estat eliminada per al permís «{permission:s}»", "additional_urls_already_removed": "URL addicional «{url:s}» ja ha estat eliminada per al permís «{permission:s}»",
"additional_urls_already_added": "URL addicional «{url:s}» ja ha estat afegida per al permís «{permission:s}»", "additional_urls_already_added": "URL addicional «{url:s}» ja ha estat afegida per al permís «{permission:s}»",
"diagnosis_backports_in_sources_list": "Sembla que apt (el gestor de paquets) està configurat per utilitzar el repositori backports. A menys de saber el que esteu fent, recomanem fortament no instal·lar paquets de backports, ja que poder causar inestabilitats o conflictes en el sistema.", "diagnosis_backports_in_sources_list": "Sembla que apt (el gestor de paquets) està configurat per utilitzar el repositori backports. A menys de saber el que esteu fent, recomanem fortament no instal·lar paquets de backports, ja que poder causar inestabilitats o conflictes en el sistema.",
"diagnosis_basesystem_hardware_model": "El model del servidor és {model}" "diagnosis_basesystem_hardware_model": "El model del servidor és {model}",
"postinstall_low_rootfsspace": "El sistema de fitxers arrel té un total de menys de 10 GB d'espai, el que es preocupant! És molt probable que us quedeu sense espai ràpidament! Es recomana tenir un mínim de 16 GB per al sistema de fitxers arrel. Si voleu instal·lar YunoHost tot i aquest avís, torneu a executar la postinstal·lació amb --force-diskspace",
"diagnosis_rootfstotalspace_critical": "El sistema de fitxers arrel només té {space} en total i és preocupant! És molt probable que us quedeu sense espai ràpidament! Es recomanar tenir un mínim de 16 GB per al sistema de fitxers arrel.",
"diagnosis_rootfstotalspace_warning": "El sistema de fitxers arrel només té {space} en total. Això no hauria de causar cap problema, però haureu de parar atenció ja que us podrieu quedar sense espai ràpidament… Es recomanar tenir un mínim de 16 GB per al sistema de fitxers arrel."
} }

View file

@ -1 +1,13 @@
{} {
"password_too_simple_1": "Heslo musí být aspoň 8 znaků dlouhé",
"app_already_installed": "{app:s} je již nainstalován/a",
"already_up_to_date": "Neprovedena žádná akce. Vše je již aktuální.",
"admin_password_too_long": "Zvolte prosím heslo kratší než 127 znaků",
"admin_password_changed": "Heslo správce bylo změněno",
"admin_password_change_failed": "Nebylo možné změnit heslo",
"admin_password": "Heslo správce",
"additional_urls_already_removed": "Dotatečný odkaz '{url:s}' byl již odebrán u oprávnění '{permission:s}'",
"additional_urls_already_added": "Dotatečný odkaz '{url:s}' byl již přidán v dodatečných odkazech pro oprávnění '{permission:s}'",
"action_invalid": "Nesprávné akce '{action:s}'",
"aborting": "Přerušení."
}

View file

@ -60,26 +60,26 @@
"dyndns_key_generating": "Generierung des DNS-Schlüssels..., das könnte eine Weile dauern.", "dyndns_key_generating": "Generierung des DNS-Schlüssels..., das könnte eine Weile dauern.",
"dyndns_registered": "DynDNS Domain registriert", "dyndns_registered": "DynDNS Domain registriert",
"dyndns_registration_failed": "DynDNS Domain konnte nicht registriert werden: {error:s}", "dyndns_registration_failed": "DynDNS Domain konnte nicht registriert werden: {error:s}",
"dyndns_unavailable": "DynDNS Subdomain ist nicht verfügbar", "dyndns_unavailable": "Die Domäne {domain:s} ist nicht verfügbar.",
"executing_command": "Führe den Behfehl '{command:s}' aus…", "executing_command": "Führe den Behfehl '{command:s}' aus…",
"executing_script": "Skript '{script:s}' wird ausgeührt…", "executing_script": "Skript '{script:s}' wird ausgeührt…",
"extracting": "Wird entpackt", "extracting": "Wird entpackt...",
"field_invalid": "Feld '{:s}' ist unbekannt", "field_invalid": "Feld '{:s}' ist unbekannt",
"firewall_reload_failed": "Die Firewall konnte nicht neu geladen werden", "firewall_reload_failed": "Firewall konnte nicht neu geladen werden",
"firewall_reloaded": "Die Firewall wurde neu geladen", "firewall_reloaded": "Firewall neu geladen",
"firewall_rules_cmd_failed": "Einzelne Firewallregeln konnten nicht übernommen werden. Mehr Informationen sind im Log zu finden.", "firewall_rules_cmd_failed": "Einige Befehle für die Firewallregeln sind gescheitert. Mehr Informationen im Log.",
"hook_exec_failed": "Skriptausführung fehlgeschlagen: {path:s}", "hook_exec_failed": "Konnte Skript nicht ausführen: {path:s}",
"hook_exec_not_terminated": "Skriptausführung noch nicht beendet: {path:s}", "hook_exec_not_terminated": "Skript ist nicht normal beendet worden: {path:s}",
"hook_list_by_invalid": "Ungültiger Wert zur Anzeige von Hooks", "hook_list_by_invalid": "Dieser Wert kann nicht verwendet werden, um Hooks anzuzeigen",
"hook_name_unknown": "Hook '{name:s}' ist nicht bekannt", "hook_name_unknown": "Hook '{name:s}' ist nicht bekannt",
"installation_complete": "Installation vollständig", "installation_complete": "Installation vollständig",
"installation_failed": "Installation fehlgeschlagen", "installation_failed": "Etwas ist mit der Installation falsch gelaufen",
"ip6tables_unavailable": "ip6tables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt", "ip6tables_unavailable": "ip6tables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt",
"iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt", "iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt",
"ldap_initialized": "LDAP wurde initialisiert", "ldap_initialized": "LDAP wurde initialisiert",
"mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden", "mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden",
"mail_domain_unknown": "Die Domäne '{domain:s}' dieser E-Mail-Adresse ist ungültig. Wähle bitte eine Domäne, welche durch diesen Server verwaltet wird.", "mail_domain_unknown": "Die Domäne '{domain:s}' dieser E-Mail-Adresse ist ungültig. Wähle bitte eine Domäne, welche durch diesen Server verwaltet wird.",
"mail_forward_remove_failed": "Mailweiterleitung '{mail:s}' konnte nicht entfernt werden", "mail_forward_remove_failed": "Die Weiterleitungs-E-Mail '{mail:s}' konnte nicht gelöscht werden",
"main_domain_change_failed": "Die Hauptdomain konnte nicht geändert werden", "main_domain_change_failed": "Die Hauptdomain konnte nicht geändert werden",
"main_domain_changed": "Die Hauptdomain wurde geändert", "main_domain_changed": "Die Hauptdomain wurde geändert",
"no_internet_connection": "Der Server ist nicht mit dem Internet verbunden", "no_internet_connection": "Der Server ist nicht mit dem Internet verbunden",
@ -170,13 +170,13 @@
"certmanager_certificate_fetching_or_enabling_failed": "Die Aktivierung des neuen Zertifikats für die {domain:s} ist fehlgeschlagen...", "certmanager_certificate_fetching_or_enabling_failed": "Die Aktivierung des neuen Zertifikats für die {domain:s} ist fehlgeschlagen...",
"certmanager_attempt_to_renew_nonLE_cert": "Das Zertifikat der Domain '{domain:s}' wurde nicht von Let's Encrypt ausgestellt. Es kann nicht automatisch erneuert werden!", "certmanager_attempt_to_renew_nonLE_cert": "Das Zertifikat der Domain '{domain:s}' wurde nicht von Let's Encrypt ausgestellt. Es kann nicht automatisch erneuert werden!",
"certmanager_attempt_to_renew_valid_cert": "Das Zertifikat der Domain {domain:s} läuft nicht in Kürze ab! (Benutze --force um diese Nachricht zu umgehen)", "certmanager_attempt_to_renew_valid_cert": "Das Zertifikat der Domain {domain:s} läuft nicht in Kürze ab! (Benutze --force um diese Nachricht zu umgehen)",
"certmanager_domain_http_not_working": "Die Domäne {domain:s} scheint über HTTP nicht erreichbar zu sein. Für weitere Informationen überprüfen Sie bitte die Kategorie 'Web' im Diagnose-Bereich. (Wenn Sie wißen was Sie tun, nutzen Sie '--no-checks' um die Überprüfung zu überspringen.)", "certmanager_domain_http_not_working": "Es scheint so, dass die Domain {domain:s} nicht über HTTP erreicht werden kann. Bitte überprüfe, ob deine DNS und nginx Konfiguration in Ordnung ist. (Wenn du weißt was du tust, nutze \"--no-checks\" um die überprüfung zu überspringen.)",
"certmanager_error_no_A_record": "Kein DNS 'A' Eintrag für die Domain {domain:s} gefunden. Dein Domainname muss auf diese Maschine weitergeleitet werden, um ein Let's Encrypt Zertifikat installieren zu können! (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )", "certmanager_error_no_A_record": "Kein DNS 'A' Eintrag für die Domain {domain:s} gefunden. Dein Domainname muss auf diese Maschine weitergeleitet werden, um ein Let's Encrypt Zertifikat installieren zu können! (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )",
"certmanager_domain_dns_ip_differs_from_public_ip": "Die DNS-Einträge der Domäne {domain:s} unterscheiden sich von der IP dieses Servers. Wenn Sie gerade Ihren A-Eintrag verändert haben, warten Sie bitte etwas, damit die Änderungen wirksam werden (Sie können die DNS Propagation mittels Website überprüfen) (Wenn Sie wißen was Sie tun, können Sie --no-checks benutzen, um diese Überprüfung zu überspringen. )", "certmanager_domain_dns_ip_differs_from_public_ip": "Der DNS-A-Eintrag der Domain {domain:s} unterscheidet sich von dieser Server-IP. Für weitere Informationen überprüfen Sie bitte die 'DNS records' (basic) Kategorie in der Diagnose. Wenn Sie gerade Ihren A-Eintrag verändert haben, warten Sie bitte etwas, damit die Änderungen wirksam werden (Sie können die DNS-Propagation mittels Website überprüfen) (Wenn Sie wissen was Sie tun, können Sie --no-checks benutzen, um diese Überprüfung zu überspringen.)",
"certmanager_cannot_read_cert": "Es ist ein Fehler aufgetreten, als es versucht wurde das aktuelle Zertifikat für die Domain {domain:s} zu öffnen (Datei: {file:s}), Grund: {reason:s}", "certmanager_cannot_read_cert": "Es ist ein Fehler aufgetreten, als es versucht wurde das aktuelle Zertifikat für die Domain {domain:s} zu öffnen (Datei: {file:s}), Grund: {reason:s}",
"certmanager_cert_install_success_selfsigned": "Ein selbstsigniertes Zertifikat für die Domain {domain:s} wurde erfolgreich installiert", "certmanager_cert_install_success_selfsigned": "Ein selbstsigniertes Zertifikat für die Domain {domain:s} wurde erfolgreich installiert",
"certmanager_cert_install_success": "Für die Domain {domain:s} wurde erfolgreich ein Let's Encrypt Zertifikat installiert.", "certmanager_cert_install_success": "Für die Domain {domain:s} wurde erfolgreich ein Let's Encrypt Zertifikat installiert.",
"certmanager_cert_renew_success": "Das Let's Encrypt Zertifikat für die Domain {domain:s} wurde erfolgreich erneuert.", "certmanager_cert_renew_success": "Das Let's Encrypt Zertifikat für die Domain {domain:s} wurde erfolgreich erneuert",
"certmanager_hit_rate_limit": "Es wurden innerhalb kurzer Zeit zu viele Zertifikate für dieselbe Domain {domain:s} ausgestellt. Bitte versuchen Sie es später nochmal. Besuchen Sie https://letsencrypt.org/docs/rate-limits/ für mehr Informationen", "certmanager_hit_rate_limit": "Es wurden innerhalb kurzer Zeit zu viele Zertifikate für dieselbe Domain {domain:s} ausgestellt. Bitte versuchen Sie es später nochmal. Besuchen Sie https://letsencrypt.org/docs/rate-limits/ für mehr Informationen",
"certmanager_cert_signing_failed": "Das neue Zertifikat konnte nicht signiert werden", "certmanager_cert_signing_failed": "Das neue Zertifikat konnte nicht signiert werden",
"certmanager_no_cert_file": "Die Zertifikatsdatei für die Domain {domain:s} (Datei: {file:s}) konnte nicht gelesen werden", "certmanager_no_cert_file": "Die Zertifikatsdatei für die Domain {domain:s} (Datei: {file:s}) konnte nicht gelesen werden",
@ -203,11 +203,11 @@
"backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden", "backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden",
"app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}", "app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}",
"backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository...", "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository...",
"global_settings_bad_type_for_setting": "Falscher Typ r Einstellung {setting:s}. Empfangen: {received_type:s}, aber erwartet: {expected_type:s}", "global_settings_bad_type_for_setting": "Falscher Typ der Einstellung {setting:s}. Empfangen: {received_type:s}, aber erwarteter Typ: {expected_type:s}",
"global_settings_bad_choice_for_enum": "Falsche Wahl für die Einstellung {setting:s}. Habe '{choice:s}' erhalten, aber es stehen nur folgende Auswahlmöglichkeiten zur Verfügung: {available_choices:s}", "global_settings_bad_choice_for_enum": "Wert des Einstellungsparameters {setting:s} ungültig. Der Wert den Sie eingegeben haben: '{choice:s}', die gültigen Werte für diese Einstellung: {available_choices:s}",
"file_does_not_exist": "Die Datei {path:s} existiert nicht.", "file_does_not_exist": "Die Datei {path: s} existiert nicht.",
"experimental_feature": "Warnung: Diese Funktion ist experimentell und gilt nicht als stabil. Sie sollten sie nur verwenden, wenn Sie wissen, was Sie tun.", "experimental_feature": "Warnung: Der Maintainer hat diese Funktion als experimentell gekennzeichnet. Sie ist nicht stabil. Sie sollten sie nur verwenden, wenn Sie wissen, was Sie tun.",
"dyndns_domain_not_provided": "Der DynDNS-Anbieter {provider:s} kann die Domain(s) {domain:s} nicht bereitstellen.", "dyndns_domain_not_provided": "Der DynDNS-Anbieter {provider:s} kann die Domäne(n) {domain:s} nicht bereitstellen.",
"dyndns_could_not_check_available": "Konnte nicht überprüfen, ob {domain:s} auf {provider:s} verfügbar ist.", "dyndns_could_not_check_available": "Konnte nicht überprüfen, ob {domain:s} auf {provider:s} verfügbar ist.",
"dyndns_could_not_check_provide": "Konnte nicht überprüft, ob {provider:s} die Domain(s) {domain:s} bereitstellen kann.", "dyndns_could_not_check_provide": "Konnte nicht überprüft, ob {provider:s} die Domain(s) {domain:s} bereitstellen kann.",
"domain_dns_conf_is_just_a_recommendation": "Dieser Befehl zeigt Ihnen die * empfohlene * Konfiguration. Die DNS-Konfiguration wird NICHT für Sie eingerichtet. Es liegt in Ihrer Verantwortung, Ihre DNS-Zone in Ihrem Registrar gemäß dieser Empfehlung zu konfigurieren.", "domain_dns_conf_is_just_a_recommendation": "Dieser Befehl zeigt Ihnen die * empfohlene * Konfiguration. Die DNS-Konfiguration wird NICHT für Sie eingerichtet. Es liegt in Ihrer Verantwortung, Ihre DNS-Zone in Ihrem Registrar gemäß dieser Empfehlung zu konfigurieren.",
@ -253,14 +253,14 @@
"backup_copying_to_organize_the_archive": "Kopieren von {size:s} MB, um das Archiv zu organisieren", "backup_copying_to_organize_the_archive": "Kopieren von {size:s} MB, um das Archiv zu organisieren",
"global_settings_setting_security_ssh_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den SSH-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)", "global_settings_setting_security_ssh_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den SSH-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)",
"group_deleted": "Gruppe '{group}' gelöscht", "group_deleted": "Gruppe '{group}' gelöscht",
"group_deletion_failed": "Kann Gruppe '{group}' nicht löschen", "group_deletion_failed": "Konnte Gruppe '{group}' nicht löschen",
"dyndns_provider_unreachable": "DynDNS-Anbieter {provider} kann nicht erreicht werden: Entweder ist dein YunoHost nicht korrekt mit dem Internet verbunden oder der Dynette-Server ist ausgefallen.", "dyndns_provider_unreachable": "DynDNS-Anbieter {provider} kann nicht erreicht werden: Entweder ist dein YunoHost nicht korrekt mit dem Internet verbunden oder der Dynette-Server ist ausgefallen.",
"group_created": "Gruppe '{group}' angelegt", "group_created": "Gruppe '{group}' angelegt",
"group_creation_failed": "Kann Gruppe '{group}' nicht anlegen", "group_creation_failed": "Konnte Gruppe '{group}' nicht anlegen",
"group_unknown": "Die Gruppe '{group:s}' ist unbekannt", "group_unknown": "Die Gruppe '{group:s}' ist unbekannt",
"group_updated": "Gruppe '{group:s}' erneuert", "group_updated": "Gruppe '{group:s}' erneuert",
"group_update_failed": "Kann Gruppe '{group:s}' nicht aktualisieren: {error}", "group_update_failed": "Kann Gruppe '{group:s}' nicht aktualisieren: {error}",
"log_does_exists": "Es gibt kein Operationsprotokoll mit dem Namen'{log}', verwende'yunohost log list', um alle verfügbaren Operationsprotokolle anzuzeigen", "log_does_exists": "Es gibt kein Operationsprotokoll mit dem Namen'{log}', verwende 'yunohost log list', um alle verfügbaren Operationsprotokolle anzuzeigen",
"log_operation_unit_unclosed_properly": "Die Operationseinheit wurde nicht richtig geschlossen", "log_operation_unit_unclosed_properly": "Die Operationseinheit wurde nicht richtig geschlossen",
"global_settings_setting_security_postfix_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den Postfix-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)", "global_settings_setting_security_postfix_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den Postfix-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)",
"log_category_404": "Die Log-Kategorie '{category}' existiert nicht", "log_category_404": "Die Log-Kategorie '{category}' existiert nicht",
@ -274,28 +274,28 @@
"backup_php5_to_php7_migration_may_fail": "Dein Archiv konnte nicht für PHP 7 konvertiert werden, Du kannst deine PHP-Anwendungen möglicherweise nicht wiederherstellen (Grund: {error:s})", "backup_php5_to_php7_migration_may_fail": "Dein Archiv konnte nicht für PHP 7 konvertiert werden, Du kannst deine PHP-Anwendungen möglicherweise nicht wiederherstellen (Grund: {error:s})",
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Erlaubt die Verwendung eines (veralteten) DSA-Hostkeys für die SSH-Daemon-Konfiguration", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Erlaubt die Verwendung eines (veralteten) DSA-Hostkeys für die SSH-Daemon-Konfiguration",
"global_settings_setting_example_string": "Beispiel einer string Option", "global_settings_setting_example_string": "Beispiel einer string Option",
"log_app_remove": "Entferne die Anwendung '{}'", "log_app_remove": "Entferne die Applikation '{}'",
"global_settings_setting_example_int": "Beispiel einer int Option", "global_settings_setting_example_int": "Beispiel einer int Option",
"global_settings_cant_open_settings": "Einstellungsdatei konnte nicht geöffnet werden, Grund: {reason:s}", "global_settings_cant_open_settings": "Einstellungsdatei konnte nicht geöffnet werden, Grund: {reason:s}",
"global_settings_cant_write_settings": "Einstellungsdatei konnte nicht gespeichert werden, Grund: {reason:s}", "global_settings_cant_write_settings": "Einstellungsdatei konnte nicht gespeichert werden, Grund: {reason:s}",
"log_app_install": "Installiere die Anwendung '{}'", "log_app_install": "Installiere die Applikation '{}'",
"global_settings_reset_success": "Frühere Einstellungen werden nun auf {path:s} gesichert", "global_settings_reset_success": "Frühere Einstellungen werden nun auf {path:s} gesichert",
"log_app_upgrade": "Upgrade der Anwendung '{}'", "log_app_upgrade": "Upgrade der Applikation '{}'",
"good_practices_about_admin_password": "Sie sind nun dabei, ein neues Administrationspasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein - obwohl es sinnvoll ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.", "good_practices_about_admin_password": "Sie sind nun dabei, ein neues Administrationspasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein, obwohl es sinnvoll ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.",
"log_corrupted_md_file": "Die mit Protokollen verknüpfte YAML-Metadatendatei ist beschädigt: '{md_file}\nFehler: {error}''", "log_corrupted_md_file": "Die mit Protokollen verknüpfte YAML-Metadatendatei ist beschädigt: '{md_file}\nFehler: {error}''",
"global_settings_cant_serialize_settings": "Einstellungsdaten konnten nicht serialisiert werden, Grund: {reason:s}", "global_settings_cant_serialize_settings": "Einstellungsdaten konnten nicht serialisiert werden, Grund: {reason:s}",
"log_help_to_get_failed_log": "Der Vorgang'{desc}' konnte nicht abgeschlossen werden. Bitte teile das vollständige Protokoll dieser Operation mit dem Befehl 'yunohost log share {name}', um Hilfe zu erhalten", "log_help_to_get_failed_log": "Der Vorgang'{desc}' konnte nicht abgeschlossen werden. Bitte teile das vollständige Protokoll dieser Operation mit dem Befehl 'yunohost log share {name}', um Hilfe zu erhalten",
"backup_no_uncompress_archive_dir": "Dieses unkomprimierte Archivverzeichnis gibt es nicht", "backup_no_uncompress_archive_dir": "Dieses unkomprimierte Archivverzeichnis gibt es nicht",
"log_app_change_url": "Ändere die URL der Anwendung '{}'", "log_app_change_url": "Ändere die URL der Applikation '{}'",
"global_settings_setting_security_password_user_strength": "Stärke des Benutzerpassworts", "global_settings_setting_security_password_user_strength": "Stärke des Benutzerpassworts",
"good_practices_about_user_password": "Du bist nun dabei, ein neues Benutzerpasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein - obwohl es ratsam ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.", "good_practices_about_user_password": "Sie sind dabei, ein neues Benutzerpasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein, obwohl es ratsam ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.",
"global_settings_setting_example_enum": "Beispiel einer enum Option", "global_settings_setting_example_enum": "Beispiel einer enum Option",
"log_link_to_failed_log": "Der Vorgang konnte nicht abgeschlossen werden '{desc}'. Bitte gib das vollständige Protokoll dieser Operation mit <a href=\"#/tools/logs/{name}\">Klicken Sie hier</a> an, um Hilfe zu erhalten", "log_link_to_failed_log": "Der Vorgang konnte nicht abgeschlossen werden '{desc}'. Bitte gib das vollständige Protokoll dieser Operation mit <a href=\"#/tools/logs/{name}\">Klicken Sie hier</a> an, um Hilfe zu erhalten",
"backup_cant_mount_uncompress_archive": "Das unkomprimierte Archiv konnte nicht als schreibgeschützt gemountet werden", "backup_cant_mount_uncompress_archive": "Das unkomprimierte Archiv konnte nicht als schreibgeschützt gemountet werden",
"backup_csv_addition_failed": "Es konnten keine Dateien zur Sicherung in die CSV-Datei hinzugefügt werden", "backup_csv_addition_failed": "Es konnten keine Dateien zur Sicherung in die CSV-Datei hinzugefügt werden",
"global_settings_setting_security_password_admin_strength": "Stärke des Admin-Passworts", "global_settings_setting_security_password_admin_strength": "Stärke des Admin-Passworts",
"global_settings_key_doesnt_exists": "Der Schlüssel'{settings_key:s}' existiert nicht in den globalen Einstellungen, du kannst alle verfügbaren Schlüssel sehen, indem du 'yunohost settings list' ausführst", "global_settings_key_doesnt_exists": "Der Schlüssel'{settings_key:s}' existiert nicht in den globalen Einstellungen, du kannst alle verfügbaren Schlüssel sehen, indem du 'yunohost settings list' ausführst",
"log_app_makedefault": "Mache '{}' zur Standard-Anwendung", "log_app_makedefault": "Mache '{}' zur Standard-Applikation",
"hook_json_return_error": "Konnte die Rückkehr vom Einsprungpunkt {path:s} nicht lesen. Fehler: {msg:s}. Unformatierter Inhalt: {raw_content}", "hook_json_return_error": "Konnte die Rückkehr vom Einsprungpunkt {path:s} nicht lesen. Fehler: {msg:s}. Unformatierter Inhalt: {raw_content}",
"app_full_domain_unavailable": "Es tut uns leid, aber diese Anwendung erfordert die Installation auf einer eigenen Domain, aber einige andere Anwendungen sind bereits auf der Domäne'{domain}' installiert. Eine mögliche Lösung ist das Hinzufügen und Verwenden einer Subdomain, die dieser Anwendung zugeordnet ist.", "app_full_domain_unavailable": "Es tut uns leid, aber diese Anwendung erfordert die Installation auf einer eigenen Domain, aber einige andere Anwendungen sind bereits auf der Domäne'{domain}' installiert. Eine mögliche Lösung ist das Hinzufügen und Verwenden einer Subdomain, die dieser Anwendung zugeordnet ist.",
"app_install_failed": "{app} kann nicht installiert werden: {error}", "app_install_failed": "{app} kann nicht installiert werden: {error}",
@ -337,7 +337,7 @@
"diagnosis_found_errors": "Habe {errors} erhebliche(s) Problem(e) in Verbindung mit {category} gefunden!", "diagnosis_found_errors": "Habe {errors} erhebliche(s) Problem(e) in Verbindung mit {category} gefunden!",
"diagnosis_found_warnings": "Habe {warnings} Ding(e) gefunden, die verbessert werden könnten für {category}.", "diagnosis_found_warnings": "Habe {warnings} Ding(e) gefunden, die verbessert werden könnten für {category}.",
"diagnosis_ip_dnsresolution_working": "Domänen-Namens-Auflösung funktioniert!", "diagnosis_ip_dnsresolution_working": "Domänen-Namens-Auflösung funktioniert!",
"diagnosis_ip_weird_resolvconf": "DNS Auflösung scheint zu funktionieren, aber seien Sie vorsichtig wenn Sie eine eigene <code>/etc/resolv.conf</code> verwendest.", "diagnosis_ip_weird_resolvconf": "DNS Auflösung scheint zu funktionieren, aber seien Sie vorsichtig wenn Sie Ihren eigenen <code>/etc/resolv.conf</code> verwenden.",
"diagnosis_display_tip": "Um die gefundenen Probleme zu sehen, können Sie zum Diagnose-Bereich des webadmin gehen, oder 'yunohost diagnosis show --issues' in der Kommandozeile ausführen.", "diagnosis_display_tip": "Um die gefundenen Probleme zu sehen, können Sie zum Diagnose-Bereich des webadmin gehen, oder 'yunohost diagnosis show --issues' in der Kommandozeile ausführen.",
"backup_archive_corrupted": "Das Backup-Archiv '{archive}' scheint beschädigt: {error}", "backup_archive_corrupted": "Das Backup-Archiv '{archive}' scheint beschädigt: {error}",
"backup_archive_cant_retrieve_info_json": "Die Informationen für das Archiv '{archive}' konnten nicht geladen werden... Die Datei info.json wurde nicht gefunden (oder ist kein gültiges json).", "backup_archive_cant_retrieve_info_json": "Die Informationen für das Archiv '{archive}' konnten nicht geladen werden... Die Datei info.json wurde nicht gefunden (oder ist kein gültiges json).",
@ -359,7 +359,7 @@
"diagnosis_domain_expiration_error": "Einige Domänen werden SEHR BALD ablaufen!", "diagnosis_domain_expiration_error": "Einige Domänen werden SEHR BALD ablaufen!",
"diagnosis_domain_expiration_success": "Deine Domänen sind registriert und werden in nächster Zeit nicht ablaufen.", "diagnosis_domain_expiration_success": "Deine Domänen sind registriert und werden in nächster Zeit nicht ablaufen.",
"diagnosis_domain_not_found_details": "Die Domäne {domain} existiert nicht in der WHOIS-Datenbank oder sie ist abgelaufen!", "diagnosis_domain_not_found_details": "Die Domäne {domain} existiert nicht in der WHOIS-Datenbank oder sie ist abgelaufen!",
"diagnosis_domain_expiration_not_found": "Konnte die Ablaufdaten für einige Domänen nicht überprüfen.", "diagnosis_domain_expiration_not_found": "Das Ablaufdatum einiger Domains kann nicht überprüft werden",
"diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von Yunohost verwaltet werden. Andernfalls können Sie mittels <cmd>yunohost dyndns update --force</cmd> ein Update erzwingen.", "diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von Yunohost verwaltet werden. Andernfalls können Sie mittels <cmd>yunohost dyndns update --force</cmd> ein Update erzwingen.",
"diagnosis_dns_point_to_doc": "Bitte schauen Sie in die Dokumentation unter <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> wenn Sie Hilfe bei der Konfiguration der DNS-Einträge brauchen.", "diagnosis_dns_point_to_doc": "Bitte schauen Sie in die Dokumentation unter <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> wenn Sie Hilfe bei der Konfiguration der DNS-Einträge brauchen.",
"diagnosis_dns_discrepancy": "Der folgende DNS-Eintrag scheint nicht den empfohlenen Einstellungen zu entsprechen: <br>Typ: <code>{type}</code><br>Name: <code>{name}</code><br> Aktueller Wert: <code>{current}</code><br> Erwarteter Wert: <code>{value}</code>", "diagnosis_dns_discrepancy": "Der folgende DNS-Eintrag scheint nicht den empfohlenen Einstellungen zu entsprechen: <br>Typ: <code>{type}</code><br>Name: <code>{name}</code><br> Aktueller Wert: <code>{current}</code><br> Erwarteter Wert: <code>{value}</code>",
@ -377,7 +377,7 @@
"service_reloaded_or_restarted": "Der Dienst '{service:s}' wurde erfolgreich neu geladen oder gestartet", "service_reloaded_or_restarted": "Der Dienst '{service:s}' wurde erfolgreich neu geladen oder gestartet",
"service_restarted": "Der Dienst '{service:s}' wurde neu gestartet", "service_restarted": "Der Dienst '{service:s}' wurde neu gestartet",
"service_regen_conf_is_deprecated": "'yunohost service regen-conf' ist veraltet! Bitte verwenden Sie stattdessen 'yunohost tools regen-conf'.", "service_regen_conf_is_deprecated": "'yunohost service regen-conf' ist veraltet! Bitte verwenden Sie stattdessen 'yunohost tools regen-conf'.",
"certmanager_warning_subdomain_dns_record": "Die Subdomain '{subdomain:s}' löst nicht dieselbe IP wie '{domain:s} auf. Einige Funktionen werden nicht verfügbar sein, solange Sie dies nicht beheben und das Zertifikat erneuern.", "certmanager_warning_subdomain_dns_record": "Die Subdomäne \"{subdomain:s}\" löst nicht zur gleichen IP Adresse auf wie \"{domain:s}\". Einige Funktionen sind nicht verfügbar bis du dies behebst und die Zertifikate neu erzeugst.",
"diagnosis_ports_ok": "Port {port} ist von außen erreichbar.", "diagnosis_ports_ok": "Port {port} ist von außen erreichbar.",
"diagnosis_ram_verylow": "Das System hat nur {available} ({available_percent}%) RAM zur Verfügung! (von insgesamt {total})", "diagnosis_ram_verylow": "Das System hat nur {available} ({available_percent}%) RAM zur Verfügung! (von insgesamt {total})",
"diagnosis_mail_outgoing_port_25_blocked_details": "Sie sollten zuerst versuchen den ausgehenden Port 25 auf Ihrer Router-Konfigurationsoberfläche oder Ihrer Hosting-Anbieter-Konfigurationsoberfläche zu öffnen. (Bei einigen Hosting-Anbieter kann es sein, daß Sie verlangen, daß man dafür ein Support-Ticket sendet).", "diagnosis_mail_outgoing_port_25_blocked_details": "Sie sollten zuerst versuchen den ausgehenden Port 25 auf Ihrer Router-Konfigurationsoberfläche oder Ihrer Hosting-Anbieter-Konfigurationsoberfläche zu öffnen. (Bei einigen Hosting-Anbieter kann es sein, daß Sie verlangen, daß man dafür ein Support-Ticket sendet).",
@ -489,5 +489,132 @@
"global_settings_setting_smtp_relay_port": "SMTP Relay Port", "global_settings_setting_smtp_relay_port": "SMTP Relay Port",
"global_settings_setting_smtp_allow_ipv6": "Erlaube die Nutzung von IPv6 um Mails zu empfangen und zu versenden", "global_settings_setting_smtp_allow_ipv6": "Erlaube die Nutzung von IPv6 um Mails zu empfangen und zu versenden",
"global_settings_setting_pop3_enabled": "Aktiviere das POP3 Protokoll für den Mailserver", "global_settings_setting_pop3_enabled": "Aktiviere das POP3 Protokoll für den Mailserver",
"domain_cannot_remove_main_add_new_one": "Du kannst \"{domain:s}\" nicht entfernen da es die Hauptdomain und deine einzige Domain ist, erst musst erst eine andere Domain hinzufügen indem du eingibst \"yunohost domain add <andere-domian.de>\", setze es dann als deine Hauptdomain indem du eingibst \"yunohost domain main-domain -n <andere-domain.de>\", erst jetzt kannst du die domain \"{domain:s}\" entfernen." "domain_cannot_remove_main_add_new_one": "Du kannst \"{domain:s}\" nicht entfernen da es die Hauptdomain und deine einzige Domain ist, erst musst erst eine andere Domain hinzufügen indem du eingibst \"yunohost domain add <andere-domian.de>\", setze es dann als deine Hauptdomain indem du eingibst \"yunohost domain main-domain -n <andere-domain.de>\", erst jetzt kannst du die domain \"{domain:s}\" entfernen.",
"diagnosis_rootfstotalspace_critical": "Das Root-Filesystem hat noch freien Speicher von {space}. Das ist besorngiserregend! Der Speicher wird schnell aufgebraucht sein. 16 GB für das Root-Filesystem werden empfohlen.",
"diagnosis_rootfstotalspace_warning": "Das Root-Filesystem hat noch freien Speicher von {space}. Möglich, dass das in Ordnung ist. Vielleicht ist er aber auch schneller aufgebraucht. 16 GB für das Root-Filesystem werden empfohlen.",
"global_settings_setting_smtp_relay_host": "Zu verwendender SMTP-Relay-Host um E-Mails zu versenden. Er wird anstelle dieser YunoHost-Instanz verwendet. Nützlich, wenn Sie in einer der folgenden Situationen sind: Ihr ISP- oder VPS-Provider hat Ihren Port 25 geblockt, eine Ihrer residentiellen IPs ist auf DUHL gelistet, Sie können keinen Reverse-DNS konfigurieren oder dieser Server ist nicht direkt mit dem Internet verbunden und Sie möchten einen anderen verwenden, um E-Mails zu versenden.",
"global_settings_setting_backup_compress_tar_archives": "Beim Erstellen von Backups die Archive komprimieren (.tar.gz) anstelle von unkomprimierten Archiven (.tar). N.B. : Diese Option ergibt leichtere Backup-Archive, aber das initiale Backupprozedere wird länger dauern und mehr CPU brauchen.",
"log_remove_on_failed_restore": "'{}' entfernen nach einer fehlerhaften Wiederherstellung aus einem Backup-Archiv",
"log_backup_restore_app": "'{}' aus einem Backup-Archiv wiederherstellen",
"log_backup_restore_system": "System aus einem Backup-Archiv wiederherstellen",
"log_available_on_yunopaste": "Das Protokoll ist nun via {url} verfügbar",
"log_app_config_apply": "Wende die Konfiguration auf die Applikation '{}' an",
"log_app_config_show_panel": "Zeige das Konfigurations-Panel der Applikation '{}'",
"log_app_action_run": "Führe Aktion der Applikation '{}' aus",
"invalid_regex": "Ungültige Regex:'{regex:s}'",
"migration_description_0016_php70_to_php73_pools": "Migrieren der php7.0-fpm-Konfigurationsdateien zu php7.3",
"mailbox_disabled": "E-Mail für Benutzer {user:s} deaktiviert",
"log_tools_reboot": "Server neustarten",
"log_tools_shutdown": "Server ausschalten",
"log_tools_upgrade": "Systempakete aktualisieren",
"log_tools_postinstall": "Post-Installation des YunoHost-Servers durchführen",
"log_tools_migrations_migrate_forward": "Migrationen durchführen",
"log_domain_main_domain": "Mache '{}' zur Hauptdomäne",
"log_user_permission_reset": "Zurücksetzen der Berechtigung '{}'",
"log_user_permission_update": "Aktualisiere Zugriffe für Berechtigung '{}'",
"log_user_update": "Aktualisiere Information für Benutzer '{}'",
"log_user_group_update": "Aktualisiere Gruppe '{}'",
"log_user_group_delete": "Lösche Gruppe '{}'",
"log_user_group_create": "Erstelle Gruppe '{}'",
"log_user_delete": "Lösche Benutzer '{}'",
"log_user_create": "Füge Benutzer '{}' hinzu",
"log_permission_url": "Aktualisiere URL, die mit der Berechtigung '{}' verknüpft ist",
"log_permission_delete": "Lösche Berechtigung '{}'",
"log_permission_create": "Erstelle Berechtigung '{}'",
"log_dyndns_update": "Die IP, die mit der YunoHost-Subdomain '{}' verbunden ist, aktualisieren",
"log_dyndns_subscribe": "Für eine YunoHost-Subdomain registrieren '{}'",
"log_domain_remove": "Entfernen der Domäne '{}' aus der Systemkonfiguration",
"log_domain_add": "Hinzufügen der Domäne '{}' zur Systemkonfiguration",
"log_remove_on_failed_install": "Entfernen von '{}' nach einer fehlgeschlagenen Installation",
"migration_0015_still_on_stretch_after_main_upgrade": "Etwas ist schiefgelaufen während dem Haupt-Upgrade. Das System scheint immer noch auf Debian Stretch zu laufen",
"migration_0015_yunohost_upgrade": "Beginne YunoHost-Core-Upgrade...",
"migration_description_0019_extend_permissions_features": "Erweitern und überarbeiten des Applikationsberechtigungs-Managementsystems",
"migrating_legacy_permission_settings": "Migrieren der Legacy-Berechtigungseinstellungen...",
"migration_description_0017_postgresql_9p6_to_11": "Migrieren der Datenbanken von PostgreSQL 9.6 nach 11",
"migration_0015_main_upgrade": "Beginne Haupt-Upgrade...",
"migration_0015_not_stretch": "Die aktuelle Debian-Distribution ist nicht Stretch!",
"migration_0015_not_enough_free_space": "Der freie Speicher in /var/ ist sehr gering! Sie sollten minimal 1GB frei haben, um diese Migration durchzuführen.",
"domain_remove_confirm_apps_removal": "Wenn Sie diese Domäne löschen, werden folgende Applikationen entfernt:\n{apps}\n\nSind Sie sicher? [{answers}]",
"migration_0015_cleaning_up": "Bereinigung des Cache und der Pakete, welche nicht mehr benötigt werden...",
"migration_0017_postgresql_96_not_installed": "PostgreSQL wurde auf ihrem System nicht installiert. Nichts zu tun.",
"migration_0015_system_not_fully_up_to_date": "Ihr System ist nicht vollständig auf dem neuesten Stand. Bitte führen Sie ein reguläres Upgrade durch, bevor Sie die Migration auf Buster durchführen.",
"migration_0015_modified_files": "Bitte beachten Sie, dass die folgenden Dateien als manuell bearbeitet erkannt wurden und beim nächsten Upgrade überschrieben werden könnten: {manually_modified_files}",
"migration_0015_general_warning": "Bitte beachten Sie, dass diese Migration eine heikle Angelegenheit darstellt. Das YunoHost-Team hat alles unternommen, um sie zu testen und zu überarbeiten. Dennoch ist es möglich, dass diese Migration Teile des Systems oder Applikationen beschädigen könnte.\n\nDeshalb ist folgendes zu empfehlen:\n…- Führen Sie ein Backup aller kritischen Daten und Applikationen durch. Mehr unter https://yunohost.org/backup;\n…- Seien Sie geduldig nachdem Sie die Migration gestartet haben: Abhängig von Ihrer Internetverbindung und Ihrer Hardware kann es einige Stunden dauern, bis das Upgrade fertig ist.",
"migration_0015_problematic_apps_warning": "Bitte beachten Sie, dass folgende möglicherweise problematischen Applikationen auf Ihrer Installation erkannt wurden. Es scheint, als ob sie nicht aus dem YunoHost-Applikationskatalog installiert oder nicht als 'working' gekennzeichnet worden sind. Folglich kann nicht garantiert werden, dass sie nach dem Upgrade immer noch funktionieren: {problematic_apps}",
"migration_0015_specific_upgrade": "Start des Upgrades der Systempakete, deren Upgrade separat durchgeführt werden muss...",
"migration_0015_weak_certs": "Die folgenden Zertifikate verwenden immer noch schwache Signierungsalgorithmen und müssen aktualisiert werden um mit der nächsten Version von nginx kompatibel zu sein: {certs}",
"migrations_pending_cant_rerun": "Diese Migrationen sind immer noch anstehend und können deshalb nicht erneut durchgeführt werden: {ids}",
"migration_0019_add_new_attributes_in_ldap": "Hinzufügen neuer Attribute für die Berechtigungen in der LDAP-Datenbank",
"migration_0019_can_not_backup_before_migration": "Das Backup des Systems konnte nicht abgeschlossen werden bevor die Migration fehlschlug. Fehlermeldung: {error}",
"migration_0019_migration_failed_trying_to_rollback": "Konnte nicht migrieren... versuche ein Rollback des Systems.",
"migrations_not_pending_cant_skip": "Diese Migrationen sind nicht anstehend und können deshalb nicht übersprungen werden: {ids}",
"migration_0018_failed_to_reset_legacy_rules": "Zurücksetzen der veralteten iptables-Regeln fehlgeschlagen: {error}",
"migration_0019_rollback_success": "Rollback des Systems durchgeführt.",
"migration_0019_slapd_config_will_be_overwritten": "Es schaut aus, als ob Sie die slapd-Konfigurationsdatei manuell bearbeitet haben. Für diese kritische Migration muss das Update der slapd-Konfiguration erzwungen werden. Von der Originaldatei wird ein Backup gemacht in {conf_backup_folder}.",
"migrations_success_forward": "Migration {id} abgeschlossen",
"migrations_cant_reach_migration_file": "Die Migrationsdateien konnten nicht aufgerufen werden im Verzeichnis '%s'",
"migrations_dependencies_not_satisfied": "Führen Sie diese Migrationen aus: '{dependencies_id}', vor der Migration {id}.",
"migrations_failed_to_load_migration": "Konnte Migration nicht laden {id}: {error}",
"migrations_list_conflict_pending_done": "Sie können nicht '--previous' und '--done' gleichzeitig benützen.",
"migrations_already_ran": "Diese Migrationen wurden bereits durchgeführt: {ids}",
"migrations_loading_migration": "Lade Migrationen {id}...",
"migrations_migration_has_failed": "Migration {id} gescheitert mit der Ausnahme {exception}: Abbruch",
"migrations_must_provide_explicit_targets": "Sie müssen konkrete Ziele angeben, wenn Sie '--skip' oder '--force-rerun' verwenden",
"migrations_need_to_accept_disclaimer": "Um die Migration {id} durchzuführen, müssen Sie den Disclaimer akzeptieren.\n---\n{disclaimer}\n---\n Wenn Sie bestätigen, dass Sie die Migration durchführen wollen, wiederholen Sie bitte den Befehl mit der Option '--accept-disclaimer'.",
"migrations_no_migrations_to_run": "Keine Migrationen durchzuführen",
"migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 ist installiert aber nicht postgreSQL 11? Etwas komisches ist Ihrem System zugestossen :(...",
"migration_0017_not_enough_space": "Stellen Siea ausreichend Speicherplatz im Verzeichnis {path} zur Verfügung um die Migration durchzuführen.",
"migration_0018_failed_to_migrate_iptables_rules": "Migration der veralteten iptables-Regeln zu nftables fehlgeschlagen: {error}",
"migration_0019_backup_before_migration": "Ein Backup der LDAP-Datenbank und der Applikationseinstellungen erstellen vor der Migration.",
"migrations_exclusive_options": "'--auto', '--skip' und '--force-rerun' sind Optionen, die sich gegenseitig ausschliessen.",
"migrations_no_such_migration": "Es existiert keine Migration genannt '{id}'",
"migrations_running_forward": "Durchführen der Migrationen {id}...",
"migrations_skip_migration": "Überspringe Migrationen {id}...",
"password_too_simple_2": "Dieses Passwort gehört zu den meistverwendeten der Welt. Bitte nehmen Sie etwas einzigartigeres.",
"password_listed": "Dieses Passwort gehört zu den meistverwendeten der Welt. Bitte nehmen Sie etwas einzigartigeres.",
"operation_interrupted": "Wurde die Operation manuell unterbrochen?",
"invalid_number": "Muss eine Zahl sein",
"migrations_to_be_ran_manually": "Die Migration {id} muss manuell durchgeführt werden. Bitte gehen Sie zu Werkzeuge → Migrationen auf der Webadmin-Seite oder führen Sie 'yunohost tools migrations run' aus.",
"permission_already_up_to_date": "Die Berechtigung wurde nicht aktualisiert, weil die Anfragen für Hinzufügen/Entfernen stimmen mit dem aktuellen Status bereits überein",
"permission_already_exist": "Berechtigung '{permission}' existiert bereits",
"permission_already_disallowed": "Für die Gruppe '{group}' wurde die Berechtigung '{permission}' deaktiviert",
"permission_already_allowed": "Die Gruppe '{group}' hat die Berechtigung '{permission}' bereits erhalten",
"pattern_password_app": "Entschuldigen Sie bitte! Passwörter dürfen folgende Zeichen nicht enthalten: {forbidden_chars}",
"pattern_email_forward": "Es muss sich um eine gültige E-Mail-Adresse handeln. Das Symbol '+' wird akzeptiert (zum Beispiel : maxmuster@beispiel.com oder maxmuster+yunohost@beispiel.com)",
"password_too_simple_4": "Dass Passwort muss mindestens 12 Zeichen lang sein und Zahlen, Klein- und Grossbuchstaben und Sonderzeichen enthalten",
"password_too_simple_3": "Das Passwort muss mindestens 8 Zeichen lang sein und Zahlen, Klein- und Grossbuchstaben und Sonderzeichen enthalten",
"regenconf_file_manually_removed": "Die Konfigurationsdatei '{conf}' wurde manuell gelöscht und wird nicht erstellt",
"regenconf_file_manually_modified": "Die Konfigurationsdatei '{conf}' wurde manuell bearbeitet und wird nicht aktualisiert",
"regenconf_file_kept_back": "Die Konfigurationsdatei '{conf}' sollte von \"regen-conf\" (Kategorie {category}) gelöscht werden, wurde aber beibehalten.",
"regenconf_file_copy_failed": "Die neue Konfigurationsdatei '{new}' kann nicht nach '{conf}' kopiert werden",
"regenconf_file_backed_up": "Die Konfigurationsdatei '{conf}' wurde unter '{backup}' gespeichert",
"permission_require_account": "Berechtigung {permission} ist nur für Benutzer mit einem Konto sinnvoll und kann daher nicht für Besucher aktiviert werden.",
"permission_protected": "Die Berechtigung ist geschützt. Sie können die Besuchergruppe nicht zu dieser Berechtigung hinzufügen oder daraus entfernen.",
"permission_updated": "Berechtigung '{permission:s}' aktualisiert",
"permission_update_failed": "Die Berechtigung '{permission}' kann nicht aktualisiert werden : {error}",
"permission_not_found": "Berechtigung nicht gefunden",
"permission_deletion_failed": "Entfernung der Berechtigung nicht möglich '{permission}': {error}",
"permission_deleted": "Berechtigung gelöscht",
"permission_currently_allowed_for_all_users": "Diese Berechtigung wird derzeit allen Benutzern zusätzlich zu anderen Gruppen erteilt. Möglicherweise möchten Sie entweder die Berechtigung 'all_users' entfernen oder die anderen Gruppen entfernen, für die sie derzeit zulässig sind.",
"permission_creation_failed": "Berechtigungserstellung nicht möglich '{permission}' : {error}",
"permission_created": "Berechtigung '{permission: s}' erstellt",
"permission_cannot_remove_main": "Entfernung einer Hauptberechtigung nicht genehmigt",
"regenconf_file_updated": "Konfigurationsdatei '{conf}' aktualisiert",
"regenconf_file_removed": "Konfigurationsdatei '{conf}' entfernt",
"regenconf_file_remove_failed": "Konnte die Konfigurationsdatei '{conf}' nicht entfernen",
"postinstall_low_rootfsspace": "Das Root-Filesystem hat insgesamt weniger als 10GB freien Speicherplatz zur Verfügung, was ziemlich besorgniserregend ist! Sie werden sehr bald keinen freien Speicherplatz mehr haben! Für das Root-Filesystem werden mindestens 16GB empfohlen. Wenn Sie YunoHost trotz dieser Warnung installieren wollen, wiederholen Sie den Befehl mit --force-diskspace",
"regenconf_up_to_date": "Die Konfiguration ist bereits aktuell für die Kategorie '{category}'",
"regenconf_now_managed_by_yunohost": "Die Konfigurationsdatei '{conf}' wird jetzt von YunoHost (Kategorie {category}) verwaltet.",
"regenconf_updated": "Konfiguration aktualisiert für '{category}'",
"regenconf_pending_applying": "Wende die anstehende Konfiguration für die Kategorie {category} an...",
"regenconf_failed": "Konnte die Konfiguration für die Kategorie(n) {categories} nicht neu erstellen",
"regenconf_dry_pending_applying": "Überprüfe die anstehende Konfiguration, welche für die Kategorie {category}' aktualisiert worden wäre…",
"regenconf_would_be_updated": "Die Konfiguration wäre für die Kategorie '{category}' aktualisiert worden",
"restore_system_part_failed": "Die Systemteile '{part:s}' konnten nicht wiederhergestellt werden",
"restore_removing_tmp_dir_failed": "Ein altes, temporäres Directory konnte nicht entfernt werden",
"restore_not_enough_disk_space": "Nicht genug Speicher (Speicher: {free_space:d} B, benötigter Speicher: {needed_space:d} B, Sicherheitspuffer: {margin:d} B)",
"restore_may_be_not_enough_disk_space": "Dein System scheint nicht genug Speicherplatz zu haben (frei: {free_space:d} B, benötigter Platz: {needed_space:d} B, Sicherheitspuffer: {margin:d} B)",
"restore_extracting": "Packe die benötigten Dateien aus dem Archiv aus…",
"restore_already_installed_apps": "Folgende Apps können nicht wiederhergestellt werden, weil sie schon installiert sind: {apps}",
"regex_with_only_domain": "Du kannst regex nicht als Domain verwenden, sondern nur als Pfad"
} }

View file

@ -291,9 +291,6 @@
"dpkg_lock_not_available": "This command can't be run right now because another program seems to be using the lock of dpkg (the system package manager)", "dpkg_lock_not_available": "This command can't be run right now because another program seems to be using the lock of dpkg (the system package manager)",
"dyndns_could_not_check_provide": "Could not check if {provider:s} can provide {domain:s}.", "dyndns_could_not_check_provide": "Could not check if {provider:s} can provide {domain:s}.",
"dyndns_could_not_check_available": "Could not check if {domain:s} is available on {provider:s}.", "dyndns_could_not_check_available": "Could not check if {domain:s} is available on {provider:s}.",
"dyndns_cron_installed": "DynDNS cron job created",
"dyndns_cron_remove_failed": "Could not remove the DynDNS cron job because: {error}",
"dyndns_cron_removed": "DynDNS cron job removed",
"dyndns_ip_update_failed": "Could not update IP address to DynDNS", "dyndns_ip_update_failed": "Could not update IP address to DynDNS",
"dyndns_ip_updated": "Updated your IP on DynDNS", "dyndns_ip_updated": "Updated your IP on DynDNS",
"dyndns_key_generating": "Generating DNS key... It may take a while.", "dyndns_key_generating": "Generating DNS key... It may take a while.",

View file

@ -54,7 +54,7 @@
"domain_dyndns_already_subscribed": "Vous avez déjà souscris à un domaine DynDNS", "domain_dyndns_already_subscribed": "Vous avez déjà souscris à un domaine DynDNS",
"domain_dyndns_root_unknown": "Domaine DynDNS principal inconnu", "domain_dyndns_root_unknown": "Domaine DynDNS principal inconnu",
"domain_exists": "Le domaine existe déjà", "domain_exists": "Le domaine existe déjà",
"domain_uninstall_app_first": "Ces applications sont toujours installées sur votre domaine :\n{apps}\n\nAfin de pouvoir procéder à la suppression du domaine, vous devez préalablement :\n- soit désinstaller toutes ces applications avec la commande 'yunohost app remove nom-de-l-application' ;\n- soit déplacer toutes ces applications vers un autre domaine avec la commande 'yunohost app change-url nom-de-l-application'", "domain_uninstall_app_first": "Ces applications sont toujours installées sur votre domaine :\n{apps}\n\nVeuillez les désinstaller avec la commande 'yunohost app remove nom-de-l-application' ou les déplacer vers un autre domaine avec la commande 'yunohost app change-url nom-de-l-application' avant de procéder à la suppression du domaine",
"domain_unknown": "Domaine inconnu", "domain_unknown": "Domaine inconnu",
"done": "Terminé", "done": "Terminé",
"downloading": "Téléchargement en cours …", "downloading": "Téléchargement en cours …",
@ -327,7 +327,7 @@
"password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux", "password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
"password_too_simple_4": "Le mot de passe doit comporter au moins 12 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux", "password_too_simple_4": "Le mot de passe doit comporter au moins 12 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
"root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager au mot de passe root !", "root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager au mot de passe root !",
"aborting": "Annulation.", "aborting": "Annulation en cours.",
"app_not_upgraded": "Lapplication {failed_app} na pas été mise à jour et par conséquence les applications suivantes nont pas été mises à jour : {apps}", "app_not_upgraded": "Lapplication {failed_app} na pas été mise à jour et par conséquence les applications suivantes nont pas été mises à jour : {apps}",
"app_start_install": "Installation de {app}...", "app_start_install": "Installation de {app}...",
"app_start_remove": "Suppression de {app}...", "app_start_remove": "Suppression de {app}...",
@ -650,7 +650,7 @@
"migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables",
"service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX",
"migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable par défaut a échoué : {error}",
"migration_0018_failed_to_migrate_iptables_rules": "La migration des règles iptables héritées vers nftables a échoué: {error}", "migration_0018_failed_to_migrate_iptables_rules": "Échec de la migration des anciennes règles iptables vers nftables : {error}",
"migration_0017_not_enough_space": "Laissez suffisamment d'espace disponible dans {path} avant de lancer la migration.", "migration_0017_not_enough_space": "Laissez suffisamment d'espace disponible dans {path} avant de lancer la migration.",
"migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 est installé mais pas posgreSQL 11 ? Il s'est sans doute passé quelque chose d'étrange sur votre système :(...", "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 est installé mais pas posgreSQL 11 ? Il s'est sans doute passé quelque chose d'étrange sur votre système :(...",
"migration_0017_postgresql_96_not_installed": "PostgreSQL n'a pas été installé sur votre système. Aucune opération à effectuer.", "migration_0017_postgresql_96_not_installed": "PostgreSQL n'a pas été installé sur votre système. Aucune opération à effectuer.",
@ -692,5 +692,9 @@
"invalid_number": "Doit être un nombre", "invalid_number": "Doit être un nombre",
"migration_description_0019_extend_permissions_features": "Étendre et retravailler le système de gestion des permissions applicatives", "migration_description_0019_extend_permissions_features": "Étendre et retravailler le système de gestion des permissions applicatives",
"diagnosis_basesystem_hardware_model": "Le modèle du serveur est {model}", "diagnosis_basesystem_hardware_model": "Le modèle du serveur est {model}",
"diagnosis_backports_in_sources_list": "Il semble qu'apt (le gestionnaire de paquets) soit configuré pour utiliser le dépôt des rétroportages (backports). A moins que vous ne sachiez vraiment ce que vous faites, nous vous déconseillons fortement d'installer des paquets provenant des rétroportages, car cela risque de créer des instabilités ou des conflits sur votre système." "diagnosis_backports_in_sources_list": "Il semble qu'apt (le gestionnaire de paquets) soit configuré pour utiliser le dépôt des rétroportages (backports). A moins que vous ne sachiez vraiment ce que vous faites, nous vous déconseillons fortement d'installer des paquets provenant des rétroportages, car cela risque de créer des instabilités ou des conflits sur votre système.",
"postinstall_low_rootfsspace": "Le système de fichiers racine a une taille totale inférieure à 10 GB, ce qui est inquiétant ! Vous allez certainement arriver à court d'espace disque rapidement ! Il est recommandé d'avoir au moins 16 GB pour ce système de fichiers. Si vous voulez installer YunoHost malgré cet avertissement, relancez le postinstall avec --force-diskspace",
"domain_remove_confirm_apps_removal": "Le retrait de ce domaine retirera aussi ces applications :\n{apps}\n\nÊtes vous sûr de vouloir cela ? [{answers}]",
"diagnosis_rootfstotalspace_critical": "Le système de fichiers racine ne fait que {space} ! Vous allez certainement les remplir très rapidement ! Il est recommandé d'avoir au moins 16 GB pour ce système de fichiers.",
"diagnosis_rootfstotalspace_warning": "Le système de fichiers racine n'est que de {space}. Ça peut suffire, mais faites attention car vous risquez de les remplire rapidement... Il est recommandé d'avoir au moins 16 GB pour ce système de fichiers."
} }

View file

@ -591,7 +591,7 @@
"log_user_permission_update": "Aggiorna gli accessi del permesso '{}'", "log_user_permission_update": "Aggiorna gli accessi del permesso '{}'",
"log_user_group_update": "Aggiorna il gruppo '{}'", "log_user_group_update": "Aggiorna il gruppo '{}'",
"log_user_group_delete": "Cancella il gruppo '{}'", "log_user_group_delete": "Cancella il gruppo '{}'",
"log_user_group_create": "Crea il gruppo '[}'", "log_user_group_create": "Crea il gruppo '{}'",
"log_permission_url": "Aggiorna l'URL collegato al permesso '{}'", "log_permission_url": "Aggiorna l'URL collegato al permesso '{}'",
"log_permission_delete": "Cancella permesso '{}'", "log_permission_delete": "Cancella permesso '{}'",
"log_permission_create": "Crea permesso '{}'", "log_permission_create": "Crea permesso '{}'",
@ -674,5 +674,9 @@
"diagnosis_mail_blacklist_reason": "Il motivo della blacklist è: {reason}", "diagnosis_mail_blacklist_reason": "Il motivo della blacklist è: {reason}",
"diagnosis_mail_blacklist_listed_by": "Il tuo IP o dominio <code>{item}</code> è nella blacklist {blacklist_name}", "diagnosis_mail_blacklist_listed_by": "Il tuo IP o dominio <code>{item}</code> è nella blacklist {blacklist_name}",
"diagnosis_backports_in_sources_list": "Sembra che apt (il package manager) sia configurato per utilizzare le backport del repository. A meno che tu non sappia quello che stai facendo, scoraggiamo fortemente di installare pacchetti tramite esse, perché ci sono alte probabilità di creare conflitti con il tuo sistema.", "diagnosis_backports_in_sources_list": "Sembra che apt (il package manager) sia configurato per utilizzare le backport del repository. A meno che tu non sappia quello che stai facendo, scoraggiamo fortemente di installare pacchetti tramite esse, perché ci sono alte probabilità di creare conflitti con il tuo sistema.",
"diagnosis_basesystem_hardware_model": "Modello server: {model}" "diagnosis_basesystem_hardware_model": "Modello server: {model}",
"postinstall_low_rootfsspace": "La radice del filesystem ha uno spazio totale inferiore ai 10 GB, ed è piuttosto preoccupante! Consumerai tutta la memoria molto velocemente! Raccomandiamo di avere almeno 16 GB per la radice del filesystem. Se vuoi installare YunoHost ignorando questo avviso, esegui nuovamente il postinstall con l'argomento --force-diskspace",
"domain_remove_confirm_apps_removal": "Rimuovere questo dominio rimuoverà anche le seguenti applicazioni:\n{apps}\n\nSei sicuro di voler continuare? [{answers}]",
"diagnosis_rootfstotalspace_critical": "La radice del filesystem ha un totale di solo {space}, ed è piuttosto preoccupante! Probabilmente consumerai tutta la memoria molto velocemente! Raccomandiamo di avere almeno 16 GB per la radice del filesystem.",
"diagnosis_rootfstotalspace_warning": "La radice del filesystem ha un totale di solo {space}. Potrebbe non essere un problema, ma stai attento perché potresti consumare tutta la memoria velocemente... Raccomandiamo di avere almeno 16 GB per la radice del filesystem."
} }

View file

@ -3,15 +3,15 @@
"admin_password": "Administrator wachtwoord", "admin_password": "Administrator wachtwoord",
"admin_password_changed": "Het administratie wachtwoord werd gewijzigd", "admin_password_changed": "Het administratie wachtwoord werd gewijzigd",
"app_already_installed": "{app:s} is al geïnstalleerd", "app_already_installed": "{app:s} is al geïnstalleerd",
"app_argument_invalid": "'{name:s}' bevat ongeldige waarde: {error:s}", "app_argument_invalid": "Kies een geldige waarde voor '{name:s}': {error:s}",
"app_argument_required": "Het '{name:s}' moet ingevuld worden", "app_argument_required": "Het '{name:s}' moet ingevuld worden",
"app_extraction_failed": "Kan installatiebestanden niet uitpakken", "app_extraction_failed": "Kan installatiebestanden niet uitpakken",
"app_id_invalid": "Ongeldige app-id", "app_id_invalid": "Ongeldige app-id",
"app_install_files_invalid": "Ongeldige installatiebestanden", "app_install_files_invalid": "Deze bestanden kunnen niet worden geïnstalleerd",
"app_manifest_invalid": "Ongeldig app-manifest", "app_manifest_invalid": "Ongeldig app-manifest",
"app_not_installed": "{app:s} is niet geïnstalleerd", "app_not_installed": "{app:s} is niet geïnstalleerd",
"app_removed": "{app:s} succesvol verwijderd", "app_removed": "{app:s} succesvol verwijderd",
"app_sources_fetch_failed": "Kan bronbestanden niet ophalen", "app_sources_fetch_failed": "Kan bronbestanden niet ophalen, klopt de URL?",
"app_unknown": "Onbekende app", "app_unknown": "Onbekende app",
"app_upgrade_failed": "Kan app {app:s} niet updaten", "app_upgrade_failed": "Kan app {app:s} niet updaten",
"app_upgraded": "{app:s} succesvol geüpgraded", "app_upgraded": "{app:s} succesvol geüpgraded",
@ -82,7 +82,7 @@
"app_argument_choice_invalid": "Ongeldige keuze voor argument '{name:s}'. Het moet een van de volgende keuzes zijn {choices:s}", "app_argument_choice_invalid": "Ongeldige keuze voor argument '{name:s}'. Het moet een van de volgende keuzes zijn {choices:s}",
"app_not_correctly_installed": "{app:s} schijnt niet juist geïnstalleerd te zijn", "app_not_correctly_installed": "{app:s} schijnt niet juist geïnstalleerd te zijn",
"app_not_properly_removed": "{app:s} werd niet volledig verwijderd", "app_not_properly_removed": "{app:s} werd niet volledig verwijderd",
"app_requirements_checking": "Controleer noodzakelijke pakketten...", "app_requirements_checking": "Noodzakelijke pakketten voor {app} aan het controleren...",
"app_requirements_unmeet": "Er wordt niet aan de aanvorderingen voldaan, het pakket {pkgname} ({version}) moet {spec} zijn", "app_requirements_unmeet": "Er wordt niet aan de aanvorderingen voldaan, het pakket {pkgname} ({version}) moet {spec} zijn",
"app_unsupported_remote_type": "Niet ondersteund besturings type voor de app", "app_unsupported_remote_type": "Niet ondersteund besturings type voor de app",
"ask_main_domain": "Hoofd-domein", "ask_main_domain": "Hoofd-domein",
@ -101,5 +101,25 @@
"already_up_to_date": "Er is niets te doen, alles is al up-to-date.", "already_up_to_date": "Er is niets te doen, alles is al up-to-date.",
"admin_password_too_long": "Gelieve een wachtwoord te kiezen met minder dan 127 karakters", "admin_password_too_long": "Gelieve een wachtwoord te kiezen met minder dan 127 karakters",
"app_action_cannot_be_ran_because_required_services_down": "De volgende diensten moeten actief zijn om deze actie uit te voeren: {services}. Probeer om deze te herstarten om verder te gaan (en om eventueel te onderzoeken waarom ze niet werken).", "app_action_cannot_be_ran_because_required_services_down": "De volgende diensten moeten actief zijn om deze actie uit te voeren: {services}. Probeer om deze te herstarten om verder te gaan (en om eventueel te onderzoeken waarom ze niet werken).",
"aborting": "Annulatie." "aborting": "Annulatie.",
"app_upgrade_app_name": "Bezig {app} te upgraden...",
"app_make_default_location_already_used": "Kan '{app}' niet de standaardapp maken op het domein, '{domain}' wordt al gebruikt door '{other_app}'",
"app_install_failed": "Kan {app} niet installeren: {error}",
"app_remove_after_failed_install": "Bezig de app te verwijderen na gefaalde installatie...",
"app_manifest_install_ask_domain": "Kies het domein waar deze app op geïnstalleerd moet worden",
"app_manifest_install_ask_path": "Kies het pad waar deze app geïnstalleerd moet worden",
"app_manifest_install_ask_admin": "Kies een administrator voor deze app",
"app_change_url_failed_nginx_reload": "Kon NGINX niet opnieuw laden. Hier is de output van 'nginx -t':\n{nginx_errors:s}",
"app_change_url_success": "{app:s} URL is nu {domain:s}{path:s}",
"app_full_domain_unavailable": "Sorry, deze app moet op haar eigen domein geïnstalleerd worden, maar andere apps zijn al geïnstalleerd op het domein '{domain}'. U kunt wel een subdomein aan deze app toewijden.",
"app_install_script_failed": "Er is een fout opgetreden in het installatiescript van de app",
"app_location_unavailable": "Deze URL is niet beschikbaar of is in conflict met de al geïnstalleerde app(s):\n{apps:s}",
"app_manifest_install_ask_password": "Kies een administratiewachtwoord voor deze app",
"app_manifest_install_ask_is_public": "Moet deze app zichtbaar zijn voor anomieme bezoekers?",
"app_not_upgraded": "De app '{failed_app}' kon niet upgraden en daardoor zijn de upgrades van de volgende apps geannuleerd: {apps}",
"app_start_install": "{app} installeren...",
"app_start_remove": "{app} verwijderen...",
"app_start_backup": "Bestanden aan het verzamelen voor de backup van {app}...",
"app_start_restore": "{app} herstellen...",
"app_upgrade_several_apps": "De volgende apps zullen worden geüpgraded: {apps}"
} }

View file

@ -327,7 +327,7 @@
"log_user_delete": "Levar lutilizaire « {} »", "log_user_delete": "Levar lutilizaire « {} »",
"log_user_update": "Actualizar las informacions de lutilizaire « {} »", "log_user_update": "Actualizar las informacions de lutilizaire « {} »",
"log_domain_main_domain": "Far venir « {} » lo domeni màger", "log_domain_main_domain": "Far venir « {} » lo domeni màger",
"log_tools_migrations_migrate_forward": "Migrar", "log_tools_migrations_migrate_forward": "Executar las migracions",
"log_tools_postinstall": "Realizar la post installacion del servidor YunoHost", "log_tools_postinstall": "Realizar la post installacion del servidor YunoHost",
"log_tools_upgrade": "Actualizacion dels paquets sistèma", "log_tools_upgrade": "Actualizacion dels paquets sistèma",
"log_tools_shutdown": "Atudar lo servidor", "log_tools_shutdown": "Atudar lo servidor",
@ -340,8 +340,8 @@
"migration_0005_not_enough_space": "I a pas pro despaci disponible sus {path} per lançar la migracion daquela passa :(.", "migration_0005_not_enough_space": "I a pas pro despaci disponible sus {path} per lançar la migracion daquela passa :(.",
"service_description_php7.0-fpm": "executa daplicacions escrichas en PHP amb nginx", "service_description_php7.0-fpm": "executa daplicacions escrichas en PHP amb nginx",
"users_available": "Lista dels utilizaires disponibles:", "users_available": "Lista dels utilizaires disponibles:",
"good_practices_about_admin_password": "Sètz per definir un nòu senhal per ladministracion. Lo senhal deu almens conténer 8 caractèrs - encara que siá de bon far dutilizar un senhal mai long quaquò (ex. una passafrasa) e/o dutilizar mantun tipes de caractèrs (majuscula, minuscula, nombre e caractèrs especials).", "good_practices_about_admin_password": "Sètz per definir un nòu senhal per ladministracion. Lo senhal deu almens conténer 8 caractèrs - encara que siá de bon far dutilizar un senhal mai long quaquò (ex. una passafrasa) e/o dutilizar mantun tipe de caractèrs (majuscula, minuscula, nombre e caractèrs especials).",
"good_practices_about_user_password": "Sètz a mand de definir un nòu senhal dutilizaire. Lo nòu senhal deu conténer almens 8 caractèrs, es de bon far dutilizar un senhal mai long (es a dire una frasa de senhal) e/o utilizar mantuns tipes de caractèrs (majusculas, minusculas, nombres e caractèrs especials).", "good_practices_about_user_password": "Sètz a mand de definir un nòu senhal dutilizaire. Lo nòu senhal deu conténer almens 8 caractèrs, es de bon far dutilizar un senhal mai long (es a dire una frasa de senhal) e/o utilizar mantun tipe de caractèrs (majusculas, minusculas, nombres e caractèrs especials).",
"migration_description_0006_sync_admin_and_root_passwords": "Sincronizar los senhals admin e root", "migration_description_0006_sync_admin_and_root_passwords": "Sincronizar los senhals admin e root",
"migration_0006_disclaimer": "Ara YunoHost sespèra que los senhals admin e root sián sincronizats. En lançant aquesta migracion, vòstre senhal root serà remplaçat pel senhal admin.", "migration_0006_disclaimer": "Ara YunoHost sespèra que los senhals admin e root sián sincronizats. En lançant aquesta migracion, vòstre senhal root serà remplaçat pel senhal admin.",
"password_listed": "Aqueste senhal es un dels mai utilizats al monde. Se vos plai utilizatz-ne un mai unic.", "password_listed": "Aqueste senhal es un dels mai utilizats al monde. Se vos plai utilizatz-ne un mai unic.",
@ -593,5 +593,19 @@
"app_argument_password_no_default": "Error pendent lanalisi de largument del senhal « {name} » : largument de senhal pòt pas aver de valor per defaut per de rason de seguretat", "app_argument_password_no_default": "Error pendent lanalisi de largument del senhal « {name} » : largument de senhal pòt pas aver de valor per defaut per de rason de seguretat",
"app_label_deprecated": "Aquesta comanda es estada renduda obsolèta. Mercés d'utilizar lo nòva \"yunohost user permission update\" per gerir letiquetada de l'aplication", "app_label_deprecated": "Aquesta comanda es estada renduda obsolèta. Mercés d'utilizar lo nòva \"yunohost user permission update\" per gerir letiquetada de l'aplication",
"additional_urls_already_removed": "URL addicionala {url:s} es ja estada elimida per la permission «#permission:s»", "additional_urls_already_removed": "URL addicionala {url:s} es ja estada elimida per la permission «#permission:s»",
"additional_urls_already_added": "URL addicionadal «{url:s}'» es ja estada aponduda per la permission «{permission:s}»" "additional_urls_already_added": "URL addicionadal «{url:s}'» es ja estada aponduda per la permission «{permission:s}»",
"migration_0015_yunohost_upgrade": "Aviada de la mesa a jorn de YunoHost...",
"migration_0015_main_upgrade": "Aviada de la mesa a nivèl generala...",
"migration_0015_patching_sources_list": "Mesa a jorn del fichièr sources.lists...",
"migration_0015_start": "Aviar la migracion cap a Buster",
"migration_description_0017_postgresql_9p6_to_11": "Migrar las basas de donadas de PostgreSQL 9.6 cap a 11",
"migration_description_0016_php70_to_php73_pools": "Migrar los fichièrs de configuracion php7.0 cap a php7.3",
"migration_description_0015_migrate_to_buster": "Mesa a nivèl dels sistèmas Debian Buster e YunoHost 4.x",
"migrating_legacy_permission_settings": "Migracion dels paramètres de permission ancians...",
"log_app_config_apply": "Aplicar la configuracion a laplicacion « {} »",
"log_app_config_show_panel": "Mostrar lo panèl de configuracion de laplicacion « {} »",
"log_app_action_run": "Executar laccion de laplicacion « {} »",
"diagnosis_basesystem_hardware_model": "Lo modèl del servidor es {model}",
"backup_archive_cant_retrieve_info_json": "Obtencion impossibla de las informacions de larchiu « {archive} »... Se pòt pas recuperar lo fichièr info.json (o es pas un fichièr json valid).",
"app_packaging_format_not_supported": "Se pòt pas installar aquesta aplicacion pramor que son format es pas pres en carga per vòstra version de YunoHost. Deuriatz considerar actualizar lo sistèma."
} }

View file

@ -1,3 +1,12 @@
{ {
"password_too_simple_1": "Hasło musi mieć co najmniej 8 znaków" "password_too_simple_1": "Hasło musi mieć co najmniej 8 znaków",
"app_already_up_to_date": "{app:s} jest obecnie aktualna",
"app_already_installed": "{app:s} jest już zainstalowane",
"already_up_to_date": "Nic do zrobienia. Wszystko jest obecnie aktualne.",
"admin_password_too_long": "Proszę wybrać hasło krótsze niż 127 znaków",
"admin_password_changed": "Hasło administratora zostało zmienione",
"admin_password_change_failed": "Nie można zmienić hasła",
"admin_password": "Hasło administratora",
"action_invalid": "Nieprawidłowa operacja '{action:s}'",
"aborting": "Przerywanie."
} }

View file

@ -2,7 +2,7 @@
"password_too_simple_1": "密码长度至少为8个字符", "password_too_simple_1": "密码长度至少为8个字符",
"backup_created": "备份已创建", "backup_created": "备份已创建",
"app_start_remove": "正在删除{app}……", "app_start_remove": "正在删除{app}……",
"admin_password_change_failed": "不能修改密码", "admin_password_change_failed": "无法修改密码",
"admin_password_too_long": "请选择一个小于127个字符的密码", "admin_password_too_long": "请选择一个小于127个字符的密码",
"app_upgrade_failed": "不能升级{app:s}{error}", "app_upgrade_failed": "不能升级{app:s}{error}",
"app_id_invalid": "无效 app ID", "app_id_invalid": "无效 app ID",

View file

@ -89,116 +89,73 @@ def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yun
if not os.path.isdir(logdir): if not os.path.isdir(logdir):
os.makedirs(logdir, 0o750) os.makedirs(logdir, 0o750)
# ####################################################################### # logging_configuration = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'console': {
'format': '%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
'tty-debug': {
'format': '%(relativeCreated)-4d %(fmessage)s'
},
'precise': {
'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
},
'filters': {
'action': {
'()': 'moulinette.utils.log.ActionFilter',
},
},
'handlers': {
'cli': {
'level': 'DEBUG' if debug else 'INFO',
'class': 'moulinette.interfaces.cli.TTYHandler',
'formatter': 'tty-debug' if debug else '',
},
'api': {
'level': 'DEBUG' if debug else 'INFO',
'class': 'moulinette.interfaces.api.APIQueueHandler',
},
'file': {
'class': 'logging.FileHandler',
'formatter': 'precise',
'filename': logfile,
'filters': ['action'],
},
},
'loggers': {
'yunohost': {
'level': 'DEBUG',
'handlers': ['file', interface] if not quiet else ['file'],
'propagate': False,
},
'moulinette': {
'level': 'DEBUG',
'handlers': ['file', interface] if not quiet else ['file'],
'propagate': False,
},
},
'root': {
'level': 'DEBUG',
'handlers': ['file', interface] if debug else ['file'],
},
}
# Logging configuration for CLI (or any other interface than api...) # # Logging configuration for CLI (or any other interface than api...) #
# ####################################################################### #
if interface != "api": if interface != "api":
configure_logging( configure_logging(logging_configuration)
{
"version": 1,
"main_logger": "yunohost",
"disable_existing_loggers": True,
"formatters": {
"tty-debug": {"format": "%(relativeCreated)-4d %(fmessage)s"},
"precise": {
"format": "%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s"
},
},
"filters": {
"action": {
"()": "moulinette.utils.log.ActionFilter",
},
},
"handlers": {
"tty": {
"level": "DEBUG" if debug else "INFO",
"class": "moulinette.interfaces.cli.TTYHandler",
"formatter": "tty-debug" if debug else "",
},
"file": {
"class": "logging.FileHandler",
"formatter": "precise",
"filename": logfile,
"filters": ["action"],
},
},
"loggers": {
"yunohost": {
"level": "DEBUG",
"handlers": ["file", "tty"] if not quiet else ["file"],
"propagate": False,
},
"moulinette": {
"level": "DEBUG",
"handlers": [],
"propagate": True,
},
"moulinette.interface": {
"level": "DEBUG",
"handlers": ["file", "tty"] if not quiet else ["file"],
"propagate": False,
},
},
"root": {
"level": "DEBUG",
"handlers": ["file", "tty"] if debug else ["file"],
},
}
)
# ####################################################################### #
# Logging configuration for API # # Logging configuration for API #
# ####################################################################### #
else: else:
configure_logging( # We use a WatchedFileHandler instead of regular FileHandler to possibly support log rotation etc
{ logging_configuration["handlers"]["file"]["class"] = 'logging.handlers.WatchedFileHandler'
"version": 1,
"disable_existing_loggers": True, # This is for when launching yunohost-api in debug mode, we want to display stuff in the console
"formatters": { if debug:
"console": { logging_configuration["loggers"]["yunohost"]["handlers"].append("cli")
"format": "%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s" logging_configuration["loggers"]["moulinette"]["handlers"].append("cli")
}, logging_configuration["root"]["handlers"].append("cli")
"precise": {
"format": "%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s" configure_logging(logging_configuration)
},
},
"filters": {
"action": {
"()": "moulinette.utils.log.ActionFilter",
},
},
"handlers": {
"api": {
"level": "DEBUG" if debug else "INFO",
"class": "moulinette.interfaces.api.APIQueueHandler",
},
"file": {
"class": "logging.handlers.WatchedFileHandler",
"formatter": "precise",
"filename": logfile,
"filters": ["action"],
},
"console": {
"class": "logging.StreamHandler",
"formatter": "console",
"stream": "ext://sys.stdout",
"filters": ["action"],
},
},
"loggers": {
"yunohost": {
"level": "DEBUG",
"handlers": ["file", "api"] + (["console"] if debug else []),
"propagate": False,
},
"moulinette": {
"level": "DEBUG",
"handlers": [],
"propagate": True,
},
},
"root": {
"level": "DEBUG",
"handlers": ["file"] + (["console"] if debug else []),
},
}
)

View file

@ -54,7 +54,7 @@ from moulinette.utils.filesystem import (
from yunohost.service import service_status, _run_service_command from yunohost.service import service_status, _run_service_command
from yunohost.utils import packages from yunohost.utils import packages
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.log import is_unit_operation, OperationLogger from yunohost.log import is_unit_operation, OperationLogger
logger = getActionLogger("yunohost.app") logger = getActionLogger("yunohost.app")
@ -192,7 +192,7 @@ def app_info(app, full=False):
from yunohost.permission import user_permission_list from yunohost.permission import user_permission_list
if not _is_installed(app): if not _is_installed(app):
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
@ -257,8 +257,9 @@ def _app_upgradable(app_infos):
return "url_required" return "url_required"
# Do not advertise upgrades for bad-quality apps # Do not advertise upgrades for bad-quality apps
level = app_in_catalog.get("level", -1)
if ( if (
not app_in_catalog.get("level", -1) >= 5 not (isinstance(level, int) and level >= 5)
or app_in_catalog.get("state") != "working" or app_in_catalog.get("state") != "working"
): ):
return "bad_quality" return "bad_quality"
@ -321,7 +322,7 @@ def app_map(app=None, raw=False, user=None):
if app is not None: if app is not None:
if not _is_installed(app): if not _is_installed(app):
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
apps = [ apps = [
@ -421,14 +422,14 @@ def app_change_url(operation_logger, app, domain, path):
installed = _is_installed(app) installed = _is_installed(app)
if not installed: if not installed:
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
if not os.path.exists( if not os.path.exists(
os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url") os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")
): ):
raise YunohostError("app_change_url_no_script", app_name=app) raise YunohostValidationError("app_change_url_no_script", app_name=app)
old_domain = app_setting(app, "domain") old_domain = app_setting(app, "domain")
old_path = app_setting(app, "path") old_path = app_setting(app, "path")
@ -438,7 +439,7 @@ def app_change_url(operation_logger, app, domain, path):
domain, path = _normalize_domain_path(domain, path) domain, path = _normalize_domain_path(domain, path)
if (domain, path) == (old_domain, old_path): if (domain, path) == (old_domain, old_path):
raise YunohostError( raise YunohostValidationError(
"app_change_url_identical_domains", domain=domain, path=path "app_change_url_identical_domains", domain=domain, path=path
) )
@ -551,12 +552,12 @@ def app_upgrade(app=[], url=None, file=None, force=False):
# Abort if any of those app is in fact not installed.. # Abort if any of those app is in fact not installed..
for app in [app_ for app_ in apps if not _is_installed(app_)]: for app in [app_ for app_ in apps if not _is_installed(app_)]:
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
if len(apps) == 0: if len(apps) == 0:
raise YunohostError("apps_already_up_to_date") raise YunohostValidationError("apps_already_up_to_date")
if len(apps) > 1: if len(apps) > 1:
logger.info(m18n.n("app_upgrade_several_apps", apps=", ".join(apps))) logger.info(m18n.n("app_upgrade_several_apps", apps=", ".join(apps)))
@ -880,11 +881,11 @@ def app_install(
confirm_install("thirdparty") confirm_install("thirdparty")
manifest, extracted_app_folder = _extract_app_from_file(app) manifest, extracted_app_folder = _extract_app_from_file(app)
else: else:
raise YunohostError("app_unknown") raise YunohostValidationError("app_unknown")
# Check ID # Check ID
if "id" not in manifest or "__" in manifest["id"]: if "id" not in manifest or "__" in manifest["id"]:
raise YunohostError("app_id_invalid") raise YunohostValidationError("app_id_invalid")
app_id = manifest["id"] app_id = manifest["id"]
label = label if label else manifest["name"] label = label if label else manifest["name"]
@ -897,7 +898,7 @@ def app_install(
instance_number = _installed_instance_number(app_id, last=True) + 1 instance_number = _installed_instance_number(app_id, last=True) + 1
if instance_number > 1: if instance_number > 1:
if "multi_instance" not in manifest or not is_true(manifest["multi_instance"]): if "multi_instance" not in manifest or not is_true(manifest["multi_instance"]):
raise YunohostError("app_already_installed", app=app_id) raise YunohostValidationError("app_already_installed", app=app_id)
# Change app_id to the forked app id # Change app_id to the forked app id
app_instance_name = app_id + "__" + str(instance_number) app_instance_name = app_id + "__" + str(instance_number)
@ -1069,7 +1070,7 @@ def app_install(
env_dict_remove["YNH_APP_ID"] = app_id env_dict_remove["YNH_APP_ID"] = app_id
env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name
env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number)
env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") env_dict_remove["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
# Execute remove script # Execute remove script
operation_logger_remove = OperationLogger( operation_logger_remove = OperationLogger(
@ -1124,8 +1125,7 @@ def app_install(
raise YunohostError( raise YunohostError(
failure_message_with_debug_instructions, failure_message_with_debug_instructions,
raw_msg=True, raw_msg=True
log_ref=operation_logger.name,
) )
# Clean hooks and add new ones # Clean hooks and add new ones
@ -1209,7 +1209,7 @@ def app_remove(operation_logger, app):
) )
if not _is_installed(app): if not _is_installed(app):
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
@ -1372,10 +1372,10 @@ def app_makedefault(operation_logger, app, domain=None):
domain = app_domain domain = app_domain
operation_logger.related_to.append(("domain", domain)) operation_logger.related_to.append(("domain", domain))
elif domain not in domain_list()["domains"]: elif domain not in domain_list()["domains"]:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
if "/" in app_map(raw=True)[domain]: if "/" in app_map(raw=True)[domain]:
raise YunohostError( raise YunohostValidationError(
"app_make_default_location_already_used", "app_make_default_location_already_used",
app=app, app=app,
domain=app_domain, domain=app_domain,
@ -1578,7 +1578,7 @@ def app_register_url(app, domain, path):
if _is_installed(app): if _is_installed(app):
settings = _get_app_settings(app) settings = _get_app_settings(app)
if "path" in settings.keys() and "domain" in settings.keys(): if "path" in settings.keys() and "domain" in settings.keys():
raise YunohostError("app_already_installed_cant_change_url") raise YunohostValidationError("app_already_installed_cant_change_url")
# Check the url is available # Check the url is available
_assert_no_conflicting_apps(domain, path) _assert_no_conflicting_apps(domain, path)
@ -1694,7 +1694,7 @@ def app_change_label(app, new_label):
installed = _is_installed(app) installed = _is_installed(app)
if not installed: if not installed:
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
logger.warning(m18n.n("app_label_deprecated")) logger.warning(m18n.n("app_label_deprecated"))
@ -1730,7 +1730,7 @@ def app_action_run(operation_logger, app, action, args=None):
actions = {x["id"]: x for x in actions} actions = {x["id"]: x for x in actions}
if action not in actions: if action not in actions:
raise YunohostError( raise YunohostValidationError(
"action '%s' not available for app '%s', available actions are: %s" "action '%s' not available for app '%s', available actions are: %s"
% (action, app, ", ".join(actions.keys())), % (action, app, ", ".join(actions.keys())),
raw_msg=True, raw_msg=True,
@ -1884,7 +1884,7 @@ def app_config_apply(operation_logger, app, args):
installed = _is_installed(app) installed = _is_installed(app)
if not installed: if not installed:
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
) )
@ -2199,7 +2199,7 @@ def _get_app_settings(app_id):
""" """
if not _is_installed(app_id): if not _is_installed(app_id):
raise YunohostError( raise YunohostValidationError(
"app_not_installed", app=app_id, all_apps=_get_all_installed_apps_id() "app_not_installed", app=app_id, all_apps=_get_all_installed_apps_id()
) )
try: try:
@ -2546,9 +2546,9 @@ def _fetch_app_from_git(app):
app_id, _ = _parse_app_instance_name(app) app_id, _ = _parse_app_instance_name(app)
if app_id not in app_dict: if app_id not in app_dict:
raise YunohostError("app_unknown") raise YunohostValidationError("app_unknown")
elif "git" not in app_dict[app_id]: elif "git" not in app_dict[app_id]:
raise YunohostError("app_unsupported_remote_type") raise YunohostValidationError("app_unsupported_remote_type")
app_info = app_dict[app_id] app_info = app_dict[app_id]
url = app_info["git"]["url"] url = app_info["git"]["url"]
@ -2684,7 +2684,7 @@ def _check_manifest_requirements(manifest, app_instance_name):
packaging_format = int(manifest.get("packaging_format", 0)) packaging_format = int(manifest.get("packaging_format", 0))
if packaging_format not in [0, 1]: if packaging_format not in [0, 1]:
raise YunohostError("app_packaging_format_not_supported") raise YunohostValidationError("app_packaging_format_not_supported")
requirements = manifest.get("requirements", dict()) requirements = manifest.get("requirements", dict())
@ -2697,7 +2697,7 @@ def _check_manifest_requirements(manifest, app_instance_name):
for pkgname, spec in requirements.items(): for pkgname, spec in requirements.items():
if not packages.meets_version_specifier(pkgname, spec): if not packages.meets_version_specifier(pkgname, spec):
version = packages.ynh_packages_version()[pkgname]["version"] version = packages.ynh_packages_version()[pkgname]["version"]
raise YunohostError( raise YunohostValidationError(
"app_requirements_unmeet", "app_requirements_unmeet",
pkgname=pkgname, pkgname=pkgname,
version=version, version=version,
@ -2796,7 +2796,7 @@ class YunoHostArgumentFormatParser(object):
# we don't have an answer, check optional and default_value # we don't have an answer, check optional and default_value
if question.value is None or question.value == "": if question.value is None or question.value == "":
if not question.optional and question.default is None: if not question.optional and question.default is None:
raise YunohostError("app_argument_required", name=question.name) raise YunohostValidationError("app_argument_required", name=question.name)
else: else:
question.value = ( question.value = (
getattr(self, "default_value", None) getattr(self, "default_value", None)
@ -2816,7 +2816,7 @@ class YunoHostArgumentFormatParser(object):
return (question.value, self.argument_type) return (question.value, self.argument_type)
def _raise_invalid_answer(self, question): def _raise_invalid_answer(self, question):
raise YunohostError( raise YunohostValidationError(
"app_argument_choice_invalid", "app_argument_choice_invalid",
name=question.name, name=question.name,
choices=", ".join(question.choices), choices=", ".join(question.choices),
@ -2854,13 +2854,13 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser):
) )
if question.default is not None: if question.default is not None:
raise YunohostError("app_argument_password_no_default", name=question.name) raise YunohostValidationError("app_argument_password_no_default", name=question.name)
return question return question
def _post_parse_value(self, question): def _post_parse_value(self, question):
if any(char in question.value for char in self.forbidden_chars): if any(char in question.value for char in self.forbidden_chars):
raise YunohostError( raise YunohostValidationError(
"pattern_password_app", forbidden_chars=self.forbidden_chars "pattern_password_app", forbidden_chars=self.forbidden_chars
) )
@ -2913,7 +2913,7 @@ class BooleanArgumentParser(YunoHostArgumentFormatParser):
if str(question.value).lower() in ["0", "no", "n", "false"]: if str(question.value).lower() in ["0", "no", "n", "false"]:
return 0 return 0
raise YunohostError( raise YunohostValidationError(
"app_argument_choice_invalid", "app_argument_choice_invalid",
name=question.name, name=question.name,
choices="yes, no, y, n, 1, 0", choices="yes, no, y, n, 1, 0",
@ -2938,7 +2938,7 @@ class DomainArgumentParser(YunoHostArgumentFormatParser):
return question return question
def _raise_invalid_answer(self, question): def _raise_invalid_answer(self, question):
raise YunohostError( raise YunohostValidationError(
"app_argument_invalid", name=question.name, error=m18n.n("domain_unknown") "app_argument_invalid", name=question.name, error=m18n.n("domain_unknown")
) )
@ -2964,7 +2964,7 @@ class UserArgumentParser(YunoHostArgumentFormatParser):
return question return question
def _raise_invalid_answer(self, question): def _raise_invalid_answer(self, question):
raise YunohostError( raise YunohostValidationError(
"app_argument_invalid", "app_argument_invalid",
name=question.name, name=question.name,
error=m18n.n("user_unknown", user=question.value), error=m18n.n("user_unknown", user=question.value),
@ -2992,7 +2992,7 @@ class NumberArgumentParser(YunoHostArgumentFormatParser):
if isinstance(question.value, str) and question.value.isdigit(): if isinstance(question.value, str) and question.value.isdigit():
return int(question.value) return int(question.value)
raise YunohostError( raise YunohostValidationError(
"app_argument_invalid", name=question.name, error=m18n.n("invalid_number") "app_argument_invalid", name=question.name, error=m18n.n("invalid_number")
) )
@ -3123,7 +3123,7 @@ def _get_conflicting_apps(domain, path, ignore_app=None):
# Abort if domain is unknown # Abort if domain is unknown
if domain not in domain_list()["domains"]: if domain not in domain_list()["domains"]:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
# Fetch apps map # Fetch apps map
apps_map = app_map(raw=True) apps_map = app_map(raw=True)
@ -3162,9 +3162,9 @@ def _assert_no_conflicting_apps(domain, path, ignore_app=None, full_domain=False
) )
if full_domain: if full_domain:
raise YunohostError("app_full_domain_unavailable", domain=domain) raise YunohostValidationError("app_full_domain_unavailable", domain=domain)
else: else:
raise YunohostError("app_location_unavailable", apps="\n".join(apps)) raise YunohostValidationError("app_location_unavailable", apps="\n".join(apps))
def _make_environment_for_app_script(app, args={}, args_prefix="APP_ARG_"): def _make_environment_for_app_script(app, args={}, args_prefix="APP_ARG_"):
@ -3465,11 +3465,19 @@ def _assert_system_is_sane_for_app(manifest, when):
if "fail2ban" not in services: if "fail2ban" not in services:
services.append("fail2ban") services.append("fail2ban")
# Wait if a service is reloading
test_nb = 0
while test_nb < 10:
if not any(s for s in services if service_status(s)["status"] == "reloading"):
break
time.sleep(0.5)
test_nb+=1
# List services currently down and raise an exception if any are found # List services currently down and raise an exception if any are found
faulty_services = [s for s in services if service_status(s)["status"] != "running"] faulty_services = [s for s in services if service_status(s)["status"] != "running"]
if faulty_services: if faulty_services:
if when == "pre": if when == "pre":
raise YunohostError( raise YunohostValidationError(
"app_action_cannot_be_ran_because_required_services_down", "app_action_cannot_be_ran_because_required_services_down",
services=", ".join(faulty_services), services=", ".join(faulty_services),
) )
@ -3480,7 +3488,7 @@ def _assert_system_is_sane_for_app(manifest, when):
if packages.dpkg_is_broken(): if packages.dpkg_is_broken():
if when == "pre": if when == "pre":
raise YunohostError("dpkg_is_broken") raise YunohostValidationError("dpkg_is_broken")
elif when == "post": elif when == "post":
raise YunohostError("this_action_broke_dpkg") raise YunohostError("this_action_broke_dpkg")
@ -3659,7 +3667,7 @@ def _patch_legacy_helpers(app_folder):
# couldn't patch the deprecated helper in the previous lines. In # couldn't patch the deprecated helper in the previous lines. In
# that case, abort the install or whichever step is performed # that case, abort the install or whichever step is performed
if helper in content and infos["important"]: if helper in content and infos["important"]:
raise YunohostError( raise YunohostValidationError(
"This app is likely pretty old and uses deprecated / outdated helpers that can't be migrated easily. It can't be installed anymore.", "This app is likely pretty old and uses deprecated / outdated helpers that can't be migrated easily. It can't be installed anymore.",
raw_msg=True, raw_msg=True,
) )

View file

@ -63,7 +63,7 @@ from yunohost.hook import (
from yunohost.tools import tools_postinstall from yunohost.tools import tools_postinstall
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger from yunohost.log import OperationLogger
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.packages import ynh_packages_version from yunohost.utils.packages import ynh_packages_version
from yunohost.settings import settings_get from yunohost.settings import settings_get
@ -348,7 +348,7 @@ class BackupManager:
# Try to recursively unmount stuff (from a previously failed backup ?) # Try to recursively unmount stuff (from a previously failed backup ?)
if not _recursive_umount(self.work_dir): if not _recursive_umount(self.work_dir):
raise YunohostError("backup_output_directory_not_empty") raise YunohostValidationError("backup_output_directory_not_empty")
else: else:
# If umount succeeded, remove the directory (we checked that # If umount succeeded, remove the directory (we checked that
# we're in /home/yunohost.backup/tmp so that should be okay... # we're in /home/yunohost.backup/tmp so that should be okay...
@ -704,9 +704,7 @@ class BackupManager:
settings_dir = os.path.join(self.work_dir, "apps", app, "settings") settings_dir = os.path.join(self.work_dir, "apps", app, "settings")
logger.info(m18n.n("app_start_backup", app=app)) logger.info(m18n.n("app_start_backup", app=app))
tmp_script = ( tmp_folder = tempfile.mkdtemp()
None # This is to make sure the var exists later in the 'finally' ...
)
try: try:
# Prepare backup directory for the app # Prepare backup directory for the app
filesystem.mkdir(tmp_app_bkp_dir, 0o750, True, uid="admin") filesystem.mkdir(tmp_app_bkp_dir, 0o750, True, uid="admin")
@ -715,8 +713,8 @@ class BackupManager:
shutil.copytree(app_setting_path, settings_dir) shutil.copytree(app_setting_path, settings_dir)
# Copy app backup script in a temporary folder and execute it # Copy app backup script in a temporary folder and execute it
_, tmp_script = tempfile.mkstemp(prefix="backup_")
app_script = os.path.join(app_setting_path, "scripts/backup") app_script = os.path.join(app_setting_path, "scripts/backup")
tmp_script = os.path.join(tmp_folder, "backup")
subprocess.call(["install", "-Dm555", app_script, tmp_script]) subprocess.call(["install", "-Dm555", app_script, tmp_script])
hook_exec( hook_exec(
@ -752,8 +750,8 @@ class BackupManager:
# Remove tmp files in all situations # Remove tmp files in all situations
finally: finally:
if tmp_script: if tmp_folder and os.path.exists(tmp_folder):
filesystem.rm(tmp_script, force=True) shutil.rmtree(tmp_folder)
filesystem.rm(env_dict["YNH_BACKUP_CSV"], force=True) filesystem.rm(env_dict["YNH_BACKUP_CSV"], force=True)
# #
@ -1029,7 +1027,7 @@ class RestoreManager:
already_installed = [app for app in to_be_restored if _is_installed(app)] already_installed = [app for app in to_be_restored if _is_installed(app)]
if already_installed != []: if already_installed != []:
if already_installed == to_be_restored: if already_installed == to_be_restored:
raise YunohostError( raise YunohostValidationError(
"restore_already_installed_apps", apps=", ".join(already_installed) "restore_already_installed_apps", apps=", ".join(already_installed)
) )
else: else:
@ -1135,14 +1133,14 @@ class RestoreManager:
return True return True
elif free_space > needed_space: elif free_space > needed_space:
# TODO Add --force options to avoid the error raising # TODO Add --force options to avoid the error raising
raise YunohostError( raise YunohostValidationError(
"restore_may_be_not_enough_disk_space", "restore_may_be_not_enough_disk_space",
free_space=free_space, free_space=free_space,
needed_space=needed_space, needed_space=needed_space,
margin=margin, margin=margin,
) )
else: else:
raise YunohostError( raise YunohostValidationError(
"restore_not_enough_disk_space", "restore_not_enough_disk_space",
free_space=free_space, free_space=free_space,
needed_space=needed_space, needed_space=needed_space,
@ -1731,7 +1729,7 @@ class BackupMethod(object):
free_space, free_space,
backup_size, backup_size,
) )
raise YunohostError("not_enough_disk_space", path=self.repo) raise YunohostValidationError("not_enough_disk_space", path=self.repo)
def _organize_files(self): def _organize_files(self):
""" """
@ -2188,7 +2186,7 @@ def backup_create(
# Validate there is no archive with the same name # Validate there is no archive with the same name
if name and name in backup_list()["archives"]: if name and name in backup_list()["archives"]:
raise YunohostError("backup_archive_name_exists") raise YunohostValidationError("backup_archive_name_exists")
# By default we backup using the tar method # By default we backup using the tar method
if not methods: if not methods:
@ -2203,14 +2201,14 @@ def backup_create(
r"^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$", r"^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$",
output_directory, output_directory,
): ):
raise YunohostError("backup_output_directory_forbidden") raise YunohostValidationError("backup_output_directory_forbidden")
if "copy" in methods: if "copy" in methods:
if not output_directory: if not output_directory:
raise YunohostError("backup_output_directory_required") raise YunohostValidationError("backup_output_directory_required")
# Check that output directory is empty # Check that output directory is empty
elif os.path.isdir(output_directory) and os.listdir(output_directory): elif os.path.isdir(output_directory) and os.listdir(output_directory):
raise YunohostError("backup_output_directory_not_empty") raise YunohostValidationError("backup_output_directory_not_empty")
# If no --system or --apps given, backup everything # If no --system or --apps given, backup everything
if system is None and apps is None: if system is None and apps is None:
@ -2383,7 +2381,7 @@ def backup_download(name):
if not os.path.lexists(archive_file): if not os.path.lexists(archive_file):
archive_file += ".gz" archive_file += ".gz"
if not os.path.lexists(archive_file): if not os.path.lexists(archive_file):
raise YunohostError("backup_archive_name_unknown", name=name) raise YunohostValidationError("backup_archive_name_unknown", name=name)
# If symlink, retrieve the real path # If symlink, retrieve the real path
if os.path.islink(archive_file): if os.path.islink(archive_file):
@ -2391,7 +2389,7 @@ def backup_download(name):
# Raise exception if link is broken (e.g. on unmounted external storage) # Raise exception if link is broken (e.g. on unmounted external storage)
if not os.path.exists(archive_file): if not os.path.exists(archive_file):
raise YunohostError("backup_archive_broken_link", path=archive_file) raise YunohostValidationError("backup_archive_broken_link", path=archive_file)
# We return a raw bottle HTTPresponse (instead of serializable data like # We return a raw bottle HTTPresponse (instead of serializable data like
# list/dict, ...), which is gonna be picked and used directly by moulinette # list/dict, ...), which is gonna be picked and used directly by moulinette
@ -2417,7 +2415,7 @@ def backup_info(name, with_details=False, human_readable=False):
if not os.path.lexists(archive_file): if not os.path.lexists(archive_file):
archive_file += ".gz" archive_file += ".gz"
if not os.path.lexists(archive_file): if not os.path.lexists(archive_file):
raise YunohostError("backup_archive_name_unknown", name=name) raise YunohostValidationError("backup_archive_name_unknown", name=name)
# If symlink, retrieve the real path # If symlink, retrieve the real path
if os.path.islink(archive_file): if os.path.islink(archive_file):
@ -2425,7 +2423,7 @@ def backup_info(name, with_details=False, human_readable=False):
# Raise exception if link is broken (e.g. on unmounted external storage) # Raise exception if link is broken (e.g. on unmounted external storage)
if not os.path.exists(archive_file): if not os.path.exists(archive_file):
raise YunohostError("backup_archive_broken_link", path=archive_file) raise YunohostValidationError("backup_archive_broken_link", path=archive_file)
info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name)
@ -2533,7 +2531,7 @@ def backup_delete(name):
""" """
if name not in backup_list()["archives"]: if name not in backup_list()["archives"]:
raise YunohostError("backup_archive_name_unknown", name=name) raise YunohostValidationError("backup_archive_name_unknown", name=name)
hook_callback("pre_backup_delete", args=[name]) hook_callback("pre_backup_delete", args=[name])

View file

@ -37,7 +37,7 @@ from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file from moulinette.utils.filesystem import read_file
from yunohost.vendor.acme_tiny.acme_tiny import get_crt as sign_certificate from yunohost.vendor.acme_tiny.acme_tiny import get_crt as sign_certificate
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.network import get_public_ip from yunohost.utils.network import get_public_ip
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
@ -90,7 +90,7 @@ def certificate_status(domain_list, full=False):
for domain in domain_list: for domain in domain_list:
# Is it in Yunohost domain list? # Is it in Yunohost domain list?
if domain not in yunohost_domains_list: if domain not in yunohost_domains_list:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
certificates = {} certificates = {}
@ -166,7 +166,7 @@ def _certificate_install_selfsigned(domain_list, force=False):
status = _get_status(domain) status = _get_status(domain)
if status["summary"]["code"] in ("good", "great"): if status["summary"]["code"] in ("good", "great"):
raise YunohostError( raise YunohostValidationError(
"certmanager_attempt_to_replace_valid_cert", domain=domain "certmanager_attempt_to_replace_valid_cert", domain=domain
) )
@ -197,6 +197,8 @@ def _certificate_install_selfsigned(domain_list, force=False):
out, _ = p.communicate() out, _ = p.communicate()
out = out.decode("utf-8")
if p.returncode != 0: if p.returncode != 0:
logger.warning(out) logger.warning(out)
raise YunohostError("domain_cert_gen_failed") raise YunohostError("domain_cert_gen_failed")
@ -267,12 +269,12 @@ def _certificate_install_letsencrypt(
for domain in domain_list: for domain in domain_list:
yunohost_domains_list = yunohost.domain.domain_list()["domains"] yunohost_domains_list = yunohost.domain.domain_list()["domains"]
if domain not in yunohost_domains_list: if domain not in yunohost_domains_list:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
# Is it self-signed? # Is it self-signed?
status = _get_status(domain) status = _get_status(domain)
if not force and status["CA_type"]["code"] != "self-signed": if not force and status["CA_type"]["code"] != "self-signed":
raise YunohostError( raise YunohostValidationError(
"certmanager_domain_cert_not_selfsigned", domain=domain "certmanager_domain_cert_not_selfsigned", domain=domain
) )
@ -370,25 +372,25 @@ def certificate_renew(
# Is it in Yunohost dmomain list? # Is it in Yunohost dmomain list?
if domain not in yunohost.domain.domain_list()["domains"]: if domain not in yunohost.domain.domain_list()["domains"]:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
status = _get_status(domain) status = _get_status(domain)
# Does it expire soon? # Does it expire soon?
if status["validity"] > VALIDITY_LIMIT and not force: if status["validity"] > VALIDITY_LIMIT and not force:
raise YunohostError( raise YunohostValidationError(
"certmanager_attempt_to_renew_valid_cert", domain=domain "certmanager_attempt_to_renew_valid_cert", domain=domain
) )
# Does it have a Let's Encrypt cert? # Does it have a Let's Encrypt cert?
if status["CA_type"]["code"] != "lets-encrypt": if status["CA_type"]["code"] != "lets-encrypt":
raise YunohostError( raise YunohostValidationError(
"certmanager_attempt_to_renew_nonLE_cert", domain=domain "certmanager_attempt_to_renew_nonLE_cert", domain=domain
) )
# Check ACME challenge configured for given domain # Check ACME challenge configured for given domain
if not _check_acme_challenge_configuration(domain): if not _check_acme_challenge_configuration(domain):
raise YunohostError( raise YunohostValidationError(
"certmanager_acme_not_configured_for_domain", domain=domain "certmanager_acme_not_configured_for_domain", domain=domain
) )
@ -898,20 +900,20 @@ def _check_domain_is_ready_for_ACME(domain):
) )
if not dnsrecords or not httpreachable: if not dnsrecords or not httpreachable:
raise YunohostError("certmanager_domain_not_diagnosed_yet", domain=domain) raise YunohostValidationError("certmanager_domain_not_diagnosed_yet", domain=domain)
# Check if IP from DNS matches public IP # Check if IP from DNS matches public IP
if not dnsrecords.get("status") in [ if not dnsrecords.get("status") in [
"SUCCESS", "SUCCESS",
"WARNING", "WARNING",
]: # Warning is for missing IPv6 record which ain't critical for ACME ]: # Warning is for missing IPv6 record which ain't critical for ACME
raise YunohostError( raise YunohostValidationError(
"certmanager_domain_dns_ip_differs_from_public_ip", domain=domain "certmanager_domain_dns_ip_differs_from_public_ip", domain=domain
) )
# Check if domain seems to be accessible through HTTP? # Check if domain seems to be accessible through HTTP?
if not httpreachable.get("status") == "SUCCESS": if not httpreachable.get("status") == "SUCCESS":
raise YunohostError("certmanager_domain_http_not_working", domain=domain) raise YunohostValidationError("certmanager_domain_http_not_working", domain=domain)
# FIXME / TODO : ideally this should not be needed. There should be a proper # FIXME / TODO : ideally this should not be needed. There should be a proper

View file

@ -1,7 +1,7 @@
import subprocess import subprocess
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration from yunohost.tools import Migration
@ -23,7 +23,7 @@ class MyMigration(Migration):
return return
if not self.package_is_installed("postgresql-11"): if not self.package_is_installed("postgresql-11"):
raise YunohostError("migration_0017_postgresql_11_not_installed") raise YunohostValidationError("migration_0017_postgresql_11_not_installed")
# Make sure there's a 9.6 cluster # Make sure there's a 9.6 cluster
try: try:
@ -37,7 +37,7 @@ class MyMigration(Migration):
if not space_used_by_directory( if not space_used_by_directory(
"/var/lib/postgresql/9.6" "/var/lib/postgresql/9.6"
) > free_space_in_directory("/var/lib/postgresql"): ) > free_space_in_directory("/var/lib/postgresql"):
raise YunohostError( raise YunohostValidationError(
"migration_0017_not_enough_space", path="/var/lib/postgresql/" "migration_0017_not_enough_space", path="/var/lib/postgresql/"
) )

View file

@ -37,7 +37,7 @@ from moulinette.utils.filesystem import (
write_to_yaml, write_to_yaml,
) )
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.hook import hook_list, hook_exec from yunohost.hook import hook_list, hook_exec
logger = log.getActionLogger("yunohost.diagnosis") logger = log.getActionLogger("yunohost.diagnosis")
@ -59,11 +59,11 @@ def diagnosis_get(category, item):
all_categories_names = [c for c, _ in all_categories] all_categories_names = [c for c, _ in all_categories]
if category not in all_categories_names: if category not in all_categories_names:
raise YunohostError("diagnosis_unknown_categories", categories=category) raise YunohostValidationError("diagnosis_unknown_categories", categories=category)
if isinstance(item, list): if isinstance(item, list):
if any("=" not in criteria for criteria in item): if any("=" not in criteria for criteria in item):
raise YunohostError( raise YunohostValidationError(
"Criterias should be of the form key=value (e.g. domain=yolo.test)" "Criterias should be of the form key=value (e.g. domain=yolo.test)"
) )
@ -91,7 +91,7 @@ def diagnosis_show(
else: else:
unknown_categories = [c for c in categories if c not in all_categories_names] unknown_categories = [c for c in categories if c not in all_categories_names]
if unknown_categories: if unknown_categories:
raise YunohostError( raise YunohostValidationError(
"diagnosis_unknown_categories", categories=", ".join(unknown_categories) "diagnosis_unknown_categories", categories=", ".join(unknown_categories)
) )
@ -181,7 +181,7 @@ def diagnosis_run(
else: else:
unknown_categories = [c for c in categories if c not in all_categories_names] unknown_categories = [c for c in categories if c not in all_categories_names]
if unknown_categories: if unknown_categories:
raise YunohostError( raise YunohostValidationError(
"diagnosis_unknown_categories", categories=", ".join(unknown_categories) "diagnosis_unknown_categories", categories=", ".join(unknown_categories)
) )
@ -270,14 +270,14 @@ def diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
# Sanity checks for the provided arguments # Sanity checks for the provided arguments
if len(filter_) == 0: if len(filter_) == 0:
raise YunohostError( raise YunohostValidationError(
"You should provide at least one criteria being the diagnosis category to ignore" "You should provide at least one criteria being the diagnosis category to ignore"
) )
category = filter_[0] category = filter_[0]
if category not in all_categories_names: if category not in all_categories_names:
raise YunohostError("%s is not a diagnosis category" % category) raise YunohostValidationError("%s is not a diagnosis category" % category)
if any("=" not in criteria for criteria in filter_[1:]): if any("=" not in criteria for criteria in filter_[1:]):
raise YunohostError( raise YunohostValidationError(
"Criterias should be of the form key=value (e.g. domain=yolo.test)" "Criterias should be of the form key=value (e.g. domain=yolo.test)"
) )
@ -331,7 +331,7 @@ def diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
configuration["ignore_filters"][category] = [] configuration["ignore_filters"][category] = []
if criterias not in configuration["ignore_filters"][category]: if criterias not in configuration["ignore_filters"][category]:
raise YunohostError("This filter does not exists.") raise YunohostValidationError("This filter does not exists.")
configuration["ignore_filters"][category].remove(criterias) configuration["ignore_filters"][category].remove(criterias)
_diagnosis_write_configuration(configuration) _diagnosis_write_configuration(configuration)
@ -551,9 +551,8 @@ class Diagnoser:
@staticmethod @staticmethod
def get_description(id_): def get_description(id_):
key = "diagnosis_description_" + id_ key = "diagnosis_description_" + id_
descr = m18n.n(key)
# If no description available, fallback to id # If no description available, fallback to id
return descr if descr != key else id_ return m18n.n(key) if m18n.key_exists(key) else id_
@staticmethod @staticmethod
def i18n(report, force_remove_html_tags=False): def i18n(report, force_remove_html_tags=False):

View file

@ -30,7 +30,7 @@ import yaml
from moulinette import m18n, msettings, msignals from moulinette import m18n, msettings, msignals
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file from moulinette.utils.filesystem import write_to_file
@ -105,16 +105,14 @@ def domain_add(operation_logger, domain, dyndns=False):
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
if domain.startswith("xmpp-upload."): if domain.startswith("xmpp-upload."):
raise YunohostError("domain_cannot_add_xmpp_upload") raise YunohostValidationError("domain_cannot_add_xmpp_upload")
ldap = _get_ldap_interface() ldap = _get_ldap_interface()
try: try:
ldap.validate_uniqueness({"virtualdomain": domain}) ldap.validate_uniqueness({"virtualdomain": domain})
except MoulinetteError: except MoulinetteError:
raise YunohostError("domain_exists") raise YunohostValidationError("domain_exists")
operation_logger.start()
# Lower domain to avoid some edge cases issues # Lower domain to avoid some edge cases issues
# See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765 # See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765
@ -123,17 +121,21 @@ def domain_add(operation_logger, domain, dyndns=False):
# DynDNS domain # DynDNS domain
if dyndns: if dyndns:
# Do not allow to subscribe to multiple dyndns domains... from yunohost.dyndns import _dyndns_provides, _guess_current_dyndns_domain
if os.path.exists("/etc/cron.d/yunohost-dyndns"):
raise YunohostError("domain_dyndns_already_subscribed")
from yunohost.dyndns import dyndns_subscribe, _dyndns_provides # Do not allow to subscribe to multiple dyndns domains...
if _guess_current_dyndns_domain("dyndns.yunohost.org") != (None, None):
raise YunohostValidationError('domain_dyndns_already_subscribed')
# Check that this domain can effectively be provided by # Check that this domain can effectively be provided by
# dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st) # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st)
if not _dyndns_provides("dyndns.yunohost.org", domain): if not _dyndns_provides("dyndns.yunohost.org", domain):
raise YunohostError("domain_dyndns_root_unknown") raise YunohostValidationError("domain_dyndns_root_unknown")
operation_logger.start()
if dyndns:
from yunohost.dyndns import dyndns_subscribe
# Actually subscribe # Actually subscribe
dyndns_subscribe(domain=domain) dyndns_subscribe(domain=domain)
@ -201,7 +203,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
# we don't want to check the domain exists because the ldap add may have # we don't want to check the domain exists because the ldap add may have
# failed # failed
if not force and domain not in domain_list()['domains']: if not force and domain not in domain_list()['domains']:
raise YunohostError('domain_name_unknown', domain=domain) raise YunohostValidationError('domain_name_unknown', domain=domain)
# Check domain is not the main domain # Check domain is not the main domain
if domain == _get_maindomain(): if domain == _get_maindomain():
@ -209,13 +211,13 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
other_domains.remove(domain) other_domains.remove(domain)
if other_domains: if other_domains:
raise YunohostError( raise YunohostValidationError(
"domain_cannot_remove_main", "domain_cannot_remove_main",
domain=domain, domain=domain,
other_domains="\n * " + ("\n * ".join(other_domains)), other_domains="\n * " + ("\n * ".join(other_domains)),
) )
else: else:
raise YunohostError("domain_cannot_remove_main_add_new_one", domain=domain) raise YunohostValidationError("domain_cannot_remove_main_add_new_one", domain=domain)
# Check if apps are installed on the domain # Check if apps are installed on the domain
apps_on_that_domain = [] apps_on_that_domain = []
@ -238,9 +240,10 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
for app, _ in apps_on_that_domain: for app, _ in apps_on_that_domain:
app_remove(app) app_remove(app)
else: else:
raise YunohostError('domain_uninstall_app_first', apps="\n".join([x[1] for x in apps_on_that_domain])) raise YunohostValidationError('domain_uninstall_app_first', apps="\n".join([x[1] for x in apps_on_that_domain]))
operation_logger.start() operation_logger.start()
ldap = _get_ldap_interface() ldap = _get_ldap_interface()
try: try:
ldap.remove("virtualdomain=" + domain + ",ou=domains") ldap.remove("virtualdomain=" + domain + ",ou=domains")
@ -249,6 +252,9 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
os.system("rm -rf /etc/yunohost/certs/%s" % domain) os.system("rm -rf /etc/yunohost/certs/%s" % domain)
# Delete dyndns keys for this domain (if any)
os.system('rm -rf /etc/yunohost/dyndns/K%s.+*' % domain)
# Sometime we have weird issues with the regenconf where some files # Sometime we have weird issues with the regenconf where some files
# appears as manually modified even though they weren't touched ... # appears as manually modified even though they weren't touched ...
# There are a few ideas why this happens (like backup/restore nginx # There are a few ideas why this happens (like backup/restore nginx
@ -288,7 +294,7 @@ def domain_dns_conf(domain):
""" """
if domain not in domain_list()["domains"]: if domain not in domain_list()["domains"]:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
domains_settings = _get_domain_settings(domain, True) domains_settings = _get_domain_settings(domain, True)
@ -345,7 +351,7 @@ def domain_main_domain(operation_logger, new_main_domain=None):
# Check domain exists # Check domain exists
if new_main_domain not in domain_list()["domains"]: if new_main_domain not in domain_list()["domains"]:
raise YunohostError("domain_name_unknown", domain=new_main_domain) raise YunohostValidationError("domain_name_unknown", domain=new_main_domain)
operation_logger.related_to.append(("domain", new_main_domain)) operation_logger.related_to.append(("domain", new_main_domain))
operation_logger.start() operation_logger.start()

View file

@ -36,10 +36,11 @@ from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, read_file from moulinette.utils.filesystem import write_to_file, read_file
from moulinette.utils.network import download_json from moulinette.utils.network import download_json
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.domain import _get_maindomain, _build_dns_conf from yunohost.domain import _get_maindomain, _build_dns_conf
from yunohost.utils.network import get_public_ip, dig from yunohost.utils.network import get_public_ip, dig
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
from yunohost.regenconf import regen_conf
logger = getActionLogger("yunohost.dyndns") logger = getActionLogger("yunohost.dyndns")
@ -121,10 +122,9 @@ def dyndns_subscribe(
subscribe_host -- Dynette HTTP API to subscribe to subscribe_host -- Dynette HTTP API to subscribe to
""" """
if len(glob.glob("/etc/yunohost/dyndns/*.key")) != 0 or os.path.exists(
"/etc/cron.d/yunohost-dyndns" if _guess_current_dyndns_domain(subscribe_host) != (None, None):
): raise YunohostValidationError('domain_dyndns_already_subscribed')
raise YunohostError("domain_dyndns_already_subscribed")
if domain is None: if domain is None:
domain = _get_maindomain() domain = _get_maindomain()
@ -132,13 +132,13 @@ def dyndns_subscribe(
# Verify if domain is provided by subscribe_host # Verify if domain is provided by subscribe_host
if not _dyndns_provides(subscribe_host, domain): if not _dyndns_provides(subscribe_host, domain):
raise YunohostError( raise YunohostValidationError(
"dyndns_domain_not_provided", domain=domain, provider=subscribe_host "dyndns_domain_not_provided", domain=domain, provider=subscribe_host
) )
# Verify if domain is available # Verify if domain is available
if not _dyndns_available(subscribe_host, domain): if not _dyndns_available(subscribe_host, domain):
raise YunohostError("dyndns_unavailable", domain=domain) raise YunohostValidationError("dyndns_unavailable", domain=domain)
operation_logger.start() operation_logger.start()
@ -169,7 +169,7 @@ def dyndns_subscribe(
try: try:
r = requests.post( r = requests.post(
"https://%s/key/%s?key_algo=hmac-sha512" "https://%s/key/%s?key_algo=hmac-sha512"
% (subscribe_host, base64.b64encode(key)), % (subscribe_host, base64.b64encode(key.encode()).decode()),
data={"subdomain": domain}, data={"subdomain": domain},
timeout=30, timeout=30,
) )
@ -186,9 +186,18 @@ def dyndns_subscribe(
error = 'Server error, code: %s. (Message: "%s")' % (r.status_code, r.text) error = 'Server error, code: %s. (Message: "%s")' % (r.status_code, r.text)
raise YunohostError("dyndns_registration_failed", error=error) raise YunohostError("dyndns_registration_failed", error=error)
logger.success(m18n.n("dyndns_registered")) # Yunohost regen conf will add the dyndns cron job if a private key exists
# in /etc/yunohost/dyndns
regen_conf(["yunohost"])
dyndns_installcron() # Add some dyndns update in 2 and 4 minutes from now such that user should
# not have to wait 10ish minutes for the conf to propagate
cmd = "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\""
# For some reason subprocess doesn't like the redirections so we have to use bash -c explicity...
subprocess.check_call(["bash", "-c", cmd.format(t="2 min")])
subprocess.check_call(["bash", "-c", cmd.format(t="4 min")])
logger.success(m18n.n('dyndns_registered'))
@is_unit_operation() @is_unit_operation()
@ -220,13 +229,17 @@ def dyndns_update(
# If domain is not given, try to guess it from keys available... # If domain is not given, try to guess it from keys available...
if domain is None: if domain is None:
(domain, key) = _guess_current_dyndns_domain(dyn_host) (domain, key) = _guess_current_dyndns_domain(dyn_host)
if domain is None:
raise YunohostValidationError('dyndns_no_domain_registered')
# If key is not given, pick the first file we find with the domain given # If key is not given, pick the first file we find with the domain given
else: else:
if key is None: if key is None:
keys = glob.glob("/etc/yunohost/dyndns/K{0}.+*.private".format(domain)) keys = glob.glob("/etc/yunohost/dyndns/K{0}.+*.private".format(domain))
if not keys: if not keys:
raise YunohostError("dyndns_key_not_found") raise YunohostValidationError("dyndns_key_not_found")
key = keys[0] key = keys[0]
@ -361,29 +374,11 @@ def dyndns_update(
def dyndns_installcron(): def dyndns_installcron():
""" logger.warning("This command is deprecated. The dyndns cron job should automatically be added/removed by the regenconf depending if there's a private key in /etc/yunohost/dyndns. You can run the regenconf yourself with 'yunohost tools regen-conf yunohost'.")
Install IP update cron
"""
with open("/etc/cron.d/yunohost-dyndns", "w+") as f:
f.write("*/2 * * * * root yunohost dyndns update >> /dev/null\n")
logger.success(m18n.n("dyndns_cron_installed"))
def dyndns_removecron(): def dyndns_removecron():
""" logger.warning("This command is deprecated. The dyndns cron job should automatically be added/removed by the regenconf depending if there's a private key in /etc/yunohost/dyndns. You can run the regenconf yourself with 'yunohost tools regen-conf yunohost'.")
Remove IP update cron
"""
try:
os.remove("/etc/cron.d/yunohost-dyndns")
except Exception as e:
raise YunohostError("dyndns_cron_remove_failed", error=e)
logger.success(m18n.n("dyndns_cron_removed"))
def _guess_current_dyndns_domain(dyn_host): def _guess_current_dyndns_domain(dyn_host):
@ -414,4 +409,4 @@ def _guess_current_dyndns_domain(dyn_host):
else: else:
return (_domain, path) return (_domain, path)
raise YunohostError("dyndns_no_domain_registered") return (None, None)

View file

@ -28,7 +28,7 @@ import yaml
import miniupnpc import miniupnpc
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import process from moulinette.utils import process
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.text import prependlines from moulinette.utils.text import prependlines
@ -366,7 +366,7 @@ def firewall_upnp(action="status", no_refresh=False):
if action == "status": if action == "status":
no_refresh = True no_refresh = True
else: else:
raise YunohostError("action_invalid", action=action) raise YunohostValidationError("action_invalid", action=action)
# Refresh port mapping using UPnP # Refresh port mapping using UPnP
if not no_refresh: if not no_refresh:

View file

@ -32,7 +32,7 @@ from glob import iglob
from importlib import import_module from importlib import import_module
from moulinette import m18n, msettings from moulinette import m18n, msettings
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import log from moulinette.utils import log
from moulinette.utils.filesystem import read_json from moulinette.utils.filesystem import read_json
@ -117,7 +117,7 @@ def hook_info(action, name):
) )
if not hooks: if not hooks:
raise YunohostError("hook_name_unknown", name=name) raise YunohostValidationError("hook_name_unknown", name=name)
return { return {
"action": action, "action": action,
"name": name, "name": name,
@ -186,7 +186,7 @@ def hook_list(action, list_by="name", show_info=False):
d.add(name) d.add(name)
else: else:
raise YunohostError("hook_list_by_invalid") raise YunohostValidationError("hook_list_by_invalid")
def _append_folder(d, folder): def _append_folder(d, folder):
# Iterate over and add hook from a folder # Iterate over and add hook from a folder
@ -273,7 +273,7 @@ def hook_callback(
try: try:
hl = hooks_names[n] hl = hooks_names[n]
except KeyError: except KeyError:
raise YunohostError("hook_name_unknown", n) raise YunohostValidationError("hook_name_unknown", n)
# Iterate over hooks with this name # Iterate over hooks with this name
for h in hl: for h in hl:
# Update hooks dict # Update hooks dict

View file

@ -35,7 +35,7 @@ from logging import FileHandler, getLogger, Formatter
from moulinette import m18n, msettings from moulinette import m18n, msettings
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.packages import get_ynh_package_version from yunohost.utils.packages import get_ynh_package_version
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, read_yaml from moulinette.utils.filesystem import read_file, read_yaml
@ -191,7 +191,7 @@ def log_show(
log_path = base_path + LOG_FILE_EXT log_path = base_path + LOG_FILE_EXT
if not os.path.exists(md_path) and not os.path.exists(log_path): if not os.path.exists(md_path) and not os.path.exists(log_path):
raise YunohostError("log_does_exists", log=path) raise YunohostValidationError("log_does_exists", log=path)
infos = {} infos = {}
@ -399,6 +399,10 @@ class RedactingFormatter(Formatter):
msg = super(RedactingFormatter, self).format(record) msg = super(RedactingFormatter, self).format(record)
self.identify_data_to_redact(msg) self.identify_data_to_redact(msg)
for data in self.data_to_redact: for data in self.data_to_redact:
# we check that data is not empty string,
# otherwise this may lead to super epic stuff
# (try to run "foo".replace("", "bar"))
if data:
msg = msg.replace(data, "**********") msg = msg.replace(data, "**********")
return msg return msg
@ -410,14 +414,8 @@ class RedactingFormatter(Formatter):
# This matches stuff like db_pwd=the_secret or admin_password=other_secret # This matches stuff like db_pwd=the_secret or admin_password=other_secret
# (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=") # (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=")
# Some names like "key" or "manifest_key" are ignored, used in helpers like ynh_app_setting_set or ynh_read_manifest # Some names like "key" or "manifest_key" are ignored, used in helpers like ynh_app_setting_set or ynh_read_manifest
match = re.search( match = re.search(r'(pwd|pass|password|secret\w*|\w+key|token)=(\S{3,})$', record.strip())
r"(pwd|pass|password|secret|\w+key|token)=(\S{3,})$", record.strip() if match and match.group(2) not in self.data_to_redact and match.group(1) not in ["key", "manifest_key"]:
)
if (
match
and match.group(2) not in self.data_to_redact
and match.group(1) not in ["key", "manifest_key"]
):
self.data_to_redact.append(match.group(2)) self.data_to_redact.append(match.group(2))
except Exception as e: except Exception as e:
logger.warning( logger.warning(
@ -633,10 +631,19 @@ class OperationLogger(object):
""" """
Close properly the unit operation Close properly the unit operation
""" """
# When the error happen's in the is_unit_operation try/except,
# we want to inject the log ref in the exception, such that it may be
# transmitted to the webadmin which can then redirect to the appropriate
# log page
if isinstance(error, Exception) and not isinstance(error, YunohostValidationError):
error.log_ref = self.name
if self.ended_at is not None or self.started_at is None: if self.ended_at is not None or self.started_at is None:
return return
if error is not None and not isinstance(error, str): if error is not None and not isinstance(error, str):
error = str(error) error = str(error)
self.ended_at = datetime.utcnow() self.ended_at = datetime.utcnow()
self._error = error self._error = error
self._success = error is None self._success = error is None

View file

@ -31,7 +31,7 @@ import random
from moulinette import m18n from moulinette import m18n
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
logger = getActionLogger("yunohost.user") logger = getActionLogger("yunohost.user")
@ -175,14 +175,14 @@ def user_permission_update(
# Refuse to add "visitors" to mail, xmpp ... they require an account to make sense. # Refuse to add "visitors" to mail, xmpp ... they require an account to make sense.
if add and "visitors" in add and permission.split(".")[0] in SYSTEM_PERMS: if add and "visitors" in add and permission.split(".")[0] in SYSTEM_PERMS:
raise YunohostError("permission_require_account", permission=permission) raise YunohostValidationError("permission_require_account", permission=permission)
# Refuse to add "visitors" to protected permission # Refuse to add "visitors" to protected permission
if ( if (
(add and "visitors" in add and existing_permission["protected"]) (add and "visitors" in add and existing_permission["protected"])
or (remove and "visitors" in remove and existing_permission["protected"]) or (remove and "visitors" in remove and existing_permission["protected"])
) and not force: ) and not force:
raise YunohostError("permission_protected", permission=permission) raise YunohostValidationError("permission_protected", permission=permission)
# Fetch currently allowed groups for this permission # Fetch currently allowed groups for this permission
@ -198,7 +198,7 @@ def user_permission_update(
groups_to_add = [add] if not isinstance(add, list) else add groups_to_add = [add] if not isinstance(add, list) else add
for group in groups_to_add: for group in groups_to_add:
if group not in all_existing_groups: if group not in all_existing_groups:
raise YunohostError("group_unknown", group=group) raise YunohostValidationError("group_unknown", group=group)
if group in current_allowed_groups: if group in current_allowed_groups:
logger.warning( logger.warning(
m18n.n( m18n.n(
@ -326,7 +326,7 @@ def user_permission_info(permission):
permission, None permission, None
) )
if existing_permission is None: if existing_permission is None:
raise YunohostError("permission_not_found", permission=permission) raise YunohostValidationError("permission_not_found", permission=permission)
return existing_permission return existing_permission
@ -391,7 +391,7 @@ def permission_create(
if ldap.get_conflict( if ldap.get_conflict(
{"cn": permission}, base_dn="ou=permission,dc=yunohost,dc=org" {"cn": permission}, base_dn="ou=permission,dc=yunohost,dc=org"
): ):
raise YunohostError("permission_already_exist", permission=permission) raise YunohostValidationError("permission_already_exist", permission=permission)
# Get random GID # Get random GID
all_gid = {x.gr_gid for x in grp.getgrall()} all_gid = {x.gr_gid for x in grp.getgrall()}
@ -427,7 +427,7 @@ def permission_create(
all_existing_groups = user_group_list()["groups"].keys() all_existing_groups = user_group_list()["groups"].keys()
for group in allowed or []: for group in allowed or []:
if group not in all_existing_groups: if group not in all_existing_groups:
raise YunohostError("group_unknown", group=group) raise YunohostValidationError("group_unknown", group=group)
operation_logger.related_to.append(("app", permission.split(".")[0])) operation_logger.related_to.append(("app", permission.split(".")[0]))
operation_logger.start() operation_logger.start()
@ -594,7 +594,7 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True)
permission = permission + ".main" permission = permission + ".main"
if permission.endswith(".main") and not force: if permission.endswith(".main") and not force:
raise YunohostError("permission_cannot_remove_main") raise YunohostValidationError("permission_cannot_remove_main")
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
@ -861,7 +861,7 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
try: try:
re.compile(regex) re.compile(regex)
except Exception: except Exception:
raise YunohostError("invalid_regex", regex=regex) raise YunohostValidationError("invalid_regex", regex=regex)
if url.startswith("re:"): if url.startswith("re:"):
@ -874,12 +874,12 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
# regex with domain # regex with domain
if "/" not in url: if "/" not in url:
raise YunohostError("regex_with_only_domain") raise YunohostValidationError("regex_with_only_domain")
domain, path = url[3:].split("/", 1) domain, path = url[3:].split("/", 1)
path = "/" + path path = "/" + path
if domain.replace("%", "").replace("\\", "") not in domains: if domain.replace("%", "").replace("\\", "") not in domains:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
validate_regex(path) validate_regex(path)
@ -914,7 +914,7 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
sanitized_url = domain + path sanitized_url = domain + path
if domain not in domains: if domain not in domains:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
_assert_no_conflicting_apps(domain, path, ignore_app=app) _assert_no_conflicting_apps(domain, path, ignore_app=app)

View file

@ -34,7 +34,7 @@ from glob import glob
from datetime import datetime from datetime import datetime
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, append_to_file, write_to_file from moulinette.utils.filesystem import read_file, append_to_file, write_to_file
@ -145,7 +145,7 @@ def service_remove(name):
services = _get_services() services = _get_services()
if name not in services: if name not in services:
raise YunohostError("service_unknown", service=name) raise YunohostValidationError("service_unknown", service=name)
del services[name] del services[name]
try: try:
@ -325,7 +325,7 @@ def service_status(names=[]):
# Validate service names requested # Validate service names requested
for name in names: for name in names:
if name not in services.keys(): if name not in services.keys():
raise YunohostError("service_unknown", service=name) raise YunohostValidationError("service_unknown", service=name)
# Filter only requested servivces # Filter only requested servivces
services = {k: v for k, v in services.items() if k in names} services = {k: v for k, v in services.items() if k in names}
@ -397,14 +397,11 @@ def _get_and_format_service_status(service, infos):
# If no description was there, try to get it from the .json locales # If no description was there, try to get it from the .json locales
if not description: if not description:
translation_key = "service_description_%s" % service
description = m18n.n(translation_key)
# If descrption is still equal to the translation key, translation_key = "service_description_%s" % service
# that mean that we don't have a translation for this string if m18n.key_exists(translation_key):
# that's the only way to test for that for now description = m18n.n(translation_key)
# if we don't have it, uses the one provided by systemd else:
if description == translation_key:
description = str(raw_status.get("Description", "")) description = str(raw_status.get("Description", ""))
output = { output = {
@ -487,7 +484,7 @@ def service_log(name, number=50):
number = int(number) number = int(number)
if name not in services.keys(): if name not in services.keys():
raise YunohostError("service_unknown", service=name) raise YunohostValidationError("service_unknown", service=name)
log_list = services[name].get("log", []) log_list = services[name].get("log", [])
@ -548,7 +545,7 @@ def service_regen_conf(
for name in names: for name in names:
if name not in services.keys(): if name not in services.keys():
raise YunohostError("service_unknown", service=name) raise YunohostValidationError("service_unknown", service=name)
if names is []: if names is []:
names = list(services.keys()) names = list(services.keys())
@ -571,7 +568,7 @@ def _run_service_command(action, service):
""" """
services = _get_services() services = _get_services()
if service not in services.keys(): if service not in services.keys():
raise YunohostError("service_unknown", service=service) raise YunohostValidationError("service_unknown", service=service)
possible_actions = [ possible_actions = [
"start", "start",

View file

@ -6,7 +6,7 @@ from datetime import datetime
from collections import OrderedDict from collections import OrderedDict
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
@ -109,7 +109,7 @@ def settings_get(key, full=False):
settings = _get_settings() settings = _get_settings()
if key not in settings: if key not in settings:
raise YunohostError("global_settings_key_doesnt_exists", settings_key=key) raise YunohostValidationError("global_settings_key_doesnt_exists", settings_key=key)
if full: if full:
return settings[key] return settings[key]
@ -137,7 +137,7 @@ def settings_set(key, value):
settings = _get_settings() settings = _get_settings()
if key not in settings: if key not in settings:
raise YunohostError("global_settings_key_doesnt_exists", settings_key=key) raise YunohostValidationError("global_settings_key_doesnt_exists", settings_key=key)
key_type = settings[key]["type"] key_type = settings[key]["type"]
@ -146,7 +146,7 @@ def settings_set(key, value):
if boolean_value[0]: if boolean_value[0]:
value = boolean_value[1] value = boolean_value[1]
else: else:
raise YunohostError( raise YunohostValidationError(
"global_settings_bad_type_for_setting", "global_settings_bad_type_for_setting",
setting=key, setting=key,
received_type=type(value).__name__, received_type=type(value).__name__,
@ -158,14 +158,14 @@ def settings_set(key, value):
try: try:
value = int(value) value = int(value)
except Exception: except Exception:
raise YunohostError( raise YunohostValidationError(
"global_settings_bad_type_for_setting", "global_settings_bad_type_for_setting",
setting=key, setting=key,
received_type=type(value).__name__, received_type=type(value).__name__,
expected_type=key_type, expected_type=key_type,
) )
else: else:
raise YunohostError( raise YunohostValidationError(
"global_settings_bad_type_for_setting", "global_settings_bad_type_for_setting",
setting=key, setting=key,
received_type=type(value).__name__, received_type=type(value).__name__,
@ -173,7 +173,7 @@ def settings_set(key, value):
) )
elif key_type == "string": elif key_type == "string":
if not isinstance(value, str): if not isinstance(value, str):
raise YunohostError( raise YunohostValidationError(
"global_settings_bad_type_for_setting", "global_settings_bad_type_for_setting",
setting=key, setting=key,
received_type=type(value).__name__, received_type=type(value).__name__,
@ -181,14 +181,14 @@ def settings_set(key, value):
) )
elif key_type == "enum": elif key_type == "enum":
if value not in settings[key]["choices"]: if value not in settings[key]["choices"]:
raise YunohostError( raise YunohostValidationError(
"global_settings_bad_choice_for_enum", "global_settings_bad_choice_for_enum",
setting=key, setting=key,
choice=str(value), choice=str(value),
available_choices=", ".join(settings[key]["choices"]), available_choices=", ".join(settings[key]["choices"]),
) )
else: else:
raise YunohostError( raise YunohostValidationError(
"global_settings_unknown_type", setting=key, unknown_type=key_type "global_settings_unknown_type", setting=key, unknown_type=key_type
) )
@ -214,7 +214,7 @@ def settings_reset(key):
settings = _get_settings() settings = _get_settings()
if key not in settings: if key not in settings:
raise YunohostError("global_settings_key_doesnt_exists", settings_key=key) raise YunohostValidationError("global_settings_key_doesnt_exists", settings_key=key)
settings[key]["value"] = settings[key]["default"] settings[key]["value"] = settings[key]["default"]
_save_settings(settings) _save_settings(settings)
@ -304,7 +304,7 @@ def _get_settings():
) )
unknown_settings[key] = value unknown_settings[key] = value
except Exception as e: except Exception as e:
raise YunohostError("global_settings_cant_open_settings", reason=e) raise YunohostValidationError("global_settings_cant_open_settings", reason=e)
if unknown_settings: if unknown_settings:
try: try:

View file

@ -5,7 +5,7 @@ import os
import pwd import pwd
import subprocess import subprocess
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostValidationError
from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, mkdir from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, mkdir
SSHD_CONFIG_PATH = "/etc/ssh/sshd_config" SSHD_CONFIG_PATH = "/etc/ssh/sshd_config"
@ -21,7 +21,7 @@ def user_ssh_allow(username):
# TODO it would be good to support different kind of shells # TODO it would be good to support different kind of shells
if not _get_user_for_ssh(username): if not _get_user_for_ssh(username):
raise YunohostError("user_unknown", user=username) raise YunohostValidationError("user_unknown", user=username)
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
@ -43,7 +43,7 @@ def user_ssh_disallow(username):
# TODO it would be good to support different kind of shells # TODO it would be good to support different kind of shells
if not _get_user_for_ssh(username): if not _get_user_for_ssh(username):
raise YunohostError("user_unknown", user=username) raise YunohostValidationError("user_unknown", user=username)
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface

View file

@ -55,18 +55,11 @@ def clean():
for folderpath in glob.glob("/var/www/*%s*" % test_app): for folderpath in glob.glob("/var/www/*%s*" % test_app):
shutil.rmtree(folderpath, ignore_errors=True) shutil.rmtree(folderpath, ignore_errors=True)
os.system( os.system("bash -c \"mysql -B 2>/dev/null <<< 'DROP DATABASE %s' \"" % test_app)
"bash -c \"mysql -u root --password=$(cat /etc/yunohost/mysql) 2>/dev/null <<< 'DROP DATABASE %s' \"" os.system("bash -c \"mysql -B 2>/dev/null <<< 'DROP USER %s@localhost'\"" % test_app)
% test_app
)
os.system(
"bash -c \"mysql -u root --password=$(cat /etc/yunohost/mysql) 2>/dev/null <<< 'DROP USER %s@localhost'\""
% test_app
)
os.system( # Reset failed quota for service to avoid running into start-limit rate ?
"systemctl reset-failed nginx" os.system("systemctl reset-failed nginx")
) # Reset failed quota for service to avoid running into start-limit rate ?
os.system("systemctl start nginx") os.system("systemctl start nginx")
# Clean permissions # Clean permissions

View file

@ -51,7 +51,7 @@ from yunohost.utils.packages import (
_list_upgradable_apt_packages, _list_upgradable_apt_packages,
ynh_packages_version, ynh_packages_version,
) )
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.log import is_unit_operation, OperationLogger from yunohost.log import is_unit_operation, OperationLogger
# FIXME this is a duplicate from apps.py # FIXME this is a duplicate from apps.py
@ -156,7 +156,7 @@ def tools_adminpw(new_password, check_strength=True):
# UNIX seems to not like password longer than 127 chars ... # UNIX seems to not like password longer than 127 chars ...
# e.g. SSH login gets broken (or even 'su admin' when entering the password) # e.g. SSH login gets broken (or even 'su admin' when entering the password)
if len(new_password) >= 127: if len(new_password) >= 127:
raise YunohostError("admin_password_too_long") raise YunohostValidationError("admin_password_too_long")
new_hash = _hash_user_password(new_password) new_hash = _hash_user_password(new_password)
@ -285,10 +285,10 @@ def tools_postinstall(
# Do some checks at first # Do some checks at first
if os.path.isfile("/etc/yunohost/installed"): if os.path.isfile("/etc/yunohost/installed"):
raise YunohostError("yunohost_already_installed") raise YunohostValidationError("yunohost_already_installed")
if os.path.isdir("/etc/yunohost/apps") and os.listdir("/etc/yunohost/apps") != []: if os.path.isdir("/etc/yunohost/apps") and os.listdir("/etc/yunohost/apps") != []:
raise YunohostError( raise YunohostValidationError(
"It looks like you're trying to re-postinstall a system that was already working previously ... If you recently had some bug or issues with your installation, please first discuss with the team on how to fix the situation instead of savagely re-running the postinstall ...", "It looks like you're trying to re-postinstall a system that was already working previously ... If you recently had some bug or issues with your installation, please first discuss with the team on how to fix the situation instead of savagely re-running the postinstall ...",
raw_msg=True, raw_msg=True,
) )
@ -301,7 +301,7 @@ def tools_postinstall(
) )
GB = 1024 ** 3 GB = 1024 ** 3
if not force_diskspace and main_space < 10 * GB: if not force_diskspace and main_space < 10 * GB:
raise YunohostError("postinstall_low_rootfsspace") raise YunohostValidationError("postinstall_low_rootfsspace")
# Check password # Check password
if not force_password: if not force_password:
@ -331,14 +331,14 @@ def tools_postinstall(
dyndns = True dyndns = True
# If not, abort the postinstall # If not, abort the postinstall
else: else:
raise YunohostError("dyndns_unavailable", domain=domain) raise YunohostValidationError("dyndns_unavailable", domain=domain)
else: else:
dyndns = False dyndns = False
else: else:
dyndns = False dyndns = False
if os.system("iptables -V >/dev/null 2>/dev/null") != 0: if os.system("iptables -V >/dev/null 2>/dev/null") != 0:
raise YunohostError( raise YunohostValidationError(
"iptables/nftables does not seems to be working on your setup. You may be in a container or your kernel does have the proper modules loaded. Sometimes, rebooting the machine may solve the issue.", "iptables/nftables does not seems to be working on your setup. You may be in a container or your kernel does have the proper modules loaded. Sometimes, rebooting the machine may solve the issue.",
raw_msg=True, raw_msg=True,
) )
@ -530,17 +530,17 @@ def tools_upgrade(
from yunohost.utils import packages from yunohost.utils import packages
if packages.dpkg_is_broken(): if packages.dpkg_is_broken():
raise YunohostError("dpkg_is_broken") raise YunohostValidationError("dpkg_is_broken")
# Check for obvious conflict with other dpkg/apt commands already running in parallel # Check for obvious conflict with other dpkg/apt commands already running in parallel
if not packages.dpkg_lock_available(): if not packages.dpkg_lock_available():
raise YunohostError("dpkg_lock_not_available") raise YunohostValidationError("dpkg_lock_not_available")
if system is not False and apps is not None: if system is not False and apps is not None:
raise YunohostError("tools_upgrade_cant_both") raise YunohostValidationError("tools_upgrade_cant_both")
if system is False and apps is None: if system is False and apps is None:
raise YunohostError("tools_upgrade_at_least_one") raise YunohostValidationError("tools_upgrade_at_least_one")
# #
# Apps # Apps
@ -825,7 +825,7 @@ def tools_migrations_list(pending=False, done=False):
# Check for option conflict # Check for option conflict
if pending and done: if pending and done:
raise YunohostError("migrations_list_conflict_pending_done") raise YunohostValidationError("migrations_list_conflict_pending_done")
# Get all migrations # Get all migrations
migrations = _get_migrations_list() migrations = _get_migrations_list()
@ -875,17 +875,17 @@ def tools_migrations_run(
if m.id == target or m.name == target or m.id.split("_")[0] == target: if m.id == target or m.name == target or m.id.split("_")[0] == target:
return m return m
raise YunohostError("migrations_no_such_migration", id=target) raise YunohostValidationError("migrations_no_such_migration", id=target)
# auto, skip and force are exclusive options # auto, skip and force are exclusive options
if auto + skip + force_rerun > 1: if auto + skip + force_rerun > 1:
raise YunohostError("migrations_exclusive_options") raise YunohostValidationError("migrations_exclusive_options")
# If no target specified # If no target specified
if not targets: if not targets:
# skip, revert or force require explicit targets # skip, revert or force require explicit targets
if skip or force_rerun: if skip or force_rerun:
raise YunohostError("migrations_must_provide_explicit_targets") raise YunohostValidationError("migrations_must_provide_explicit_targets")
# Otherwise, targets are all pending migrations # Otherwise, targets are all pending migrations
targets = [m for m in all_migrations if m.state == "pending"] targets = [m for m in all_migrations if m.state == "pending"]
@ -897,11 +897,11 @@ def tools_migrations_run(
pending = [t.id for t in targets if t.state == "pending"] pending = [t.id for t in targets if t.state == "pending"]
if skip and done: if skip and done:
raise YunohostError("migrations_not_pending_cant_skip", ids=", ".join(done)) raise YunohostValidationError("migrations_not_pending_cant_skip", ids=", ".join(done))
if force_rerun and pending: if force_rerun and pending:
raise YunohostError("migrations_pending_cant_rerun", ids=", ".join(pending)) raise YunohostValidationError("migrations_pending_cant_rerun", ids=", ".join(pending))
if not (skip or force_rerun) and done: if not (skip or force_rerun) and done:
raise YunohostError("migrations_already_ran", ids=", ".join(done)) raise YunohostValidationError("migrations_already_ran", ids=", ".join(done))
# So, is there actually something to do ? # So, is there actually something to do ?
if not targets: if not targets:

View file

@ -37,7 +37,7 @@ from moulinette import msignals, msettings, m18n
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.service import service_status from yunohost.service import service_status
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
@ -125,7 +125,7 @@ def user_create(
# Validate domain used for email address/xmpp account # Validate domain used for email address/xmpp account
if domain is None: if domain is None:
if msettings.get("interface") == "api": if msettings.get("interface") == "api":
raise YunohostError("Invalide usage, specify domain argument") raise YunohostValidationError("Invalid usage, you should specify a domain argument")
else: else:
# On affiche les differents domaines possibles # On affiche les differents domaines possibles
msignals.display(m18n.n("domains_available")) msignals.display(m18n.n("domains_available"))
@ -141,24 +141,24 @@ def user_create(
# Check that the domain exists # Check that the domain exists
if domain not in domain_list()["domains"]: if domain not in domain_list()["domains"]:
raise YunohostError("domain_name_unknown", domain=domain) raise YunohostValidationError("domain_name_unknown", domain=domain)
mail = username + "@" + domain mail = username + "@" + domain
ldap = _get_ldap_interface() ldap = _get_ldap_interface()
if username in user_list()["users"]: if username in user_list()["users"]:
raise YunohostError("user_already_exists", user=username) raise YunohostValidationError("user_already_exists", user=username)
# Validate uniqueness of username and mail in LDAP # Validate uniqueness of username and mail in LDAP
try: try:
ldap.validate_uniqueness({"uid": username, "mail": mail, "cn": username}) ldap.validate_uniqueness({"uid": username, "mail": mail, "cn": username})
except Exception as e: except Exception as e:
raise YunohostError("user_creation_failed", user=username, error=e) raise YunohostValidationError("user_creation_failed", user=username, error=e)
# Validate uniqueness of username in system users # Validate uniqueness of username in system users
all_existing_usernames = {x.pw_name for x in pwd.getpwall()} all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
if username in all_existing_usernames: if username in all_existing_usernames:
raise YunohostError("system_username_exists") raise YunohostValidationError("system_username_exists")
main_domain = _get_maindomain() main_domain = _get_maindomain()
aliases = [ aliases = [
@ -170,7 +170,7 @@ def user_create(
] ]
if mail in aliases: if mail in aliases:
raise YunohostError("mail_unavailable") raise YunohostValidationError("mail_unavailable")
operation_logger.start() operation_logger.start()
@ -264,7 +264,7 @@ def user_delete(operation_logger, username, purge=False):
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
if username not in user_list()["users"]: if username not in user_list()["users"]:
raise YunohostError("user_unknown", user=username) raise YunohostValidationError("user_unknown", user=username)
operation_logger.start() operation_logger.start()
@ -347,7 +347,7 @@ def user_update(
attrs=attrs_to_fetch, attrs=attrs_to_fetch,
) )
if not result: if not result:
raise YunohostError("user_unknown", user=username) raise YunohostValidationError("user_unknown", user=username)
user = result[0] user = result[0]
env_dict = {"YNH_USER_USERNAME": username} env_dict = {"YNH_USER_USERNAME": username}
@ -396,13 +396,13 @@ def user_update(
try: try:
ldap.validate_uniqueness({"mail": mail}) ldap.validate_uniqueness({"mail": mail})
except Exception as e: except Exception as e:
raise YunohostError("user_update_failed", user=username, error=e) raise YunohostValidationError("user_update_failed", user=username, error=e)
if mail[mail.find("@") + 1 :] not in domains: if mail[mail.find("@") + 1 :] not in domains:
raise YunohostError( raise YunohostValidationError(
"mail_domain_unknown", domain=mail[mail.find("@") + 1 :] "mail_domain_unknown", domain=mail[mail.find("@") + 1 :]
) )
if mail in aliases: if mail in aliases:
raise YunohostError("mail_unavailable") raise YunohostValidationError("mail_unavailable")
del user["mail"][0] del user["mail"][0]
new_attr_dict["mail"] = [mail] + user["mail"] new_attr_dict["mail"] = [mail] + user["mail"]
@ -414,9 +414,9 @@ def user_update(
try: try:
ldap.validate_uniqueness({"mail": mail}) ldap.validate_uniqueness({"mail": mail})
except Exception as e: except Exception as e:
raise YunohostError("user_update_failed", user=username, error=e) raise YunohostValidationError("user_update_failed", user=username, error=e)
if mail[mail.find("@") + 1 :] not in domains: if mail[mail.find("@") + 1 :] not in domains:
raise YunohostError( raise YunohostValidationError(
"mail_domain_unknown", domain=mail[mail.find("@") + 1 :] "mail_domain_unknown", domain=mail[mail.find("@") + 1 :]
) )
user["mail"].append(mail) user["mail"].append(mail)
@ -429,7 +429,7 @@ def user_update(
if len(user["mail"]) > 1 and mail in user["mail"][1:]: if len(user["mail"]) > 1 and mail in user["mail"][1:]:
user["mail"].remove(mail) user["mail"].remove(mail)
else: else:
raise YunohostError("mail_alias_remove_failed", mail=mail) raise YunohostValidationError("mail_alias_remove_failed", mail=mail)
new_attr_dict["mail"] = user["mail"] new_attr_dict["mail"] = user["mail"]
if "mail" in new_attr_dict: if "mail" in new_attr_dict:
@ -451,7 +451,7 @@ def user_update(
if len(user["maildrop"]) > 1 and mail in user["maildrop"][1:]: if len(user["maildrop"]) > 1 and mail in user["maildrop"][1:]:
user["maildrop"].remove(mail) user["maildrop"].remove(mail)
else: else:
raise YunohostError("mail_forward_remove_failed", mail=mail) raise YunohostValidationError("mail_forward_remove_failed", mail=mail)
new_attr_dict["maildrop"] = user["maildrop"] new_attr_dict["maildrop"] = user["maildrop"]
if "maildrop" in new_attr_dict: if "maildrop" in new_attr_dict:
@ -500,7 +500,7 @@ def user_info(username):
if result: if result:
user = result[0] user = result[0]
else: else:
raise YunohostError("user_unknown", user=username) raise YunohostValidationError("user_unknown", user=username)
result_dict = { result_dict = {
"username": user["uid"][0], "username": user["uid"][0],
@ -638,7 +638,7 @@ def user_group_create(
{"cn": groupname}, base_dn="ou=groups,dc=yunohost,dc=org" {"cn": groupname}, base_dn="ou=groups,dc=yunohost,dc=org"
) )
if conflict: if conflict:
raise YunohostError("group_already_exist", group=groupname) raise YunohostValidationError("group_already_exist", group=groupname)
# Validate uniqueness of groupname in system group # Validate uniqueness of groupname in system group
all_existing_groupnames = {x.gr_name for x in grp.getgrall()} all_existing_groupnames = {x.gr_name for x in grp.getgrall()}
@ -651,7 +651,7 @@ def user_group_create(
"sed --in-place '/^%s:/d' /etc/group" % groupname, shell=True "sed --in-place '/^%s:/d' /etc/group" % groupname, shell=True
) )
else: else:
raise YunohostError("group_already_exist_on_system", group=groupname) raise YunohostValidationError("group_already_exist_on_system", group=groupname)
if not gid: if not gid:
# Get random GID # Get random GID
@ -705,7 +705,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
existing_groups = list(user_group_list()["groups"].keys()) existing_groups = list(user_group_list()["groups"].keys())
if groupname not in existing_groups: if groupname not in existing_groups:
raise YunohostError("group_unknown", group=groupname) raise YunohostValidationError("group_unknown", group=groupname)
# Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam') # Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam')
# without the force option... # without the force option...
@ -714,7 +714,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
existing_users = list(user_list()["users"].keys()) existing_users = list(user_list()["users"].keys())
undeletable_groups = existing_users + ["all_users", "visitors"] undeletable_groups = existing_users + ["all_users", "visitors"]
if groupname in undeletable_groups and not force: if groupname in undeletable_groups and not force:
raise YunohostError("group_cannot_be_deleted", group=groupname) raise YunohostValidationError("group_cannot_be_deleted", group=groupname)
operation_logger.start() operation_logger.start()
ldap = _get_ldap_interface() ldap = _get_ldap_interface()
@ -756,11 +756,11 @@ def user_group_update(
# We also can't edit "all_users" without the force option because that's a special group... # We also can't edit "all_users" without the force option because that's a special group...
if not force: if not force:
if groupname == "all_users": if groupname == "all_users":
raise YunohostError("group_cannot_edit_all_users") raise YunohostValidationError("group_cannot_edit_all_users")
elif groupname == "visitors": elif groupname == "visitors":
raise YunohostError("group_cannot_edit_visitors") raise YunohostValidationError("group_cannot_edit_visitors")
elif groupname in existing_users: elif groupname in existing_users:
raise YunohostError("group_cannot_edit_primary_group", group=groupname) raise YunohostValidationError("group_cannot_edit_primary_group", group=groupname)
# We extract the uid for each member of the group to keep a simple flat list of members # We extract the uid for each member of the group to keep a simple flat list of members
current_group = user_group_info(groupname)["members"] current_group = user_group_info(groupname)["members"]
@ -771,7 +771,7 @@ def user_group_update(
for user in users_to_add: for user in users_to_add:
if user not in existing_users: if user not in existing_users:
raise YunohostError("user_unknown", user=user) raise YunohostValidationError("user_unknown", user=user)
if user in current_group: if user in current_group:
logger.warning( logger.warning(
@ -843,7 +843,7 @@ def user_group_info(groupname):
) )
if not result: if not result:
raise YunohostError("group_unknown", group=groupname) raise YunohostValidationError("group_unknown", group=groupname)
infos = result[0] infos = result[0]

View file

@ -25,6 +25,8 @@ from moulinette import m18n
class YunohostError(MoulinetteError): class YunohostError(MoulinetteError):
http_code = 500
""" """
Yunohost base exception Yunohost base exception
@ -49,3 +51,8 @@ class YunohostError(MoulinetteError):
return super(YunohostError, self).content() return super(YunohostError, self).content()
else: else:
return {"error": self.strerror, "log_ref": self.log_ref} return {"error": self.strerror, "log_ref": self.log_ref}
class YunohostValidationError(YunohostError):
http_code = 400

View file

@ -13,7 +13,6 @@ from yunohost.app import (
) )
from yunohost.permission import ( from yunohost.permission import (
permission_create, permission_create,
user_permission_list,
user_permission_update, user_permission_update,
permission_sync_to_user, permission_sync_to_user,
) )
@ -280,7 +279,7 @@ def migrate_legacy_permission_settings(app=None):
auth_header=True, auth_header=True,
label=legacy_permission_label(app, "protected"), label=legacy_permission_label(app, "protected"),
show_tile=False, show_tile=False,
allowed=user_permission_list()["permissions"][app + ".main"]["allowed"], allowed=[],
protected=True, protected=True,
sync_perm=False, sync_perm=False,
) )
@ -308,6 +307,9 @@ def translate_legacy_rules_in_ssowant_conf_json_persistent():
if not os.path.exists(persistent_file_name): if not os.path.exists(persistent_file_name):
return return
# Ugly hack because for some reason so many people have tabs in their conf.json.persistent ...
os.system(r"sed -i 's/\t/ /g' /etc/ssowat/conf.json.persistent")
# Ugly hack to try not to misarably fail migration # Ugly hack to try not to misarably fail migration
persistent = read_yaml(persistent_file_name) persistent = read_yaml(persistent_file_name)

View file

@ -90,11 +90,11 @@ class PasswordValidator(object):
# on top (at least not the moulinette ones) # on top (at least not the moulinette ones)
# because the moulinette needs to be correctly initialized # because the moulinette needs to be correctly initialized
# as well as modules available in python's path. # as well as modules available in python's path.
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostValidationError
status, msg = self.validation_summary(password) status, msg = self.validation_summary(password)
if status == "error": if status == "error":
raise YunohostError(msg) raise YunohostValidationError(msg)
def validation_summary(self, password): def validation_summary(self, password):
""" """

View file

@ -25,10 +25,12 @@ def find_expected_string_keys():
# Try to find : # Try to find :
# m18n.n( "foo" # m18n.n( "foo"
# YunohostError("foo" # YunohostError("foo"
# YunohostValidationError("foo"
# # i18n: foo # # i18n: foo
p1 = re.compile(r"m18n\.n\(\n*\s*[\"\'](\w+)[\"\']") p1 = re.compile(r"m18n\.n\(\n*\s*[\"\'](\w+)[\"\']")
p2 = re.compile(r"YunohostError\(\n*\s*[\'\"](\w+)[\'\"]") p2 = re.compile(r"YunohostError\(\n*\s*[\'\"](\w+)[\'\"]")
p3 = re.compile(r"# i18n: [\'\"]?(\w+)[\'\"]?") p3 = re.compile(r"YunohostValidationError\(\n*\s*[\'\"](\w+)[\'\"]")
p4 = re.compile(r"# i18n: [\'\"]?(\w+)[\'\"]?")
python_files = glob.glob("src/yunohost/*.py") python_files = glob.glob("src/yunohost/*.py")
python_files.extend(glob.glob("src/yunohost/utils/*.py")) python_files.extend(glob.glob("src/yunohost/utils/*.py"))
@ -47,6 +49,10 @@ def find_expected_string_keys():
continue continue
yield m yield m
for m in p3.findall(content): for m in p3.findall(content):
if m.endswith("_"):
continue
yield m
for m in p4.findall(content):
yield m yield m
# For each diagnosis, try to find strings like "diagnosis_stuff_foo" (c.f. diagnosis summaries) # For each diagnosis, try to find strings like "diagnosis_stuff_foo" (c.f. diagnosis summaries)