2019-01-21 10:44:01 +00:00
|
|
|
import os.path
|
2018-11-22 01:05:54 +00:00
|
|
|
import sys
|
2018-11-01 16:53:41 +00:00
|
|
|
from datetime import timedelta
|
2019-07-04 19:17:09 +00:00
|
|
|
|
|
|
|
from PyQt5 import QtCore, uic
|
2019-02-02 02:01:09 +00:00
|
|
|
from PyQt5.QtGui import QDesktopServices
|
2019-07-04 19:17:09 +00:00
|
|
|
from PyQt5.QtWidgets import (QHeaderView, QMessageBox, QTableView,
|
2021-02-18 01:44:10 +00:00
|
|
|
QTableWidgetItem, QInputDialog, QMenu,
|
2021-10-04 11:31:41 +00:00
|
|
|
QToolButton, QApplication)
|
|
|
|
|
|
|
|
from vorta.borg.check import BorgCheckJob
|
|
|
|
from vorta.borg.delete import BorgDeleteJob
|
|
|
|
from vorta.borg.diff import BorgDiffJob
|
|
|
|
from vorta.borg.extract import BorgExtractJob
|
|
|
|
from vorta.borg.list_archive import BorgListArchiveJob
|
|
|
|
from vorta.borg.list_repo import BorgListRepoJob
|
|
|
|
from vorta.borg.info_archive import BorgInfoArchiveJob
|
|
|
|
from vorta.borg.mount import BorgMountJob
|
|
|
|
from vorta.borg.prune import BorgPruneJob
|
|
|
|
from vorta.borg.umount import BorgUmountJob
|
|
|
|
from vorta.borg.rename import BorgRenameJob
|
2019-02-19 13:42:19 +00:00
|
|
|
from vorta.i18n import trans_late
|
2019-07-04 19:17:09 +00:00
|
|
|
from vorta.models import ArchiveModel, BackupProfileMixin
|
|
|
|
from vorta.utils import (choose_file_dialog, format_archive_name, get_asset,
|
|
|
|
get_mount_points, pretty_bytes)
|
2020-11-29 00:51:24 +00:00
|
|
|
from vorta.views.source_tab import SizeItem
|
2019-07-04 19:17:09 +00:00
|
|
|
from vorta.views.diff_dialog import DiffDialog
|
|
|
|
from vorta.views.diff_result import DiffResult
|
|
|
|
from vorta.views.extract_dialog import ExtractDialog
|
2020-05-31 11:29:26 +00:00
|
|
|
from vorta.views.utils import get_colored_icon
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2018-11-22 01:18:12 +00:00
|
|
|
uifile = get_asset('UI/archivetab.ui')
|
2020-05-31 11:29:26 +00:00
|
|
|
ArchiveTabUI, ArchiveTabBase = uic.loadUiType(uifile)
|
2018-10-27 17:24:34 +00:00
|
|
|
|
|
|
|
|
2018-11-22 01:18:12 +00:00
|
|
|
class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin):
|
2018-11-04 08:23:17 +00:00
|
|
|
prune_intervals = ['hour', 'day', 'week', 'month', 'year']
|
|
|
|
|
2020-10-30 05:17:24 +00:00
|
|
|
def __init__(self, parent=None, app=None):
|
2018-10-27 17:24:34 +00:00
|
|
|
super().__init__(parent)
|
|
|
|
self.setupUi(parent)
|
2019-01-21 10:44:01 +00:00
|
|
|
self.mount_points = {}
|
|
|
|
self.menu = None
|
2020-10-30 05:17:24 +00:00
|
|
|
self.app = app
|
2018-12-14 08:03:26 +00:00
|
|
|
self.toolBox.setCurrentIndex(0)
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2018-11-22 01:18:12 +00:00
|
|
|
header = self.archiveTable.horizontalHeader()
|
2018-10-27 17:24:34 +00:00
|
|
|
header.setVisible(True)
|
2018-11-02 06:06:25 +00:00
|
|
|
header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
2018-10-27 17:24:34 +00:00
|
|
|
header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
2018-11-01 16:53:41 +00:00
|
|
|
header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
|
2019-01-28 00:41:42 +00:00
|
|
|
header.setSectionResizeMode(3, QHeaderView.Interactive)
|
2019-01-21 10:44:01 +00:00
|
|
|
header.setSectionResizeMode(4, QHeaderView.Stretch)
|
2018-11-22 05:48:16 +00:00
|
|
|
header.setStretchLastSection(True)
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2018-11-22 01:05:54 +00:00
|
|
|
if sys.platform != 'darwin':
|
|
|
|
self._set_status('') # Set platform-specific hints.
|
|
|
|
|
2018-11-22 01:18:12 +00:00
|
|
|
self.archiveTable.setSelectionBehavior(QTableView.SelectRows)
|
2019-01-21 10:44:01 +00:00
|
|
|
self.archiveTable.setSelectionMode(QTableView.SingleSelection)
|
2018-11-22 01:18:12 +00:00
|
|
|
self.archiveTable.setEditTriggers(QTableView.NoEditTriggers)
|
2019-01-28 00:41:42 +00:00
|
|
|
self.archiveTable.setWordWrap(False)
|
|
|
|
self.archiveTable.setTextElideMode(QtCore.Qt.ElideLeft)
|
2018-11-22 07:03:19 +00:00
|
|
|
self.archiveTable.setAlternatingRowColors(True)
|
2019-01-21 10:44:01 +00:00
|
|
|
self.archiveTable.cellDoubleClicked.connect(self.cell_double_clicked)
|
2019-10-02 04:05:40 +00:00
|
|
|
self.archiveTable.setSortingEnabled(True)
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2018-11-04 08:23:17 +00:00
|
|
|
self.listButton.clicked.connect(self.list_action)
|
|
|
|
self.pruneButton.clicked.connect(self.prune_action)
|
2018-11-04 15:37:46 +00:00
|
|
|
self.checkButton.clicked.connect(self.check_action)
|
2019-07-04 19:17:09 +00:00
|
|
|
self.diffButton.clicked.connect(self.diff_action)
|
2021-02-18 01:44:10 +00:00
|
|
|
|
|
|
|
self.archiveActionMenu = QMenu(parent=self)
|
|
|
|
self.archiveActionMenu.aboutToShow.connect(self.showArchiveActionMenu)
|
|
|
|
self.archiveActionButton.setMenu(self.archiveActionMenu)
|
|
|
|
self.archiveActionButton.setPopupMode(QToolButton.InstantPopup)
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2018-12-14 08:03:26 +00:00
|
|
|
self.archiveNameTemplate.textChanged.connect(
|
|
|
|
lambda tpl, key='new_archive_name': self.save_archive_template(tpl, key))
|
|
|
|
self.prunePrefixTemplate.textChanged.connect(
|
|
|
|
lambda tpl, key='prune_prefix': self.save_archive_template(tpl, key))
|
|
|
|
|
2018-11-17 08:51:53 +00:00
|
|
|
self.populate_from_profile()
|
2020-03-23 06:20:09 +00:00
|
|
|
self.selected_archives = None
|
2021-02-18 01:44:10 +00:00
|
|
|
self.set_icons()
|
2020-03-23 06:20:09 +00:00
|
|
|
|
2020-05-31 11:29:26 +00:00
|
|
|
def set_icons(self):
|
2021-02-18 01:44:10 +00:00
|
|
|
"Used when changing between light- and dark mode"
|
2020-05-31 11:29:26 +00:00
|
|
|
self.checkButton.setIcon(get_colored_icon('check-circle'))
|
|
|
|
self.diffButton.setIcon(get_colored_icon('stream-solid'))
|
|
|
|
self.pruneButton.setIcon(get_colored_icon('cut'))
|
|
|
|
self.listButton.setIcon(get_colored_icon('refresh'))
|
|
|
|
self.toolBox.setItemIcon(0, get_colored_icon('tasks'))
|
|
|
|
self.toolBox.setItemIcon(1, get_colored_icon('cut'))
|
2021-02-18 01:44:10 +00:00
|
|
|
self.archiveActionButton.setIcon(get_colored_icon('ellipsis-v'))
|
2020-05-31 11:29:26 +00:00
|
|
|
|
2021-01-20 05:07:32 +00:00
|
|
|
def cancel_action(self):
|
|
|
|
self._set_status(self.tr("Action cancelled."))
|
|
|
|
self._toggle_all_buttons(True)
|
|
|
|
|
2018-11-04 15:37:46 +00:00
|
|
|
def _set_status(self, text):
|
2018-10-28 15:40:38 +00:00
|
|
|
self.mountErrors.setText(text)
|
|
|
|
self.mountErrors.repaint()
|
|
|
|
|
2018-11-04 15:37:46 +00:00
|
|
|
def _toggle_all_buttons(self, enabled=True):
|
2019-01-24 00:36:44 +00:00
|
|
|
for button in [self.checkButton, self.listButton, self.pruneButton,
|
2021-02-18 01:44:10 +00:00
|
|
|
self.diffButton, self.archiveActionButton]:
|
2018-11-27 11:33:16 +00:00
|
|
|
button.setEnabled(enabled)
|
|
|
|
button.repaint()
|
2018-11-04 15:37:46 +00:00
|
|
|
|
2021-02-18 01:44:10 +00:00
|
|
|
def showArchiveActionMenu(self):
|
|
|
|
archive_name = self.selected_archive_name()
|
|
|
|
menu = self.archiveActionMenu
|
|
|
|
menu.clear()
|
|
|
|
|
|
|
|
if not archive_name:
|
|
|
|
action = menu.addAction(self.tr("Select an archive first."))
|
|
|
|
action.setEnabled(False)
|
|
|
|
return menu
|
|
|
|
|
|
|
|
if archive_name in self.mount_points:
|
2021-10-28 11:02:09 +00:00
|
|
|
unmountAction = menu.addAction("Unmount", self.umount_action)
|
2021-02-18 01:44:10 +00:00
|
|
|
unmountAction.setIcon(get_colored_icon('eject'))
|
|
|
|
else:
|
|
|
|
mountAction = menu.addAction("Mount", self.mount_action)
|
|
|
|
mountAction.setIcon(get_colored_icon('folder-open'))
|
|
|
|
|
2021-02-23 02:12:09 +00:00
|
|
|
extractAction = menu.addAction("Extract", self.list_archive_action)
|
|
|
|
refreshAction = menu.addAction("Refresh", self.refresh_archive_action)
|
|
|
|
renameAction = menu.addAction("Rename", self.rename_action)
|
|
|
|
deleteAction = menu.addAction("Delete", self.delete_action)
|
|
|
|
|
2021-02-18 01:44:10 +00:00
|
|
|
extractAction.setIcon(get_colored_icon('cloud-download'))
|
2021-02-23 02:12:09 +00:00
|
|
|
refreshAction.setIcon(get_colored_icon('refresh'))
|
2021-02-18 01:44:10 +00:00
|
|
|
renameAction.setIcon(get_colored_icon('edit'))
|
2021-02-23 02:12:09 +00:00
|
|
|
deleteAction.setIcon(get_colored_icon('trash'))
|
2021-02-18 01:44:10 +00:00
|
|
|
return menu
|
|
|
|
|
2018-11-17 08:51:53 +00:00
|
|
|
def populate_from_profile(self):
|
2018-11-27 11:33:16 +00:00
|
|
|
"""Populate archive list and prune settings from profile."""
|
2018-11-17 08:51:53 +00:00
|
|
|
profile = self.profile()
|
2018-11-20 04:06:09 +00:00
|
|
|
if profile.repo is not None:
|
2019-01-21 10:44:01 +00:00
|
|
|
self.mount_points = get_mount_points(profile.repo.url)
|
2019-01-20 03:50:10 +00:00
|
|
|
self.toolBox.setItemText(0, self.tr('Archives for %s') % profile.repo.url)
|
2018-11-22 05:48:16 +00:00
|
|
|
archives = [s for s in profile.repo.archives.select().order_by(ArchiveModel.time.desc())]
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2021-02-11 06:19:37 +00:00
|
|
|
sorting = self.archiveTable.isSortingEnabled()
|
|
|
|
self.archiveTable.setSortingEnabled(False)
|
2018-11-22 05:48:16 +00:00
|
|
|
for row, archive in enumerate(archives):
|
2018-11-22 01:18:12 +00:00
|
|
|
self.archiveTable.insertRow(row)
|
2018-11-22 05:48:16 +00:00
|
|
|
|
|
|
|
formatted_time = archive.time.strftime('%Y-%m-%d %H:%M')
|
2018-11-22 01:18:12 +00:00
|
|
|
self.archiveTable.setItem(row, 0, QTableWidgetItem(formatted_time))
|
2020-11-29 00:51:24 +00:00
|
|
|
self.archiveTable.setItem(row, 1, SizeItem(pretty_bytes(archive.size)))
|
2018-11-22 05:48:16 +00:00
|
|
|
if archive.duration is not None:
|
|
|
|
formatted_duration = str(timedelta(seconds=round(archive.duration)))
|
2018-11-01 16:53:41 +00:00
|
|
|
else:
|
2018-11-22 05:48:16 +00:00
|
|
|
formatted_duration = ''
|
2019-01-21 10:44:01 +00:00
|
|
|
|
2018-11-22 01:18:12 +00:00
|
|
|
self.archiveTable.setItem(row, 2, QTableWidgetItem(formatted_duration))
|
2019-01-21 10:44:01 +00:00
|
|
|
|
|
|
|
mount_point = self.mount_points.get(archive.name)
|
|
|
|
if mount_point is not None:
|
2019-01-28 00:41:42 +00:00
|
|
|
item = QTableWidgetItem(mount_point)
|
2019-01-21 10:44:01 +00:00
|
|
|
self.archiveTable.setItem(row, 3, item)
|
|
|
|
|
|
|
|
self.archiveTable.setItem(row, 4, QTableWidgetItem(archive.name))
|
|
|
|
|
2018-11-22 05:48:16 +00:00
|
|
|
self.archiveTable.setRowCount(len(archives))
|
2021-02-11 06:19:37 +00:00
|
|
|
self.archiveTable.setSortingEnabled(sorting)
|
2018-11-27 11:33:16 +00:00
|
|
|
item = self.archiveTable.item(0, 0)
|
|
|
|
self.archiveTable.scrollToItem(item)
|
2018-11-17 08:51:53 +00:00
|
|
|
self._toggle_all_buttons(enabled=True)
|
2018-11-02 11:14:54 +00:00
|
|
|
else:
|
2019-01-21 10:44:01 +00:00
|
|
|
self.mount_points = {}
|
2018-11-22 01:18:12 +00:00
|
|
|
self.archiveTable.setRowCount(0)
|
2019-01-20 03:50:10 +00:00
|
|
|
self.toolBox.setItemText(0, self.tr('Archives'))
|
2018-11-17 08:51:53 +00:00
|
|
|
self._toggle_all_buttons(enabled=False)
|
2018-11-04 15:37:46 +00:00
|
|
|
|
2018-12-14 08:03:26 +00:00
|
|
|
self.archiveNameTemplate.setText(profile.new_archive_name)
|
|
|
|
self.prunePrefixTemplate.setText(profile.prune_prefix)
|
|
|
|
|
2019-08-21 12:54:00 +00:00
|
|
|
# Populate pruning options from database
|
|
|
|
profile = self.profile()
|
|
|
|
for i in self.prune_intervals:
|
|
|
|
getattr(self, f'prune_{i}').setValue(getattr(profile, f'prune_{i}'))
|
|
|
|
getattr(self, f'prune_{i}').valueChanged.connect(self.save_prune_setting)
|
|
|
|
self.prune_keep_within.setText(profile.prune_keep_within)
|
|
|
|
self.prune_keep_within.editingFinished.connect(self.save_prune_setting)
|
|
|
|
|
2018-12-14 08:03:26 +00:00
|
|
|
def save_archive_template(self, tpl, key):
|
|
|
|
profile = self.profile()
|
|
|
|
try:
|
2019-01-20 03:50:10 +00:00
|
|
|
preview = self.tr('Preview: %s') % format_archive_name(profile, tpl)
|
2018-12-14 08:03:26 +00:00
|
|
|
setattr(profile, key, tpl)
|
|
|
|
profile.save()
|
|
|
|
except Exception:
|
2019-01-20 03:50:10 +00:00
|
|
|
preview = self.tr('Error in archive name template.')
|
2018-12-14 08:03:26 +00:00
|
|
|
|
|
|
|
if key == 'new_archive_name':
|
|
|
|
self.archiveNamePreview.setText(preview)
|
|
|
|
else:
|
|
|
|
self.prunePrefixPreview.setText(preview)
|
|
|
|
|
2018-11-04 15:37:46 +00:00
|
|
|
def check_action(self):
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgCheckJob.prepare(self.profile())
|
2018-11-23 13:33:31 +00:00
|
|
|
if not params['ok']:
|
2019-02-19 13:42:19 +00:00
|
|
|
self._set_status(params['message'])
|
2018-11-23 13:33:31 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Conditions are met (borg binary available, etc)
|
|
|
|
row_selected = self.archiveTable.selectionModel().selectedRows()
|
|
|
|
if row_selected:
|
2019-01-21 10:44:01 +00:00
|
|
|
archive_cell = self.archiveTable.item(row_selected[0].row(), 4)
|
2019-01-20 03:50:10 +00:00
|
|
|
if archive_cell:
|
|
|
|
archive_name = archive_cell.text()
|
|
|
|
params['cmd'][-1] += f'::{archive_name}'
|
2018-11-23 13:33:31 +00:00
|
|
|
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgCheckJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self._set_status)
|
|
|
|
job.result.connect(self.check_result)
|
2018-11-23 13:33:31 +00:00
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2018-11-04 15:37:46 +00:00
|
|
|
|
|
|
|
def check_result(self, result):
|
|
|
|
if result['returncode'] == 0:
|
|
|
|
self._toggle_all_buttons(True)
|
|
|
|
|
2018-11-04 08:23:17 +00:00
|
|
|
def prune_action(self):
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgPruneJob.prepare(self.profile())
|
2018-11-04 08:23:17 +00:00
|
|
|
if params['ok']:
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgPruneJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self._set_status)
|
|
|
|
job.result.connect(self.prune_result)
|
2018-11-04 15:37:46 +00:00
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2021-02-18 01:44:10 +00:00
|
|
|
else:
|
|
|
|
self._set_status(params['message'])
|
2018-11-04 08:23:17 +00:00
|
|
|
|
|
|
|
def prune_result(self, result):
|
|
|
|
if result['returncode'] == 0:
|
2019-01-20 03:50:10 +00:00
|
|
|
self._set_status(self.tr('Pruning finished.'))
|
2018-11-04 08:23:17 +00:00
|
|
|
self.list_action()
|
|
|
|
else:
|
2018-11-04 15:37:46 +00:00
|
|
|
self._toggle_all_buttons(True)
|
2018-11-04 08:23:17 +00:00
|
|
|
|
|
|
|
def list_action(self):
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgListRepoJob.prepare(self.profile())
|
2018-11-04 08:23:17 +00:00
|
|
|
if params['ok']:
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgListRepoJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self._set_status)
|
|
|
|
job.result.connect(self.list_result)
|
2018-11-04 15:37:46 +00:00
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2021-02-18 01:44:10 +00:00
|
|
|
else:
|
|
|
|
self._set_status(params['message'])
|
2018-11-04 08:23:17 +00:00
|
|
|
|
|
|
|
def list_result(self, result):
|
2018-11-04 15:37:46 +00:00
|
|
|
self._toggle_all_buttons(True)
|
2018-11-04 08:23:17 +00:00
|
|
|
if result['returncode'] == 0:
|
2019-01-20 03:50:10 +00:00
|
|
|
self._set_status(self.tr('Refreshed archives.'))
|
2018-11-17 08:51:53 +00:00
|
|
|
self.populate_from_profile()
|
2021-02-23 02:12:09 +00:00
|
|
|
|
|
|
|
def refresh_archive_action(self):
|
|
|
|
archive_name = self.selected_archive_name()
|
|
|
|
if archive_name is not None:
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgInfoArchiveJob.prepare(self.profile(), archive_name)
|
2021-02-23 02:12:09 +00:00
|
|
|
if params['ok']:
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgInfoArchiveJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self._set_status)
|
|
|
|
job.result.connect(self.refresh_archive_result)
|
2021-02-23 02:12:09 +00:00
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2021-02-23 02:12:09 +00:00
|
|
|
|
|
|
|
def refresh_archive_result(self, result):
|
|
|
|
self._toggle_all_buttons(True)
|
|
|
|
if result['returncode'] == 0:
|
|
|
|
self._set_status(self.tr('Refreshed archive.'))
|
|
|
|
self.populate_from_profile()
|
2018-11-04 08:23:17 +00:00
|
|
|
|
2019-01-21 10:44:01 +00:00
|
|
|
def selected_archive_name(self):
|
|
|
|
row_selected = self.archiveTable.selectionModel().selectedRows()
|
|
|
|
if row_selected:
|
|
|
|
archive_cell = self.archiveTable.item(row_selected[0].row(), 4)
|
|
|
|
if archive_cell:
|
|
|
|
return archive_cell.text()
|
|
|
|
return None
|
|
|
|
|
2018-11-04 15:37:46 +00:00
|
|
|
def mount_action(self):
|
2018-11-04 08:23:17 +00:00
|
|
|
profile = self.profile()
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgMountJob.prepare(profile)
|
2018-11-13 00:12:17 +00:00
|
|
|
if not params['ok']:
|
2019-02-19 13:42:19 +00:00
|
|
|
self._set_status(params['message'])
|
2018-11-13 00:12:17 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Conditions are met (borg binary available, etc)
|
2019-01-21 10:44:01 +00:00
|
|
|
archive_name = self.selected_archive_name()
|
|
|
|
if archive_name:
|
|
|
|
params['cmd'][-1] += f'::{archive_name}'
|
|
|
|
params['current_archive'] = archive_name
|
2018-10-27 17:24:34 +00:00
|
|
|
|
2018-11-20 00:50:52 +00:00
|
|
|
def receive():
|
2018-11-22 04:06:10 +00:00
|
|
|
mount_point = dialog.selectedFiles()
|
|
|
|
if mount_point:
|
|
|
|
params['cmd'].append(mount_point[0])
|
2019-04-07 07:28:02 +00:00
|
|
|
if params.get('current_archive', False):
|
|
|
|
self.mount_points[params['current_archive']] = mount_point[0]
|
2018-11-20 00:50:52 +00:00
|
|
|
if params['ok']:
|
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgMountJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self.mountErrors.setText)
|
|
|
|
job.result.connect(self.mount_result)
|
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2018-11-20 00:50:52 +00:00
|
|
|
|
2019-01-21 10:44:01 +00:00
|
|
|
dialog = choose_file_dialog(self, self.tr("Choose Mount Point"), want_folder=True)
|
2018-11-20 00:50:52 +00:00
|
|
|
dialog.open(receive)
|
|
|
|
|
2018-11-04 15:37:46 +00:00
|
|
|
def mount_result(self, result):
|
|
|
|
self._toggle_all_buttons(True)
|
2018-10-27 17:24:34 +00:00
|
|
|
if result['returncode'] == 0:
|
2019-01-20 03:50:10 +00:00
|
|
|
self._set_status(self.tr('Mounted successfully.'))
|
2019-04-07 07:28:02 +00:00
|
|
|
if result['params'].get('current_archive'):
|
|
|
|
archive_name = result['params']['current_archive']
|
|
|
|
row = self.row_of_archive(archive_name)
|
|
|
|
item = QTableWidgetItem(result['cmd'][-1])
|
|
|
|
self.archiveTable.setItem(row, 3, item)
|
2018-11-22 04:06:10 +00:00
|
|
|
|
|
|
|
def umount_action(self):
|
2019-01-21 10:44:01 +00:00
|
|
|
archive_name = self.selected_archive_name()
|
|
|
|
mount_point = self.mount_points.get(archive_name)
|
|
|
|
|
|
|
|
if mount_point is not None:
|
2018-11-22 04:06:10 +00:00
|
|
|
profile = self.profile()
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgUmountJob.prepare(profile)
|
2018-11-22 04:06:10 +00:00
|
|
|
if not params['ok']:
|
2019-02-19 13:42:19 +00:00
|
|
|
self._set_status(params['message'])
|
2018-11-22 04:06:10 +00:00
|
|
|
return
|
|
|
|
|
2019-01-21 10:44:01 +00:00
|
|
|
params['current_archive'] = archive_name
|
|
|
|
|
|
|
|
if os.path.normpath(mount_point) in params['active_mount_points']:
|
|
|
|
params['cmd'].append(mount_point)
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgUmountJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self.mountErrors.setText)
|
|
|
|
job.result.connect(self.umount_result)
|
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2018-11-22 04:06:10 +00:00
|
|
|
else:
|
2019-01-21 10:44:01 +00:00
|
|
|
self._set_status(self.tr('Mount point not active.'))
|
2018-11-22 04:06:10 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
def umount_result(self, result):
|
|
|
|
self._toggle_all_buttons(True)
|
2020-12-16 03:10:38 +00:00
|
|
|
archive_name = result['params']['current_archive']
|
2018-11-22 04:06:10 +00:00
|
|
|
if result['returncode'] == 0:
|
2019-01-20 03:50:10 +00:00
|
|
|
self._set_status(self.tr('Un-mounted successfully.'))
|
2019-01-21 10:44:01 +00:00
|
|
|
del self.mount_points[archive_name]
|
|
|
|
row = self.row_of_archive(archive_name)
|
|
|
|
item = QTableWidgetItem('')
|
|
|
|
self.archiveTable.setItem(row, 3, item)
|
2020-12-16 03:10:38 +00:00
|
|
|
else:
|
|
|
|
self._set_status(self.tr('Unmounting failed. Make sure no programs are using {}').format(
|
|
|
|
self.mount_points.get(archive_name)))
|
2018-11-01 16:53:41 +00:00
|
|
|
|
2018-11-24 02:01:50 +00:00
|
|
|
def save_prune_setting(self, new_value=None):
|
2018-11-04 08:23:17 +00:00
|
|
|
profile = self.profile()
|
|
|
|
for i in self.prune_intervals:
|
|
|
|
setattr(profile, f'prune_{i}', getattr(self, f'prune_{i}').value())
|
2018-11-24 02:01:50 +00:00
|
|
|
profile.prune_keep_within = self.prune_keep_within.text()
|
2018-11-04 08:23:17 +00:00
|
|
|
profile.save()
|
2018-11-22 10:25:22 +00:00
|
|
|
|
2018-11-27 11:33:16 +00:00
|
|
|
def list_archive_action(self):
|
|
|
|
profile = self.profile()
|
|
|
|
|
|
|
|
row_selected = self.archiveTable.selectionModel().selectedRows()
|
|
|
|
if row_selected:
|
2019-01-21 10:44:01 +00:00
|
|
|
archive_cell = self.archiveTable.item(row_selected[0].row(), 4)
|
2018-11-27 11:33:16 +00:00
|
|
|
if archive_cell:
|
|
|
|
archive_name = archive_cell.text()
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgListArchiveJob.prepare(profile, archive_name)
|
2018-11-27 11:33:16 +00:00
|
|
|
|
|
|
|
if not params['ok']:
|
2019-02-19 13:42:19 +00:00
|
|
|
self._set_status(params['message'])
|
2018-11-27 11:33:16 +00:00
|
|
|
return
|
|
|
|
self._set_status('')
|
|
|
|
self._toggle_all_buttons(False)
|
|
|
|
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgListArchiveJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self.mountErrors.setText)
|
|
|
|
job.result.connect(self.list_archive_result)
|
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
|
|
|
return job
|
2018-11-27 11:33:16 +00:00
|
|
|
else:
|
2019-01-20 03:50:10 +00:00
|
|
|
self._set_status(self.tr('Select an archive to restore first.'))
|
2018-11-27 11:33:16 +00:00
|
|
|
|
|
|
|
def list_archive_result(self, result):
|
|
|
|
self._set_status('')
|
|
|
|
if result['returncode'] == 0:
|
2020-11-20 00:46:09 +00:00
|
|
|
def process_result():
|
2018-11-27 11:33:16 +00:00
|
|
|
def receive():
|
|
|
|
extraction_folder = dialog.selectedFiles()
|
|
|
|
if extraction_folder:
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgExtractJob.prepare(
|
2018-11-27 11:33:16 +00:00
|
|
|
self.profile(), archive.name, window.selected, extraction_folder[0])
|
|
|
|
if params['ok']:
|
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgExtractJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self.mountErrors.setText)
|
|
|
|
job.result.connect(self.extract_archive_result)
|
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2018-11-27 11:33:16 +00:00
|
|
|
else:
|
2019-02-19 13:42:19 +00:00
|
|
|
self._set_status(params['message'])
|
2018-11-27 11:33:16 +00:00
|
|
|
|
2019-01-21 10:44:01 +00:00
|
|
|
dialog = choose_file_dialog(self, self.tr("Choose Extraction Point"), want_folder=True)
|
2018-11-27 11:33:16 +00:00
|
|
|
dialog.open(receive)
|
|
|
|
|
2020-11-20 00:46:09 +00:00
|
|
|
archive = ArchiveModel.get(name=result['params']['archive_name'])
|
|
|
|
window = ExtractDialog(result['data'], archive)
|
|
|
|
self._toggle_all_buttons(True)
|
|
|
|
window.setParent(self, QtCore.Qt.Sheet)
|
|
|
|
self._window = window # for testing
|
|
|
|
window.show()
|
|
|
|
window.accepted.connect(process_result)
|
|
|
|
|
2018-11-27 11:33:16 +00:00
|
|
|
def extract_archive_result(self, result):
|
|
|
|
self._toggle_all_buttons(True)
|
2019-01-21 10:44:01 +00:00
|
|
|
|
|
|
|
def cell_double_clicked(self, row, column):
|
|
|
|
if column == 3:
|
|
|
|
archive_name = self.selected_archive_name()
|
|
|
|
if not archive_name:
|
|
|
|
return
|
|
|
|
|
|
|
|
mount_point = self.mount_points.get(archive_name)
|
|
|
|
|
|
|
|
if mount_point is not None:
|
|
|
|
QDesktopServices.openUrl(QtCore.QUrl(f'file:///{mount_point}'))
|
|
|
|
|
|
|
|
def row_of_archive(self, archive_name):
|
|
|
|
items = self.archiveTable.findItems(archive_name, QtCore.Qt.MatchExactly)
|
|
|
|
rows = [item.row() for item in items if item.column() == 4]
|
|
|
|
return rows[0] if rows else None
|
2019-01-24 00:36:44 +00:00
|
|
|
|
2019-02-02 02:01:09 +00:00
|
|
|
def confirm_dialog(self, title, text):
|
2019-02-02 04:29:33 +00:00
|
|
|
msg = QMessageBox()
|
|
|
|
msg.setIcon(QMessageBox.Information)
|
|
|
|
msg.setText(text)
|
|
|
|
msg.setWindowTitle(title)
|
|
|
|
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
|
|
|
|
msg.button(msg.Yes).setText(self.tr("Yes"))
|
|
|
|
msg.button(msg.Cancel).setText(self.tr("Cancel"))
|
|
|
|
return msg.exec_() == QMessageBox.Yes
|
2019-02-02 02:01:09 +00:00
|
|
|
|
2019-01-24 00:36:44 +00:00
|
|
|
def delete_action(self):
|
2021-10-04 11:31:41 +00:00
|
|
|
# Since this function modify the UI, we can't put the whole function in a JobQUeue.
|
|
|
|
|
|
|
|
params = BorgDeleteJob.prepare(self.profile())
|
2019-01-24 00:36:44 +00:00
|
|
|
if not params['ok']:
|
2019-02-19 13:42:19 +00:00
|
|
|
self._set_status(params['message'])
|
2019-01-24 00:36:44 +00:00
|
|
|
return
|
|
|
|
|
2020-11-20 00:46:09 +00:00
|
|
|
self.archive_name = self.selected_archive_name()
|
|
|
|
if self.archive_name is not None:
|
2019-02-02 04:29:33 +00:00
|
|
|
if not self.confirm_dialog(trans_late('ArchiveTab', "Confirm deletion"),
|
|
|
|
trans_late('ArchiveTab', "Are you sure you want to delete the archive?")):
|
2019-02-02 02:01:09 +00:00
|
|
|
return
|
2020-11-20 00:46:09 +00:00
|
|
|
params['cmd'][-1] += f'::{self.archive_name}'
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgDeleteJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self._set_status)
|
|
|
|
job.result.connect(self.delete_result)
|
2019-01-24 00:36:44 +00:00
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
|
|
|
|
2019-01-24 00:36:44 +00:00
|
|
|
else:
|
2019-02-02 02:01:09 +00:00
|
|
|
self._set_status(self.tr("No archive selected"))
|
2019-01-24 00:36:44 +00:00
|
|
|
|
|
|
|
def delete_result(self, result):
|
|
|
|
if result['returncode'] == 0:
|
|
|
|
self._set_status(self.tr('Archive deleted.'))
|
2020-11-20 00:46:09 +00:00
|
|
|
deleted_row = self.archiveTable.findItems(self.archive_name, QtCore.Qt.MatchExactly)[0].row()
|
|
|
|
self.archiveTable.removeRow(deleted_row)
|
|
|
|
ArchiveModel.get(name=self.archive_name).delete_instance()
|
|
|
|
del self.archive_name
|
2019-01-24 00:36:44 +00:00
|
|
|
else:
|
|
|
|
self._toggle_all_buttons(True)
|
2019-07-04 19:17:09 +00:00
|
|
|
|
|
|
|
def diff_action(self):
|
2020-11-20 00:46:09 +00:00
|
|
|
def process_result():
|
2020-03-23 06:20:09 +00:00
|
|
|
if window.selected_archives:
|
|
|
|
self.selected_archives = window.selected_archives
|
|
|
|
archive_cell_newer = self.archiveTable.item(self.selected_archives[0], 4)
|
|
|
|
archive_cell_older = self.archiveTable.item(self.selected_archives[1], 4)
|
2019-07-04 19:17:09 +00:00
|
|
|
if archive_cell_older and archive_cell_newer:
|
|
|
|
archive_name_newer = archive_cell_newer.text()
|
|
|
|
archive_name_older = archive_cell_older.text()
|
|
|
|
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgDiffJob.prepare(profile, archive_name_older, archive_name_newer)
|
2019-07-04 19:17:09 +00:00
|
|
|
|
|
|
|
if params['ok']:
|
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgDiffJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self.mountErrors.setText)
|
|
|
|
job.result.connect(self.list_diff_result)
|
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2019-07-04 19:17:09 +00:00
|
|
|
else:
|
|
|
|
self._set_status(params['message'])
|
|
|
|
|
2020-11-20 00:46:09 +00:00
|
|
|
profile = self.profile()
|
|
|
|
|
|
|
|
window = DiffDialog(self.archiveTable)
|
|
|
|
self._toggle_all_buttons(True)
|
|
|
|
window.setParent(self, QtCore.Qt.Sheet)
|
|
|
|
self._window = window # for testing
|
|
|
|
window.show()
|
|
|
|
window.accepted.connect(process_result)
|
|
|
|
|
2019-07-04 19:17:09 +00:00
|
|
|
def list_diff_result(self, result):
|
|
|
|
self._set_status('')
|
|
|
|
if result['returncode'] == 0:
|
|
|
|
archive_newer = ArchiveModel.get(name=result['params']['archive_name_newer'])
|
|
|
|
archive_older = ArchiveModel.get(name=result['params']['archive_name_older'])
|
2021-03-29 23:47:27 +00:00
|
|
|
window = DiffResult(result['data'], archive_newer, archive_older, result['params']['json_lines'])
|
2019-07-04 19:17:09 +00:00
|
|
|
self._toggle_all_buttons(True)
|
|
|
|
window.setParent(self, QtCore.Qt.Sheet)
|
2020-03-23 06:20:09 +00:00
|
|
|
self._resultwindow = window # for testing
|
2019-07-04 19:17:09 +00:00
|
|
|
window.show()
|
2021-02-18 01:44:10 +00:00
|
|
|
|
|
|
|
def rename_action(self):
|
2021-10-04 11:31:41 +00:00
|
|
|
|
2021-02-18 01:44:10 +00:00
|
|
|
profile = self.profile()
|
2021-10-04 11:31:41 +00:00
|
|
|
params = BorgRenameJob.prepare(profile)
|
2021-02-18 01:44:10 +00:00
|
|
|
if not params['ok']:
|
|
|
|
self._set_status(params['message'])
|
|
|
|
return
|
|
|
|
|
|
|
|
archive_name = self.selected_archive_name()
|
|
|
|
if archive_name is not None:
|
|
|
|
new_name, finished = QInputDialog.getText(
|
|
|
|
self,
|
|
|
|
self.tr("Change name"),
|
|
|
|
self.tr("New archive name:"),
|
|
|
|
text=archive_name)
|
|
|
|
|
|
|
|
if not finished:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not new_name:
|
|
|
|
self._set_status(self.tr('Archive name cannot be blank.'))
|
|
|
|
return
|
|
|
|
|
|
|
|
new_name_exists = ArchiveModel.get_or_none(name=new_name, repo=profile.repo)
|
|
|
|
if new_name_exists is not None:
|
|
|
|
self._set_status(self.tr('An archive with this name already exists.'))
|
|
|
|
return
|
|
|
|
|
|
|
|
params['cmd'][-1] += f'::{archive_name}'
|
|
|
|
params['cmd'].append(new_name)
|
2021-10-04 11:31:41 +00:00
|
|
|
job = BorgRenameJob(params['cmd'], params, self.profile().repo.id)
|
|
|
|
job.updated.connect(self._set_status)
|
|
|
|
job.result.connect(self.rename_result)
|
2021-02-18 01:44:10 +00:00
|
|
|
self._toggle_all_buttons(False)
|
2021-10-04 11:31:41 +00:00
|
|
|
QApplication.instance().scheduler.jobs_manager.add_job(job)
|
2021-02-18 01:44:10 +00:00
|
|
|
else:
|
|
|
|
self._set_status(self.tr("No archive selected"))
|
|
|
|
|
|
|
|
def rename_result(self, result):
|
|
|
|
if result['returncode'] == 0:
|
|
|
|
self._set_status(self.tr('Archive renamed.'))
|
|
|
|
self.populate_from_profile()
|
|
|
|
else:
|
|
|
|
self._toggle_all_buttons(True)
|