Skip to content

Commit fe98dcd

Browse files
Merge pull request #186 from syslabcom/scrum-2408-getVocabulary
getVocabulary: Call scrub_html on the end result
2 parents 45d40f4 + 4d13977 commit fe98dcd

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed

src/recensio/plone/browser/configure.zcml

+8
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,14 @@
403403
layer="recensio.plone.interfaces.IRecensioPloneLayer"
404404
/>
405405

406+
<browser:page
407+
name="getVocabulary"
408+
for="*"
409+
class=".vocabulary.RecensioVocabularyView"
410+
permission="zope2.View"
411+
layer="recensio.plone.interfaces.IRecensioPloneLayer"
412+
/>
413+
406414
<browser:view
407415
name="r"
408416
for="*"
+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
from AccessControl import getSecurityManager
2+
from Acquisition import aq_base
3+
from html import unescape
4+
from plone.app.content.browser.vocabulary import _parseJSON
5+
from plone.app.content.browser.vocabulary import _safe_callable_metadata
6+
from plone.app.content.browser.vocabulary import _unsafe_metadata
7+
from plone.app.content.browser.vocabulary import DEFAULT_PERMISSION_SECURE
8+
from plone.app.content.browser.vocabulary import MAX_BATCH_SIZE
9+
from plone.app.content.browser.vocabulary import VocabLookupException
10+
from plone.app.content.browser.vocabulary import VocabularyView
11+
from plone.app.content.utils import json_dumps
12+
from plone.base import PloneMessageFactory as _
13+
from plone.base.utils import safe_text
14+
from Products.CMFCore.utils import getToolByName
15+
from Products.MimetypesRegistry.MimeTypeItem import guess_icon_path
16+
from Products.MimetypesRegistry.MimeTypeItem import PREFIX
17+
from Products.PortalTransforms.transforms.safe_html import SafeHTML
18+
from zope.i18n import translate
19+
20+
import itertools
21+
22+
23+
class RecensioVocabularyView(VocabularyView):
24+
def __call__(self): # noqa: C901
25+
"""
26+
Accepts GET parameters of:
27+
name: Name of the vocabulary
28+
field: Name of the field the vocabulary is being retrieved for
29+
query: string or json object of criteria and options.
30+
json value consists of a structure:
31+
{
32+
criteria: object,
33+
sort_on: index,
34+
sort_order: (asc|reversed)
35+
}
36+
attributes: comma separated, or json object list
37+
batch: {
38+
page: 1-based page of results,
39+
size: size of paged results
40+
}
41+
"""
42+
context = self.get_context()
43+
self.request.response.setHeader(
44+
"Content-Type", "application/json; charset=utf-8"
45+
)
46+
47+
try:
48+
vocabulary = self.get_vocabulary()
49+
except VocabLookupException as e:
50+
return json_dumps({"error": e.args[0]})
51+
52+
results_are_brains = False
53+
if hasattr(vocabulary, "search_catalog"):
54+
query = self.parsed_query()
55+
results = vocabulary.search_catalog(query)
56+
results_are_brains = True
57+
elif hasattr(vocabulary, "search"):
58+
try:
59+
query = self.parsed_query()["SearchableText"]["query"]
60+
except KeyError:
61+
results = iter(vocabulary)
62+
else:
63+
results = vocabulary.search(query)
64+
else:
65+
results = vocabulary
66+
67+
try:
68+
total = len(results)
69+
except TypeError:
70+
# do not error if object does not support __len__
71+
# we'll check again later if we can figure some size
72+
# out
73+
total = 0
74+
75+
# get batch
76+
batch = _parseJSON(self.request.get("batch", ""))
77+
if batch and ("size" not in batch or "page" not in batch):
78+
batch = None # batching not providing correct options
79+
if batch:
80+
# must be sliceable for batching support
81+
page = int(batch["page"])
82+
size = int(batch["size"])
83+
if size > MAX_BATCH_SIZE:
84+
raise Exception("Max batch size is 500")
85+
# page is being passed in is 1-based
86+
start = (max(page - 1, 0)) * size
87+
end = start + size
88+
# Try __getitem__-based slice, then iterator slice.
89+
# The iterator slice has to consume the iterator through
90+
# to the desired slice, but that shouldn't be the end
91+
# of the world because at some point the user will hopefully
92+
# give up scrolling and search instead.
93+
try:
94+
results = results[start:end]
95+
except TypeError:
96+
results = itertools.islice(results, start, end)
97+
98+
# build result items
99+
items = []
100+
101+
attributes = _parseJSON(self.request.get("attributes", ""))
102+
if isinstance(attributes, str) and attributes:
103+
attributes = attributes.split(",")
104+
105+
translate_ignored = self.get_translated_ignored()
106+
transform = SafeHTML()
107+
if attributes:
108+
base_path = self.get_base_path(context)
109+
sm = getSecurityManager()
110+
can_edit = sm.checkPermission(DEFAULT_PERMISSION_SECURE, context)
111+
mtt = getToolByName(self.context, "mimetypes_registry")
112+
for vocab_item in results:
113+
if not results_are_brains:
114+
vocab_item = vocab_item.value
115+
item = {}
116+
for attr in attributes:
117+
key = attr
118+
if ":" in attr:
119+
key, attr = attr.split(":", 1)
120+
if attr in _unsafe_metadata and not can_edit:
121+
continue
122+
if key == "path":
123+
attr = "getPath"
124+
val = getattr(vocab_item, attr, None)
125+
if callable(val):
126+
if attr in _safe_callable_metadata:
127+
val = val()
128+
else:
129+
continue
130+
if key == "path" and val is not None:
131+
val = val[len(base_path) :]
132+
if key not in translate_ignored and isinstance(val, str):
133+
val = translate(_(safe_text(val)), context=self.request)
134+
item[key] = val
135+
if key == "getMimeIcon":
136+
item[key] = None
137+
# get mime type icon url from mimetype registry'
138+
contenttype = aq_base(getattr(vocab_item, "mime_type", None))
139+
if contenttype:
140+
ctype = mtt.lookup(contenttype)
141+
if ctype:
142+
item[key] = "/".join(
143+
[base_path, guess_icon_path(ctype[0])]
144+
)
145+
else:
146+
item[key] = "/".join(
147+
[
148+
base_path,
149+
PREFIX.rstrip("/"),
150+
"unknown.png",
151+
]
152+
)
153+
items.append(item)
154+
else:
155+
items = [
156+
{
157+
"id": item.value,
158+
"text": (item.title if item.title else ""),
159+
}
160+
for item in results
161+
]
162+
163+
if total == 0:
164+
total = len(items)
165+
166+
return unescape(
167+
transform.scrub_html(json_dumps({"results": items, "total": total}))
168+
)

0 commit comments

Comments
 (0)