mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Multiline, file, tags management + prefilled cli
This commit is contained in:
parent
bc725e9768
commit
f1e5309d40
3 changed files with 177 additions and 86 deletions
|
@ -838,8 +838,8 @@ app:
|
||||||
arguments:
|
arguments:
|
||||||
app:
|
app:
|
||||||
help: App name
|
help: App name
|
||||||
panel:
|
key:
|
||||||
help: Select a specific panel
|
help: Select a specific panel, section or a question
|
||||||
nargs: '?'
|
nargs: '?'
|
||||||
-f:
|
-f:
|
||||||
full: --full
|
full: --full
|
||||||
|
|
|
@ -96,51 +96,72 @@ ynh_value_set() {
|
||||||
|
|
||||||
_ynh_panel_get() {
|
_ynh_panel_get() {
|
||||||
# From settings
|
# From settings
|
||||||
local params_sources
|
local lines
|
||||||
params_sources=`python3 << EOL
|
lines=`python3 << EOL
|
||||||
import toml
|
import toml
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
with open("../config_panel.toml", "r") as f:
|
with open("../config_panel.toml", "r") as f:
|
||||||
file_content = f.read()
|
file_content = f.read()
|
||||||
loaded_toml = toml.loads(file_content, _dict=OrderedDict)
|
loaded_toml = toml.loads(file_content, _dict=OrderedDict)
|
||||||
|
|
||||||
for panel_name,panel in loaded_toml.items():
|
for panel_name, panel in loaded_toml.items():
|
||||||
if isinstance(panel, dict):
|
if not isinstance(panel, dict): continue
|
||||||
for section_name, section in panel.items():
|
for section_name, section in panel.items():
|
||||||
if isinstance(section, dict):
|
if not isinstance(section, dict): continue
|
||||||
for name, param in section.items():
|
for name, param in section.items():
|
||||||
if isinstance(param, dict) and param.get('type', 'string') not in ['success', 'info', 'warning', 'danger', 'display_text', 'markdown']:
|
if not isinstance(param, dict):
|
||||||
print("%s=%s" % (name, param.get('source', 'settings')))
|
continue
|
||||||
|
print(';'.join([
|
||||||
|
name,
|
||||||
|
param.get('type', 'string'),
|
||||||
|
param.get('source', 'settings' if param.get('type', 'string') != 'file' else '')
|
||||||
|
]))
|
||||||
EOL
|
EOL
|
||||||
`
|
`
|
||||||
for param_source in $params_sources
|
for line in $lines
|
||||||
do
|
do
|
||||||
local short_setting="$(echo $param_source | cut -d= -f1)"
|
IFS=';' read short_setting type source <<< "$line"
|
||||||
local getter="get__${short_setting}"
|
local getter="get__${short_setting}"
|
||||||
local source="$(echo $param_source | cut -d= -f2)"
|
|
||||||
sources[${short_setting}]="$source"
|
sources[${short_setting}]="$source"
|
||||||
|
types[${short_setting}]="$type"
|
||||||
file_hash[${short_setting}]=""
|
file_hash[${short_setting}]=""
|
||||||
|
formats[${short_setting}]=""
|
||||||
# Get value from getter if exists
|
# Get value from getter if exists
|
||||||
if type -t $getter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
if type -t $getter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
||||||
old[$short_setting]="$($getter)"
|
old[$short_setting]="$($getter)"
|
||||||
|
formats[${short_setting}]="yaml"
|
||||||
|
|
||||||
|
elif [[ "$source" == "" ]] ; then
|
||||||
|
old[$short_setting]="YNH_NULL"
|
||||||
|
|
||||||
# Get value from app settings or from another file
|
# Get value from app settings or from another file
|
||||||
elif [[ "$source" == "settings" ]] || [[ "$source" == *":"* ]] ; then
|
elif [[ "$type" == "file" ]] ; then
|
||||||
|
if [[ "$source" == "settings" ]] ; then
|
||||||
|
ynh_die "File '${short_setting}' can't be stored in settings"
|
||||||
|
fi
|
||||||
|
old[$short_setting]="$(ls $(echo $source | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/) 2> /dev/null || echo YNH_NULL)"
|
||||||
|
file_hash[$short_setting]="true"
|
||||||
|
|
||||||
|
# Get multiline text from settings or from a full file
|
||||||
|
elif [[ "$type" == "text" ]] ; then
|
||||||
|
if [[ "$source" == "settings" ]] ; then
|
||||||
|
old[$short_setting]="$(ynh_app_setting_get $app $short_setting)"
|
||||||
|
elif [[ "$source" == *":"* ]] ; then
|
||||||
|
ynh_die "For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
|
||||||
|
else
|
||||||
|
old[$short_setting]="$(cat $(echo $source | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/) 2> /dev/null || echo YNH_NULL)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get value from a kind of key/value file
|
||||||
|
else
|
||||||
if [[ "$source" == "settings" ]] ; then
|
if [[ "$source" == "settings" ]] ; then
|
||||||
source=":/etc/yunohost/apps/$app/settings.yml"
|
source=":/etc/yunohost/apps/$app/settings.yml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local source_key="$(echo "$source" | cut -d: -f1)"
|
local source_key="$(echo "$source" | cut -d: -f1)"
|
||||||
source_key=${source_key:-$short_setting}
|
source_key=${source_key:-$short_setting}
|
||||||
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
||||||
old[$short_setting]="$(ynh_value_get --file="${source_file}" --key="${source_key}")"
|
old[$short_setting]="$(ynh_value_get --file="${source_file}" --key="${source_key}")"
|
||||||
|
|
||||||
# Specific case for files (all content of the file is the source)
|
|
||||||
else
|
|
||||||
|
|
||||||
old[$short_setting]="$(ls $(echo $source | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/) 2> /dev/null || echo YNH_NULL)"
|
|
||||||
file_hash[$short_setting]="true"
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -152,27 +173,42 @@ _ynh_panel_apply() {
|
||||||
do
|
do
|
||||||
local setter="set__${short_setting}"
|
local setter="set__${short_setting}"
|
||||||
local source="${sources[$short_setting]}"
|
local source="${sources[$short_setting]}"
|
||||||
|
local type="${types[$short_setting]}"
|
||||||
if [ "${changed[$short_setting]}" == "true" ] ; then
|
if [ "${changed[$short_setting]}" == "true" ] ; then
|
||||||
# Apply setter if exists
|
# Apply setter if exists
|
||||||
if type -t $setter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
if type -t $setter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
||||||
$setter
|
$setter
|
||||||
|
|
||||||
# Copy file in right place
|
elif [[ "$source" == "" ]] ; then
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Save in a file
|
||||||
|
elif [[ "$type" == "file" ]] ; then
|
||||||
|
if [[ "$source" == "settings" ]] ; then
|
||||||
|
ynh_die "File '${short_setting}' can't be stored in settings"
|
||||||
|
fi
|
||||||
|
local source_file="$(echo "$source" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
||||||
|
cp "${!short_setting}" "$source_file"
|
||||||
|
|
||||||
|
# Save value in app settings
|
||||||
elif [[ "$source" == "settings" ]] ; then
|
elif [[ "$source" == "settings" ]] ; then
|
||||||
ynh_app_setting_set $app $short_setting "${!short_setting}"
|
ynh_app_setting_set $app $short_setting "${!short_setting}"
|
||||||
|
|
||||||
# Get value from a kind of key/value file
|
# Save multiline text in a file
|
||||||
elif [[ "$source" == *":"* ]]
|
elif [[ "$type" == "text" ]] ; then
|
||||||
then
|
if [[ "$source" == *":"* ]] ; then
|
||||||
|
ynh_die "For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
|
||||||
|
fi
|
||||||
|
local source_file="$(echo "$source" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
||||||
|
echo "${!short_setting}" > "$source_file"
|
||||||
|
|
||||||
|
# Set value into a kind of key/value file
|
||||||
|
else
|
||||||
local source_key="$(echo "$source" | cut -d: -f1)"
|
local source_key="$(echo "$source" | cut -d: -f1)"
|
||||||
source_key=${source_key:-$short_setting}
|
source_key=${source_key:-$short_setting}
|
||||||
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
||||||
ynh_value_set --file="${source_file}" --key="${source_key}" --value="${!short_setting}"
|
ynh_value_set --file="${source_file}" --key="${source_key}" --value="${!short_setting}"
|
||||||
|
|
||||||
# Specific case for files (all content of the file is the source)
|
|
||||||
else
|
|
||||||
local source_file="$(echo "$source" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
|
||||||
cp "${!short_setting}" "$source_file"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -182,7 +218,13 @@ _ynh_panel_show() {
|
||||||
for short_setting in "${!old[@]}"
|
for short_setting in "${!old[@]}"
|
||||||
do
|
do
|
||||||
if [[ "${old[$short_setting]}" != YNH_NULL ]] ; then
|
if [[ "${old[$short_setting]}" != YNH_NULL ]] ; then
|
||||||
ynh_return "${short_setting}: \"${old[$short_setting]}\""
|
if [[ "${formats[$short_setting]}" == "yaml" ]] ; then
|
||||||
|
ynh_return "${short_setting}:"
|
||||||
|
ynh_return "$(echo "${old[$short_setting]}" | sed 's/^/ /g')"
|
||||||
|
else
|
||||||
|
ynh_return "${short_setting}: \"$(echo "${old[$short_setting]}" | sed ':a;N;$!ba;s/\n/\n\n/g')\""
|
||||||
|
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
@ -225,7 +267,8 @@ _ynh_panel_validate() {
|
||||||
done
|
done
|
||||||
if [[ "$is_error" == "true" ]]
|
if [[ "$is_error" == "true" ]]
|
||||||
then
|
then
|
||||||
ynh_die "Nothing has changed"
|
ynh_print_info "Nothing has changed"
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run validation if something is changed
|
# Run validation if something is changed
|
||||||
|
@ -241,7 +284,7 @@ _ynh_panel_validate() {
|
||||||
if [ -n "$result" ]
|
if [ -n "$result" ]
|
||||||
then
|
then
|
||||||
local key="YNH_ERROR_${short_setting}"
|
local key="YNH_ERROR_${short_setting}"
|
||||||
ynh_return "$key: $result"
|
ynh_return "$key: \"$result\""
|
||||||
is_error=true
|
is_error=true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -274,6 +317,9 @@ ynh_panel_run() {
|
||||||
declare -Ag changed=()
|
declare -Ag changed=()
|
||||||
declare -Ag file_hash=()
|
declare -Ag file_hash=()
|
||||||
declare -Ag sources=()
|
declare -Ag sources=()
|
||||||
|
declare -Ag types=()
|
||||||
|
declare -Ag formats=()
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
show)
|
show)
|
||||||
ynh_panel_get
|
ynh_panel_get
|
||||||
|
@ -281,12 +327,12 @@ ynh_panel_run() {
|
||||||
;;
|
;;
|
||||||
apply)
|
apply)
|
||||||
max_progression=4
|
max_progression=4
|
||||||
ynh_script_progression --message="Reading config panel description and current configuration..." --weight=1
|
ynh_script_progression --message="Reading config panel description and current configuration..."
|
||||||
ynh_panel_get
|
ynh_panel_get
|
||||||
|
|
||||||
ynh_panel_validate
|
ynh_panel_validate
|
||||||
|
|
||||||
ynh_script_progression --message="Applying the new configuration..." --weight=1
|
ynh_script_progression --message="Applying the new configuration..."
|
||||||
ynh_panel_apply
|
ynh_panel_apply
|
||||||
ynh_script_progression --message="Configuration of $app completed" --last
|
ynh_script_progression --message="Configuration of $app completed" --last
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -35,6 +35,7 @@ import glob
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import base64
|
import base64
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import readline
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from moulinette import msignals, m18n, msettings
|
from moulinette import msignals, m18n, msettings
|
||||||
|
@ -1754,36 +1755,21 @@ def app_action_run(operation_logger, app, action, args=None):
|
||||||
# * docstrings
|
# * docstrings
|
||||||
# * merge translations on the json once the workflow is in place
|
# * merge translations on the json once the workflow is in place
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def app_config_show(operation_logger, app, panel='', full=False):
|
def app_config_show(operation_logger, app, key='', full=False):
|
||||||
# logger.warning(m18n.n("experimental_feature"))
|
# logger.warning(m18n.n("experimental_feature"))
|
||||||
|
|
||||||
# Check app is installed
|
# Check app is installed
|
||||||
_assert_is_installed(app)
|
_assert_is_installed(app)
|
||||||
|
|
||||||
panel = panel if panel else ''
|
key = key if key else ''
|
||||||
operation_logger.start()
|
|
||||||
|
|
||||||
# Read config panel toml
|
# Read config panel toml
|
||||||
config_panel = _get_app_config_panel(app, filter_key=panel)
|
config_panel = _get_app_hydrated_config_panel(operation_logger,
|
||||||
|
app, filter_key=key)
|
||||||
|
|
||||||
if not config_panel:
|
if not config_panel:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Call config script to extract current values
|
|
||||||
parsed_values = _call_config_script(operation_logger, app, 'show')
|
|
||||||
|
|
||||||
# # Check and transform values if needed
|
|
||||||
# options = [option for _, _, option in _get_options_iterator(config_panel)]
|
|
||||||
# args_dict = _parse_args_in_yunohost_format(
|
|
||||||
# parsed_values, options, False
|
|
||||||
# )
|
|
||||||
|
|
||||||
# Hydrate
|
|
||||||
logger.debug("Hydrating config with current value")
|
|
||||||
for _, _, option in _get_options_iterator(config_panel):
|
|
||||||
if option['name'] in parsed_values:
|
|
||||||
option["value"] = parsed_values[option['name']] #args_dict[option["name"]][0]
|
|
||||||
|
|
||||||
# Format result in full or reduce mode
|
# Format result in full or reduce mode
|
||||||
if full:
|
if full:
|
||||||
operation_logger.success()
|
operation_logger.success()
|
||||||
|
@ -1800,8 +1786,8 @@ def app_config_show(operation_logger, app, panel='', full=False):
|
||||||
}
|
}
|
||||||
if not option.get('optional', False):
|
if not option.get('optional', False):
|
||||||
r_option['ask'] += ' *'
|
r_option['ask'] += ' *'
|
||||||
if option.get('value', None) is not None:
|
if option.get('current_value', None) is not None:
|
||||||
r_option['value'] = option['value']
|
r_option['value'] = option['current_value']
|
||||||
|
|
||||||
operation_logger.success()
|
operation_logger.success()
|
||||||
return result
|
return result
|
||||||
|
@ -1812,7 +1798,6 @@ def app_config_get(operation_logger, app, key):
|
||||||
# Check app is installed
|
# Check app is installed
|
||||||
_assert_is_installed(app)
|
_assert_is_installed(app)
|
||||||
|
|
||||||
operation_logger.start()
|
|
||||||
|
|
||||||
# Read config panel toml
|
# Read config panel toml
|
||||||
config_panel = _get_app_config_panel(app, filter_key=key)
|
config_panel = _get_app_config_panel(app, filter_key=key)
|
||||||
|
@ -1820,6 +1805,8 @@ def app_config_get(operation_logger, app, key):
|
||||||
if not config_panel:
|
if not config_panel:
|
||||||
raise YunohostError("app_config_no_panel")
|
raise YunohostError("app_config_no_panel")
|
||||||
|
|
||||||
|
operation_logger.start()
|
||||||
|
|
||||||
# Call config script to extract current values
|
# Call config script to extract current values
|
||||||
parsed_values = _call_config_script(operation_logger, app, 'show')
|
parsed_values = _call_config_script(operation_logger, app, 'show')
|
||||||
|
|
||||||
|
@ -1851,7 +1838,8 @@ def app_config_set(operation_logger, app, key=None, value=None, args=None):
|
||||||
filter_key = key if key else ''
|
filter_key = key if key else ''
|
||||||
|
|
||||||
# Read config panel toml
|
# Read config panel toml
|
||||||
config_panel = _get_app_config_panel(app, filter_key=filter_key)
|
config_panel = _get_app_hydrated_config_panel(operation_logger,
|
||||||
|
app, filter_key=filter_key)
|
||||||
|
|
||||||
if not config_panel:
|
if not config_panel:
|
||||||
raise YunohostError("app_config_no_panel")
|
raise YunohostError("app_config_no_panel")
|
||||||
|
@ -1862,12 +1850,16 @@ def app_config_set(operation_logger, app, key=None, value=None, args=None):
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
# Prepare pre answered questions
|
# Prepare pre answered questions
|
||||||
args = dict(urllib.parse.parse_qsl(args, keep_blank_values=True)) if args else {}
|
if args:
|
||||||
|
args = { key: ','.join(value) for key, value in urllib.parse.parse_qs(args, keep_blank_values=True).items() }
|
||||||
|
else:
|
||||||
|
args = {}
|
||||||
if value is not None:
|
if value is not None:
|
||||||
args = {filter_key.split('.')[-1]: value}
|
args = {filter_key.split('.')[-1]: value}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.debug("Asking unanswered question and prevalidating...")
|
logger.debug("Asking unanswered question and prevalidating...")
|
||||||
|
args_dict = {}
|
||||||
for panel in config_panel.get("panel", []):
|
for panel in config_panel.get("panel", []):
|
||||||
if msettings.get('interface') == 'cli' and len(filter_key.split('.')) < 3:
|
if msettings.get('interface') == 'cli' and len(filter_key.split('.')) < 3:
|
||||||
msignals.display(colorize("\n" + "=" * 40, 'purple'))
|
msignals.display(colorize("\n" + "=" * 40, 'purple'))
|
||||||
|
@ -1878,13 +1870,13 @@ def app_config_set(operation_logger, app, key=None, value=None, args=None):
|
||||||
msignals.display(colorize(f"\n# {section['name']}", 'purple'))
|
msignals.display(colorize(f"\n# {section['name']}", 'purple'))
|
||||||
|
|
||||||
# Check and ask unanswered questions
|
# Check and ask unanswered questions
|
||||||
args_dict = _parse_args_in_yunohost_format(
|
args_dict.update(_parse_args_in_yunohost_format(
|
||||||
args, section['options']
|
args, section['options']
|
||||||
)
|
))
|
||||||
|
|
||||||
# Call config script to extract current values
|
# Call config script to extract current values
|
||||||
logger.info("Running config script...")
|
logger.info("Running config script...")
|
||||||
env = {key: str(value[0]) for key, value in args_dict.items()}
|
env = {key: str(value[0]) for key, value in args_dict.items() if not value[0] is None}
|
||||||
|
|
||||||
errors = _call_config_script(operation_logger, app, 'apply', env=env)
|
errors = _call_config_script(operation_logger, app, 'apply', env=env)
|
||||||
# Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception
|
# Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception
|
||||||
|
@ -2246,6 +2238,37 @@ def _get_app_config_panel(app_id, filter_key=''):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _get_app_hydrated_config_panel(operation_logger, app, filter_key=''):
|
||||||
|
|
||||||
|
# Read config panel toml
|
||||||
|
config_panel = _get_app_config_panel(app, filter_key=filter_key)
|
||||||
|
|
||||||
|
if not config_panel:
|
||||||
|
return None
|
||||||
|
|
||||||
|
operation_logger.start()
|
||||||
|
|
||||||
|
# Call config script to extract current values
|
||||||
|
parsed_values = _call_config_script(operation_logger, app, 'show')
|
||||||
|
|
||||||
|
# # Check and transform values if needed
|
||||||
|
# options = [option for _, _, option in _get_options_iterator(config_panel)]
|
||||||
|
# args_dict = _parse_args_in_yunohost_format(
|
||||||
|
# parsed_values, options, False
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Hydrate
|
||||||
|
logger.debug("Hydrating config with current value")
|
||||||
|
for _, _, option in _get_options_iterator(config_panel):
|
||||||
|
if option['name'] in parsed_values:
|
||||||
|
value = parsed_values[option['name']]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
option.update(value)
|
||||||
|
else:
|
||||||
|
option["current_value"] = value #args_dict[option["name"]][0]
|
||||||
|
|
||||||
|
return config_panel
|
||||||
|
|
||||||
|
|
||||||
def _get_app_settings(app_id):
|
def _get_app_settings(app_id):
|
||||||
"""
|
"""
|
||||||
|
@ -2808,6 +2831,7 @@ class YunoHostArgumentFormatParser(object):
|
||||||
parsed_question.name = question["name"]
|
parsed_question.name = question["name"]
|
||||||
parsed_question.type = question.get("type", 'string')
|
parsed_question.type = question.get("type", 'string')
|
||||||
parsed_question.default = question.get("default", None)
|
parsed_question.default = question.get("default", None)
|
||||||
|
parsed_question.current_value = question.get("current_value")
|
||||||
parsed_question.optional = question.get("optional", False)
|
parsed_question.optional = question.get("optional", False)
|
||||||
parsed_question.choices = question.get("choices", [])
|
parsed_question.choices = question.get("choices", [])
|
||||||
parsed_question.pattern = question.get("pattern")
|
parsed_question.pattern = question.get("pattern")
|
||||||
|
@ -2835,11 +2859,20 @@ class YunoHostArgumentFormatParser(object):
|
||||||
msignals.display(text_for_user_input_in_cli)
|
msignals.display(text_for_user_input_in_cli)
|
||||||
|
|
||||||
elif question.value is None:
|
elif question.value is None:
|
||||||
question.value = msignals.prompt(
|
prefill = None
|
||||||
message=text_for_user_input_in_cli,
|
if question.current_value is not None:
|
||||||
is_password=self.hide_user_input_in_prompt,
|
prefill = question.current_value
|
||||||
confirm=self.hide_user_input_in_prompt
|
elif question.default is not None:
|
||||||
)
|
prefill = question.default
|
||||||
|
readline.set_startup_hook(lambda: readline.insert_text(prefill))
|
||||||
|
try:
|
||||||
|
question.value = msignals.prompt(
|
||||||
|
message=text_for_user_input_in_cli,
|
||||||
|
is_password=self.hide_user_input_in_prompt,
|
||||||
|
confirm=self.hide_user_input_in_prompt
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
readline.set_startup_hook()
|
||||||
|
|
||||||
|
|
||||||
# Apply default value
|
# Apply default value
|
||||||
|
@ -2897,8 +2930,6 @@ class YunoHostArgumentFormatParser(object):
|
||||||
if question.choices:
|
if question.choices:
|
||||||
text_for_user_input_in_cli += " [{0}]".format(" | ".join(question.choices))
|
text_for_user_input_in_cli += " [{0}]".format(" | ".join(question.choices))
|
||||||
|
|
||||||
if question.default is not None:
|
|
||||||
text_for_user_input_in_cli += " (default: {0})".format(question.default)
|
|
||||||
if question.help or question.helpLink:
|
if question.help or question.helpLink:
|
||||||
text_for_user_input_in_cli += ":\033[m"
|
text_for_user_input_in_cli += ":\033[m"
|
||||||
if question.help:
|
if question.help:
|
||||||
|
@ -2919,6 +2950,18 @@ class StringArgumentParser(YunoHostArgumentFormatParser):
|
||||||
default_value = ""
|
default_value = ""
|
||||||
|
|
||||||
|
|
||||||
|
class TagsArgumentParser(YunoHostArgumentFormatParser):
|
||||||
|
argument_type = "tags"
|
||||||
|
|
||||||
|
def _prevalidate(self, question):
|
||||||
|
values = question.value
|
||||||
|
for value in values.split(','):
|
||||||
|
question.value = value
|
||||||
|
super()._prevalidate(question)
|
||||||
|
question.value = values
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordArgumentParser(YunoHostArgumentFormatParser):
|
class PasswordArgumentParser(YunoHostArgumentFormatParser):
|
||||||
hide_user_input_in_prompt = True
|
hide_user_input_in_prompt = True
|
||||||
argument_type = "password"
|
argument_type = "password"
|
||||||
|
@ -2938,13 +2981,15 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser):
|
||||||
return question
|
return question
|
||||||
|
|
||||||
def _prevalidate(self, question):
|
def _prevalidate(self, question):
|
||||||
if any(char in question.value for char in self.forbidden_chars):
|
super()._prevalidate(question)
|
||||||
raise YunohostValidationError(
|
|
||||||
"pattern_password_app", forbidden_chars=self.forbidden_chars
|
|
||||||
)
|
|
||||||
|
|
||||||
# If it's an optional argument the value should be empty or strong enough
|
if question.value is not None:
|
||||||
if not question.optional or question.value:
|
if any(char in question.value for char in self.forbidden_chars):
|
||||||
|
raise YunohostValidationError(
|
||||||
|
"pattern_password_app", forbidden_chars=self.forbidden_chars
|
||||||
|
)
|
||||||
|
|
||||||
|
# If it's an optional argument the value should be empty or strong enough
|
||||||
from yunohost.utils.password import assert_password_is_strong_enough
|
from yunohost.utils.password import assert_password_is_strong_enough
|
||||||
|
|
||||||
assert_password_is_strong_enough("user", question.value)
|
assert_password_is_strong_enough("user", question.value)
|
||||||
|
@ -3098,23 +3143,26 @@ class DisplayTextArgumentParser(YunoHostArgumentFormatParser):
|
||||||
readonly = True
|
readonly = True
|
||||||
|
|
||||||
def parse_question(self, question, user_answers):
|
def parse_question(self, question, user_answers):
|
||||||
question = super(DisplayTextArgumentParser, self).parse_question(
|
question_parsed = super().parse_question(
|
||||||
question, user_answers
|
question, user_answers
|
||||||
)
|
)
|
||||||
|
|
||||||
question.optional = True
|
question_parsed.optional = True
|
||||||
|
question_parsed.style = question.get('style', 'info')
|
||||||
|
|
||||||
return question
|
return question_parsed
|
||||||
|
|
||||||
def _format_text_for_user_input_in_cli(self, question):
|
def _format_text_for_user_input_in_cli(self, question):
|
||||||
text = question.ask['en']
|
text = question.ask['en']
|
||||||
if question.type in ['info', 'warning', 'danger']:
|
|
||||||
|
if question.style in ['success', 'info', 'warning', 'danger']:
|
||||||
color = {
|
color = {
|
||||||
|
'success': 'green',
|
||||||
'info': 'cyan',
|
'info': 'cyan',
|
||||||
'warning': 'yellow',
|
'warning': 'yellow',
|
||||||
'danger': 'red'
|
'danger': 'red'
|
||||||
}
|
}
|
||||||
return colorize(m18n.g(question.type), color[question.type]) + f" {text}"
|
return colorize(m18n.g(question.style), color[question.style]) + f" {text}"
|
||||||
else:
|
else:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -3137,7 +3185,7 @@ class FileArgumentParser(YunoHostArgumentFormatParser):
|
||||||
if question.get('accept'):
|
if question.get('accept'):
|
||||||
question_parsed.accept = question.get('accept').replace(' ', '').split(',')
|
question_parsed.accept = question.get('accept').replace(' ', '').split(',')
|
||||||
else:
|
else:
|
||||||
question.accept = []
|
question_parsed.accept = []
|
||||||
if msettings.get('interface') == 'api':
|
if msettings.get('interface') == 'api':
|
||||||
if user_answers.get(question_parsed.name):
|
if user_answers.get(question_parsed.name):
|
||||||
question_parsed.value = {
|
question_parsed.value = {
|
||||||
|
@ -3200,7 +3248,7 @@ ARGUMENTS_TYPE_PARSERS = {
|
||||||
"string": StringArgumentParser,
|
"string": StringArgumentParser,
|
||||||
"text": StringArgumentParser,
|
"text": StringArgumentParser,
|
||||||
"select": StringArgumentParser,
|
"select": StringArgumentParser,
|
||||||
"tags": StringArgumentParser,
|
"tags": TagsArgumentParser,
|
||||||
"email": StringArgumentParser,
|
"email": StringArgumentParser,
|
||||||
"url": StringArgumentParser,
|
"url": StringArgumentParser,
|
||||||
"date": StringArgumentParser,
|
"date": StringArgumentParser,
|
||||||
|
@ -3214,10 +3262,7 @@ ARGUMENTS_TYPE_PARSERS = {
|
||||||
"number": NumberArgumentParser,
|
"number": NumberArgumentParser,
|
||||||
"range": NumberArgumentParser,
|
"range": NumberArgumentParser,
|
||||||
"display_text": DisplayTextArgumentParser,
|
"display_text": DisplayTextArgumentParser,
|
||||||
"success": DisplayTextArgumentParser,
|
"alert": DisplayTextArgumentParser,
|
||||||
"danger": DisplayTextArgumentParser,
|
|
||||||
"warning": DisplayTextArgumentParser,
|
|
||||||
"info": DisplayTextArgumentParser,
|
|
||||||
"markdown": DisplayTextArgumentParser,
|
"markdown": DisplayTextArgumentParser,
|
||||||
"file": FileArgumentParser,
|
"file": FileArgumentParser,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue