0005-misc-use-tokens-to-store-autocompletion-context-6066.patch
wcs/api.py | ||
---|---|---|
1147 | 1147 | |
1148 | 1148 |
class AutocompleteDirectory(Directory): |
1149 | 1149 |
def _q_lookup(self, component): |
1150 |
info = get_session().get_data_source_query_info_from_token(component) |
|
1151 |
if not info: |
|
1150 |
try: |
|
1151 |
token = get_publisher().token_class.get(component) |
|
1152 |
if token.type != 'autocomplete': |
|
1153 |
raise KeyError() |
|
1154 |
if token.context['session_id'] != get_session().id: |
|
1155 |
raise KeyError() |
|
1156 |
if token.context.get('url') == '': |
|
1157 |
# this is a datasource without a json url |
|
1158 |
# (typically a Python source) |
|
1159 |
raise KeyError() |
|
1160 |
except KeyError: |
|
1152 | 1161 |
raise AccessForbiddenError() |
1153 | 1162 |
get_response().set_content_type('application/json') |
1154 | 1163 | |
1155 |
if isinstance(info, str) and not info.startswith('carddef:'): |
|
1156 |
# legacy json source |
|
1157 |
info = {'url': info} |
|
1158 |
elif isinstance(info, str): |
|
1159 |
# legacy carddef source |
|
1160 |
info = {'carddef_ref': info} |
|
1164 |
info = token.context |
|
1161 | 1165 | |
1162 | 1166 |
if 'url' in info: |
1163 | 1167 |
url = info['url'] |
... | ... | |
1180 | 1184 | |
1181 | 1185 |
# carddef_ref in info |
1182 | 1186 |
carddef_ref = info['carddef_ref'] |
1187 |
custom_view = None |
|
1188 |
if 'dynamic_custom_view' in info: |
|
1189 |
custom_view = get_publisher().custom_view_class.get(info['dynamic_custom_view']) |
|
1190 |
custom_view.filters = info['dynamic_custom_view_filters'] |
|
1183 | 1191 |
values = CardDef.get_data_source_items( |
1184 | 1192 |
carddef_ref, |
1185 |
custom_view=info.get('dynamic_custom_view'),
|
|
1193 |
custom_view=custom_view,
|
|
1186 | 1194 |
query=get_request().form.get('q', ''), |
1187 | 1195 |
limit=get_request().form.get('page_limit'), |
1188 | 1196 |
) |
... | ... | |
1195 | 1203 |
try: |
1196 | 1204 |
data_source = get_data_source_object({'type': component}, ignore_errors=False) |
1197 | 1205 |
except KeyError: |
1198 |
info = get_session().get_data_source_query_info_from_token(component) |
|
1199 |
if not info: |
|
1206 |
try: |
|
1207 |
token = get_publisher().token_class.get(component) |
|
1208 |
if token.type != 'autocomplete': |
|
1209 |
raise KeyError() |
|
1210 |
if token.context['session_id'] != get_session().id: |
|
1211 |
raise KeyError() |
|
1212 |
except KeyError: |
|
1200 | 1213 |
raise TraversalError() |
1214 |
info = token.context |
|
1201 | 1215 |
try: |
1202 | 1216 |
data_source = get_data_source_object({'type': info['slug']}, ignore_errors=False) |
1203 | 1217 |
except KeyError: |
wcs/data_sources.py | ||
---|---|---|
599 | 599 |
if self.type == 'jsonp': |
600 | 600 |
return self.data_source.get('value') |
601 | 601 | |
602 |
token_context = {} |
|
602 | 603 |
if self.type == 'json' and self.query_parameter: |
603 | 604 |
json_url = self.get_json_query_url() |
604 |
info = None |
|
605 |
if json_url: |
|
606 |
info = {'url': json_url, 'data_source': self.id} |
|
607 |
return '/api/autocomplete/%s' % (get_session().get_data_source_query_info_token(info)) |
|
605 |
token_context = {'url': json_url, 'data_source': self.id} |
|
608 | 606 | |
609 |
if self.type and self.type.startswith('carddef:'): |
|
610 |
api_url = '/api/autocomplete/%s' % ( |
|
611 |
get_session().get_data_source_query_info_token( |
|
612 |
{ |
|
613 |
'carddef_ref': self.type, |
|
614 |
} |
|
615 |
) |
|
616 |
) |
|
617 |
parts = self.type.split(':') |
|
618 |
if len(parts) <= 2: |
|
619 |
return api_url |
|
620 | ||
621 |
# custom view, check if it's dynamic |
|
622 |
from wcs.carddef import CardDef |
|
623 |
from wcs.workflows import WorkflowStatusItem |
|
607 |
elif self.type and self.type.startswith('carddef:'): |
|
608 |
token_context = {'carddef_ref': self.type} |
|
624 | 609 | |
625 |
custom_view = CardDef.get_data_source_custom_view(self.type) |
|
626 |
if custom_view is None: |
|
627 |
get_publisher().record_error( |
|
628 |
_('Unknown custom view "%s" for CardDef "%s"') % (parts[2], parts[1]), |
|
629 |
context='[DATASOURCE]', |
|
630 |
notify=True, |
|
631 |
record=True, |
|
632 |
) |
|
633 |
return api_url |
|
634 | ||
635 |
had_template = False |
|
636 |
for filter_key, filter_value in custom_view.filters.items(): |
|
637 |
if not Template.is_template_string(filter_value): |
|
638 |
continue |
|
639 |
custom_view.filters[filter_key] = WorkflowStatusItem.compute(filter_value) |
|
640 |
had_template = True |
|
641 |
if had_template: |
|
642 |
# keep altered custom view in session |
|
643 |
api_url = '/api/autocomplete/%s' % ( |
|
644 |
get_session().get_data_source_query_info_token( |
|
645 |
{'carddef_ref': self.type, 'dynamic_custom_view': custom_view} |
|
610 |
parts = self.type.split(':') |
|
611 |
if len(parts) > 2: |
|
612 |
# custom view, check if it's dynamic |
|
613 |
from wcs.carddef import CardDef |
|
614 |
from wcs.workflows import WorkflowStatusItem |
|
615 | ||
616 |
custom_view = CardDef.get_data_source_custom_view(self.type) |
|
617 |
if custom_view is None: |
|
618 |
get_publisher().record_error( |
|
619 |
_('Unknown custom view "%s" for CardDef "%s"') % (parts[2], parts[1]), |
|
620 |
context='[DATASOURCE]', |
|
621 |
notify=True, |
|
622 |
record=True, |
|
646 | 623 |
) |
647 |
) |
|
648 |
return api_url |
|
624 |
else: |
|
625 |
had_template = False |
|
626 |
for filter_key, filter_value in custom_view.filters.items(): |
|
627 |
if not Template.is_template_string(filter_value): |
|
628 |
continue |
|
629 |
custom_view.filters[filter_key] = WorkflowStatusItem.compute(filter_value) |
|
630 |
had_template = True |
|
631 |
if had_template: |
|
632 |
# keep altered custom view in token |
|
633 |
token_context = { |
|
634 |
'carddef_ref': self.type, |
|
635 |
'dynamic_custom_view': custom_view.id, |
|
636 |
'dynamic_custom_view_filters': custom_view.filters, |
|
637 |
} |
|
638 | ||
639 |
if token_context: |
|
640 |
token_context['session_id'] = get_session().id |
|
641 |
token, dummy = get_publisher().token_class.get_or_create( |
|
642 |
type='autocomplete', context=token_context |
|
643 |
) |
|
644 |
token.set_expiration_delay(3600) |
|
645 |
token.store() |
|
646 |
return '/api/autocomplete/%s' % token.id |
|
649 | 647 | |
650 | 648 |
return None |
651 | 649 | |
... | ... | |
656 | 654 |
context = get_publisher().substitutions.get_context_variables(mode='lazy') |
657 | 655 |
new_url = get_variadic_url(url, context) |
658 | 656 |
if new_url != url: |
659 |
info = {'url': new_url, 'slug': self.slug} |
|
660 |
return '/api/geojson/%s' % get_session().get_data_source_query_info_token(info) |
|
657 |
token_context = {'session_id': get_session().id, 'url': new_url, 'slug': self.slug} |
|
658 |
token, dummy = get_publisher().token_class.get_or_create( |
|
659 |
type='autocomplete', context=token_context |
|
660 |
) |
|
661 |
token.set_expiration_delay(3600) |
|
662 |
token.store() |
|
663 |
return '/api/geojson/%s' % token.id |
|
661 | 664 |
return '/api/geojson/%s' % self.slug |
662 | 665 | |
663 | 666 |
def get_geojson_data(self, force_url=None): |
wcs/qommon/tokens.py | ||
---|---|---|
19 | 19 |
import string |
20 | 20 | |
21 | 21 |
from django.utils.timezone import make_aware, now |
22 | ||
23 | 22 |
from quixote import get_publisher |
24 | 23 | |
25 |
from .storage import Less, StorableObject |
|
24 |
from .storage import Equal, Less, StorableObject
|
|
26 | 25 | |
27 | 26 | |
28 | 27 |
class Token(StorableObject): |
... | ... | |
49 | 48 |
if not cls.has_key(id): |
50 | 49 |
return id |
51 | 50 | |
51 |
@classmethod |
|
52 |
def get_or_create(cls, type, context): |
|
53 |
try: |
|
54 |
return (cls.select(clause=[Equal('type', type), Equal('context', context)])[0], False) |
|
55 |
except IndexError: |
|
56 |
token = cls() |
|
57 |
token.type = type |
|
58 |
token.context = context |
|
59 |
return (token, True) |
|
60 | ||
52 | 61 |
def migrate(self): |
53 | 62 |
if isinstance(self.expiration, (float, int)): |
54 | 63 |
self.expiration = make_aware(datetime.datetime.fromtimestamp(self.expiration)) |
wcs/sessions.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import os |
18 | 18 |
import time |
19 |
import uuid |
|
20 | 19 | |
21 | 20 |
from .qommon import sessions |
22 | 21 |
from .qommon.sessions import Session |
... | ... | |
27 | 26 |
magictokens = None |
28 | 27 |
anonymous_formdata_keys = None |
29 | 28 |
visiting_objects = None |
30 |
data_source_query_url_tokens = None |
|
31 | 29 | |
32 | 30 |
def has_info(self): |
33 | 31 |
return ( |
34 | 32 |
self.anonymous_formdata_keys |
35 | 33 |
or self.magictokens |
36 | 34 |
or self.visiting_objects |
37 |
or self.data_source_query_url_tokens |
|
38 | 35 |
or Session.has_info(self) |
39 | 36 |
) |
40 | 37 | |
... | ... | |
149 | 146 |
del session.visiting_objects[object_key] |
150 | 147 |
session.store() |
151 | 148 | |
152 |
def get_data_source_query_info_token(self, info): |
|
153 |
# info can be an URL in case of remote JSON source or a cards |
|
154 |
# reference, like carddef:foobar. It is stored in a dictionary |
|
155 |
# with _url_ in its name for historical reasons. |
|
156 |
if not self.data_source_query_url_tokens: |
|
157 |
self.data_source_query_url_tokens = {} |
|
158 |
for key, value in self.data_source_query_url_tokens.items(): |
|
159 |
if value == info: |
|
160 |
return key |
|
161 |
key = str(uuid.uuid4()) |
|
162 |
self.data_source_query_url_tokens[key] = info |
|
163 |
if self.id: |
|
164 |
# do not save if the session has not been created yet, this happens |
|
165 |
# for API calls to create card/form data. |
|
166 |
self.store() |
|
167 |
return key |
|
168 | ||
169 |
def get_data_source_query_info_from_token(self, token): |
|
170 |
if not self.data_source_query_url_tokens: |
|
171 |
return None |
|
172 |
return self.data_source_query_url_tokens.get(token) |
|
173 | ||
174 | 149 | |
175 | 150 |
sessions.BasicSession = BasicSession |
176 | 151 |
StorageSessionManager = sessions.StorageSessionManager |
177 |
- |