mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge pull request #1406 from Tagadda/enh-domain-default-app
Manage default application with DomainConfigPanel
This commit is contained in:
commit
981c7b5649
7 changed files with 127 additions and 31 deletions
|
@ -310,6 +310,7 @@
|
||||||
"domain_config_auth_key": "Authentication key",
|
"domain_config_auth_key": "Authentication key",
|
||||||
"domain_config_auth_secret": "Authentication secret",
|
"domain_config_auth_secret": "Authentication secret",
|
||||||
"domain_config_auth_token": "Authentication token",
|
"domain_config_auth_token": "Authentication token",
|
||||||
|
"domain_config_default_app": "Default app",
|
||||||
"domain_config_features_disclaimer": "So far, enabling/disabling mail or XMPP features only impact the recommended and automatic DNS configuration, not system configurations!",
|
"domain_config_features_disclaimer": "So far, enabling/disabling mail or XMPP features only impact the recommended and automatic DNS configuration, not system configurations!",
|
||||||
"domain_config_mail_in": "Incoming emails",
|
"domain_config_mail_in": "Incoming emails",
|
||||||
"domain_config_mail_out": "Outgoing emails",
|
"domain_config_mail_out": "Outgoing emails",
|
||||||
|
|
|
@ -896,6 +896,10 @@ app:
|
||||||
-d:
|
-d:
|
||||||
full: --domain
|
full: --domain
|
||||||
help: Specific domain to put app on (the app domain by default)
|
help: Specific domain to put app on (the app domain by default)
|
||||||
|
-u:
|
||||||
|
full: --undo
|
||||||
|
help: Undo redirection
|
||||||
|
action: store_true
|
||||||
|
|
||||||
### app_ssowatconf()
|
### app_ssowatconf()
|
||||||
ssowatconf:
|
ssowatconf:
|
||||||
|
|
|
@ -11,6 +11,11 @@ i18n = "domain_config"
|
||||||
#
|
#
|
||||||
|
|
||||||
[feature]
|
[feature]
|
||||||
|
[feature.app]
|
||||||
|
[feature.app.default_app]
|
||||||
|
type = "app"
|
||||||
|
filters = ["is_webapp"]
|
||||||
|
default = "_none"
|
||||||
|
|
||||||
[feature.mail]
|
[feature.mail]
|
||||||
#services = ['postfix', 'dovecot']
|
#services = ['postfix', 'dovecot']
|
||||||
|
|
61
src/app.py
61
src/app.py
|
@ -117,6 +117,7 @@ def app_info(app, full=False):
|
||||||
Get info for a specific app
|
Get info for a specific app
|
||||||
"""
|
"""
|
||||||
from yunohost.permission import user_permission_list
|
from yunohost.permission import user_permission_list
|
||||||
|
from yunohost.domain import domain_config_get
|
||||||
|
|
||||||
_assert_is_installed(app)
|
_assert_is_installed(app)
|
||||||
|
|
||||||
|
@ -153,6 +154,9 @@ def app_info(app, full=False):
|
||||||
|
|
||||||
ret["is_webapp"] = "domain" in settings and "path" in settings
|
ret["is_webapp"] = "domain" in settings and "path" in settings
|
||||||
|
|
||||||
|
if ret["is_webapp"]:
|
||||||
|
ret["is_default"] = domain_config_get(settings["domain"], "feature.app.default_app") == app
|
||||||
|
|
||||||
ret["supports_change_url"] = os.path.exists(
|
ret["supports_change_url"] = os.path.exists(
|
||||||
os.path.join(setting_path, "scripts", "change_url")
|
os.path.join(setting_path, "scripts", "change_url")
|
||||||
)
|
)
|
||||||
|
@ -989,6 +993,7 @@ def app_remove(operation_logger, app, purge=False):
|
||||||
permission_delete,
|
permission_delete,
|
||||||
permission_sync_to_user,
|
permission_sync_to_user,
|
||||||
)
|
)
|
||||||
|
from yunohost.domain import domain_list, domain_config_get, domain_config_set
|
||||||
|
|
||||||
if not _is_installed(app):
|
if not _is_installed(app):
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
|
@ -1048,12 +1053,16 @@ def app_remove(operation_logger, app, purge=False):
|
||||||
|
|
||||||
hook_remove(app)
|
hook_remove(app)
|
||||||
|
|
||||||
|
for domain in domain_list()["domains"]:
|
||||||
|
if (domain_config_get(domain, "feature.app.default_app") == app):
|
||||||
|
domain_config_set(domain, "feature.app.default_app", "_none")
|
||||||
|
|
||||||
permission_sync_to_user()
|
permission_sync_to_user()
|
||||||
_assert_system_is_sane_for_app(manifest, "post")
|
_assert_system_is_sane_for_app(manifest, "post")
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def app_makedefault(operation_logger, app, domain=None):
|
def app_makedefault(operation_logger, app, domain=None, undo=False):
|
||||||
"""
|
"""
|
||||||
Redirect domain root to an app
|
Redirect domain root to an app
|
||||||
|
|
||||||
|
@ -1062,11 +1071,10 @@ def app_makedefault(operation_logger, app, domain=None):
|
||||||
domain
|
domain
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.domain import _assert_domain_exists
|
from yunohost.domain import _assert_domain_exists, domain_config_set
|
||||||
|
|
||||||
app_settings = _get_app_settings(app)
|
app_settings = _get_app_settings(app)
|
||||||
app_domain = app_settings["domain"]
|
app_domain = app_settings["domain"]
|
||||||
app_path = app_settings["path"]
|
|
||||||
|
|
||||||
if domain is None:
|
if domain is None:
|
||||||
domain = app_domain
|
domain = app_domain
|
||||||
|
@ -1075,36 +1083,12 @@ def app_makedefault(operation_logger, app, domain=None):
|
||||||
|
|
||||||
operation_logger.related_to.append(("domain", domain))
|
operation_logger.related_to.append(("domain", domain))
|
||||||
|
|
||||||
if "/" in app_map(raw=True)[domain]:
|
|
||||||
raise YunohostValidationError(
|
|
||||||
"app_make_default_location_already_used",
|
|
||||||
app=app,
|
|
||||||
domain=app_domain,
|
|
||||||
other_app=app_map(raw=True)[domain]["/"]["id"],
|
|
||||||
)
|
|
||||||
|
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
# TODO / FIXME : current trick is to add this to conf.json.persisten
|
if undo:
|
||||||
# This is really not robust and should be improved
|
domain_config_set(domain, 'feature.app.default_app', "_none")
|
||||||
# e.g. have a flag in /etc/yunohost/apps/$app/ to say that this is the
|
|
||||||
# default app or idk...
|
|
||||||
if not os.path.exists("/etc/ssowat/conf.json.persistent"):
|
|
||||||
ssowat_conf = {}
|
|
||||||
else:
|
else:
|
||||||
ssowat_conf = read_json("/etc/ssowat/conf.json.persistent")
|
domain_config_set(domain, 'feature.app.default_app', app)
|
||||||
|
|
||||||
if "redirected_urls" not in ssowat_conf:
|
|
||||||
ssowat_conf["redirected_urls"] = {}
|
|
||||||
|
|
||||||
ssowat_conf["redirected_urls"][domain + "/"] = app_domain + app_path
|
|
||||||
|
|
||||||
write_to_json(
|
|
||||||
"/etc/ssowat/conf.json.persistent", ssowat_conf, sort_keys=True, indent=4
|
|
||||||
)
|
|
||||||
chmod("/etc/ssowat/conf.json.persistent", 0o644)
|
|
||||||
|
|
||||||
logger.success(m18n.n("ssowat_conf_updated"))
|
|
||||||
|
|
||||||
|
|
||||||
def app_setting(app, key, value=None, delete=False):
|
def app_setting(app, key, value=None, delete=False):
|
||||||
|
@ -1303,7 +1287,7 @@ def app_ssowatconf():
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.domain import domain_list, _get_maindomain
|
from yunohost.domain import domain_list, _get_maindomain, domain_config_get
|
||||||
from yunohost.permission import user_permission_list
|
from yunohost.permission import user_permission_list
|
||||||
|
|
||||||
main_domain = _get_maindomain()
|
main_domain = _get_maindomain()
|
||||||
|
@ -1341,6 +1325,21 @@ def app_ssowatconf():
|
||||||
redirected_urls.update(app_settings.get("redirected_urls", {}))
|
redirected_urls.update(app_settings.get("redirected_urls", {}))
|
||||||
redirected_regex.update(app_settings.get("redirected_regex", {}))
|
redirected_regex.update(app_settings.get("redirected_regex", {}))
|
||||||
|
|
||||||
|
from .utils.legacy import translate_legacy_default_app_in_ssowant_conf_json_persistent
|
||||||
|
|
||||||
|
translate_legacy_default_app_in_ssowant_conf_json_persistent()
|
||||||
|
|
||||||
|
for domain in domains:
|
||||||
|
default_app = domain_config_get(domain, "feature.app.default_app")
|
||||||
|
if default_app != "_none" and _is_installed(default_app):
|
||||||
|
app_settings = _get_app_settings(default_app)
|
||||||
|
app_domain = app_settings["domain"]
|
||||||
|
app_path = app_settings["path"]
|
||||||
|
|
||||||
|
# Prevent infinite redirect loop...
|
||||||
|
if domain + "/" != app_domain + app_path:
|
||||||
|
redirected_urls[domain + "/"] = app_domain + app_path
|
||||||
|
|
||||||
# New permission system
|
# New permission system
|
||||||
for perm_name, perm_info in all_permissions.items():
|
for perm_name, perm_info in all_permissions.items():
|
||||||
|
|
||||||
|
|
|
@ -454,6 +454,21 @@ class DomainConfigPanel(ConfigPanel):
|
||||||
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
|
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
|
||||||
save_mode = "diff"
|
save_mode = "diff"
|
||||||
|
|
||||||
|
def _apply(self):
|
||||||
|
if ("default_app" in self.future_values and self.future_values["default_app"] != self.values["default_app"]):
|
||||||
|
from yunohost.app import app_ssowatconf, app_map
|
||||||
|
|
||||||
|
if "/" in app_map(raw=True)[self.entity]:
|
||||||
|
raise YunohostValidationError(
|
||||||
|
"app_make_default_location_already_used",
|
||||||
|
app=self.future_values["default_app"],
|
||||||
|
domain=self.entity,
|
||||||
|
other_app=app_map(raw=True)[self.entity]["/"]["id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
super()._apply()
|
||||||
|
app_ssowatconf()
|
||||||
|
|
||||||
def _get_toml(self):
|
def _get_toml(self):
|
||||||
from yunohost.dns import _get_registrar_config_section
|
from yunohost.dns import _get_registrar_config_section
|
||||||
|
|
||||||
|
|
|
@ -440,6 +440,7 @@ class ConfigPanel:
|
||||||
"step",
|
"step",
|
||||||
"accept",
|
"accept",
|
||||||
"redact",
|
"redact",
|
||||||
|
"filters",
|
||||||
],
|
],
|
||||||
"defaults": {},
|
"defaults": {},
|
||||||
},
|
},
|
||||||
|
@ -705,6 +706,7 @@ class Question:
|
||||||
self.ask = question.get("ask", {"en": self.name})
|
self.ask = question.get("ask", {"en": self.name})
|
||||||
self.help = question.get("help")
|
self.help = question.get("help")
|
||||||
self.redact = question.get("redact", False)
|
self.redact = question.get("redact", False)
|
||||||
|
self.filters = question.get("filters", [])
|
||||||
# .current_value is the currently stored value
|
# .current_value is the currently stored value
|
||||||
self.current_value = question.get("current_value")
|
self.current_value = question.get("current_value")
|
||||||
# .value is the "proposed" value which we got from the user
|
# .value is the "proposed" value which we got from the user
|
||||||
|
@ -1126,6 +1128,28 @@ class DomainQuestion(Question):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class AppQuestion(Question):
|
||||||
|
argument_type = "app"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, question, context: Mapping[str, Any] = {}, hooks: Dict[str, Callable] = {}
|
||||||
|
):
|
||||||
|
from yunohost.app import app_list
|
||||||
|
|
||||||
|
super().__init__(question, context, hooks)
|
||||||
|
|
||||||
|
apps = app_list(full=True)["apps"]
|
||||||
|
for _filter in self.filters:
|
||||||
|
apps = [app for app in apps if _filter in app and app[_filter]]
|
||||||
|
|
||||||
|
def _app_display(app):
|
||||||
|
domain_path = f" ({app['domain_path']})" if 'domain_path' in app else ""
|
||||||
|
return app["label"] + domain_path
|
||||||
|
|
||||||
|
self.choices = {"_none": "---"}
|
||||||
|
self.choices.update({app['id']: _app_display(app) for app in apps})
|
||||||
|
|
||||||
|
|
||||||
class UserQuestion(Question):
|
class UserQuestion(Question):
|
||||||
argument_type = "user"
|
argument_type = "user"
|
||||||
|
|
||||||
|
@ -1319,6 +1343,7 @@ ARGUMENTS_TYPE_PARSERS = {
|
||||||
"alert": DisplayTextQuestion,
|
"alert": DisplayTextQuestion,
|
||||||
"markdown": DisplayTextQuestion,
|
"markdown": DisplayTextQuestion,
|
||||||
"file": FileQuestion,
|
"file": FileQuestion,
|
||||||
|
"app": AppQuestion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from moulinette.utils.filesystem import (
|
||||||
read_file,
|
read_file,
|
||||||
write_to_file,
|
write_to_file,
|
||||||
write_to_yaml,
|
write_to_yaml,
|
||||||
|
write_to_json,
|
||||||
read_yaml,
|
read_yaml,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,6 +69,52 @@ def legacy_permission_label(app, permission_type):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def translate_legacy_default_app_in_ssowant_conf_json_persistent():
|
||||||
|
from yunohost.app import app_list
|
||||||
|
from yunohost.domain import domain_config_set
|
||||||
|
|
||||||
|
persistent_file_name = "/etc/ssowat/conf.json.persistent"
|
||||||
|
if not os.path.exists(persistent_file_name):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ugly hack because for some reason so many people have tabs in their conf.json.persistent ...
|
||||||
|
os.system(r"sed -i 's/\t/ /g' /etc/ssowat/conf.json.persistent")
|
||||||
|
|
||||||
|
# Ugly hack to try not to misarably fail migration
|
||||||
|
persistent = read_yaml(persistent_file_name)
|
||||||
|
|
||||||
|
if "redirected_urls" not in persistent:
|
||||||
|
return
|
||||||
|
|
||||||
|
redirected_urls = persistent["redirected_urls"]
|
||||||
|
|
||||||
|
if not any(from_url.count('/') == 1 and from_url.endswith('/') for from_url in redirected_urls):
|
||||||
|
return
|
||||||
|
|
||||||
|
apps = app_list()['apps']
|
||||||
|
|
||||||
|
if not any(app.get('domain_path') in redirected_urls.values() for app in apps):
|
||||||
|
return
|
||||||
|
|
||||||
|
for from_url, dest_url in redirected_urls.copy().items():
|
||||||
|
# Not a root domain, skip
|
||||||
|
if from_url.count('/') != 1 or not from_url.endswith('/'):
|
||||||
|
continue
|
||||||
|
for app in apps:
|
||||||
|
if app.get('domain_path') != dest_url:
|
||||||
|
continue
|
||||||
|
domain_config_set(from_url.strip('/'), "feature.app.default_app", app['id'])
|
||||||
|
del redirected_urls[from_url]
|
||||||
|
|
||||||
|
persistent["redirected_urls"] = redirected_urls
|
||||||
|
|
||||||
|
write_to_json(persistent_file_name, persistent, sort_keys=True, indent=4)
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"YunoHost automatically translated some legacy redirections in /etc/ssowat/conf.json.persistent to match the new default application using domain configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
LEGACY_PHP_VERSION_REPLACEMENTS = [
|
LEGACY_PHP_VERSION_REPLACEMENTS = [
|
||||||
("/etc/php5", "/etc/php/7.4"),
|
("/etc/php5", "/etc/php/7.4"),
|
||||||
("/etc/php/7.0", "/etc/php/7.4"),
|
("/etc/php/7.0", "/etc/php/7.4"),
|
||||||
|
|
Loading…
Add table
Reference in a new issue