0001-forms-extend-json-autocomplete-support-for-be-asynch.patch
tests/test_form_pages.py | ||
---|---|---|
11 | 11 |
from urlparse import urlparse |
12 | 12 |
import zipfile |
13 | 13 |
import base64 |
14 |
from webtest import Upload |
|
14 |
from webtest import Upload, Hidden
|
|
15 | 15 |
import mock |
16 | 16 | |
17 | 17 |
try: |
... | ... | |
4785 | 4785 |
assert formdef.data_class().select()[0].data['0'] == ['2'] |
4786 | 4786 |
assert formdef.data_class().select()[0].data['0_display'] == 'world' |
4787 | 4787 | |
4788 |
def test_item_field_autocomplete_json_source(http_requests, pub): |
|
4789 |
user = create_user(pub) |
|
4790 |
formdef = create_formdef() |
|
4791 |
formdef.data_class().wipe() |
|
4792 | ||
4793 |
NamedDataSource.wipe() |
|
4794 |
data_source = NamedDataSource(name='foobar') |
|
4795 |
data_source.data_source = {'type': 'json', 'value': 'http://remote.example.net/json'} |
|
4796 |
data_source.store() |
|
4797 | ||
4798 |
formdef.fields = [ |
|
4799 |
fields.ItemField(id='0', label='string', type='item', |
|
4800 |
data_source={'type': 'foobar'}, |
|
4801 |
display_mode='autocomplete', |
|
4802 |
), |
|
4803 |
] |
|
4804 |
formdef.store() |
|
4805 | ||
4806 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4807 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}, |
|
4808 |
{'id': '2', 'text': 'world', 'extra': 'bar'}]} |
|
4809 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4810 |
resp = get_app(pub).get('/test/') |
|
4811 |
assert "$('#form_f0').select2();" in resp.body |
|
4812 |
resp.form['f0'] = '2' |
|
4813 |
resp = resp.form.submit('submit') # -> validation page |
|
4814 |
resp = resp.form.submit('submit') # -> submit |
|
4815 |
assert formdef.data_class().select()[0].data['0'] == '2' |
|
4816 |
assert formdef.data_class().select()[0].data['0_display'] == 'world' |
|
4817 |
assert formdef.data_class().select()[0].data['0_structured'] == data['data'][1] |
|
4818 | ||
4819 |
# check with possibility of remote query |
|
4820 |
data_source.query_parameter = 'q' |
|
4821 |
data_source.id_parameter = 'id' |
|
4822 |
data_source.store() |
|
4823 | ||
4824 |
formdef.data_class().wipe() |
|
4825 | ||
4826 |
app = get_app(pub) |
|
4827 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4828 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}, |
|
4829 |
{'id': '2', 'text': 'world', 'extra': 'bar'}]} |
|
4830 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4831 |
resp = app.get('/test/') |
|
4832 |
assert urlopen.call_count == 0 |
|
4833 |
pq = resp.pyquery.remove_namespaces() |
|
4834 |
select2_url = pq('select').attr['data-select2-url'] |
|
4835 | ||
4836 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4837 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]} |
|
4838 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4839 |
resp2 = app.get(select2_url + '?q=hell') |
|
4840 |
assert urlopen.call_count == 1 |
|
4841 |
assert urlopen.call_args[0][0] == 'http://remote.example.net/json?q=hell' |
|
4842 |
assert resp2.json == data |
|
4843 | ||
4844 |
# check unauthorized access |
|
4845 |
resp2 = get_app(pub).get(select2_url + '?q=hell', status=403) |
|
4846 | ||
4847 |
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget |
|
4848 |
resp.form.fields['f0_display'] = Hidden(form=resp.form, tag='input', name='f0_display', pos=10) |
|
4849 |
resp.form['f0'].force_value('1') |
|
4850 |
resp.form.fields['f0_display'].force_value('hello') |
|
4851 | ||
4852 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4853 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]} |
|
4854 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4855 |
resp = resp.form.submit('submit') # -> validation page |
|
4856 |
assert urlopen.call_count == 1 |
|
4857 |
assert urlopen.call_args[0][0] == 'http://remote.example.net/json?id=1' |
|
4858 |
assert resp.form['f0'].value == '1' |
|
4859 |
assert resp.form['f0_label'].value == 'hello' |
|
4860 | ||
4861 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4862 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]} |
|
4863 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4864 |
resp = resp.form.submit('submit') # -> submit |
|
4865 |
assert urlopen.call_count == 1 |
|
4866 |
assert urlopen.call_args[0][0] == 'http://remote.example.net/json?id=1' |
|
4867 |
assert formdef.data_class().select()[0].data['0'] == '1' |
|
4868 |
assert formdef.data_class().select()[0].data['0_display'] == 'hello' |
|
4869 |
assert formdef.data_class().select()[0].data['0_structured'] == data['data'][0] |
|
4870 | ||
4871 |
# same thing with signed URLs |
|
4872 |
formdef.data_class().wipe() |
|
4873 |
open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('''\ |
|
4874 |
[wscall-secrets] |
|
4875 |
remote.example.net = 1234 |
|
4876 |
''') |
|
4877 | ||
4878 |
app = get_app(pub) |
|
4879 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4880 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}, |
|
4881 |
{'id': '2', 'text': 'world', 'extra': 'bar'}]} |
|
4882 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4883 |
resp = app.get('/test/') |
|
4884 |
assert urlopen.call_count == 0 |
|
4885 |
pq = resp.pyquery.remove_namespaces() |
|
4886 |
select2_url = pq('select').attr['data-select2-url'] |
|
4887 | ||
4888 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4889 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]} |
|
4890 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4891 |
resp2 = app.get(select2_url + '?q=hell') |
|
4892 |
assert urlopen.call_count == 1 |
|
4893 |
assert urlopen.call_args[0][0].startswith('http://remote.example.net/json?q=hell&orig=example.net&') |
|
4894 |
assert resp2.json == data |
|
4895 | ||
4896 |
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget |
|
4897 |
resp.form.fields['f0_display'] = Hidden(form=resp.form, tag='input', name='f0_display', pos=10) |
|
4898 |
resp.form['f0'].force_value('1') |
|
4899 |
resp.form.fields['f0_display'].force_value('hello') |
|
4900 | ||
4901 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4902 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]} |
|
4903 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4904 |
resp = resp.form.submit('submit') # -> validation page |
|
4905 |
assert urlopen.call_count == 1 |
|
4906 |
assert urlopen.call_args[0][0].startswith('http://remote.example.net/json?id=1&orig=example.net&') |
|
4907 |
assert resp.form['f0'].value == '1' |
|
4908 |
assert resp.form['f0_label'].value == 'hello' |
|
4909 | ||
4910 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4911 |
data = {'data': [{'id': '1', 'text': 'hello', 'extra': 'foo'}]} |
|
4912 |
urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data)) |
|
4913 |
resp = resp.form.submit('submit') # -> submit |
|
4914 |
assert urlopen.call_count == 1 |
|
4915 |
assert urlopen.call_args[0][0].startswith('http://remote.example.net/json?id=1&orig=example.net&') |
|
4916 |
assert formdef.data_class().select()[0].data['0'] == '1' |
|
4917 |
assert formdef.data_class().select()[0].data['0_display'] == 'hello' |
|
4918 |
assert formdef.data_class().select()[0].data['0_structured'] == data['data'][0] |
|
4919 | ||
4920 |
def test_item_field_autocomplete_jsonp_source(http_requests, pub): |
|
4921 |
user = create_user(pub) |
|
4922 |
formdef = create_formdef() |
|
4923 |
formdef.data_class().wipe() |
|
4924 | ||
4925 |
NamedDataSource.wipe() |
|
4926 |
data_source = NamedDataSource(name='foobar') |
|
4927 |
data_source.data_source = {'type': 'jsonp', 'value': 'http://remote.example.net/jsonp'} |
|
4928 |
data_source.store() |
|
4929 | ||
4930 |
formdef.fields = [ |
|
4931 |
fields.ItemField(id='0', label='string', type='item', |
|
4932 |
data_source={'type': 'foobar'}, |
|
4933 |
display_mode='autocomplete', |
|
4934 |
), |
|
4935 |
] |
|
4936 |
formdef.store() |
|
4937 | ||
4938 |
app = get_app(pub) |
|
4939 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4940 |
resp = app.get('/test/') |
|
4941 |
assert urlopen.call_count == 0 |
|
4942 |
pq = resp.pyquery.remove_namespaces() |
|
4943 |
select2_url = pq('select').attr['data-select2-url'] |
|
4944 |
assert select2_url == 'http://remote.example.net/jsonp' |
|
4945 | ||
4946 |
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget |
|
4947 |
resp.form.fields['f0_display'] = [Hidden(form=resp.form, tag='input', name='f0_display', pos=10)] |
|
4948 |
resp.form.field_order.append(('f0_display', resp.form.fields['f0_display'][0])) |
|
4949 |
resp.form['f0'].force_value('1') |
|
4950 |
resp.form['f0_display'].force_value('hello') |
|
4951 | ||
4952 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4953 |
resp = resp.form.submit('submit') # -> validation page |
|
4954 |
assert urlopen.call_count == 0 |
|
4955 |
assert resp.form['f0'].value == '1' |
|
4956 |
assert resp.form['f0_label'].value == 'hello' |
|
4957 | ||
4958 |
with mock.patch('qommon.misc.urlopen') as urlopen: |
|
4959 |
resp = resp.form.submit('submit') # -> submit |
|
4960 |
assert urlopen.call_count == 0 |
|
4961 |
assert formdef.data_class().select()[0].data['0'] == '1' |
|
4962 |
assert formdef.data_class().select()[0].data['0_display'] == 'hello' |
|
4963 |
# no _structured data for pure jsonp sources |
|
4964 |
assert '0_structured' not in formdef.data_class().select()[0].data |
|
4965 | ||
4788 | 4966 |
def test_form_data_keywords(pub): |
4789 | 4967 |
formdef = create_formdef() |
4790 | 4968 |
formdef.keywords = 'hello,world' |
wcs/admin/data_sources.py | ||
---|---|---|
56 | 56 |
'data-dynamic-display-child-of': 'data_source$type', |
57 | 57 |
'data-dynamic-display-value': _('JSON URL'), |
58 | 58 |
}) |
59 |
form.add(StringWidget, 'query_parameter', |
|
60 |
value=self.datasource.query_parameter, |
|
61 |
title=_('Query Parameter'), |
|
62 |
hint=_('Name of the parameter to use for querying source (typically, q)'), |
|
63 |
required=False, |
|
64 |
advanced=False, |
|
65 |
attrs={ |
|
66 |
'data-dynamic-display-child-of': 'data_source$type', |
|
67 |
'data-dynamic-display-value': _('JSON URL'), |
|
68 |
}) |
|
69 |
form.add(StringWidget, 'id_parameter', |
|
70 |
value=self.datasource.id_parameter, |
|
71 |
title=_('Id Parameter'), |
|
72 |
hint=_('Name of the parameter to use to get a given entry from data source (typically, id)'), |
|
73 |
required=False, |
|
74 |
advanced=False, |
|
75 |
attrs={ |
|
76 |
'data-dynamic-display-child-of': 'data_source$type', |
|
77 |
'data-dynamic-display-value': _('JSON URL'), |
|
78 |
}) |
|
59 | 79 |
if self.datasource.slug: |
60 | 80 |
form.add(StringWidget, 'slug', |
61 | 81 |
value=self.datasource.slug, |
... | ... | |
88 | 108 |
self.datasource.description = form.get_widget('description').parse() |
89 | 109 |
self.datasource.data_source = form.get_widget('data_source') |
90 | 110 |
self.datasource.cache_duration = form.get_widget('cache_duration').parse() |
111 |
self.datasource.query_parameter = form.get_widget('query_parameter').parse() |
|
112 |
self.datasource.id_parameter = form.get_widget('id_parameter').parse() |
|
91 | 113 |
if self.datasource.slug: |
92 | 114 |
self.datasource.slug = slug |
93 | 115 |
self.datasource.store() |
wcs/api.py | ||
---|---|---|
19 | 19 |
import time |
20 | 20 |
import sys |
21 | 21 | |
22 |
from quixote import get_request, get_publisher, get_response |
|
22 |
from quixote import get_request, get_publisher, get_response, get_session
|
|
23 | 23 |
from quixote.directory import Directory |
24 | 24 | |
25 |
from django.utils.six.moves.urllib import parse as urllib |
|
26 | ||
25 | 27 |
from qommon import _ |
26 | 28 |
from qommon import misc |
27 | 29 |
from qommon.evalutils import make_datetime |
... | ... | |
31 | 33 | |
32 | 34 |
from wcs.categories import Category |
33 | 35 |
from wcs.conditions import Condition, ValidationError |
36 |
from wcs.data_sources import NamedDataSource |
|
34 | 37 |
from wcs.formdef import FormDef |
35 | 38 |
from wcs.roles import Role, logged_users_role |
36 | 39 |
from wcs.forms.common import FormStatusPage |
37 | 40 |
import wcs.qommon.storage as st |
38 |
from wcs.api_utils import is_url_signed, get_user_from_api_query_string |
|
41 |
from wcs.api_utils import MissingSecret, sign_url, get_secret_and_orig, is_url_signed, get_user_from_api_query_string
|
|
39 | 42 | |
40 | 43 |
from backoffice.management import FormPage as BackofficeFormPage |
41 | 44 |
from backoffice.management import ManagementDirectory |
... | ... | |
741 | 744 |
return json.dumps(data) |
742 | 745 | |
743 | 746 | |
747 |
class AutocompleteDirectory(Directory): |
|
748 |
def _q_lookup(self, component): |
|
749 |
url = get_session().get_data_source_query_url_from_token(component) |
|
750 |
if not url: |
|
751 |
raise AccessForbiddenError() |
|
752 |
url += urllib.quote(get_request().form['q']) |
|
753 |
unsigned_url = url |
|
754 |
try: |
|
755 |
signature_key, orig = get_secret_and_orig(url) |
|
756 |
except MissingSecret: |
|
757 |
pass |
|
758 |
else: |
|
759 |
url += '&orig=%s' % orig |
|
760 |
url = sign_url(url, signature_key) |
|
761 |
get_response().set_content_type('application/json') |
|
762 |
return misc.urlopen(url).read() |
|
763 | ||
764 | ||
744 | 765 |
class ApiDirectory(Directory): |
745 | 766 |
_q_exports = ['forms', 'roles', ('reverse-geocoding', 'reverse_geocoding'), |
746 |
'formdefs', 'categories', 'user', 'users', 'code', |
|
767 |
'formdefs', 'categories', 'user', 'users', 'code', 'autocomplete',
|
|
747 | 768 |
('validate-expression', 'validate_expression'), |
748 | 769 |
('validate-condition', 'validate_condition')] |
749 | 770 | |
... | ... | |
753 | 774 |
user = ApiUserDirectory() |
754 | 775 |
users = ApiUsersDirectory() |
755 | 776 |
code = ApiTrackingCodeDirectory() |
777 |
autocomplete = AutocompleteDirectory() |
|
756 | 778 | |
757 | 779 |
def reverse_geocoding(self): |
758 | 780 |
get_response().set_content_type('application/json') |
wcs/data_sources.py | ||
---|---|---|
21 | 21 |
from django.utils.six.moves.urllib import parse as urllib |
22 | 22 |
from django.utils.six.moves.urllib import parse as urlparse |
23 | 23 | |
24 |
from quixote import get_publisher, get_request |
|
24 |
from quixote import get_publisher, get_request, get_session
|
|
25 | 25 |
from quixote.html import TemplateIO |
26 | 26 | |
27 | 27 |
from qommon import _ |
... | ... | |
226 | 226 |
return NamedDataSource.get_by_slug(ds_type).data_source |
227 | 227 | |
228 | 228 | |
229 |
def get_object(data_source): |
|
230 |
if not data_source: |
|
231 |
return None |
|
232 |
ds_type = data_source.get('type') |
|
233 |
if ds_type in ('json', 'jsonp', 'formula'): |
|
234 |
named_data_source = NamedDataSource() |
|
235 |
named_data_source.data_source = data_source |
|
236 |
return named_data_source |
|
237 |
return NamedDataSource.get_by_slug(ds_type) |
|
238 | ||
239 | ||
229 | 240 |
class NamedDataSource(XmlStorableObject): |
230 | 241 |
_names = 'datasources' |
231 | 242 |
_xml_tagname = 'datasources' |
... | ... | |
235 | 246 |
description = None |
236 | 247 |
data_source = None |
237 | 248 |
cache_duration = None |
249 |
query_parameter = None |
|
250 |
id_parameter = None |
|
238 | 251 | |
239 | 252 |
# declarations for serialization |
240 | 253 |
XML_NODES = [('name', 'str'), ('slug', 'str'), ('description', 'str'), |
241 | 254 |
('cache_duration', 'str'), |
255 |
('query_parameter', 'str'), |
|
256 |
('id_parameter', 'str'), |
|
242 | 257 |
('data_source', 'data_source'), |
243 | 258 |
] |
244 | 259 | |
... | ... | |
246 | 261 |
StorableObject.__init__(self) |
247 | 262 |
self.name = name |
248 | 263 | |
264 |
@property |
|
265 |
def type(self): |
|
266 |
return self.data_source.get('type') |
|
267 | ||
268 |
def can_jsonp(self): |
|
269 |
if self.type == 'jsonp': |
|
270 |
return True |
|
271 |
if self.type == 'json' and self.query_parameter: |
|
272 |
return True |
|
273 |
return False |
|
274 | ||
249 | 275 |
def migrate(self): |
250 | 276 |
changed = False |
251 | 277 | |
... | ... | |
294 | 320 |
return objects[0] |
295 | 321 |
raise KeyError("data source '%s' does not exist" % slug) |
296 | 322 | |
323 |
def get_json_query_url(self): |
|
324 |
url = self.data_source.get('value').strip() |
|
325 |
if Template.is_template_string(url): |
|
326 |
vars = get_publisher().substitutions.get_context_variables(mode='lazy') |
|
327 |
url = get_variadic_url(url, vars) |
|
328 |
if not '?' in url: |
|
329 |
url += '?' + self.query_parameter + '=' |
|
330 |
else: |
|
331 |
url += '&' + self.query_parameter + '=' |
|
332 |
return url |
|
333 | ||
334 |
def get_jsonp_url(self): |
|
335 |
if self.type == 'jsonp': |
|
336 |
return self.data_source.get('value') |
|
337 |
if self.type == 'json' and self.query_parameter: |
|
338 |
return '/api/autocomplete/%s' % ( |
|
339 |
get_session().get_data_source_query_url_token(self.get_json_query_url())) |
|
340 |
return None |
|
341 | ||
342 |
def load_json(self, param_name, param_value): |
|
343 |
url = self.data_source.get('value').strip() |
|
344 |
if Template.is_template_string(url): |
|
345 |
vars = get_publisher().substitutions.get_context_variables(mode='lazy') |
|
346 |
url = get_variadic_url(url, vars) |
|
347 | ||
348 |
if not '?' in url: |
|
349 |
url += '?' |
|
350 |
else: |
|
351 |
url += '&' |
|
352 |
url += param_name + '=' + urllib.quote(param_value) |
|
353 | ||
354 |
request = get_request() |
|
355 |
if hasattr(request, 'datasources_cache') and url in request.datasources_cache: |
|
356 |
return request.datasources_cache[url] |
|
357 | ||
358 |
unsigned_url = url |
|
359 |
try: |
|
360 |
signature_key, orig = get_secret_and_orig(url) |
|
361 |
except MissingSecret: |
|
362 |
pass |
|
363 |
else: |
|
364 |
url += '&orig=%s' % orig |
|
365 |
url = sign_url(url, signature_key) |
|
366 |
resp = qommon.misc.urlopen(url).read() |
|
367 |
if hasattr(request, 'datasources_cache'): |
|
368 |
request.datasources_cache[unsigned_url] = resp |
|
369 |
return resp |
|
370 | ||
371 |
def get_display_value(self, option_id): |
|
372 |
value = self.get_structured_value(option_id) |
|
373 |
if value: |
|
374 |
return value.get('text') |
|
375 |
return None |
|
376 | ||
377 |
def get_structured_value(self, option_id): |
|
378 |
value = None |
|
379 |
if self.type == 'json' and self.id_parameter: |
|
380 |
resp = self.load_json(self.id_parameter, option_id) |
|
381 |
response = qommon.misc.json_loads(resp) |
|
382 |
if response['data']: |
|
383 |
value = response['data'][0] |
|
384 |
else: |
|
385 |
for item in get_structured_items(self.data_source, mode='lazy'): |
|
386 |
if str(item['id']) == str(option_id): |
|
387 |
value = item |
|
388 |
break |
|
389 |
if value is None: |
|
390 |
return None |
|
391 |
return value |
|
392 | ||
297 | 393 |
@classmethod |
298 | 394 |
def get_substitution_variables(cls): |
299 | 395 |
return {'data_source': DataSourcesSubstitutionProxy()} |
wcs/fields.py | ||
---|---|---|
1224 | 1224 |
return [(x, x) for x in self.items] |
1225 | 1225 |
return [] |
1226 | 1226 | |
1227 |
def perform_more_widget_changes(self, form, kwargs, edit = True): |
|
1228 |
real_data_source = data_sources.get_real(self.data_source) |
|
1229 |
if real_data_source and real_data_source.get('type') == 'jsonp': |
|
1230 |
kwargs['url'] = real_data_source.get('value') |
|
1227 |
def perform_more_widget_changes(self, form, kwargs, edit=True): |
|
1228 |
data_source = data_sources.get_object(self.data_source) |
|
1229 | ||
1230 |
if data_source and data_source.type == 'jsonp': |
|
1231 |
# a source defined as JSONP can only be used in autocomplete mode |
|
1232 |
self.display_mode = 'autocomplete' |
|
1233 | ||
1234 |
if self.display_mode == 'autocomplete' and data_source and data_source.can_jsonp(): |
|
1235 |
self.url = kwargs['url'] = data_source.get_jsonp_url() |
|
1231 | 1236 |
self.widget_class = JsonpSingleSelectWidget |
1232 |
elif self.items: |
|
1237 |
return |
|
1238 | ||
1239 |
if self.items: |
|
1233 | 1240 |
kwargs['options'] = self.get_options() |
1234 | 1241 |
elif self.data_source: |
1235 | 1242 |
items = data_sources.get_items(self.data_source, |
... | ... | |
1254 | 1261 |
kwargs['select2'] = True |
1255 | 1262 | |
1256 | 1263 |
def get_display_value(self, value): |
1257 |
real_value = value |
|
1258 |
label_value = str(value or '') |
|
1259 |
kwargs = {} |
|
1260 |
self.perform_more_widget_changes(None, kwargs, False) |
|
1261 |
real_data_source = data_sources.get_real(self.data_source) |
|
1262 |
if real_data_source and real_data_source.get('type') == 'jsonp': |
|
1264 |
data_source = data_sources.get_object(self.data_source) |
|
1265 |
if data_source is None: |
|
1266 |
return value or '' |
|
1267 | ||
1268 |
if data_source.type == 'jsonp': |
|
1263 | 1269 |
if not get_session().jsonp_display_values: |
1264 | 1270 |
get_session().jsonp_display_values = {} |
1265 |
label_value = get_session().jsonp_display_values.get( |
|
1266 |
'%s_%s' % (real_data_source.get('value'), value)) |
|
1267 |
elif type(kwargs['options'][0]) in (tuple, list): |
|
1268 |
if len(kwargs['options'][0]) == 2: |
|
1269 |
for key, value in kwargs['options']: |
|
1270 |
if str(key) == str(real_value): |
|
1271 |
label_value = value |
|
1272 |
break |
|
1273 |
elif len(kwargs['options'][0]) == 3: |
|
1274 |
for key, value, key2 in kwargs['options']: |
|
1275 |
if str(key) == str(real_value): |
|
1276 |
label_value = value |
|
1277 |
break |
|
1278 |
return label_value |
|
1271 |
return get_session().jsonp_display_values.get( |
|
1272 |
'%s_%s' % (data_source.get_jsonp_url(), value)) |
|
1273 | ||
1274 |
return data_source.get_display_value(value) |
|
1279 | 1275 | |
1280 | 1276 |
def add_to_view_form(self, form, value = None): |
1281 | 1277 |
real_value = value |
... | ... | |
1295 | 1291 | |
1296 | 1292 |
def store_display_value(self, data, field_id): |
1297 | 1293 |
value = data.get(field_id) |
1298 |
kwargs = {} |
|
1299 |
self.perform_more_widget_changes(None, kwargs, False) |
|
1300 |
real_data_source = data_sources.get_real(self.data_source) |
|
1301 |
if real_data_source and real_data_source.get('type') == 'jsonp': |
|
1294 |
data_source = data_sources.get_object(self.data_source) |
|
1295 |
if data_source and data_source.type == 'jsonp': |
|
1302 | 1296 |
if get_request(): |
1303 | 1297 |
display_value = get_request().form.get('f%s_display' % field_id) |
1298 |
real_data_source = data_source.data_source |
|
1304 | 1299 |
if display_value is None: |
1305 | 1300 |
if not get_session().jsonp_display_values: |
1306 | 1301 |
get_session().jsonp_display_values = {} |
... | ... | |
1312 | 1307 |
get_session().jsonp_display_values[ |
1313 | 1308 |
'%s_%s' % (real_data_source.get('value'), value)] = display_value |
1314 | 1309 |
return display_value |
1315 |
elif type(kwargs['options'][0]) in (tuple, list): |
|
1316 |
if len(kwargs['options'][0]) == 2: |
|
1317 |
for key, option_value in kwargs['options']: |
|
1318 |
if str(key) == str(value): |
|
1319 |
return option_value |
|
1320 |
elif len(kwargs['options'][0]) == 3: |
|
1321 |
for key, option_value, key_repeat in kwargs['options']: |
|
1322 |
if str(key) == str(value): |
|
1323 |
return option_value |
|
1324 |
return str(value) |
|
1310 |
return self.get_display_value(value) |
|
1325 | 1311 | |
1326 | 1312 |
def store_structured_value(self, data, field_id): |
1327 |
value = data.get(field_id)
|
|
1328 |
if not self.data_source:
|
|
1313 |
data_source = data_sources.get_object(self.data_source)
|
|
1314 |
if data_source is None:
|
|
1329 | 1315 |
return |
1330 |
structured_options = data_sources.get_structured_items(self.data_source) |
|
1331 |
if not structured_options:
|
|
1316 | ||
1317 |
if data_source.type == 'jsonp':
|
|
1332 | 1318 |
return |
1333 |
if not set(structured_options[0].keys()) != set(['id', 'text']): |
|
1319 | ||
1320 |
value = data_source.get_structured_value(data.get(field_id)) |
|
1321 |
if value is None or set(value.keys()) == set(['id', 'text']): |
|
1334 | 1322 |
return |
1335 |
for structured_option in structured_options: |
|
1336 |
if str(structured_option.get('id')) == str(value): |
|
1337 |
return structured_option |
|
1338 |
return None |
|
1323 |
return value |
|
1339 | 1324 | |
1340 | 1325 |
def fill_admin_form(self, form): |
1341 | 1326 |
WidgetField.fill_admin_form(self, form) |
wcs/forms/root.py | ||
---|---|---|
766 | 766 |
return self.removedraft() |
767 | 767 | |
768 | 768 |
form_data = session.get_by_magictoken(magictoken, {}) |
769 |
data = self.formdef.get_data(form) |
|
769 |
with get_publisher().substitutions.temporary_feed( |
|
770 |
transient_formdata, force_mode='lazy'): |
|
771 |
data = self.formdef.get_data(form) |
|
770 | 772 |
form_data.update(data) |
771 | 773 | |
772 | 774 |
if self.has_draft_support() and form.get_submit() == 'savedraft': |
... | ... | |
809 | 811 |
page_error_messages=page_error_messages) |
810 | 812 | |
811 | 813 |
form_data = session.get_by_magictoken(magictoken, {}) |
812 |
data = self.formdef.get_data(form) |
|
814 |
with get_publisher().substitutions.temporary_feed( |
|
815 |
transient_formdata, force_mode='lazy'): |
|
816 |
data = self.formdef.get_data(form) |
|
813 | 817 |
form_data.update(data) |
814 | 818 | |
815 | 819 |
session.add_magictoken(magictoken, form_data) |
... | ... | |
1092 | 1096 | |
1093 | 1097 |
for field in displayed_fields: |
1094 | 1098 |
if field.key == 'item' and field.data_source: |
1095 |
real_data_source = data_sources.get_real(field.data_source)
|
|
1096 |
if real_data_source.get('type') != 'json':
|
|
1099 |
data_source = data_sources.get_object(field.data_source)
|
|
1100 |
if data_source.type != 'json':
|
|
1097 | 1101 |
continue |
1098 | 1102 |
varnames = re.findall(r'\bform[_\.]var[_\.]([a-zA-Z0-9_]+?)(?:_raw|\b)', |
1099 |
real_data_source.get('value')) |
|
1103 |
data_source.data_source.get('value')) |
|
1104 |
if (modified_field_varname is None or modified_field_varname in varnames) and ( |
|
1105 |
field.display_mode == 'autocomplete' and data_source.query_parameter): |
|
1106 |
# computed earlier, in perform_more_widget_changes, when the field |
|
1107 |
# was added to the form |
|
1108 |
result[field.id]['source_url'] = field.url |
|
1100 | 1109 |
if modified_field_varname in varnames: |
1101 | 1110 |
result[field.id]['items'] = [ |
1102 | 1111 |
{'id': x[2], 'text': x[1]} for x in field.get_options(mode='lazy')] |
wcs/qommon/static/js/qommon.forms.js | ||
---|---|---|
115 | 115 |
var $widget = $('[data-field-id="' + key + '"]'); |
116 | 116 |
$widget.html(value.content); |
117 | 117 |
} |
118 |
if (value.source_url) { |
|
119 |
// json change of URL |
|
120 |
$widget.find('[data-select2-url]').data('select2-url', value.source_url); |
|
121 |
} |
|
118 | 122 |
}); |
119 | 123 |
} |
120 | 124 |
}); |
wcs/sessions.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import random |
|
18 | 17 |
import time |
18 |
import uuid |
|
19 | 19 | |
20 | 20 |
import qommon.sessions |
21 | 21 |
from qommon.sessions import Session |
... | ... | |
25 | 25 |
magictokens = None |
26 | 26 |
anonymous_formdata_keys = None |
27 | 27 |
visiting_objects = None |
28 |
data_source_query_url_tokens = None |
|
28 | 29 | |
29 | 30 |
def has_info(self): |
30 | 31 |
return (self.anonymous_formdata_keys or |
31 |
self.magictokens or self.visiting_objects or Session.has_info(self)) |
|
32 |
self.magictokens or |
|
33 |
self.visiting_objects or |
|
34 |
self.data_source_query_url_tokens or |
|
35 |
Session.has_info(self)) |
|
32 | 36 |
is_dirty = has_info |
33 | 37 | |
34 | 38 |
def add_magictoken(self, token, data): |
... | ... | |
120 | 124 |
del session.visiting_objects[object_key] |
121 | 125 |
session.store() |
122 | 126 | |
127 |
def get_data_source_query_url_token(self, url): |
|
128 |
if not self.data_source_query_url_tokens: |
|
129 |
self.data_source_query_url_tokens = {} |
|
130 |
for key, value in self.data_source_query_url_tokens.items(): |
|
131 |
if value == url: |
|
132 |
return key |
|
133 |
key = str(uuid.uuid4()) |
|
134 |
self.data_source_query_url_tokens[key] = url |
|
135 |
self.store() |
|
136 |
return key |
|
137 | ||
138 |
def get_data_source_query_url_from_token(self, token): |
|
139 |
if not self.data_source_query_url_tokens: |
|
140 |
return None |
|
141 |
return self.data_source_query_url_tokens.get(token) |
|
142 | ||
123 | 143 |
qommon.sessions.BasicSession = BasicSession |
124 | 144 |
StorageSessionManager = qommon.sessions.StorageSessionManager |
125 |
- |