mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-06 22:54:16 +02:00
0.8.1
This commit is contained in:
35
CHANGELOG.md
35
CHANGELOG.md
@@ -4,22 +4,43 @@ 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.8.1]
|
||||
## [0.8.1] 2020-01-14
|
||||
### Features:
|
||||
- Flatpak:
|
||||
- allow the user to choose the application installation level: **user** or **system** [#47](https://github.com/vinifmor/bauh/issues/47)
|
||||
- able to deal with user and system applications / runtimes [#47](https://github.com/vinifmor/bauh/issues/47)
|
||||
- able to list partial updates for Flatpak >= 1.4
|
||||
- new configuration file located at **~/.config/bauh/flatpak.yml** ( it allows to define a default installation level )
|
||||
|
||||
### Improvements
|
||||
- All icons are now SVG files
|
||||
- HDPI support ( by [octopusSD](https://github.com/octopusSD) )
|
||||
- HDPI support improvements ( by [octopusSD](https://github.com/octopusSD) )
|
||||
- Flatpak:
|
||||
- the application name tooltip now displays the installation level. e.g: **gedit ( system )**
|
||||
- info window displaying the installation level
|
||||
- "remote not set" warning dropped in favor of the new behavior: automatically adds Flathub as the default remote at the user level
|
||||
- Snap:
|
||||
- snapd checking routine refactored
|
||||
- Web:
|
||||
- not using HTTP sessions anymore to perform the searches. It seems to avoid URLs not being found after an internet drop event.
|
||||
- not using HTTP sessions anymore to perform the searches. It seems to avoid URLs not being found after an internet drop event
|
||||
- supporting JPEG images as custom icons
|
||||
- UI:
|
||||
- widgets visibility settings: the main widgets now should always be visible ( e.g: toolbar buttons )
|
||||
- scaling
|
||||
|
||||
### Fixes
|
||||
- missing categories i18n [48](https://github.com/vinifmor/bauh/issues/48)
|
||||
- not verifying if an icon path is a file
|
||||
- missing categories i18n [#48](https://github.com/vinifmor/bauh/issues/48)
|
||||
- Flatpak:
|
||||
- updating application dependencies during updating and downgrading
|
||||
- Web:
|
||||
- not handling HTTP connection issues
|
||||
- not passing the Home path as a String ( does not work in Python 3.5 )
|
||||
- not passing the Home path as a String for subprocesses ( an exception happens for Python 3.5 )
|
||||
- UI:
|
||||
- not verifying if an icon path is a file
|
||||
- minor fixes
|
||||
|
||||
### UI
|
||||
- Default **Type** icon removed from the Type filter to make the design more consistent with the Category filter.
|
||||
- Default **Type** icon removed from the Type filter to make the design more consistent
|
||||
|
||||
## [0.8.0] 2019-12-24
|
||||
### Features
|
||||
|
||||
@@ -21,6 +21,7 @@ To contribute with this project, have a look at [CONTRIBUTING.md](https://github
|
||||
- **pip3**
|
||||
- **python3-requests**
|
||||
- **python-yaml**
|
||||
- **qt5dxcb-plugin**
|
||||
- **python3-venv** ( only for [Manual installation](https://github.com/vinifmor/bauh/tree/wgem#manual-installation) )
|
||||
- **libappindicator3** ( for the **tray mode** in GTK3 desktop environments )
|
||||
|
||||
@@ -99,6 +100,11 @@ Before uninstalling bauh via your package manager, consider executing `bauh --re
|
||||
|
||||

|
||||
|
||||
- The configuration file is located at **~/.config/bauh/flatpak.yml** and it allows the following customizations:
|
||||
```
|
||||
installation_level: null # defines a default installation level: user or system. ( the popup will not be displayed if a value is defined )
|
||||
```
|
||||
|
||||
- Required dependencies:
|
||||
- Any distro: **flatpak**
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import List
|
||||
from typing import List, Tuple
|
||||
|
||||
from bauh.api.abstract.view import MessageType, ViewComponent
|
||||
|
||||
@@ -65,3 +65,9 @@ class ProcessWatcher:
|
||||
"""
|
||||
:return: if the use requested to stop the process.
|
||||
"""
|
||||
|
||||
def request_root_password(self) -> Tuple[str, bool]:
|
||||
"""
|
||||
asks the root password for the user
|
||||
:return: a tuple with the typed password and if it is valid
|
||||
"""
|
||||
@@ -175,6 +175,12 @@ class SoftwarePackage(ABC):
|
||||
"""
|
||||
return not self.installed
|
||||
|
||||
def get_name_tooltip(self) -> str:
|
||||
"""
|
||||
:return: the application name that should be displayed on the UI tooltips.
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
return '{} (id={}, name={})'.format(self.__class__.__name__, self.id, self.name)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from threading import Thread
|
||||
|
||||
import urllib3
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from bauh import __version__, __app_name__, app_args, ROOT_DIR
|
||||
@@ -15,7 +14,7 @@ from bauh.view.core.controller import GenericSoftwareManager
|
||||
from bauh.view.core.downloader import AdaptableFileDownloader
|
||||
from bauh.view.qt.systray import TrayIcon
|
||||
from bauh.view.qt.window import ManageWindow
|
||||
from bauh.view.util import util, logs, resource, translation
|
||||
from bauh.view.util import util, logs, translation
|
||||
from bauh.view.util.cache import DefaultMemoryCacheFactory, CacheCleaner
|
||||
from bauh.view.util.disk import DefaultDiskCacheLoaderFactory
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
5
bauh/commons/user.py
Normal file
5
bauh/commons/user.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import os
|
||||
|
||||
|
||||
def is_root():
|
||||
return os.getuid() == 0
|
||||
@@ -27,11 +27,10 @@ from bauh.gems.appimage.config import read_config
|
||||
from bauh.gems.appimage.model import AppImage
|
||||
from bauh.gems.appimage.worker import DatabaseUpdater
|
||||
|
||||
HOME_PATH = str(Path.home())
|
||||
DB_APPS_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/apps.db')
|
||||
DB_RELEASES_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/releases.db')
|
||||
DB_APPS_PATH = '{}/{}'.format(str(Path.home()), '.local/share/bauh/appimage/apps.db')
|
||||
DB_RELEASES_PATH = '{}/{}'.format(str(Path.home()), '.local/share/bauh/appimage/releases.db')
|
||||
|
||||
DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(HOME_PATH)
|
||||
DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(str(Path.home()))
|
||||
|
||||
RE_DESKTOP_EXEC = re.compile(r'Exec\s*=\s*.+\n')
|
||||
RE_DESKTOP_ICON = re.compile(r'Icon\s*=\s*.+\n')
|
||||
@@ -298,7 +297,7 @@ class AppImageManager(SoftwareManager):
|
||||
|
||||
file_path = out_dir + '/' + file_name
|
||||
downloaded = self.file_downloader.download(file_url=pkg.url_download, watcher=watcher,
|
||||
output_path=file_path, cwd=HOME_PATH)
|
||||
output_path=file_path, cwd=str(Path.home()))
|
||||
|
||||
if downloaded:
|
||||
watcher.change_substatus(self.i18n['appimage.install.permission'].format(bold(file_name)))
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
SUGGESTIONS_FILE = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/flatpak/suggestions.txt'
|
||||
CONFIG_FILE = '{}/.config/bauh/flatpak.yml'.format(Path.home())
|
||||
|
||||
7
bauh/gems/flatpak/config.py
Normal file
7
bauh/gems/flatpak/config.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from bauh.commons.config import read_config as read
|
||||
from bauh.gems.flatpak import CONFIG_FILE
|
||||
|
||||
|
||||
def read_config(update_file: bool = False) -> dict:
|
||||
template = {'installation_level': None}
|
||||
return read(CONFIG_FILE, template, update_file=update_file)
|
||||
@@ -9,9 +9,11 @@ from bauh.api.abstract.handler import ProcessWatcher
|
||||
from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \
|
||||
SuggestionPriority
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.commons import user
|
||||
from bauh.commons.html import strip_html, bold
|
||||
from bauh.commons.system import SystemProcess, ProcessHandler
|
||||
from bauh.gems.flatpak import flatpak, SUGGESTIONS_FILE
|
||||
from bauh.commons.system import SystemProcess, ProcessHandler, SimpleProcess
|
||||
from bauh.gems.flatpak import flatpak, SUGGESTIONS_FILE, CONFIG_FILE
|
||||
from bauh.gems.flatpak.config import read_config
|
||||
from bauh.gems.flatpak.constants import FLATHUB_API_URL
|
||||
from bauh.gems.flatpak.model import FlatpakApplication
|
||||
from bauh.gems.flatpak.worker import FlatpakAsyncDataLoader, FlatpakUpdateLoader
|
||||
@@ -37,7 +39,7 @@ class FlatpakManager(SoftwareManager):
|
||||
|
||||
def _map_to_model(self, app_json: dict, installed: bool, disk_loader: DiskCacheLoader, internet: bool = True) -> FlatpakApplication:
|
||||
|
||||
app = FlatpakApplication(**app_json)
|
||||
app = FlatpakApplication(**app_json, i18n=self.i18n)
|
||||
app.installed = installed
|
||||
api_data = self.api_cache.get(app_json['id'])
|
||||
|
||||
@@ -57,12 +59,27 @@ class FlatpakManager(SoftwareManager):
|
||||
|
||||
return app
|
||||
|
||||
def _get_search_remote(self) -> str:
|
||||
remotes = flatpak.list_remotes()
|
||||
|
||||
if remotes['system']:
|
||||
remote_level = 'system'
|
||||
elif remotes['user']:
|
||||
remote_level = 'user'
|
||||
else:
|
||||
remote_level = 'user'
|
||||
ProcessHandler().handle_simple(flatpak.set_default_remotes(remote_level))
|
||||
|
||||
return remote_level
|
||||
|
||||
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
|
||||
if is_url:
|
||||
return SearchResult([], [], 0)
|
||||
|
||||
remote_level = self._get_search_remote()
|
||||
|
||||
res = SearchResult([], [], 0)
|
||||
apps_found = flatpak.search(flatpak.get_version(), words)
|
||||
apps_found = flatpak.search(flatpak.get_version(), words, remote_level)
|
||||
|
||||
if apps_found:
|
||||
already_read = set()
|
||||
@@ -101,27 +118,44 @@ class FlatpakManager(SoftwareManager):
|
||||
models = []
|
||||
|
||||
if installed:
|
||||
update_map = None
|
||||
if thread_updates:
|
||||
thread_updates.join()
|
||||
update_map = updates[0]
|
||||
|
||||
for app_json in installed:
|
||||
model = self._map_to_model(app_json=app_json, installed=True,
|
||||
disk_loader=disk_loader, internet=internet_available)
|
||||
if version >= '1.5.0':
|
||||
model.update = '{}/{}'.format(app_json['id'], app_json['branch']) in updates[0] if updates else None
|
||||
else:
|
||||
model.update = app_json['ref'] in updates[0] if updates else None
|
||||
|
||||
model.update = None
|
||||
models.append(model)
|
||||
|
||||
if update_map and (update_map['full'] or update_map['partial']):
|
||||
if version >= '1.4.0':
|
||||
update_id = '{}/{}/{}'.format(app_json['id'], app_json['branch'], app_json['installation'])
|
||||
|
||||
if update_map['full'] and update_id in update_map['full']:
|
||||
model.update = True
|
||||
|
||||
if update_map['partial']:
|
||||
for partial in update_map['partial']:
|
||||
partial_data = partial.split('/')
|
||||
if app_json['id'] in partial_data[0] and\
|
||||
app_json['branch'] == partial_data[1] and\
|
||||
app_json['installation'] == partial_data[2]:
|
||||
partial_model = model.gen_partial(partial.split('/')[0])
|
||||
partial_model.update = True
|
||||
models.append(partial_model)
|
||||
else:
|
||||
model.update = '{}/{}'.format(app_json['installation'], app_json['ref']) in update_map['full']
|
||||
|
||||
return SearchResult(models, None, len(models))
|
||||
|
||||
def downgrade(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
pkg.commit = flatpak.get_commit(pkg.id, pkg.branch)
|
||||
pkg.commit = flatpak.get_commit(pkg.id, pkg.branch, pkg.installation)
|
||||
|
||||
watcher.change_progress(10)
|
||||
watcher.change_substatus(self.i18n['flatpak.downgrade.commits'])
|
||||
commits = flatpak.get_app_commits(pkg.ref, pkg.origin)
|
||||
commits = flatpak.get_app_commits(pkg.ref, pkg.origin, pkg.installation)
|
||||
|
||||
commit_idx = commits.index(pkg.commit)
|
||||
|
||||
@@ -133,7 +167,7 @@ class FlatpakManager(SoftwareManager):
|
||||
commit = commits[commit_idx + 1]
|
||||
watcher.change_substatus(self.i18n['flatpak.downgrade.reverting'])
|
||||
watcher.change_progress(50)
|
||||
success = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.downgrade(pkg.ref, commit, root_password),
|
||||
success = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.downgrade(pkg.ref, commit, pkg.installation, root_password),
|
||||
success_phrases=['Changes complete.', 'Updates complete.'],
|
||||
wrong_error_phrase='Warning'))
|
||||
watcher.change_progress(100)
|
||||
@@ -144,10 +178,10 @@ class FlatpakManager(SoftwareManager):
|
||||
self.api_cache.delete(pkg.id)
|
||||
|
||||
def update(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.update(pkg.ref)))
|
||||
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.update(pkg.ref, pkg.installation)))
|
||||
|
||||
def uninstall(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref)))
|
||||
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref, pkg.installation)))
|
||||
|
||||
if self.suggestions_cache:
|
||||
self.suggestions_cache.delete(pkg.id)
|
||||
@@ -156,11 +190,14 @@ class FlatpakManager(SoftwareManager):
|
||||
|
||||
def get_info(self, app: FlatpakApplication) -> dict:
|
||||
if app.installed:
|
||||
app_info = flatpak.get_app_info_fields(app.id, app.branch)
|
||||
app_info = flatpak.get_app_info_fields(app.id, app.branch, app.installation)
|
||||
app_info['name'] = app.name
|
||||
app_info['type'] = 'runtime' if app.runtime else 'app'
|
||||
app_info['description'] = strip_html(app.description) if app.description else ''
|
||||
|
||||
if app.installation:
|
||||
app_info['installation'] = app.installation
|
||||
|
||||
if app_info.get('installed'):
|
||||
app_info['installed'] = app_info['installed'].replace('?', ' ')
|
||||
|
||||
@@ -193,8 +230,8 @@ class FlatpakManager(SoftwareManager):
|
||||
return {}
|
||||
|
||||
def get_history(self, pkg: FlatpakApplication) -> PackageHistory:
|
||||
pkg.commit = flatpak.get_commit(pkg.id, pkg.branch)
|
||||
commits = flatpak.get_app_commits_data(pkg.ref, pkg.origin)
|
||||
pkg.commit = flatpak.get_commit(pkg.id, pkg.branch, pkg.installation)
|
||||
commits = flatpak.get_app_commits_data(pkg.ref, pkg.origin, pkg.installation)
|
||||
status_idx = 0
|
||||
|
||||
for idx, data in enumerate(commits):
|
||||
@@ -205,11 +242,56 @@ class FlatpakManager(SoftwareManager):
|
||||
return PackageHistory(pkg=pkg, history=commits, pkg_status_idx=status_idx)
|
||||
|
||||
def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
res = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.install(pkg.id, pkg.origin), wrong_error_phrase='Warning'))
|
||||
|
||||
config = read_config()
|
||||
|
||||
install_level = config['installation_level']
|
||||
|
||||
if install_level is not None:
|
||||
self.logger.info("Default Flaptak installation level defined: {}".format(install_level))
|
||||
|
||||
if install_level not in ('user', 'system'):
|
||||
watcher.show_message(title=self.i18n['error'].capitalize(),
|
||||
body=self.i18n['flatpak.install.bad_install_level.body'].format(field=bold('installation_level'),
|
||||
file=bold(CONFIG_FILE)),
|
||||
type_=MessageType.ERROR)
|
||||
return False
|
||||
|
||||
pkg.installation = install_level
|
||||
else:
|
||||
user_level = watcher.request_confirmation(title=self.i18n['flatpak.install.install_level.title'],
|
||||
body=self.i18n['flatpak.install.install_level.body'].format(bold(pkg.name)),
|
||||
confirmation_label=self.i18n['no'].capitalize(),
|
||||
deny_label=self.i18n['yes'].capitalize())
|
||||
pkg.installation = 'user' if user_level else 'system'
|
||||
|
||||
remotes = flatpak.list_remotes()
|
||||
|
||||
handler = ProcessHandler(watcher)
|
||||
|
||||
if pkg.installation == 'user' and not remotes['user']:
|
||||
handler.handle_simple(flatpak.set_default_remotes('user'))
|
||||
elif pkg.installation == 'system' and not remotes['system']:
|
||||
if user.is_root():
|
||||
handler.handle_simple(flatpak.set_default_remotes('system'))
|
||||
else:
|
||||
user_password, valid = watcher.request_root_password()
|
||||
if not valid:
|
||||
watcher.print('Operation aborted')
|
||||
return False
|
||||
else:
|
||||
if not handler.handle_simple(flatpak.set_default_remotes('system', user_password)):
|
||||
watcher.show_message(title=self.i18n['error'].capitalize(),
|
||||
body=self.i18n['flatpak.remotes.system_flathub.error'],
|
||||
type_=MessageType.ERROR)
|
||||
watcher.print("Operation cancelled")
|
||||
return False
|
||||
|
||||
res = handler.handle(SystemProcess(subproc=flatpak.install(str(pkg.id), pkg.origin, pkg.installation), wrong_error_phrase='Warning'))
|
||||
|
||||
if res:
|
||||
try:
|
||||
fields = flatpak.get_fields(pkg.id, pkg.branch, ['Ref', 'Branch'])
|
||||
fields = flatpak.get_fields(str(pkg.id), pkg.branch, ['Ref', 'Branch'])
|
||||
|
||||
if fields:
|
||||
pkg.ref = fields[0]
|
||||
@@ -229,7 +311,7 @@ class FlatpakManager(SoftwareManager):
|
||||
return flatpak.is_installed()
|
||||
|
||||
def requires_root(self, action: str, pkg: FlatpakApplication):
|
||||
return action == 'downgrade'
|
||||
return action == 'downgrade' and pkg.installation == 'system'
|
||||
|
||||
def prepare(self):
|
||||
pass
|
||||
@@ -253,17 +335,14 @@ class FlatpakManager(SoftwareManager):
|
||||
loader.join()
|
||||
|
||||
for app in to_update:
|
||||
updates.append(PackageUpdate(pkg_id='{}:{}'.format(app.id, app.branch),
|
||||
updates.append(PackageUpdate(pkg_id='{}:{}:{}'.format(app.id, app.branch, app.installation),
|
||||
pkg_type='flatpak',
|
||||
version=app.version))
|
||||
|
||||
return updates
|
||||
|
||||
def list_warnings(self, internet_available: bool) -> List[str]:
|
||||
if flatpak.is_installed():
|
||||
if not flatpak.has_remotes_set():
|
||||
return [self.i18n['flatpak.notification.no_remotes'],
|
||||
self.i18n['flatpak.notification.disable'].format(bold('Flatpak'), bold(self.i18n['manage_window.settings.gems']))]
|
||||
return []
|
||||
|
||||
def list_suggestions(self, limit: int, filter_installed: bool) -> List[PackageSuggestion]:
|
||||
cli_version = flatpak.get_version()
|
||||
@@ -277,6 +356,7 @@ class FlatpakManager(SoftwareManager):
|
||||
return res
|
||||
else:
|
||||
self.logger.info("Mapping suggestions")
|
||||
remote_level = self._get_search_remote()
|
||||
installed = {i.id for i in self.read_installed(disk_loader=None).installed} if filter_installed else None
|
||||
|
||||
for line in file.text.split('\n'):
|
||||
@@ -295,7 +375,7 @@ class FlatpakManager(SoftwareManager):
|
||||
if cached_sug:
|
||||
res.append(cached_sug)
|
||||
else:
|
||||
app_json = flatpak.search(cli_version, appid, app_id=True)
|
||||
app_json = flatpak.search(cli_version, appid, remote_level, app_id=True)
|
||||
|
||||
if app_json:
|
||||
model = PackageSuggestion(self._map_to_model(app_json[0], False, None), priority)
|
||||
@@ -310,7 +390,7 @@ class FlatpakManager(SoftwareManager):
|
||||
def is_default_enabled(self) -> bool:
|
||||
return True
|
||||
|
||||
def launch(self, pkg: SoftwarePackage):
|
||||
def launch(self, pkg: FlatpakApplication):
|
||||
flatpak.run(str(pkg.id))
|
||||
|
||||
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import re
|
||||
import subprocess
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from io import StringIO
|
||||
from typing import List
|
||||
from typing import List, Dict, Set
|
||||
|
||||
from bauh.api.exception import NoInternetException
|
||||
from bauh.commons.system import new_subprocess, run_cmd, new_root_subprocess
|
||||
from bauh.commons.system import new_subprocess, run_cmd, new_root_subprocess, SimpleProcess
|
||||
|
||||
BASE_CMD = 'flatpak'
|
||||
RE_SEVERAL_SPACES = re.compile(r'\s+')
|
||||
|
||||
|
||||
def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_runtime: bool = False):
|
||||
info = re.findall(r'\w+:\s.+', get_app_info(app_id, branch))
|
||||
def get_app_info_fields(app_id: str, branch: str, installation: str, fields: List[str] = [], check_runtime: bool = False):
|
||||
info = re.findall(r'\w+:\s.+', get_app_info(app_id, branch, installation))
|
||||
data = {}
|
||||
fields_to_retrieve = len(fields) + (1 if check_runtime and 'ref' not in fields else 0)
|
||||
|
||||
@@ -62,12 +62,12 @@ def get_version():
|
||||
return res.split(' ')[1].strip() if res else None
|
||||
|
||||
|
||||
def get_app_info(app_id: str, branch: str):
|
||||
return run_cmd('{} info {} {}'.format(BASE_CMD, app_id, branch))
|
||||
def get_app_info(app_id: str, branch: str, installation: str):
|
||||
return run_cmd('{} info {} {}'.format(BASE_CMD, app_id, branch, '--{}'.format(installation)))
|
||||
|
||||
|
||||
def get_commit(app_id: str, branch: str) -> str:
|
||||
info = new_subprocess([BASE_CMD, 'info', app_id, branch])
|
||||
def get_commit(app_id: str, branch: str, installation: str) -> str:
|
||||
info = new_subprocess([BASE_CMD, 'info', app_id, branch, '--{}'.format(installation)])
|
||||
|
||||
for o in new_subprocess(['grep', 'Commit:', '--color=never'], stdin=info.stdout).stdout:
|
||||
if o:
|
||||
@@ -96,6 +96,7 @@ def list_installed(version: str) -> List[dict]:
|
||||
'description': None,
|
||||
'origin': data[1],
|
||||
'runtime': runtime,
|
||||
'installation': 'user' if 'user' in data[5] else 'system',
|
||||
'version': ref_split[2] if runtime else None
|
||||
})
|
||||
|
||||
@@ -135,52 +136,100 @@ def list_installed(version: str) -> List[dict]:
|
||||
'description': data[4],
|
||||
'origin': data[5],
|
||||
'runtime': runtime,
|
||||
'installation': 'user' if 'user' in data[6] else 'system',
|
||||
'version': app_ver})
|
||||
return apps
|
||||
|
||||
|
||||
def update(app_ref: str):
|
||||
def update(app_ref: str, installation: str):
|
||||
"""
|
||||
Updates the app reference
|
||||
:param app_ref:
|
||||
:return:
|
||||
"""
|
||||
return new_subprocess([BASE_CMD, 'update', '--no-related', '-y', app_ref])
|
||||
return new_subprocess([BASE_CMD, 'update', '--no-related', '--no-deps', '-y', app_ref, '--{}'.format(installation)])
|
||||
|
||||
|
||||
def uninstall(app_ref: str):
|
||||
def register_flathub(installation: str) -> SimpleProcess:
|
||||
return SimpleProcess([BASE_CMD,
|
||||
'remote-add',
|
||||
'--if-not-exists',
|
||||
'flathub',
|
||||
'https://flathub.org/repo/flathub.flatpakrepo',
|
||||
'--{}'.format(installation)])
|
||||
|
||||
|
||||
def uninstall(app_ref: str, installation: str):
|
||||
"""
|
||||
Removes the app by its reference
|
||||
:param app_ref:
|
||||
:return:
|
||||
"""
|
||||
return new_subprocess([BASE_CMD, 'uninstall', app_ref, '-y'])
|
||||
return new_subprocess([BASE_CMD, 'uninstall', app_ref, '-y', '--{}'.format(installation)])
|
||||
|
||||
|
||||
def list_updates_as_str(version: str):
|
||||
def list_updates_as_str(version: str) -> Dict[str, set]:
|
||||
updates = read_updates(version, 'system')
|
||||
user_updates = read_updates(version, 'user')
|
||||
|
||||
for attr in ('full', 'partial'):
|
||||
updates[attr].update(user_updates[attr])
|
||||
|
||||
return updates
|
||||
|
||||
|
||||
def read_updates(version: str, installation: str) -> Dict[str, set]:
|
||||
res = {'partial': set(), 'full': set()}
|
||||
if version < '1.2':
|
||||
return run_cmd('{} update --no-related'.format(BASE_CMD), ignore_return_code=True)
|
||||
try:
|
||||
output = run_cmd('{} update --no-related --no-deps --{}'.format(BASE_CMD, installation), ignore_return_code=True)
|
||||
|
||||
if 'Updating in {}'.format(installation) in output:
|
||||
for line in output.split('Updating in {}:\n'.format(installation))[1].split('\n'):
|
||||
if not line.startswith('Is this ok'):
|
||||
res['full'].add('{}/{}'.format(installation, line.split('\t')[0].strip()))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
updates = new_subprocess([BASE_CMD, 'update']).stdout
|
||||
updates = new_subprocess([BASE_CMD, 'update', '--{}'.format(installation)]).stdout
|
||||
|
||||
out = StringIO()
|
||||
reg = r'[0-9]+\.\s+.+'
|
||||
|
||||
reg = r'[0-9]+\.\s+(\w+|\.)+\s+(\w|\.)+' if version >= '1.5.0' else r'[0-9]+\.\s+(\w+|\.)+\s+\w+\s+(\w|\.)+'
|
||||
try:
|
||||
for o in new_subprocess(['grep', '-E', reg, '-o', '--color=never'], stdin=updates).stdout:
|
||||
if o:
|
||||
line_split = o.decode().strip().split('\t')
|
||||
|
||||
for o in new_subprocess(['grep', '-E', reg, '-o', '--color=never'], stdin=updates).stdout:
|
||||
if o:
|
||||
out.write('/'.join(o.decode().strip().split('\t')[2:]) + '\n')
|
||||
if version >= '1.5.0':
|
||||
update_id = '{}/{}/{}'.format(line_split[2], line_split[3], installation)
|
||||
else:
|
||||
update_id = '{}/{}/{}'.format(line_split[2], line_split[4], installation)
|
||||
|
||||
out.seek(0)
|
||||
return out.read()
|
||||
if len(line_split) >= 6:
|
||||
if line_split[4] != 'i':
|
||||
if '(partial)' in line_split[-1]:
|
||||
res['partial'].add(update_id)
|
||||
else:
|
||||
res['full'].add(update_id)
|
||||
else:
|
||||
res['full'].add(update_id)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def downgrade(app_ref: str, commit: str, root_password: str) -> subprocess.Popen:
|
||||
return new_root_subprocess([BASE_CMD, 'update', '--no-related', '--commit={}'.format(commit), app_ref, '-y'], root_password)
|
||||
def downgrade(app_ref: str, commit: str, installation: str, root_password: str) -> subprocess.Popen:
|
||||
cmd = [BASE_CMD, 'update', '--no-related', '--no-deps', '--commit={}'.format(commit), app_ref, '-y', '--{}'.format(installation)]
|
||||
|
||||
if installation == 'system':
|
||||
return new_root_subprocess(cmd, root_password)
|
||||
else:
|
||||
return new_subprocess(cmd)
|
||||
|
||||
|
||||
def get_app_commits(app_ref: str, origin: str) -> List[str]:
|
||||
log = run_cmd('{} remote-info --log {} {}'.format(BASE_CMD, origin, app_ref))
|
||||
def get_app_commits(app_ref: str, origin: str, installation: str) -> List[str]:
|
||||
log = run_cmd('{} remote-info --log {} {} --{}'.format(BASE_CMD, origin, app_ref, installation))
|
||||
|
||||
if log:
|
||||
return re.findall(r'Commit+:\s(.+)', log)
|
||||
@@ -188,8 +237,8 @@ def get_app_commits(app_ref: str, origin: str) -> List[str]:
|
||||
raise NoInternetException()
|
||||
|
||||
|
||||
def get_app_commits_data(app_ref: str, origin: str) -> List[dict]:
|
||||
log = run_cmd('{} remote-info --log {} {}'.format(BASE_CMD, origin, app_ref))
|
||||
def get_app_commits_data(app_ref: str, origin: str, installation: str) -> List[dict]:
|
||||
log = run_cmd('{} remote-info --log {} {} --{}'.format(BASE_CMD, origin, app_ref, installation))
|
||||
|
||||
if not log:
|
||||
raise NoInternetException()
|
||||
@@ -214,9 +263,9 @@ def get_app_commits_data(app_ref: str, origin: str) -> List[dict]:
|
||||
return commits
|
||||
|
||||
|
||||
def search(version: str, word: str, app_id: bool = False) -> List[dict]:
|
||||
def search(version: str, word: str, installation: str, app_id: bool = False) -> List[dict]:
|
||||
|
||||
res = run_cmd('{} search {}'.format(BASE_CMD, word))
|
||||
res = run_cmd('{} search {} --{}'.format(BASE_CMD, word, installation))
|
||||
|
||||
found = []
|
||||
|
||||
@@ -293,17 +342,36 @@ def search(version: str, word: str, app_id: bool = False) -> List[dict]:
|
||||
return found
|
||||
|
||||
|
||||
def install(app_id: str, origin: str):
|
||||
return new_subprocess([BASE_CMD, 'install', origin, app_id, '-y'])
|
||||
def install(app_id: str, origin: str, installation: str):
|
||||
return new_subprocess([BASE_CMD, 'install', origin, app_id, '-y', '--{}'.format(installation)])
|
||||
|
||||
|
||||
def set_default_remotes():
|
||||
run_cmd('{} remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo'.format(BASE_CMD))
|
||||
def set_default_remotes(installation: str, root_password: str = None) -> SimpleProcess:
|
||||
cmd = [BASE_CMD, 'remote-add', '--if-not-exists', 'flathub', 'https://flathub.org/repo/flathub.flatpakrepo', '--{}'.format(installation)]
|
||||
return SimpleProcess(cmd, root_password=root_password)
|
||||
|
||||
|
||||
def has_remotes_set() -> bool:
|
||||
return bool(run_cmd('{} remotes'.format(BASE_CMD)).strip())
|
||||
|
||||
|
||||
def list_remotes() -> Dict[str, Set[str]]:
|
||||
res = {'system': set(), 'user': set()}
|
||||
output = run_cmd('{} remotes'.format(BASE_CMD)).strip()
|
||||
|
||||
if output:
|
||||
lines = output.split('\n')
|
||||
|
||||
for line in lines:
|
||||
remote = line.split('\t')
|
||||
|
||||
if 'system' in remote[1]:
|
||||
res['system'].add(remote[0].strip())
|
||||
elif 'user' in remote[1]:
|
||||
res['user'].add(remote[0].strip())
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def run(app_id: str):
|
||||
subprocess.Popen([BASE_CMD, 'run', app_id])
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from bauh.api.abstract.model import SoftwarePackage
|
||||
import copy
|
||||
|
||||
from bauh.api.abstract.model import SoftwarePackage
|
||||
from bauh.commons import resource
|
||||
from bauh.gems.flatpak import ROOT_DIR
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
|
||||
class FlatpakApplication(SoftwarePackage):
|
||||
|
||||
def __init__(self, id: str = None, name: str = None, version: str = None, latest_version: str = None, description: str = None,
|
||||
branch: str = None, arch: str = None, origin: str = None, runtime: bool = False, ref: str = None, commit: str = None):
|
||||
branch: str = None, arch: str = None, origin: str = None, runtime: bool = False, ref: str = None, commit: str = None,
|
||||
installation: str = None, i18n: I18n = None):
|
||||
super(FlatpakApplication, self).__init__(id=id, name=name, version=version,
|
||||
latest_version=latest_version, description=description)
|
||||
self.ref = ref
|
||||
@@ -16,6 +19,9 @@ class FlatpakApplication(SoftwarePackage):
|
||||
self.origin = origin
|
||||
self.runtime = runtime
|
||||
self.commit = commit
|
||||
self.partial = False
|
||||
self.installation = installation if installation else 'system'
|
||||
self.i18n = i18n
|
||||
|
||||
if runtime:
|
||||
self.categories = ['runtime']
|
||||
@@ -23,14 +29,14 @@ class FlatpakApplication(SoftwarePackage):
|
||||
def is_incomplete(self):
|
||||
return self.description is None and self.icon_url
|
||||
|
||||
def has_history(self):
|
||||
return self.installed and self.ref
|
||||
def has_history(self) -> bool:
|
||||
return not self.partial and self.installed and self.ref
|
||||
|
||||
def has_info(self):
|
||||
return bool(self.id)
|
||||
|
||||
def can_be_downgraded(self):
|
||||
return self.installed and self.ref
|
||||
return not self.partial and self.installed and self.ref
|
||||
|
||||
def get_type(self):
|
||||
return 'flatpak'
|
||||
@@ -63,7 +69,23 @@ class FlatpakApplication(SoftwarePackage):
|
||||
setattr(self, attr, data[attr])
|
||||
|
||||
def can_be_run(self) -> bool:
|
||||
return self.installed and not self.runtime
|
||||
return self.installed and not self.runtime and not self.partial
|
||||
|
||||
def get_publisher(self):
|
||||
return self.origin
|
||||
|
||||
def gen_partial(self, partial_id: str) -> "FlatpakApplication":
|
||||
partial = copy.deepcopy(self)
|
||||
partial.id = partial_id
|
||||
|
||||
if self.ref:
|
||||
partial.ref = '/'.join((partial_id, *self.ref.split('/')[1:]))
|
||||
|
||||
partial.partial = True
|
||||
return partial
|
||||
|
||||
def get_name_tooltip(self) -> str:
|
||||
if self.installation and self.i18n is not None:
|
||||
return '{} ( {} )'.format(self.name, self.i18n[self.installation.lower()])
|
||||
|
||||
return self.name
|
||||
|
||||
@@ -7,6 +7,8 @@ flatpak.info.date=data
|
||||
flatpak.info.description=descripció
|
||||
flatpak.info.id=id
|
||||
flatpak.info.installation=instal·lació
|
||||
flatpak.info.installation.user=usuari
|
||||
flatpak.info.installation.system=sistema
|
||||
flatpak.info.installed=instal·lat
|
||||
flatpak.info.license=llicència
|
||||
flatpak.info.name=nom
|
||||
@@ -40,3 +42,7 @@ flatpak.info.instoresincedate=afegit el
|
||||
flatpak.info.projectlicense=llicència
|
||||
flatpak.info.translateurl=Traducció
|
||||
flatpak.info.developername=desenvolupador
|
||||
flatpak.install.install_level.title=Tipus d'instal·lació
|
||||
flatpak.install.install_level.body=S'ha d'instal·lar {} per a tots els usuaris del dispositiu ( sistema ) ?
|
||||
flatpak.install.bad_install_level.body=Valor invàlid per a {field} al fitxer de configuració {file}
|
||||
flatpak.remotes.system_flathub.error=No s'ha pogut afegir Flathub com a dipòsit del sistema ( remote )
|
||||
@@ -26,6 +26,8 @@ flatpak.info.date=Datum
|
||||
flatpak.info.description=Beschreibung
|
||||
flatpak.info.id=Id
|
||||
flatpak.info.installation=Installation
|
||||
flatpak.info.installation.user=Benutzer
|
||||
flatpak.info.installation.system=System
|
||||
flatpak.info.installed=installiert
|
||||
flatpak.info.license=Lizenz
|
||||
flatpak.info.name=Name
|
||||
@@ -38,4 +40,8 @@ flatpak.info.subject=Thema
|
||||
flatpak.info.type=Typ
|
||||
flatpak.info.version=Version
|
||||
flatpak.history.date=Datum
|
||||
flatpak.history.commit=Commit
|
||||
flatpak.history.commit=Commit
|
||||
flatpak.install.install_level.title=Installationstyp
|
||||
flatpak.install.install_level.body=Sollte {} für alle Gerätebenutzer installiert werden ( system ) ?
|
||||
flatpak.install.bad_install_level.body=Ungültiger Wert für {field} in der Konfigurationsdatei {file}
|
||||
flatpak.remotes.system_flathub.error=Flathub konnte nicht als System-Repository ( remote ) hinzugefügt werden
|
||||
@@ -26,6 +26,8 @@ flatpak.info.date=date
|
||||
flatpak.info.description=description
|
||||
flatpak.info.id=id
|
||||
flatpak.info.installation=installation
|
||||
flatpak.info.installation.user=user
|
||||
flatpak.info.installation.system=system
|
||||
flatpak.info.installed=installed
|
||||
flatpak.info.license=license
|
||||
flatpak.info.name=name
|
||||
@@ -38,4 +40,8 @@ flatpak.info.subject=subject
|
||||
flatpak.info.type=type
|
||||
flatpak.info.version=version
|
||||
flatpak.history.date=date
|
||||
flatpak.history.commit=commit
|
||||
flatpak.history.commit=commit
|
||||
flatpak.install.install_level.title=Installation type
|
||||
flatpak.install.install_level.body=Should {} be installed for all the device users ( system ) ?
|
||||
flatpak.install.bad_install_level.body=Invalid value for {field} in the configuration file {file}
|
||||
flatpak.remotes.system_flathub.error=It was not possible to add Flathub as a system repository ( remote )
|
||||
@@ -7,6 +7,8 @@ flatpak.info.date=fecha
|
||||
flatpak.info.description=descripción
|
||||
flatpak.info.id=id
|
||||
flatpak.info.installation=instalación
|
||||
flatpak.info.installation.user=usuario
|
||||
flatpak.info.installation.system=sistema
|
||||
flatpak.info.installed=instalado
|
||||
flatpak.info.license=licencia
|
||||
flatpak.info.name=nombre
|
||||
@@ -39,4 +41,8 @@ flatpak.info.homepageurl=página
|
||||
flatpak.info.instoresincedate=agregado en
|
||||
flatpak.info.projectlicense=licencia
|
||||
flatpak.info.translateurl=Traducción
|
||||
flatpak.info.developername=desarrollador
|
||||
flatpak.info.developername=desarrollador
|
||||
flatpak.install.install_level.title=Tipo de instalación
|
||||
flatpak.install.install_level.body=¿Debería {} estar instalado para todos los usuarios del dispositivo ( sistema )?
|
||||
flatpak.install.bad_install_level.body=Valor inválido para {field} en el archivo de configuración {file}
|
||||
flatpak.remotes.system_flathub.error=No fue posible agregar Flathub como repositorio del sistema ( remote )
|
||||
@@ -18,3 +18,10 @@ flatpak.info.instoresincedate=aggiunto in
|
||||
flatpak.info.projectlicense=licenza
|
||||
flatpak.info.translateurl=traduci url
|
||||
flatpak.info.developername=sviluppatore
|
||||
flatpak.info.installation=installazione
|
||||
flatpak.info.installation.user=utente
|
||||
flatpak.info.installation.system=sistema
|
||||
flatpak.install.install_level.title=Tipo di installazione
|
||||
flatpak.install.install_level.body={} deve essere installato per tutti gli utenti del dispositivo ( sistema ) ?
|
||||
flatpak.install.bad_install_level.body=Valore non valido per {field} nel file di configurazione {file}
|
||||
flatpak.remotes.system_flathub.error=Non è stato possibile aggiungere Flathub come repository di sistema ( remote )
|
||||
@@ -7,6 +7,8 @@ flatpak.info.date=data
|
||||
flatpak.info.description=descrição
|
||||
flatpak.info.id=id
|
||||
flatpak.info.installation=instalação
|
||||
flatpak.info.installation.user=usuário
|
||||
flatpak.info.installation.system=sistema
|
||||
flatpak.info.installed=instalado
|
||||
flatpak.info.license=licença
|
||||
flatpak.info.name=nome
|
||||
@@ -39,4 +41,8 @@ flatpak.info.homepageurl=página
|
||||
flatpak.info.instoresincedate=adicionado em
|
||||
flatpak.info.projectlicense=licença
|
||||
flatpak.info.translateurl=tradução
|
||||
flatpak.info.developername=desenvolvedor
|
||||
flatpak.info.developername=desenvolvedor
|
||||
flatpak.install.install_level.title=Tipo de instalação
|
||||
flatpak.install.install_level.body={} deve ser instalado para todos os usuários desse dispositivo ( sistema ) ?
|
||||
flatpak.install.bad_install_level.body=Valor inválido para {field} no arquivo de configuração {file}
|
||||
flatpak.remotes.system_flathub.error=Não foi possível adicionar o Flathub como um repositório do sistema ( remote )
|
||||
@@ -9,7 +9,7 @@ from bauh.gems.snap.model import SnapApplication
|
||||
|
||||
BASE_CMD = 'snap'
|
||||
RE_SNAPD_STATUS = re.compile('\s+')
|
||||
SNAPD_RUNNING_STATUS = {'listening', 'running'}
|
||||
RE_SNAPD_SERVICES = re.compile(r'snapd\.\w+.+')
|
||||
|
||||
|
||||
def is_installed():
|
||||
@@ -18,24 +18,23 @@ def is_installed():
|
||||
|
||||
|
||||
def is_snapd_running() -> bool:
|
||||
services = new_subprocess(['systemctl', 'list-units'])
|
||||
output = run_cmd('systemctl list-units')
|
||||
|
||||
service, service_running = False, False
|
||||
socket, socket_running = False, False
|
||||
for o in new_subprocess(['grep', '-Eo', 'snapd.+'], stdin=services.stdout).stdout:
|
||||
if o:
|
||||
line = o.decode().strip()
|
||||
snapd_services = RE_SNAPD_SERVICES.findall(output)
|
||||
|
||||
if line:
|
||||
line_split = RE_SNAPD_STATUS.split(line)
|
||||
running = line_split[3] in SNAPD_RUNNING_STATUS
|
||||
socket, socket_running, service, service_running = False, False, False, False
|
||||
if snapd_services:
|
||||
for service_line in snapd_services:
|
||||
line_split = RE_SNAPD_STATUS.split(service_line)
|
||||
|
||||
if line_split[0] == 'snapd.service':
|
||||
service = True
|
||||
service_running = running
|
||||
elif line_split[0] == 'snapd.socket':
|
||||
socket = True
|
||||
socket_running = running
|
||||
running = line_split[3] in {'listening', 'running'}
|
||||
|
||||
if line_split[0] == 'snapd.service':
|
||||
service = True
|
||||
service_running = running
|
||||
elif line_split[0] == 'snapd.socket':
|
||||
socket = True
|
||||
socket_running = running
|
||||
|
||||
return socket and socket_running and (not service or service_running)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from pathlib import Path
|
||||
from threading import Thread
|
||||
from typing import List, Type, Set, Tuple
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
from colorama import Fore
|
||||
from requests import exceptions
|
||||
@@ -152,14 +153,14 @@ class WebApplicationManager(SoftwareManager):
|
||||
def serialize_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool):
|
||||
super(WebApplicationManager, self).serialize_to_disk(pkg=pkg, icon_bytes=None, only_icon=False)
|
||||
|
||||
def _map_url(self, url: str) -> "BeautifulSoup":
|
||||
def _map_url(self, url: str) -> Tuple["BeautifulSoup", requests.Response]:
|
||||
headers = {'Accept-language': self._get_lang_header(), 'User-Agent': UA_CHROME}
|
||||
|
||||
try:
|
||||
url_res = self.http_client.get(url, headers=headers, ignore_ssl=True, single_call=True, session=False)
|
||||
|
||||
if url_res:
|
||||
return BeautifulSoup(url_res.text, 'lxml', parse_only=SoupStrainer('head'))
|
||||
return BeautifulSoup(url_res.text, 'lxml', parse_only=SoupStrainer('head')), url_res
|
||||
except exceptions.ConnectionError as e:
|
||||
self.logger.warning("Could not get {}: {}".format(url, e.__class__.__name__))
|
||||
|
||||
@@ -182,14 +183,21 @@ class WebApplicationManager(SoftwareManager):
|
||||
if installed_matches:
|
||||
res.installed.extend(installed_matches)
|
||||
else:
|
||||
soup = self._map_url(url)
|
||||
soup_map = self._map_url(url)
|
||||
|
||||
if soup_map:
|
||||
soup, response = soup_map[0], soup_map[1]
|
||||
|
||||
final_url = response.url
|
||||
|
||||
if final_url.endswith('/'):
|
||||
final_url = final_url[0:-1]
|
||||
|
||||
if soup:
|
||||
name = self._get_app_name(url_no_protocol, soup)
|
||||
desc = self._get_app_description(url, soup)
|
||||
icon_url = self._get_app_icon_url(url, soup)
|
||||
desc = self._get_app_description(final_url, soup)
|
||||
icon_url = self._get_app_icon_url(final_url, soup)
|
||||
|
||||
app = WebApplication(url=url, name=name, description=desc, icon_url=icon_url)
|
||||
app = WebApplication(url=final_url, name=name, description=desc, icon_url=icon_url)
|
||||
|
||||
if self.env_settings.get('electron') and self.env_settings['electron'].get('version'):
|
||||
app.version = self.env_settings['electron']['version']
|
||||
@@ -420,7 +428,7 @@ class WebApplicationManager(SoftwareManager):
|
||||
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'])
|
||||
|
||||
icon_chooser = FileChooserComponent(allowed_extensions={'png', 'svg', 'ico', 'xpm'}, label=self.i18n['web.install.option.icon.label'])
|
||||
icon_chooser = FileChooserComponent(allowed_extensions={'png', 'svg', 'ico', 'jpg', 'jpeg'}, label=self.i18n['web.install.option.icon.label'])
|
||||
|
||||
form_1 = FormComponent(components=[inp_url, inp_name, inp_desc, inp_cat, inp_icon, icon_chooser, inp_tray], label=self.i18n['web.install.options.basic'].capitalize())
|
||||
|
||||
@@ -515,7 +523,7 @@ class WebApplicationManager(SoftwareManager):
|
||||
|
||||
def _ask_update_permission(self, to_update: List[EnvironmentComponent], watcher: ProcessWatcher) -> bool:
|
||||
|
||||
icon = resource.get_path('img/web.svg', ROOT_DIR)
|
||||
icon = resource.get_path('img/web.png', ROOT_DIR)
|
||||
opts = [InputOption(label='{} ( {} )'.format(f.name, f.size or '?'),
|
||||
tooltip=f.url, icon_path=icon, read_only=True, value=f.name) for f in to_update]
|
||||
|
||||
@@ -747,9 +755,16 @@ class WebApplicationManager(SoftwareManager):
|
||||
pass
|
||||
|
||||
def _fill_suggestion(self, app: WebApplication):
|
||||
soup = self._map_url(app.url)
|
||||
soup_map = self._map_url(app.url)
|
||||
|
||||
if soup_map:
|
||||
soup, res = soup_map[0], soup_map[1]
|
||||
|
||||
app.url = res.url
|
||||
|
||||
if app.url.endswith('/'):
|
||||
app.url = app.url[0:-1]
|
||||
|
||||
if soup:
|
||||
if not app.name:
|
||||
app.name = self._get_app_name(app.url, soup)
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class WebApplication(SoftwarePackage):
|
||||
self.set_custom_icon(self.custom_icon)
|
||||
|
||||
def can_be_run(self) -> bool:
|
||||
return self.installed and self.installation_dir
|
||||
return self.installed and bool(self.installation_dir)
|
||||
|
||||
def is_trustable(self) -> bool:
|
||||
return False
|
||||
|
||||
@@ -157,6 +157,7 @@ class GenericSoftwareManager(SoftwareManager):
|
||||
|
||||
disk_loader = None
|
||||
|
||||
internet_available = None
|
||||
if not pkg_types: # any type
|
||||
for man in self.managers:
|
||||
if self._can_work(man):
|
||||
@@ -164,11 +165,12 @@ class GenericSoftwareManager(SoftwareManager):
|
||||
disk_loader = self.disk_loader_factory.new()
|
||||
disk_loader.start()
|
||||
|
||||
if thread_internet_check.isAlive():
|
||||
if internet_available is None:
|
||||
thread_internet_check.join()
|
||||
internet_available = net_check.get('available', True)
|
||||
|
||||
mti = time.time()
|
||||
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=net_check['available'])
|
||||
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=internet_available)
|
||||
mtf = time.time()
|
||||
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(mtf - mti))
|
||||
|
||||
@@ -185,9 +187,12 @@ class GenericSoftwareManager(SoftwareManager):
|
||||
disk_loader = self.disk_loader_factory.new()
|
||||
disk_loader.start()
|
||||
|
||||
thread_internet_check.join()
|
||||
if internet_available is None:
|
||||
thread_internet_check.join()
|
||||
internet_available = net_check.get('available', True)
|
||||
|
||||
mti = time.time()
|
||||
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=net_check['available'])
|
||||
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=internet_available)
|
||||
mtf = time.time()
|
||||
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(mtf - mti))
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import operator
|
||||
import os
|
||||
from functools import reduce
|
||||
from threading import Lock
|
||||
from typing import List
|
||||
|
||||
@@ -6,7 +8,7 @@ from PyQt5.QtCore import Qt, QUrl, QSize
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QCursor
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
|
||||
QHeaderView, QLabel, QHBoxLayout, QPushButton, QToolBar
|
||||
QHeaderView, QLabel, QHBoxLayout, QPushButton, QToolBar, QSizePolicy
|
||||
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
from bauh.api.abstract.model import PackageStatus
|
||||
@@ -14,7 +16,6 @@ from bauh.commons.html import strip_html
|
||||
from bauh.view.qt import dialog
|
||||
from bauh.view.qt.components import IconButton
|
||||
from bauh.view.qt.view_model import PackageView, PackageViewStatus
|
||||
from bauh.view.qt.view_utils import load_resource_icon
|
||||
from bauh.view.util import resource
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
@@ -29,7 +30,7 @@ class UpdateToggleButton(QWidget):
|
||||
|
||||
def __init__(self, app_view: PackageView, root: QWidget, i18n: I18n, checked: bool = True, clickable: bool = True):
|
||||
super(UpdateToggleButton, self).__init__()
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.app_view = app_view
|
||||
self.root = root
|
||||
|
||||
@@ -74,9 +75,11 @@ class AppsTable(QTableWidget):
|
||||
self.setShowGrid(False)
|
||||
self.verticalHeader().setVisible(False)
|
||||
self.horizontalHeader().setVisible(False)
|
||||
self.horizontalHeader().setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.setHorizontalHeaderLabels(['' for _ in range(self.columnCount())])
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.icon_logo = QIcon(resource.get_path('img/logo.svg'))
|
||||
self.pixmap_verified = QIcon(resource.get_path('img/verified.svg')).pixmap(QSize(10, 10))
|
||||
|
||||
@@ -154,7 +157,9 @@ class AppsTable(QTableWidget):
|
||||
if app_v.status == PackageViewStatus.LOADING and app_v.model.status == PackageStatus.READY:
|
||||
|
||||
if self.download_icons:
|
||||
self.network_man.get(QNetworkRequest(QUrl(app_v.model.icon_url)))
|
||||
icon_request = QNetworkRequest(QUrl(app_v.model.icon_url))
|
||||
icon_request.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
|
||||
self.network_man.get(icon_request)
|
||||
|
||||
self._update_row(app_v, change_update_col=False)
|
||||
app_v.status = PackageViewStatus.READY
|
||||
@@ -289,7 +294,7 @@ class AppsTable(QTableWidget):
|
||||
pixmap = self.cache_type_icon.get(pkg.model.get_type())
|
||||
|
||||
if not pixmap:
|
||||
pixmap = QIcon(pkg.model.get_type_icon_path()).pixmap(QSize(14,14))
|
||||
pixmap = QIcon(pkg.model.get_type_icon_path()).pixmap(QSize(16, 16))
|
||||
self.cache_type_icon[pkg.model.get_type()] = pixmap
|
||||
|
||||
item = QLabel()
|
||||
@@ -328,7 +333,7 @@ class AppsTable(QTableWidget):
|
||||
|
||||
if pkg.model.name:
|
||||
name = pkg.model.name
|
||||
item.setToolTip('{}: {}'.format(self.i18n['app.name'].lower(), name))
|
||||
item.setToolTip('{}: {}'.format(self.i18n['app.name'].lower(), pkg.model.get_name_tooltip()))
|
||||
else:
|
||||
name = '...'
|
||||
item.setToolTip(self.i18n['app.name'].lower())
|
||||
@@ -341,8 +346,9 @@ class AppsTable(QTableWidget):
|
||||
|
||||
item.setText(name)
|
||||
|
||||
if self.disk_cache and pkg.model.supports_disk_cache() and pkg.model.get_disk_icon_path() and os.path.exists(pkg.model.get_disk_icon_path()):
|
||||
with open(pkg.model.get_disk_icon_path(), 'rb') as f:
|
||||
icon_path = pkg.model.get_disk_icon_path()
|
||||
if self.disk_cache and pkg.model.supports_disk_cache() and icon_path and os.path.isfile(icon_path):
|
||||
with open(icon_path, 'rb') as f:
|
||||
icon_bytes = f.read()
|
||||
pixmap = QPixmap()
|
||||
pixmap.loadFromData(icon_bytes)
|
||||
@@ -420,32 +426,38 @@ class AppsTable(QTableWidget):
|
||||
|
||||
def _set_col_settings(self, col: int, pkg: PackageView):
|
||||
item = QToolBar()
|
||||
item.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
|
||||
if pkg.model.can_be_run():
|
||||
|
||||
if pkg.model.installed:
|
||||
def run():
|
||||
self.window.run_app(pkg)
|
||||
|
||||
item.addWidget(IconButton(QIcon(resource.get_path('img/app_play.svg')), action=run, background='#088A08', tooltip=self.i18n['action.run.tooltip']))
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_play.svg')), i18n=self.i18n, action=run, background='#088A08', tooltip=self.i18n['action.run.tooltip'])
|
||||
bt.setEnabled(pkg.model.can_be_run())
|
||||
item.addWidget(bt)
|
||||
|
||||
if pkg.model.has_info():
|
||||
def get_info():
|
||||
self.window.get_app_info(pkg)
|
||||
|
||||
def get_info():
|
||||
self.window.get_app_info(pkg)
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_info.svg')), i18n=self.i18n, action=get_info, background='#2E68D3', tooltip=self.i18n['action.info.tooltip'])
|
||||
bt.setEnabled(bool(pkg.model.has_info()))
|
||||
item.addWidget(bt)
|
||||
|
||||
item.addWidget(IconButton(QIcon(resource.get_path('img/app_info.svg')), action=get_info, background='#2E68D3', tooltip=self.i18n['action.info.tooltip']))
|
||||
|
||||
if pkg.model.has_screenshots():
|
||||
if not pkg.model.installed:
|
||||
def get_screenshots():
|
||||
self.window.get_screenshots(pkg)
|
||||
|
||||
item.addWidget(IconButton(QIcon(resource.get_path('img/camera.svg')), action=get_screenshots, background='purple', tooltip=self.i18n['action.screenshots.tooltip']))
|
||||
bt = IconButton(QIcon(resource.get_path('img/camera.svg')), i18n=self.i18n, action=get_screenshots, background='purple', tooltip=self.i18n['action.screenshots.tooltip'])
|
||||
bt.setEnabled(bool(pkg.model.has_screenshots()))
|
||||
item.addWidget(bt)
|
||||
|
||||
def handle_click():
|
||||
self.show_pkg_settings(pkg)
|
||||
|
||||
if self.has_any_settings(pkg):
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_settings.svg')), action=handle_click, background='#12ABAB', tooltip=self.i18n['action.settings.tooltip'])
|
||||
settings = self.has_any_settings(pkg)
|
||||
if pkg.model.installed:
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_settings.svg')), i18n=self.i18n, action=handle_click, background='#12ABAB', tooltip=self.i18n['action.settings.tooltip'])
|
||||
bt.setEnabled(bool(settings))
|
||||
item.addWidget(bt)
|
||||
|
||||
self.setCellWidget(pkg.table_index, col, item)
|
||||
@@ -454,3 +466,6 @@ class AppsTable(QTableWidget):
|
||||
header_horizontal = self.horizontalHeader()
|
||||
for i in range(self.columnCount()):
|
||||
header_horizontal.setSectionResizeMode(i, policy)
|
||||
|
||||
def get_width(self):
|
||||
return reduce(operator.add, [self.columnWidth(i) for i in range(self.columnCount())])
|
||||
|
||||
@@ -241,14 +241,20 @@ class InputFilter(QLineEdit):
|
||||
|
||||
class IconButton(QWidget):
|
||||
|
||||
def __init__(self, icon: QIcon, action, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None):
|
||||
def __init__(self, icon: QIcon, action, i18n: I18n, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None):
|
||||
super(IconButton, self).__init__()
|
||||
self.bt = QToolButton()
|
||||
self.bt.setIcon(icon)
|
||||
self.bt.clicked.connect(action)
|
||||
self.i18n = i18n
|
||||
self.default_tootip = tooltip
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.bt.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
|
||||
if background:
|
||||
self.bt.setStyleSheet('QToolButton { color: white; background: ' + background + '}')
|
||||
style = 'QToolButton { color: white; background: ' + background + '} '
|
||||
style += 'QToolButton:disabled { color: white; background: grey }'
|
||||
self.bt.setStyleSheet(style)
|
||||
|
||||
if tooltip:
|
||||
self.bt.setToolTip(tooltip)
|
||||
@@ -259,6 +265,14 @@ class IconButton(QWidget):
|
||||
layout.addWidget(self.bt)
|
||||
self.setLayout(layout)
|
||||
|
||||
def setEnabled(self, enabled):
|
||||
super(IconButton, self).setEnabled(enabled)
|
||||
|
||||
if not enabled:
|
||||
self.bt.setToolTip(self.i18n['icon_button.tooltip.disabled'])
|
||||
else:
|
||||
self.bt.setToolTip(self.default_tootip)
|
||||
|
||||
|
||||
class FormQt(QGroupBox):
|
||||
|
||||
@@ -319,7 +333,7 @@ class FormQt(QGroupBox):
|
||||
options = QFileDialog.Options()
|
||||
|
||||
if c.allowed_extensions:
|
||||
exts = ';;'.join({'{} {} (*.{})'.format(self.i18n['files'].capitalize(), e.upper(), e) for e in c.allowed_extensions})
|
||||
exts = ';;'.join({'*.{}'.format(e) for e in c.allowed_extensions})
|
||||
else:
|
||||
exts = '{}} (*);;'.format(self.i18n['all_files'].capitalize())
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtCore import QSize, Qt
|
||||
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame, QSizePolicy
|
||||
from PyQt5.QtCore import QSize
|
||||
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
|
||||
|
||||
from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent, TextInputComponent, \
|
||||
FormComponent
|
||||
from bauh.view.qt import css
|
||||
from bauh.view.qt.components import MultipleSelectQt, new_single_select, TextInputQt, FormQt, new_spacer
|
||||
from bauh.view.qt.components import MultipleSelectQt, new_single_select, TextInputQt, FormQt
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ class ConfirmationDialog(QMessageBox):
|
||||
self.setStyleSheet('QLabel { margin-right: 25px; }')
|
||||
self.bt_yes = self.addButton(i18n['popup.button.yes'] if not confirmation_label else confirmation_label.capitalize(), QMessageBox.YesRole)
|
||||
self.bt_yes.setStyleSheet(css.OK_BUTTON)
|
||||
self.setDefaultButton(self.bt_yes)
|
||||
|
||||
self.addButton(i18n['popup.button.no'] if not deny_label else deny_label.capitalize(), QMessageBox.NoRole)
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(reso
|
||||
|
||||
bt_yes = diag.addButton(i18n['popup.button.yes'], QMessageBox.YesRole)
|
||||
bt_yes.setStyleSheet(css.OK_BUTTON)
|
||||
diag.setDefaultButton(bt_yes)
|
||||
|
||||
diag.addButton(i18n['popup.button.no'], QMessageBox.NoRole)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class ScreenshotsDialog(QDialog):
|
||||
|
||||
self.bottom_bar = QToolBar()
|
||||
|
||||
self.bt_back = QPushButton(self.i18n['screenshots.bt_back.label'].capitalize())
|
||||
self.bt_back = QPushButton(' < ' + self.i18n['screenshots.bt_back.label'].capitalize())
|
||||
self.bt_back.clicked.connect(self.back)
|
||||
self.ref_bt_back = self.bottom_bar.addWidget(self.bt_back)
|
||||
self.bottom_bar.addWidget(new_spacer(50))
|
||||
@@ -65,7 +65,7 @@ class ScreenshotsDialog(QDialog):
|
||||
self.ref_progress_bar = self.bottom_bar.addWidget(self.progress_bar)
|
||||
self.bottom_bar.addWidget(new_spacer(50))
|
||||
|
||||
self.bt_next = QPushButton(self.i18n['screenshots.bt_next.label'].capitalize())
|
||||
self.bt_next = QPushButton(self.i18n['screenshots.bt_next.label'].capitalize() + ' > ')
|
||||
self.bt_next.clicked.connect(self.next)
|
||||
self.ref_bt_next = self.bottom_bar.addWidget(self.bt_next)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Type, Set
|
||||
from typing import List, Type, Set, Tuple
|
||||
|
||||
import requests
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
@@ -28,11 +28,13 @@ class AsyncAction(QThread, ProcessWatcher):
|
||||
signal_status = pyqtSignal(str) # changes the GUI status message
|
||||
signal_substatus = pyqtSignal(str) # changes the GUI substatus message
|
||||
signal_progress = pyqtSignal(int)
|
||||
signal_root_password = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
super(AsyncAction, self).__init__()
|
||||
self.wait_confirmation = False
|
||||
self.confirmation_res = None
|
||||
self.root_password = None
|
||||
self.stop = False
|
||||
|
||||
def request_confirmation(self, title: str, body: str, components: List[InputViewComponent] = None, confirmation_label: str = None, deny_label: str = None) -> bool:
|
||||
@@ -41,10 +43,22 @@ class AsyncAction(QThread, ProcessWatcher):
|
||||
self.wait_user()
|
||||
return self.confirmation_res
|
||||
|
||||
def request_root_password(self) -> Tuple[str, bool]:
|
||||
self.wait_confirmation = True
|
||||
self.signal_root_password.emit()
|
||||
self.wait_user()
|
||||
res = self.root_password
|
||||
self.root_password = None
|
||||
return res
|
||||
|
||||
def confirm(self, res: bool):
|
||||
self.confirmation_res = res
|
||||
self.wait_confirmation = False
|
||||
|
||||
def set_root_password(self, password: str, valid: bool):
|
||||
self.root_password = (password, valid)
|
||||
self.wait_confirmation = False
|
||||
|
||||
def wait_user(self):
|
||||
while self.wait_confirmation:
|
||||
time.sleep(0.01)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import logging
|
||||
import operator
|
||||
import time
|
||||
from functools import reduce
|
||||
from pathlib import Path
|
||||
from typing import List, Type, Set
|
||||
|
||||
from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor
|
||||
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QCursor
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \
|
||||
QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction, QApplication, QListView
|
||||
|
||||
@@ -16,9 +14,10 @@ from bauh.api.abstract.controller import SoftwareManager
|
||||
from bauh.api.abstract.model import SoftwarePackage, PackageAction
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.api.http import HttpClient
|
||||
from bauh.commons import user
|
||||
from bauh.commons.html import bold
|
||||
from bauh.view.core.controller import GenericSoftwareManager
|
||||
from bauh.view.qt import dialog, commons, qt_utils
|
||||
from bauh.view.qt import dialog, commons, qt_utils, root
|
||||
from bauh.view.qt.about import AboutDialog
|
||||
from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton
|
||||
from bauh.view.qt.components import new_spacer, InputFilter, IconButton
|
||||
@@ -26,14 +25,14 @@ from bauh.view.qt.confirmation import ConfirmationDialog
|
||||
from bauh.view.qt.gem_selector import GemSelectorPanel
|
||||
from bauh.view.qt.history import HistoryDialog
|
||||
from bauh.view.qt.info import InfoDialog
|
||||
from bauh.view.qt.root import is_root, ask_root_password
|
||||
from bauh.view.qt.root import ask_root_password
|
||||
from bauh.view.qt.screenshots import ScreenshotsDialog
|
||||
from bauh.view.qt.styles import StylesComboBox
|
||||
from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
|
||||
GetAppHistory, SearchPackages, InstallPackage, AnimateProgress, VerifyModels, FindSuggestions, ListWarnings, \
|
||||
AsyncAction, LaunchApp, ApplyFilters, CustomAction, GetScreenshots
|
||||
from bauh.view.qt.view_model import PackageView
|
||||
from bauh.view.qt.view_utils import load_icon, load_resource_icon
|
||||
from bauh.view.qt.view_utils import load_icon
|
||||
from bauh.view.util import util, resource
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
@@ -48,6 +47,7 @@ class ManageWindow(QWidget):
|
||||
__BASE_HEIGHT__ = 400
|
||||
|
||||
signal_user_res = pyqtSignal(bool)
|
||||
signal_root_password = pyqtSignal(str, bool)
|
||||
signal_table_update = pyqtSignal()
|
||||
|
||||
def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager, screen_size, config: dict,
|
||||
@@ -118,8 +118,7 @@ class ManageWindow(QWidget):
|
||||
self.toolbar_search.addWidget(self.input_search)
|
||||
|
||||
label_pos_search = QLabel()
|
||||
# label_pos_search.setPixmap(QPixmap(resource.get_path('img/search.svg')))
|
||||
label_pos_search.setPixmap(QIcon(resource.get_path('img/search.svg')).pixmap(QSize(10,10)))
|
||||
label_pos_search.setPixmap(QIcon(resource.get_path('img/search.svg')).pixmap(QSize(10, 10)))
|
||||
label_pos_search.setStyleSheet("""
|
||||
background: white; padding-right: 10px;
|
||||
border-top-right-radius: 5px;
|
||||
@@ -158,7 +157,6 @@ class ManageWindow(QWidget):
|
||||
self.combo_filter_type.setEditable(True)
|
||||
self.combo_filter_type.lineEdit().setReadOnly(True)
|
||||
self.combo_filter_type.lineEdit().setAlignment(Qt.AlignCenter)
|
||||
self.combo_filter_type.setIconSize(QSize(14,14))
|
||||
self.combo_filter_type.activated.connect(self._handle_type_filter)
|
||||
self.combo_filter_type.addItem('--- {} ---'.format(self.i18n['type'].capitalize()), self.any_type_filter)
|
||||
self.ref_combo_filter_type = self.toolbar.addWidget(self.combo_filter_type)
|
||||
@@ -324,6 +322,7 @@ class ManageWindow(QWidget):
|
||||
bt_settings = IconButton(QIcon(resource.get_path('img/app_settings.svg')),
|
||||
action=self._show_settings_menu,
|
||||
background='#12ABAB',
|
||||
i18n=self.i18n,
|
||||
tooltip=self.i18n['manage_window.bt_settings.tooltip'])
|
||||
self.ref_bt_settings = self.toolbar_bottom.addWidget(bt_settings)
|
||||
|
||||
@@ -395,8 +394,10 @@ class ManageWindow(QWidget):
|
||||
action.signal_status.connect(self._change_label_status)
|
||||
action.signal_substatus.connect(self._change_label_substatus)
|
||||
action.signal_progress.connect(self._update_process_progress)
|
||||
action.signal_root_password.connect(self._ask_root_password)
|
||||
|
||||
self.signal_user_res.connect(action.confirm)
|
||||
self.signal_root_password.connect(action.set_root_password)
|
||||
|
||||
return action
|
||||
|
||||
@@ -413,6 +414,12 @@ class ManageWindow(QWidget):
|
||||
self.thread_animate_progress.animate()
|
||||
self.signal_user_res.emit(res)
|
||||
|
||||
def _ask_root_password(self):
|
||||
self.thread_animate_progress.pause()
|
||||
password, valid = root.ask_root_password(self.i18n)
|
||||
self.thread_animate_progress.animate()
|
||||
self.signal_root_password.emit(password, valid)
|
||||
|
||||
def _show_message(self, msg: dict):
|
||||
self.thread_animate_progress.pause()
|
||||
dialog.show_message(title=msg['title'], body=msg['body'], type_=msg['type'])
|
||||
@@ -548,7 +555,7 @@ class ManageWindow(QWidget):
|
||||
pwd = None
|
||||
requires_root = self.manager.requires_root('uninstall', app.model)
|
||||
|
||||
if not is_root() and requires_root:
|
||||
if not user.is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.i18n)
|
||||
|
||||
if not ok:
|
||||
@@ -780,7 +787,7 @@ class ManageWindow(QWidget):
|
||||
icon = self.cache_type_filter_icons.get(app_type)
|
||||
|
||||
if not icon:
|
||||
icon = QIcon(icon_path)
|
||||
icon = load_icon(icon_path, 14)
|
||||
self.cache_type_filter_icons[app_type] = icon
|
||||
|
||||
self.combo_filter_type.addItem(icon, app_type.capitalize(), app_type)
|
||||
@@ -831,20 +838,16 @@ class ManageWindow(QWidget):
|
||||
self.ref_combo_categories.setVisible(False)
|
||||
|
||||
def resize_and_center(self, accept_lower_width: bool = True):
|
||||
if self.pkgs:
|
||||
new_width = reduce(operator.add, [self.table_apps.columnWidth(i) for i in range(self.table_apps.columnCount())])
|
||||
table_width = self.table_apps.get_width()
|
||||
toolbar_width = self.toolbar.sizeHint().width()
|
||||
topbar_width = self.toolbar_top.sizeHint().width()
|
||||
|
||||
if self.ref_bt_upgrade.isVisible() or self.ref_bt_settings.isVisible():
|
||||
new_width *= 1.07
|
||||
else:
|
||||
new_width = self.toolbar_top.width()
|
||||
new_width = max(table_width, toolbar_width, topbar_width)
|
||||
new_width *= 1.05 # this extra size is not because of the toolbar button, but the table upgrade buttons
|
||||
|
||||
if accept_lower_width or new_width > self.width():
|
||||
if (self.pkgs and accept_lower_width) or new_width > self.width():
|
||||
self.resize(new_width, self.height())
|
||||
|
||||
if self.ref_bt_upgrade.isVisible() and self.bt_upgrade.visibleRegion().isEmpty():
|
||||
self.adjustSize()
|
||||
|
||||
qt_utils.centralize(self)
|
||||
|
||||
def update_selected(self):
|
||||
@@ -866,7 +869,7 @@ class ManageWindow(QWidget):
|
||||
widgets=[UpdateToggleButton(None, self, self.i18n, clickable=False)]):
|
||||
pwd = None
|
||||
|
||||
if not is_root() and requires_root:
|
||||
if not user.is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.i18n)
|
||||
|
||||
if not ok:
|
||||
@@ -984,7 +987,7 @@ class ManageWindow(QWidget):
|
||||
pwd = None
|
||||
requires_root = self.manager.requires_root('downgrade', pkgv.model)
|
||||
|
||||
if not is_root() and requires_root:
|
||||
if not user.is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.i18n)
|
||||
|
||||
if not ok:
|
||||
@@ -1077,7 +1080,7 @@ class ManageWindow(QWidget):
|
||||
pwd = None
|
||||
requires_root = self.manager.requires_root('install', pkg.model)
|
||||
|
||||
if not is_root() and requires_root:
|
||||
if not user.is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.i18n)
|
||||
|
||||
if not ok:
|
||||
@@ -1132,7 +1135,7 @@ class ManageWindow(QWidget):
|
||||
def execute_custom_action(self, pkg: PackageView, action: PackageAction):
|
||||
pwd = None
|
||||
|
||||
if not is_root() and action.requires_root:
|
||||
if not user.is_root() and action.requires_root:
|
||||
pwd, ok = ask_root_password(self.i18n)
|
||||
|
||||
if not ok:
|
||||
|
||||
@@ -48,7 +48,7 @@ popup.history.selected.tooltip=Versió actual
|
||||
popup.button.yes=Sí
|
||||
popup.button.no=No
|
||||
popup.button.cancel=Cancel·la
|
||||
tray.action.manage=Gestiona les aplicacions
|
||||
tray.action.manage=Aplicacions
|
||||
tray.action.exit=Surt
|
||||
tray.action.about=Quant a
|
||||
tray.action.refreshing=Refrescant
|
||||
@@ -217,4 +217,6 @@ files=fitxers
|
||||
all_files=tots els fitxers
|
||||
file_chooser.title=Selector de fitxers
|
||||
message.file.not_exist=Fitxer no existeix
|
||||
message.file.not_exist.body=El fitxer {} sembla no existir
|
||||
message.file.not_exist.body=El fitxer {} sembla no existir
|
||||
icon_button.tooltip.disabled=Aquesta acció no està disponible
|
||||
user=usuari
|
||||
@@ -48,7 +48,7 @@ popup.history.selected.tooltip=Aktuelle Version
|
||||
popup.button.yes=Ja
|
||||
popup.button.no=Nein
|
||||
popup.button.cancel=Abbrechen
|
||||
tray.action.manage=Anwendungen verwalten
|
||||
tray.action.manage=Anwendungen
|
||||
tray.action.exit=Beenden
|
||||
tray.action.about=Über
|
||||
tray.action.refreshing=Aktualisieren
|
||||
@@ -172,4 +172,6 @@ all_files=Alle Dateien
|
||||
file_chooser.title=Dateiauswahl
|
||||
message.file.not_exist=Datei existiert nicht
|
||||
message.file.not_exist.body=Die Datei {} scheint nicht zu existieren
|
||||
development=Entwicklung
|
||||
development=Entwicklung
|
||||
icon_button.tooltip.disabled=Diese Aktion ist nicht verfügbar
|
||||
user=Benutzer
|
||||
@@ -4,9 +4,9 @@ manage_window.columns.update=Upgrade ?
|
||||
manage_window.apps_table.row.actions.info=Information
|
||||
manage_window.apps_table.row.actions.history=History
|
||||
manage_window.apps_table.row.actions.uninstall.popup.title=Uninstall
|
||||
manage_window.apps_table.row.actions.uninstall.popup.body=Remove {} from your computer ?
|
||||
manage_window.apps_table.row.actions.uninstall.popup.body=Remove {} from your device ?
|
||||
manage_window.apps_table.row.actions.install.popup.title=Installation
|
||||
manage_window.apps_table.row.actions.install.popup.body=Install {} on your computer ?
|
||||
manage_window.apps_table.row.actions.install.popup.body=Install {} on your device ?
|
||||
manage_window.apps_table.row.actions.downgrade=Downgrade
|
||||
manage_window.apps_table.row.actions.downgrade.popup.body=Do you really want to downgrade {} ?
|
||||
manage_window.apps_table.row.actions.install=Install
|
||||
@@ -48,7 +48,7 @@ popup.history.selected.tooltip=Current version
|
||||
popup.button.yes=Yes
|
||||
popup.button.no=No
|
||||
popup.button.cancel=Cancel
|
||||
tray.action.manage=Manage applications
|
||||
tray.action.manage=Applications
|
||||
tray.action.exit=Quit
|
||||
tray.action.about=About
|
||||
tray.action.refreshing=Refreshing
|
||||
@@ -176,4 +176,6 @@ all_files=all files
|
||||
file_chooser.title=File selector
|
||||
message.file.not_exist=File does not exist
|
||||
message.file.not_exist.body=The file {} seems not to exist
|
||||
development=development
|
||||
development=development
|
||||
icon_button.tooltip.disabled=This action is unavailable
|
||||
user=user
|
||||
@@ -49,7 +49,7 @@ popup.history.selected.tooltip=Versión actual
|
||||
popup.button.yes=Sí
|
||||
popup.button.no=No
|
||||
popup.button.cancel=Cancelar
|
||||
tray.action.manage=Administrar aplicaciones
|
||||
tray.action.manage=Aplicaciones
|
||||
tray.action.exit=Cerrar
|
||||
tray.action.about=Acerca de
|
||||
tray.action.refreshing=Recargando
|
||||
@@ -216,4 +216,6 @@ files=archivos
|
||||
all_files=todos los archivos
|
||||
file_chooser.title=Selector de archivos
|
||||
message.file.not_exist=Archivo no existe
|
||||
message.file.not_exist.body=El archivo {} parece no existir
|
||||
message.file.not_exist.body=El archivo {} parece no existir
|
||||
icon_button.tooltip.disabled=This action is unavailable
|
||||
user=usuario
|
||||
@@ -4,9 +4,9 @@ manage_window.columns.update=Upgrade ?
|
||||
manage_window.apps_table.row.actions.info=Informazioni
|
||||
manage_window.apps_table.row.actions.history=Cronologia
|
||||
manage_window.apps_table.row.actions.uninstall.popup.title=Disinstalla
|
||||
manage_window.apps_table.row.actions.uninstall.popup.body=Remuovi {} dal tuo computer ?
|
||||
manage_window.apps_table.row.actions.uninstall.popup.body=Remuovi {} dal tuo dispositivo ?
|
||||
manage_window.apps_table.row.actions.install.popup.title=Installazione
|
||||
manage_window.apps_table.row.actions.install.popup.body=Installa {} nel tuo computer ?
|
||||
manage_window.apps_table.row.actions.install.popup.body=Installa {} nel tuo dispositivo ?
|
||||
manage_window.apps_table.row.actions.downgrade=Downgrade
|
||||
manage_window.apps_table.row.actions.downgrade.popup.body=Vuoi veramente fare il downgrade {} ?
|
||||
manage_window.apps_table.row.actions.install=Installa
|
||||
@@ -48,7 +48,7 @@ popup.history.selected.tooltip=Version corrente
|
||||
popup.button.yes=Si
|
||||
popup.button.no=No
|
||||
popup.button.cancel=Cancella
|
||||
tray.action.manage=Gestisci applicazioni
|
||||
tray.action.manage=Applicazioni
|
||||
tray.action.exit=Abbandona
|
||||
tray.action.about=Informazioni su
|
||||
tray.action.refreshing=Refreshing
|
||||
@@ -173,4 +173,6 @@ all_files=tutti i files
|
||||
file_chooser.title=Selettore file
|
||||
message.file.not_exist=File non esiste
|
||||
message.file.not_exist.body=Il file {} sembra non esistere
|
||||
development=sviluppo
|
||||
development=sviluppo
|
||||
icon_button.tooltip.disabled=Questa azione non è disponibile
|
||||
user=utente
|
||||
@@ -6,9 +6,9 @@ manage_window.apps_table.row.actions.info=Informação
|
||||
manage_window.apps_table.row.actions.history=Histórico
|
||||
manage_window.apps_table.row.actions.uninstall=Desinstalar
|
||||
manage_window.apps_table.row.actions.uninstall.popup.title=Desinstalação
|
||||
manage_window.apps_table.row.actions.uninstall.popup.body=Remover {} do seu computador ?
|
||||
manage_window.apps_table.row.actions.uninstall.popup.body=Remover {} do seu dispositivo ?
|
||||
manage_window.apps_table.row.actions.install.popup.title=Instalação
|
||||
manage_window.apps_table.row.actions.install.popup.body=Instalar {} no seu computador ?
|
||||
manage_window.apps_table.row.actions.install.popup.body=Instalar {} no seu dispositivo ?
|
||||
manage_window.apps_table.row.actions.downgrade=Reverter versão
|
||||
manage_window.apps_table.row.actions.downgrade.popup.body=Você realmente quer reverter a versão atual de {} ?
|
||||
manage_window.apps_table.row.actions.install=Instalar
|
||||
@@ -49,7 +49,7 @@ popup.history.selected.tooltip=Versão atual
|
||||
popup.button.yes=Sim
|
||||
popup.button.no=Não
|
||||
popup.button.cancel=Cancelar
|
||||
tray.action.manage=Gerenciar aplicativos
|
||||
tray.action.manage=Aplicativos
|
||||
tray.action.exit=Fechar
|
||||
tray.action.about=Sobre
|
||||
tray.action.refreshing=Recarregando
|
||||
@@ -219,4 +219,6 @@ files=arquivos
|
||||
all_files=todos os arquivos
|
||||
file_chooser.title=Seletor arquivos
|
||||
message.file.not_exist=Arquivo não existe
|
||||
message.file.not_exist.body=O arquivo {} parece não existir
|
||||
message.file.not_exist.body=O arquivo {} parece não existir
|
||||
icon_button.tooltip.disabled=Esta ação está indisponível
|
||||
user=usuário
|
||||
Reference in New Issue
Block a user