[wgem] allowing the user to give a custom name for the app

This commit is contained in:
Vinícius Moreira
2019-12-13 12:19:37 -03:00
parent af35b83d47
commit c07c6a0463
4 changed files with 74 additions and 39 deletions

View File

@@ -16,7 +16,7 @@ class HttpClient:
self.sleep = sleep self.sleep = sleep
self.logger = logger self.logger = logger
def get(self, url: str, params: dict = None, headers: dict = None, allow_redirects: bool = True): def get(self, url: str, params: dict = None, headers: dict = None, allow_redirects: bool = True, ignore_ssl: bool = False):
cur_attempts = 1 cur_attempts = 1
while cur_attempts <= self.max_attempts: while cur_attempts <= self.max_attempts:
@@ -31,6 +31,9 @@ class HttpClient:
if headers: if headers:
args['headers'] = headers args['headers'] = headers
if ignore_ssl:
args['verify'] = False
res = self.session.get(url, **args) res = self.session.get(url, **args)
if res.status_code == 200: if res.status_code == 200:

View File

@@ -5,6 +5,7 @@ import os
import re import re
import shutil import shutil
import subprocess import subprocess
import time
import traceback import traceback
from pathlib import Path from pathlib import Path
from threading import Thread from threading import Thread
@@ -16,7 +17,7 @@ from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory
from bauh.api.abstract.view import MessageType, MultipleSelectComponent, InputOption, SingleSelectComponent, \ from bauh.api.abstract.view import MessageType, MultipleSelectComponent, InputOption, SingleSelectComponent, \
SelectViewType SelectViewType, TextInputComponent
from bauh.commons.html import bold from bauh.commons.html import bold
from bauh.commons.system import ProcessHandler, get_dir_size, get_human_size_str from bauh.commons.system import ProcessHandler, get_dir_size, get_human_size_str
from bauh.gems.web import INSTALLED_PATH, nativefier, DESKTOP_ENTRY_PATH_PATTERN, URL_FIX_PATTERN from bauh.gems.web import INSTALLED_PATH, nativefier, DESKTOP_ENTRY_PATH_PATTERN, URL_FIX_PATTERN
@@ -37,6 +38,7 @@ except:
LXML_AVAILABLE = False LXML_AVAILABLE = False
RE_PROTOCOL_STRIP = re.compile(r'[a-zA-Z]+://') RE_PROTOCOL_STRIP = re.compile(r'[a-zA-Z]+://')
RE_SEVERAL_SPACES = re.compile(r'\s+')
class WebApplicationManager(SoftwareManager): class WebApplicationManager(SoftwareManager):
@@ -70,6 +72,12 @@ class WebApplicationManager(SoftwareManager):
if not name: if not name:
name = url_no_protocol.split('.')[0] name = url_no_protocol.split('.')[0]
if name:
name = name.replace('|', '').replace('_', ' ')
if ' ' in name:
name = RE_SEVERAL_SPACES.sub(' ', name)
return name return name
def _get_app_icon_url(self, url: str, soup: BeautifulSoup) -> str: def _get_app_icon_url(self, url: str, soup: BeautifulSoup) -> str:
@@ -100,12 +108,12 @@ class WebApplicationManager(SoftwareManager):
url_no_protocol = self._strip_url_protocol(url) url_no_protocol = self._strip_url_protocol(url)
installed_matches = [app for app in installed if self._strip_url_protocol(app.url)== url_no_protocol] installed_matches = [app for app in installed if self._strip_url_protocol(app.url) == url_no_protocol]
if installed_matches: if installed_matches:
res.installed.extend(installed_matches) res.installed.extend(installed_matches)
else: else:
url_res = self.http_client.get(url, headers={'Accept-language': self._get_lang_header()}) url_res = self.http_client.get(url, headers={'Accept-language': self._get_lang_header()}, ignore_ssl=True)
if url_res: if url_res:
soup = BeautifulSoup(url_res.text, 'lxml', parse_only=SoupStrainer('head')) soup = BeautifulSoup(url_res.text, 'lxml', parse_only=SoupStrainer('head'))
@@ -201,10 +209,10 @@ class WebApplicationManager(SoftwareManager):
def get_history(self, pkg: SoftwarePackage) -> PackageHistory: def get_history(self, pkg: SoftwarePackage) -> PackageHistory:
pass pass
def _ask_install_options(self, watcher: ProcessWatcher) -> Tuple[bool, List[str]]: def _ask_install_options(self, app: WebApplication, watcher: ProcessWatcher) -> Tuple[bool, List[str]]:
watcher.change_substatus(self.i18n['web.install.substatus.options']) watcher.change_substatus(self.i18n['web.install.substatus.options'])
bt_continue = self.i18n['continue'].capitalize() inp_name = TextInputComponent(label=self.i18n['name'], value=app.name)
op_single = InputOption(id_='single', label=self.i18n['web.install.option.single.label'], value="--single-instance", tooltip=self.i18n['web.install.option.single.tip']) op_single = InputOption(id_='single', label=self.i18n['web.install.option.single.label'], value="--single-instance", tooltip=self.i18n['web.install.option.single.tip'])
op_max = InputOption(id_='max', label=self.i18n['web.install.option.max.label'], value="--maximize", tooltip=self.i18n['web.install.option.max.tip']) op_max = InputOption(id_='max', label=self.i18n['web.install.option.max.label'], value="--maximize", tooltip=self.i18n['web.install.option.max.tip'])
@@ -222,9 +230,10 @@ class WebApplicationManager(SoftwareManager):
check_options = MultipleSelectComponent(options=[op_single, op_allow_urls, op_max, op_fs, op_nframe, op_ncache, op_insecure, op_igcert], default_options={op_single}, label='') check_options = MultipleSelectComponent(options=[op_single, op_allow_urls, op_max, op_fs, op_nframe, op_ncache, op_insecure, op_igcert], default_options={op_single}, label='')
input_tray = SingleSelectComponent(type_=SelectViewType.COMBO, options=[tray_op_off, tray_op_default, tray_op_min], label=self.i18n['web.install.option.tray.label']) input_tray = SingleSelectComponent(type_=SelectViewType.COMBO, options=[tray_op_off, tray_op_default, tray_op_min], label=self.i18n['web.install.option.tray.label'])
bt_continue = self.i18n['continue'].capitalize()
res = watcher.request_confirmation(title=self.i18n['web.install.options_dialog.title'], res = watcher.request_confirmation(title=self.i18n['web.install.options_dialog.title'],
body=self.i18n['web.install.options_dialog.body'].format(bold(bt_continue)), body=self.i18n['web.install.options_dialog.body'].format(bold(bt_continue)),
components=[check_options, input_tray], components=[inp_name, check_options, input_tray],
confirmation_label=bt_continue, confirmation_label=bt_continue,
deny_label=self.i18n['cancel'].capitalize()) deny_label=self.i18n['cancel'].capitalize())
@@ -238,13 +247,41 @@ class WebApplicationManager(SoftwareManager):
if tray_mode is not None and tray_mode != 0: if tray_mode is not None and tray_mode != 0:
selected.append(tray_mode) selected.append(tray_mode)
custom_name = inp_name.get_value()
if custom_name:
app.name = custom_name
return res, selected return res, selected
return False, [] return False, []
def _gen_app_id(self, name: str) -> Tuple[str, str]:
treated_name = name.lower().strip().replace(' ', '-')
while True:
random_number = str(int(time.time()))
app_id = '{}-{}'.format(random_number, treated_name)
if not os.path.exists('{}/{}'.format(INSTALLED_PATH, app_id)):
return app_id, treated_name
def _gen_desktop_entry_path(self, app_id: str) -> str:
base_id = app_id
counter = 1
while True:
desk_path = DESKTOP_ENTRY_PATH_PATTERN.format(name=base_id)
if not os.path.exists(desk_path):
return desk_path
else:
base_id = '{}_{}'.format(app_id, counter)
counter += 1
def install(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher) -> bool: def install(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher) -> bool:
continue_install, install_options = self._ask_install_options(watcher) continue_install, install_options = self._ask_install_options(pkg, watcher)
if not continue_install: if not continue_install:
watcher.print("Installation aborted by the user") watcher.print("Installation aborted by the user")
@@ -262,28 +299,22 @@ class WebApplicationManager(SoftwareManager):
Path(INSTALLED_PATH).mkdir(parents=True, exist_ok=True) Path(INSTALLED_PATH).mkdir(parents=True, exist_ok=True)
pkg_name = pkg.name.lower() app_id, treated_name = self._gen_app_id(pkg.name)
pkg.id = app_id
app_name_id, app_dir = pkg_name, '{}/{}'.format(INSTALLED_PATH, pkg_name) app_dir = '{}/{}'.format(INSTALLED_PATH, app_id)
counter = 1
while True:
if not os.path.exists(app_dir):
break
else:
app_name_id = pkg_name + str(counter)
app_dir = '{}/{}'.format(INSTALLED_PATH, app_name_id)
counter += 1
watcher.change_substatus(self.i18n['web.install.substatus.checking_fixes']) watcher.change_substatus(self.i18n['web.install.substatus.checking_fixes'])
fix = self._get_fix_for(url_no_protocol=self._strip_url_protocol(pkg.url)) fix = self._get_fix_for(url_no_protocol=self._strip_url_protocol(pkg.url))
fix_path = '{}/fix.js'.format(app_dir)
if fix: if fix:
# just adding the fix as an installation option. The file will be written later
self.logger.info('Fix found for {}'.format(pkg.url))
watcher.print('Fix found for {}'.format(pkg.url)) watcher.print('Fix found for {}'.format(pkg.url))
print(fix) install_options.append('--inject={}'.format(fix_path))
watcher.change_substatus(self.i18n['web.install.substatus.call_nativefier'].format(bold('nativefier'))) watcher.change_substatus(self.i18n['web.install.substatus.call_nativefier'].format(bold('nativefier')))
installed = handler.handle_simple(nativefier.install(url=pkg.url, name=pkg_name, output_dir=app_dir, installed = handler.handle_simple(nativefier.install(url=pkg.url, name=app_id, output_dir=app_dir,
electron_version=self.env_settings['electron']['version'], electron_version=self.env_settings['electron']['version'],
cwd=INSTALLED_PATH, cwd=INSTALLED_PATH,
extra_options=install_options)) extra_options=install_options))
@@ -304,11 +335,17 @@ class WebApplicationManager(SoftwareManager):
# bringing the inner app folder to the 'installed' folder level: # bringing the inner app folder to the 'installed' folder level:
inner_dir = '{}/{}'.format(app_dir, inner_dir[0]) inner_dir = '{}/{}'.format(app_dir, inner_dir[0])
temp_dir = '{}/tmp_{}'.format(INSTALLED_PATH, app_name_id) temp_dir = '{}/tmp_{}'.format(INSTALLED_PATH, treated_name)
os.rename(inner_dir, temp_dir) os.rename(inner_dir, temp_dir)
shutil.rmtree(app_dir) shutil.rmtree(app_dir)
os.rename(temp_dir, app_dir) os.rename(temp_dir, app_dir)
# injecting a fix
if fix:
self.logger.info('Writting JS fix at {}'.format(fix_path))
with open(fix_path, 'w+') as f:
f.write(fix)
pkg.installation_dir = app_dir pkg.installation_dir = app_dir
version_path = '{}/version'.format(app_dir) version_path = '{}/version'.format(app_dir)
@@ -320,22 +357,14 @@ class WebApplicationManager(SoftwareManager):
watcher.change_substatus(self.i18n['web.install.substatus.shortcut']) watcher.change_substatus(self.i18n['web.install.substatus.shortcut'])
entry_id = app_name_id desktop_entry_path = self._gen_desktop_entry_path(app_id)
desktop_entry = DESKTOP_ENTRY_PATH_PATTERN.format(name=entry_id)
while True:
if not os.path.exists(desktop_entry):
break
else:
counter += 1
entry_id = pkg_name + str(counter)
desktop_entry += DESKTOP_ENTRY_PATH_PATTERN.format(name=entry_id)
entry_content = self._gen_desktop_entry_content(pkg) entry_content = self._gen_desktop_entry_content(pkg)
with open(desktop_entry, 'w+') as f: with open(desktop_entry_path, 'w+') as f:
f.write(entry_content) f.write(entry_content)
pkg.desktop_entry = desktop_entry pkg.desktop_entry = desktop_entry_path
return True return True
def _gen_desktop_entry_content(self, pkg: WebApplication) -> str: def _gen_desktop_entry_content(self, pkg: WebApplication) -> str:

