Projet

Général

Profil

0001-maps-add-handling-of-marker-behaviour-on-click-21034.patch

Josué Kouka, 16 mai 2018 12:06

Télécharger (9,71 ko)

Voir les différences:

Subject: [PATCH] maps: add handling of marker behaviour on click (#21034)

 .../maps/migrations/0006_auto_20180215_1020.py     | 19 ++++++++++++
 combo/apps/maps/models.py                          | 36 +++++++++++++++++++++-
 combo/apps/maps/static/js/combo.map.js             | 19 ++++++++++++
 combo/apps/maps/templates/maps/map_cell.html       |  1 +
 combo/apps/maps/views.py                           |  2 +-
 tests/test_maps_cells.py                           | 23 +++++++++++++-
 6 files changed, 97 insertions(+), 3 deletions(-)
 create mode 100644 combo/apps/maps/migrations/0006_auto_20180215_1020.py
combo/apps/maps/migrations/0006_auto_20180215_1020.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import migrations, models
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('maps', '0005_auto_20180212_1742'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='map',
16
            name='marker_onclick_behaviour',
17
            field=models.CharField(default=b'none', max_length=32, verbose_name='Marker behaviour on clik', choices=[(b'none', 'Nothing'), (b'display_data', 'Display data in popup')]),
18
        ),
19
    ]
combo/apps/maps/models.py
18 18

  
19 19
from django.core import serializers
20 20
from django.db import models
21
from django.utils.html import escape
21 22
from django.utils.text import slugify
22 23
from django.utils.translation import ugettext_lazy as _
23 24
from django.core.urlresolvers import reverse_lazy
......
67 68
    ('wheelchair', _('Wheelchair')),
68 69
]
69 70

  
71
MARKER_ONCLIK_BEHAVIOUR = [
72
    ('none', _('Nothing')),
73
    ('display_data', _('Display data in popup')),
74
]
75

  
70 76
ZOOM_LEVELS = [ ('0', _('Whole world')),
71 77
                ('9', _('Wide area')),
72 78
                ('11', _('Area')),
......
76 82
                ('19', _('Ant')),]
77 83

  
78 84

  
85
def escape_properties(properties):
86
    if isinstance(properties, (list,)):
87
        escaped = []
88
        for prop in properties:
89
            if isinstance(prop, (list,)):
90
                prop = escape_properties(prop)
91
                escaped.append(prop)
92
            else:
93
                escaped.append(escape(prop))
94
    else:
95
        escaped = {}
96
        for key, value in properties.iteritems():
97
            if isinstance(value, (dict,)):
98
                value = escape_properties(value)
99
            escaped[escape(key)] = escape(value)
100
    return escaped
101

  
102

  
79 103
class MapLayerManager(models.Manager):
80 104
    def get_by_natural_key(self, slug):
81 105
        return self.get(slug=slug)
......
172 196
            features = [x for x in features if match(x)]
173 197

  
174 198
        for feature in features:
199
            properties = feature.get('properties', {})
200
            display_fields = properties.get('display_fields')
201
            if display_fields:
202
                feature['properties']['display_fields'] = escape_properties(display_fields)
203
            else:
204
                feature['properties'] = escape_properties(properties)
205

  
175 206
            feature['properties']['layer'] = {
176 207
                'colour': self.marker_colour,
177 208
                'icon_colour': self.icon_colour,
......
201 232
    max_zoom = models.CharField(_('Maximal zoom level'), max_length=2,
202 233
                                choices=ZOOM_LEVELS, default=19)
203 234
    group_markers = models.BooleanField(_('Group markers in clusters'), default=False)
235
    marker_onclick_behaviour = models.CharField(_('Marker behaviour on clik'), max_length=32,
236
                                                default='none', choices=MARKER_ONCLIK_BEHAVIOUR)
204 237
    layers = models.ManyToManyField(MapLayer, verbose_name=_('Layers'), blank=True)
205 238

  
206 239
    template_name = 'maps/map_cell.html'
......
218 251

  
219 252
    def get_default_form_class(self):
220 253
        fields = ('title', 'initial_state', 'initial_zoom', 'min_zoom',
221
                  'max_zoom', 'group_markers', 'layers')
254
                  'max_zoom', 'group_markers', 'marker_onclick_behaviour', 'layers')
222 255
        widgets = {'layers': forms.widgets.CheckboxSelectMultiple}
223 256
        return forms.models.modelform_factory(self.__class__, fields=fields,
224 257
                                             widgets=widgets)
