Merge branch 'dev' into less-madness-for-hook-exec

This commit is contained in:
Alexandre Aubin 2021-01-19 23:25:43 +01:00 committed by GitHub
commit 09acc2a466
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 808 additions and 636 deletions

View file

@ -3,14 +3,6 @@
######################################## ########################################
# later we must fix lint and format-check jobs and remove "allow_failure" # later we must fix lint and format-check jobs and remove "allow_failure"
lint27:
stage: lint
image: "before-install"
needs: []
allow_failure: true
script:
- tox -e py27-lint
lint37: lint37:
stage: lint stage: lint
image: "before-install" image: "before-install"
@ -19,17 +11,9 @@ lint37:
script: script:
- tox -e py37-lint - tox -e py37-lint
invalidcode27:
stage: lint
image: "before-install"
needs: []
script:
- tox -e py27-invalidcode
invalidcode37: invalidcode37:
stage: lint stage: lint
image: "before-install" image: "before-install"
allow_failure: true
needs: [] needs: []
script: script:
- tox -e py37-invalidcode - tox -e py37-invalidcode

View file

@ -36,7 +36,7 @@ full-tests:
- *install_debs - *install_debs
- yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns - yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns
script: script:
- python -m pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml - python3 -m pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml
needs: needs:
- job: build-yunohost - job: build-yunohost
artifacts: true artifacts: true
@ -51,70 +51,70 @@ full-tests:
root-tests: root-tests:
extends: .test-stage extends: .test-stage
script: script:
- python -m pytest tests - python3 -m pytest tests
test-apps: test-apps:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_apps.py - python3 -m pytest tests/test_apps.py
test-appscatalog: test-appscatalog:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_appscatalog.py - python3 -m pytest tests/test_appscatalog.py
test-appurl: test-appurl:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_appurl.py - python3 -m pytest tests/test_appurl.py
test-apps-arguments-parsing: test-apps-arguments-parsing:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_apps_arguments_parsing.py - python3 -m pytest tests/test_apps_arguments_parsing.py
test-backuprestore: test-backuprestore:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_backuprestore.py - python3 -m pytest tests/test_backuprestore.py
test-changeurl: test-changeurl:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_changeurl.py - python3 -m pytest tests/test_changeurl.py
test-permission: test-permission:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_permission.py - python3 -m pytest tests/test_permission.py
test-settings: test-settings:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_settings.py - python3 -m pytest tests/test_settings.py
test-user-group: test-user-group:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_user-group.py - python3 -m pytest tests/test_user-group.py
test-regenconf: test-regenconf:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_regenconf.py - python3 -m pytest tests/test_regenconf.py
test-service: test-service:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- python -m pytest tests/test_service.py - python3 -m pytest tests/test_service.py

View file

@ -1,4 +1,4 @@
#! /usr/bin/python #! /usr/bin/python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os

View file

@ -1,4 +1,4 @@
#! /usr/bin/python #! /usr/bin/python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys

View file

@ -165,8 +165,11 @@ user:
full: --change-password full: --change-password
help: New password to set help: New password to set
metavar: PASSWORD metavar: PASSWORD
nargs: "?"
const: 0
extra: extra:
pattern: *pattern_password pattern: *pattern_password
comment: good_practices_about_user_password
--add-mailforward: --add-mailforward:
help: Mailforward addresses to add help: Mailforward addresses to add
nargs: "*" nargs: "*"

View file

@ -550,7 +550,7 @@ ynh_pin_repo () {
fi fi
# Sury pinning is managed by the regenconf in the core... # Sury pinning is managed by the regenconf in the core...
[[ "$name" != "extra_php_version" ]] || return [[ "$name" != "extra_php_version" ]] || return 0
mkdir --parents "/etc/apt/preferences.d" mkdir --parents "/etc/apt/preferences.d"
echo "Package: $package echo "Package: $package

View file

@ -35,7 +35,7 @@ ynh_print_info() {
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
echo "$message" >> "$YNH_STDINFO" echo "$message" >&$YNH_STDINFO
} }
# Ignore the yunohost-cli log to prevent errors with conditional commands # Ignore the yunohost-cli log to prevent errors with conditional commands

406
data/helpers.d/permission Normal file
View file

