From 9c1ffbd3564b5a7e3fb63a5249c30debddc58ea9 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Sun, 16 Aug 2020 12:39:20 -0300 Subject: [PATCH] [arch] feature -> AUR: allowing to edit the PKGBUILD file of a package to be installed/upgraded/downgraded --- CHANGELOG.md | 18 ++ README.md | 6 +- bauh/api/abstract/model.py | 19 +- bauh/api/abstract/view.py | 21 +- bauh/commons/view_utils.py | 18 ++ bauh/gems/appimage/controller.py | 4 +- bauh/gems/arch/__init__.py | 1 + bauh/gems/arch/config.py | 3 +- bauh/gems/arch/controller.py | 203 ++++++++++++++++-- bauh/gems/arch/makepkg.py | 11 + bauh/gems/arch/mapper.py | 12 +- bauh/gems/arch/model.py | 37 +++- .../gems/arch/resources/img/mark_pkgbuild.svg | 120 +++++++++++ .../arch/resources/img/unmark_pkgbuild.svg | 129 +++++++++++ bauh/gems/arch/resources/locale/ca | 8 + bauh/gems/arch/resources/locale/de | 8 + bauh/gems/arch/resources/locale/en | 9 + bauh/gems/arch/resources/locale/es | 8 + bauh/gems/arch/resources/locale/it | 8 + bauh/gems/arch/resources/locale/pt | 9 + bauh/gems/arch/resources/locale/ru | 8 + bauh/gems/arch/resources/locale/tr | 8 + bauh/gems/snap/controller.py | 5 +- bauh/gems/snap/resources/locale/ca | 1 + bauh/gems/snap/resources/locale/de | 1 + bauh/gems/snap/resources/locale/en | 1 + bauh/gems/snap/resources/locale/es | 1 + bauh/gems/snap/resources/locale/it | 1 + bauh/gems/snap/resources/locale/pt | 1 + bauh/gems/snap/resources/locale/ru | 1 + bauh/gems/snap/resources/locale/tr | 1 + bauh/gems/web/controller.py | 2 +- bauh/view/core/controller.py | 4 +- bauh/view/core/settings.py | 102 ++++----- bauh/view/qt/apps_table.py | 13 +- bauh/view/qt/components.py | 52 +++-- bauh/view/qt/window.py | 4 +- bauh/view/resources/img/ignore_update.svg | 158 +++++++------- pictures/releases/0.9.7/aur_pkgbuild.png | Bin 0 -> 38812 bytes pictures/releases/0.9.7/mark_pkgbuild.png | Bin 0 -> 10951 bytes pictures/releases/0.9.7/unmark_pkgbuild.png | Bin 0 -> 7561 bytes 41 files changed, 811 insertions(+), 205 deletions(-) create mode 100644 bauh/commons/view_utils.py create mode 100644 bauh/gems/arch/resources/img/mark_pkgbuild.svg create mode 100644 bauh/gems/arch/resources/img/unmark_pkgbuild.svg create mode 100644 pictures/releases/0.9.7/aur_pkgbuild.png create mode 100644 pictures/releases/0.9.7/mark_pkgbuild.png create mode 100644 pictures/releases/0.9.7/unmark_pkgbuild.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dafa14f..e02a7045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [0.9.7] 2020 +### Features +- Arch + - AUR: + - allowing to edit the PKGBUILD file of a package to be installed/upgraded/downgraded. If enabled, a popup will be displayed during this acctions allowing the PKGBUILD to be edited. +

+ +

+ - mark a given PKGBUILD of a package as editable (if the property above is enabled, the same behavior will be applied) +

+ +

+ - unmark a given PKGBUILD of a package as editable (it prevents the behavior described above to happen) +

+ +