View File

@@ -7,9 +7,9 @@ from bauh.gems.web import ROOT_DIR
class WebApplication(SoftwarePackage): class WebApplication(SoftwarePackage):
def __init__(self, url: str = None, name: str = None, description: str = None, icon_url: str = None, def __init__(self, id: str = None, url: str = None, name: str = None, description: str = None, icon_url: str = None,
installation_dir: str = None, desktop_entry: str = None, installed: bool = False, version: str = None): installation_dir: str = None, desktop_entry: str = None, installed: bool = False, version: str = None):
super(WebApplication, self).__init__(id=url, name=name, description=description, super(WebApplication, self).__init__(id=id if id else url, name=name, description=description,
icon_url=icon_url, installed=installed, version=version) icon_url=icon_url, installed=installed, version=version)
self.url = url self.url = url
self.installation_dir = installation_dir self.installation_dir = installation_dir
@@ -23,14 +23,14 @@ class WebApplication(SoftwarePackage):
@staticmethod @staticmethod
def _get_cached_attrs() -> tuple: def _get_cached_attrs() -> tuple:
return 'name', 'version', 'url', 'description', 'icon_url', 'installation_dir', 'desktop_entry' return 'id', 'name', 'version', 'url', 'description', 'icon_url', 'installation_dir', 'desktop_entry'
def can_be_downgraded(self): def can_be_downgraded(self):
return False return False
def get_exec_path(self) -> str: def get_exec_path(self) -> str:
if self.installation_dir: if self.installation_dir:
return '{}/{}'.format(self.installation_dir, self.installation_dir.split('/')[-1]) return '{}/{}'.format(self.installation_dir, self.id)
def get_type(self): def get_type(self):
return 'web' return 'web'

View File

@@ -123,7 +123,7 @@ class TextInputQt(QGroupBox):
self.model = model self.model = model
self.setLayout(QGridLayout()) self.setLayout(QGridLayout())
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}') self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
self.layout().addWidget(QLabel(model.label + ' :'), 0, 0) self.layout().addWidget(QLabel(model.label.capitalize() + ' :' if model.label else ''), 0, 0)
self.text_input = QLineEdit() self.text_input = QLineEdit()
@@ -133,6 +133,9 @@ class TextInputQt(QGroupBox):
if model.tooltip: if model.tooltip:
self.text_input.setToolTip(model.tooltip) self.text_input.setToolTip(model.tooltip)
if model.value:
self.text_input.setText(model.value)
self.text_input.textChanged.connect(self._update_model) self.text_input.textChanged.connect(self._update_model)
self.layout().addWidget(self.text_input, 0, 1) self.layout().addWidget(self.text_input, 0, 1)