[arch] feature -> AUR: allowing to edit the PKGBUILD file of a package to be installed/upgraded/downgraded

This commit is contained in:
Vinicius Moreira
2020-08-16 12:39:20 -03:00
parent ef6909be38
commit 9c1ffbd356
41 changed files with 811 additions and 205 deletions

View File

@@ -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.
<p align="center">
<img src="https://raw.githubusercontent.com/vinifmor/bauh/staging/pictures/releases/0.9.7/aur_pkgbuild.png">
</p>
- mark a given PKGBUILD of a package as editable (if the property above is enabled, the same behavior will be applied)
<p align="center">
<img src="https://raw.githubusercontent.com/vinifmor/bauh/staging/pictures/releases/0.9.7/mark_pkgbuild.png">
</p>
- unmark a given PKGBUILD of a package as editable (it prevents the behavior described above to happen)
<p align="center">
<img src="https://raw.githubusercontent.com/vinifmor/bauh/staging/pictures/releases/0.9.7/unmark_pkgbuild.png">
</p>
### 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

View File

@@ -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,6 +193,7 @@ 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**

View File

@@ -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

View File

@@ -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,14 +165,16 @@ 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):
if val != self.value:
self.value = val
if self.observers:

View File

@@ -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)

View File

@@ -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,

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)
@@ -435,10 +442,12 @@ class ArchManager(SoftwareManager):
output.append(pkg)
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,13 +2176,22 @@ class ArchManager(SoftwareManager):
else:
res = self._install_from_repository(install_context)
if res and os.path.exists(pkg.get_disk_data_path()):
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 = []
if res and disk_loader and install_context.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

View File

@@ -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

View File

@@ -1,3 +1,4 @@
import os
import re
from datetime import datetime
@@ -110,6 +111,11 @@ class ArchDataMapper:
return False
def fill_package_build(self, pkg: ArchPackage):
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:

View File

