Skip to content

Commit 41c5760

Browse files
authored
Merge pull request #241 from karlch/fix-xdg-trash
Fix compliance with the xdg trash standard
2 parents 4147700 + 68159b6 commit 41c5760

File tree

5 files changed

+54
-18
lines changed

5 files changed

+54
-18
lines changed

docs/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Fixed:
3434
* Various issues when handling backslash and percent characters in paths and
3535
completions. Thanks
3636
`@woefe <https://github.com/woefe>`_ for pointing these out!
37+
* Quoting of paths and the date format of the trashinfo file created by the ``:delete``
38+
command. Thanks `@woefe <https://github.com/woefe>`_ for the bug report.
3739

3840

3941
v0.7.0 (2020-05-17)

tests/end2end/features/completion/completion.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ Feature: Using completion.
132132
When I run delete %
133133
When I run command --text="undelete "
134134
Then a possible completion should contain undelete image_01.jpg
135+
And the trash completion for 'image_01.jpg' should show the trashinfo
135136

136137
Scenario: Do not show trash completion in manipulate mode
137138
Given I open 2 images

tests/end2end/features/completion/test_completion_bdd.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
# Copyright 2017-2020 Christian Karl (karlch) <karlch at protonmail dot com>
55
# License: GNU GPL v3, see the "LICENSE" and "AUTHORS" files for details.
66

7+
import os
8+
79
import pytest
810
import pytest_bdd as bdd
911

1012
import vimiv.gui.completionwidget
13+
from vimiv.utils import trash_manager
1114

1215

1316
bdd.scenarios("completion.feature")
@@ -18,21 +21,24 @@ def completionwidget():
1821
return vimiv.gui.completionwidget.CompletionView.instance
1922

2023

24+
@pytest.fixture()
25+
def completiondata(completionwidget):
26+
model = completionwidget.model()
27+
return [
28+
[model.index(row, column).data() for column in range(model.columnCount())]
29+
for row in range(model.rowCount())
30+
]
31+
32+
2133
@bdd.then("no completion should be selected")
2234
def check_no_completion_selected(completionwidget):
2335
assert not completionwidget.selectedIndexes()
2436

2537

2638
@bdd.then(bdd.parsers.parse("a possible completion should contain {text}"))
2739
@bdd.then("a possible completion should contain <text>")
28-
def check_available_completion_text(completionwidget, text):
29-
model = completionwidget.model()
30-
completion_data = [
31-
model.index(row, column).data()
32-
for row in range(model.rowCount())
33-
for column in range(model.columnCount())
34-
]
35-
completion_text = "\n".join(completion_data)
40+
def check_available_completion_text(completiondata, text):
41+
completion_text = "\n".join(" ".join(row) for row in completiondata)
3642
assert text in completion_text
3743

3844

@@ -48,3 +54,21 @@ def check_selected_completion_row(completionwidget, row):
4854
if row == -1:
4955
row = completionwidget.model().rowCount() - 1
5056
assert completionwidget.row() == row
57+
58+
59+
@bdd.then(
60+
bdd.parsers.parse("the trash completion for '{basename}' should show the trashinfo")
61+
)
62+
def check_show_deletion_date(completiondata, basename):
63+
original_path, deletion_date = trash_manager.trash_info(basename)
64+
# Space for formatting and remove seconds
65+
expected_date = deletion_date.replace("T", " ")[:-3]
66+
expected_dir = os.path.dirname(original_path)
67+
for completion_text, completion_dir, completion_date in completiondata:
68+
completion_basename = completion_text.replace(":undelete ", "")
69+
if completion_basename == basename:
70+
assert expected_date == completion_date
71+
assert expected_dir == completion_dir
72+
break
73+
else:
74+
assert False, f"Completion for '{basename}' not found"

vimiv/completion/completionmodels.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def __init__(self):
190190
column_widths=(0.4, 0.45, 0.15),
191191
valid_modes=api.modes.GLOBALS,
192192
)
193+
self._old_date_re = re.compile(r"(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})")
194+
self._date_re = re.compile(r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})")
193195

194196
def on_enter(self, text: str) -> None:
195197
"""Update trash model on enter to include any newly un-/deleted paths."""
@@ -200,13 +202,16 @@ def on_enter(self, text: str) -> None:
200202
original, date = trash_manager.trash_info(path)
201203
original = original.replace(os.path.expanduser("~"), "~")
202204
original = os.path.dirname(original)
203-
date = "%s-%s-%s %s:%s" % (
204-
date[2:4],
205-
date[4:6],
206-
date[6:8],
207-
date[9:11],
208-
date[11:13],
209-
)
205+
date_match = self._date_re.match(date)
206+
if date_match is None:
207+
# Wrong date formatting that was used up to v0.7.0
208+
# TODO remove after releasing v1.0.0
209+
date_match = self._old_date_re.match(date)
210+
if date_match is not None:
211+
year, month, day, hour, minute, _ = date_match.groups()
212+
date = f"{year}-{month}-{day} {hour}:{minute}"
213+
else:
214+
date = "date unknown"
210215
# Append data in column form
211216
data.append((cmd, original, date))
212217
self.set_data(data)

vimiv/utils/trash_manager.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,13 @@ def trash_info(filename: str) -> Tuple[str, str]:
8686
original_filename: The absolute path to the original file.
8787
deletion_date: The deletion date.
8888
"""
89+
from urllib.parse import unquote
90+
8991
info_filename = _get_info_filename(filename)
9092
info = TrashInfoParser()
9193
info.read(info_filename)
9294
content = info["Trash Info"]
93-
original_filename = content["Path"]
95+
original_filename = unquote(content["Path"])
9496
deletion_date = content["DeletionDate"]
9597
return original_filename, deletion_date
9698

@@ -132,13 +134,15 @@ def _create_info_file(trash_filename: str, original_filename: str) -> None:
132134
trash_filename: The name of the file in self.files_directory.
133135
original_filename: The original name of the file.
134136
"""
137+
from urllib.parse import quote
138+
135139
# Write to temporary file and use shutil.move to make sure the
136140
# operation is an atomic operation as specified by the standard
137141
temp_file = tempfile.NamedTemporaryFile(dir=_info_directory, delete=False, mode="w")
138142
info = TrashInfoParser()
139143
info["Trash Info"] = {
140-
"Path": original_filename,
141-
"DeletionDate": time.strftime("%Y%m%dT%H%M%S"),
144+
"Path": quote(original_filename),
145+
"DeletionDate": time.strftime("%Y-%m-%dT%H:%M:%S"),
142146
}
143147
info.write(temp_file, space_around_delimiters=False)
144148
# Move to proper filename

0 commit comments

Comments
 (0)