......
248 281
        ctx['map_attribution'] = settings.COMBO_MAP_ATTRIBUTION
249 282
        ctx['max_bounds'] = settings.COMBO_MAP_MAX_BOUNDS
250 283
        ctx['group_markers'] = self.group_markers
284
        ctx['marker_onclick_behaviour'] = self.marker_onclick_behaviour
251 285
        return ctx
combo/apps/maps/static/js/combo.map.js
1 1
$(function() {
2

  
2 3
    L.Map.include(
3 4
      {
4 5
        add_geojson_layer: function(callback) {
......
16 17
              var geo_json = L.geoJson(data, {
17 18
                  onEachFeature: function(feature, layer) {
18 19
                      $(cell).trigger('combo:map-feature-prepare', {'feature': feature, 'layer': layer});
20
                      var marker_onclick_behaviour = $map_widget.data('marker-onclick-behaviour');
21
                      if (marker_onclick_behaviour === 'display_data') {
22
                            var popup = '';
23
                            if (feature.properties.display_fields) {
24
                                $.each(feature.properties.display_fields, function(key, value) {
25
                                    popup += '<p class="popup-field"><span class="field-value">' + value[1] + '</span>';
26
                                });
27
                            } else {
28
                                var properties = feature.properties;
29
                                $.each(Object.keys(properties).sort(), function(idx, key) {
30
                                    // exclude object type properties
31
                                    if (typeof(properties[key]) !== 'object') {
32
                                        popup += '<p class="popup-field"><span class="field-value">' + properties[key] + '</span>' + '</p>';
33
                                    }
34
                                });
35
                            }
36
                            layer.bindPopup(popup);
37
                      }
19 38
                  },
20 39
                  pointToLayer: function (feature, latlng) {
21 40
                      var markerStyles = "background-color: " + feature.properties.layer.colour + ";";
combo/apps/maps/templates/maps/map_cell.html
7 7
        data-init-lng="{{ init_lng }}" data-geojson-url="{{ geojson_url }}"
8 8
        data-tile-urltemplate="{{ tile_urltemplate}}" data-map-attribution="{{ map_attribution}}"
9 9
        {% if group_markers %}data-group-markers="1"{% endif %}
10
        data-marker-onclick-behaviour="{{ cell.marker_onclick_behaviour }}"
10 11
        {% if max_bounds.corner1.lat %}
11 12
        data-max-bounds-lat1="{{ max_bounds.corner1.lat }}"
12 13
        data-max-bounds-lng1="{{ max_bounds.corner1.lng }}"
combo/apps/maps/views.py
16 16

  
17 17
import json
18 18

  
19
from django.views.generic.base import View
20 19
from django.http import HttpResponse, Http404, HttpResponseForbidden
20
from django.views.generic.base import View
21 21

  
22 22
from .models import Map
23 23

  
tests/test_maps_cells.py
8 8
from django.test import Client
9 9
from django.core.urlresolvers import reverse
10 10
from django.contrib.auth.models import Group
11
from django.utils.html import escape
11 12

  
12 13
from combo.data.models import Page
13
from combo.apps.maps.models import MapLayer, Map
14
from combo.apps.maps.models import MapLayer, Map, escape_properties
14 15

  
15 16
pytestmark = pytest.mark.django_db
16 17

  
......
225 226
        assert len(json.loads(resp.content)['features']) == 1
226 227
        assert 'orig=combo' in requests_get.call_args[0][1]
227 228
        assert not 'email=admin%40localhost&' in requests_get.call_args[0][1]
229

  
230

  
231
def test_feature_properties_escaping():
232
    list_properties = [
233
        ['code', '<script>alert("hello")</script>'],
234
        ['category', 'potholes'],
235
    ]
236
    escaped_list_properties = escape_properties(list_properties)
237
    assert escaped_list_properties[0][0] == 'code'
238
    assert escaped_list_properties[0][1] == escape(list_properties[0][1])
239
    assert escaped_list_properties[1][0] == 'category'
240
    assert escaped_list_properties[1][1] == 'potholes'
241
    dict_properties = {
242
        'code': '<script>alert("hello")</script>',
243
        'category': 'potholes'
244
    }
245
    escaped_dict_properties = escape_properties(dict_properties)
246
    for key, value in escaped_dict_properties.items():
247
        assert key in ('code', 'category')
248
        assert value in (escape(dict_properties['code']), 'potholes')
228
-