Skip to content

Commit 7d47de0

Browse files
authored
Merge pull request #2035 from pbiering/sharing-fix-get-filename-propose
Sharing fix get filename propose
2 parents ebb5dfb + 9c47796 commit 7d47de0

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

radicale/app/get.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,34 @@
2020

2121
import posixpath
2222
from http import client
23+
from typing import Union
2324
from urllib.parse import quote
2425

2526
from radicale import httputils, pathutils, storage, types, xmlutils
2627
from radicale.app.base import Access, ApplicationBase
2728
from radicale.log import logger
2829

2930

30-
def propose_filename(collection: storage.BaseCollection) -> str:
31+
def propose_filename(collection: storage.BaseCollection, share: Union[dict, None] = None) -> str:
3132
"""Propose a filename for a collection."""
32-
if collection.tag == "VADDRESSBOOK":
33+
share_bday_automap = False
34+
if share and share['ShareType'] == "bday":
35+
share_bday_automap = True
36+
if collection.tag == "VADDRESSBOOK" and not share_bday_automap:
3337
fallback_title = "Address book"
3438
suffix = ".vcf"
35-
elif collection.tag == "VCALENDAR":
39+
elif collection.tag == "VCALENDAR" or share_bday_automap:
3640
fallback_title = "Calendar"
3741
suffix = ".ics"
3842
else:
3943
fallback_title = posixpath.basename(collection.path)
4044
suffix = ""
41-
title = collection.get_meta("D:displayname") or fallback_title
45+
if share and 'Properties' in share and share['Properties'] and "D:displayname" in share['Properties']:
46+
title = share['Properties']["D:displayname"]
47+
elif share_bday_automap:
48+
title = fallback_title
49+
else:
50+
title = collection.get_meta("D:displayname") or fallback_title
4251
if title and not title.lower().endswith(suffix.lower()):
4352
title += suffix
4453
return title
@@ -77,6 +86,7 @@ def do_GET(self, environ: types.WSGIEnviron, base_prefix: str, path: str,
7786
# Dispatch /.web path to web module
7887
return self._web.get(environ, base_prefix, path, user)
7988
permissions_filter = None
89+
share = None
8090
if self._sharing._enabled:
8191
# Sharing by token or map (if enabled)
8292
share = self._sharing.sharing_collection_resolver(path, user)
@@ -102,9 +112,12 @@ def do_GET(self, environ: types.WSGIEnviron, base_prefix: str, path: str,
102112
if not item.tag:
103113
return (httputils.NOT_ALLOWED if limited_access else
104114
httputils.DIRECTORY_LISTING)
105-
content_type = xmlutils.MIMETYPES[item.tag]
115+
if share and share['ShareType'] == "bday":
116+
content_type = xmlutils.MIMETYPES["VCALENDAR"]
117+
else:
118+
content_type = xmlutils.MIMETYPES[item.tag]
106119
content_disposition = self._content_disposition_attachment(
107-
propose_filename(item))
120+
propose_filename(item, share))
108121
elif limited_access:
109122
return httputils.NOT_ALLOWED
110123
else:

radicale/tests/test_sharing.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,8 @@ def test_sharing_api_map_usage(self) -> None:
903903
"collection_by_map": "True",
904904
"collection_by_token": "True"},
905905
"logging": {"request_header_on_debug": "False",
906+
"response_header_on_debug": "True",
907+
"response_content_on_debug": "True",
906908
"request_content_on_debug": "False"},
907909
"rights": {"type": "owner_only"}})
908910

@@ -983,6 +985,9 @@ def test_sharing_api_map_usage(self) -> None:
983985
logging.info("\n*** fetch collection (with credentials) as owner")
984986
_, headers, answer = self.request("GET", path_mapped, check=200, login="owner:ownerpw")
985987
assert "UID:event" in answer
988+
assert 'Content-Disposition' in headers
989+
# fallback title
990+
assert 'Calendar.ics' in headers['Content-Disposition']
986991

987992
logging.info("\n*** fetch item (with credentials) as owner")
988993
_, headers, answer = self.request("GET", path_mapped_item1, check=200, login="owner:ownerpw")
@@ -995,6 +1000,9 @@ def test_sharing_api_map_usage(self) -> None:
9951000
_, headers, answer = self.request("GET", path_shared, check=200, login="user:userpw")
9961001
assert "UID:event1" in answer
9971002
assert "UID:event2" in answer
1003+
assert 'Content-Disposition' in headers
1004+
# title from Properties
1005+
assert 'Test.ics' in headers['Content-Disposition']
9981006

