mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
configpanel: update _apply
This commit is contained in:
parent
7a60703ef5
commit
5f9ea58313
4 changed files with 81 additions and 88 deletions
14
src/app.py
14
src/app.py
|
@ -27,7 +27,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import copy
|
import copy
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import TYPE_CHECKING, List, Tuple, Dict, Any, Iterator, Optional
|
from typing import TYPE_CHECKING, List, Tuple, Dict, Any, Iterator, Optional, Union
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -73,7 +73,10 @@ from yunohost.app_catalog import ( # noqa
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
|
||||||
|
|
||||||
from yunohost.utils.configpanel import ConfigPanelModel, RawSettings
|
from yunohost.utils.configpanel import ConfigPanelModel, RawSettings
|
||||||
|
from yunohost.utils.form import FormModel
|
||||||
|
|
||||||
logger = getLogger("yunohost.app")
|
logger = getLogger("yunohost.app")
|
||||||
|
|
||||||
|
@ -1809,8 +1812,13 @@ class AppConfigPanel(ConfigPanel):
|
||||||
def _get_raw_settings(self, config: "ConfigPanelModel") -> "RawSettings":
|
def _get_raw_settings(self, config: "ConfigPanelModel") -> "RawSettings":
|
||||||
return self._call_config_script("show")
|
return self._call_config_script("show")
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(
|
||||||
env = {key: str(value) for key, value in self.new_values.items()}
|
self,
|
||||||
|
form: "FormModel",
|
||||||
|
previous_settings: dict[str, Any],
|
||||||
|
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None,
|
||||||
|
):
|
||||||
|
env = {key: str(value) for key, value in form.dict().items()}
|
||||||
return_content = self._call_config_script("apply", env=env)
|
return_content = self._call_config_script("apply", env=env)
|
||||||
|
|
||||||
# If the script returned validation error
|
# If the script returned validation error
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, List, Optional
|
from typing import TYPE_CHECKING, Any, List, Optional, Union
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
|
@ -48,7 +48,10 @@ from yunohost.utils.dns import is_yunohost_dyndns_domain
|
||||||
from yunohost.log import is_unit_operation
|
from yunohost.log import is_unit_operation
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
|
||||||
|
|
||||||
from yunohost.utils.configpanel import RawConfig
|
from yunohost.utils.configpanel import RawConfig
|
||||||
|
from yunohost.utils.form import FormModel
|
||||||
|
|
||||||
logger = getLogger("yunohost.domain")
|
logger = getLogger("yunohost.domain")
|
||||||
|
|
||||||
|
@ -669,7 +672,7 @@ class DomainConfigPanel(ConfigPanel):
|
||||||
|
|
||||||
# Portal settings are only available on "topest" domains
|
# Portal settings are only available on "topest" domains
|
||||||
if _get_parent_domain_of(self.entity, topest=True) is not None:
|
if _get_parent_domain_of(self.entity, topest=True) is not None:
|
||||||
del toml["feature"]["portal"]
|
del raw_config["feature"]["portal"]
|
||||||
|
|
||||||
# Optimize wether or not to load the DNS section,
|
# Optimize wether or not to load the DNS section,
|
||||||
# e.g. we don't want to trigger the whole _get_registary_config_section
|
# e.g. we don't want to trigger the whole _get_registary_config_section
|
||||||
|
@ -712,17 +715,23 @@ class DomainConfigPanel(ConfigPanel):
|
||||||
|
|
||||||
return raw_config
|
return raw_config
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(
|
||||||
if (
|
self,
|
||||||
"default_app" in self.future_values
|
form: "FormModel",
|
||||||
and self.future_values["default_app"] != self.values["default_app"]
|
previous_settings: dict[str, Any],
|
||||||
|
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None,
|
||||||
):
|
):
|
||||||
|
next_settings = {
|
||||||
|
k: v for k, v in form.dict().items() if previous_settings.get(k) != v
|
||||||
|
}
|
||||||
|
|
||||||
|
if "default_app" in next_settings:
|
||||||
from yunohost.app import app_ssowatconf, app_map
|
from yunohost.app import app_ssowatconf, app_map
|
||||||
|
|
||||||
if "/" in app_map(raw=True).get(self.entity, {}):
|
if "/" in app_map(raw=True).get(self.entity, {}):
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
"app_make_default_location_already_used",
|
"app_make_default_location_already_used",
|
||||||
app=self.future_values["default_app"],
|
app=next_settings["default_app"],
|
||||||
domain=self.entity,
|
domain=self.entity,
|
||||||
other_app=app_map(raw=True)[self.entity]["/"]["id"],
|
other_app=app_map(raw=True)[self.entity]["/"]["id"],
|
||||||
)
|
)
|
||||||
|
@ -735,8 +744,7 @@ class DomainConfigPanel(ConfigPanel):
|
||||||
"portal_theme",
|
"portal_theme",
|
||||||
]
|
]
|
||||||
if _get_parent_domain_of(self.entity, topest=True) is None and any(
|
if _get_parent_domain_of(self.entity, topest=True) is None and any(
|
||||||
option in self.future_values
|
option in next_settings
|
||||||
and self.new_values[option] != self.values.get(option)
|
|
||||||
for option in portal_options
|
for option in portal_options
|
||||||
):
|
):
|
||||||
from yunohost.portal import PORTAL_SETTINGS_DIR
|
from yunohost.portal import PORTAL_SETTINGS_DIR
|
||||||
|
@ -744,9 +752,8 @@ class DomainConfigPanel(ConfigPanel):
|
||||||
# Portal options are also saved in a `domain.portal.yml` file
|
# Portal options are also saved in a `domain.portal.yml` file
|
||||||
# that can be read by the portal API.
|
# that can be read by the portal API.
|
||||||
# FIXME remove those from the config panel saved values?
|
# FIXME remove those from the config panel saved values?
|
||||||
portal_values = {
|
|
||||||
option: self.future_values[option] for option in portal_options
|
portal_values = form.dict(include=portal_options)
|
||||||
}
|
|
||||||
|
|
||||||
portal_settings_path = Path(f"{PORTAL_SETTINGS_DIR}/{self.entity}.json")
|
portal_settings_path = Path(f"{PORTAL_SETTINGS_DIR}/{self.entity}.json")
|
||||||
portal_settings = {"apps": {}}
|
portal_settings = {"apps": {}}
|
||||||
|
@ -760,38 +767,21 @@ class DomainConfigPanel(ConfigPanel):
|
||||||
str(portal_settings_path), portal_settings, sort_keys=True, indent=4
|
str(portal_settings_path), portal_settings, sort_keys=True, indent=4
|
||||||
)
|
)
|
||||||
|
|
||||||
super()._apply()
|
super()._apply(form, previous_settings)
|
||||||
|
|
||||||
# Reload ssowat if default app changed
|
# Reload ssowat if default app changed
|
||||||
if (
|
if "default_app" in next_settings:
|
||||||
"default_app" in self.future_values
|
|
||||||
and self.future_values["default_app"] != self.values["default_app"]
|
|
||||||
):
|
|
||||||
app_ssowatconf()
|
app_ssowatconf()
|
||||||
|
|
||||||
stuff_to_regen_conf = []
|
stuff_to_regen_conf = set()
|
||||||
if (
|
if "xmpp" in next_settings:
|
||||||
"xmpp" in self.future_values
|
stuff_to_regen_conf.update({"nginx", "metronome"})
|
||||||
and self.future_values["xmpp"] != self.values["xmpp"]
|
|
||||||
):
|
|
||||||
stuff_to_regen_conf.append("nginx")
|
|
||||||
stuff_to_regen_conf.append("metronome")
|
|
||||||
|
|
||||||
if (
|
if "mail_in" in next_settings or "mail_out" in next_settings:
|
||||||
"mail_in" in self.future_values
|
stuff_to_regen_conf.update({"nginx", "postfix", "dovecot", "rspamd"})
|
||||||
and self.future_values["mail_in"] != self.values["mail_in"]
|
|
||||||
) or (
|
|
||||||
"mail_out" in self.future_values
|
|
||||||
and self.future_values["mail_out"] != self.values["mail_out"]
|
|
||||||
):
|
|
||||||
if "nginx" not in stuff_to_regen_conf:
|
|
||||||
stuff_to_regen_conf.append("nginx")
|
|
||||||
stuff_to_regen_conf.append("postfix")
|
|
||||||
stuff_to_regen_conf.append("dovecot")
|
|
||||||
stuff_to_regen_conf.append("rspamd")
|
|
||||||
|
|
||||||
if stuff_to_regen_conf:
|
if stuff_to_regen_conf:
|
||||||
regen_conf(names=stuff_to_regen_conf)
|
regen_conf(names=list(stuff_to_regen_conf))
|
||||||
|
|
||||||
|
|
||||||
def domain_action_run(domain, action, args=None):
|
def domain_action_run(domain, action, args=None):
|
||||||
|
|
|
@ -31,12 +31,15 @@ from yunohost.log import is_unit_operation
|
||||||
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
|
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
|
||||||
|
|
||||||
from yunohost.utils.configpanel import (
|
from yunohost.utils.configpanel import (
|
||||||
ConfigPanelGetMode,
|
ConfigPanelGetMode,
|
||||||
ConfigPanelModel,
|
ConfigPanelModel,
|
||||||
RawConfig,
|
RawConfig,
|
||||||
RawSettings,
|
RawSettings,
|
||||||
)
|
)
|
||||||
|
from yunohost.utils.form import FormModel
|
||||||
|
|
||||||
logger = getLogger("yunohost.settings")
|
logger = getLogger("yunohost.settings")
|
||||||
|
|
||||||
|
@ -217,19 +220,15 @@ class SettingsConfigPanel(ConfigPanel):
|
||||||
|
|
||||||
return raw_settings
|
return raw_settings
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(
|
||||||
root_password = self.new_values.pop("root_password", None)
|
self,
|
||||||
root_password_confirm = self.new_values.pop("root_password_confirm", None)
|
form: "FormModel",
|
||||||
passwordless_sudo = self.new_values.pop("passwordless_sudo", None)
|
previous_settings: dict[str, Any],
|
||||||
|
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None,
|
||||||
self.values = {
|
):
|
||||||
k: v for k, v in self.values.items() if k not in self.virtual_settings
|
root_password = form.get("root_password", None)
|
||||||
}
|
root_password_confirm = form.get("root_password_confirm", None)
|
||||||
self.new_values = {
|
passwordless_sudo = form.get("passwordless_sudo", None)
|
||||||
k: v for k, v in self.new_values.items() if k not in self.virtual_settings
|
|
||||||
}
|
|
||||||
|
|
||||||
assert all(v not in self.future_values for v in self.virtual_settings)
|
|
||||||
|
|
||||||
if root_password and root_password.strip():
|
if root_password and root_password.strip():
|
||||||
if root_password != root_password_confirm:
|
if root_password != root_password_confirm:
|
||||||
|
@ -248,15 +247,20 @@ class SettingsConfigPanel(ConfigPanel):
|
||||||
{"sudoOption": ["!authenticate"] if passwordless_sudo else []},
|
{"sudoOption": ["!authenticate"] if passwordless_sudo else []},
|
||||||
)
|
)
|
||||||
|
|
||||||
super()._apply()
|
# First save settings except virtual + default ones
|
||||||
|
super()._apply(form, previous_settings, exclude=self.virtual_settings)
|
||||||
settings = {
|
next_settings = {
|
||||||
k: v for k, v in self.future_values.items() if self.values.get(k) != v
|
k: v
|
||||||
|
for k, v in form.dict(exclude=self.virtual_settings).items()
|
||||||
|
if previous_settings.get(k) != v
|
||||||
}
|
}
|
||||||
for setting_name, value in settings.items():
|
|
||||||
|
for setting_name, value in next_settings.items():
|
||||||
try:
|
try:
|
||||||
|
# FIXME not sure to understand why we need the previous value if
|
||||||
|
# updated_settings has already been filtered
|
||||||
trigger_post_change_hook(
|
trigger_post_change_hook(
|
||||||
setting_name, self.values.get(setting_name), value
|
setting_name, previous_settings.get(setting_name), value
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Post-change hook for setting failed : {e}")
|
logger.error(f"Post-change hook for setting failed : {e}")
|
||||||
|
|
|
@ -47,6 +47,8 @@ from yunohost.utils.i18n import _value_for_locale
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pydantic.fields import ModelField
|
from pydantic.fields import ModelField
|
||||||
|
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
|
||||||
|
|
||||||
from yunohost.utils.form import FormModel, Hooks
|
from yunohost.utils.form import FormModel, Hooks
|
||||||
|
|
||||||
logger = getLogger("yunohost.configpanel")
|
logger = getLogger("yunohost.configpanel")
|
||||||
|
@ -678,43 +680,32 @@ class ConfigPanel:
|
||||||
hooks=hooks,
|
hooks=hooks,
|
||||||
)
|
)
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
# Check and ask unanswered questions
|
|
||||||
prefilled_answers = self.args.copy()
|
|
||||||
prefilled_answers.update(self.new_values)
|
|
||||||
|
|
||||||
questions = ask_questions_and_parse_answers(
|
|
||||||
{question["id"]: question for question in section["options"]},
|
|
||||||
prefilled_answers=prefilled_answers,
|
|
||||||
current_values=self.values,
|
|
||||||
hooks=self.hooks,
|
|
||||||
)
|
|
||||||
self.new_values.update(
|
|
||||||
{
|
|
||||||
question.id: question.value
|
|
||||||
for question in questions
|
|
||||||
if not question.readonly and question.value is not None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
=======
|
|
||||||
return settings
|
return settings
|
||||||
>>>>>>> be777b928 (configpanel: update _ask)
|
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(
|
||||||
|
self,
|
||||||
|
form: "FormModel",
|
||||||
|
previous_settings: dict[str, Any],
|
||||||
|
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Save settings in yaml file.
|
||||||
|
If `save_mode` is `"diff"` (which is the default), only values that are
|
||||||
|
different from their default value will be saved.
|
||||||
|
"""
|
||||||
logger.info("Saving the new configuration...")
|
logger.info("Saving the new configuration...")
|
||||||
|
|
||||||
dir_path = os.path.dirname(os.path.realpath(self.save_path))
|
dir_path = os.path.dirname(os.path.realpath(self.save_path))
|
||||||
if not os.path.exists(dir_path):
|
if not os.path.exists(dir_path):
|
||||||
mkdir(dir_path, mode=0o700)
|
mkdir(dir_path, mode=0o700)
|
||||||
|
|
||||||
values_to_save = self.future_values
|
exclude_defaults = self.save_mode == "diff"
|
||||||
if self.save_mode == "diff":
|
settings = form.dict(exclude_defaults=exclude_defaults, exclude=exclude) # type: ignore
|
||||||
defaults = self._get_default_values()
|
|
||||||
values_to_save = {
|
|
||||||
k: v for k, v in values_to_save.items() if defaults.get(k) != v
|
|
||||||
}
|
|
||||||
|
|
||||||
# Save the settings to the .yaml file
|
# Save the settings to the .yaml file
|
||||||
write_to_yaml(self.save_path, values_to_save)
|
write_to_yaml(self.save_path, settings)
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
def _reload_services(self):
|
def _reload_services(self):
|
||||||
from yunohost.service import service_reload_or_restart
|
from yunohost.service import service_reload_or_restart
|
||||||
|
|
Loading…
Add table
Reference in a new issue