0003-fields-use-new-map-marker-selection-widget-in-front-.patch
tests/form_pages/test_all.py | ||
---|---|---|
9 | 9 |
import time |
10 | 10 |
import zipfile |
11 | 11 |
import base64 |
12 |
from webtest import Upload, Hidden |
|
12 |
from webtest import Upload, Hidden, Radio
|
|
13 | 13 |
import mock |
14 | 14 |
import xml.etree.ElementTree as ET |
15 | 15 | |
... | ... | |
8957 | 8957 |
'1_structured': {'id': '1', 'text': 'un', 'more': 'foo'}, |
8958 | 8958 |
} |
8959 | 8959 |
assert '2020-04-18' in formdata.evolution[0].parts[0].content |
8960 | ||
8961 | ||
8962 |
def test_form_item_map_data_source(pub, http_requests): |
|
8963 |
NamedDataSource.wipe() |
|
8964 |
data_source = NamedDataSource(name='foobar') |
|
8965 |
data_source.data_source = { |
|
8966 |
'type': 'geojson', |
|
8967 |
'value': 'http://remote.example.net/geojson', |
|
8968 |
} |
|
8969 |
data_source.id_property = 'id' |
|
8970 |
data_source.label_template_property = '{{ text }}' |
|
8971 |
data_source.cache_duration = '5' |
|
8972 |
data_source.store() |
|
8973 | ||
8974 |
formdef = create_formdef() |
|
8975 |
formdef.fields = [ |
|
8976 |
fields.ItemField(id='1', label='map', display_mode='map', data_source={'type': 'foobar'}), |
|
8977 |
] |
|
8978 |
formdef.store() |
|
8979 |
formdef.data_class().wipe() |
|
8980 |
app = get_app(pub) |
|
8981 |
resp = app.get('/test/') |
|
8982 |
assert resp.pyquery('div[data-markers-radio-name]')[0].attrib['data-markers-url'] == '/api/geojson/foobar' |
|
8983 |
assert resp.pyquery('div[data-markers-radio-name]')[0].attrib['data-markers-radio-name'] == 'f1$marker_id' |
|
8984 |
app.get('/api/geojson/wrong-foobar', status=404) |
|
8985 |
resp_geojson = app.get('/api/geojson/foobar') |
|
8986 |
assert len(resp_geojson.json['features']) == 2 |
|
8987 |
assert http_requests.count() == 1 |
|
8988 |
assert http_requests.get_last('url') == 'http://remote.example.net/geojson' |
|
8989 |
resp_geojson = app.get('/api/geojson/foobar') |
|
8990 |
assert http_requests.count() == 1 # cache was used |
|
8991 |
assert len(resp_geojson.json['features']) == 2 |
|
8992 |
# simulate qommon.map.js that will create radio inputs |
|
8993 |
resp.form.fields['f1$marker_id'] = [Radio(form=resp.form, tag='input', name='f1$marker_id', pos=5)] |
|
8994 |
resp.form.fields['f1$marker_id'][0].options.append(('1', False, None)) |
|
8995 |
resp.form.fields['f1$marker_id'][0].options.append(('2', False, None)) |
|
8996 |
resp.form.fields['f1$marker_id'][0].optionPositions.append(5) |
|
8997 |
resp.form.fields['f1$marker_id'][0].optionPositions.append(6) |
|
8998 |
resp.form.field_order.append(('f1$marker_id', resp.form.fields['f1$marker_id'][0])) |
|
8999 |
resp.form['f1$marker_id'].value = '1' # click on marker |
|
9000 |
resp.form['f1$latlng'] = '1;2' # set via js |
|
9001 |
resp = resp.form.submit('submit') |
|
9002 |
assert 'Check values then click submit.' in resp |
|
9003 |
# selected option is displayed as readonly: |
|
9004 |
assert resp.pyquery('input[type=text][value=foo][readonly]') |
|
9005 |
resp = resp.form.submit('submit') |
|
9006 |
resp = resp.follow() |
|
9007 |
assert 'The form has been recorded' in resp |
|
9008 |
assert '<div class="value">foo</div>' in resp |
|
9009 |
assert formdef.data_class().count() == 1 |
|
9010 |
data_id = formdef.data_class().select()[0].id |
|
9011 |
formdata = formdef.data_class().get(data_id) |
|
9012 |
assert formdata.data['1_structured']['geometry']['coordinates'] == [1, 2] |
tests/utilities.py | ||
---|---|---|
1 | 1 |
import email.header |
2 | 2 |
import email.parser |
3 |
import json |
|
3 | 4 |
import os |
4 | 5 |
import tempfile |
5 | 6 |
import random |
... | ... | |
326 | 327 | |
327 | 328 |
with open(os.path.join(os.path.dirname(__file__), 'idp_metadata.xml')) as fd: |
328 | 329 |
metadata = fd.read() |
330 |
geojson = { |
|
331 |
'features': [ |
|
332 |
{'properties': {'id': '1', 'text': 'foo'}, |
|
333 |
'geometry': {'type': 'Point', 'coordinates': [1, 2]} |
|
334 |
}, |
|
335 |
{'properties': {'id': '2', 'text': 'bar'}, |
|
336 |
'geometry': {'type': 'Point', 'coordinates': [3, 4]} |
|
337 |
} |
|
338 |
] |
|
339 |
} |
|
329 | 340 |
status, data, headers = { |
330 | 341 |
'http://remote.example.net/204': (204, None, None), |
331 | 342 |
'http://remote.example.net/400': (400, 'bad request', None), |
... | ... | |
336 | 347 |
'http://remote.example.net/json': (200, '{"foo": "bar"}', None), |
337 | 348 |
'http://remote.example.net/json-list': (200, '{"data": [{"id": "a", "text": "b"}]}', None), |
338 | 349 |
'http://remote.example.net/json-list-extra': (200, '{"data": [{"id": "a", "text": "b", "foo": "bar"}]}', None), |
350 |
'http://remote.example.net/geojson': (200, json.dumps(geojson), None), |
|
339 | 351 |
'http://remote.example.net/json-err0': (200, '{"data": "foo", "err": 0}', None), |
340 | 352 |
'http://remote.example.net/json-err1': (200, '{"data": "", "err": 1}', None), |
341 | 353 |
'http://remote.example.net/json-list-err1': (200, '{"data": [{"id": "a", "text": "b"}], "err": 1}', None), |
wcs/fields.py | ||
---|---|---|
1498 | 1498 |
if getattr(element.find('show_as_radio'), 'text', None) == 'True': |
1499 | 1499 |
self.display_mode = 'radio' |
1500 | 1500 | |
1501 |
@property |
|
1502 |
def extra_attributes(self): |
|
1503 |
if self.display_mode == 'map': |
|
1504 |
return ['initial_zoom', 'min_zoom', 'max_zoom', 'data_source'] |
|
1505 |
return [] |
|
1506 | ||
1501 | 1507 |
def get_options(self, mode=None): |
1502 | 1508 |
if self.data_source: |
1503 | 1509 |
return [x[:3] for x in data_sources.get_items(self.data_source, mode=mode)] |
... | ... | |
1531 | 1537 |
self.widget_class = JsonpSingleSelectWidget |
1532 | 1538 |
return |
1533 | 1539 | |
1534 |
if self.data_source: |
|
1535 |
items = data_sources.get_items(self.data_source, |
|
1536 |
include_disabled=self.display_disabled_items) |
|
1537 |
kwargs['options'] = [x[:3] for x in items if not x[-1].get('disabled')] |
|
1538 |
kwargs['options_with_attributes'] = items[:] |
|
1539 |
else: |
|
1540 |
kwargs['options'] = self.get_options() |
|
1541 |
if not kwargs.get('options'): |
|
1542 |
kwargs['options'] = [(None, '---')] |
|
1540 |
if self.display_mode != 'map': |
|
1541 |
if self.data_source: |
|
1542 |
items = data_sources.get_items(self.data_source, |
|
1543 |
include_disabled=self.display_disabled_items) |
|
1544 |
kwargs['options'] = [x[:3] for x in items if not x[-1].get('disabled')] |
|
1545 |
kwargs['options_with_attributes'] = items[:] |
|
1546 |
else: |
|
1547 |
kwargs['options'] = self.get_options() |
|
1548 |
if not kwargs.get('options'): |
|
1549 |
kwargs['options'] = [(None, '---')] |
|
1543 | 1550 |
if display_mode == 'radio': |
1544 | 1551 |
self.widget_class = RadiobuttonsWidget |
1545 | 1552 |
if type(kwargs['options'][0]) is str: |
... | ... | |
1552 | 1559 |
kwargs['delim'] = htmltext('<br />') |
1553 | 1560 |
elif display_mode == 'autocomplete': |
1554 | 1561 |
kwargs['select2'] = True |
1562 |
elif display_mode == 'map': |
|
1563 |
self.widget_class = MapMarkerSelectionWidget |
|
1555 | 1564 | |
1556 | 1565 |
def get_display_value(self, value): |
1557 | 1566 |
data_source = data_sources.get_object(self.data_source) |
wcs/qommon/form.py | ||
---|---|---|
2470 | 2470 |
self.value = '%s;%s' % (lat_lon['lat'], lat_lon['lon']) |
2471 | 2471 | |
2472 | 2472 | |
2473 |
class MapMarkerSelectionWidget(MapWidget): |
|
2474 |
template_name = 'qommon/forms/widgets/map-marker-selection.html' |
|
2475 | ||
2476 |
def __init__(self, name, value=None, **kwargs): |
|
2477 |
CompositeWidget.__init__(self, name, value, **kwargs) |
|
2478 |
self.add(HiddenWidget, 'marker_id', value=value) |
|
2479 |
self.readonly = kwargs.pop('readonly', False) |
|
2480 | ||
2481 |
self.map_attributes = {} |
|
2482 |
self.map_attributes.update(get_publisher().get_map_attributes()) |
|
2483 |
self.sync_map_and_address_fields = get_publisher().has_site_option('sync-map-and-address-fields') |
|
2484 |
for attribute in ('initial_zoom', 'min_zoom', 'max_zoom', 'init_with_geoloc'): |
|
2485 |
if attribute in kwargs: |
|
2486 |
self.map_attributes['data-' + attribute] = kwargs.pop(attribute) |
|
2487 | ||
2488 |
from wcs import data_sources |
|
2489 |
data_source = data_sources.get_object(kwargs['data_source']) |
|
2490 |
self.geojson_markers_url = data_source.get_geojson_url() |
|
2491 | ||
2492 |
def initial_position(self): |
|
2493 |
return None |
|
2494 | ||
2495 |
def _parse(self, request): |
|
2496 |
CompositeWidget._parse(self, request) |
|
2497 |
self.value = self.get('marker_id') |
|
2498 | ||
2499 | ||
2473 | 2500 |
class HiddenErrorWidget(HiddenWidget): |
2474 | 2501 |
def set_error(self, error): |
2475 | 2502 |
Widget.set_error(self, error) |
wcs/qommon/static/js/qommon.map.js | ||
---|---|---|
46 | 46 |
var hidden = $(this).prev(); |
47 | 47 |
map.marker = null; |
48 | 48 |
var latlng; |
49 |
var initial_marker_id = $map_widget.data('markers-initial-id'); |
|
49 | 50 |
if ($map_widget.data('init-lat')) { |
50 | 51 |
latlng = [$map_widget.data('init-lat'), $map_widget.data('init-lng')] |
51 |
map.marker = L.marker(latlng); |
|
52 |
map.marker.addTo(map); |
|
52 |
if (typeof initial_marker_id == 'undefined') { |
|
53 |
// if markers are used they will appear via their input widget |
|
54 |
map.marker = L.marker(latlng); |
|
55 |
map.marker.addTo(map); |
|
56 |
} |
|
53 | 57 |
} else if ($map_widget.data('def-lat')) { |
54 | 58 |
latlng = [$map_widget.data('def-lat'), $map_widget.data('def-lng')] |
55 | 59 |
} else { |
... | ... | |
67 | 71 |
} else { |
68 | 72 |
map.addControl(gps_control); |
69 | 73 |
} |
70 |
if (! $map_widget.data('readonly')) { |
|
74 |
if ($map_widget.data('markers-url')) { |
|
75 |
var radio_name = $map_widget.data('markers-radio-name'); |
|
76 |
$map_widget.on('change', 'input[name="' + radio_name + '"]', function() { |
|
77 |
var $radio = $(this); |
|
78 |
if ($radio.is(':checked')) { |
|
79 |
hidden.val($radio.data('lat') + ';' + $radio.data('lng')); |
|
80 |
hidden.trigger('change'); |
|
81 |
} |
|
82 |
}); |
|
83 |
$.getJSON($map_widget.data('markers-url')).done( |
|
84 |
function(data) { |
|
85 |
var geo_json = L.geoJson(data, { |
|
86 |
pointToLayer: function (feature, latlng) { |
|
87 |
var $label = $('<label>', { |
|
88 |
title: feature.properties._text |
|
89 |
}); |
|
90 |
var $radio = $('<input>', { |
|
91 |
value: feature.properties._id, |
|
92 |
name: radio_name, |
|
93 |
type: 'radio', |
|
94 |
'data-lat': latlng.lat, |
|
95 |
'data-lng': latlng.lng |
|
96 |
}); |
|
97 |
if (typeof initial_marker_id !== 'undefined' && feature.properties._id == initial_marker_id) { |
|
98 |
$radio.attr('checked', 'checked'); |
|
99 |
} |
|
100 |
$label.append($radio); |
|
101 |
$label.append($('<span></span>')); |
|
102 |
var div_marker = L.divIcon({ |
|
103 |
className: 'item-marker', |
|
104 |
html: '<div>' + $label.prop('outerHTML') + '</div>' |
|
105 |
}); |
|
106 |
return L.marker(latlng, {icon: div_marker}); |
|
107 |
} |
|
108 |
}); |
|
109 |
map.fitBounds(geo_json.getBounds()); |
|
110 |
geo_json.addTo(map); |
|
111 |
} |
|
112 |
); |
|
113 |
} else if ($map_widget.data('readonly') && initial_marker_id) { |
|
114 |
// readonly and marker |
|
115 |
map.marker = L.marker(latlng); |
|
116 |
map.marker.addTo(map); |
|
117 |
} |
|
118 | ||
119 |
if (! $map_widget.data('readonly') && ! $map_widget.data('markers-url')) { |
|
71 | 120 |
map.on('click', function(e) { |
72 | 121 |
$map_widget.trigger('set-geolocation', e.latlng); |
73 | 122 |
}); |
wcs/qommon/templates/qommon/forms/widgets/map-marker-selection.html | ||
---|---|---|
1 |
{% extends "qommon/forms/widgets/map.html" %} |
|
2 | ||
3 |
{% block widget-control-attributes %} |
|
4 |
{% if not widget.readonly %} |
|
5 |
data-markers-url="{{ widget.geojson_markers_url }}" |
|
6 |
data-markers-radio-name="{{widget.name}}$marker_id" |
|
7 |
{% endif %} |
|
8 |
{% if widget.value %} |
|
9 |
data-markers-initial-id="{{ widget.value }}" |
|
10 |
{% endif %} |
|
11 |
{% endblock %} |
|
12 | ||
13 |
{% block widget-control %} |
|
14 |
{{ block.super }} |
|
15 | ||
16 |
<style> |
|
17 |
.item-marker input + span { |
|
18 |
position: relative; |
|
19 |
z-index: 1000; |
|
20 |
margin-top: -55px; |
|
21 |
margin-left: -5px; |
|
22 |
display: block; |
|
23 |
width: 25px; |
|
24 |
height: 41px; |
|
25 |
background: url(/static/images/blank-marker-icon.png); |
|
26 |
} |
|
27 |
.item-marker input:checked + span { |
|
28 |
background: url(/static/xstatic/images/marker-icon.png); |
|
29 |
} |
|
30 |
</style> |
|
31 |
{% endblock %} |
wcs/qommon/templates/qommon/forms/widgets/map.html | ||
---|---|---|
10 | 10 |
data-init-lat="{{ widget.initial_position.lat }}" |
11 | 11 |
data-init-lng="{{ widget.initial_position.lng }}" |
12 | 12 |
{% endif %} |
13 |
{% block widget-control-attributes %}{% endblock %} |
|
13 | 14 |
></div> |
14 | 15 |
{% endblock %} |
15 |
- |