mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-08 16:44:16 +02:00
Downgrade
This commit is contained in:
@@ -8,6 +8,7 @@ from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidg
|
||||
|
||||
from fpakman.core import resource
|
||||
from fpakman.core.controller import FlatpakController
|
||||
from fpakman.view.qt import dialog
|
||||
|
||||
|
||||
class UpdateToggleButton(QToolButton):
|
||||
@@ -48,6 +49,7 @@ class AppsTable(QTableWidget):
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.icon_flathub = QIcon(resource.get_path('img/flathub_45.svg'))
|
||||
self.icon_uninstall = QIcon(resource.get_path('img/uninstall.svg'))
|
||||
self.icon_downgrade = QIcon(resource.get_path('img/downgrade.svg'))
|
||||
|
||||
self.network_man = QNetworkAccessManager()
|
||||
self.network_man.finished.connect(self._load_icon)
|
||||
@@ -60,21 +62,39 @@ class AppsTable(QTableWidget):
|
||||
action_uninstall = QAction(self.parent.locale_keys["manage_window.apps_table.row.actions.uninstall"])
|
||||
action_uninstall.setIcon(self.icon_uninstall)
|
||||
action_uninstall.triggered.connect(self._uninstall_app)
|
||||
|
||||
menu_row.addAction(action_uninstall)
|
||||
|
||||
app = self._get_selected_app()
|
||||
|
||||
if not app['model']['runtime']: # downgrade only allowed for apps
|
||||
action_downgrade = QAction(self.parent.locale_keys["manage_window.apps_table.row.actions.downgrade"])
|
||||
action_downgrade.triggered.connect(self._downgrade_app)
|
||||
action_downgrade.setIcon(self.icon_downgrade)
|
||||
menu_row.addAction(action_downgrade)
|
||||
|
||||
menu_row.adjustSize()
|
||||
menu_row.popup(QCursor.pos())
|
||||
menu_row.exec_()
|
||||
|
||||
def _uninstall_app(self):
|
||||
selected_app = self.parent.apps[self.currentRow()]
|
||||
def _get_selected_app(self):
|
||||
return self.parent.apps[self.currentRow()]
|
||||
|
||||
confirmation = QMessageBox.question(self, self.parent.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.title'],
|
||||
self.parent.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(selected_app['model']['name']),
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||
if confirmation == QMessageBox.Yes:
|
||||
def _uninstall_app(self):
|
||||
selected_app = self._get_selected_app()
|
||||
|
||||
if dialog.ask_confirmation(title=self.parent.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.title'],
|
||||
body=self.parent.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(selected_app['model']['name']),
|
||||
locale_keys=self.parent.locale_keys):
|
||||
self.parent.uninstall_app(selected_app['model']['ref'])
|
||||
|
||||
def _downgrade_app(self):
|
||||
selected_app = self._get_selected_app()
|
||||
|
||||
if dialog.ask_confirmation(title=self.parent.locale_keys['manage_window.apps_table.row.actions.downgrade'],
|
||||
body=self.parent.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app['model']['name']),
|
||||
locale_keys=self.parent.locale_keys):
|
||||
self.parent.downgrade_app(selected_app['model']['ref'])
|
||||
|
||||
def _load_icon(self, http_response):
|
||||
icon_url = http_response.url().toString()
|
||||
pixmap = QPixmap()
|
||||
@@ -122,7 +142,7 @@ class AppsTable(QTableWidget):
|
||||
col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||
self.setItem(idx, 2, col_release)
|
||||
|
||||
if app['model']['update']:
|
||||
if app['model']['version'] and app['model']['latest_version'] and app['model']['version'] < app['model']['latest_version']:
|
||||
col_release.setForeground(QColor('orange'))
|
||||
|
||||
col_branch = QTableWidgetItem()
|
||||
|
||||
31
fpakman/view/qt/dialog.py
Normal file
31
fpakman/view/qt/dialog.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from fpakman.core import resource
|
||||
|
||||
|
||||
def show_error(title: str, body: str, icon: QIcon = QIcon(resource.get_path('img/flathub_45.svg'))):
|
||||
error_msg = QMessageBox()
|
||||
error_msg.setIcon(QMessageBox.Critical)
|
||||
error_msg.setWindowTitle(title)
|
||||
error_msg.setText(body)
|
||||
|
||||
if icon:
|
||||
error_msg.setWindowIcon(icon)
|
||||
error_msg.exec_()
|
||||
|
||||
|
||||
def ask_confirmation(title: str, body: str, locale_keys: dict, icon: QIcon = QIcon(resource.get_path('img/flathub_45.svg'))):
|
||||
dialog_confirmation = QMessageBox()
|
||||
dialog_confirmation.setIcon(QMessageBox.Critical)
|
||||
dialog_confirmation.setWindowTitle(title)
|
||||
dialog_confirmation.setText(body)
|
||||
bt_yes = dialog_confirmation.addButton(locale_keys['popup.button.yes'], QMessageBox.YesRole)
|
||||
dialog_confirmation.addButton(locale_keys['popup.button.no'], QMessageBox.NoRole)
|
||||
|
||||
if icon:
|
||||
dialog_confirmation.setWindowIcon(icon)
|
||||
|
||||
dialog_confirmation.exec_()
|
||||
|
||||
return dialog_confirmation.clickedButton() == bt_yes
|
||||
39
fpakman/view/qt/root.py
Normal file
39
fpakman/view/qt/root.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from PyQt5.QtWidgets import QInputDialog, QLineEdit
|
||||
|
||||
from fpakman.view.qt.dialog import show_error
|
||||
|
||||
|
||||
def is_root():
|
||||
return os.getuid() == 0
|
||||
|
||||
|
||||
def ask_root_password(locale_keys: dict):
|
||||
|
||||
dialog_pwd = QInputDialog()
|
||||
dialog_pwd.setInputMode(QInputDialog.TextInput)
|
||||
dialog_pwd.setTextEchoMode(QLineEdit.Password)
|
||||
dialog_pwd.setWindowTitle(locale_keys['popup.root.title'])
|
||||
dialog_pwd.setLabelText(locale_keys['popup.root.password'] + ':')
|
||||
dialog_pwd.setCancelButtonText(locale_keys['popup.button.cancel'])
|
||||
dialog_pwd.resize(400, 200)
|
||||
|
||||
ok = dialog_pwd.exec_()
|
||||
|
||||
if ok:
|
||||
if not validate_password(dialog_pwd.textValue()):
|
||||
show_error(title=locale_keys['popup.root.bad_password.title'],
|
||||
body=locale_keys['popup.root.bad_password.body'])
|
||||
ok = False
|
||||
|
||||
return dialog_pwd.textValue(), ok
|
||||
|
||||
|
||||
def validate_password(password: str) -> bool:
|
||||
f = os.popen('echo {} | sudo -S whoami'.format(password))
|
||||
res = f.read()
|
||||
f.close()
|
||||
|
||||
return bool(res.strip())
|
||||
@@ -4,6 +4,7 @@ import time
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
|
||||
from fpakman.core.model import FlatpakManager
|
||||
|
||||
from fpakman.core import resource
|
||||
from fpakman.core.controller import FlatpakController
|
||||
@@ -34,10 +35,11 @@ class UpdateCheck(QThread):
|
||||
|
||||
class TrayIcon(QSystemTrayIcon):
|
||||
|
||||
def __init__(self, locale_keys: dict, controller: FlatpakController, check_interval: int = 60):
|
||||
def __init__(self, locale_keys: dict, controller: FlatpakController, manager: FlatpakManager, check_interval: int = 60):
|
||||
super(TrayIcon, self).__init__()
|
||||
self.locale_keys = locale_keys
|
||||
self.controller = controller
|
||||
self.manager = manager
|
||||
|
||||
self.icon_default = QIcon(resource.get_path('img/flathub_45.svg'))
|
||||
self.icon_update = QIcon(resource.get_path('img/update_logo.svg'))
|
||||
@@ -50,7 +52,7 @@ class TrayIcon(QSystemTrayIcon):
|
||||
self.action_exit.triggered.connect(lambda: QCoreApplication.exit())
|
||||
self.setContextMenu(self.menu)
|
||||
|
||||
self.manage_window = ManageWindow(locale_keys=self.locale_keys, controller=controller, tray_icon=self)
|
||||
self.manage_window = ManageWindow(locale_keys=self.locale_keys, controller=controller, manager=self.manager, tray_icon=self)
|
||||
self.check_thread = UpdateCheck(check_interval=check_interval, controller=self.controller)
|
||||
self.check_thread.signal.connect(self.notify_updates)
|
||||
self.check_thread.start()
|
||||
|
||||
@@ -7,18 +7,21 @@ from PyQt5.QtCore import QThread, pyqtSignal, QEvent
|
||||
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
|
||||
QSizePolicy, QLabel, QMessageBox, QPlainTextEdit
|
||||
from fpakman.core.model import FlatpakManager, ImpossibleDowngradeException
|
||||
|
||||
from fpakman import __version__
|
||||
from fpakman.core import resource
|
||||
from fpakman.core.controller import FlatpakController
|
||||
from fpakman.view.qt import dialog
|
||||
from fpakman.view.qt.apps_table import AppsTable
|
||||
from fpakman.view.qt.root import is_root, ask_root_password
|
||||
|
||||
|
||||
class ManageWindow(QWidget):
|
||||
|
||||
__BASE_HEIGHT__ = 400
|
||||
|
||||
def __init__(self, locale_keys: dict, controller: FlatpakController, tray_icon = None):
|
||||
def __init__(self, locale_keys: dict, controller: FlatpakController, manager: FlatpakManager, tray_icon = None):
|
||||
super(ManageWindow, self).__init__()
|
||||
self.locale_keys = locale_keys
|
||||
self.column_names = [locale_keys['manage_window.columns.name'],
|
||||
@@ -30,6 +33,7 @@ class ManageWindow(QWidget):
|
||||
locale_keys['manage_window.columns.origin'],
|
||||
locale_keys['manage_window.columns.update']]
|
||||
self.controller = controller
|
||||
self.manager = manager
|
||||
self.tray_icon = tray_icon
|
||||
self.thread_lock = Lock()
|
||||
self.working = False # restrict the number of threaded actions
|
||||
@@ -104,6 +108,10 @@ class ManageWindow(QWidget):
|
||||
self.thread_uninstall.signal_output.connect(self._update_action_output)
|
||||
self.thread_uninstall.signal_finished.connect(self._finish_uninstall)
|
||||
|
||||
self.thread_downgrade = DowngradeApp(self.manager)
|
||||
self.thread_downgrade.signal_output.connect(self._update_action_output)
|
||||
self.thread_downgrade.signal_finished.connect(self._finish_downgrade)
|
||||
|
||||
self.toolbar_bottom = QToolBar()
|
||||
self.label_updates = QLabel('')
|
||||
self.toolbar_bottom.addWidget(self.label_updates)
|
||||
@@ -132,12 +140,9 @@ class ManageWindow(QWidget):
|
||||
def _check_flatpak_installed(self):
|
||||
|
||||
if not self.controller.check_installed():
|
||||
error_msg = QMessageBox()
|
||||
error_msg.setIcon(QMessageBox.Critical)
|
||||
error_msg.setWindowTitle(self.locale_keys['popup.flatpak_not_installed.title'])
|
||||
error_msg.setText(self.locale_keys['popup.flatpak_not_installed.msg'] + '...')
|
||||
error_msg.setWindowIcon(self.icon_flathub)
|
||||
error_msg.exec_()
|
||||
dialog.show_error(title=self.locale_keys['popup.flatpak_not_installed.title'],
|
||||
body=self.locale_keys['popup.flatpak_not_installed.msg'] + '...',
|
||||
icon=self.icon_flathub)
|
||||
exit(1)
|
||||
|
||||
if self.label_flatpak:
|
||||
@@ -199,6 +204,11 @@ class ManageWindow(QWidget):
|
||||
self._release_lock()
|
||||
self.refresh(clear_output=False)
|
||||
|
||||
def _finish_downgrade(self):
|
||||
self.finish_action()
|
||||
self._release_lock()
|
||||
self.refresh(clear_output=False)
|
||||
|
||||
def filter_only_apps(self, only_apps: int):
|
||||
|
||||
if self.apps:
|
||||
@@ -318,6 +328,29 @@ class ManageWindow(QWidget):
|
||||
self.table_apps.setEnabled(True)
|
||||
self.label_status.setText('')
|
||||
|
||||
def downgrade_app(self, app_ref: str):
|
||||
|
||||
self._check_flatpak_installed()
|
||||
|
||||
if self._acquire_lock():
|
||||
|
||||
pwd = None
|
||||
|
||||
if not is_root():
|
||||
pwd, ok = ask_root_password(self.locale_keys)
|
||||
|
||||
if not ok:
|
||||
self._release_lock()
|
||||
return
|
||||
|
||||
self.textarea_output.clear()
|
||||
self.textarea_output.setVisible(True)
|
||||
self._begin_action(self.locale_keys['manage_window.status.downgrading'] + '...')
|
||||
|
||||
self.thread_downgrade.app_ref = app_ref
|
||||
self.thread_downgrade.root_password = pwd
|
||||
self.thread_downgrade.start()
|
||||
|
||||
|
||||
# Threaded actions
|
||||
class UpdateSelectedApps(QThread):
|
||||
@@ -372,3 +405,31 @@ class UninstallApp(QThread):
|
||||
self.signal_output.emit(line)
|
||||
|
||||
self.signal_finished.emit()
|
||||
|
||||
|
||||
class DowngradeApp(QThread):
|
||||
signal_finished = pyqtSignal()
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
def __init__(self, manager: FlatpakManager):
|
||||
super(DowngradeApp, self).__init__()
|
||||
self.manager = manager
|
||||
self.app_ref = None
|
||||
self.root_password = None
|
||||
|
||||
def run(self):
|
||||
if self.app_ref:
|
||||
try:
|
||||
for output in self.manager.downgrade_app(self.app_ref, self.root_password):
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
self.signal_output.emit(line)
|
||||
|
||||
except ImpossibleDowngradeException:
|
||||
dialog.show_error(title=self.locale_keys['popup.downgrade.impossible.title'],
|
||||
body=self.locale_keys['popup.downgrade.impossible.body'],
|
||||
icon=self.icon_flathub)
|
||||
|
||||
self.app_ref = None
|
||||
self.root_password = None
|
||||
self.signal_finished.emit()
|
||||
|
||||
Reference in New Issue
Block a user