@@ -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

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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="23.999998"
width="24.000002"
sodipodi:docname="edit_pkgbuild.svg"
xml:space="preserve"
viewBox="0 0 24.000003 23.999999"
y="0px"
x="0px"
id="Capa_1"
version="1.1"><metadata
id="metadata1194"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs1192" /><sodipodi:namedview
inkscape:current-layer="Capa_1"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="-9.5853392"
inkscape:cx="75.093443"
inkscape:zoom="3.2847783"
fit-margin-bottom="0"
fit-margin-right="0"
fit-margin-left="0"
fit-margin-top="0"
showgrid="false"
id="namedview1190"
inkscape:window-height="739"
inkscape:window-width="1366"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff" />
<g
style="fill:#91a069;fill-opacity:1;stroke-width:1.00086;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.06909101,0,0,0.06879136,-0.05230155,-1.8775432e-7)"
id="g1157">
<path
style="fill:#91a069;fill-opacity:1;stroke-width:1.00086;stroke-miterlimit:4;stroke-dasharray:none"
id="path1153"
d="m 333.988,11.758 -0.42,-0.383 C 325.538,4.04 315.129,0 304.258,0 292.071,0 280.37,5.159 272.154,14.153 L 116.803,184.231 c -1.416,1.55 -2.49,3.379 -3.154,5.37 l -18.267,54.762 c -2.112,6.331 -1.052,13.333 2.835,18.729 3.918,5.438 10.23,8.685 16.886,8.685 0,0 0.001,0 0.001,0 2.879,0 5.693,-0.592 8.362,-1.76 l 52.89,-23.138 c 1.923,-0.841 3.648,-2.076 5.063,-3.626 L 336.771,73.176 C 352.937,55.479 351.69,27.929 333.988,11.758 Z m -203.607,222.489 10.719,-32.134 0.904,-0.99 20.316,18.556 -0.904,0.99 z M 314.621,52.943 182.553,197.53 162.237,178.974 294.305,34.386 c 2.583,-2.828 6.118,-4.386 9.954,-4.386 3.365,0 6.588,1.252 9.082,3.53 l 0.419,0.383 c 5.484,5.009 5.87,13.546 0.861,19.03 z" />
<path
style="fill:#91a069;fill-opacity:1;stroke-width:1.00086;stroke-miterlimit:4;stroke-dasharray:none"
id="path1155"
d="m 303.85,138.388 c -8.284,0 -15,6.716 -15,15 v 127.347 c 0,21.034 -17.113,38.147 -38.147,38.147 H 68.904 c -21.035,0 -38.147,-17.113 -38.147,-38.147 V 100.413 c 0,-21.034 17.113,-38.147 38.147,-38.147 h 131.587 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 H 68.904 c -37.577,0 -68.147,30.571 -68.147,68.147 v 180.321 c 0,37.576 30.571,68.147 68.147,68.147 h 181.798 c 37.576,0 68.147,-30.571 68.147,-68.147 V 153.388 c 0.001,-8.284 -6.715,-15 -14.999,-15 z" />
</g>
<g
transform="translate(-0.757)"
id="g1159">
</g>
<g
transform="translate(-0.757)"
id="g1161">
</g>
<g
transform="translate(-0.757)"
id="g1163">
</g>
<g
transform="translate(-0.757)"
id="g1165">
</g>
<g
transform="translate(-0.757)"
id="g1167">
</g>
<g
transform="translate(-0.757)"
id="g1169">
</g>
<g
transform="translate(-0.757)"
id="g1171">
</g>
<g
transform="translate(-0.757)"
id="g1173">
</g>
<g
transform="translate(-0.757)"
id="g1175">
</g>
<g
transform="translate(-0.757)"
id="g1177">
</g>
<g
transform="translate(-0.757)"
id="g1179">
</g>
<g
transform="translate(-0.757)"
id="g1181">
</g>
<g
transform="translate(-0.757)"
id="g1183">
</g>
<g
transform="translate(-0.757)"
id="g1185">
</g>
<g
transform="translate(-0.757)"
id="g1187">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 24.000003 23.999999"
xml:space="preserve"
sodipodi:docname="unmark_pkgbuild.svg"
width="24.000002"
height="23.999998"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
id="metadata1194"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs1192" /><sodipodi:namedview
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview1190"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="3.2847783"
inkscape:cx="75.093443"
inkscape:cy="-9.5853392"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g1157"
transform="matrix(0.06909101,0,0,0.06879136,-0.05230155,-1.8775432e-7)"
style="fill:#91a069;fill-opacity:1;stroke-width:1.00086;stroke-miterlimit:4;stroke-dasharray:none">
<path
d="m 333.988,11.758 -0.42,-0.383 C 325.538,4.04 315.129,0 304.258,0 292.071,0 280.37,5.159 272.154,14.153 L 116.803,184.231 c -1.416,1.55 -2.49,3.379 -3.154,5.37 l -18.267,54.762 c -2.112,6.331 -1.052,13.333 2.835,18.729 3.918,5.438 10.23,8.685 16.886,8.685 0,0 0.001,0 0.001,0 2.879,0 5.693,-0.592 8.362,-1.76 l 52.89,-23.138 c 1.923,-0.841 3.648,-2.076 5.063,-3.626 L 336.771,73.176 C 352.937,55.479 351.69,27.929 333.988,11.758 Z m -203.607,222.489 10.719,-32.134 0.904,-0.99 20.316,18.556 -0.904,0.99 z M 314.621,52.943 182.553,197.53 162.237,178.974 294.305,34.386 c 2.583,-2.828 6.118,-4.386 9.954,-4.386 3.365,0 6.588,1.252 9.082,3.53 l 0.419,0.383 c 5.484,5.009 5.87,13.546 0.861,19.03 z"
id="path1153"
style="fill:#91a069;fill-opacity:1;stroke-width:1.00086;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 303.85,138.388 c -8.284,0 -15,6.716 -15,15 v 127.347 c 0,21.034 -17.113,38.147 -38.147,38.147 H 68.904 c -21.035,0 -38.147,-17.113 -38.147,-38.147 V 100.413 c 0,-21.034 17.113,-38.147 38.147,-38.147 h 131.587 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 H 68.904 c -37.577,0 -68.147,30.571 -68.147,68.147 v 180.321 c 0,37.576 30.571,68.147 68.147,68.147 h 181.798 c 37.576,0 68.147,-30.571 68.147,-68.147 V 153.388 c 0.001,-8.284 -6.715,-15 -14.999,-15 z"
id="path1155"
style="fill:#91a069;fill-opacity:1;stroke-width:1.00086;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
<g
id="g1159"
transform="translate(-0.757)">
</g>
<g
id="g1161"
transform="translate(-0.757)">
</g>
<g
id="g1163"
transform="translate(-0.757)">
</g>
<g
id="g1165"
transform="translate(-0.757)">
</g>
<g
id="g1167"
transform="translate(-0.757)">
</g>
<g
id="g1169"
transform="translate(-0.757)">
</g>
<g
id="g1171"
transform="translate(-0.757)">
</g>
<g
id="g1173"
transform="translate(-0.757)">
</g>
<g
id="g1175"
transform="translate(-0.757)">
</g>
<g
id="g1177"
transform="translate(-0.757)">
</g>
<g
id="g1179"
transform="translate(-0.757)">
</g>
<g
id="g1181"
transform="translate(-0.757)">
</g>
<g
id="g1183"
transform="translate(-0.757)">
</g>
<g
id="g1185"
transform="translate(-0.757)">
</g>
<g
id="g1187"
transform="translate(-0.757)">
</g>
<g
transform="matrix(-0.05511385,0,0,0.05511385,23.757631,0.24239608)"
style="fill:#ff0000"
id="g940"><g
style="fill:#ff0000"
id="g938"><path
style="fill:#ff0000"
id="path936"
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" /></g></g></svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -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 sha 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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=Оптимизация