+ ### Improvements - AppImage - Manual file installation/upgrade: @@ -20,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - upgrading firstly the keyring packages declared in **SyncFirst** (**/etc/pacman.conf**) to avoid pacman downloading issues - only removing packages after downloading the required ones - "Multi-threaded download (repositories)" is not the default behavior anymore (current pacman download approach is faster). If your settings has this property set as 'Yes', just change it to 'No'. + - AUR: caching the PKGBUILD file used for the package installation/upgrade/downgrade (**~/.cache/bauh/arch/installed/$pkgname/PKGBUILD**) - Flatpak - creating the exports path **~/.local/share/flatpak/exports/share** (if it does not exist) and adding it to install/upgrade/downgrade/remove commands path to prevent warning messages. [#128](https://github.com/vinifmor/bauh/issues/128) @@ -38,6 +55,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - not displaying all packages that must be uninstalled - displaying "required size" for packages that must be uninstalled - some conflict resolution scenarios when upgrading several packages + - AUR: info dialog of installed packages displays the latest PKGBUILD file instead of the one used for installation/upgrade/downgrade - Flatpak - downgrading crashing with version 1.8.X - history: the top commit is returned as "(null)" in version 1.8.X diff --git a/README.md b/README.md index 99da07ed..7f9aa11b 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,9 @@ db_updater: - **refresh mirrors**: allows the user to define multiple mirrors locations and sort by the fastest (`sudo pacman-mirrors -c country1,country2 && sudo pacman-mirrors --fasttrack 5 && sudo pacman -Syy`) - **quick system upgrade**: it executes a default pacman upgrade (`pacman -Syyu --noconfirm`) - **clean cache**: it cleans the pacman cache diretory (default: `/var/cache/pacman/pkg`) + - **mark PKGBUILD as editable**: it marks a given PKGBUILD of a package as editable (a popup with the PKGBUILD will be displayed before upgrading/downgrading this package). Action only available when the configuration property **edit_aur_pkgbuild** is not **false**. + - **unmark PKGBUILD as editable**: reverts the action described above. Action only available when the configuration property **edit_aur_pkgbuild** is not **false**. +- Installed AUR packages have their **PKGBUILD** files cached at **~/.cache/bauh/arch/installed/$pkgname** - Packages with ignored updates are defined at **~/.config/bauh/arch/updates_ignored.txt** - The configuration file is located at **~/.config/bauh/arch.yml** and it allows the following customizations: ``` @@ -190,7 +193,8 @@ aur: true # allows to manage AUR packages repositories: true # allows to manage packages from the configured repositories repositories_mthread_download: false # enable multi-threaded download for repository packages if aria2/axel is installed automatch_providers: true # if a possible provider for a given package dependency exactly matches its name, it will be chosen instead of asking for the user to decide (false). -``` +edit_aur_pkgbuild: false # if the AUR PKGBUILD file should be displayed for edition before the make process. true (PKGBUILD will always be displayed for edition), false (PKGBUILD never will be displayed), null (a popup will ask if the user want to edit the PKGBUILD) +``` - Required dependencies: - **pacman** - **wget** diff --git a/bauh/api/abstract/model.py b/bauh/api/abstract/model.py index 66f56d9b..3cb78981 100644 --- a/bauh/api/abstract/model.py +++ b/bauh/api/abstract/model.py @@ -7,9 +7,12 @@ from bauh.api.constants import CACHE_PATH class CustomSoftwareAction: - def __init__(self, i18_label_key: str, i18n_status_key: str, icon_path: str, manager_method: str, requires_root: bool, manager: "SoftwareManager" = None, backup: bool = False, refresh: bool = True): + def __init__(self, i18n_label_key: str, i18n_status_key: str, icon_path: str, manager_method: str, + requires_root: bool, manager: "SoftwareManager" = None, + backup: bool = False, refresh: bool = True, + i18n_confirm_key: str = None): """ - :param i18_label_key: the i18n key that will be used to display the action name + :param i18n_label_key: the i18n key that will be used to display the action name :param i18n_status_key: the i18n key that will be used to display the action name being executed :param icon_path: the action icon path. Use None for no icon :param manager_method: the SoftwareManager method name that should be called. The method must has the following parameters: (pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) @@ -17,8 +20,9 @@ class CustomSoftwareAction: :param backup: if a system backup should be performed before executing the action :param requires_root: :param refresh: if the a full app refresh should be done if the action succeeds + :param i18n_confirm_key: action confirmation message """ - self.i18_label_key = i18_label_key + self.i18n_label_key = i18n_label_key self.i18n_status_key = i18n_status_key self.icon_path = icon_path self.manager_method = manager_method @@ -26,12 +30,13 @@ class CustomSoftwareAction: self.manager = manager self.backup = backup self.refresh = refresh + self.i18n_confirm_key = i18n_confirm_key def __hash__(self): - return self.i18_label_key.__hash__() + self.i18n_status_key.__hash__() + self.manager_method.__hash__() + return self.i18n_label_key.__hash__() + self.i18n_status_key.__hash__() + self.manager_method.__hash__() def __repr__(self): - return "CustomAction (label={}, method={})".format(self.i18_label_key, self.manager_method) + return "CustomAction (label={}, method={})".format(self.i18n_label_key, self.manager_method) class PackageStatus(Enum): @@ -252,6 +257,10 @@ class PackageHistory: self.history = history self.pkg_status_idx = pkg_status_idx + @classmethod + def empyt(cls, pkg: SoftwarePackage): + return cls(pkg=pkg, history=[], pkg_status_idx=-1) + class SuggestionPriority(Enum): LOW = 0 diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py index bd0adf52..fe39b081 100644 --- a/bauh/api/abstract/view.py +++ b/bauh/api/abstract/view.py @@ -148,10 +148,15 @@ class TwoStateButtonComponent(ViewComponent): self.state = state +class TextInputType(Enum): + SINGLE_LINE = 0 + MULTIPLE_LINES = 1 + + class TextInputComponent(ViewComponent): def __init__(self, label: str, value: str = '', placeholder: str = None, tooltip: str = None, read_only: bool =False, - id_: str = None, only_int: bool = False, max_width: int = -1): + id_: str = None, only_int: bool = False, max_width: int = -1, type_: TextInputType = TextInputType.SINGLE_LINE): super(TextInputComponent, self).__init__(id_=id_) self.label = label self.value = value @@ -160,20 +165,22 @@ class TextInputComponent(ViewComponent): self.read_only = read_only self.only_int = only_int self.max_width = max_width + self.type = type_ def get_value(self) -> str: if self.value is not None: - return self.value.strip() + return self.value else: return '' def set_value(self, val: Optional[str], caller: object = None): - self.value = val + if val != self.value: + self.value = val - if self.observers: - for o in self.observers: - if caller != o: - o.on_change(val) + if self.observers: + for o in self.observers: + if caller != o: + o.on_change(val) def get_int_value(self) -> int: if self.value is not None: diff --git a/bauh/commons/view_utils.py b/bauh/commons/view_utils.py new file mode 100644 index 00000000..11611bd0 --- /dev/null +++ b/bauh/commons/view_utils.py @@ -0,0 +1,18 @@ +from typing import List, Tuple, Optional + +from bauh.api.abstract.view import SelectViewType, InputOption, SingleSelectComponent + + +def new_select(label: str, tip: str, id_: str, opts: List[Tuple[Optional[str], object, Optional[str]]], value: object, max_width: int, + type_: SelectViewType = SelectViewType.RADIO, capitalize_label: bool = True): + inp_opts = [InputOption(label=o[0].capitalize(), value=o[1], tooltip=o[2]) for o in opts] + def_opt = [o for o in inp_opts if o.value == value] + return SingleSelectComponent(label=label, + tooltip=tip, + options=inp_opts, + default_option=def_opt[0] if def_opt else inp_opts[0], + max_per_line=len(inp_opts), + max_width=max_width, + type_=type_, + id_=id_, + capitalize_label=capitalize_label) diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index 087af469..36738aea 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -82,13 +82,13 @@ class AppImageManager(SoftwareManager): self.logger = context.logger self.file_downloader = context.file_downloader self.db_locks = {DB_APPS_PATH: Lock(), DB_RELEASES_PATH: Lock()} - self.custom_actions = [CustomSoftwareAction(i18_label_key='appimage.custom_action.install_file', + self.custom_actions = [CustomSoftwareAction(i18n_label_key='appimage.custom_action.install_file', i18n_status_key='appimage.custom_action.install_file.status', manager=self, manager_method='install_file', icon_path=resource.get_path('img/appimage.svg', ROOT_DIR), requires_root=False)] - self.custom_app_actions = [CustomSoftwareAction(i18_label_key='appimage.custom_action.manual_update', + self.custom_app_actions = [CustomSoftwareAction(i18n_label_key='appimage.custom_action.manual_update', i18n_status_key='appimage.custom_action.manual_update.status', manager_method='update_file', requires_root=False, diff --git a/bauh/gems/arch/__init__.py b/bauh/gems/arch/__init__.py index 274fa271..6a1cb5c2 100644 --- a/bauh/gems/arch/__init__.py +++ b/bauh/gems/arch/__init__.py @@ -16,6 +16,7 @@ AUR_INDEX_FILE = '{}/arch.txt'.format(BUILD_DIR) CONFIG_FILE = '{}/arch.yml'.format(CONFIG_PATH) SUGGESTIONS_FILE = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/arch/aur_suggestions.txt' UPDATES_IGNORED_FILE = '{}/updates_ignored.txt'.format(CONFIG_DIR) +EDITABLE_PKGBUILDS_FILE = '{}/aur/editable_pkgbuilds.txt'.format(CONFIG_DIR) def get_icon_path() -> str: diff --git a/bauh/gems/arch/config.py b/bauh/gems/arch/config.py index abdb0d27..f2d6b5a1 100644 --- a/bauh/gems/arch/config.py +++ b/bauh/gems/arch/config.py @@ -12,5 +12,6 @@ def read_config(update_file: bool = False) -> dict: "sync_databases_startup": True, 'mirrors_sort_limit': 5, 'repositories_mthread_download': False, - 'automatch_providers': True} + 'automatch_providers': True, + 'edit_aur_pkgbuild': False} return read(CONFIG_FILE, template, update_file=update_file) diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index 93453c41..06a3fe98 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -22,17 +22,18 @@ from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion, PackageStatus, \ SuggestionPriority, CustomSoftwareAction from bauh.api.abstract.view import MessageType, FormComponent, InputOption, SingleSelectComponent, SelectViewType, \ - ViewComponent, PanelComponent, MultipleSelectComponent, TextInputComponent, TextComponent + ViewComponent, PanelComponent, MultipleSelectComponent, TextInputComponent, TextComponent, TextInputType from bauh.api.constants import TEMP_DIR from bauh.commons import user, internet from bauh.commons.category import CategoriesDownloader from bauh.commons.config import save_config from bauh.commons.html import bold from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, SimpleProcess +from bauh.commons.view_utils import new_select from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, message, confirmation, disk, git, \ gpg, URL_CATEGORIES_FILE, CATEGORIES_FILE_PATH, CUSTOM_MAKEPKG_FILE, SUGGESTIONS_FILE, \ CONFIG_FILE, get_icon_path, database, mirrors, sorting, cpu_manager, ARCH_CACHE_PATH, UPDATES_IGNORED_FILE, \ - CONFIG_DIR + CONFIG_DIR, EDITABLE_PKGBUILDS_FILE from bauh.gems.arch.aur import AURClient from bauh.gems.arch.config import read_config from bauh.gems.arch.dependencies import DependenciesAnalyser @@ -65,7 +66,8 @@ class TransactionContext: remote_repo_map: Dict[str, str] = None, provided_map: Dict[str, Set[str]] = None, remote_provided_map: Dict[str, Set[str]] = None, aur_idx: Set[str] = None, missing_deps: List[Tuple[str, str]] = None, installed: Set[str] = None, removed: Dict[str, SoftwarePackage] = None, - disk_loader: DiskCacheLoader = None, disk_cache_updater: Thread = None): + disk_loader: DiskCacheLoader = None, disk_cache_updater: Thread = None, + new_pkg: bool = False): self.name = name self.base = base self.maintainer = maintainer @@ -90,13 +92,15 @@ class TransactionContext: self.removed = removed self.disk_loader = disk_loader self.disk_cache_updater = disk_cache_updater + self.pkgbuild_edited = False + self.new_pkg = new_pkg @classmethod def gen_context_from(cls, pkg: ArchPackage, arch_config: dict, root_password: str, handler: ProcessHandler) -> "TransactionContext": return cls(name=pkg.name, base=pkg.get_base_name(), maintainer=pkg.maintainer, repository=pkg.repository, arch_config=arch_config, watcher=handler.watcher, handler=handler, skip_opt_deps=True, change_progress=True, root_password=root_password, dependency=False, - installed=set(), removed={}) + installed=set(), removed={}, new_pkg=not pkg.installed) def get_base_name(self): return self.base if self.base else self.name @@ -173,26 +177,26 @@ class ArchManager(SoftwareManager): self.deps_analyser = DependenciesAnalyser(self.aur_client, self.i18n) self.http_client = context.http_client self.custom_actions = { - 'sys_up': CustomSoftwareAction(i18_label_key='arch.custom_action.upgrade_system', + 'sys_up': CustomSoftwareAction(i18n_label_key='arch.custom_action.upgrade_system', i18n_status_key='arch.custom_action.upgrade_system.status', manager_method='upgrade_system', icon_path=get_icon_path(), requires_root=True, backup=True, manager=self), - 'ref_dbs': CustomSoftwareAction(i18_label_key='arch.custom_action.refresh_dbs', + 'ref_dbs': CustomSoftwareAction(i18n_label_key='arch.custom_action.refresh_dbs', i18n_status_key='arch.sync_databases.substatus', manager_method='sync_databases', icon_path=get_icon_path(), requires_root=True, manager=self), - 'ref_mirrors': CustomSoftwareAction(i18_label_key='arch.custom_action.refresh_mirrors', + 'ref_mirrors': CustomSoftwareAction(i18n_label_key='arch.custom_action.refresh_mirrors', i18n_status_key='arch.task.mirrors', manager_method='refresh_mirrors', icon_path=get_icon_path(), requires_root=True, manager=self), - 'clean_cache': CustomSoftwareAction(i18_label_key='arch.custom_action.clean_cache', + 'clean_cache': CustomSoftwareAction(i18n_label_key='arch.custom_action.clean_cache', i18n_status_key='arch.custom_action.clean_cache.status', manager_method='clean_cache', icon_path=get_icon_path(), @@ -416,7 +420,8 @@ class ArchManager(SoftwareManager): res.total = len(res.installed) + len(res.new) return res - def _fill_aur_pkgs(self, aur_pkgs: dict, output: list, disk_loader: DiskCacheLoader, internet_available: bool): + def _fill_aur_pkgs(self, aur_pkgs: dict, output: List[ArchPackage], disk_loader: DiskCacheLoader, internet_available: bool, + arch_config: dict): downgrade_enabled = git.is_enabled() if internet_available: @@ -424,9 +429,11 @@ class ArchManager(SoftwareManager): pkgsinfo = self.aur_client.get_info(aur_pkgs.keys()) if pkgsinfo: + editable_pkgbuilds = self._read_editable_pkgbuilds() if arch_config['edit_aur_pkgbuild'] is not False else None for pkgdata in pkgsinfo: pkg = self.mapper.map_api_data(pkgdata, aur_pkgs, self.categories) pkg.downgrade_enabled = downgrade_enabled + pkg.pkgbuild_editable = pkg.name in editable_pkgbuilds if editable_pkgbuilds is not None else None if disk_loader: disk_loader.fill(pkg) @@ -434,11 +441,13 @@ class ArchManager(SoftwareManager): output.append(pkg) - return + return + except requests.exceptions.ConnectionError: self.logger.warning('Could not retrieve installed AUR packages API data. It seems the internet connection is off.') self.logger.info("Reading only local AUR packages data") + editable_pkgbuilds = self._read_editable_pkgbuilds() if arch_config['edit_aur_pkgbuild'] is not False else None for name, data in aur_pkgs.items(): pkg = ArchPackage(name=name, version=data.get('version'), latest_version=data.get('version'), description=data.get('description'), @@ -446,6 +455,7 @@ class ArchManager(SoftwareManager): pkg.categories = self.categories.get(pkg.name) pkg.downgrade_enabled = downgrade_enabled + pkg.pkgbuild_editable = pkg.name in editable_pkgbuilds if editable_pkgbuilds is not None else None if disk_loader: disk_loader.fill(pkg) @@ -538,7 +548,7 @@ class ArchManager(SoftwareManager): map_threads = [] if aur_pkgs: - t = Thread(target=self._fill_aur_pkgs, args=(aur_pkgs, pkgs, disk_loader, internet_available), daemon=True) + t = Thread(target=self._fill_aur_pkgs, args=(aur_pkgs, pkgs, disk_loader, internet_available, arch_config), daemon=True) t.start() map_threads.append(t) @@ -1207,6 +1217,8 @@ class ArchManager(SoftwareManager): self._revert_ignored_updates(to_uninstall) + self._remove_from_editable_pkgbuilds(context.name) + self._update_progress(context, 100) return uninstalled @@ -1314,8 +1326,12 @@ class ArchManager(SoftwareManager): run_cmd('git clone ' + URL_GIT.format(base_name), print_error=False, cwd=temp_dir) clone_path = '{}/{}'.format(temp_dir, base_name) + srcinfo_path = '{}/.SRCINFO'.format(clone_path) + if not os.path.exists(srcinfo_path): + return PackageHistory.empyt(pkg) + commits = git.list_commits(clone_path) if commits: @@ -1572,7 +1588,62 @@ class ArchManager(SoftwareManager): return True + def _display_pkgbuild_for_editing(self, pkgname: str, watcher: ProcessWatcher, pkgbuild_path: str) -> bool: + with open(pkgbuild_path) as f: + pkgbuild = f.read() + + pkgbuild_input = TextInputComponent(label='', value=pkgbuild, type_=TextInputType.MULTIPLE_LINES) + + watcher.request_confirmation(title='PKGBUILD ({})'.format(pkgname), + body='', + components=[pkgbuild_input], + confirmation_label=self.i18n['proceed'].capitalize(), + deny_button=False) + + if pkgbuild_input.get_value() != pkgbuild: + with open(pkgbuild_path, 'w+') as f: + f.write(pkgbuild_input.get_value()) + + return makepkg.update_srcinfo('/'.join(pkgbuild_path.split('/')[0:-1])) + + return False + + def _ask_for_pkgbuild_edition(self, pkgname: str, arch_config: dict, watcher: ProcessWatcher, pkgbuild_path: str) -> bool: + if pkgbuild_path: + if arch_config['edit_aur_pkgbuild'] is None: + if watcher.request_confirmation(title=self.i18n['confirmation'].capitalize(), + body=self.i18n['arch.aur.action.edit_pkgbuild.body'].format( + bold(pkgname))): + return self._display_pkgbuild_for_editing(pkgname, watcher, pkgbuild_path) + elif arch_config['edit_aur_pkgbuild']: + return self._display_pkgbuild_for_editing(pkgname, watcher, pkgbuild_path) + + return False + + def _edit_pkgbuild_and_update_context(self, context: TransactionContext): + if context.new_pkg or context.name in self._read_editable_pkgbuilds(): + if self._ask_for_pkgbuild_edition(pkgname=context.name, + arch_config=context.config, + watcher=context.watcher, + pkgbuild_path='{}/PKGBUILD'.format(context.project_dir)): + context.pkgbuild_edited = True + srcinfo = aur.map_srcinfo(makepkg.gen_srcinfo(context.project_dir)) + + if srcinfo: + context.name = srcinfo['pkgname'] + context.base = srcinfo['pkgbase'] + + if context.pkg: + for pkgattr, srcattr in {'name': 'pkgname', + 'package_base': 'pkgbase', + 'version': 'pkgversion', + 'latest_version': 'pkgversion', + 'license': 'license', + 'description': 'pkgdesc'}.items(): + setattr(context.pkg, pkgattr, srcinfo.get(srcattr, getattr(context.pkg, pkgattr))) + def _build(self, context: TransactionContext) -> bool: + self._edit_pkgbuild_and_update_context(context) self._pre_download_source(context.project_dir, context.watcher) self._update_progress(context, 50) @@ -1608,6 +1679,7 @@ class ArchManager(SoftwareManager): context.install_file = '{}/{}'.format(context.project_dir, gen_file[0]) if self._install(context=context): + self._save_pkgbuild(context) if context.dependency or context.skip_opt_deps: return True @@ -1620,6 +1692,24 @@ class ArchManager(SoftwareManager): return False + def _save_pkgbuild(self, context: TransactionContext): + cache_path = ArchPackage.disk_cache_path(context.name) + if not os.path.exists(cache_path): + try: + os.mkdir(cache_path) + except: + print("Could not create cache directory '{}'".format(cache_path)) + traceback.print_exc() + return + + src_pkgbuild = '{}/PKGBUILD'.format(context.project_dir) + dest_pkgbuild = '{}/PKGBUILD'.format(cache_path) + try: + shutil.copy(src_pkgbuild, dest_pkgbuild) + except: + context.watcher.print("Could not copy '{}' to '{}'".format(src_pkgbuild, dest_pkgbuild)) + traceback.print_exc() + def _ask_and_install_missing_deps(self, context: TransactionContext, missing_deps: List[Tuple[str, str]]) -> bool: context.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(context.name))) @@ -2086,12 +2176,21 @@ class ArchManager(SoftwareManager): else: res = self._install_from_repository(install_context) - if res and os.path.exists(pkg.get_disk_data_path()): - with open(pkg.get_disk_data_path()) as f: - data = f.read() - if data: - data = json.loads(data) - pkg.fill_cached_data(data) + if res: + pkg.name = install_context.name # changes the package name in case the PKGBUILD was edited + + if os.path.exists(pkg.get_disk_data_path()): + with open(pkg.get_disk_data_path()) as f: + data = f.read() + if data: + data = json.loads(data) + pkg.fill_cached_data(data) + + if install_context.new_pkg and install_context.config['edit_aur_pkgbuild'] is not False and pkg.repository == 'aur': + if install_context.pkgbuild_edited: + pkg.pkgbuild_editable = self._add_as_editable_pkgbuild(pkg.name) + else: + pkg.pkgbuild_editable = not self._remove_from_editable_pkgbuilds(pkg.name) installed = [] @@ -2311,7 +2410,7 @@ class ArchManager(SoftwareManager): def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: local_config = read_config() - max_width = floor(screen_width * 0.15) + max_width = floor(screen_width * 0.22) db_sync_start = self._gen_bool_selector(id_='sync_dbs_start', label_key='arch.config.sync_dbs', @@ -2370,7 +2469,19 @@ class ArchManager(SoftwareManager): tooltip=self.i18n['arch.config.mirrors_sort_limit.tip'], only_int=True, max_width=max_width, - value=local_config['mirrors_sort_limit'] if isinstance(local_config['mirrors_sort_limit'], int) else '') + value=local_config['mirrors_sort_limit'] if isinstance(local_config['mirrors_sort_limit'], int) else ''), + new_select(label=self.i18n['arch.config.edit_aur_pkgbuild'], + tip=self.i18n['arch.config.edit_aur_pkgbuild.tip'], + id_='edit_aur_pkgbuild', + opts=[(self.i18n['yes'].capitalize(), True, None), + (self.i18n['no'].capitalize(), False, None), + (self.i18n['ask'].capitalize(), None, None), + ], + value=local_config['edit_aur_pkgbuild'], + max_width=max_width, + type_=SelectViewType.RADIO, + capitalize_label=False) + ] return PanelComponent([FormComponent(fields, spaces=False)]) @@ -2389,6 +2500,7 @@ class ArchManager(SoftwareManager): config['mirrors_sort_limit'] = form_install.get_component('mirrors_sort_limit').get_int_value() config['repositories_mthread_download'] = form_install.get_component('mthread_download').get_selected() config['automatch_providers'] = form_install.get_component('autoprovs').get_selected() + config['edit_aur_pkgbuild'] = form_install.get_component('edit_aur_pkgbuild').get_selected() try: save_config(config, CONFIG_FILE) @@ -2614,3 +2726,56 @@ class ArchManager(SoftwareManager): def revert_ignored_update(self, pkg: ArchPackage): self._revert_ignored_updates({pkg.name}) pkg.update_ignored = False + + def _add_as_editable_pkgbuild(self, pkgname: str): + try: + Path('/'.join(EDITABLE_PKGBUILDS_FILE.split('/')[0:-1])).mkdir(parents=True, exist_ok=True) + + editable = self._read_editable_pkgbuilds() + + if pkgname not in editable: + editable.add(pkgname) + + self._write_editable_pkgbuilds(editable) + return True + except: + traceback.print_exc() + return False + + def _write_editable_pkgbuilds(self, editable: Set[str]): + if editable: + with open(EDITABLE_PKGBUILDS_FILE, 'w+') as f: + for name in sorted([*editable]): + f.write('{}\n'.format(name)) + else: + os.remove(EDITABLE_PKGBUILDS_FILE) + + def _remove_from_editable_pkgbuilds(self, pkgname: str): + if os.path.exists(EDITABLE_PKGBUILDS_FILE): + try: + editable = self._read_editable_pkgbuilds() + + if pkgname in editable: + editable.remove(pkgname) + + self._write_editable_pkgbuilds(editable) + except: + traceback.print_exc() + return False + + return True + + def _read_editable_pkgbuilds(self) -> Set[str]: + if os.path.exists(EDITABLE_PKGBUILDS_FILE): + with open(EDITABLE_PKGBUILDS_FILE) as f: + return {l.strip() for l in f.readlines() if l and l.strip()} + + return set() + + def enable_pkgbuild_edition(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher): + if self._add_as_editable_pkgbuild(pkg.name): + pkg.pkgbuild_editable = True + + def disable_pkgbuild_edition(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher): + if self._remove_from_editable_pkgbuilds(pkg.name): + pkg.pkgbuild_editable = False diff --git a/bauh/gems/arch/makepkg.py b/bauh/gems/arch/makepkg.py index 1b9ee6d6..f76d96cb 100644 --- a/bauh/gems/arch/makepkg.py +++ b/bauh/gems/arch/makepkg.py @@ -55,3 +55,14 @@ def make(pkgdir: str, optimize: bool, handler: ProcessHandler) -> Tuple[bool, st handler.watcher.print('Custom optimized makepkg.conf ( {} ) not found'.format(CUSTOM_MAKEPKG_FILE)) return handler.handle_simple(SimpleProcess(cmd, cwd=pkgdir)) + + +def update_srcinfo(project_dir: str) -> bool: + updated_src = run_cmd('makepkg --printsrcinfo', cwd=project_dir) + + if updated_src: + with open('{}/.SRCINFO'.format(project_dir), 'w+') as f: + f.write(updated_src) + return True + + return False diff --git a/bauh/gems/arch/mapper.py b/bauh/gems/arch/mapper.py index f489789a..e4311a2b 100644 --- a/bauh/gems/arch/mapper.py +++ b/bauh/gems/arch/mapper.py @@ -1,3 +1,4 @@ +import os import re from datetime import datetime @@ -110,10 +111,15 @@ class ArchDataMapper: return False def fill_package_build(self, pkg: ArchPackage): - res = self.http_client.get(pkg.get_pkg_build_url()) + cached_pkgbuild = pkg.get_cached_pkgbuild_path() + if os.path.exists(cached_pkgbuild): + with open(cached_pkgbuild) as f: + pkg.pkgbuild = f.read() + else: + res = self.http_client.get(pkg.get_pkg_build_url()) - if res and res.status_code == 200 and res.text: - pkg.pkgbuild = res.text + if res and res.status_code == 200 and res.text: + pkg.pkgbuild = res.text def map_api_data(self, apidata: dict, installed: dict, categories: dict) -> ArchPackage: data = installed.get(apidata.get('Name')) if installed else None diff --git a/bauh/gems/arch/model.py b/bauh/gems/arch/model.py index fc3d452e..d72c256d 100644 --- a/bauh/gems/arch/model.py +++ b/bauh/gems/arch/model.py @@ -1,13 +1,27 @@ import datetime from typing import List, Set -from bauh.api.abstract.model import SoftwarePackage +from bauh.api.abstract.model import SoftwarePackage, CustomSoftwareAction from bauh.commons import resource from bauh.gems.arch import ROOT_DIR, ARCH_CACHE_PATH from bauh.view.util.translation import I18n CACHED_ATTRS = {'command', 'icon_path', 'repository', 'maintainer', 'desktop_entry', 'categories'} +ACTIONS_AUR_ENABLE_PKGBUILD_EDITION = [CustomSoftwareAction(i18n_label_key='arch.action.enable_pkgbuild_edition', + i18n_status_key='arch.action.enable_pkgbuild_edition.status', + i18n_confirm_key='arch.action.enable_pkgbuild_edition.confirm', + requires_root=False, + manager_method='enable_pkgbuild_edition', + icon_path=resource.get_path('img/mark_pkgbuild.svg', ROOT_DIR))] + +ACTIONS_AUR_DISABLE_PKGBUILD_EDITION = [CustomSoftwareAction(i18n_label_key='arch.action.disable_pkgbuild_edition', + i18n_status_key='arch.action.disable_pkgbuild_edition', + i18n_confirm_key='arch.action.disable_pkgbuild_edition.confirm', + requires_root=False, + manager_method='disable_pkgbuild_edition', + icon_path=resource.get_path('img/unmark_pkgbuild.svg', ROOT_DIR))] + class ArchPackage(SoftwarePackage): @@ -16,7 +30,8 @@ class ArchPackage(SoftwarePackage): first_submitted: datetime.datetime = None, last_modified: datetime.datetime = None, maintainer: str = None, url_download: str = None, pkgbuild: str = None, repository: str = None, desktop_entry: str = None, installed: bool = False, srcinfo: dict = None, dependencies: Set[str] = None, - categories: List[str] = None, i18n: I18n = None, update_ignored: bool = False, arch: str = None): + categories: List[str] = None, i18n: I18n = None, update_ignored: bool = False, arch: str = None, + pkgbuild_editable: bool = None): super(ArchPackage, self).__init__(name=name, version=version, latest_version=latest_version, description=description, installed=installed, categories=categories) @@ -38,6 +53,8 @@ class ArchPackage(SoftwarePackage): self.arch = arch self.i18n = i18n self.update_ignored = update_ignored + self.view_name = name # name displayed on the view + self.pkgbuild_editable = pkgbuild_editable # if the PKGBUILD can be edited by the user (only for AUR) @staticmethod def disk_cache_path(pkgname: str): @@ -48,7 +65,7 @@ class ArchPackage(SoftwarePackage): return 'https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=' + self.package_base def has_history(self): - return self.installed and self.repository == 'aur' + return self.can_be_downgraded() def has_info(self): return True @@ -148,4 +165,18 @@ class ArchPackage(SoftwarePackage): def __eq__(self, other): if isinstance(other, ArchPackage): + if self.view_name is not None and other.view_name is not None: + return self.view_name == other.view_name and self.repository == other.repository + return self.name == other.name and self.repository == other.repository + + def get_cached_pkgbuild_path(self) -> str: + return '{}/PKGBUILD'.format(self.get_disk_cache_path()) + + def get_custom_supported_actions(self) -> List[CustomSoftwareAction]: + if self.installed and self.pkgbuild_editable is not None and self.repository == 'aur': + if self.pkgbuild_editable: + return ACTIONS_AUR_DISABLE_PKGBUILD_EDITION + else: + return ACTIONS_AUR_ENABLE_PKGBUILD_EDITION + diff --git a/bauh/gems/arch/resources/img/mark_pkgbuild.svg b/bauh/gems/arch/resources/img/mark_pkgbuild.svg new file mode 100644 index 00000000..d9ab0b45 --- /dev/null +++ b/bauh/gems/arch/resources/img/mark_pkgbuild.svg @@ -0,0 +1,120 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bauh/gems/arch/resources/img/unmark_pkgbuild.svg b/bauh/gems/arch/resources/img/unmark_pkgbuild.svg new file mode 100644 index 00000000..43ced941 --- /dev/null +++ b/bauh/gems/arch/resources/img/unmark_pkgbuild.svg @@ -0,0 +1,129 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bauh/gems/arch/resources/locale/ca b/bauh/gems/arch/resources/locale/ca index 12a4dd5d..a8b0ae4d 100644 --- a/bauh/gems/arch/resources/locale/ca +++ b/bauh/gems/arch/resources/locale/ca @@ -3,6 +3,12 @@ arch.action.db_locked.body.l2=It is necessary to unlock it to continue. arch.action.db_locked.confirmation=Unlock and continue arch.action.db_locked.error=It was not possible to unlock the database. arch.action.db_locked.title=Database locked +arch.action.disable_pkgbuild_edition=Unmark PKGBUILD as editable +arch.action.disable_pkgbuild_edition.confirm=Unmark PKGBUILD of {} as editable ? +arch.action.disable_pkgbuild_edition.status=Unmarking PKGBUILD as editable +arch.action.enable_pkgbuild_edition=Mark PKGBUILD as editable +arch.action.enable_pkgbuild_edition.confirm=Mark PKGBUILD of {} as editable ? +arch.action.enable_pkgbuild_edition.status=Marking PKGBUILD as editable arch.aur.install.pgp.body=Per a instal·lar {} cal rebre les claus PGP següents arch.aur.install.pgp.receive_fail=Could not receive PGP key {} arch.aur.install.pgp.sign_fail=No s’ha pogut rebre la clau PGP {} @@ -26,6 +32,8 @@ arch.config.automatch_providers=Auto-define dependency providers arch.config.automatch_providers.tip=It automatically chooses which provider will be used for a package dependency when both names are equal. arch.config.clean_cache=Elimina les versions antigues arch.config.clean_cache.tip=Si cal eliminar les versions antigues d'un paquet emmagatzemat al disc durant la desinstal·lació +arch.config.edit_aur_pkgbuild=Edit PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=If the PKGBUILD file of an AUR package should be displayed for editing before its installation/upgrade/downgrade arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. arch.config.optimize=optimize diff --git a/bauh/gems/arch/resources/locale/de b/bauh/gems/arch/resources/locale/de index 053c80d3..add22c05 100644 --- a/bauh/gems/arch/resources/locale/de +++ b/bauh/gems/arch/resources/locale/de @@ -3,6 +3,12 @@ arch.action.db_locked.body.l2=It is necessary to unlock it to continue. arch.action.db_locked.confirmation=Unlock and continue arch.action.db_locked.error=It was not possible to unlock the database. arch.action.db_locked.title=Database locked +arch.action.disable_pkgbuild_edition=Unmark PKGBUILD as editable +arch.action.disable_pkgbuild_edition.confirm=Unmark PKGBUILD of {} as editable ? +arch.action.disable_pkgbuild_edition.status=Unmarking PKGBUILD as editable +arch.action.enable_pkgbuild_edition=Mark PKGBUILD as editable +arch.action.enable_pkgbuild_edition.confirm=Mark PKGBUILD of {} as editable ? +arch.action.enable_pkgbuild_edition.status=Marking PKGBUILD as editable arch.aur.install.pgp.body=Um {} zu installieren sind folgende PGP Schlüssel nötig arch.aur.install.pgp.receive_fail=PGP Schlüssel {} konnte nicht empfangen werden arch.aur.install.pgp.sign_fail=PGP Schlüssel {} konnte nicht signiert werden @@ -26,6 +32,8 @@ arch.config.automatch_providers=Auto-define dependency providers arch.config.automatch_providers.tip=It automatically chooses which provider will be used for a package dependency when both names are equal. arch.config.clean_cache=Remove old versions arch.config.clean_cache.tip=Whether old versions of a package stored on disk should be removed during uninstall +arch.config.edit_aur_pkgbuild=Edit PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=If the PKGBUILD file of an AUR package should be displayed for editing before its installation/upgrade/downgrade arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. arch.config.optimize=optimize diff --git a/bauh/gems/arch/resources/locale/en b/bauh/gems/arch/resources/locale/en index 197975e8..581655f1 100644 --- a/bauh/gems/arch/resources/locale/en +++ b/bauh/gems/arch/resources/locale/en @@ -3,6 +3,13 @@ arch.action.db_locked.body.l2=It is necessary to unlock it to continue. arch.action.db_locked.confirmation=Unlock and continue arch.action.db_locked.error=It was not possible to unlock the database. arch.action.db_locked.title=Database locked +arch.action.disable_pkgbuild_edition=Unmark PKGBUILD as editable +arch.action.disable_pkgbuild_edition.confirm=Unmark PKGBUILD of {} as editable ? +arch.action.disable_pkgbuild_edition.status=Unmarking PKGBUILD as editable +arch.action.enable_pkgbuild_edition=Mark PKGBUILD as editable +arch.action.enable_pkgbuild_edition.confirm=Mark PKGBUILD of {} as editable ? +arch.action.enable_pkgbuild_edition.status=Marking PKGBUILD as editable +arch.aur.action.edit_pkgbuild.body=Edit the PKGBUILD file of {} before continuing ? arch.aur.install.pgp.body=To install {} is necessary to receive the following PGP keys arch.aur.install.pgp.receive_fail=Could not receive PGP key {} arch.aur.install.pgp.sign_fail=Could not sign PGP key {} @@ -26,6 +33,8 @@ arch.config.automatch_providers=Auto-define dependency providers arch.config.automatch_providers.tip=It automatically chooses which provider will be used for a package dependency when both names are equal. arch.config.clean_cache=Remove old versions arch.config.clean_cache.tip=Whether old versions of a package stored on disk should be removed during uninstall +arch.config.edit_aur_pkgbuild=Edit PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=If the PKGBUILD file of an AUR package should be displayed for editing before its installation/upgrade/downgrade arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. arch.config.optimize=optimize diff --git a/bauh/gems/arch/resources/locale/es b/bauh/gems/arch/resources/locale/es index b69b0b21..01ffa1fe 100644 --- a/bauh/gems/arch/resources/locale/es +++ b/bauh/gems/arch/resources/locale/es @@ -3,6 +3,12 @@ arch.action.db_locked.body.l2=Es necesario desbloquearla para continuar. arch.action.db_locked.confirmation=Desbloquear y continuar arch.action.db_locked.error=No fue posible desbloquear la base de datos. arch.action.db_locked.title=Base de dados bloqueada +arch.action.disable_pkgbuild_edition=Desmarcar PKGBUILD como editable +arch.action.disable_pkgbuild_edition.confirm=Desmarcar PKGBUILD de {} como editable ? +arch.action.disable_pkgbuild_edition.status=Desmarcando PKGBUILD como editable +arch.action.enable_pkgbuild_edition=Marcar PKGBUILD como editable +arch.action.enable_pkgbuild_edition.confirm=Marcar PKGBUILD de {} como editable ? +arch.action.enable_pkgbuild_edition.status=Marcando PKGBUILD como editable arch.aur.install.pgp.body=Para instalar {} es necesario recibir las siguientes claves PGP arch.aur.install.pgp.receive_fail=Could not receive PGP key {} arch.aur.install.pgp.sign_fail=No fue posible recibir la clave PGP {} @@ -26,6 +32,8 @@ arch.config.automatch_providers=Autodefinir proveedores de dependencia arch.config.automatch_providers.tip=Elige automáticamente qué proveedor se usará para una dependencia de paquete cuando ambos nombres son iguales. arch.config.clean_cache=Eliminar versiones antiguas arch.config.clean_cache.tip=Si las versiones antiguas de un paquete almacenado en el disco deben ser eliminadas durante la desinstalación +arch.config.edit_aur_pkgbuild=Editar PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=Si el archivo PKGBUILD de un paquete AUR debe ser exhibido para edición antes de su instalación/actualización/degradación arch.config.mirrors_sort_limit=Límite de ordenación de espejos arch.config.mirrors_sort_limit.tip=Define el número máximo de espejos que se utilizarán para la ordenación por velocidad. Use 0 para no limitar o déjelo en blanco para deshabilitar la clasificación. arch.config.optimize=optimizar diff --git a/bauh/gems/arch/resources/locale/it b/bauh/gems/arch/resources/locale/it index 4294be60..50301ffb 100644 --- a/bauh/gems/arch/resources/locale/it +++ b/bauh/gems/arch/resources/locale/it @@ -3,6 +3,12 @@ arch.action.db_locked.body.l2=It is necessary to unlock it to continue. arch.action.db_locked.confirmation=Unlock and continue arch.action.db_locked.error=It was not possible to unlock the database. arch.action.db_locked.title=Database locked +arch.action.disable_pkgbuild_edition=Unmark PKGBUILD as editable +arch.action.disable_pkgbuild_edition.confirm=Unmark PKGBUILD of {} as editable ? +arch.action.disable_pkgbuild_edition.status=Unmarking PKGBUILD as editable +arch.action.enable_pkgbuild_edition=Mark PKGBUILD as editable +arch.action.enable_pkgbuild_edition.confirm=Mark PKGBUILD of {} as editable ? +arch.action.enable_pkgbuild_edition.status=Marking PKGBUILD as editable arch.aur.install.pgp.body=Per installare {} è necessario ricevere le seguenti chiavi PGP arch.aur.install.pgp.receive_fail=Impossibile ricevere la chiave PGP {} arch.aur.install.pgp.sign_fail=Impossibile firmare la chiave PGP {} @@ -26,6 +32,8 @@ arch.config.automatch_providers=Auto-define dependency providers arch.config.automatch_providers.tip=It automatically chooses which provider will be used for a package dependency when both names are equal. arch.config.clean_cache=Rimuovi le vecchie versioni arch.config.clean_cache.tip=Se le vecchie versioni di un pacchetto memorizzate sul disco devono essere rimosse durante la disinstallazione +arch.config.edit_aur_pkgbuild=Edit PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=If the PKGBUILD file of an AUR package should be displayed for editing before its installation/upgrade/downgrade arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. arch.config.optimize=optimize diff --git a/bauh/gems/arch/resources/locale/pt b/bauh/gems/arch/resources/locale/pt index e5ba5d0e..109e02ad 100644 --- a/bauh/gems/arch/resources/locale/pt +++ b/bauh/gems/arch/resources/locale/pt @@ -3,6 +3,13 @@ arch.action.db_locked.body.l2=É necessário desbloquea-lo para continuar. arch.action.db_locked.confirmation=Desbloquear e continuar arch.action.db_locked.error=Não foi possível desbloquear o banco de dados. arch.action.db_locked.title=Banco de dados bloqueado +arch.action.disable_pkgbuild_edition=Desmarcar PKGBUILD como editável +arch.action.disable_pkgbuild_edition.confirm=Desmarcar o PKGBUILD de {} como editável ? +arch.action.disable_pkgbuild_edition.status=Desmarcando PKGBUILD como editável +arch.action.enable_pkgbuild_edition=Marcando PKGBUILD como editável +arch.action.enable_pkgbuild_edition.confirm=Marcar o PKGBUILD de {} como editável ? +arch.action.enable_pkgbuild_edition.status=Marcando PKGBUILD como editável +arch.aur.action.edit_pkgbuild.body=Editar o arquivo PKGBUILD de {} antes de continuar ? arch.aur.install.pgp.body=Para instalar {} é necessário receber as seguintes chaves PGP arch.aur.install.pgp.receive_fail=Não foi possível receber a chave PGP {} arch.aur.install.pgp.sign_fail=Não foi possível assinar a chave PGP {} @@ -26,6 +33,8 @@ arch.config.automatch_providers=Auto-definir provedores de dependências arch.config.automatch_providers.tip=Escolhe automaticamente qual provedor será utilizado para determinada dependência de um pacote caso os nomes de ambos sejam iguais. arch.config.clean_cache=Remover versões antigas arch.config.clean_cache.tip=Se versões antigas de um pacote armazenadas em disco devem ser removidas durante a desinstalação +arch.config.edit_aur_pkgbuild=Editar PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=Se o arquivo PKGBUILD de um pacote do AUR deve ser exibido para edição antes da instalação/atualização/reversão arch.config.mirrors_sort_limit=Limite de ordenação de espelhos arch.config.mirrors_sort_limit.tip=Define o número máximo de espelhos que serão utilizados para a ordenação por velocidade. Use 0 para não limitar ou deixe em branco para desabilitar a ordenação. arch.config.optimize=Otimizar diff --git a/bauh/gems/arch/resources/locale/ru b/bauh/gems/arch/resources/locale/ru index ed3444f3..cd80b584 100644 --- a/bauh/gems/arch/resources/locale/ru +++ b/bauh/gems/arch/resources/locale/ru @@ -3,6 +3,12 @@ arch.action.db_locked.body.l2=It is necessary to unlock it to continue. arch.action.db_locked.confirmation=Unlock and continue arch.action.db_locked.error=It was not possible to unlock the database. arch.action.db_locked.title=Database locked +arch.action.disable_pkgbuild_edition=Unmark PKGBUILD as editable +arch.action.disable_pkgbuild_edition.confirm=Unmark PKGBUILD of {} as editable ? +arch.action.disable_pkgbuild_edition.status=Unmarking PKGBUILD as editable +arch.action.enable_pkgbuild_edition=Mark PKGBUILD as editable +arch.action.enable_pkgbuild_edition.confirm=Mark PKGBUILD of {} as editable ? +arch.action.enable_pkgbuild_edition.status=Marking PKGBUILD as editable arch.aur.install.pgp.body=Для установки {} необходимо получить следующие PGP ключи arch.aur.install.pgp.receive_fail=Не удалось получить PGP-ключ {} arch.aur.install.pgp.sign_fail=Не удалось подписать PGP-ключ {} @@ -26,6 +32,8 @@ arch.config.automatch_providers=Auto-define dependency providers arch.config.automatch_providers.tip=It automatically chooses which provider will be used for a package dependency when both names are equal. arch.config.clean_cache=Remove old versions arch.config.clean_cache.tip=Whether old versions of a package stored on disk should be removed during uninstall +arch.config.edit_aur_pkgbuild=Edit PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=If the PKGBUILD file of an AUR package should be displayed for editing before its installation/upgrade/downgrade arch.config.mirrors_sort_limit=Ограничение сортировки зеркал arch.config.mirrors_sort_limit.tip=Определяет максимальное количество зеркал, которые будут использоваться для сортировки по скорости. Используйте 0 для No limit или оставьте его пустым, чтобы отключить сортировку. arch.config.optimize=Оптимизация diff --git a/bauh/gems/arch/resources/locale/tr b/bauh/gems/arch/resources/locale/tr index ebe03ea9..ce5ed856 100644 --- a/bauh/gems/arch/resources/locale/tr +++ b/bauh/gems/arch/resources/locale/tr @@ -3,6 +3,12 @@ arch.action.db_locked.body.l2=Devam etmek için veritabanı kilidini açmak gere arch.action.db_locked.confirmation=Veritabanı kilidini aç ve devam et arch.action.db_locked.error=Veritabanının kilidini açmak mümkün olmadı. arch.action.db_locked.title=Veritabanı kilitli +arch.action.disable_pkgbuild_edition=Unmark PKGBUILD as editable +arch.action.disable_pkgbuild_edition.confirm=Unmark PKGBUILD of {} as editable ? +arch.action.disable_pkgbuild_edition.status=Unmarking PKGBUILD as editable +arch.action.enable_pkgbuild_edition=Mark PKGBUILD as editable +arch.action.enable_pkgbuild_edition.confirm=Mark PKGBUILD of {} as editable ? +arch.action.enable_pkgbuild_edition.status=Marking PKGBUILD as editable arch.aur.install.pgp.body={} kurmak için aşağıdaki PGP anahtarlarını almak gereklidir arch.aur.install.pgp.receive_fail=PGP anahtarı alınamadı {} arch.aur.install.pgp.sign_fail=PGP anahtarı {} imzalanamadı @@ -26,6 +32,8 @@ arch.config.automatch_providers=Auto-define dependency providers arch.config.automatch_providers.tip=It automatically chooses which provider will be used for a package dependency when both names are equal. arch.config.clean_cache=Önbelleği temizle arch.config.clean_cache.tip=Disk üzerinde kurulu bir paketin eski sürümlerinin kaldırma sırasında kaldırılıp kaldırılmayacağı +arch.config.edit_aur_pkgbuild=Edit PKGBUILD (AUR) +arch.config.edit_aur_pkgbuild.tip=If the PKGBUILD file of an AUR package should be displayed for editing before its installation/upgrade/downgrade arch.config.mirrors_sort_limit=Yansı sıralama sınırı arch.config.mirrors_sort_limit.tip=Hız sıralama için kullanılacak maksimum yansı sayısını tanımlar. Sınırsız olması için 0 kullanın veya sıralamayı devre dışı bırakmak için boş bırakın. arch.config.optimize=optimize diff --git a/bauh/gems/snap/controller.py b/bauh/gems/snap/controller.py index 6bf69e60..43eea85a 100644 --- a/bauh/gems/snap/controller.py +++ b/bauh/gems/snap/controller.py @@ -40,10 +40,11 @@ class SnapManager(SoftwareManager): self.info_path = None self.custom_actions = [ CustomSoftwareAction(i18n_status_key='snap.action.refresh.status', - i18_label_key='snap.action.refresh.label', + i18n_label_key='snap.action.refresh.label', icon_path=resource.get_path('img/refresh.svg', context.get_view_path()), manager_method='refresh', - requires_root=True) + requires_root=True, + i18n_confirm_key='snap.action.refresh.confirm') ] def get_info_path(self) -> str: diff --git a/bauh/gems/snap/resources/locale/ca b/bauh/gems/snap/resources/locale/ca index d5c4966c..04da1593 100644 --- a/bauh/gems/snap/resources/locale/ca +++ b/bauh/gems/snap/resources/locale/ca @@ -1,4 +1,5 @@ gem.snap.info=Aplicacions publicades a https://snapcraft.io/store +snap.action.refresh.confirm=Actualitza {} ? snap.action.refresh.label=Actualitza snap.action.refresh.status=S’està actualitzant snap.info.commands=ordres diff --git a/bauh/gems/snap/resources/locale/de b/bauh/gems/snap/resources/locale/de index 5883a71a..da013a37 100644 --- a/bauh/gems/snap/resources/locale/de +++ b/bauh/gems/snap/resources/locale/de @@ -1,4 +1,5 @@ gem.snap.info=Anwendungen von https://snapcraft.io/store +snap.action.refresh.confirm=Erneuern {} ? snap.action.refresh.label=Erneuern snap.action.refresh.status=Erneuern snap.info.commands=Kommandos diff --git a/bauh/gems/snap/resources/locale/en b/bauh/gems/snap/resources/locale/en index 33dcfcbc..5dc82da9 100644 --- a/bauh/gems/snap/resources/locale/en +++ b/bauh/gems/snap/resources/locale/en @@ -1,4 +1,5 @@ gem.snap.info=Applications published at https://snapcraft.io/store +snap.action.refresh.confirm=Refresh {} ? snap.action.refresh.label=Refresh snap.action.refresh.status=Refreshing snap.info.commands=commands diff --git a/bauh/gems/snap/resources/locale/es b/bauh/gems/snap/resources/locale/es index 15319d96..f6494044 100644 --- a/bauh/gems/snap/resources/locale/es +++ b/bauh/gems/snap/resources/locale/es @@ -1,4 +1,5 @@ gem.snap.info=Aplicativos publicados en https://snapcraft.io/store +snap.action.refresh.confirm=Actualizar {} ? snap.action.refresh.label=Actualizar snap.action.refresh.status=Actualizando snap.info.commands=comandos diff --git a/bauh/gems/snap/resources/locale/it b/bauh/gems/snap/resources/locale/it index ed6a1d96..6d66aee1 100644 --- a/bauh/gems/snap/resources/locale/it +++ b/bauh/gems/snap/resources/locale/it @@ -1,4 +1,5 @@ gem.snap.info=Applicazioni pubblicate su https://snapcraft.io/store +snap.action.refresh.confirm=Ripristina {} ? snap.action.refresh.label=Ripristina snap.action.refresh.status=Ripristinare snap.info.commands=commands diff --git a/bauh/gems/snap/resources/locale/pt b/bauh/gems/snap/resources/locale/pt index 0b3ad650..35223efa 100644 --- a/bauh/gems/snap/resources/locale/pt +++ b/bauh/gems/snap/resources/locale/pt @@ -1,4 +1,5 @@ gem.snap.info=Aplicativos publicados em https://snapcraft.io/store +snap.action.refresh.confirm=Atualizar {} ? snap.action.refresh.label=Atualizar snap.action.refresh.status=Atualizando snap.info.commands=comandos diff --git a/bauh/gems/snap/resources/locale/ru b/bauh/gems/snap/resources/locale/ru index 1b3a3564..a7d60891 100644 --- a/bauh/gems/snap/resources/locale/ru +++ b/bauh/gems/snap/resources/locale/ru @@ -1,4 +1,5 @@ gem.snap.info=Приложения, опубликованные на https://snapcraft.io/store +snap.action.refresh.confirm=Обновить {} ? snap.action.refresh.label=Обновить snap.action.refresh.status=Обновляется snap.info.commands=Команды diff --git a/bauh/gems/snap/resources/locale/tr b/bauh/gems/snap/resources/locale/tr index 9e9e5fd7..9347c7ce 100644 --- a/bauh/gems/snap/resources/locale/tr +++ b/bauh/gems/snap/resources/locale/tr @@ -1,4 +1,5 @@ gem.snap.info=https://snapcraft.io/store adresinde yayınlanan uygulamalar +snap.action.refresh.confirm=Yenile {} ? snap.action.refresh.label=Yenile snap.action.refresh.status=Yenileniyor snap.info.commands=komutlar diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index b0ec0426..365fb8f5 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -69,7 +69,7 @@ class WebApplicationManager(SoftwareManager): self.env_thread = None self.suggestions_downloader = suggestions_downloader self.suggestions = {} - self.custom_actions = [CustomSoftwareAction(i18_label_key='web.custom_action.clean_env', + self.custom_actions = [CustomSoftwareAction(i18n_label_key='web.custom_action.clean_env', i18n_status_key='web.custom_action.clean_env.status', manager=self, manager_method='clean_environment', diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py index b7379f9e..5affb4db 100755 --- a/bauh/view/core/controller.py +++ b/bauh/view/core/controller.py @@ -53,14 +53,14 @@ class GenericSoftwareManager(SoftwareManager): self.config = config self.settings_manager = settings_manager self.http_client = context.http_client - self.extra_actions = [CustomSoftwareAction(i18_label_key='action.reset', + self.extra_actions = [CustomSoftwareAction(i18n_label_key='action.reset', i18n_status_key='action.reset.status', manager_method='reset', manager=self, icon_path=resource.get_path('img/logo.svg'), requires_root=False, refresh=False)] - self.dynamic_extra_actions = {CustomSoftwareAction(i18_label_key='action.backups', + self.dynamic_extra_actions = {CustomSoftwareAction(i18n_label_key='action.backups', i18n_status_key='action.backups.status', manager_method='launch_timeshift', manager=self, diff --git a/bauh/view/core/settings.py b/bauh/view/core/settings.py index dddcfece..ef920d6a 100644 --- a/bauh/view/core/settings.py +++ b/bauh/view/core/settings.py @@ -12,6 +12,7 @@ from bauh.api.abstract.download import FileDownloader from bauh.api.abstract.view import ViewComponent, TabComponent, InputOption, TextComponent, MultipleSelectComponent, \ PanelComponent, FormComponent, TabGroupComponent, SingleSelectComponent, SelectViewType, TextInputComponent, \ FileChooserComponent, RangeInputComponent +from bauh.commons.view_utils import new_select from bauh.view.core import config, timeshift from bauh.view.core.config import read_config from bauh.view.core.downloader import AdaptableFileDownloader @@ -100,7 +101,7 @@ class GenericSettingsManager: max_width=default_width, id_="icon_exp") - select_trim_up = self._gen_select(label=self.i18n['core.config.trim.after_upgrade'], + select_trim_up = new_select(label=self.i18n['core.config.trim.after_upgrade'], tip=self.i18n['core.config.trim.after_upgrade.tip'], value=core_config['disk']['trim']['after_upgrade'], max_width=default_width, @@ -141,7 +142,7 @@ class GenericSettingsManager: if current_mthread_client not in available_mthread_clients: current_mthread_client = None - return self._gen_select(label=self.i18n['core.config.download.multithreaded_client'], + return new_select(label=self.i18n['core.config.download.multithreaded_client'], tip=self.i18n['core.config.download.multithreaded_client.tip'], id_="mthread_client", max_width=default_width, @@ -293,7 +294,7 @@ class GenericSettingsManager: max_width=default_width, id_="sugs_by_type") - inp_reboot = self._gen_select(label=self.i18n['core.config.updates.reboot'], + inp_reboot = new_select(label=self.i18n['core.config.updates.reboot'], tip=self.i18n['core.config.updates.reboot.tip'], id_='ask_for_reboot', max_width=default_width, @@ -479,64 +480,51 @@ class GenericSettingsManager: (self.i18n['no'].capitalize(), False, None), (self.i18n['ask'].capitalize(), None, None)] - install_mode = self._gen_select(label=self.i18n['core.config.backup.install'], - tip=None, - value=core_config['backup']['install'], - opts=ops_opts, - max_width=default_width, - id_='install') + install_mode = new_select(label=self.i18n['core.config.backup.install'], + tip=None, + value=core_config['backup']['install'], + opts=ops_opts, + max_width=default_width, + id_='install') - uninstall_mode = self._gen_select(label=self.i18n['core.config.backup.uninstall'], - tip=None, - value=core_config['backup']['uninstall'], - opts=ops_opts, - max_width=default_width, - id_='uninstall') + uninstall_mode = new_select(label=self.i18n['core.config.backup.uninstall'], + tip=None, + value=core_config['backup']['uninstall'], + opts=ops_opts, + max_width=default_width, + id_='uninstall') - upgrade_mode = self._gen_select(label=self.i18n['core.config.backup.upgrade'], - tip=None, - value=core_config['backup']['upgrade'], - opts=ops_opts, - max_width=default_width, - id_='upgrade') + upgrade_mode = new_select(label=self.i18n['core.config.backup.upgrade'], + tip=None, + value=core_config['backup']['upgrade'], + opts=ops_opts, + max_width=default_width, + id_='upgrade') - downgrade_mode = self._gen_select(label=self.i18n['core.config.backup.downgrade'], - tip=None, - value=core_config['backup']['downgrade'], - opts=ops_opts, - max_width=default_width, - id_='downgrade') + downgrade_mode = new_select(label=self.i18n['core.config.backup.downgrade'], + tip=None, + value=core_config['backup']['downgrade'], + opts=ops_opts, + max_width=default_width, + id_='downgrade') - mode = self._gen_select(label=self.i18n['core.config.backup.mode'], - tip=None, - value=core_config['backup']['mode'], - opts=[ - (self.i18n['core.config.backup.mode.incremental'], 'incremental', - self.i18n['core.config.backup.mode.incremental.tip']), - (self.i18n['core.config.backup.mode.only_one'], 'only_one', - self.i18n['core.config.backup.mode.only_one.tip']) - ], - max_width=default_width, - id_='mode') - type_ = self._gen_select(label=self.i18n['type'].capitalize(), - tip=None, - value=core_config['backup']['type'], - opts=[('rsync', 'rsync', None), ('btrfs', 'btrfs', None)], - max_width=default_width, - id_='type') + mode = new_select(label=self.i18n['core.config.backup.mode'], + tip=None, + value=core_config['backup']['mode'], + opts=[ + (self.i18n['core.config.backup.mode.incremental'], 'incremental', + self.i18n['core.config.backup.mode.incremental.tip']), + (self.i18n['core.config.backup.mode.only_one'], 'only_one', + self.i18n['core.config.backup.mode.only_one.tip']) + ], + max_width=default_width, + id_='mode') + type_ = new_select(label=self.i18n['type'].capitalize(), + tip=None, + value=core_config['backup']['type'], + opts=[('rsync', 'rsync', None), ('btrfs', 'btrfs', None)], + max_width=default_width, + id_='type') sub_comps = [FormComponent([enabled_opt, mode, type_, install_mode, uninstall_mode, upgrade_mode, downgrade_mode], spaces=False)] return TabComponent(self.i18n['core.config.tab.backup'].capitalize(), PanelComponent(sub_comps), None, 'core.bkp') - - def _gen_select(self, label: str, tip: str, id_: str, opts: List[tuple], value: object, max_width: int, type_: SelectViewType = SelectViewType.RADIO): - inp_opts = [InputOption(label=o[0].capitalize(), value=o[1], tooltip=o[2]) for o in opts] - def_opt = [o for o in inp_opts if o.value == value] - return SingleSelectComponent(label=label, - tooltip=tip, - options=inp_opts, - default_option=def_opt[0] if def_opt else inp_opts[0], - max_per_line=len(inp_opts), - max_width=max_width, - type_=type_, - id_=id_) - diff --git a/bauh/view/qt/apps_table.py b/bauh/view/qt/apps_table.py index 6c717b3b..397f4165 100644 --- a/bauh/view/qt/apps_table.py +++ b/bauh/view/qt/apps_table.py @@ -12,7 +12,7 @@ from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidg from bauh.api.abstract.cache import MemoryCache from bauh.api.abstract.model import PackageStatus -from bauh.commons.html import strip_html +from bauh.commons.html import strip_html, bold from bauh.view.qt import dialog from bauh.view.qt.colors import GREEN, BROWN from bauh.view.qt.components import IconButton @@ -167,15 +167,20 @@ class AppsTable(QTableWidget): if bool(pkg.model.get_custom_supported_actions()): for action in pkg.model.get_custom_supported_actions(): - item = QAction(self.i18n[action.i18_label_key]) + item = QAction(self.i18n[action.i18n_label_key]) if action.icon_path: item.setIcon(QIcon(action.icon_path)) def custom_action(): + if action.i18n_confirm_key: + body = self.i18n[action.i18n_confirm_key].format(bold(pkg.model.name)) + else: + body = '{} ?'.format(self.i18n[action.i18n_label_key]) + if dialog.ask_confirmation( - title=self.i18n[action.i18_label_key], - body=self._parag('{} {} ?'.format(self.i18n[action.i18_label_key], self._bold(str(pkg)))), + title=self.i18n[action.i18n_label_key], + body=self._parag(body), i18n=self.i18n): self.window.begin_execute_custom_action(pkg, action) diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index 90603fdf..006d4f95 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -7,11 +7,11 @@ from PyQt5.QtCore import Qt, QSize, QTimer from PyQt5.QtGui import QIcon, QPixmap, QIntValidator, QCursor from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGridLayout, QWidget, \ QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout, QFormLayout, QFileDialog, QTabWidget, QVBoxLayout, \ - QSlider, QScrollArea, QFrame, QAction, QSpinBox + QSlider, QScrollArea, QFrame, QAction, QSpinBox, QPlainTextEdit from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \ TextInputComponent, FormComponent, FileChooserComponent, ViewComponent, TabGroupComponent, PanelComponent, \ - TwoStateButtonComponent, TextComponent, SpacerComponent, RangeInputComponent, ViewObserver + TwoStateButtonComponent, TextComponent, SpacerComponent, RangeInputComponent, ViewObserver, TextInputType from bauh.view.qt import css from bauh.view.qt.colors import RED from bauh.view.util import resource @@ -434,7 +434,24 @@ class QLineEditObserver(QLineEdit, ViewObserver): super(QLineEditObserver, self).__init__(**kwargs) def on_change(self, change: str): - self.setText(change if change is not None else '') + if self.text() != change: + self.setText(change if change is not None else '') + + +class QPlainTextEditObserver(QPlainTextEdit, ViewObserver): + + def __init__(self, **kwargs): + super(QPlainTextEditObserver, self).__init__(**kwargs) + + def on_change(self, change: str): + self.setText(change) + + def setText(self, text: str): + if text != self.toPlainText(): + self.setPlainText(text if text is not None else '') + + def setCursorPosition(self, idx: int): + self.textCursor().setPosition(idx) class TextInputQt(QGroupBox): @@ -449,7 +466,7 @@ class TextInputQt(QGroupBox): if self.model.max_width > 0: self.setMaximumWidth(self.model.max_width) - self.text_input = QLineEditObserver() + self.text_input = QLineEditObserver() if model.type == TextInputType.SINGLE_LINE else QPlainTextEditObserver() if model.only_int: self.text_input.setValidator(QIntValidator()) @@ -469,8 +486,9 @@ class TextInputQt(QGroupBox): self.model.observers.append(self.text_input) self.layout().addWidget(self.text_input, 0, 1) - def _update_model(self, text: str): - self.model.set_value(val=text, caller=self) + def _update_model(self, *args): + change = args[0] if args else self.text_input.toPlainText() + self.model.set_value(val=change, caller=self) class MultipleSelectQt(QGroupBox): @@ -775,29 +793,29 @@ class FormQt(QGroupBox): return tip_icon def _new_text_input(self, c: TextInputComponent) -> Tuple[QLabel, QLineEdit]: - line_edit = QLineEditObserver() + view = QLineEditObserver() if c.type == TextInputType.SINGLE_LINE else QPlainTextEditObserver() if c.only_int: - line_edit.setValidator(QIntValidator()) + view.setValidator(QIntValidator()) if c.tooltip: - line_edit.setToolTip(c.tooltip) + view.setToolTip(c.tooltip) if c.placeholder: - line_edit.setPlaceholderText(c.placeholder) + view.setPlaceholderText(c.placeholder) if c.value: - line_edit.setText(str(c.value) if c.value else '') - line_edit.setCursorPosition(0) + view.setText(str(c.value) if c.value else '') + view.setCursorPosition(0) if c.read_only: - line_edit.setEnabled(False) + view.setEnabled(False) def update_model(text: str): - c.set_value(val=text, caller=line_edit) + c.set_value(val=text, caller=view) - line_edit.textChanged.connect(update_model) - c.observers.append(line_edit) + view.textChanged.connect(update_model) + c.observers.append(view) label = QWidget() label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) @@ -812,7 +830,7 @@ class FormQt(QGroupBox): if c.tooltip: label.layout().addWidget(self.gen_tip_icon(c.tooltip)) - return label, self._wrap(line_edit, c) + return label, self._wrap(view, c) def _new_range_input(self, model: RangeInputComponent) -> QSpinBox: spinner = QSpinBox() diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 33631520..5ff7b61d 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -1394,7 +1394,7 @@ class ManageWindow(QWidget): def begin_execute_custom_action(self, pkg: PackageView, action: CustomSoftwareAction): if pkg is None and not dialog.ask_confirmation(title=self.i18n['confirmation'].capitalize(), - body=self.i18n['custom_action.proceed_with'].capitalize().format('"{}"'.format(self.i18n[action.i18_label_key])), + body=self.i18n['custom_action.proceed_with'].capitalize().format('"{}"'.format(self.i18n[action.i18n_label_key])), icon=QIcon(action.icon_path) if action.icon_path else QIcon(resource.get_path('img/logo.svg')), i18n=self.i18n): return False @@ -1450,7 +1450,7 @@ class ManageWindow(QWidget): self.settings_window.show() def _map_custom_action(self, action: CustomSoftwareAction) -> QAction: - custom_action = QAction(self.i18n[action.i18_label_key]) + custom_action = QAction(self.i18n[action.i18n_label_key]) if action.icon_path: try: diff --git a/bauh/view/resources/img/ignore_update.svg b/bauh/view/resources/img/ignore_update.svg index e1148552..804dafb4 100644 --- a/bauh/view/resources/img/ignore_update.svg +++ b/bauh/view/resources/img/ignore_update.svg @@ -7,128 +7,128 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - height="24" - width="23.515306" - sodipodi:docname="ignore_update.svg" - xml:space="preserve" - viewBox="0 0 23.515307 24" - y="0px" - x="0px" + version="1.1" id="Capa_1" - version="1.1">image/svg+xml + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1366" + inkscape:window-height="739" + id="namedview973" + showgrid="false" + showborder="true" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:zoom="9.4874926" + inkscape:cx="25.836555" + inkscape:cy="15.198259" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="Capa_1" /> + id="g942" + transform="translate(-99.310818,-31.505501)"> + id="g944" + transform="translate(-99.310818,-31.505501)"> + id="g946" + transform="translate(-99.310818,-31.505501)"> + id="g948" + transform="translate(-99.310818,-31.505501)"> + id="g950" + transform="translate(-99.310818,-31.505501)"> + id="g952" + transform="translate(-99.310818,-31.505501)"> + id="g954" + transform="translate(-99.310818,-31.505501)"> + id="g956" + transform="translate(-99.310818,-31.505501)"> + id="g958" + transform="translate(-99.310818,-31.505501)"> + id="g960" + transform="translate(-99.310818,-31.505501)"> + id="g962" + transform="translate(-99.310818,-31.505501)"> + id="g964" + transform="translate(-99.310818,-31.505501)"> + id="g966" + transform="translate(-99.310818,-31.505501)"> + id="g968" + transform="translate(-99.310818,-31.505501)"> + id="g970" + transform="translate(-99.310818,-31.505501)"> + id="g940"> + style="fill:#ff0000" + id="g938"> + d="M 213.333,0 C 95.467,0 0,95.467 0,213.333 0,331.199 95.467,426.666 213.333,426.666 331.199,426.666 426.667,331.2 426.667,213.333 426.667,95.466 331.2,0 213.333,0 Z M 42.667,213.333 c 0,-94.293 76.373,-170.667 170.667,-170.667 39.467,0 75.627,13.547 104.533,35.947 L 78.613,317.867 C 56.213,288.96 42.667,252.8 42.667,213.333 Z M 213.333,384 C 173.866,384 137.706,370.453 108.8,348.053 L 348.053,108.8 C 370.453,137.707 384,173.867 384,213.333 384,307.627 307.627,384 213.333,384 Z" /> diff --git a/pictures/releases/0.9.7/aur_pkgbuild.png b/pictures/releases/0.9.7/aur_pkgbuild.png new file mode 100644 index 0000000000000000000000000000000000000000..edfe89cdc852415bbf5e65b3ee8150924b45d5e3 GIT binary patch literal 38812 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�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

(th5;L6dE)=-6Y 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 B>9zm> literal 0 HcmV?d00001 diff --git a/pictures/releases/0.9.7/mark_pkgbuild.png b/pictures/releases/0.9.7/mark_pkgbuild.png new file mode 100644 index 0000000000000000000000000000000000000000..438068ce5708a08ef62e996812ec2651c6428f53 GIT binary patch literal 10951 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 K3Rei~|M)L7)X@k4 literal 0 HcmV?d00001 diff --git a/pictures/releases/0.9.7/unmark_pkgbuild.png b/pictures/releases/0.9.7/unmark_pkgbuild.png new file mode 100644 index 0000000000000000000000000000000000000000..6e356129ab85824d8ed756ce640fbdbb9e1376ed GIT binary patch literal 7561 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 literal 0 HcmV?d00001