9991007
logging.info("\n*** fetch item via map (with credentials) as user")
10001008
_, headers, answer = self.request("GET", path_shared_item1, check=200, login="user:userpw")
@@ -4197,6 +4205,7 @@ def test_sharing_api_bday_basic(self) -> None:
41974205
"collection_by_bday": "True"},
41984206
"logging": {"request_header_on_debug": "False",
41994207
"response_content_on_debug": "True",
4208+
"response_header_on_debug": "True",
42004209
"request_content_on_debug": "True"},
42014210
"rights": {"type": "owner_only"}})
42024211

@@ -4238,9 +4247,12 @@ def test_sharing_api_bday_basic(self) -> None:
42384247

42394248
# execute GET as owner
42404249
logging.info("\n*** GET VCF collection owner -> ok")
4241-
_, answer = self.get(path_mapped, login="owner:ownerpw")
4250+
_, headers, answer = self.request("GET", path_mapped, login="owner:ownerpw")
42424251
assert "contact1" in answer
42434252
assert "contact2" in answer
4253+
# title from fallback
4254+
assert 'Content-Disposition' in headers
4255+
assert 'Address%20book.vcf' in headers['Content-Disposition']
42444256

42454257
# create map
42464258
logging.info("\n*** create bday user/owner:r -> ok")
@@ -4251,6 +4263,7 @@ def test_sharing_api_bday_basic(self) -> None:
42514263
json_dict['Permissions'] = "r"
42524264
json_dict['Enabled'] = True
42534265
json_dict['Hidden'] = False
4266+
json_dict['Properties'] = {"D:displayname": "Test-BDAY"}
42544267
_, headers, answer = self._sharing_api_json("bday", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
42554268
answer_dict = json.loads(answer)
42564269
assert answer_dict['Status'] == "success"
@@ -4283,14 +4296,20 @@ def test_sharing_api_bday_basic(self) -> None:
42834296

42844297
# verify content as user
42854298
logging.info("\n*** GET collection user -> ok")
4286-
_, answer = self.get(path_shared_r, login="user:userpw")
4299+
_, headers, answer = self.request("GET", path_shared_r, login="user:userpw")
42874300
assert "BEGIN:VCARD" not in answer
42884301
assert "BEGIN:VCALENDAR" in answer
42894302
assert "RRULE:FREQ=YEARLY" in answer
42904303
assert "DTSTART;VALUE=DATE:19700101" in answer
42914304
assert "DTEND;VALUE=DATE:19700102" in answer
42924305
assert "TRANSP:TRANSPARENT" in answer
42934306
assert "DESCRIPTION:BDAY=1970-01-01" in answer
4307+
# content type must be adjusted
4308+
assert 'Content-Type' in headers
4309+
assert 'text/calendar' in headers['Content-Type']
4310+
# title from Properties
4311+
assert 'Content-Disposition' in headers
4312+
assert 'Test-BDAY.ics' in headers['Content-Disposition']
42944313

42954314
# verify report as user
42964315
logging.info("\n*** REPORT collection user -> ok")
@@ -4549,6 +4568,7 @@ def test_sharing_api_bday_self(self) -> None:
45494568
"enforce_properties_overlay": "True",
45504569
"collection_by_bday": "True"},
45514570
"logging": {"request_header_on_debug": "False",
4571+
"response_header_on_debug": "True",
45524572
"response_content_on_debug": "True",
45534573
"request_content_on_debug": "True"},
45544574
"rights": {"type": "owner_only"}})
@@ -4674,3 +4694,12 @@ def test_sharing_api_bday_self(self) -> None:
46744694
assert "D:sync-token" not in response
46754695
assert "C:supported-calendar-component-set" in response
46764696
assert "D:current-user-privilege-set" in response
4697+
4698+
# verify content as owner
4699+
logging.info("\n*** GET collection owner -> ok")
4700+
_, headers, answer = self.request("GET", path_shared, login="owner:ownerpw")
4701+
assert 'Content-Type' in headers
4702+
assert 'text/calendar' in headers['Content-Type']
4703+
# title from default
4704+
assert 'Content-Disposition' in headers
4705+
assert 'Calendar.ics' in headers['Content-Disposition']

0 commit comments

Comments
 (0)