From d33f35e10b4c3027b3b12881152c157eb1b2cc8c Mon Sep 17 00:00:00 2001 From: buhtz Date: Thu, 28 Mar 2024 09:52:52 +0100 Subject: [PATCH] fix: Open symlinked folders in fileview Symlink folders in fileview now can be opened via double click in all circumstances. Fix #1476 --- CHANGES | 3 ++- common/snapshots.py | 6 +++++- common/test/test_sid.py | 14 +++++++------- common/test/test_takeSnapshot.py | 32 ++++++++++++++++---------------- qt/app.py | 20 +++++++++++--------- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index cbe2f3b55..aadc27e2e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,10 @@ Back In Time Version 1.4.4-dev (development of upcoming release) +* Fix bug: Open symlinked folders in file view (#1476) +* Fix bug: Respect dark mode using color roles (#1601) * Fix: "Highly recommended" exclusion pattern in "Manage Profile" dialog's "Exclude" tab show missings only (#1620) * Build: Activate PyLint error E0401 (import-error) -* Fix: Respect dark mode using color roles (#1601) * Dependency: Migration to PyQt6 * Build: PyLint unit test is skipped if PyLint isn't installed, but will always run on TravisCI (#1634) * Feature: Support rsync '--one-file-system' in Expert Options (#1598) diff --git a/common/snapshots.py b/common/snapshots.py index ab6a90644..4a8c80c44 100644 --- a/common/snapshots.py +++ b/common/snapshots.py @@ -2625,7 +2625,7 @@ def exists(self): """ return os.path.isdir(self.path()) and os.path.isdir(self.pathBackup()) - def canOpenPath(self, path): + def isExistingPathInsideSnapshotFolder(self, path): """ ``True`` if path is a file inside this snapshot @@ -2636,13 +2636,17 @@ def canOpenPath(self, path): bool: ``True`` if file exists """ fullPath = self.pathBackup(path) + if not os.path.exists(fullPath): return False + if not os.path.islink(fullPath): return True + basePath = self.pathBackup() target = os.readlink(fullPath) target = os.path.join(os.path.abspath(os.path.dirname(fullPath)), target) + return target.startswith(basePath) @property diff --git a/common/test/test_sid.py b/common/test/test_sid.py index 6574aea39..1152178d2 100644 --- a/common/test/test_sid.py +++ b/common/test/test_sid.py @@ -185,7 +185,7 @@ def test_exists(self): 'backup')) self.assertTrue(sid.exists()) - def test_canOpenPath(self): + def test_isExistingPathInsideSnapshotFolder(self): sid = snapshots.SID('20151219-010324-123', self.cfg) backupPath = os.path.join(self.snapshotPath, '20151219-010324-123', @@ -193,32 +193,32 @@ def test_canOpenPath(self): os.makedirs(os.path.join(backupPath, 'foo')) #test existing file and non existing file - self.assertTrue(sid.canOpenPath('/foo')) - self.assertFalse(sid.canOpenPath('/tmp')) + self.assertTrue(sid.isExistingPathInsideSnapshotFolder('/foo')) + self.assertFalse(sid.isExistingPathInsideSnapshotFolder('/tmp')) #test valid absolute symlink inside snapshot os.symlink(os.path.join(backupPath, 'foo'), os.path.join(backupPath, 'bar')) self.assertIsLink(backupPath, 'bar') - self.assertTrue(sid.canOpenPath('/bar')) + self.assertTrue(sid.isExistingPathInsideSnapshotFolder('/bar')) #test valid relative symlink inside snapshot os.symlink('./foo', os.path.join(backupPath, 'baz')) self.assertIsLink(backupPath, 'baz') - self.assertTrue(sid.canOpenPath('/baz')) + self.assertTrue(sid.isExistingPathInsideSnapshotFolder('/baz')) #test invalid symlink os.symlink(os.path.join(backupPath, 'asdf'), os.path.join(backupPath, 'qwer')) self.assertIsLink(backupPath, 'qwer') - self.assertFalse(sid.canOpenPath('/qwer')) + self.assertFalse(sid.isExistingPathInsideSnapshotFolder('/qwer')) #test valid symlink outside snapshot os.symlink('/tmp', os.path.join(backupPath, 'bla')) self.assertIsLink(backupPath, 'bla') - self.assertFalse(sid.canOpenPath('/bla')) + self.assertFalse(sid.isExistingPathInsideSnapshotFolder('/bla')) def test_name(self): sid = snapshots.SID('20151219-010324-123', self.cfg) diff --git a/common/test/test_takeSnapshot.py b/common/test/test_takeSnapshot.py index 2bb5862ab..e85dac7f9 100644 --- a/common/test/test_takeSnapshot.py +++ b/common/test/test_takeSnapshot.py @@ -66,9 +66,9 @@ def test_takeSnapshot(self, sleep): ) self.assertTrue(sid1.exists()) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'test'))) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'file with spaces'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'file with spaces'))) self.assertExists(self.cfg.anacronSpoolFile()) for f in ('config', 'fileinfo.bz2', @@ -100,7 +100,7 @@ def test_takeSnapshot(self, sleep): self.assertListEqual([True, False], self.sn.takeSnapshot(sid3, now, [(self.include.name, 0),])) self.assertTrue(sid3.exists()) - self.assertTrue(sid3.canOpenPath(os.path.join(self.include.name, 'lalala'))) + self.assertTrue(sid3.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'lalala'))) inode1 = self.getInode(sid1) inode3 = self.getInode(sid3) self.assertEqual(inode1, inode3) @@ -113,8 +113,8 @@ def test_takeSnapshot(self, sleep): self.assertListEqual([True, False], self.sn.takeSnapshot(sid4, now, [(self.include.name, 0),])) self.assertTrue(sid4.exists()) - self.assertTrue(sid4.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) - self.assertTrue(sid4.canOpenPath(os.path.join(self.include.name, 'test'))) + self.assertTrue(sid4.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) + self.assertTrue(sid4.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) @patch('time.sleep') # speed up unittest def test_takeSnapshot_with_spaces_in_include(self, sleep): @@ -125,8 +125,8 @@ def test_takeSnapshot_with_spaces_in_include(self, sleep): self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(include, 0),])) self.assertTrue(sid1.exists()) - self.assertTrue(sid1.canOpenPath(os.path.join(include, 'foo', 'bar', 'baz'))) - self.assertTrue(sid1.canOpenPath(os.path.join(include, 'test'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(include, 'foo', 'bar', 'baz'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(include, 'test'))) for f in ('config', 'fileinfo.bz2', 'info', @@ -145,9 +145,9 @@ def test_takeSnapshot_exclude(self, sleep): self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'foo', 'bar'))) - self.assertFalse(sid1.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'test'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar'))) + self.assertFalse(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) for f in ('config', 'fileinfo.bz2', 'info', @@ -168,9 +168,9 @@ def test_takeSnapshot_with_spaces_in_exclude(self, sleep): self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'test'))) - self.assertFalse(sid1.canOpenPath(exclude)) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) + self.assertFalse(sid1.isExistingPathInsideSnapshotFolder(exclude)) for f in ('config', 'fileinfo.bz2', 'info', @@ -189,8 +189,8 @@ def test_takeSnapshot_error(self, sleep): self.assertListEqual([True, True], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) - self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) - self.assertFalse(sid1.canOpenPath(os.path.join(self.include.name, 'test'))) + self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) + self.assertFalse(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) for f in ('config', 'fileinfo.bz2', 'info', diff --git a/qt/app.py b/qt/app.py index e23a60771..07a3831d8 100644 --- a/qt/app.py +++ b/qt/app.py @@ -1554,18 +1554,16 @@ def btnFolderUpClicked(self): self.updateFilesView(0) def btnFolderHistoryPreviousClicked(self): - path = self.path_history.previous() - full_path = self.sid.pathBackup(path) - - if os.path.isdir(full_path) and self.sid.canOpenPath(path): - self.path = path - self.updateFilesView(0) + self._folderHistoryClicked(self.path_history.previous()) def btnFolderHistoryNextClicked(self): - path = self.path_history.next() + self._folderHistoryClicked(self.path_history.next()) + + def _folderHistoryClicked(self, path): full_path = self.sid.pathBackup(path) - if os.path.isdir(full_path) and self.sid.canOpenPath(path): + if (os.path.isdir(full_path) + and self.sid.isExistingPathInsideSnapshotFolder(path)): self.path = path self.updateFilesView(0) @@ -1646,7 +1644,11 @@ def openPath(self, rel_path): rel_path = os.path.join(self.path, rel_path) full_path = self.sid.pathBackup(rel_path) - if os.path.exists(full_path) and self.sid.canOpenPath(rel_path): + # The class "GenericNonSnapshot" indicates that "Now" is selected + # in the snapshots timeline widget. + if (os.path.exists(full_path) + and (isinstance(self.sid, snapshots.GenericNonSnapshot) # "Now" + or self.sid.isExistingPathInsideSnapshotFolder(rel_path))): if os.path.isdir(full_path): self.path = rel_path