diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d453f8d..1669bafc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.9.24]
+### Features
+- Web
+ - new custom action button to install apps (to improve usability since some users don't know about how to install Web apps through the search bar)
+
+
+
+
### Improvements
- General
- handling http redirect errors
diff --git a/bauh/api/http.py b/bauh/api/http.py
index 4fc9ba68..e4836ec5 100644
--- a/bauh/api/http.py
+++ b/bauh/api/http.py
@@ -56,6 +56,9 @@ class HttpClient:
elif isinstance(e, requests.exceptions.TooManyRedirects):
self.logger.warning(f"Too many redirects for GET -> {url}")
raise e
+ elif e.__class__ in (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema):
+ self.logger.warning(f"The URL '{url}' has an invalid schema")
+ raise e
self.logger.error("Could not retrieve data from '{}'".format(url))
traceback.print_exc()
diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py
index 7bf63c31..155acc05 100644
--- a/bauh/gems/web/controller.py
+++ b/bauh/gems/web/controller.py
@@ -14,7 +14,7 @@ from typing import List, Type, Set, Tuple, Optional, Dict
import requests
import yaml
from colorama import Fore
-from requests import exceptions, Response
+from requests import Response
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, TransactionResult, \
@@ -74,13 +74,22 @@ class WebApplicationManager(SoftwareManager):
self.suggestions = {}
self.configman = WebConfigManager()
self.idxman = SearchIndexManager(logger=context.logger)
- self.custom_actions = [CustomSoftwareAction(i18n_label_key='web.custom_action.clean_env',
- i18n_status_key='web.custom_action.clean_env.status',
- manager=self,
- manager_method='clean_environment',
- icon_path=resource.get_path('img/web.svg', ROOT_DIR),
- requires_root=False,
- refresh=False)]
+ self.custom_actions = [
+ CustomSoftwareAction(i18n_label_key='web.custom_action.install_app',
+ i18n_status_key='web.custom_action.install_app.status',
+ manager=self,
+ manager_method='install_app',
+ icon_path=resource.get_path('img/web.svg', ROOT_DIR),
+ requires_root=False,
+ refresh=True),
+ CustomSoftwareAction(i18n_label_key='web.custom_action.clean_env',
+ i18n_status_key='web.custom_action.clean_env.status',
+ manager=self,
+ manager_method='clean_environment',
+ icon_path=resource.get_path('img/web.svg', ROOT_DIR),
+ requires_root=False,
+ refresh=False)
+ ]
def _get_lang_header(self) -> str:
try:
@@ -222,7 +231,7 @@ class WebApplicationManager(SoftwareManager):
def _strip_url_protocol(self, url: str) -> str:
return RE_PROTOCOL_STRIP.split(url)[1].strip().lower()
- def serialize_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool):
+ def serialize_to_disk(self, pkg: SoftwarePackage, icon_bytes: Optional[bytes], only_icon: bool):
super(WebApplicationManager, self).serialize_to_disk(pkg=pkg, icon_bytes=None, only_icon=False)
def _request_url(self, url: str) -> Optional[Response]:
@@ -230,8 +239,8 @@ class WebApplicationManager(SoftwareManager):
try:
return self.http_client.get(url, headers=headers, ignore_ssl=True, single_call=True, session=False, allow_redirects=True)
- except (exceptions.ConnectionError, requests.exceptions.TooManyRedirects) as e:
- self.logger.warning(f"Could not GET {url}. Exception: {e.__class__.__name__}")
+ except Exception as e:
+ self.logger.warning(f"Could not GET '{url}'. Exception: {e.__class__.__name__}")
def _map_url(self, url: str) -> Tuple["BeautifulSoup", requests.Response]:
url_res = self._request_url(url)
@@ -473,10 +482,10 @@ class WebApplicationManager(SoftwareManager):
def get_history(self, pkg: SoftwarePackage) -> PackageHistory:
pass
- def _ask_install_options(self, app: WebApplication, watcher: ProcessWatcher) -> Tuple[bool, List[str]]:
+ def _ask_install_options(self, app: WebApplication, watcher: ProcessWatcher, pre_validated: bool) -> Tuple[bool, List[str]]:
watcher.change_substatus(self.i18n['web.install.substatus.options'])
- inp_url = TextInputComponent(label=self.i18n['address'], value=app.url, read_only=True)
+ inp_url = TextInputComponent(label=self.i18n['address'], value=app.url, read_only=pre_validated)
inp_name = TextInputComponent(label=self.i18n['name'], value=app.name)
inp_desc = TextInputComponent(label=self.i18n['description'], value=app.description)
@@ -513,11 +522,15 @@ class WebApplicationManager(SoftwareManager):
icon_op_ded = InputOption(id_='icon_ded', label=self.i18n['web.install.option.wicon.deducted.label'], value=0,
tooltip=self.i18n['web.install.option.wicon.deducted.tip'].format('Nativefier'))
- icon_op_disp = InputOption(id_='icon_disp', label=self.i18n['web.install.option.wicon.displayed.label'],
- value=1, tooltip=self.i18n['web.install.option.wicon.displayed.tip'])
+
+ if pre_validated:
+ icon_op_disp = InputOption(id_='icon_disp', label=self.i18n['web.install.option.wicon.displayed.label'],
+ value=1, tooltip=self.i18n['web.install.option.wicon.displayed.tip'])
+ else:
+ icon_op_disp = None
inp_icon = SingleSelectComponent(type_=SelectViewType.COMBO,
- options=[icon_op_disp, icon_op_ded],
+ options=[op for op in (icon_op_ded, icon_op_disp) if op],
default_option=icon_op_disp if app.icon_url and app.save_icon else icon_op_ded,
label=self.i18n['web.install.option.wicon.label'])
@@ -544,13 +557,24 @@ class WebApplicationManager(SoftwareManager):
check_options = MultipleSelectComponent(options=adv_opts, default_options=def_adv_opts, label=self.i18n['web.install.options.advanced'].capitalize())
- res = watcher.request_confirmation(title=self.i18n['web.install.options_dialog.title'],
- body=None,
- components=[form_1, check_options],
- confirmation_label=self.i18n['continue'].capitalize(),
- deny_label=self.i18n['cancel'].capitalize())
+ install_ = watcher.request_confirmation(title=self.i18n['web.install.options_dialog.title'],
+ body=None,
+ components=[form_1, check_options],
+ confirmation_label=self.i18n['continue'].capitalize(),
+ deny_label=self.i18n['cancel'].capitalize())
+
+ if install_:
+ if not pre_validated:
+ typed_url = inp_url.get_value().strip()
+
+ if not typed_url or not self._request_url(typed_url):
+ watcher.show_message(title=self.i18n['error'].capitalize(),
+ type_=MessageType.ERROR,
+ body=self.i18n['web.custom_action.install_app.invalid_url'].format(url=bold(f'"{inp_url.get_value()}"')))
+ return False, []
+ else:
+ app.url = typed_url
- if res:
selected = []
if check_options.values:
@@ -579,9 +603,9 @@ class WebApplicationManager(SoftwareManager):
app.set_custom_icon(icon_chooser.file_path)
selected.append('--icon={}'.format(icon_chooser.file_path))
- app.save_icon = inp_icon.value == icon_op_disp
+ app.save_icon = inp_icon.value == icon_op_disp if icon_op_disp else False
- return res, selected
+ return True, selected
return False, []
@@ -649,13 +673,9 @@ class WebApplicationManager(SoftwareManager):
pkg.name))
traceback.print_exc()
- def install(self, pkg: WebApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
- continue_install, install_options = self._ask_install_options(pkg, watcher)
+ def _install(self, pkg: WebApplication, install_options: List[str], watcher: ProcessWatcher) -> TransactionResult:
widevine_support = '--widevine' in install_options
- if not continue_install:
- return TransactionResult(success=False, installed=[], removed=[])
-
watcher.change_substatus(self.i18n['web.env.checking'])
handler = ProcessHandler(watcher)
@@ -664,7 +684,8 @@ class WebApplicationManager(SoftwareManager):
if web_config['environment']['system'] and not nativefier.is_available():
watcher.show_message(title=self.i18n['error'].capitalize(),
- body=self.i18n['web.install.global_nativefier.unavailable'].format(n=bold('Nativefier'), app=bold(pkg.name)) + '.',
+ body=self.i18n['web.install.global_nativefier.unavailable'].format(
+ n=bold('Nativefier'), app=bold(pkg.name)) + '.',
type_=MessageType.ERROR)
return TransactionResult(success=False, installed=[], removed=[])
@@ -677,8 +698,9 @@ class WebApplicationManager(SoftwareManager):
if comps_to_update and not self._ask_update_permission(comps_to_update, watcher):
return TransactionResult(success=False, installed=[], removed=[])
- if not self.env_updater.update(components=comps_to_update, handler=handler):
- watcher.show_message(title=self.i18n['error'], body=self.i18n['web.env.error'].format(bold(pkg.name)), type_=MessageType.ERROR)
+ if not self.env_updater.update(components=comps_to_update, handler=handler):
+ watcher.show_message(title=self.i18n['error'], body=self.i18n['web.env.error'].format(bold(pkg.name)),
+ type_=MessageType.ERROR)
return TransactionResult(success=False, installed=[], removed=[])
Path(INSTALLED_PATH).mkdir(parents=True, exist_ok=True)
@@ -737,11 +759,13 @@ class WebApplicationManager(SoftwareManager):
if hasattr(pkg, prop):
try:
setattr(pkg, prop, val)
- self.logger.info(f"Using custom installation property '{prop}' ({val if val else ''}) for '{url_domain}' "
- f"(Electron: {electron_version})")
+ self.logger.info(
+ f"Using custom installation property '{prop}' ({val if val else ''}) for '{url_domain}' "
+ f"(Electron: {electron_version})")
except:
- self.logger.error(f"Could not set the custom installation property '{prop}' ({val if val else ''}) "
- f"for '{url_domain}' (Electron: {electron_version})")
+ self.logger.error(
+ f"Could not set the custom installation property '{prop}' ({val if val else ''}) "
+ f"for '{url_domain}' (Electron: {electron_version})")
watcher.change_substatus(self.i18n['web.install.substatus.call_nativefier'].format(bold('nativefier')))
@@ -754,7 +778,8 @@ class WebApplicationManager(SoftwareManager):
if not installed:
msg = '{}.{}.'.format(self.i18n['wen.install.error'].format(bold(pkg.name)),
- self.i18n['web.install.nativefier.error.unknown'].format(bold(self.i18n['details'].capitalize())))
+ self.i18n['web.install.nativefier.error.unknown'].format(
+ bold(self.i18n['details'].capitalize())))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.ERROR)
return TransactionResult(success=False, installed=[], removed=[])
@@ -825,6 +850,27 @@ class WebApplicationManager(SoftwareManager):
return TransactionResult(success=True, installed=[pkg], removed=[])
+ def install(self, pkg: WebApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
+ continue_install, install_options = self._ask_install_options(pkg, watcher, pre_validated=True)
+
+ if not continue_install:
+ return TransactionResult(success=False, installed=[], removed=[])
+
+ return self._install(pkg, install_options, watcher)
+
+ def install_app(self, root_password: str, watcher: ProcessWatcher) -> bool:
+ pkg = WebApplication()
+ continue_install, install_options = self._ask_install_options(pkg, watcher, pre_validated=False)
+
+ if not continue_install:
+ return False
+
+ if self._install(pkg, install_options, watcher).success:
+ self.serialize_to_disk(pkg, icon_bytes=None, only_icon=False)
+ return True
+
+ return False
+
def _gen_desktop_entry_content(self, pkg: WebApplication) -> str:
return """
[Desktop Entry]
diff --git a/bauh/gems/web/resources/locale/ca b/bauh/gems/web/resources/locale/ca
index 8132eeda..783eb459 100644
--- a/bauh/gems/web/resources/locale/ca
+++ b/bauh/gems/web/resources/locale/ca
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=An error occurred during the installation env
web.custom_action.clean_env.status=Cleaning the installation environment
web.custom_action.clean_env.success=Installation environment cleaned
web.custom_action.clean_env=Clean installation environment
+web.custom_action.install_app=Install Web application
+web.custom_action.install_app.invalid_url=The address {url} could not be found. It is not possible to finish the installation.
+web.custom_action.install_app.status=Installing Web application
web.env.checking=Checking the Web installation environment
web.env.error=It seems there are issues with the Web installation environment. It wil not be possible to install {}.
web.environment.install=Installing {}
diff --git a/bauh/gems/web/resources/locale/en b/bauh/gems/web/resources/locale/en
index 40d71c09..6f9bcfd9 100644
--- a/bauh/gems/web/resources/locale/en
+++ b/bauh/gems/web/resources/locale/en
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=An error occurred during the installation env
web.custom_action.clean_env.status=Cleaning the installation environment
web.custom_action.clean_env.success=Installation environment cleaned
web.custom_action.clean_env=Clean installation environment
+web.custom_action.install_app=Install Web application
+web.custom_action.install_app.invalid_url=The address {url} could not be found. It is not possible to finish the installation.
+web.custom_action.install_app.status=Installing Web application
web.env.checking=Checking the Web installation environment
web.env.error=It seems there are issues with the Web installation environment. It wil not be possible to install {}.
web.environment.install=Installing {}
diff --git a/bauh/gems/web/resources/locale/es b/bauh/gems/web/resources/locale/es
index 2d9f61a0..e7feb5b3 100644
--- a/bauh/gems/web/resources/locale/es
+++ b/bauh/gems/web/resources/locale/es
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=Se produjo un error durante la limpieza del a
web.custom_action.clean_env.status=Limpiando el ambiente de instalación
web.custom_action.clean_env.success=Ambiente de instalación limpio
web.custom_action.clean_env=Limpiar ambiente de instalación
+web.custom_action.install_app=Instalar aplicación Web
+web.custom_action.install_app.invalid_url=No se pudo encontrar la dirección {url}. No es posible finalizar la instalación.
+web.custom_action.install_app.status=Instalando aplicación Web
web.env.checking=Verificando el ambiente de instalación web
web.env.error=Parece que hay problemas con el ambiente de instalación web. No será posible instalar {}.
web.environment.install=Instalando {}
diff --git a/bauh/gems/web/resources/locale/fr b/bauh/gems/web/resources/locale/fr
index 25674c98..ce716c8a 100644
--- a/bauh/gems/web/resources/locale/fr
+++ b/bauh/gems/web/resources/locale/fr
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=Une erreur est survenue lors du nettoyage de
web.custom_action.clean_env.status=Nettoyage de l'environnement d'installation
web.custom_action.clean_env.success=Environnement d'installation nettoyé
web.custom_action.clean_env=Nettoyage de l'environnement d'installation Web.
+web.custom_action.install_app=Install Web application
+web.custom_action.install_app.invalid_url=The address {url} could not be found. It is not possible to finish the installation.
+web.custom_action.install_app.status=Installing Web application
web.env.checking=Verification de l'environnement d'installation Web.
web.env.error=Il y a des problèmes avec l'environnement d'installation Web. {} ne pourra pas être installé.
web.environment.install=Intallation de {}
diff --git a/bauh/gems/web/resources/locale/it b/bauh/gems/web/resources/locale/it
index 8132eeda..783eb459 100644
--- a/bauh/gems/web/resources/locale/it
+++ b/bauh/gems/web/resources/locale/it
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=An error occurred during the installation env
web.custom_action.clean_env.status=Cleaning the installation environment
web.custom_action.clean_env.success=Installation environment cleaned
web.custom_action.clean_env=Clean installation environment
+web.custom_action.install_app=Install Web application
+web.custom_action.install_app.invalid_url=The address {url} could not be found. It is not possible to finish the installation.
+web.custom_action.install_app.status=Installing Web application
web.env.checking=Checking the Web installation environment
web.env.error=It seems there are issues with the Web installation environment. It wil not be possible to install {}.
web.environment.install=Installing {}
diff --git a/bauh/gems/web/resources/locale/pt b/bauh/gems/web/resources/locale/pt
index 96ad226a..7447d4fa 100644
--- a/bauh/gems/web/resources/locale/pt
+++ b/bauh/gems/web/resources/locale/pt
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=Ocorreu um problema durante a limpeza do ambi
web.custom_action.clean_env.status=Limpando ambiente de instalação
web.custom_action.clean_env.success=Ambiente de instalação limpo
web.custom_action.clean_env=Limpar ambiente de instalação
+web.custom_action.install_app=Instalar aplicação Web
+web.custom_action.install_app.invalid_url=O endereço {url} não foi encontrado. Não é possível finalizar a instalação.
+web.custom_action.install_app.status=Instalando aplicação Web
web.env.checking=Verificando o ambiente de instalação Web
web.env.error=Parce que existem problemas com o ambiente de instalação Web. Não será possível instalar {}.
web.environment.nativefier=Instalando {}
diff --git a/bauh/gems/web/resources/locale/ru b/bauh/gems/web/resources/locale/ru
index c8224f9f..e2cc09ee 100644
--- a/bauh/gems/web/resources/locale/ru
+++ b/bauh/gems/web/resources/locale/ru
@@ -4,6 +4,9 @@ web.custom_action.clean_env=Очистка среды установки
web.custom_action.clean_env.failed=Произошла ошибка при очистке среды установки
web.custom_action.clean_env.status=Среда установки будет очищена
web.custom_action.clean_env.success=Среда установки очищена!
+web.custom_action.install_app=Install Web application
+web.custom_action.install_app.invalid_url=The address {url} could not be found. It is not possible to finish the installation.
+web.custom_action.install_app.status=Installing Web application
web.env.checking=Проверка Веб-среды
web.env.error=Проблемы с Веб-средой. Невозможно установить {}.
web.environment.install=Устанавливается {}
diff --git a/bauh/gems/web/resources/locale/tr b/bauh/gems/web/resources/locale/tr
index 41120cf4..a38acc50 100644
--- a/bauh/gems/web/resources/locale/tr
+++ b/bauh/gems/web/resources/locale/tr
@@ -4,6 +4,9 @@ web.custom_action.clean_env.failed=Kurulum ortamı temizliği sırasında bir ha
web.custom_action.clean_env.status=Kurulum ortamını temizleme
web.custom_action.clean_env.success=Kurulum ortamı temizlendi
web.custom_action.clean_env=Temiz kurulum ortamı
+web.custom_action.install_app=Install Web application
+web.custom_action.install_app.invalid_url=The address {url} could not be found. It is not possible to finish the installation.
+web.custom_action.install_app.status=Installing Web application
web.env.checking=Web kurulum ortamını kontrol et
web.env.error=Görünüşe göre Web kurulum ortamıyla ilgili sorunlar var. {} kurmak mümkün olmayacak.
web.environment.install={} yükleniyor