@ -0,0 +1,406 @@
#!/bin/bash
# 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 \
# --label="My app admin" --show_tile=true
#
# 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'.
# - 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 \
# --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.
# In case of an API with need to be always public it avoid that the admin break anything.
# With this permission all client will be allowed to access to the url 'domain.tld/api'.
# Note that in this case no tile will be show on the SSO.
# Note that the auth_header parameter is to 'false'. So no authentication header will be passed to the application.
# Generally the API is requested by an application and enabling the auth_header has no advantage and could bring some issues in some case.
# So in this case it's better to disable this option for all API.
#
#
# 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]
# [--protected=true|false]
# | 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.
# | 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: -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: -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: -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
# start with '/'. For example:
# / -> domain.tld/app
# /admin -> domain.tld/app/admin
# domain.tld/app/api -> domain.tld/app/api
#
# 'url' or 'additional_urls' can be treated as a PCRE (not lua) regex if it starts with "re:".
# For example:
# re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
# re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
#
# Note that globally the parameter 'url' and 'additional_urls' are same. The only difference is:
# - 'url' is only one url, 'additional_urls' can be a list of urls. There are no limitation of 'additional_urls'
# - 'url' is used for the url of tile in the SSO (if enabled with the 'show_tile' parameter)
#
#
# About the authentication header (auth_header parameter).
# The SSO pass (by default) to the application theses following HTTP header (linked to the authenticated user) to the application:
# - "Auth-User": username
# - "Remote-User": username
# - "Email": user email
#
# Generally this feature is usefull to authenticate automatically the user in the application but in some case the application don't work with theses header and theses header need to be disabled to have the application to work correctly.
# See https://github.com/YunoHost/issues/issues/1420 for more informations
#
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_create() {
# Declare an array to define the options of this helper.
local legacy_args=puAhaltP
local -A args_array=( [p]=permission= [u]=url= [A]=additional_urls= [h]=auth_header= [a]=allowed= [l]=label= [t]=show_tile= [P]=protected= )
local permission
local url
local additional_urls
local auth_header
local allowed
local label
local show_tile
local protected
ynh_handle_getopts_args "$@"
url=${url:-}
additional_urls=${additional_urls:-}
auth_header=${auth_header:-}
allowed=${allowed:-}
label=${label:-}
show_tile=${show_tile:-}
protected=${protected:-}
if [[ -n $url ]]
then
url=",url='$url'"
fi
if [[ -n $additional_urls ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# By example:
# --additional_urls /urlA /urlB
# will be:
# additional_urls=['/urlA', '/urlB']
additional_urls=",additional_urls=['${additional_urls//;/\',\'}']"
fi
if [[ -n $auth_header ]]
then
if [ $auth_header == "true" ]
then
auth_header=",auth_header=True"
else
auth_header=",auth_header=False"
fi
fi
if [[ -n $allowed ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# By example:
# --allowed alice bob
# will be:
# allowed=['alice', 'bob']
allowed=",allowed=['${allowed//;/\',\'}']"
fi
if [[ -n ${label:-} ]]; then
label=",label='$label'"
else
label=",label='$permission'"
fi
if [[ -n ${show_tile:-} ]]
then
if [ $show_tile == "true" ]
then
show_tile=",show_tile=True"
else
show_tile=",show_tile=False"
fi
fi
if [[ -n ${protected:-} ]]
then
if [ $protected == "true" ]
then
protected=",protected=True"
else
protected=",protected=False"
fi
fi
yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission' $url $additional_urls $auth_header $allowed $label $show_tile $protected)"
}
# Remove a permission for the app (note that when the app is removed all permission is automatically removed)
#
# example: ynh_permission_delete --permission=editors
#
# usage: ynh_permission_delete --permission="permission"
# | arg: -p, --permission= - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_delete() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=permission= )
local permission
ynh_handle_getopts_args "$@"
yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission')"
}
# Check if a permission exists
#
# usage: ynh_permission_exists --permission=permission
# | arg: -p, --permission= - the permission to check
# | exit: Return 1 if the permission doesn't exist, 0 otherwise
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_exists() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=permission= )
local permission
ynh_handle_getopts_args "$@"
yunohost user permission list --short | grep --word-regexp --quiet "$app.$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" ]]
# [--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: -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 ("").
# | 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: -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)
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_url() {
# Declare an array to define the options of this helper.
local legacy_args=puarhc
local -A args_array=( [p]=permission= [u]=url= [a]=add_url= [r]=remove_url= [h]=auth_header= [c]=clear_urls )
local permission
local url
local add_url
local remove_url
local auth_header
local clear_urls
ynh_handle_getopts_args "$@"
url=${url:-}
add_url=${add_url:-}
remove_url=${remove_url:-}
auth_header=${auth_header:-}
clear_urls=${clear_urls:-}
if [[ -n $url ]]
then
url=",url='$url'"
fi
if [[ -n $add_url ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --add_url /urlA /urlB
# will be:
# add_url=['/urlA', '/urlB']
add_url=",add_url=['${add_url//;/\',\'}']"
fi
if [[ -n $remove_url ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --remove_url /urlA /urlB
# will be:
# remove_url=['/urlA', '/urlB']
remove_url=",remove_url=['${remove_url//;/\',\'}']"
fi
if [[ -n $auth_header ]]
then
if [ $auth_header == "true" ]
then
auth_header=",auth_header=True"
else
auth_header=",auth_header=False"
fi
fi
if [[ -n $clear_urls ]] && [ $clear_urls -eq 1 ]
then
clear_urls=",clear_urls=True"
fi
yunohost tools shell -c "from yunohost.permission import permission_url; permission_url('$app.$permission' $url $add_url $remove_url $auth_header $clear_urls)"
}
# Update a permission for the app
#
# usage: ynh_permission_update --permission "permission" [--add="group" ["group" ...]] [--remove="group" ["group" ...]]
# [--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: -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: -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: -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.
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_update() {
# Declare an array to define the options of this helper.
local legacy_args=parltP
local -A args_array=( [p]=permission= [a]=add= [r]=remove= [l]=label= [t]=show_tile= [P]=protected= )
local permission
local add
local remove
local label
local show_tile
local protected
ynh_handle_getopts_args "$@"
add=${add:-}
remove=${remove:-}
label=${label:-}
show_tile=${show_tile:-}
protected=${protected:-}
if [[ -n $add ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --add alice bob
# will be:
# add=['alice', 'bob']
add=",add=['${add//';'/"','"}']"
fi
if [[ -n $remove ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --remove alice bob
# will be:
# remove=['alice', 'bob']
remove=",remove=['${remove//';'/"','"}']"
fi
if [[ -n $label ]]
then
label=",label='$label'"
fi
if [[ -n $show_tile ]]
then
if [ $show_tile == "true" ]
then
show_tile=",show_tile=True"
else
show_tile=",show_tile=False"
fi
fi
if [[ -n $protected ]]; then
if [ $protected == "true" ]
then
protected=",protected=True"
else
protected=",protected=False"
fi
fi
yunohost tools shell -c "from yunohost.permission import user_permission_update; user_permission_update('$app.$permission' $add $remove $label $show_tile $protected , force=True)"
}
# Check if a permission has an user
#
# example: ynh_permission_has_user --permission=main --user=visitors
#
# usage: ynh_permission_has_user --permission=permission --user=user
# | arg: -p, --permission= - the permission to check
# | arg: -u, --user= - the user seek in the permission
# | exit: Return 1 if the permission doesn't have that user or doesn't exist, 0 otherwise
#
# Requires YunoHost version 3.7.1 or higher.
ynh_permission_has_user() {
local legacy_args=pu
# Declare an array to define the options of this helper.
local -A args_array=( [p]=permission= [u]=user= )
local permission
local user
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! ynh_permission_exists --permission=$permission
then
return 1
fi
yunohost user permission info "$app.$permission" | grep --word-regexp --quiet "$user"
}
# Check if a legacy permissions exist
#
# usage: ynh_legacy_permissions_exists
# | exit: Return 1 if the permission doesn't exist, 0 otherwise
#
# Requires YunoHost version 4.1.2 or higher.
ynh_legacy_permissions_exists () {
for permission in "skipped" "unprotected" "protected"
do
if ynh_permission_exists --permission="legacy_${permission}_uris"; then
return 0
fi
done
return 1
}
# Remove all legacy permissions
#
# usage: ynh_legacy_permissions_delete_all
#
# example:
# if ynh_legacy_permissions_exists
# then
# ynh_legacy_permissions_delete_all
# # You can recreate the required permissions here with ynh_permission_create
# fi
# Requires YunoHost version 4.1.2 or higher.
ynh_legacy_permissions_delete_all () {
for permission in "skipped" "unprotected" "protected"
do
if ynh_permission_exists --permission="legacy_${permission}_uris"; then
ynh_permission_delete --permission="legacy_${permission}_uris"
fi
done
}

View file

@ -367,7 +367,7 @@ ynh_install_php () {
fi fi
# Add an extra repository for those packages # Add an extra repository for those packages
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --priority=995 --name=extra_php_version --priority=600 ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
# Install requested dependencies from this extra repository. # Install requested dependencies from this extra repository.
# Install PHP-FPM first, otherwise PHP will install apache as a dependency. # Install PHP-FPM first, otherwise PHP will install apache as a dependency.

View file

@ -77,8 +77,9 @@ ynh_app_setting_delete() {
# [internal] # [internal]
# #
ynh_app_setting() ynh_app_setting()
{ {
ACTION="$1" APP="$2" KEY="$3" VALUE="${4:-}" python2.7 - <<EOF set +o xtrace # set +x
ACTION="$1" APP="$2" KEY="$3" VALUE="${4:-}" python3 - <<EOF
import os, yaml, sys import os, yaml, sys
app, action = os.environ['APP'], os.environ['ACTION'].lower() app, action = os.environ['APP'], os.environ['ACTION'].lower()
key, value = os.environ['KEY'], os.environ.get('VALUE', None) key, value = os.environ['KEY'], os.environ.get('VALUE', None)
@ -102,6 +103,7 @@ else:
with open(setting_file, "w") as f: with open(setting_file, "w") as f:
yaml.safe_dump(settings, f, default_flow_style=False) yaml.safe_dump(settings, f, default_flow_style=False)
EOF EOF
set -o xtrace # set -x
} }
# Check availability of a web path # Check availability of a web path
@ -147,372 +149,3 @@ ynh_webpath_register () {
yunohost app register-url $app $domain $path_url yunohost app register-url $app $domain $path_url
} }
# 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 \
# --label="My app admin" --show_tile=true
#
# 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'.
# - 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 \
# --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.
# In case of an API with need to be always public it avoid that the admin break anything.
# With this permission all client will be allowed to access to the url 'domain.tld/api'.
# Note that in this case no tile will be show on the SSO.
# Note that the auth_header parameter is to 'false'. So no authentication header will be passed to the application.
# Generally the API is requested by an application and enabling the auth_header has no advantage and could bring some issues in some case.
# So in this case it's better to disable this option for all API.
#
#
# 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]
# [--protected=true|false]
# | 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.
# | 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: -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: -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: -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
# start with '/'. For example:
# / -> domain.tld/app
# /admin -> domain.tld/app/admin
# domain.tld/app/api -> domain.tld/app/api
#
# 'url' or 'additional_urls' can be treated as a PCRE (not lua) regex if it starts with "re:".
# For example:
# re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
# re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
#
# Note that globally the parameter 'url' and 'additional_urls' are same. The only difference is:
# - 'url' is only one url, 'additional_urls' can be a list of urls. There are no limitation of 'additional_urls'
# - 'url' is used for the url of tile in the SSO (if enabled with the 'show_tile' parameter)
#
#
# About the authentication header (auth_header parameter).
# The SSO pass (by default) to the application theses following HTTP header (linked to the authenticated user) to the application:
# - "Auth-User": username
# - "Remote-User": username
# - "Email": user email
#
# Generally this feature is usefull to authenticate automatically the user in the application but in some case the application don't work with theses header and theses header need to be disabled to have the application to work correctly.
# See https://github.com/YunoHost/issues/issues/1420 for more informations
#
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_create() {
# Declare an array to define the options of this helper.
local legacy_args=puAhaltP
local -A args_array=( [p]=permission= [u]=url= [A]=additional_urls= [h]=auth_header= [a]=allowed= [l]=label= [t]=show_tile= [P]=protected= )
local permission
local url
local additional_urls
local auth_header
local allowed
local label
local show_tile
local protected
ynh_handle_getopts_args "$@"
url=${url:-}
additional_urls=${additional_urls:-}
auth_header=${auth_header:-}
allowed=${allowed:-}
label=${label:-}
show_tile=${show_tile:-}
protected=${protected:-}
if [[ -n $url ]]
then
url=",url='$url'"
fi
if [[ -n $additional_urls ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# By example:
# --additional_urls /urlA /urlB
# will be:
# additional_urls=['/urlA', '/urlB']
additional_urls=",additional_urls=['${additional_urls//;/\',\'}']"
fi
if [[ -n $auth_header ]]
then
if [ $auth_header == "true" ]
then
auth_header=",auth_header=True"
else
auth_header=",auth_header=False"
fi
fi
if [[ -n $allowed ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# By example:
# --allowed alice bob
# will be:
# allowed=['alice', 'bob']
allowed=",allowed=['${allowed//;/\',\'}']"
fi
if [[ -n ${label:-} ]]; then
label=",label='$label'"
else
label=",label='$permission'"
fi
if [[ -n ${show_tile:-} ]]
then
if [ $show_tile == "true" ]
then
show_tile=",show_tile=True"
else
show_tile=",show_tile=False"
fi
fi
if [[ -n ${protected:-} ]]
then
if [ $protected == "true" ]
then
protected=",protected=True"
else
protected=",protected=False"
fi
fi
yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission' $url $additional_urls $auth_header $allowed $label $show_tile $protected)"
}
# Remove a permission for the app (note that when the app is removed all permission is automatically removed)
#
# example: ynh_permission_delete --permission=editors
#
# usage: ynh_permission_delete --permission="permission"
# | arg: -p, --permission= - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_delete() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=permission= )
local permission
ynh_handle_getopts_args "$@"
yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission')"
}
# Check if a permission exists
#
# usage: ynh_permission_exists --permission=permission
# | arg: -p, --permission= - the permission to check
# | exit: Return 1 if the permission doesn't exist, 0 otherwise
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_exists() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=permission= )
local permission
ynh_handle_getopts_args "$@"
yunohost user permission list --short | grep --word-regexp --quiet "$app.$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" ]]
# [--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: -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 ("").
# | 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: -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)
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_url() {
# Declare an array to define the options of this helper.
local legacy_args=puarhc
local -A args_array=( [p]=permission= [u]=url= [a]=add_url= [r]=remove_url= [h]=auth_header= [c]=clear_urls )
local permission
local url
local add_url
local remove_url
local auth_header
local clear_urls
ynh_handle_getopts_args "$@"
url=${url:-}
add_url=${add_url:-}
remove_url=${remove_url:-}
auth_header=${auth_header:-}
clear_urls=${clear_urls:-}
if [[ -n $url ]]
then
url=",url='$url'"
fi
if [[ -n $add_url ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --add_url /urlA /urlB
# will be:
# add_url=['/urlA', '/urlB']
add_url=",add_url=['${add_url//;/\',\'}']"
fi
if [[ -n $remove_url ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --remove_url /urlA /urlB
# will be:
# remove_url=['/urlA', '/urlB']
remove_url=",remove_url=['${remove_url//;/\',\'}']"
fi
if [[ -n $auth_header ]]
then
if [ $auth_header == "true" ]
then
auth_header=",auth_header=True"
else
auth_header=",auth_header=False"
fi
fi
if [[ -n $clear_urls ]] && [ $clear_urls -eq 1 ]
then
clear_urls=",clear_urls=True"
fi
yunohost tools shell -c "from yunohost.permission import permission_url; permission_url('$app.$permission' $url $add_url $remove_url $auth_header $clear_urls)"
}
# Update a permission for the app
#
# usage: ynh_permission_update --permission "permission" [--add="group" ["group" ...]] [--remove="group" ["group" ...]]
# [--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: -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: -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: -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.
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_update() {
# Declare an array to define the options of this helper.
local legacy_args=parltP
local -A args_array=( [p]=permission= [a]=add= [r]=remove= [l]=label= [t]=show_tile= [P]=protected= )
local permission
local add
local remove
local label
local show_tile
local protected
ynh_handle_getopts_args "$@"
add=${add:-}
remove=${remove:-}
label=${label:-}
show_tile=${show_tile:-}
protected=${protected:-}
if [[ -n $add ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --add alice bob
# will be:
# add=['alice', 'bob']
add=",add=['${add//';'/"','"}']"
fi
if [[ -n $remove ]]
then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
# --remove alice bob
# will be:
# remove=['alice', 'bob']
remove=",remove=['${remove//';'/"','"}']"
fi
if [[ -n $label ]]
then
label=",label='$label'"
fi
if [[ -n $show_tile ]]
then
if [ $show_tile == "true" ]
then
show_tile=",show_tile=True"
else
show_tile=",show_tile=False"
fi
fi
if [[ -n $protected ]]; then
if [ $protected == "true" ]
then
protected=",protected=True"
else
protected=",protected=False"
fi
fi
yunohost tools shell -c "from yunohost.permission import user_permission_update; user_permission_update('$app.$permission' $add $remove $label $show_tile $protected , force=True)"
}
# Check if a permission has an user
#
# example: ynh_permission_has_user --permission=main --user=visitors
#
# usage: ynh_permission_has_user --permission=permission --user=user
# | arg: -p, --permission= - the permission to check
# | arg: -u, --user= - the user seek in the permission
# | exit: Return 1 if the permission doesn't have that user or doesn't exist, 0 otherwise
#
# Requires YunoHost version 3.7.1 or higher.
ynh_permission_has_user() {
local legacy_args=pu
# Declare an array to define the options of this helper.
local -A args_array=( [p]=permission= [u]=user= )
local permission
local user
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! ynh_permission_exists --permission=$permission
then
return 1
fi
yunohost user permission info "$app.$permission" | grep --word-regexp --quiet "$user"
}

View file

@ -393,7 +393,8 @@ ynh_replace_vars () {
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
test -n "${!one_var:-}" || ynh_die --message="\$$one_var wasn't initialized when trying to replace __${one_var^^}__ in $file" # Explanation for the weird '+x' syntax: https://stackoverflow.com/a/13864829
test -n "${one_var+x}" || 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^^}__"
@ -421,7 +422,7 @@ ynh_render_template() {
local output_path=$2 local output_path=$2
mkdir -p "$(dirname $output_path)" mkdir -p "$(dirname $output_path)"
# Taken from https://stackoverflow.com/a/35009576 # Taken from https://stackoverflow.com/a/35009576
python2.7 -c 'import os, sys, jinja2; sys.stdout.write( python3 -c 'import os, sys, jinja2; sys.stdout.write(
jinja2.Template(sys.stdin.read() jinja2.Template(sys.stdin.read()
).render(os.environ));' < $template_path > $output_path ).render(os.environ));' < $template_path > $output_path
} }
@ -583,12 +584,12 @@ ynh_app_upstream_version () {
if [[ "$manifest" != "" ]] && [[ -e "$manifest" ]]; if [[ "$manifest" != "" ]] && [[ -e "$manifest" ]];
then then
version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") version_key_=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version")
else else
version_key=$YNH_APP_MANIFEST_VERSION version_key_=$YNH_APP_MANIFEST_VERSION
fi fi
echo "${version_key/~ynh*/}" echo "${version_key_/~ynh*/}"
} }
# Read package version from the manifest # Read package version from the manifest
@ -611,57 +612,33 @@ ynh_app_package_version () {
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
version_key=$YNH_APP_MANIFEST_VERSION version_key_=$YNH_APP_MANIFEST_VERSION
echo "${version_key/*~ynh/}" echo "${version_key_/*~ynh/}"
} }
# 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_APP if the upstream app version has changed
# - UPGRADE_PACKAGE if only the YunoHost package has changed # - UPGRADE_PACKAGE if only the YunoHost package has changed
# # - UPGRADE_APP otherwise
# It stops the current script without error if the package is up-to-date
# #
# 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, # To force an upgrade, even if the package is up to date,
# you have to set the variable YNH_FORCE_UPGRADE before. # you have to use the parameter --force (or -F).
# example: sudo YNH_FORCE_UPGRADE=1 yunohost app upgrade MyApp # example: sudo yunohost app upgrade MyApp --force
# #
# usage: ynh_check_app_version_changed # 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 force_upgrade=${YNH_FORCE_UPGRADE:-0} local return_value=${YNH_APP_UPGRADE_TYPE}
local package_check=${PACKAGE_CHECK_EXEC:-0}
# By default, upstream app version has changed if [ "$return_value" == "UPGRADE_FULL" ] || [ "$return_value" == "UPGRADE_FORCED" ] || [ "$return_value" == "DOWNGRADE_FORCED" ]
local return_value="UPGRADE_APP"
local current_version=$(ynh_read_manifest --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json" --manifest_key="version" || echo 1.0)
local current_upstream_version="$(ynh_app_upstream_version --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json")"
local update_version=$(ynh_read_manifest --manifest="../manifest.json" --manifest_key="version" || echo 1.0)
local update_upstream_version="$(ynh_app_upstream_version)"
if [ "$current_version" == "$update_version" ]
then then
# Complete versions are the same return_value="UPGRADE_APP"
if [ "$force_upgrade" != "0" ]
then
ynh_print_info --message="Upgrade forced by YNH_FORCE_UPGRADE."
unset YNH_FORCE_UPGRADE
elif [ "$package_check" != "0" ]
then
ynh_print_info --message="Upgrade forced for package check."
else
ynh_die "Up-to-date, nothing to do" 0
fi
elif [ "$current_upstream_version" == "$update_upstream_version" ]
then
# Upstream versions are the same, only YunoHost package versions differ
return_value="UPGRADE_PACKAGE"
fi fi
echo $return_value echo $return_value
} }
@ -673,7 +650,7 @@ ynh_check_app_version_changed () {
# #
# Generally you might probably use it as follow in the upgrade script # Generally you might probably use it as follow in the upgrade script
# #
# if ynh_compare_current_package_version --comparaison 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
@ -699,12 +676,12 @@ ynh_compare_current_package_version() {
# Check the syntax of the versions # Check the syntax of the versions
if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]] if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]]
then then
ynh_die "Invalid argument for version." ynh_die --message="Invalid argument for version."
fi fi
# 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 "Invialid comparator must be : lt, le, eq, ne, ge, gt" ynh_die --message="Invialid 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

@ -7,5 +7,5 @@ mkdir -p $YNH_CWD
cd "$YNH_CWD" cd "$YNH_CWD"
# Backup the configuration # Backup the configuration
ynh_backup --src_path="/etc/yunohost/dyndns" --not_mandatory ynh_exec_warn_less ynh_backup --src_path="/etc/yunohost/dyndns" --not_mandatory
ynh_backup --src_path="/etc/cron.d/yunohost-dyndns" --not_mandatory ynh_exec_warn_less ynh_backup --src_path="/etc/cron.d/yunohost-dyndns" --not_mandatory

View file

@ -115,7 +115,7 @@ do_post_regen() {
} }
_update_services() { _update_services() {
python2 - << EOF python3 - << EOF
import yaml import yaml

View file

@ -26,11 +26,13 @@ do_pre_regen() {
# Add possibility to specify a relay # Add possibility to specify a relay
# Could be useful with some isp with no 25 port open or more complex setup # Could be useful with some isp with no 25 port open or more complex setup
export relay_port=""
export relay_user=""
export relay_host="$(yunohost settings get 'smtp.relay.host')" export relay_host="$(yunohost settings get 'smtp.relay.host')"
if [ -n "${relay_host}" ] if [ -n "${relay_host}" ]
then then
export relay_port="$(yunohost settings get 'smtp.relay.port')" relay_port="$(yunohost settings get 'smtp.relay.port')"
export relay_user="$(yunohost settings get 'smtp.relay.user')" relay_user="$(yunohost settings get 'smtp.relay.user')"
relay_password="$(yunohost settings get 'smtp.relay.password')" relay_password="$(yunohost settings get 'smtp.relay.password')"
# Avoid to display "Relay account paswword" to other users # Avoid to display "Relay account paswword" to other users

View file

@ -98,6 +98,11 @@ class BaseSystemDiagnoser(Diagnoser):
summary="diagnosis_package_installed_from_sury", summary="diagnosis_package_installed_from_sury",
details=["diagnosis_package_installed_from_sury_details"]) details=["diagnosis_package_installed_from_sury_details"])
if self.backports_in_sources_list():
yield dict(meta={"test": "backports_in_sources_list"},
status="WARNING",
summary="diagnosis_backports_in_sources_list")
def bad_sury_packages(self): def bad_sury_packages(self):
packages_to_check = ["openssl", "libssl1.1", "libssl-dev"] packages_to_check = ["openssl", "libssl1.1", "libssl-dev"]
@ -111,6 +116,11 @@ class BaseSystemDiagnoser(Diagnoser):
version_to_downgrade_to = check_output(cmd) version_to_downgrade_to = check_output(cmd)
yield (package, version_to_downgrade_to) yield (package, version_to_downgrade_to)
def backports_in_sources_list(self):
cmd = "grep -q -nr '^ *deb .*-backports' /etc/apt/sources.list*"
return os.system(cmd) == 0
def is_vulnerable_to_meltdown(self): def is_vulnerable_to_meltdown(self):
# meltdown CVE: https://security-tracker.debian.org/tracker/CVE-2017-5754 # meltdown CVE: https://security-tracker.debian.org/tracker/CVE-2017-5754
@ -149,7 +159,8 @@ class BaseSystemDiagnoser(Diagnoser):
# "missing some kernel info (see -v), accuracy might be reduced" # "missing some kernel info (see -v), accuracy might be reduced"
# Dunno what to do about that but we probably don't want to harass # Dunno what to do about that but we probably don't want to harass
# users with this warning ... # users with this warning ...
output, err = call.communicate() output, _ = call.communicate()
output = output.decode()
assert call.returncode in (0, 2, 3), "Return code: %s" % call.returncode assert call.returncode in (0, 2, 3), "Return code: %s" % call.returncode
# If there are multiple lines, sounds like there was some messages # If there are multiple lines, sounds like there was some messages

View file

@ -1,10 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import psutil import psutil
import subprocess
import datetime import datetime
import re import re
from moulinette.utils.process import check_output
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
@ -119,7 +120,7 @@ class SystemResourcesDiagnoser(Diagnoser):
def analyzed_kern_log(): def analyzed_kern_log():
cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process" || true' cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process" || true'
out = subprocess.check_output(cmd, shell=True).strip() out = check_output(cmd)
lines = out.split("\n") if out else [] lines = out.split("\n") if out else []
now = datetime.datetime.now() now = datetime.datetime.now()

67
debian/changelog vendored
View file

@ -1,3 +1,70 @@
yunohost (4.1.5) stable; urgency=low
- [fix] Update helpers ([#1136](https://github.com/yunohost/yunohost/pull/11346))
- [fix] Certificate during regen conf on some setup (1d2b1d9)
- [fix] Empty password is not an error if it's optional ([#1135](https://github.com/yunohost/yunohost/pull/11345))
- [fix] Remove useless warnings during system backup ([#1138](https://github.com/yunohost/yunohost/pull/11348))
- [fix] We can now use "true" or "false" for a boolean ([#1134](https://github.com/yunohost/yunohost/pull/1134))
- [i18n] Translations updated for Catalan, French, Italian, Spanish
Thanks to all contributors <3 ! (Aleks, Kay0u, Omnia89, jorge-vitrubio, YohannEpitech, xaloc33)
-- Kayou <pierre@kayou.io> Thu, 14 Jan 2021 21:23:39 +0100
yunohost (4.1.4.4) stable; urgency=low
- [fix] Add the -F flag to grep command for fixed string mode, prevent special chars in the password to be interpreted as regex pattern ([#1132](https://github.com/yunohost/yunohost/pull/1132))
- [fix] apt helpers: explicitly return 0, otherwise the return code of last command is used, which in that case is 1 ... (c56883d0)
Thanks to all contributors <3 ! (Saxodwarf)
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 11 Jan 2021 14:17:37 +0100
yunohost (4.1.4.3) stable; urgency=low
- [fix] ynh_replace_vars in case var is defined but empty (30dde208)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 10 Jan 2021 01:58:35 +0100
yunohost (4.1.4.2) stable; urgency=low
- [fix] Prevent info from being redacted (because of foobar_key=) by the logging system (8f1b05f3)
- [fix] For some reason sometimes submetadata is None ... (00508c96)
- [enh] Reduce the noise in logs because of ynh_app_setting (ac4b62ce)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 09 Jan 2021 18:59:01 +0100
yunohost (4.1.4.1) stable; urgency=low
- [hotfix] Postfix conf always included the relay snippets (b25cde0b)
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 08 Jan 2021 16:21:07 +0100
yunohost (4.1.4) stable; urgency=low
- [fix] firewall: force source port for UPnP. ([#1109](https://github.com/yunohost/yunohost/pull/1109))
- Stable release
Thanks to all contributors <3 ! (Léo Le Bouter)
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 08 Jan 2021 03:09:14 +0100
yunohost (4.1.3) testing; urgency=low
- [enh] Do not advertise upgrades for bad-quality apps ([#1066](https://github.com/yunohost/yunohost/pull/1066))
- [enh] Display domain_path of app in the output of app list ([#1120](https://github.com/yunohost/yunohost/pull/1120))
- [enh] Diagnosis: report usage of backports repository in apt's sources.list ([#1069](https://github.com/yunohost/yunohost/pull/1069))
- [mod] Code cleanup, misc fixes (165d2b32, [#1121](https://github.com/yunohost/yunohost/pull/1121), [#1122](https://github.com/yunohost/yunohost/pull/1122), [#1123](https://github.com/yunohost/yunohost/pull/1123), [#1131](https://github.com/yunohost/yunohost/pull/1131))
- [mod] Also display app label on remove_domain with apps ([#1124](https://github.com/yunohost/yunohost/pull/1124))
- [enh] Be able to change user password in CLI without writing it in clear ([#1075](https://github.com/YunoHost/yunohost/pull/1075))
- [enh] New permissions helpers ([#1117](https://github.com/yunohost/yunohost/pull/1117))
- [i18n] Translations updated for French, German
Thanks to all contributors <3 ! (C. Wehrli, cricriiiiii, Kay0u, Bram, ljf, ppr)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 07 Jan 2021 00:46:09 +0100
yunohost (4.1.2) testing; urgency=low yunohost (4.1.2) testing; urgency=low
- [enh] diagnosis: Detect moar hardware name (b685a274) - [enh] diagnosis: Detect moar hardware name (b685a274)

13
debian/control vendored
View file

@ -2,19 +2,18 @@ Source: yunohost
Section: utils Section: utils
Priority: extra Priority: extra
Maintainer: YunoHost Contributors <contrib@yunohost.org> Maintainer: YunoHost Contributors <contrib@yunohost.org>
Build-Depends: debhelper (>=9), dh-systemd, dh-python, python-all (>= 2.7), python-yaml, python-jinja2 Build-Depends: debhelper (>=9), dh-systemd, dh-python, python3-all (>= 3.7), python3-yaml, python3-jinja2
Standards-Version: 3.9.6 Standards-Version: 3.9.6
X-Python-Version: >= 2.7
Homepage: https://yunohost.org/ Homepage: https://yunohost.org/
Package: yunohost Package: yunohost
Essential: yes Essential: yes
Architecture: all Architecture: all
Depends: ${python:Depends}, ${misc:Depends} Depends: ${python3:Depends}, ${misc:Depends}
, moulinette (>= 4.1.0.1), ssowat (>= 4.0) , moulinette (>= 4.1.0.1), ssowat (>= 4.0)
, python-psutil, python-requests, python-dnspython, python-openssl , python3-psutil, python3-requests, python3-dnspython, python3-openssl
, python-miniupnpc, python-dbus, python-jinja2 , python3-miniupnpc, python3-dbus, python3-jinja2
, python-toml, python-packaging, python-publicsuffix , python3-toml, python3-packaging, python3-publicsuffix
, apt, apt-transport-https, apt-utils, dirmngr , apt, apt-transport-https, apt-utils, dirmngr
, php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl , php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl
, mariadb-server, php7.3-mysql , mariadb-server, php7.3-mysql
@ -33,7 +32,7 @@ Recommends: yunohost-admin
, ntp, inetutils-ping | iputils-ping , ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog , bash-completion, rsyslog
, php7.3-gd, php7.3-curl, php-gettext , php7.3-gd, php7.3-curl, php-gettext
, python-pip , python3-pip
, unattended-upgrades , unattended-upgrades
, libdbd-ldap-perl, libnet-dns-perl , libdbd-ldap-perl, libnet-dns-perl
Suggests: htop, vim, rsync, acpi-support-base, udisks2 Suggests: htop, vim, rsync, acpi-support-base, udisks2

2
debian/rules vendored
View file

@ -5,7 +5,7 @@
#export DH_VERBOSE=1 #export DH_VERBOSE=1
%: %:
dh ${@} --with=python2,systemd dh ${@} --with=python3,systemd
override_dh_auto_build: override_dh_auto_build:
# Generate bash completion file # Generate bash completion file

View file

@ -1,4 +1,4 @@
#!/usr/env/python2.7 #!/usr/env/python3
import os import os
import glob import glob

View file

@ -140,7 +140,7 @@
"domain_dyndns_already_subscribed": "Ja us heu subscrit a un domini DynDNS", "domain_dyndns_already_subscribed": "Ja us heu subscrit a un domini DynDNS",
"domain_dyndns_root_unknown": "Domini DynDNS principal desconegut", "domain_dyndns_root_unknown": "Domini DynDNS principal desconegut",
"domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió. Això podria causar problemes més tard (podria no passar res).", "domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió. Això podria causar problemes més tard (podria no passar res).",
"domain_uninstall_app_first": "Aquestes aplicacions encara estan instal·lades en el vostre domini: {apps}. Desinstal·leu les abans d'eliminar el domini", "domain_uninstall_app_first": "Aquestes aplicacions encara estan instal·lades en el vostre domini:\n{apps}\n\nDesinstal·leu-les utilitzant l'ordre «yunohost app remove id_de_lapplicació» o moveu-les a un altre domini amb «yunohost app change-url id_de_lapplicació» abans d'eliminar el domini",
"domain_unknown": "Domini desconegut", "domain_unknown": "Domini desconegut",
"domains_available": "Dominis disponibles:", "domains_available": "Dominis disponibles:",
"done": "Fet", "done": "Fet",
@ -606,7 +606,7 @@
"diagnosis_dns_point_to_doc": "Consulteu la documentació a <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si necessiteu ajuda per configurar els registres DNS.", "diagnosis_dns_point_to_doc": "Consulteu la documentació a <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si necessiteu ajuda per configurar els registres DNS.",
"diagnosis_mail_outgoing_port_25_ok": "El servidor de correu electrònic SMTP pot enviar correus electrònics (el port de sortida 25 no està bloquejat).", "diagnosis_mail_outgoing_port_25_ok": "El servidor de correu electrònic SMTP pot enviar correus electrònics (el port de sortida 25 no està bloquejat).",
"diagnosis_mail_outgoing_port_25_blocked_details": "Primer heu d'intentar desbloquejar el port 25 en la interfície del vostre router o en la interfície del vostre allotjador. (Alguns proveïdors d'allotjament demanen enviar un tiquet de suport en aquests casos).", "diagnosis_mail_outgoing_port_25_blocked_details": "Primer heu d'intentar desbloquejar el port 25 en la interfície del vostre router o en la interfície del vostre allotjador. (Alguns proveïdors d'allotjament demanen enviar un tiquet de suport en aquests casos).",
"diagnosis_mail_ehlo_ok": "El servidor de correu electrònic SMTP no és accessible des de l'exterior i per tant no pot rebre correus electrònics!", "diagnosis_mail_ehlo_ok": "El servidor de correu electrònic SMTP és accessible des de l'exterior i per tant pot rebre correus electrònics!",
"diagnosis_mail_ehlo_unreachable": "El servidor de correu electrònic SMTP no és accessible des de l'exterior amb IPv{ipversion}. No podrà rebre correus electrònics.", "diagnosis_mail_ehlo_unreachable": "El servidor de correu electrònic SMTP no és accessible des de l'exterior amb IPv{ipversion}. No podrà rebre correus electrònics.",
"diagnosis_mail_ehlo_bad_answer": "Un servei no SMTP a respost en el port 25 amb IPv{ipversion}", "diagnosis_mail_ehlo_bad_answer": "Un servei no SMTP a respost en el port 25 amb IPv{ipversion}",
"diagnosis_mail_ehlo_bad_answer_details": "Podria ser que sigui per culpa d'una altra màquina responent en lloc del servidor.", "diagnosis_mail_ehlo_bad_answer_details": "Podria ser que sigui per culpa d'una altra màquina responent en lloc del servidor.",
@ -712,5 +712,7 @@
"app_label_deprecated": "Aquesta ordre està desestimada! Si us plau utilitzeu la nova ordre «yunohost user permission update» per gestionar l'etiqueta de l'aplicació.", "app_label_deprecated": "Aquesta ordre està desestimada! Si us plau utilitzeu la nova ordre «yunohost user permission update» per gestionar l'etiqueta de l'aplicació.",
"app_argument_password_no_default": "Hi ha hagut un error al analitzar l'argument de la contrasenya «{name}»: l'argument de contrasenya no pot tenir un valor per defecte per raons de seguretat", "app_argument_password_no_default": "Hi ha hagut un error al analitzar l'argument de la contrasenya «{name}»: l'argument de contrasenya no pot tenir un valor per defecte per raons de seguretat",
"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_basesystem_hardware_model": "El model del servidor és {model}"
} }

View file

@ -181,7 +181,7 @@
"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",
"certmanager_conflicting_nginx_file": "Die Domain konnte nicht für die ACME challenge vorbereitet werden: Die nginx Konfigurationsdatei {filepath:s} verursacht Probleme und sollte vorher entfernt werden", "certmanager_conflicting_nginx_file": "Die Domain konnte nicht für die ACME challenge vorbereitet werden: Die nginx Konfigurationsdatei {filepath:s} verursacht Probleme und sollte vorher entfernt werden",
"domain_cannot_remove_main": "Die primäre Domain konnten nicht entfernt werden. Lege zuerst einen neue primäre Domain fest", "domain_cannot_remove_main": "Die primäre Domain konnten nicht entfernt werden. Lege zuerst einen neue primäre Domain Sie können die Domäne '{domain:s}' nicht entfernen, weil Sie die Hauptdomäne ist. Sie müssen zuerst eine andere Domäne als Hauptdomäne festlegen. Sie können das mit dem Befehl <cmd>'yunohost domain main-domain -n <another-domain></cmd> tun. Hier ist eine Liste der möglichen Domänen: {other_domains:s}",
"certmanager_self_ca_conf_file_not_found": "Die Konfigurationsdatei der Zertifizierungsstelle für selbstsignierte Zertifikate wurde nicht gefunden (Datei {file:s})", "certmanager_self_ca_conf_file_not_found": "Die Konfigurationsdatei der Zertifizierungsstelle für selbstsignierte Zertifikate wurde nicht gefunden (Datei {file:s})",
"certmanager_acme_not_configured_for_domain": "Die ACME Challenge kann im Moment nicht für {domain} ausgeführt werden, weil in ihrer nginx conf das entsprechende Code-Snippet fehlt... Bitte stellen Sie sicher, dass Ihre nginx-Konfiguration mit 'yunohost tools regen-conf nginx --dry-run --with-diff' auf dem neuesten Stand ist.", "certmanager_acme_not_configured_for_domain": "Die ACME Challenge kann im Moment nicht für {domain} ausgeführt werden, weil in ihrer nginx conf das entsprechende Code-Snippet fehlt... Bitte stellen Sie sicher, dass Ihre nginx-Konfiguration mit 'yunohost tools regen-conf nginx --dry-run --with-diff' auf dem neuesten Stand ist.",
"certmanager_unable_to_parse_self_CA_name": "Der Name der Zertifizierungsstelle für selbstsignierte Zertifikate konnte nicht aufgelöst werden (Datei: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Der Name der Zertifizierungsstelle für selbstsignierte Zertifikate konnte nicht aufgelöst werden (Datei: {file:s})",
@ -472,5 +472,8 @@
"diagnosis_http_hairpinning_issue_details": "Das ist wahrscheinlich aufgrund Ihrer ISP Box / Router. Als Konsequenz können Personen von ausserhalb Ihres Netzwerkes aber nicht von innerhalb Ihres lokalen Netzwerkes (wie wahrscheinlich Sie selber?) wie gewohnt auf Ihren Server zugreifen, wenn Sie ihre Domäne oder Ihre öffentliche IP verwenden. Sie können die Situation wahrscheinlich verbessern, indem Sie ein einen Blick in <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a> werfen", "diagnosis_http_hairpinning_issue_details": "Das ist wahrscheinlich aufgrund Ihrer ISP Box / Router. Als Konsequenz können Personen von ausserhalb Ihres Netzwerkes aber nicht von innerhalb Ihres lokalen Netzwerkes (wie wahrscheinlich Sie selber?) wie gewohnt auf Ihren Server zugreifen, wenn Sie ihre Domäne oder Ihre öffentliche IP verwenden. Sie können die Situation wahrscheinlich verbessern, indem Sie ein einen Blick in <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a> werfen",
"diagnosis_http_nginx_conf_not_up_to_date": "Jemand hat anscheinend die Konfiguration von Nginx manuell geändert. Diese Änderung verhindert, dass Yunohost eine Diagnose durchführen kann, wenn er via HTTP erreichbar ist.", "diagnosis_http_nginx_conf_not_up_to_date": "Jemand hat anscheinend die Konfiguration von Nginx manuell geändert. Diese Änderung verhindert, dass Yunohost eine Diagnose durchführen kann, wenn er via HTTP erreichbar ist.",
"diagnosis_http_bad_status_code": "Anscheinend beantwortet ein anderes Gerät als Ihr Server die Anfrage (Vielleicht ihr Internetrouter).<br>1. Die häufigste Ursache ist, dass Port 80 (und 443) <a href='https://yunohost.org/isp_box_config'>nicht richtig auf Ihren Server weitergeleitet wird</a>.<br> 2. Bei komplexeren Setups: Vergewissern Sie sich, dass keine Firewall und keine Reverse-Proxy interferieren.", "diagnosis_http_bad_status_code": "Anscheinend beantwortet ein anderes Gerät als Ihr Server die Anfrage (Vielleicht ihr Internetrouter).<br>1. Die häufigste Ursache ist, dass Port 80 (und 443) <a href='https://yunohost.org/isp_box_config'>nicht richtig auf Ihren Server weitergeleitet wird</a>.<br> 2. Bei komplexeren Setups: Vergewissern Sie sich, dass keine Firewall und keine Reverse-Proxy interferieren.",
"diagnosis_never_ran_yet": "Sie haben kürzlich einen neuen Yunohost-Server installiert aber es gibt davon noch keinen Diagnosereport. Sie sollten eine Diagnose anstossen. Sie können das entweder vom Webadmin aus oder in der Kommandozeile machen. In der Kommandozeile verwenden Sie dafür den Befehl 'yunohost diagnosis run'." "diagnosis_never_ran_yet": "Sie haben kürzlich einen neuen Yunohost-Server installiert aber es gibt davon noch keinen Diagnosereport. Sie sollten eine Diagnose anstossen. Sie können das entweder vom Webadmin aus oder in der Kommandozeile machen. In der Kommandozeile verwenden Sie dafür den Befehl 'yunohost diagnosis run'.",
"diagnosis_http_nginx_conf_not_up_to_date_details": "Um dieses Problem zu beheben, geben Sie in der Kommandozeile <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> ein. Dieses Tool zeigt ihnen den Unterschied an. Wenn Sie damit einverstanden sind, können Sie mit <cmd>yunohost tools regen-conf nginx --force</cmd> die Änderungen übernehmen.",
"diagnosis_backports_in_sources_list": "Sie haben anscheinend apt (den Paketmanager) für das Backports-Repository konfiguriert. Wir raten strikte davon ab, Pakete aus dem Backports-Repository zu installieren. Diese würden wahrscheinlich zu Instabilitäten und Konflikten führen. Es sei denn, Sie wissen was Sie tun.",
"diagnosis_basesystem_hardware_model": "Das Servermodell ist {model}"
} }

View file

@ -147,6 +147,7 @@
"diagnosis_basesystem_ynh_single_version": "{package} version: {version} ({repo})", "diagnosis_basesystem_ynh_single_version": "{package} version: {version} ({repo})",
"diagnosis_basesystem_ynh_main_version": "Server is running YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Server is running YunoHost {main_version} ({repo})",
"diagnosis_basesystem_ynh_inconsistent_versions": "You are running inconsistent versions of the YunoHost packages... most probably because of a failed or partial upgrade.", "diagnosis_basesystem_ynh_inconsistent_versions": "You are running inconsistent versions of the YunoHost packages... most probably because of a failed or partial upgrade.",
"diagnosis_backports_in_sources_list": "It looks like apt (the package manager) is configured to use the backports repository. Unless you really know what you are doing, we strongly discourage from installing packages from backports, because it's likely to create unstabilities or conflicts on your system.",
"diagnosis_package_installed_from_sury": "Some system packages should be downgraded", "diagnosis_package_installed_from_sury": "Some system packages should be downgraded",
"diagnosis_package_installed_from_sury_details": "Some packages were inadvertendly installed from a third-party repository called Sury. The Yunohost team improved the strategy that handle these packages, but it's expected that some setups that installed PHP7.3 apps while still on Stretch have some remaining inconsistencies. To fix this situation, you should try running the following command: <cmd>{cmd_to_fix}</cmd>", "diagnosis_package_installed_from_sury_details": "Some packages were inadvertendly installed from a third-party repository called Sury. The Yunohost team improved the strategy that handle these packages, but it's expected that some setups that installed PHP7.3 apps while still on Stretch have some remaining inconsistencies. To fix this situation, you should try running the following command: <cmd>{cmd_to_fix}</cmd>",
"diagnosis_display_tip": "To see the issues found, you can go to the Diagnosis section of the webadmin, or run 'yunohost diagnosis show --issues' from the command-line.", "diagnosis_display_tip": "To see the issues found, you can go to the Diagnosis section of the webadmin, or run 'yunohost diagnosis show --issues' from the command-line.",

View file

@ -1,7 +1,7 @@
{ {
"action_invalid": "Acción no válida '{action:s} 1'", "action_invalid": "Acción no válida '{action:s} 1'",
"admin_password": "Contraseña administrativa", "admin_password": "Contraseña administrativa",
"admin_password_change_failed": "No se puede cambiar la contraseña", "admin_password_change_failed": "No se pudo cambiar la contraseña",
"admin_password_changed": "La contraseña de administración fue cambiada", "admin_password_changed": "La contraseña de administración fue cambiada",
"app_already_installed": "{app:s} ya está instalada", "app_already_installed": "{app:s} ya está instalada",
"app_argument_choice_invalid": "Use una de estas opciones «{choices:s}» para el argumento «{name:s}»", "app_argument_choice_invalid": "Use una de estas opciones «{choices:s}» para el argumento «{name:s}»",
@ -12,7 +12,7 @@
"app_install_files_invalid": "Estos archivos no se pueden instalar", "app_install_files_invalid": "Estos archivos no se pueden instalar",
"app_manifest_invalid": "Algo va mal con el manifiesto de la aplicación: {error}", "app_manifest_invalid": "Algo va mal con el manifiesto de la aplicación: {error}",
"app_not_correctly_installed": "La aplicación {app:s} 8 parece estar incorrectamente instalada", "app_not_correctly_installed": "La aplicación {app:s} 8 parece estar incorrectamente instalada",
"app_not_installed": "No se pudo encontrar la aplicación «{app:s}» en la lista de aplicaciones instaladas: {all_apps}", "app_not_installed": "No se pudo encontrar «{app:s}» en la lista de aplicaciones instaladas: {all_apps}",
"app_not_properly_removed": "La {app:s} 0 no ha sido desinstalada correctamente", "app_not_properly_removed": "La {app:s} 0 no ha sido desinstalada correctamente",
"app_removed": "Eliminado {app:s}", "app_removed": "Eliminado {app:s}",
"app_requirements_checking": "Comprobando los paquetes necesarios para {app}…", "app_requirements_checking": "Comprobando los paquetes necesarios para {app}…",
@ -28,8 +28,8 @@
"ask_main_domain": "Dominio principal", "ask_main_domain": "Dominio principal",
"ask_new_admin_password": "Nueva contraseña administrativa", "ask_new_admin_password": "Nueva contraseña administrativa",
"ask_password": "Contraseña", "ask_password": "Contraseña",
"backup_app_failed": "No se pudo respaldar la aplicación «{app:s}»", "backup_app_failed": "No se pudo respaldar «{app:s}»",
"backup_archive_app_not_found": "No se pudo encontrar la aplicación «{app:s}» en el archivo de respaldo", "backup_archive_app_not_found": "No se pudo encontrar «{app:s}» en el archivo de respaldo",
"backup_archive_name_exists": "Ya existe un archivo de respaldo con este nombre.", "backup_archive_name_exists": "Ya existe un archivo de respaldo con este nombre.",
"backup_archive_name_unknown": "Copia de seguridad local desconocida '{name:s}'", "backup_archive_name_unknown": "Copia de seguridad local desconocida '{name:s}'",
"backup_archive_open_failed": "No se pudo abrir el archivo de respaldo", "backup_archive_open_failed": "No se pudo abrir el archivo de respaldo",
@ -44,7 +44,7 @@
"backup_output_directory_forbidden": "Elija un directorio de salida diferente. Las copias de seguridad no se pueden crear en /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var o /home/yunohost.backup/archives subcarpetas", "backup_output_directory_forbidden": "Elija un directorio de salida diferente. Las copias de seguridad no se pueden crear en /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var o /home/yunohost.backup/archives subcarpetas",
"backup_output_directory_not_empty": "Debe elegir un directorio de salida vacío", "backup_output_directory_not_empty": "Debe elegir un directorio de salida vacío",
"backup_output_directory_required": "Debe proporcionar un directorio de salida para la copia de seguridad", "backup_output_directory_required": "Debe proporcionar un directorio de salida para la copia de seguridad",
"backup_running_hooks": "Ejecutando los hooks de copia de seguridad...", "backup_running_hooks": "Ejecutando los hooks de copia de respaldo...",
"custom_app_url_required": "Debe proporcionar una URL para actualizar su aplicación personalizada {app:s}", "custom_app_url_required": "Debe proporcionar una URL para actualizar su aplicación personalizada {app:s}",
"domain_cert_gen_failed": "No se pudo generar el certificado", "domain_cert_gen_failed": "No se pudo generar el certificado",
"domain_created": "Dominio creado", "domain_created": "Dominio creado",
@ -54,7 +54,7 @@
"domain_dyndns_already_subscribed": "Ya se ha suscrito a un dominio de DynDNS", "domain_dyndns_already_subscribed": "Ya se ha suscrito a un dominio de DynDNS",
"domain_dyndns_root_unknown": "Dominio raíz de DynDNS desconocido", "domain_dyndns_root_unknown": "Dominio raíz de DynDNS desconocido",
"domain_exists": "El dominio ya existe", "domain_exists": "El dominio ya existe",
"domain_uninstall_app_first": "Una o más aplicaciones están instaladas en este dominio. Debe desinstalarlas antes de eliminar el dominio", "domain_uninstall_app_first": "Estas aplicaciones están todavía instaladas en tu dominio:\n{apps}\n\nPor favor desinstálalas utilizando <code>yunohost app remove the_app_id</code> o cambialas a otro dominio usando <code>yunohost app change-url the_app_id</code> antes de continuar con el borrado del dominio.",
"domain_unknown": "Dominio desconocido", "domain_unknown": "Dominio desconocido",
"done": "Hecho.", "done": "Hecho.",
"downloading": "Descargando…", "downloading": "Descargando…",
@ -168,9 +168,9 @@
"certmanager_certificate_fetching_or_enabling_failed": "El intento de usar el nuevo certificado para {domain:s} no ha funcionado…", "certmanager_certificate_fetching_or_enabling_failed": "El intento de usar el nuevo certificado para {domain:s} no ha funcionado…",
"certmanager_attempt_to_renew_nonLE_cert": "El certificado para el dominio «{domain:s}» no ha sido emitido por Let's Encrypt. ¡No se puede renovar automáticamente!", "certmanager_attempt_to_renew_nonLE_cert": "El certificado para el dominio «{domain:s}» no ha sido emitido por Let's Encrypt. ¡No se puede renovar automáticamente!",
"certmanager_attempt_to_renew_valid_cert": "¡El certificado para el dominio «{domain:s}» no está a punto de expirar! (Puede usar --force si sabe lo que está haciendo)", "certmanager_attempt_to_renew_valid_cert": "¡El certificado para el dominio «{domain:s}» no está a punto de expirar! (Puede usar --force si sabe lo que está haciendo)",
"certmanager_domain_http_not_working": "Parece que no se puede acceder al dominio {domain:s} a través de HTTP. Compruebe que la configuración del DNS y de NGINX es correcta", "certmanager_domain_http_not_working": "Parece que no se puede acceder al dominio {domain:s} a través de HTTP. Por favor compruebe en los diagnósticos la categoría 'Web'para más información. (Si sabe lo que está haciendo, utilice '--no-checks' para no realizar estas comprobaciones.)",
"certmanager_error_no_A_record": "No se ha encontrado un registro DNS «A» para el dominio {domain:s}. Debe hacer que su nombre de dominio apunte a su máquina para poder instalar un certificado de Let's Encrypt. (Si sabe lo que está haciendo, use «--no-checks» para desactivar esas comprobaciones.)", "certmanager_error_no_A_record": "No se ha encontrado un registro DNS «A» para el dominio {domain:s}. Debe hacer que su nombre de dominio apunte a su máquina para poder instalar un certificado de Let's Encrypt. (Si sabe lo que está haciendo, use «--no-checks» para desactivar esas comprobaciones.)",
"certmanager_domain_dns_ip_differs_from_public_ip": "El registro DNS 'A' para el dominio '{domain:s}' es diferente de la IP de este servidor. Si recientemente modificó su registro A, espere a que se propague (algunos verificadores de propagación de DNS están disponibles en línea). (Si sabe lo que está haciendo, use '--no-checks' para desactivar esos cheques)", "certmanager_domain_dns_ip_differs_from_public_ip": "El registro DNS 'A' para el dominio '{domain:s}' es diferente de la IP de este servidor. Por favor comprueba los 'registros DNS' (básicos) la categoría de diagnósticos para mayor información. Si recientemente modificó su registro 'A', espere a que se propague (algunos verificadores de propagación de DNS están disponibles en línea). (Si sabe lo que está haciendo, use '--no-checks' para desactivar esos cheques)",
"certmanager_cannot_read_cert": "Se ha producido un error al intentar abrir el certificado actual para el dominio {domain:s} (archivo: {file:s}), razón: {reason:s}", "certmanager_cannot_read_cert": "Se ha producido un error al intentar abrir el certificado actual para el dominio {domain:s} (archivo: {file:s}), razón: {reason:s}",
"certmanager_cert_install_success_selfsigned": "Instalado correctamente un certificado autofirmado para el dominio «{domain:s}»", "certmanager_cert_install_success_selfsigned": "Instalado correctamente un certificado autofirmado para el dominio «{domain:s}»",
"certmanager_cert_install_success": "Instalado correctamente un certificado de Let's Encrypt para el dominio «{domain:s}»", "certmanager_cert_install_success": "Instalado correctamente un certificado de Let's Encrypt para el dominio «{domain:s}»",
@ -184,7 +184,7 @@
"certmanager_unable_to_parse_self_CA_name": "No se pudo procesar el nombre de la autoridad de autofirma (archivo: {file:s})", "certmanager_unable_to_parse_self_CA_name": "No se pudo procesar el nombre de la autoridad de autofirma (archivo: {file:s})",
"domains_available": "Dominios disponibles:", "domains_available": "Dominios disponibles:",
"backup_archive_broken_link": "No se pudo acceder al archivo de respaldo (enlace roto a {path:s})", "backup_archive_broken_link": "No se pudo acceder al archivo de respaldo (enlace roto a {path:s})",
"certmanager_acme_not_configured_for_domain": "El certificado para el dominio «{domain:s}» no parece que esté instalado correctamente. Ejecute primero «cert-install» para este dominio.", "certmanager_acme_not_configured_for_domain": "El reto ACME no ha podido ser realizado para {domain} porque su configuración de nginx no tiene el el código correcto... Por favor, asegurate que la configuración de nginx es correcta ejecutando en el terminal `yunohost tools regen-conf nginx --dry-run --with-diff`.",
"certmanager_http_check_timeout": "Tiempo de espera agotado cuando el servidor intentaba conectarse consigo mismo a través de HTTP usando una dirección IP pública (dominio «{domain:s}» con IP «{ip:s}»). Puede que esté experimentando un problema de redirección («hairpinning»), o que el cortafuegos o el enrutador de su servidor esté mal configurado.", "certmanager_http_check_timeout": "Tiempo de espera agotado cuando el servidor intentaba conectarse consigo mismo a través de HTTP usando una dirección IP pública (dominio «{domain:s}» con IP «{ip:s}»). Puede que esté experimentando un problema de redirección («hairpinning»), o que el cortafuegos o el enrutador de su servidor esté mal configurado.",
"certmanager_couldnt_fetch_intermediate_cert": "Tiempo de espera agotado intentando obtener el certificado intermedio de Let's Encrypt. Cancelada la instalación o renovación del certificado. Vuelva a intentarlo más tarde.", "certmanager_couldnt_fetch_intermediate_cert": "Tiempo de espera agotado intentando obtener el certificado intermedio de Let's Encrypt. Cancelada la instalación o renovación del certificado. Vuelva a intentarlo más tarde.",
"domain_hostname_failed": "No se pudo establecer un nuevo nombre de anfitrión («hostname»). Esto podría causar problemas más tarde (no es seguro... podría ir bien).", "domain_hostname_failed": "No se pudo establecer un nuevo nombre de anfitrión («hostname»). Esto podría causar problemas más tarde (no es seguro... podría ir bien).",
@ -197,16 +197,16 @@
"app_location_unavailable": "Este URL o no está disponible o está en conflicto con otra(s) aplicación(es) instalada(s):\n{apps:s}", "app_location_unavailable": "Este URL o no está disponible o está en conflicto con otra(s) aplicación(es) instalada(s):\n{apps:s}",
"app_already_up_to_date": "La aplicación {app:s} ya está actualizada", "app_already_up_to_date": "La aplicación {app:s} ya está actualizada",
"app_upgrade_some_app_failed": "No se pudieron actualizar algunas aplicaciones", "app_upgrade_some_app_failed": "No se pudieron actualizar algunas aplicaciones",
"app_make_default_location_already_used": "No puede hacer que la aplicación «{app}» sea la predeterminada en el dominio, «{domain}» ya está siendo usado por otra aplicación «{other_app}»", "app_make_default_location_already_used": "No pudo hacer que la aplicación «{app}» sea la predeterminada en el dominio, «{domain}» ya está siendo usado por la aplicación «{other_app}»",
"app_upgrade_app_name": "Actualizando ahora {app}…", "app_upgrade_app_name": "Ahora actualizando {app}…",
"backup_abstract_method": "Este método de respaldo aún no se ha implementado", "backup_abstract_method": "Este método de respaldo aún no se ha implementado",
"backup_applying_method_borg": "Enviando todos los archivos para la copia de seguridad al repositorio de borg-backup…", "backup_applying_method_borg": "Enviando todos los archivos para la copia de seguridad al repositorio de borg-backup…",
"backup_applying_method_copy": "Copiando todos los archivos a la copia de seguridad…", "backup_applying_method_copy": "Copiando todos los archivos en la copia de respaldo…",
"backup_applying_method_custom": "Llamando al método de copia de seguridad personalizado «{method:s}»…", "backup_applying_method_custom": "Llamando al método de copia de seguridad personalizado «{method:s}»…",
"backup_applying_method_tar": "Creando el archivo TAR de respaldo…", "backup_applying_method_tar": "Creando el archivo TAR de respaldo…",
"backup_archive_system_part_not_available": "La parte del sistema «{part:s}» no está disponible en esta copia de seguridad", "backup_archive_system_part_not_available": "La parte del sistema «{part:s}» no está disponible en esta copia de seguridad",
"backup_archive_writing_error": "No se pudieron añadir los archivos «{source:s}» (llamados en el archivo «{dest:s}») para ser respaldados en el archivo comprimido «{archive:s}»", "backup_archive_writing_error": "No se pudieron añadir los archivos «{source:s}» (llamados en el archivo «{dest:s}») para ser respaldados en el archivo comprimido «{archive:s}»",
"backup_ask_for_copying_if_needed": "¿Quiere realizar la copia de seguridad usando {size:s} MB temporalmente? (Se usa este modo ya que algunos archivos no se pudieron preparar usando un método más eficiente.)", "backup_ask_for_copying_if_needed": "¿Quiere realizar la copia de seguridad usando {size:s}MB temporalmente? (Se usa este modo ya que algunos archivos no se pudieron preparar usando un método más eficiente.)",
"backup_borg_not_implemented": "El método de respaldo de Borg aún no ha sido implementado", "backup_borg_not_implemented": "El método de respaldo de Borg aún no ha sido implementado",
"backup_cant_mount_uncompress_archive": "No se pudo montar el archivo descomprimido como protegido contra escritura", "backup_cant_mount_uncompress_archive": "No se pudo montar el archivo descomprimido como protegido contra escritura",
"backup_copying_to_organize_the_archive": "Copiando {size:s}MB para organizar el archivo", "backup_copying_to_organize_the_archive": "Copiando {size:s}MB para organizar el archivo",
@ -218,7 +218,7 @@
"backup_php5_to_php7_migration_may_fail": "No se pudo convertir su archivo para que sea compatible con PHP 7, puede que no pueda restaurar sus aplicaciones de PHP (motivo: {error:s})", "backup_php5_to_php7_migration_may_fail": "No se pudo convertir su archivo para que sea compatible con PHP 7, puede que no pueda restaurar sus aplicaciones de PHP (motivo: {error:s})",
"backup_system_part_failed": "No se pudo respaldar la parte del sistema «{part:s}»", "backup_system_part_failed": "No se pudo respaldar la parte del sistema «{part:s}»",
"backup_with_no_backup_script_for_app": "La aplicación «{app:s}» no tiene un guión de respaldo. Omitiendo.", "backup_with_no_backup_script_for_app": "La aplicación «{app:s}» no tiene un guión de respaldo. Omitiendo.",
"backup_with_no_restore_script_for_app": "La aplicación «{app:s}» no tiene un guión de restauración, no podrá restaurar automáticamente la copia de seguridad de esta aplicación.", "backup_with_no_restore_script_for_app": "«{app:s}» no tiene un script de restauración, no podá restaurar automáticamente la copia de seguridad de esta aplicación.",
"dyndns_could_not_check_provide": "No se pudo verificar si {provider:s} puede ofrecer {domain:s}.", "dyndns_could_not_check_provide": "No se pudo verificar si {provider:s} puede ofrecer {domain:s}.",
"dyndns_domain_not_provided": "El proveedor de DynDNS {provider:s} no puede proporcionar el dominio {domain:s}.", "dyndns_domain_not_provided": "El proveedor de DynDNS {provider:s} no puede proporcionar el dominio {domain:s}.",
"experimental_feature": "Aviso : esta funcionalidad es experimental y no se considera estable, no debería usarla a menos que sepa lo que está haciendo.", "experimental_feature": "Aviso : esta funcionalidad es experimental y no se considera estable, no debería usarla a menos que sepa lo que está haciendo.",
@ -446,14 +446,14 @@
"dyndns_could_not_check_available": "No se pudo comprobar si {domain:s} está disponible en {provider:s}.", "dyndns_could_not_check_available": "No se pudo comprobar si {domain:s} está disponible en {provider:s}.",
"domain_dns_conf_is_just_a_recommendation": "Esta orden muestra la configuración *recomendada*. No configura el DNS en realidad. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.", "domain_dns_conf_is_just_a_recommendation": "Esta orden muestra la configuración *recomendada*. No configura el DNS en realidad. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.",
"dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento ,parece que programa está usando el bloqueo de dpkg (el gestor de paquetes del sistema)", "dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento ,parece que programa está usando el bloqueo de dpkg (el gestor de paquetes del sistema)",
"dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/apt (los gestores de paquetes del sistema) parecen estar en un estado roto... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo dpkg --configure -a`.", "dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/APT (los gestores de paquetes del sistema) parecen estar mal configurados... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo apt install --fix-broken` y/o `sudo dpkg --configure -a`.",
"confirm_app_install_thirdparty": "¡PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de Yunohost. La instalación de aplicaciones de terceros puede comprometer la integridad y la seguridad de su sistema. Probablemente NO debería instalarlo a menos que sepa lo que está haciendo. NO se proporcionará SOPORTE si esta aplicación no funciona o rompe su sistema ... Si de todos modos está dispuesto a correr ese riesgo, escriba '{answers:s}'", "confirm_app_install_thirdparty": "¡PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de Yunohost. La instalación de aplicaciones de terceros puede comprometer la integridad y la seguridad de su sistema. Probablemente NO debería instalarlo a menos que sepa lo que está haciendo. NO se proporcionará SOPORTE si esta aplicación no funciona o rompe su sistema ... Si de todos modos está dispuesto a correr ese riesgo, escriba '{answers:s}'",
"confirm_app_install_danger": "¡PELIGRO! ¡Se sabe que esta aplicación sigue siendo experimental (si no explícitamente no funciona)! Probablemente NO debería instalarlo a menos que sepa lo que está haciendo. NO se proporcionará SOPORTE si esta aplicación no funciona o rompe su sistema ... Si de todos modos está dispuesto a correr ese riesgo, escriba '{answers:s}'", "confirm_app_install_danger": "¡PELIGRO! ¡Se sabe que esta aplicación sigue siendo experimental (si no explícitamente no funciona)! Probablemente NO debería instalarlo a menos que sepa lo que está haciendo. NO se proporcionará SOPORTE si esta aplicación no funciona o rompe su sistema ... Si de todos modos está dispuesto a correr ese riesgo, escriba '{answers:s}'",
"confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ", "confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ",
"backup_unable_to_organize_files": "No se pudo usar el método rápido de organización de los archivos en el archivo", "backup_unable_to_organize_files": "No se pudo usar el método rápido de organización de los archivos en el archivo",
"backup_permission": "Permiso de respaldo para la aplicación {app:s}", "backup_permission": "Permiso de respaldo para {app:s}",
"backup_output_symlink_dir_broken": "El directorio de su archivo «{path:s}» es un enlace simbólico roto. Tal vez olvidó (re)montarlo o conectarlo al medio de almacenamiento al que apunta.", "backup_output_symlink_dir_broken": "El directorio de su archivo «{path:s}» es un enlace simbólico roto. Tal vez olvidó (re)montarlo o conectarlo al medio de almacenamiento al que apunta.",
"backup_mount_archive_for_restore": "Preparando el archivo para la restauración…", "backup_mount_archive_for_restore": "Preparando el archivo para restaurarlo…",
"backup_method_tar_finished": "Creado el archivo TAR de respaldo", "backup_method_tar_finished": "Creado el archivo TAR de respaldo",
"backup_method_custom_finished": "Terminado el método «{method:s}» de respaldo personalizado", "backup_method_custom_finished": "Terminado el método «{method:s}» de respaldo personalizado",
"backup_method_copy_finished": "Terminada la copia de seguridad", "backup_method_copy_finished": "Terminada la copia de seguridad",
@ -463,10 +463,10 @@
"ask_new_path": "Nueva ruta", "ask_new_path": "Nueva ruta",
"ask_new_domain": "Nuevo dominio", "ask_new_domain": "Nuevo dominio",
"app_upgrade_several_apps": "Las siguientes aplicaciones se actualizarán: {apps}", "app_upgrade_several_apps": "Las siguientes aplicaciones se actualizarán: {apps}",
"app_start_restore": "Restaurando aplicación «{app}»…", "app_start_restore": "Restaurando «{app}»…",
"app_start_backup": "Obteniendo archivos para el respaldo de «{app}»…", "app_start_backup": "Obteniendo archivos para el respaldo de «{app}»…",
"app_start_remove": "Eliminando aplicación «{app}»…", "app_start_remove": "Eliminando «{app}»…",
"app_start_install": "Instalando aplicación «{app}»…", "app_start_install": "Instalando «{app}»…",
"app_not_upgraded": "La aplicación '{failed_app}' no se pudo actualizar y, como consecuencia, se cancelaron las actualizaciones de las siguientes aplicaciones: {apps}", "app_not_upgraded": "La aplicación '{failed_app}' no se pudo actualizar y, como consecuencia, se cancelaron las actualizaciones de las siguientes aplicaciones: {apps}",
"app_action_cannot_be_ran_because_required_services_down": "Estos servicios necesarios deberían estar funcionando para ejecutar esta acción: {services}. Pruebe a reiniciarlos para continuar (y posiblemente investigar por qué están caídos).", "app_action_cannot_be_ran_because_required_services_down": "Estos servicios necesarios deberían estar funcionando para ejecutar esta acción: {services}. Pruebe a reiniciarlos para continuar (y posiblemente investigar por qué están caídos).",
"already_up_to_date": "Nada que hacer. Todo está actualizado.", "already_up_to_date": "Nada que hacer. Todo está actualizado.",
@ -509,7 +509,7 @@
"diagnosis_basesystem_ynh_main_version": "El servidor está ejecutando YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_main_version": "El servidor está ejecutando YunoHost {main_version} ({repo})",
"diagnosis_basesystem_ynh_inconsistent_versions": "Está ejecutando versiones inconsistentes de los paquetes de YunoHost ... probablemente debido a una actualización parcial o fallida.", "diagnosis_basesystem_ynh_inconsistent_versions": "Está ejecutando versiones inconsistentes de los paquetes de YunoHost ... probablemente debido a una actualización parcial o fallida.",
"diagnosis_failed_for_category": "Error de diagnóstico para la categoría '{category}': {error}", "diagnosis_failed_for_category": "Error de diagnóstico para la categoría '{category}': {error}",
"diagnosis_cache_still_valid": "(Caché aún válida para el diagnóstico de {category}. ¡Aún no se ha rediagnosticado!)", "diagnosis_cache_still_valid": "(Caché aún válida para el diagnóstico de {category}. ¡No se volvera a comprobar de momento!)",
"diagnosis_found_errors_and_warnings": "¡Encontrado(s) error(es) significativo(s) {errors} (y aviso(s) {warnings}) relacionado(s) con {category}!", "diagnosis_found_errors_and_warnings": "¡Encontrado(s) error(es) significativo(s) {errors} (y aviso(s) {warnings}) relacionado(s) con {category}!",
"diagnosis_display_tip_web": "Puede ir a la sección de diagnóstico (en la pantalla principal) para ver los problemas encontrados.", "diagnosis_display_tip_web": "Puede ir a la sección de diagnóstico (en la pantalla principal) para ver los problemas encontrados.",
"diagnosis_display_tip_cli": "Puede ejecutar «yunohost diagnosis show --issues» para mostrar los problemas encontrados.", "diagnosis_display_tip_cli": "Puede ejecutar «yunohost diagnosis show --issues» para mostrar los problemas encontrados.",
@ -527,7 +527,7 @@
"diagnosis_no_cache": "Todavía no hay una caché de diagnóstico para la categoría '{category}'", "diagnosis_no_cache": "Todavía no hay una caché de diagnóstico para la categoría '{category}'",
"diagnosis_ip_no_ipv4": "El servidor no cuenta con ipv4 funcional.", "diagnosis_ip_no_ipv4": "El servidor no cuenta con ipv4 funcional.",
"diagnosis_ip_not_connected_at_all": "¿¡Está conectado el servidor a internet!?", "diagnosis_ip_not_connected_at_all": "¿¡Está conectado el servidor a internet!?",
"diagnosis_ip_broken_resolvconf": "DNS parece no funcionar en tu servidor, lo que parece estar relacionado con /etc/resolv.conf no apuntando a 127.0.0.1.", "diagnosis_ip_broken_resolvconf": "La resolución de nombres de dominio parece no funcionar en tu servidor, lo que parece estar relacionado con que <code>/etc/resolv.conf</code> no apunta a <code>127.0.0.1</code>.",
"diagnosis_dns_missing_record": "Según la configuración DNS recomendada, deberías añadir un registro DNS\ntipo: {type}\nnombre: {name}\nvalor: {value}", "diagnosis_dns_missing_record": "Según la configuración DNS recomendada, deberías añadir un registro DNS\ntipo: {type}\nnombre: {name}\nvalor: {value}",
"diagnosis_diskusage_low": "El almacenamiento {mountpoint} (en dispositivo {device}) solo tiene {free} ({free_percent}%) de espacio disponible. Ten cuidado.", "diagnosis_diskusage_low": "El almacenamiento {mountpoint} (en dispositivo {device}) solo tiene {free} ({free_percent}%) de espacio disponible. Ten cuidado.",
"diagnosis_services_bad_status_tip": "Puedes intentar reiniciar el servicio, y si no funciona, echar un vistazo a los logs del servicio usando 'yunohost service log {service}' o a través de la sección 'Servicios' en webadmin.", "diagnosis_services_bad_status_tip": "Puedes intentar reiniciar el servicio, y si no funciona, echar un vistazo a los logs del servicio usando 'yunohost service log {service}' o a través de la sección 'Servicios' en webadmin.",
@ -535,11 +535,11 @@
"diagnosis_ip_no_ipv6": "El servidor no cuenta con IPv6 funcional.", "diagnosis_ip_no_ipv6": "El servidor no cuenta con IPv6 funcional.",
"diagnosis_ip_dnsresolution_working": "¡DNS no está funcionando!", "diagnosis_ip_dnsresolution_working": "¡DNS no está funcionando!",
"diagnosis_ip_broken_dnsresolution": "Parece que no funciona la resolución de nombre de dominio por alguna razón... ¿Hay algún firewall bloqueando peticiones DNS?", "diagnosis_ip_broken_dnsresolution": "Parece que no funciona la resolución de nombre de dominio por alguna razón... ¿Hay algún firewall bloqueando peticiones DNS?",
"diagnosis_ip_weird_resolvconf": "Parece que DNS funciona, pero ten cuidado, porque estás utilizando /etc/resolv.conf modificado.", "diagnosis_ip_weird_resolvconf": "La resolución de nombres de dominio DNS funciona, aunque parece que estás utilizando <code>/etc/resolv.conf</code> personalizada.",
"diagnosis_ip_weird_resolvconf_details": "En su lugar, este fichero debería ser un enlace simbólico a /etc/resolvconf/run/resolv.conf apuntando a 127.0.0.1 (dnsmasq). Los servidores de nombre de domino deben configurarse a través de /etc/resolv.dnsmasq.conf.", "diagnosis_ip_weird_resolvconf_details": "El fichero <code>/etc/resolv.conf</code> debería ser un enlace simbólico a <code>/etc/resolvconf/run/resolv.conf</code> a su vez debe apuntar a <code>127.0.0.1</code> (dnsmasq). Si lo que quieres es configurar la resolución DNS manualmente, porfavor modifica <code>/etc/resolv.dnsmasq.conf</code>.",
"diagnosis_dns_good_conf": "Buena configuración DNS para el dominio {domain} (categoría {category})", "diagnosis_dns_good_conf": "La configuración de registros DNS es correcta para {domain} (categoría {category})",
"diagnosis_dns_bad_conf": "Configuración mala o faltante de los DNS para el dominio {domain} (categoría {category})", "diagnosis_dns_bad_conf": "Algunos registros DNS faltan o están mal cofigurados para el dominio {domain} (categoría {category})",
"diagnosis_dns_discrepancy": "El registro DNS con tipo {type} y nombre {name} no se corresponde a la configuración recomendada.\nValor actual: {current}\nValor esperado: {value}", "diagnosis_dns_discrepancy": "El siguiente registro DNS parace que no sigue la configuración recomendada <br>Tipo: <code>{type}</code><br>Nombre: <code>{name}</code><br>Valor Actual: <code>{current}</code><br>Valor esperado: <code>{value}</code>",
"diagnosis_services_bad_status": "El servicio {service} está {status} :(", "diagnosis_services_bad_status": "El servicio {service} está {status} :(",
"diagnosis_diskusage_verylow": "El almacenamiento {mountpoint} (en el dispositivo {device}) sólo tiene {free} ({free_percent}%) de espacio disponible. Deberías considerar la posibilidad de limpiar algo de espacio.", "diagnosis_diskusage_verylow": "El almacenamiento {mountpoint} (en el dispositivo {device}) sólo tiene {free} ({free_percent}%) de espacio disponible. Deberías considerar la posibilidad de limpiar algo de espacio.",
"diagnosis_diskusage_ok": "¡El almacenamiento {mountpoint} (en el dispositivo {device}) todavía tiene {free} ({free_percent}%) de espacio libre!", "diagnosis_diskusage_ok": "¡El almacenamiento {mountpoint} (en el dispositivo {device}) todavía tiene {free} ({free_percent}%) de espacio libre!",
@ -556,8 +556,8 @@
"diagnosis_mail_ougoing_port_25_ok": "El puerto de salida 25 no esta bloqueado y los correos electrónicos pueden ser enviados a otros servidores.", "diagnosis_mail_ougoing_port_25_ok": "El puerto de salida 25 no esta bloqueado y los correos electrónicos pueden ser enviados a otros servidores.",
"diagnosis_mail_outgoing_port_25_blocked": "El puerto de salida 25 parece estar bloqueado. Intenta desbloquearlo con el panel de configuración de tu proveedor de servicios de Internet (o proveedor de halbergue). Mientras tanto, el servidor no podrá enviar correos electrónicos a otros servidores.", "diagnosis_mail_outgoing_port_25_blocked": "El puerto de salida 25 parece estar bloqueado. Intenta desbloquearlo con el panel de configuración de tu proveedor de servicios de Internet (o proveedor de halbergue). Mientras tanto, el servidor no podrá enviar correos electrónicos a otros servidores.",
"diagnosis_regenconf_allgood": "Todos los archivos de configuración están en linea con la configuración recomendada!", "diagnosis_regenconf_allgood": "Todos los archivos de configuración están en linea con la configuración recomendada!",
"diagnosis_regenconf_manually_modified": "El archivo de configuración {file} fue modificado manualmente.", "diagnosis_regenconf_manually_modified": "El archivo de configuración {file} parece que ha sido modificado manualmente.",
"diagnosis_regenconf_manually_modified_details": "Esto este probablemente BIEN siempre y cuando sepas lo que estas haciendo ;) !", "diagnosis_regenconf_manually_modified_details": "¡Esto probablemente esta BIEN si sabes lo que estás haciendo! YunoHost dejará de actualizar este fichero automáticamente... Pero ten en cuenta que las actualizaciones de YunoHost pueden contener importantes cambios que están recomendados. Si quieres puedes comprobar las diferencias mediante <cmd>yunohost tools regen-conf {category} --dry-run --with-diff</cmd> o puedes forzar el volver a las opciones recomendadas mediante el comando <cmd>yunohost tools regen-conf {category} --force</cmd>",
"diagnosis_regenconf_manually_modified_debian": "El archivos de configuración {file} fue modificado manualmente comparado con el valor predeterminado de Debian.", "diagnosis_regenconf_manually_modified_debian": "El archivos de configuración {file} fue modificado manualmente comparado con el valor predeterminado de Debian.",
"diagnosis_regenconf_manually_modified_debian_details": "Esto este probablemente BIEN, pero igual no lo pierdas de vista...", "diagnosis_regenconf_manually_modified_debian_details": "Esto este probablemente BIEN, pero igual no lo pierdas de vista...",
"diagnosis_security_all_good": "Ninguna vulnerabilidad critica de seguridad fue encontrada.", "diagnosis_security_all_good": "Ninguna vulnerabilidad critica de seguridad fue encontrada.",
@ -586,26 +586,26 @@
"log_app_config_apply": "Aplica la configuración de la aplicación '{}'", "log_app_config_apply": "Aplica la configuración de la aplicación '{}'",
"log_app_config_show_panel": "Muestra el panel de configuración de la aplicación '{}'", "log_app_config_show_panel": "Muestra el panel de configuración de la aplicación '{}'",
"log_app_action_run": "Inicializa la acción de la aplicación '{}'", "log_app_action_run": "Inicializa la acción de la aplicación '{}'",
"group_already_exist_on_system_but_removing_it": "El grupo {group} ya existe en el grupo de sistema, pero YunoHost lo suprimirá …", "group_already_exist_on_system_but_removing_it": "El grupo {group} ya existe en los grupos del sistema, pero YunoHost lo suprimirá …",
"global_settings_setting_pop3_enabled": "Habilita el protocolo POP3 para el servidor de correo electrónico", "global_settings_setting_pop3_enabled": "Habilita el protocolo POP3 para el servidor de correo electrónico",
"domain_cannot_remove_main_add_new_one": "No se puede remover '{domain:s}' porque es su principal y único dominio. Primero debe agregar un nuevo dominio con la linea de comando 'yunohost domain add <another-domain.com>', entonces configurarlo como dominio principal con 'yunohost domain main-domain -n <another-domain.com>' y finalmente borrar el dominio '{domain:s}' con 'yunohost domain remove {domain:s}'.'", "domain_cannot_remove_main_add_new_one": "No se puede remover '{domain:s}' porque es su principal y único dominio. Primero debe agregar un nuevo dominio con la linea de comando 'yunohost domain add <another-domain.com>', entonces configurarlo como dominio principal con 'yunohost domain main-domain -n <another-domain.com>' y finalmente borrar el dominio '{domain:s}' con 'yunohost domain remove {domain:s}'.'",
"diagnosis_never_ran_yet": "Este servidor todavía no tiene reportes de diagnostico. Puede iniciar un diagnostico completo desde la interface administrador web o con la linea de comando 'yunohost diagnosis run'.", "diagnosis_never_ran_yet": "Este servidor todavía no tiene reportes de diagnostico. Puede iniciar un diagnostico completo desde la interface administrador web o con la linea de comando 'yunohost diagnosis run'.",
"diagnosis_unknown_categories": "Las siguientes categorías están desconocidas: {categories}", "diagnosis_unknown_categories": "Las siguientes categorías están desconocidas: {categories}",
"diagnosis_http_unreachable": "El dominio {domain} esta fuera de alcance desde internet y a través de HTTP.", "diagnosis_http_unreachable": "El dominio {domain} esta fuera de alcance desde internet y a través de HTTP.",
"diagnosis_http_bad_status_code": "El sistema de diagnostico no pudo comunicarse con su servidor. Puede ser otra maquina que contesto en lugar del servidor. Debería verificar en su firewall que el re-direccionamiento del puerto 80 esta correcto.", "diagnosis_http_bad_status_code": "Parece que otra máquina (quizás el router de conexión a internet) haya respondido en vez de tu servidor.<br>1. La causa más común es que el puerto 80 (y el 443) <a href='https://yunohost.org/isp_box_config'>no hayan sido redirigidos a tu servidor</a>.<br>2. En situaciones más complejas: asegurate de que ni el cortafuegos ni el proxy inverso están interfiriendo.",
"diagnosis_http_connection_error": "Error de conexión: Ne se pudo conectar al dominio solicitado.", "diagnosis_http_connection_error": "Error de conexión: Ne se pudo conectar al dominio solicitado.",
"diagnosis_http_timeout": "El intento de contactar a su servidor desde internet corrió fuera de tiempo. Al parece esta incomunicado. Debería verificar que nginx corre en el puerto 80, y que la redireción del puerto 80 no interfiere con en el firewall.", "diagnosis_http_timeout": "Tiempo de espera agotado al intentar contactar tu servidor desde el exterior. Parece que no sea alcanzable.<br>1. La causa más común es que el puerto 80 (y el 443) <a href='https://yunohost.org/isp_box_config'>no estén correctamente redirigidos a tu servidor</a>.<br>2. Deberías asegurarte que el servicio nginx está en marcha.<br>3. En situaciones más complejas: asegurate de que ni el cortafuegos ni el proxy inverso estén interfiriendo.",
"diagnosis_http_ok": "El Dominio {domain} es accesible desde internet a través de HTTP.", "diagnosis_http_ok": "El Dominio {domain} es accesible desde internet a través de HTTP.",
"diagnosis_http_could_not_diagnose": "No se pudo verificar si el dominio es accesible desde internet.", "diagnosis_http_could_not_diagnose": "No se pudo verificar si el dominio es accesible desde internet.",
"diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_http_could_not_diagnose_details": "Error: {error}",
"diagnosis_ports_forwarding_tip": "Para solucionar este incidente, debería configurar el \"port forwading\" en su router como especificado en https://yunohost.org/isp_box_config", "diagnosis_ports_forwarding_tip": "Para solucionar este incidente, lo más seguro deberías configurar la redirección de los puertos en el router como se especifica en <a href='https://yunohost.org/isp_box_config'>https://yunohost.org/isp_box_config</a>",
"certmanager_warning_subdomain_dns_record": "El subdominio '{subdomain:s}' no se resuelve en la misma dirección IP que '{domain:s}'. Algunas funciones no estarán disponibles hasta que solucione esto y regenere el certificado.", "certmanager_warning_subdomain_dns_record": "El subdominio '{subdomain:s}' no se resuelve en la misma dirección IP que '{domain:s}'. Algunas funciones no estarán disponibles hasta que solucione esto y regenere el certificado.",
"domain_cannot_add_xmpp_upload": "No puede agregar dominios que comiencen con 'xmpp-upload'. Este tipo de nombre está reservado para la función de carga XMPP integrada en YunoHost.", "domain_cannot_add_xmpp_upload": "No puede agregar dominios que comiencen con 'xmpp-upload'. Este tipo de nombre está reservado para la función de carga XMPP integrada en YunoHost.",
"yunohost_postinstall_end_tip": "¡La post-instalación completada! Para finalizar su configuración, considere:\n - agregar un primer usuario a través de la sección 'Usuarios' del webadmin (o 'yunohost user create <username>' en la línea de comandos);\n - diagnostique problemas potenciales a través de la sección 'Diagnóstico' de webadmin (o 'ejecución de diagnóstico yunohost' en la línea de comandos);\n - leyendo las partes 'Finalizando su configuración' y 'Conociendo a Yunohost' en la documentación del administrador: https://yunohost.org/admindoc.", "yunohost_postinstall_end_tip": "¡La post-instalación completada! Para finalizar su configuración, considere:\n - agregar un primer usuario a través de la sección 'Usuarios' del webadmin (o 'yunohost user create <username>' en la línea de comandos);\n - diagnostique problemas potenciales a través de la sección 'Diagnóstico' de webadmin (o 'ejecución de diagnóstico yunohost' en la línea de comandos);\n - leyendo las partes 'Finalizando su configuración' y 'Conociendo a Yunohost' en la documentación del administrador: https://yunohost.org/admindoc.",
"diagnosis_dns_point_to_doc": "Por favor, consulta la documentación en <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si necesitas ayuda para configurar los registros DNS.", "diagnosis_dns_point_to_doc": "Por favor, consulta la documentación en <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si necesitas ayuda para configurar los registros DNS.",
"diagnosis_ip_global": "IP Global: <code>{global}</code>", "diagnosis_ip_global": "IP Global: <code>{global}</code>",
"diagnosis_mail_outgoing_port_25_ok": "El servidor de email SMTP puede mandar emails (puerto saliente 25 no está bloqueado).", "diagnosis_mail_outgoing_port_25_ok": "El servidor de email SMTP puede mandar emails (puerto saliente 25 no está bloqueado).",
"diagnosis_mail_outgoing_port_25_blocked_details": "Deberías intentar desbloquear el puerto 25 saliente en la interfaz de tu router o en la interfaz de tu provedor de hosting. (Algunos hosting pueden necesitar que les abras un ticket de soporte para esto).", "diagnosis_mail_outgoing_port_25_blocked_details": "Primeramente deberías intentar desbloquear el puerto de salida 25 en la interfaz de control de tu router o en la interfaz de tu provedor de hosting. (Algunos hosting pueden necesitar que les abras un ticket de soporte para esto).",
"diagnosis_swap_tip": "Por favor tenga cuidado y sepa que si el servidor contiene swap en una tarjeta SD o un disco duro de estado sólido, esto reducirá drásticamente la vida útil del dispositivo.", "diagnosis_swap_tip": "Por favor tenga cuidado y sepa que si el servidor contiene swap en una tarjeta SD o un disco duro de estado sólido, esto reducirá drásticamente la vida útil del dispositivo.",
"diagnosis_domain_expires_in": "{domain} expira en {days} días.", "diagnosis_domain_expires_in": "{domain} expira en {days} días.",
"diagnosis_domain_expiration_error": "¡Algunos dominios expirarán MUY PRONTO!", "diagnosis_domain_expiration_error": "¡Algunos dominios expirarán MUY PRONTO!",
@ -631,5 +631,62 @@
"app_manifest_install_ask_path": "Seleccione el path donde esta aplicación debería ser instalada", "app_manifest_install_ask_path": "Seleccione el path donde esta aplicación debería ser instalada",
"app_manifest_install_ask_domain": "Seleccione el dominio donde esta app debería ser instalada", "app_manifest_install_ask_domain": "Seleccione el dominio donde esta app debería ser instalada",
"app_label_deprecated": "Este comando está depreciado! Favor usar el nuevo comando 'yunohost user permission update' para administrar la etiqueta de app.", "app_label_deprecated": "Este comando está depreciado! Favor usar el nuevo comando 'yunohost user permission update' para administrar la etiqueta de app.",
"app_argument_password_no_default": "Error al interpretar argumento de contraseña'{name}': El argumento de contraseña no puede tener un valor por defecto por razón de seguridad" "app_argument_password_no_default": "Error al interpretar argumento de contraseña'{name}': El argumento de contraseña no puede tener un valor por defecto por razón de seguridad",
"migration_0015_not_enough_free_space": "¡El espacio es muy bajo en `/var/`! Deberías tener almenos 1Gb de espacio libre para ejecutar la migración.",
"migration_0015_not_stretch": "¡La distribución actual de Debian no es Stretch!",
"migration_0015_yunohost_upgrade": "Iniciando la actualización del núcleo de YunoHost...",
"migration_0015_still_on_stretch_after_main_upgrade": "Algo fue mal durante la actualización principal, el sistema parece que está todavía en Debian Stretch",
"migration_0015_main_upgrade": "Comenzando la actualización principal...",
"migration_0015_patching_sources_list": "Adaptando las sources.lists...",
"migration_0015_start": "Comenzando la migración a Buster",
"migration_description_0019_extend_permissions_features": "Extiende/rehaz el sistema de gestión de permisos de la aplicación",
"migration_description_0018_xtable_to_nftable": "Migra las viejas reglas de tráfico de red al nuevo sistema nftable",
"migration_description_0017_postgresql_9p6_to_11": "Migra las bases de datos de PostgreSQL 9.6 a 11",
"migration_description_0016_php70_to_php73_pools": "Migra el «pool» de ficheros php7.0-fpm a php7.3",
"migration_description_0015_migrate_to_buster": "Actualiza el sistema a Debian Buster y YunoHost 4.x",
"migrating_legacy_permission_settings": "Migrando los antiguos parámetros de permisos...",
"invalid_regex": "Regex no valido: «{regex:s}»",
"global_settings_setting_backup_compress_tar_archives": "Cuando se creen nuevas copias de respaldo, comprimir los archivos (.tar.gz) en lugar de descomprimir los archivos (.tar). N.B.: activar esta opción quiere decir que los archivos serán más pequeños pero que el proceso tardará más y utilizará más CPU.",
"global_settings_setting_smtp_relay_password": "Clave de uso del SMTP",
"global_settings_setting_smtp_relay_user": "Cuenta de uso de SMTP",
"global_settings_setting_smtp_relay_port": "Puerto de envio / relay SMTP",
"global_settings_setting_smtp_relay_host": "El servidor relay de SMTP para enviar correo en lugar de esta instalación YunoHost. Útil si estás en una de estas situaciones: tu puerto 25 esta bloqueado por tu ISP o VPS, si estás en usado una IP marcada como residencial o DUHL, si no puedes configurar un DNS inverso o si el servidor no está directamente expuesto a internet y quieres utilizar otro servidor para enviar correos.",
"global_settings_setting_smtp_allow_ipv6": "Permitir el uso de IPv6 para enviar y recibir correo",
"domain_name_unknown": "Dominio «{domain}» desconocido",
"diagnosis_processes_killed_by_oom_reaper": "Algunos procesos fueron terminados por el sistema recientemente porque se quedó sin memoria. Típicamente es sintoma de falta de memoria o de un proceso que se adjudicó demasiada memoria.<br>Resumen de los procesos terminados:<br>\n{kills_summary}",
"diagnosis_http_nginx_conf_not_up_to_date_details": "Para arreglar este asunto, estudia las diferencias mediante el comando <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> y si te parecen bien aplica los cambios mediante <cmd>yunohost tools regen-conf nginx --force</cmd>.",
"diagnosis_http_nginx_conf_not_up_to_date": "Parece que la configuración nginx de este dominio haya sido modificada manualmente, esto no deja que YunoHost pueda diagnosticar si es accesible mediante HTTP.",
"diagnosis_http_partially_unreachable": "El dominio {domain} parece que no es accesible mediante HTTP desde fuera de la red local mediante IPv{failed}, aunque si que funciona mediante IPv{passed}.",
"diagnosis_http_hairpinning_issue_details": "Esto quizás es debido a tu router o máquina en el ISP. Como resultado, la gente fuera de tu red local podrá acceder a tu servidor como es de esperar, pero no así las persona que estén dentro de la red local (como tu probablemente) o cuando usen el nombre de dominio o la IP global. Quizás puedes mejorar o arreglar esta situación leyendo <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a>",
"diagnosis_http_hairpinning_issue": "Parece que tu red local no tiene la opción hairpinning activada.",
"diagnosis_ports_partially_unreachable": "El port {port} no es accesible desde el exterior mediante IPv{failed}.",
"diagnosis_mail_queue_too_big": "Demasiados correos electrónicos pendientes en la cola ({nb_pending} correos electrónicos)",
"diagnosis_mail_queue_unavailable_details": "Error: {error}",
"diagnosis_mail_queue_unavailable": "No se ha podido consultar el número de correos electrónicos pendientes en la cola",
"diagnosis_mail_queue_ok": "{nb_pending} correos esperando e la cola de correos electrónicos",
"diagnosis_mail_blacklist_website": "Cuando averigües y arregles el motivo por el que aprareces en la lista maligna, no dudes en solicitar que tu IP o dominio sea retirado de la {blacklist_website}",
"diagnosis_mail_blacklist_reason": "El motivo de estar en la lista maligna es: {reason}",
"diagnosis_mail_blacklist_listed_by": "Tu IP o dominio <code>{item}</code> está marcado como maligno en {blacklist_name}",
"diagnosis_mail_blacklist_ok": "Las IP y los dominios utilizados en este servidor no parece que estén en ningún listado maligno (blacklist)",
"diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "El DNS inverso actual es: <code>{rdns_domain}</code><br>Valor esperado: <code>{ehlo_domain}</code>",
"diagnosis_mail_fcrdns_different_from_ehlo_domain": "La resolución de DNS inverso no está correctamente configurada mediante IPv{ipversion}. Algunos correos pueden fallar al ser enviados o pueden ser marcados como basura.",
"diagnosis_mail_fcrdns_nok_alternatives_6": "Algunos proveedores no permiten configurar el DNS inverso (o su funcionalidad puede estar rota...). Si tu DNS inverso está configurado correctamente para IPv4, puedes intentar deshabilitarlo para IPv6 cuando envies correos mediante el comando <cmd>yunohost settings set smtp.allow_ipv6 -v off</cmd>. Nota: esta solución quiere decir que no podrás enviar ni recibir correos con los pocos servidores que utilizan exclusivamente IPv6.",
"diagnosis_mail_fcrdns_nok_alternatives_4": "Algunos proveedores no te permitirán que configures un DNS inverso (o puede que esta opción esté rota...). Si estás sufriendo problemas por este asunto, quizás te sirvan las siguientes soluciones:<br>- Algunos ISP proporcionan una alternativa mediante <a href='https://yunohost.org/#/smtp_relay'>el uso de un relay de servidor de correo</a> aunque esto implica que el relay podrá espiar tu tráfico de correo electrónico.<br>- Una solución amigable con la privacidad es utilizar una VPN con una *IP pública dedicada* para evitar este tipo de limitaciones. Mira en <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a><br>- Quizás tu solución sea <a href='https://yunohost.org/#/isp'>cambiar de proveedor de internet</a>",
"diagnosis_mail_fcrdns_nok_details": "Primero deberías intentar configurar el DNS inverso mediante <code>{ehlo_domain}</code> en la interfaz de internet de tu router o en la de tu proveedor de internet. (Algunos proveedores de internet en ocasiones necesitan que les solicites un ticket de soporte para ello).",
"diagnosis_mail_fcrdns_dns_missing": "No hay definida ninguna DNS inversa mediante IPv{ipversion}. Algunos correos puede que fallen al enviarse o puede que se marquen como basura.",
"diagnosis_mail_fcrdns_ok": "¡Las DNS inversas están bien configuradas!",
"diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}",
"diagnosis_mail_ehlo_could_not_diagnose": "No pudimos diagnosticar si el servidor de correo postfix es accesible desde el exterior utilizando IPv{ipversion}.",
"diagnosis_mail_ehlo_wrong_details": "El EHLO recibido por el diagnosticador remoto de IPv{ipversion} es diferente del dominio de tu servidor.<br>EHLO recibido: <code>{wrong_ehlo}</code><br>EHLO esperado: <code>{right_ehlo}</code><br> La causa más común de este error suele ser que el puerto 25 <a href='https://yunohost.org/isp_box_config'>no está correctamente enrutado hacia tu servidor</a>. Así mismo asegurate que ningún firewall ni reverse-proxy está interfiriendo.",
"diagnosis_mail_ehlo_wrong": "Un servidor diferente de SMTP está respondiendo mediante IPv{ipversion}. Es probable que tu servidor no pueda recibir correos.",
"diagnosis_mail_ehlo_bad_answer_details": "Podría ser debido a otra máquina en lugar de tu servidor.",
"diagnosis_mail_ehlo_bad_answer": "Un servicio que no es SMTP respondió en el puerto 25 mediante IPv{ipversion}",
"diagnosis_mail_ehlo_unreachable_details": "No pudo abrirse la conexión en el puerto 25 de tu servidor mediante IPv{ipversion}. Parece que no se puede contactar.<br>1. La causa más común en estos casos suele ser que el puerto 25 <a href='https://yunohost.org/isp_box_config'>no está correctamente redireccionado a tu servidor</a>.<br>2. También deberías asegurarte que el servicio postfix está en marcha.<br>3. En casos más complejos: asegurate que no estén interfiriendo ni el firewall ni el reverse-proxy.",
"diagnosis_mail_ehlo_unreachable": "El servidor de correo SMTP no puede contactarse desde el exterior mediante IPv{ipversion}. No puede recibir correos",
"diagnosis_mail_ehlo_ok": "¡El servidor de correo SMTP puede contactarse desde el exterior por lo que puede recibir correos!",
"diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Algunos proveedores de internet no le permitirán desbloquear el puerto 25 porque no les importa la Neutralidad de la Red.<br> - Algunos proporcionan una alternativa usando <a href='https://yunohost.org/#/smtp_relay'>un relay como servidor de correo</a> lo que implica que el relay podrá espiar tu trafico de correo.<br>- Una alternativa buena para la privacidad es utilizar una VPN *con una IP pública dedicada* para evitar estas limitaciones. Mira en <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a><br>- Otra alternativa es cambiar de proveedor de inteernet a <a href='https://yunohost.org/#/isp'>uno más amable con la Neutralidad de la Red</a>",
"diagnosis_backports_in_sources_list": "Parece que apt (el gestor de paquetes) está configurado para usar el repositorio backports. A menos que realmente sepas lo que estás haciendo, desaconsejamos absolutamente instalar paquetes desde backports, ya que pueden provocar comportamientos intestables o conflictos en el sistema.",
"diagnosis_basesystem_hardware_model": "El modelo de servidor es {model}",
"additional_urls_already_removed": "La URL adicional «{url:s}» ya se ha eliminado para el permiso «{permission:s}»",
"additional_urls_already_added": "La URL adicional «{url:s}» ya se ha añadido para el permiso «{permission:s}»"
} }

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: {apps}. Veuillez dabord les désinstaller avant de supprimer ce domaine", "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_unknown": "Domaine inconnu", "domain_unknown": "Domaine inconnu",
"done": "Terminé", "done": "Terminé",
"downloading": "Téléchargement en cours …", "downloading": "Téléchargement en cours …",
@ -687,8 +687,10 @@
"invalid_regex": "Regex non valide : '{regex:s}'", "invalid_regex": "Regex non valide : '{regex:s}'",
"domain_name_unknown": "Domaine '{domain}' inconnu", "domain_name_unknown": "Domaine '{domain}' inconnu",
"app_label_deprecated": "Cette commande est obsolète ! Veuillez utiliser la nouvelle commande 'yunohost user permission update' pour gérer l'étiquette de l'application.", "app_label_deprecated": "Cette commande est obsolète ! Veuillez utiliser la nouvelle commande 'yunohost user permission update' pour gérer l'étiquette de l'application.",
"additional_urls_already_removed": "URL supplémentaire '{url:s}' déjà supprimée pour la permission '{permission:s}'", "additional_urls_already_removed": "URL supplémentaire '{url:s}' déjà supprimées pour la permission '{permission:s}'",
"migration_0019_rollback_success": "Retour à l'état antérieur du système.", "migration_0019_rollback_success": "Retour à l'état antérieur du système.",
"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_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."
} }

View file

@ -69,7 +69,7 @@
"domain_dyndns_already_subscribed": "Hai già sottoscritto un dominio DynDNS", "domain_dyndns_already_subscribed": "Hai già sottoscritto un dominio DynDNS",
"domain_dyndns_root_unknown": "Dominio radice DynDNS sconosciuto", "domain_dyndns_root_unknown": "Dominio radice DynDNS sconosciuto",
"domain_hostname_failed": "Impossibile impostare il nuovo hostname. Potrebbe causare problemi in futuro (o anche no).", "domain_hostname_failed": "Impossibile impostare il nuovo hostname. Potrebbe causare problemi in futuro (o anche no).",
"domain_uninstall_app_first": "Queste applicazioni sono già installate su questo dominio: {apps}. Disinstallale prima di procedere alla cancellazione di un dominio", "domain_uninstall_app_first": "Queste applicazioni sono già installate su questo dominio:\n{apps}\n\nDisinstallale eseguendo 'yunohost app remove app_id' o spostale in un altro dominio eseguendo 'yunohost app change-url app_id' prima di procedere alla cancellazione del dominio",
"domain_unknown": "Dominio sconosciuto", "domain_unknown": "Dominio sconosciuto",
"done": "Terminato", "done": "Terminato",
"domains_available": "Domini disponibili:", "domains_available": "Domini disponibili:",
@ -672,5 +672,7 @@
"diagnosis_mail_queue_ok": "{nb_pending} emails in attesa nelle code", "diagnosis_mail_queue_ok": "{nb_pending} emails in attesa nelle code",
"diagnosis_mail_blacklist_website": "Dopo aver identificato il motivo e averlo risolto, sentiti libero di chiedere di rimuovere il tuo IP o dominio da {blacklist_website}", "diagnosis_mail_blacklist_website": "Dopo aver identificato il motivo e averlo risolto, sentiti libero di chiedere di rimuovere il tuo IP o dominio da {blacklist_website}",
"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_basesystem_hardware_model": "Modello server: {model}"
} }

View file

@ -30,16 +30,16 @@ import shutil
import yaml import yaml
import time import time
import re import re
import urlparse import urllib.parse
import subprocess import subprocess
import glob import glob
import urllib import urllib.request, urllib.parse, urllib.error
from collections import OrderedDict from collections import OrderedDict
from moulinette import msignals, m18n, msettings from moulinette import msignals, m18n, msettings
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.network import download_json from moulinette.utils.network import download_json
from moulinette.utils.process import run_commands from moulinette.utils.process import run_commands, check_output
from moulinette.utils.filesystem import read_file, read_json, read_toml, read_yaml, write_to_file, write_to_json, write_to_yaml, chmod, chown, mkdir from moulinette.utils.filesystem import read_file, read_json, read_toml, read_yaml, write_to_file, write_to_json, write_to_yaml, chmod, chown, mkdir
from yunohost.service import service_status, _run_service_command from yunohost.service import service_status, _run_service_command
@ -424,10 +424,7 @@ def app_change_url(operation_logger, app, domain, path):
# grab nginx errors # grab nginx errors
# the "exit 0" is here to avoid check_output to fail because 'nginx -t' # the "exit 0" is here to avoid check_output to fail because 'nginx -t'
# will return != 0 since we are in a failed state # will return != 0 since we are in a failed state
nginx_errors = subprocess.check_output("nginx -t; exit 0", nginx_errors = check_output("nginx -t; exit 0")
stderr=subprocess.STDOUT,
shell=True).rstrip()
raise YunohostError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors) raise YunohostError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors)
logger.success(m18n.n("app_change_url_success", logger.success(m18n.n("app_change_url_success",
@ -747,7 +744,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
# Retrieve arguments list for install script # Retrieve arguments list for install script
args_dict = {} if not args else \ args_dict = {} if not args else \
dict(urlparse.parse_qsl(args, keep_blank_values=True)) dict(urllib.parse.parse_qsl(args, keep_blank_values=True))
args_odict = _parse_args_from_manifest(manifest, 'install', args=args_dict) args_odict = _parse_args_from_manifest(manifest, 'install', args=args_dict)
# Validate domain / path availability for webapps # Validate domain / path availability for webapps
@ -766,7 +763,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
# Also redact the % escaped version of the password that might appear in # Also redact the % escaped version of the password that might appear in
# the 'args' section of metadata (relevant for password with non-alphanumeric char) # the 'args' section of metadata (relevant for password with non-alphanumeric char)
data_to_redact = [value[0] for value in args_odict.values() if value[1] == "password"] data_to_redact = [value[0] for value in args_odict.values() if value[1] == "password"]
data_to_redact += [urllib.quote(data) for data in data_to_redact if urllib.quote(data) != data] data_to_redact += [urllib.parse.quote(data) for data in data_to_redact if urllib.parse.quote(data) != data]
operation_logger.data_to_redact.extend(data_to_redact) operation_logger.data_to_redact.extend(data_to_redact)
operation_logger.related_to = [s for s in operation_logger.related_to if s[0] != "app"] operation_logger.related_to = [s for s in operation_logger.related_to if s[0] != "app"]
@ -841,7 +838,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
logger.error(m18n.n("app_install_failed", app=app_id, error=error)) logger.error(m18n.n("app_install_failed", app=app_id, error=error))
failure_message_with_debug_instructions = operation_logger.error(error) failure_message_with_debug_instructions = operation_logger.error(error)
# Something wrong happened in Yunohost's code (most probably hook_exec) # Something wrong happened in Yunohost's code (most probably hook_exec)
except Exception as e: except Exception:
import traceback import traceback
error = m18n.n('unexpected_error', error="\n" + traceback.format_exc()) error = m18n.n('unexpected_error', error="\n" + traceback.format_exc())
logger.error(m18n.n("app_install_failed", app=app_id, error=error)) logger.error(m18n.n("app_install_failed", app=app_id, error=error))
@ -1410,7 +1407,7 @@ def app_ssowatconf():
write_to_json('/etc/ssowat/conf.json', conf_dict, sort_keys=True, indent=4) write_to_json('/etc/ssowat/conf.json', conf_dict, sort_keys=True, indent=4)
from utils.legacy import translate_legacy_rules_in_ssowant_conf_json_persistent from .utils.legacy import translate_legacy_rules_in_ssowant_conf_json_persistent
translate_legacy_rules_in_ssowant_conf_json_persistent() translate_legacy_rules_in_ssowant_conf_json_persistent()
logger.debug(m18n.n('ssowat_conf_generated')) logger.debug(m18n.n('ssowat_conf_generated'))
@ -1460,7 +1457,7 @@ def app_action_run(operation_logger, app, action, args=None):
action_declaration = actions[action] action_declaration = actions[action]
# Retrieve arguments list for install script # Retrieve arguments list for install script
args_dict = dict(urlparse.parse_qsl(args, keep_blank_values=True)) if args else {} args_dict = dict(urllib.parse.parse_qsl(args, keep_blank_values=True)) if args else {}
args_odict = _parse_args_for_action(actions[action], args=args_dict) args_odict = _parse_args_for_action(actions[action], args=args_dict)
env_dict = _make_environment_for_app_script(app, args=args_odict, args_prefix="ACTION_") env_dict = _make_environment_for_app_script(app, args=args_odict, args_prefix="ACTION_")
@ -1600,7 +1597,7 @@ def app_config_apply(operation_logger, app, args):
"YNH_APP_INSTANCE_NAME": app, "YNH_APP_INSTANCE_NAME": app,
"YNH_APP_INSTANCE_NUMBER": str(app_instance_nb), "YNH_APP_INSTANCE_NUMBER": str(app_instance_nb),
} }
args = dict(urlparse.parse_qsl(args, keep_blank_values=True)) if args else {} args = dict(urllib.parse.parse_qsl(args, keep_blank_values=True)) if args else {}
for tab in config_panel.get("panel", []): for tab in config_panel.get("panel", []):
tab_id = tab["id"] # this makes things easier to debug on crash tab_id = tab["id"] # this makes things easier to debug on crash
@ -1819,8 +1816,7 @@ def _get_app_config_panel(app_id):
"panel": [], "panel": [],
} }
panels = filter(lambda key_value: key_value[0] not in ("name", "version") and isinstance(key_value[1], OrderedDict), panels = [key_value for key_value in toml_config_panel.items() if key_value[0] not in ("name", "version") and isinstance(key_value[1], OrderedDict)]
toml_config_panel.items())
for key, value in panels: for key, value in panels:
panel = { panel = {
@ -1829,8 +1825,7 @@ def _get_app_config_panel(app_id):
"sections": [], "sections": [],
} }
sections = filter(lambda k_v1: k_v1[0] not in ("name",) and isinstance(k_v1[1], OrderedDict), sections = [k_v1 for k_v1 in value.items() if k_v1[0] not in ("name",) and isinstance(k_v1[1], OrderedDict)]
value.items())
for section_key, section_value in sections: for section_key, section_value in sections:
section = { section = {
@ -1839,8 +1834,7 @@ def _get_app_config_panel(app_id):
"options": [], "options": [],
} }
options = filter(lambda k_v: k_v[0] not in ("name",) and isinstance(k_v[1], OrderedDict), options = [k_v for k_v in section_value.items() if k_v[0] not in ("name",) and isinstance(k_v[1], OrderedDict)]
section_value.items())
for option_key, option_value in options: for option_key, option_value in options:
option = dict(option_value) option = dict(option_value)
@ -1878,7 +1872,7 @@ def _get_app_settings(app_id):
settings = yaml.load(f) settings = yaml.load(f)
# If label contains unicode char, this may later trigger issues when building strings... # If label contains unicode char, this may later trigger issues when building strings...
# FIXME: this should be propagated to read_yaml so that this fix applies everywhere I think... # FIXME: this should be propagated to read_yaml so that this fix applies everywhere I think...
settings = {k: _encode_string(v) for k, v in settings.items()} settings = {k: v for k, v in settings.items()}
if app_id == settings['id']: if app_id == settings['id']:
return settings return settings
except (IOError, TypeError, KeyError): except (IOError, TypeError, KeyError):
@ -2144,10 +2138,9 @@ def _get_git_last_commit_hash(repository, reference='HEAD'):
""" """
try: try:
commit = subprocess.check_output( cmd = "git ls-remote --exit-code {0} {1} | awk '{{print $1}}'"\
"git ls-remote --exit-code {0} {1} | awk '{{print $1}}'".format( .format(repository, reference)
repository, reference), commit = check_output(cmd)
shell=True)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
logger.exception("unable to get last commit from %s", repository) logger.exception("unable to get last commit from %s", repository)
raise ValueError("Unable to get last commit with git") raise ValueError("Unable to get last commit with git")
@ -2305,21 +2298,12 @@ def _value_for_locale(values):
for lang in [m18n.locale, m18n.default_locale]: for lang in [m18n.locale, m18n.default_locale]:
try: try:
return _encode_string(values[lang]) return values[lang]
except KeyError: except KeyError:
continue continue
# Fallback to first value # Fallback to first value
return _encode_string(values.values()[0]) return list(values.values())[0]
def _encode_string(value):
"""
Return the string encoded in utf-8 if needed
"""
if isinstance(value, unicode):
return value.encode('utf8')
return value
def _check_manifest_requirements(manifest, app_instance_name): def _check_manifest_requirements(manifest, app_instance_name):
@ -2410,6 +2394,10 @@ class YunoHostArgumentFormatParser(object):
if parsed_question.ask is None: if parsed_question.ask is None:
parsed_question.ask = "Enter value for '%s':" % parsed_question.name parsed_question.ask = "Enter value for '%s':" % parsed_question.name
# Empty value is parsed as empty string
if parsed_question.default == "":
parsed_question.default = None
return parsed_question return parsed_question
@ -2524,10 +2512,10 @@ class BooleanArgumentParser(YunoHostArgumentFormatParser):
if isinstance(question.value, bool): if isinstance(question.value, bool):
return 1 if question.value else 0 return 1 if question.value else 0
if str(question.value).lower() in ["1", "yes", "y"]: if str(question.value).lower() in ["1", "yes", "y", "true"]:
return 1 return 1
if str(question.value).lower() in ["0", "no", "n"]: if str(question.value).lower() in ["0", "no", "n", "false"]:
return 0 return 0
raise YunohostError('app_argument_choice_invalid', name=question.name, raise YunohostError('app_argument_choice_invalid', name=question.name,
@ -2928,7 +2916,7 @@ def _load_apps_catalog():
try: try:
apps_catalog_content = read_json(cache_file) if os.path.exists(cache_file) else None apps_catalog_content = read_json(cache_file) if os.path.exists(cache_file) else None
except Exception as e: except Exception as e:
raise ("Unable to read cache for apps_catalog %s : %s" % (apps_catalog_id, str(e))) raise YunohostError("Unable to read cache for apps_catalog %s : %s" % (cache_file, e), raw_msg=True)
# Check that the version of the data matches version .... # Check that the version of the data matches version ....
# ... otherwise it means we updated yunohost in the meantime # ... otherwise it means we updated yunohost in the meantime
@ -2978,7 +2966,7 @@ def is_true(arg):
""" """
if isinstance(arg, bool): if isinstance(arg, bool):
return arg return arg
elif isinstance(arg, basestring): elif isinstance(arg, str):
return arg.lower() in ['yes', 'true', 'on'] return arg.lower() in ['yes', 'true', 'on']
else: else:
logger.debug('arg should be a boolean or a string, got %r', arg) logger.debug('arg should be a boolean or a string, got %r', arg)

View file

@ -41,6 +41,7 @@ from moulinette import msignals, m18n, msettings
from moulinette.utils import filesystem from moulinette.utils import filesystem
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml
from moulinette.utils.process import check_output
from yunohost.app import ( from yunohost.app import (
app_info, _is_installed, app_info, _is_installed,
@ -176,11 +177,11 @@ class BackupRestoreTargetsManager(object):
or (exclude and isinstance(exclude, list) and not include) or (exclude and isinstance(exclude, list) and not include)
if include: if include:
return [target.encode("Utf-8") for target in self.targets[category] return [target for target in self.targets[category]
if self.results[category][target] in include] if self.results[category][target] in include]
if exclude: if exclude:
return [target.encode("Utf-8") for target in self.targets[category] return [target for target in self.targets[category]
if self.results[category][target] not in exclude] if self.results[category][target] not in exclude]
@ -599,7 +600,7 @@ class BackupManager():
for hook, infos in ret.items() for hook, infos in ret.items()
if any(result["state"] == "failed" for result in infos.values())} if any(result["state"] == "failed" for result in infos.values())}
if ret_succeed.keys() != []: if list(ret_succeed.keys()) != []:
self.system_return = ret_succeed self.system_return = ret_succeed
# Add files from targets (which they put in the CSV) to the list of # Add files from targets (which they put in the CSV) to the list of
@ -884,7 +885,7 @@ class RestoreManager():
End a restore operations by cleaning the working directory and End a restore operations by cleaning the working directory and
regenerate ssowat conf (if some apps were restored) regenerate ssowat conf (if some apps were restored)
""" """
from permission import permission_sync_to_user from .permission import permission_sync_to_user
permission_sync_to_user() permission_sync_to_user()
@ -1644,7 +1645,7 @@ class BackupMethod(object):
try: try:
subprocess.check_call(["mount", "--rbind", src, dest]) subprocess.check_call(["mount", "--rbind", src, dest])
subprocess.check_call(["mount", "-o", "remount,ro,bind", dest]) subprocess.check_call(["mount", "-o", "remount,ro,bind", dest])
except Exception as e: except Exception:
logger.warning(m18n.n("backup_couldnt_bind", src=src, dest=dest)) logger.warning(m18n.n("backup_couldnt_bind", src=src, dest=dest))
# To check if dest is mounted, use /proc/mounts that # To check if dest is mounted, use /proc/mounts that
# escape spaces as \040 # escape spaces as \040
@ -2166,7 +2167,7 @@ def backup_list(with_info=False, human_readable=False):
d[archive] = backup_info(archive, human_readable=human_readable) d[archive] = backup_info(archive, human_readable=human_readable)
except YunohostError as e: except YunohostError as e:
logger.warning(str(e)) logger.warning(str(e))
except Exception as e: except Exception:
import traceback import traceback
logger.warning("Could not check infos for archive %s: %s" % (archive, '\n' + traceback.format_exc())) logger.warning("Could not check infos for archive %s: %s" % (archive, '\n' + traceback.format_exc()))
@ -2387,7 +2388,7 @@ def _recursive_umount(directory):
Args: Args:
directory -- a directory path directory -- a directory path
""" """
mount_lines = subprocess.check_output("mount").split("\n") mount_lines = check_output("mount").split("\n")
points_to_umount = [line.split(" ")[2] points_to_umount = [line.split(" ")[2]
for line in mount_lines for line in mount_lines
@ -2413,8 +2414,8 @@ def disk_usage(path):
# We don't do this in python with os.stat because we don't want # We don't do this in python with os.stat because we don't want
# to follow symlinks # to follow symlinks
du_output = subprocess.check_output(['du', '-sb', path]) du_output = check_output(['du', '-sb', path], shell=False)
return int(du_output.split()[0].decode('utf-8')) return int(du_output.split()[0])
def binary_to_human(n, customary=False): def binary_to_human(n, customary=False):

View file

@ -385,7 +385,7 @@ def certificate_renew(domain_list, force=False, no_checks=False, email=False, st
_fetch_and_enable_new_certificate(domain, staging, no_checks=no_checks) _fetch_and_enable_new_certificate(domain, staging, no_checks=no_checks)
except Exception as e: except Exception as e:
import traceback import traceback
from StringIO import StringIO from io import StringIO
stack = StringIO() stack = StringIO()
traceback.print_exc(file=stack) traceback.print_exc(file=stack)
msg = "Certificate renewing for %s failed !" % (domain) msg = "Certificate renewing for %s failed !" % (domain)
@ -638,7 +638,7 @@ def _get_status(domain):
cert_subject = cert.get_subject().CN cert_subject = cert.get_subject().CN
cert_issuer = cert.get_issuer().CN cert_issuer = cert.get_issuer().CN
organization_name = cert.get_issuer().O organization_name = cert.get_issuer().O
valid_up_to = datetime.strptime(cert.get_notAfter(), "%Y%m%d%H%M%SZ") valid_up_to = datetime.strptime(cert.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%SZ")
days_remaining = (valid_up_to - datetime.utcnow()).days days_remaining = (valid_up_to - datetime.utcnow()).days
if cert_issuer == _name_self_CA(): if cert_issuer == _name_self_CA():
@ -818,11 +818,11 @@ def _regen_dnsmasq_if_needed():
for domainconf in domainsconf: for domainconf in domainsconf:
# Look for the IP, it's in the lines with this format : # Look for the IP, it's in the lines with this format :
# address=/the.domain.tld/11.22.33.44 # host-record=the.domain.tld,11.22.33.44
for line in open(domainconf).readlines(): for line in open(domainconf).readlines():
if not line.startswith("address"): if not line.startswith("host-record"):
continue continue
ip = line.strip().split("/")[2] ip = line.strip().split(",")[-1]
# Compared found IP to current IPv4 / IPv6 # Compared found IP to current IPv4 / IPv6
# IPv6 IPv4 # IPv6 IPv4

View file

@ -98,7 +98,7 @@ class MyMigration(Migration):
# Migrate old settings # Migrate old settings
migrate_legacy_permission_settings() migrate_legacy_permission_settings()
except Exception as e: except Exception:
logger.warn(m18n.n("migration_0019_migration_failed_trying_to_rollback")) logger.warn(m18n.n("migration_0019_migration_failed_trying_to_rollback"))
os.system("systemctl stop slapd") os.system("systemctl stop slapd")
os.system("rm -r /etc/ldap/slapd.d") # To be sure that we don't keep some part of the old config os.system("rm -r /etc/ldap/slapd.d") # To be sure that we don't keep some part of the old config

View file

@ -451,7 +451,7 @@ class Diagnoser():
key = "diagnosis_description_" + id_ key = "diagnosis_description_" + id_
descr = m18n.n(key) descr = m18n.n(key)
# If no description available, fallback to id # If no description available, fallback to id
return descr if descr.decode('utf-8') != key else id_ return descr if descr != key else id_
@staticmethod @staticmethod
def i18n(report, force_remove_html_tags=False): def i18n(report, force_remove_html_tags=False):

View file

@ -62,18 +62,15 @@ def domain_list(exclude_subdomains=False):
result_list.append(domain) result_list.append(domain)
def cmp_domain(domain1, domain2): def cmp_domain(domain):
# Keep the main part of the domain and the extension together # Keep the main part of the domain and the extension together
# eg: this.is.an.example.com -> ['example.com', 'an', 'is', 'this'] # eg: this.is.an.example.com -> ['example.com', 'an', 'is', 'this']
domain1 = domain1.split('.') domain = domain.split('.')
domain2 = domain2.split('.') domain[-1] = domain[-2] + domain.pop()
domain1[-1] = domain1[-2] + domain1.pop() domain = list(reversed(domain))
domain2[-1] = domain2[-2] + domain2.pop() return domain
domain1 = list(reversed(domain1))
domain2 = list(reversed(domain2))
return cmp(domain1, domain2)
result_list = sorted(result_list, cmp_domain) result_list = sorted(result_list, key=cmp_domain)
return { return {
'domains': result_list, 'domains': result_list,

View file

@ -105,7 +105,7 @@ def _dyndns_available(provider, domain):
raise YunohostError('dyndns_could_not_check_available', raise YunohostError('dyndns_could_not_check_available',
domain=domain, provider=provider) domain=domain, provider=provider)
return r == u"Domain %s is available" % domain return r == "Domain %s is available" % domain
@is_unit_operation() @is_unit_operation()

View file

@ -336,7 +336,7 @@ def firewall_upnp(action='status', no_refresh=False):
# Refresh port mapping using UPnP # Refresh port mapping using UPnP
if not no_refresh: if not no_refresh:
upnpc = miniupnpc.UPnP() upnpc = miniupnpc.UPnP(localport=1)
upnpc.discoverdelay = 3000 upnpc.discoverdelay = 3000
# Discover UPnP device(s) # Discover UPnP device(s)

View file

@ -182,7 +182,8 @@ def hook_list(action, list_by='name', show_info=False):
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
for f in os.listdir(folder + action): for f in os.listdir(folder + action):
if f[0] == '.' or f[-1] == '~' or f.endswith(".pyc"): if f[0] == '.' or f[-1] == '~' or f.endswith(".pyc") \
or (f.startswith("__") and f.endswith("__")):
continue continue
path = '%s%s/%s' % (folder, action, f) path = '%s%s/%s' % (folder, action, f)
priority, name = _extract_filename_parts(f) priority, name = _extract_filename_parts(f)
@ -383,9 +384,6 @@ def _hook_exec_bash(path, args, chdir, env, return_format, loggers):
env['YNH_INTERFACE'] = msettings.get('interface') env['YNH_INTERFACE'] = msettings.get('interface')
stdinfo = os.path.join(tempfile.mkdtemp(), "stdinfo")
env['YNH_STDINFO'] = stdinfo
stdreturn = os.path.join(tempfile.mkdtemp(), "stdreturn") stdreturn = os.path.join(tempfile.mkdtemp(), "stdreturn")
with open(stdreturn, 'w') as f: with open(stdreturn, 'w') as f:
f.write('') f.write('')
@ -402,8 +400,8 @@ def _hook_exec_bash(path, args, chdir, env, return_format, loggers):
_env.update(env) _env.update(env)
returncode = call_async_output( returncode = call_async_output(
cmd, loggers, shell=True, cwd=chdir, cmd, loggers, shell=False, cwd=chdir,
stdinfo=stdinfo, env=_env env=_env
) )
raw_content = None raw_content = None

View file

@ -65,8 +65,7 @@ def log_list(limit=None, with_details=False, with_suboperations=False):
operations = {} operations = {}
logs = filter(lambda x: x.endswith(METADATA_FILE_EXT), logs = [x for x in os.listdir(OPERATIONS_PATH) if x.endswith(METADATA_FILE_EXT)]
os.listdir(OPERATIONS_PATH))
logs = list(reversed(sorted(logs))) logs = list(reversed(sorted(logs)))
if limit is not None: if limit is not None:
@ -257,7 +256,7 @@ def log_display(path, number=None, share=False, filter_irrelevant=False, with_su
except Exception: except Exception:
continue continue
if submetadata.get("parent") == base_filename: if submetadata and submetadata.get("parent") == base_filename:
yield { yield {
"name": filename[:-len(METADATA_FILE_EXT)], "name": filename[:-len(METADATA_FILE_EXT)],
"description": _get_description_from_name(filename[:-len(METADATA_FILE_EXT)]), "description": _get_description_from_name(filename[:-len(METADATA_FILE_EXT)]),
@ -337,7 +336,7 @@ def is_unit_operation(entities=['app', 'domain', 'group', 'service', 'user'],
entity_type = entity entity_type = entity
if entity in kwargs and kwargs[entity] is not None: if entity in kwargs and kwargs[entity] is not None:
if isinstance(kwargs[entity], basestring): if isinstance(kwargs[entity], str):
related_to.append((entity_type, kwargs[entity])) related_to.append((entity_type, kwargs[entity]))
else: else:
for x in kwargs[entity]: for x in kwargs[entity]:
@ -596,7 +595,7 @@ class OperationLogger(object):
""" """
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, basestring): 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

View file

@ -106,7 +106,7 @@ def user_permission_list(short=False, full=False, ignore_system_perms=False, abs
infos["label"] = "%s (%s)" % (main_perm_label, infos["label"]) infos["label"] = "%s (%s)" % (main_perm_label, infos["label"])
if short: if short:
permissions = permissions.keys() permissions = list(permissions.keys())
return {'permissions': permissions} return {'permissions': permissions}
@ -668,7 +668,7 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
For example: For example:
re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
We can also have less-trivial regexes like: We can also have less-trivial regexes like:
re:^\/api\/.*|\/scripts\/api.js$ re:^\/api\/.*|\/scripts\/api.js$
""" """

View file

@ -21,7 +21,6 @@
import os import os
import yaml import yaml
import subprocess
import shutil import shutil
import hashlib import hashlib
@ -30,6 +29,7 @@ from datetime import datetime
from moulinette import m18n from moulinette import m18n
from moulinette.utils import log, filesystem from moulinette.utils import log, filesystem
from moulinette.utils.process import check_output
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
@ -654,10 +654,10 @@ def manually_modified_files():
def manually_modified_files_compared_to_debian_default(ignore_handled_by_regenconf=False): def manually_modified_files_compared_to_debian_default(ignore_handled_by_regenconf=False):
# from https://serverfault.com/a/90401 # from https://serverfault.com/a/90401
files = subprocess.check_output("dpkg-query -W -f='${Conffiles}\n' '*' \ files = check_output("dpkg-query -W -f='${Conffiles}\n' '*' \
| awk 'OFS=\" \"{print $2,$1}' \ | awk 'OFS=\" \"{print $2,$1}' \
| md5sum -c 2>/dev/null \ | md5sum -c 2>/dev/null \
| awk -F': ' '$2 !~ /OK/{print $1}'", shell=True) | awk -F': ' '$2 !~ /OK/{print $1}'")
files = files.strip().split("\n") files = files.strip().split("\n")
if ignore_handled_by_regenconf: if ignore_handled_by_regenconf:

View file

@ -35,6 +35,7 @@ from datetime import datetime
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
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
@ -358,7 +359,7 @@ def _get_and_format_service_status(service, infos):
# that mean that we don't have a translation for this string # that mean that we don't have a translation for this string
# that's the only way to test for that for now # that's the only way to test for that for now
# if we don't have it, uses the one provided by systemd # if we don't have it, uses the one provided by systemd
if description.decode('utf-8') == translation_key: if description == translation_key:
description = str(raw_status.get("Description", "")) description = str(raw_status.get("Description", ""))
output = { output = {
@ -489,7 +490,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
raise YunohostError('service_unknown', service=name) raise YunohostError('service_unknown', service=name)
if names is []: if names is []:
names = services.keys() names = list(services.keys())
logger.warning(m18n.n("service_regen_conf_is_deprecated")) logger.warning(m18n.n("service_regen_conf_is_deprecated"))
@ -563,8 +564,7 @@ def _give_lock(action, service, p):
while son_PID == 0 and p.poll() is None: while son_PID == 0 and p.poll() is None:
# Call systemctl to get the PID # Call systemctl to get the PID
# Output of the command is e.g. ControlPID=1234 # Output of the command is e.g. ControlPID=1234
son_PID = subprocess.check_output(cmd_get_son_PID.split()) \ son_PID = check_output(cmd_get_son_PID).split("=")[1]
.strip().split("=")[1]
son_PID = int(son_PID) son_PID = int(son_PID)
time.sleep(1) time.sleep(1)
@ -599,7 +599,7 @@ def _get_services():
# some services are marked as None to remove them from YunoHost # some services are marked as None to remove them from YunoHost
# filter this # filter this
for key, value in services.items(): for key, value in list(services.items()):
if value is None: if value is None:
del services[key] del services[key]
@ -720,7 +720,7 @@ def _get_journalctl_logs(service, number="all"):
services = _get_services() services = _get_services()
systemd_service = services.get(service, {}).get("actual_systemd_service", service) systemd_service = services.get(service, {}).get("actual_systemd_service", service)
try: try:
return subprocess.check_output("journalctl --no-hostname --no-pager -u {0} -n{1}".format(systemd_service, number), shell=True) return check_output("journalctl --no-hostname --no-pager -u {0} -n{1}".format(systemd_service, number))
except: except:
import traceback import traceback
return "error while get services logs from journalctl:\n%s" % traceback.format_exc() return "error while get services logs from journalctl:\n%s" % traceback.format_exc()

View file

@ -29,7 +29,7 @@ def is_boolean(value):
""" """
if isinstance(value, bool): if isinstance(value, bool):
return True, value return True, value
elif isinstance(value, basestring): elif isinstance(value, str):
if str(value).lower() in ['true', 'on', 'yes', 'false', 'off', 'no']: if str(value).lower() in ['true', 'on', 'yes', 'false', 'off', 'no']:
return True, str(value).lower() in ['true', 'on', 'yes'] return True, str(value).lower() in ['true', 'on', 'yes']
else: else:
@ -141,7 +141,7 @@ def settings_set(key, value):
raise YunohostError('global_settings_bad_type_for_setting', setting=key, raise YunohostError('global_settings_bad_type_for_setting', setting=key,
received_type=type(value).__name__, expected_type=key_type) received_type=type(value).__name__, expected_type=key_type)
elif key_type == "string": elif key_type == "string":
if not isinstance(value, basestring): if not isinstance(value, str):
raise YunohostError('global_settings_bad_type_for_setting', setting=key, raise YunohostError('global_settings_bad_type_for_setting', setting=key,
received_type=type(value).__name__, expected_type=key_type) received_type=type(value).__name__, expected_type=key_type)
elif key_type == "enum": elif key_type == "enum":

View file

@ -4,7 +4,7 @@ import pytest
import shutil import shutil
import requests import requests
from conftest import message, raiseYunohostError, get_test_apps_dir from .conftest import message, raiseYunohostError, get_test_apps_dir
from moulinette.utils.filesystem import mkdir from moulinette.utils.filesystem import mkdir

View file

@ -2,7 +2,7 @@ import sys
import pytest import pytest
from mock import patch from mock import patch
from StringIO import StringIO from io import StringIO
from collections import OrderedDict from collections import OrderedDict
from moulinette import msignals from moulinette import msignals
@ -260,6 +260,10 @@ def test_parse_args_in_yunohost_format_password_no_input_optional():
assert _parse_args_in_yunohost_format(answers, questions) == expected_result assert _parse_args_in_yunohost_format(answers, questions) == expected_result
questions = [{"name": "some_password", "type": "password", "optional": True, "default": ""}]
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
def test_parse_args_in_yunohost_format_password_optional_with_input(): def test_parse_args_in_yunohost_format_password_optional_with_input():
questions = [ questions = [
@ -616,6 +620,18 @@ def test_parse_args_in_yunohost_format_boolean_all_yes():
_parse_args_in_yunohost_format({"some_boolean": True}, questions) == _parse_args_in_yunohost_format({"some_boolean": True}, questions) ==
expected_result expected_result
) )
assert (
_parse_args_in_yunohost_format({"some_boolean": "True"}, questions) ==
expected_result
)
assert (
_parse_args_in_yunohost_format({"some_boolean": "TRUE"}, questions) ==
expected_result
)
assert (
_parse_args_in_yunohost_format({"some_boolean": "true"}, questions) ==
expected_result
)
def test_parse_args_in_yunohost_format_boolean_all_no(): def test_parse_args_in_yunohost_format_boolean_all_no():
@ -653,6 +669,18 @@ def test_parse_args_in_yunohost_format_boolean_all_no():
_parse_args_in_yunohost_format({"some_boolean": False}, questions) == _parse_args_in_yunohost_format({"some_boolean": False}, questions) ==
expected_result expected_result
) )
assert (
_parse_args_in_yunohost_format({"some_boolean": "False"}, questions) ==
expected_result
)
assert (
_parse_args_in_yunohost_format({"some_boolean": "FALSE"}, questions) ==
expected_result
)
assert (
_parse_args_in_yunohost_format({"some_boolean": "false"}, questions) ==
expected_result
)
# XXX apparently boolean are always False (0) by default, I'm not sure what to think about that # XXX apparently boolean are always False (0) by default, I'm not sure what to think about that

View file

@ -1,7 +1,7 @@
import pytest import pytest
import os import os
from conftest import get_test_apps_dir from .conftest import get_test_apps_dir
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from yunohost.app import app_install, app_remove, _normalize_domain_path from yunohost.app import app_install, app_remove, _normalize_domain_path

View file

@ -3,7 +3,7 @@ import os
import shutil import shutil
import subprocess import subprocess
from conftest import message, raiseYunohostError, get_test_apps_dir from .conftest import message, raiseYunohostError, get_test_apps_dir
from yunohost.app import app_install, app_remove, app_ssowatconf from yunohost.app import app_install, app_remove, app_ssowatconf
from yunohost.app import _is_installed from yunohost.app import _is_installed
@ -23,8 +23,6 @@ def setup_function(function):
global maindomain global maindomain
maindomain = _get_maindomain() maindomain = _get_maindomain()
print ""
assert backup_test_dependencies_are_met() assert backup_test_dependencies_are_met()
clean_tmp_backup_directory() clean_tmp_backup_directory()
@ -150,7 +148,7 @@ def clean_tmp_backup_directory():
if tmp_backup_directory_is_empty(): if tmp_backup_directory_is_empty():
return return
mount_lines = subprocess.check_output("mount").split("\n") mount_lines = subprocess.check_output("mount").decode().split("\n")
points_to_umount = [line.split(" ")[2] points_to_umount = [line.split(" ")[2]
for line in mount_lines for line in mount_lines
@ -638,6 +636,7 @@ def test_backup_binds_are_readonly(mocker, monkeypatch):
confssh = os.path.join(self.work_dir, "conf/ssh") confssh = os.path.join(self.work_dir, "conf/ssh")
output = subprocess.check_output("touch %s/test 2>&1 || true" % confssh, output = subprocess.check_output("touch %s/test 2>&1 || true" % confssh,
shell=True, env={'LANG': 'en_US.UTF-8'}) shell=True, env={'LANG': 'en_US.UTF-8'})
output = output.decode()
assert "Read-only file system" in output assert "Read-only file system" in output

View file

@ -3,7 +3,7 @@ import time
import requests import requests
import os import os
from conftest import get_test_apps_dir from .conftest import get_test_apps_dir
from yunohost.app import app_install, app_change_url, app_remove, app_map from yunohost.app import app_install, app_change_url, app_remove, app_map
from yunohost.domain import _get_maindomain from yunohost.domain import _get_maindomain

View file

@ -6,7 +6,7 @@ import os
import json import json
import shutil import shutil
from conftest import message, raiseYunohostError, get_test_apps_dir from .conftest import message, raiseYunohostError, get_test_apps_dir
from yunohost.app import app_install, app_upgrade, app_remove, app_change_url, app_map, _installed_apps, APPS_SETTING_PATH, _set_app_settings, _get_app_settings from yunohost.app import app_install, app_upgrade, app_remove, app_change_url, app_map, _installed_apps, APPS_SETTING_PATH, _set_app_settings, _get_app_settings
from yunohost.user import user_list, user_create, user_delete, \ from yunohost.user import user_list, user_create, user_delete, \

View file

@ -1,6 +1,6 @@
import os import os
from conftest import message from .conftest import message
from yunohost.domain import domain_add, domain_remove, domain_list from yunohost.domain import domain_add, domain_remove, domain_list
from yunohost.regenconf import regen_conf, manually_modified_files, _get_conf_hashes, _force_clear_hashes from yunohost.regenconf import regen_conf, manually_modified_files, _get_conf_hashes, _force_clear_hashes

View file

@ -1,6 +1,6 @@
import os import os
from conftest import raiseYunohostError from .conftest import raiseYunohostError
from yunohost.service import _get_services, _save_services, service_status, service_add, service_remove, service_log from yunohost.service import _get_services, _save_services, service_status, service_add, service_remove, service_log

View file

@ -1,6 +1,6 @@
import pytest import pytest
from conftest import message, raiseYunohostError from .conftest import message, raiseYunohostError
from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \
user_group_list, user_group_create, user_group_delete, user_group_update user_group_list, user_group_create, user_group_delete, user_group_update

View file

@ -303,7 +303,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
'/home/yunohost.app' '/home/yunohost.app'
] ]
for folder in filter(lambda x: not os.path.exists(x), folders_to_create): for folder in [x for x in folders_to_create if not os.path.exists(x)]:
os.makedirs(folder) os.makedirs(folder)
# Change folders permissions # Change folders permissions
@ -953,7 +953,7 @@ def _get_migrations_list():
# (in particular, pending migrations / not already ran are not listed # (in particular, pending migrations / not already ran are not listed
states = tools_migrations_state()["migrations"] states = tools_migrations_state()["migrations"]
for migration_file in filter(lambda x: re.match(r"^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)): for migration_file in [x for x in os.listdir(migrations_path) if re.match(r"^\d+_[a-zA-Z0-9_]+\.py$", x)]:
m = _load_migration(migration_file) m = _load_migration(migration_file)
m.state = states.get(m.id, "pending") m.state = states.get(m.id, "pending")
migrations.append(m) migrations.append(m)
@ -972,7 +972,7 @@ def _get_migration_by_name(migration_name):
raise AssertionError("Unable to find migration with name %s" % migration_name) raise AssertionError("Unable to find migration with name %s" % migration_name)
migrations_path = data_migrations.__path__[0] migrations_path = data_migrations.__path__[0]
migrations_found = filter(lambda x: re.match(r"^\d+_%s\.py$" % migration_name, x), os.listdir(migrations_path)) migrations_found = [x for x in os.listdir(migrations_path) if re.match(r"^\d+_%s\.py$" % migration_name, x)]
assert len(migrations_found) == 1, "Unable to find migration with name %s" % migration_name assert len(migrations_found) == 1, "Unable to find migration with name %s" % migration_name

View file

@ -35,6 +35,7 @@ import copy
from moulinette import msignals, msettings, m18n 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 yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from yunohost.service import service_status from yunohost.service import service_status
@ -339,7 +340,13 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
if lastname and firstname: if lastname and firstname:
new_attr_dict['cn'] = new_attr_dict['displayName'] = [firstname + ' ' + lastname] new_attr_dict['cn'] = new_attr_dict['displayName'] = [firstname + ' ' + lastname]
if change_password: # change_password is None if user_update is not called to change the password
if change_password is not None:
# when in the cli interface if the option to change the password is called
# without a specified value, change_password will be set to the const 0.
# In this case we prompt for the new password.
if msettings.get('interface') == 'cli' and not change_password:
change_password = msignals.prompt(m18n.n("ask_password"), True, True)
# Ensure sufficiently complex password # Ensure sufficiently complex password
assert_password_is_strong_enough("user", change_password) assert_password_is_strong_enough("user", change_password)
@ -492,8 +499,7 @@ def user_info(username):
else: else:
try: try:
cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0] cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0]
cmd_result = subprocess.check_output(cmd, stderr=subprocess.STDOUT, cmd_result = check_output(cmd)
shell=True)
except Exception as e: except Exception as e:
cmd_result = "" cmd_result = ""
logger.warning("Failed to fetch quota info ... : %s " % str(e)) logger.warning("Failed to fetch quota info ... : %s " % str(e))
@ -565,7 +571,7 @@ def user_group_list(short=False, full=False, include_primary_groups=True):
groups[name]["permissions"] = [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])] groups[name]["permissions"] = [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])]
if short: if short:
groups = groups.keys() groups = list(groups.keys())
return {'groups': groups} return {'groups': groups}
@ -650,7 +656,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
from yunohost.permission import permission_sync_to_user from yunohost.permission import permission_sync_to_user
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
existing_groups = 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 YunohostError('group_unknown', group=groupname)
@ -658,7 +664,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
# without the force option... # without the force option...
# #
# We also can't delete "all_users" because that's a special group... # We also can't delete "all_users" because that's a special group...
existing_users = 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 YunohostError('group_cannot_be_deleted', group=groupname)
@ -694,7 +700,7 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force=
from yunohost.permission import permission_sync_to_user from yunohost.permission import permission_sync_to_user
from yunohost.utils.ldap import _get_ldap_interface from yunohost.utils.ldap import _get_ldap_interface
existing_users = user_list()['users'].keys() existing_users = list(user_list()['users'].keys())
# Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam') # Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam')
# Those kind of group should only ever contain the user (e.g. sam) and only this one. # Those kind of group should only ever contain the user (e.g. sam) and only this one.

View file

@ -2,7 +2,7 @@ import os
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json, write_to_json, read_yaml from moulinette.utils.filesystem import write_to_json, read_yaml
from yunohost.user import user_list, user_group_create, user_group_update from yunohost.user import user_list, user_group_create, user_group_update
from yunohost.app import app_setting, _installed_apps, _get_app_settings, _set_app_settings from yunohost.app import app_setting, _installed_apps, _get_app_settings, _set_app_settings
@ -22,7 +22,7 @@ class SetupGroupPermissions():
try: try:
objects = ldap.search(target + ",dc=yunohost,dc=org") objects = ldap.search(target + ",dc=yunohost,dc=org")
# ldap search will raise an exception if no corresponding object is found >.> ... # ldap search will raise an exception if no corresponding object is found >.> ...
except Exception as e: except Exception:
logger.debug("%s does not exist, no need to delete it" % target) logger.debug("%s does not exist, no need to delete it" % target)
return return
@ -100,7 +100,7 @@ class SetupGroupPermissions():
url = "/" if domain and path else None url = "/" if domain and path else None
if permission: if permission:
known_users = user_list()["users"].keys() known_users = list(user_list()["users"].keys())
allowed = [user for user in permission.split(',') if user in known_users] allowed = [user for user in permission.split(',') if user in known_users]
else: else:
allowed = ["all_users"] allowed = ["all_users"]
@ -211,10 +211,12 @@ def migrate_legacy_permission_settings(app=None):
def translate_legacy_rules_in_ssowant_conf_json_persistent(): def translate_legacy_rules_in_ssowant_conf_json_persistent():
if not os.path.exists("/etc/ssowat/conf.json.persistent"): persistent_file_name = "/etc/ssowat/conf.json.persistent"
if not os.path.exists(persistent_file_name):
return return
persistent = read_json("/etc/ssowat/conf.json.persistent") # Ugly hack to try not to misarably fail migration
persistent = read_yaml(persistent_file_name)
legacy_rules = [ legacy_rules = [
"skipped_urls", "skipped_urls",
@ -235,7 +237,7 @@ def translate_legacy_rules_in_ssowant_conf_json_persistent():
protected_urls = persistent.get("protected_urls", []) + ["re:" + r for r in persistent.get("protected_regex", [])] protected_urls = persistent.get("protected_urls", []) + ["re:" + r for r in persistent.get("protected_regex", [])]
unprotected_urls = persistent.get("unprotected_urls", []) + ["re:" + r for r in persistent.get("unprotected_regex", [])] unprotected_urls = persistent.get("unprotected_urls", []) + ["re:" + r for r in persistent.get("unprotected_regex", [])]
known_users = user_list()["users"].keys() known_users = list(user_list()["users"].keys())
for legacy_rule in legacy_rules: for legacy_rule in legacy_rules:
if legacy_rule in persistent: if legacy_rule in persistent:
@ -271,6 +273,6 @@ def translate_legacy_rules_in_ssowant_conf_json_persistent():
"uris": protected_urls + persistent["permissions"].get("custom_protected", {}).get("uris", []), "uris": protected_urls + persistent["permissions"].get("custom_protected", {}).get("uris", []),
} }
write_to_json("/etc/ssowat/conf.json.persistent", persistent, sort_keys=True, indent=4) write_to_json(persistent_file_name, persistent, sort_keys=True, indent=4)
logger.warning("Yunohost automatically translated some legacy rules in /etc/ssowat/conf.json.persistent to match the new permission system") logger.warning("Yunohost automatically translated some legacy rules in /etc/ssowat/conf.json.persistent to match the new permission system")

View file

@ -70,7 +70,11 @@ def meets_version_specifier(pkg_name, specifier):
op, req_version = re.search(r'(<<|<=|=|>=|>>) *([\d\.]+)', specifier).groups() op, req_version = re.search(r'(<<|<=|=|>=|>>) *([\d\.]+)', specifier).groups()
req_version = version.parse(req_version) req_version = version.parse(req_version)
# cmp is a python builtin that returns (-1, 0, 1) depending on comparison # Python2 had a builtin that returns (-1, 0, 1) depending on comparison
# c.f. https://stackoverflow.com/a/22490617
def cmp(a, b):
return (a > b) - (a < b)
deb_operators = { deb_operators = {
"<<": lambda v1, v2: cmp(v1, v2) in [-1], "<<": lambda v1, v2: cmp(v1, v2) in [-1],
"<=": lambda v1, v2: cmp(v1, v2) in [-1, 0], "<=": lambda v1, v2: cmp(v1, v2) in [-1, 0],

View file

@ -171,9 +171,9 @@ class PasswordValidator(object):
# Grep the password in the file # Grep the password in the file
# We use '-f -' to feed the pattern (= the password) through # We use '-f -' to feed the pattern (= the password) through
# stdin to avoid it being shown in ps -ef --forest... # stdin to avoid it being shown in ps -ef --forest...
command = "grep -q -f - %s" % MOST_USED_PASSWORDS command = "grep -q -F -f - %s" % MOST_USED_PASSWORDS
p = subprocess.Popen(command.split(), stdin=subprocess.PIPE) p = subprocess.Popen(command.split(), stdin=subprocess.PIPE)
p.communicate(input=password) p.communicate(input=password.encode('utf-8'))
return not bool(p.returncode) return not bool(p.returncode)

View file

@ -1,12 +1,12 @@
[tox] [tox]
envlist = py{27,37}-{lint,invalidcode},py37-black envlist = py37-{lint,invalidcode},py37-black
[testenv] [testenv]
skip_install=True skip_install=True
deps = deps =
py{27,37}-{lint,invalidcode}: flake8 py37-{lint,invalidcode}: flake8
py37-black: black py37-black: black
commands = commands =
py{27,37}-lint: flake8 src doc data tests --ignore E402,E501 --exclude src/yunohost/vendor py37-lint: flake8 src doc data tests --ignore E402,E501 --exclude src/yunohost/vendor
py{27,37}-invalidcode: flake8 src data --exclude src/yunohost/tests,src/yunohost/vendor --select F py37-invalidcode: flake8 src data --exclude src/yunohost/tests,src/yunohost/vendor --select F
py37-black: black --check --diff src doc data tests py37-black: black --check --diff src doc data tests