View File

@@ -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

View File

@@ -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:

View File

@@ -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=Sestà actualitzant
snap.info.commands=ordres

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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=Команды

View File

@@ -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

View File

@@ -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',

View File

@@ -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,

View File

@@ -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,35 +480,35 @@ 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'],
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'],
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'],
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'],
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'],
mode = new_select(label=self.i18n['core.config.backup.mode'],
tip=None,
value=core_config['backup']['mode'],
opts=[
@@ -518,7 +519,7 @@ class GenericSettingsManager:
],
max_width=default_width,
id_='mode')
type_ = self._gen_select(label=self.i18n['type'].capitalize(),
type_ = new_select(label=self.i18n['type'].capitalize(),
tip=None,
value=core_config['backup']['type'],
opts=[('rsync', 'rsync', None), ('btrfs', 'btrfs', None)],
@@ -527,16 +528,3 @@ class GenericSettingsManager:
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_)

View File

@@ -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)

View File

@@ -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,9 +434,26 @@ class QLineEditObserver(QLineEdit, ViewObserver):
super(QLineEditObserver, self).__init__(**kwargs)
def on_change(self, change: str):
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):
def __init__(self, model: TextInputComponent):
@@ -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()

View File

@@ -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:

View File

@@ -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"><metadata
x="0px"
y="0px"
viewBox="0 0 23.515307 24"
xml:space="preserve"
sodipodi:docname="ignore_update.svg"
width="23.515306"
height="24"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
id="metadata977"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs975" /><sodipodi:namedview
inkscape:current-layer="Capa_1"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="15.198259"
inkscape:cx="25.836555"
inkscape:zoom="9.4874926"
fit-margin-bottom="0"
fit-margin-right="0"
fit-margin-left="0"
fit-margin-top="0"
showborder="true"
showgrid="false"
id="namedview973"
inkscape:window-height="703"
inkscape:window-width="1366"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
pagecolor="#ffffff" />
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" />
<g
transform="translate(-99.310818,-31.505501)"
id="g942">
id="g942"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g944">
id="g944"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g946">
id="g946"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g948">
id="g948"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g950">
id="g950"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g952">
id="g952"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g954">
id="g954"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g956">
id="g956"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g958">
id="g958"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g960">
id="g960"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g962">
id="g962"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g964">
id="g964"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g966">
id="g966"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g968">
id="g968"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="translate(-99.310818,-31.505501)"
id="g970">
id="g970"
transform="translate(-99.310818,-31.505501)">
</g>
<g
transform="scale(0.48990007)"
id="g3081"><g
style="fill:#91a069;fill-opacity:1;stroke:#91a069;stroke-width:0.0521502;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.8424663,0,0,1.9956679,0.04804185,-0.95415234)"
id="g6"><path
style="fill:#91a069;fill-opacity:1;stroke:#91a069;stroke-width:0.0521502;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
id="g982"><g
id="g6"
transform="matrix(0.90262437,0,0,0.97767784,0.02353571,-0.4674393)"
style="fill:#91a069;fill-opacity:1;stroke:#91a069;stroke-width:0.0521502;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 25,17 h -2 c -0.6,0 -1,0.4 -1,1 v 2.5 C 22,20.8 21.8,21 21.5,21 H 4.5 C 4.2,21 4,20.8 4,20.5 V 18 C 4,17.4 3.6,17 3,17 H 1 c -0.6,0 -1,0.4 -1,1 v 6 c 0,0.6 0.4,1 1,1 h 24 c 0.6,0 1,-0.4 1,-1 v -6 c 0,-0.6 -0.4,-1 -1,-1 z"
id="path2"
d="m 25,17 h -2 c -0.6,0 -1,0.4 -1,1 v 2.5 C 22,20.8 21.8,21 21.5,21 H 4.5 C 4.2,21 4,20.8 4,20.5 V 18 C 4,17.4 3.6,17 3,17 H 1 c -0.6,0 -1,0.4 -1,1 v 6 c 0,0.6 0.4,1 1,1 h 24 c 0.6,0 1,-0.4 1,-1 v -6 c 0,-0.6 -0.4,-1 -1,-1 z" /><path
style="fill:#91a069;fill-opacity:1;stroke:#91a069;stroke-width:0.0521502;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
style="fill:#91a069;fill-opacity:1;stroke:#91a069;stroke-width:0.0521502;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
d="m 12.3,16.7 c 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 l 6,-6 C 19.9,10.5 20,10.3 20,10 20,9.7 19.9,9.5 19.7,9.3 L 18.3,7.9 C 18.1,7.7 17.9,7.6 17.6,7.6 c -0.3,0 -0.5,0.1 -0.7,0.3 l -1,1 C 15.6,9.2 15,9 15,8.5 V 2 C 15,1.4 14.6,1 14,1 H 12 C 11.4,1 11,1.4 11,2 V 8.6 C 11,9 10.5,9.3 10.1,9 L 9.1,8 C 8.9,7.8 8.7,7.7 8.4,7.7 8.1,7.7 7.9,7.8 7.7,8 L 6.3,9.4 C 6.1,9.6 6,9.8 6,10.1 c 0,0.3 0.1,0.5 0.3,0.7 z"
id="path4"
d="m 12.3,16.7 c 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 l 6,-6 C 19.9,10.5 20,10.3 20,10 20,9.7 19.9,9.5 19.7,9.3 L 18.3,7.9 C 18.1,7.7 17.9,7.6 17.6,7.6 c -0.3,0 -0.5,0.1 -0.7,0.3 l -1,1 C 15.6,9.2 15,9 15,8.5 V 2 C 15,1.4 14.6,1 14,1 H 12 C 11.4,1 11,1.4 11,2 V 8.6 C 11,9 10.5,9.3 10.1,9 L 9.1,8 C 8.9,7.8 8.7,7.7 8.4,7.7 8.1,7.7 7.9,7.8 7.7,8 L 6.3,9.4 C 6.1,9.6 6,9.8 6,10.1 c 0,0.3 0.1,0.5 0.3,0.7 z" /></g><g
id="g940"
inkscape:connector-curvature="0"
style="fill:#91a069;fill-opacity:1;stroke:#91a069;stroke-width:0.0521502;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g><g
transform="matrix(-0.05511385,0,0,0.05511385,23.515284,-1.6640481e-7)"
style="fill:#ff0000"
transform="matrix(0.11250018,0,0,0.11250018,4.659997e-5,-3.3967093e-7)">
id="g940">
<g
id="g938"
style="fill:#ff0000">
style="fill:#ff0000"
id="g938">
<path
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"
style="fill:#ff0000"
id="path936"
style="fill:#ff0000" />
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" />
</g>
</g></g></svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB