0002-maps-add-map-cell-8454.patch
combo/apps/maps/migrations/0002_map.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 |
('data', '0027_page_picture'), |
|
11 |
('auth', '0006_require_contenttypes_0002'), |
|
12 |
('maps', '0001_initial'), |
|
13 |
] |
|
14 | ||
15 |
operations = [ |
|
16 |
migrations.CreateModel( |
|
17 |
name='Map', |
|
18 |
fields=[ |
|
19 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
20 |
('placeholder', models.CharField(max_length=20)), |
|
21 |
('order', models.PositiveIntegerField()), |
|
22 |
('slug', models.SlugField(verbose_name='Slug', blank=True)), |
|
23 |
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)), |
|
24 |
('public', models.BooleanField(default=True, verbose_name='Public')), |
|
25 |
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')), |
|
26 |
('last_update_timestamp', models.DateTimeField(auto_now=True)), |
|
27 |
('title', models.CharField(max_length=150, verbose_name='Title', blank=True)), |
|
28 |
('default_position', models.CharField(max_length=128, null=True, verbose_name='Default position', blank=True)), |
|
29 |
('initial_zoom', models.CharField(default=b'13', max_length=2, verbose_name='Initial zoom level', choices=[(b'0', 'Whole world'), (b'9', 'Wide area'), (b'11', 'Area'), (b'13', 'Town'), (b'16', 'Small road'), (b'19', 'Ant')])), |
|
30 |
('min_zoom', models.CharField(default=b'0', max_length=2, verbose_name='Minimal zoom level', choices=[(b'0', 'Whole world'), (b'9', 'Wide area'), (b'11', 'Area'), (b'13', 'Town'), (b'16', 'Small road'), (b'19', 'Ant')])), |
|
31 |
('max_zoom', models.CharField(default=19, max_length=2, verbose_name='Maximal zoom level', choices=[(b'0', 'Whole world'), (b'9', 'Wide area'), (b'11', 'Area'), (b'13', 'Town'), (b'16', 'Small road'), (b'19', 'Ant')])), |
|
32 |
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)), |
|
33 |
('layers', models.ManyToManyField(to='maps.MapLayer', verbose_name='Layers', blank=True)), |
|
34 |
('page', models.ForeignKey(to='data.Page')), |
|
35 |
], |
|
36 |
options={ |
|
37 |
'verbose_name': 'Map', |
|
38 |
}, |
|
39 |
), |
|
40 |
] |
combo/apps/maps/models.py | ||
---|---|---|
18 | 18 |
from django.db import models |
19 | 19 |
from django.utils.text import slugify |
20 | 20 |
from django.utils.translation import ugettext_lazy as _ |
21 |
from django.core.urlresolvers import reverse_lazy |
|
22 |
from django import forms |
|
23 |
from django import template |
|
24 |
from django.conf import settings |
|
25 |
from django.core.exceptions import PermissionDenied |
|
21 | 26 | |
27 |
from combo.data.models import CellBase |
|
28 |
from combo.data.library import register_cell_class |
|
22 | 29 |
from combo.utils import requests |
23 | 30 | |
24 | 31 | |
... | ... | |
38 | 45 |
('truck', _('Truck')), |
39 | 46 |
] |
40 | 47 | |
48 |
ZOOM_LEVELS = [ ('0', _('Whole world')), |
|
49 |
('9', _('Wide area')), |
|
50 |
('11', _('Area')), |
|
51 |
('13', _('Town')), |
|
52 |
('16', _('Small road')), |
|
53 |
('19', _('Ant')),] |
|
54 | ||
55 | ||
56 |
class MapWidget(forms.TextInput): |
|
57 |
template_name = 'maps/map_widget.html' |
|
58 | ||
59 |
def render(self, name, value, attrs): |
|
60 |
final_attrs = self.build_attrs(attrs, name=name, value=value, |
|
61 |
type='hidden') |
|
62 |
cell_form_template = template.loader.get_template(self.template_name) |
|
63 |
return cell_form_template.render(final_attrs) |
|
64 | ||
41 | 65 | |
42 | 66 |
class MapLayer(models.Model): |
43 | 67 |
label = models.CharField(_('Label'), max_length=128) |
... | ... | |
84 | 108 |
feature['properties']['label'] = self.label |
85 | 109 |
feature['properties']['icon'] = self.icon |
86 | 110 |
return features |
111 | ||
112 | ||
113 |
@register_cell_class |
|
114 |
class Map(CellBase): |
|
115 |
title = models.CharField(_('Title'), max_length=150, blank=True) |
|
116 |
default_position = models.CharField(_('Default position'), null=True, blank=True, |
|
117 |
max_length=128) |
|
118 |
initial_zoom = models.CharField(_('Initial zoom level'), max_length=2, |
|
119 |
choices=ZOOM_LEVELS, default='13') |
|
120 |
min_zoom = models.CharField(_('Minimal zoom level'), max_length=2, |
|
121 |
choices=ZOOM_LEVELS, default='0') |
|
122 |
max_zoom = models.CharField(_('Maximal zoom level'), max_length=2, |
|
123 |
choices=ZOOM_LEVELS, default=19) |
|
124 |
layers = models.ManyToManyField(MapLayer, verbose_name=_('Layers'), blank=True) |
|
125 | ||
126 |
template_name = 'maps/map_cell.html' |
|
127 | ||
128 |
class Meta: |
|
129 |
verbose_name = _('Map') |
|
130 | ||
131 |
class Media: |
|
132 |
js = ('xstatic/leaflet.js', 'js/combo.map.js') |
|
133 |
css = {'all': ('xstatic/leaflet.css', 'css/combo.map.css')} |
|
134 | ||
135 |
def get_default_position(self): |
|
136 |
if self.default_position: |
|
137 |
return {'lat': self.default_position.split(';')[0], |
|
138 |
'lng': self.default_position.split(';')[1]} |
|
139 |
return settings.COMBO_MAP_DEFAULT_POSITION |
|
140 | ||
141 |
def get_default_form_class(self): |
|
142 |
fields = ('title', 'default_position', 'initial_zoom', 'min_zoom', |
|
143 |
'max_zoom', 'layers') |
|
144 | ||
145 |
default_position = self.get_default_position() |
|
146 |
map_attrs = {'init_lat': default_position['lat'], 'init_lng': default_position['lng'], |
|
147 |
'tile_urltemplate': settings.COMBO_MAP_TILE_URLTEMPLATE, |
|
148 |
'map_attribution': settings.COMBO_MAP_ATTRIBUTION} |
|
149 |
widgets = {'layers': forms.widgets.CheckboxSelectMultiple, |
|
150 |
'default_position': MapWidget(attrs=map_attrs)} |
|
151 |
return forms.models.modelform_factory(self.__class__, fields=fields, |
|
152 |
widgets=widgets) |
|
153 | ||
154 |
def get_geojson(self, request): |
|
155 |
geojson = {'type': 'FeatureCollection', 'features': []} |
|
156 |
for layer in self.layers.all(): |
|
157 |
geojson['features'] += layer.get_geojson(request) |
|
158 |
return geojson |
|
159 | ||
160 | ||
161 |
@classmethod |
|
162 |
def is_enabled(cls): |
|
163 |
return MapLayer.objects.count() > 0 |
|
164 | ||
165 |
def get_cell_extra_context(self, context): |
|
166 |
ctx = super(Map, self).get_cell_extra_context(context) |
|
167 |
ctx['title'] = self.title |
|
168 |
default_position = self.get_default_position() |
|
169 |
ctx['init_lat'] = default_position['lat'] |
|
170 |
ctx['init_lng'] = default_position['lng'] |
|
171 |
ctx['initial_zoom'] = self.initial_zoom |
|
172 |
ctx['min_zoom'] = self.min_zoom |
|
173 |
ctx['max_zoom'] = self.max_zoom |
|
174 |
ctx['geojson_url'] = reverse_lazy('mapcell-geojson', kwargs={'cell_id': self.pk}) |
|
175 |
ctx['tile_urltemplate'] = settings.COMBO_MAP_TILE_URLTEMPLATE |
|
176 |
ctx['map_attribution'] = settings.COMBO_MAP_ATTRIBUTION |
|
177 |
return ctx |
combo/apps/maps/static/css/combo.map.css | ||
---|---|---|
1 |
div.combo-cell-map { |
|
2 |
height: 60vh; |
|
3 |
} |
|
4 | ||
5 |
/* leaflet styles */ |
|
6 | ||
7 |
div.leaflet-div-icon span { |
|
8 |
width: 2.3rem; |
|
9 |
height: 2.3rem; |
|
10 |
display: block; |
|
11 |
left: -1rem; |
|
12 |
top: -1rem; |
|
13 |
position: relative; |
|
14 |
border-radius: 11rem 6rem 0.8rem; |
|
15 |
transform: scale(1, 1.3) rotate(45deg); |
|
16 |
border: 1px solid #aaa; |
|
17 |
} |
|
18 | ||
19 |
div.leaflet-popup-content span { |
|
20 |
display: block; |
|
21 |
} |
|
22 | ||
23 |
div.leaflet-popup-content span.field-value { |
|
24 |
font-weight: bold; |
|
25 |
} |
|
26 | ||
27 |
div.leaflet-div-icon span i:before { |
|
28 |
display: inline-block; |
|
29 |
margin: 9px; |
|
30 |
transform: scale(1.1) rotate(-45deg); |
|
31 |
} |
|
32 | ||
33 |
/* leaflet markers icons */ |
|
34 | ||
35 |
i.leaflet-marker-icon { |
|
36 |
font: normal normal normal 1rem/1 FontAwesome; |
|
37 |
} |
|
38 | ||
39 |
i.leaflet-marker-icon.home::before { |
|
40 |
content: "\f015"; /* home */ |
|
41 |
} |
|
42 | ||
43 |
i.leaflet-marker-icon.building::before { |
|
44 |
content: "\f0f7"; /* building */ |
|
45 |
} |
|
46 | ||
47 |
i.leaflet-marker-icon.hospital::before { |
|
48 |
content: "\f0f8"; /* hospital */ |
|
49 |
} |
|
50 | ||
51 |
i.leaflet-marker-icon.ambulance::before { |
|
52 |
content: "\f0f9"; /* ambulance */ |
|
53 |
} |
|
54 | ||
55 |
i.leaflet-marker-icon.taxi::before { |
|
56 |
content: "\f1ba"; /* taxi */ |
|
57 |
} |
|
58 | ||
59 |
i.leaflet-marker-icon.subway::before { |
|
60 |
content: "\f239"; /* subway */ |
|
61 |
} |
|
62 | ||
63 |
i.leaflet-marker-icon.wheelchair::before { |
|
64 |
content: "\f193"; /* wheelchair */ |
|
65 |
} |
|
66 | ||
67 |
i.leaflet-marker-icon.bicycle::before { |
|
68 |
content: "\f206"; /* bicycle */ |
|
69 |
} |
|
70 | ||
71 |
i.leaflet-marker-icon.car::before { |
|
72 |
content: "\f1b9"; /* car */ |
|
73 |
} |
|
74 | ||
75 |
i.leaflet-marker-icon.train::before { |
|
76 |
content: "\f238"; /* train */ |
|
77 |
} |
|
78 | ||
79 |
i.leaflet-marker-icon.bus::before { |
|
80 |
content: "\f207"; /* bus */ |
|
81 |
} |
|
82 | ||
83 |
i.leaflet-marker-icon.motorcycle::before { |
|
84 |
content: "\f21c"; /* motorcycle */ |
|
85 |
} |
|
86 | ||
87 |
i.leaflet-marker-icon.truck::before { |
|
88 |
content: "\f0d1"; /* truck */ |
|
89 |
} |
|
90 |
combo/apps/maps/static/js/combo.map.js | ||
---|---|---|
1 |
$(function() { |
|
2 |
function render_map(container) { |
|
3 |
$container = $(container); |
|
4 |
$container.find('div div.combo-cell-map').each(function() { |
|
5 |
var $map_widget = $(this); |
|
6 |
var map_options = Object(); |
|
7 |
var initial_zoom = parseInt($map_widget.data('init-zoom')); |
|
8 |
if (! isNaN(initial_zoom)) { |
|
9 |
map_options.zoom = initial_zoom; |
|
10 |
} else { |
|
11 |
map_options.zoom = 13; |
|
12 |
} |
|
13 |
var max_zoom = parseInt($map_widget.data('max_zoom')); |
|
14 |
if (!isNaN(max_zoom)) map_options.maxZoom = max_zoom; |
|
15 |
var min_zoom = parseInt($map_widget.data('min-zoom')); |
|
16 |
if (!isNaN(min_zoom)) map_options.minZoom = min_zoom; |
|
17 |
var latlng = [$map_widget.data('init-lat'), $map_widget.data('init-lng')]; |
|
18 |
var geojson_url = $map_widget.data('geojson-url'); |
|
19 |
var map_tile_url = $map_widget.data('tile-urltemplate'); |
|
20 |
var map_attribution = $map_widget.data('map-attribution'); |
|
21 |
var map = L.map(this, map_options); |
|
22 |
var store_position_selector = $map_widget.data('store-position'); |
|
23 |
map.setView(latlng, map_options.zoom); |
|
24 | ||
25 |
L.tileLayer(map_tile_url, |
|
26 |
{ |
|
27 |
attribution: map_attribution |
|
28 |
}).addTo(map); |
|
29 |
if (store_position_selector) { |
|
30 |
map.marker = L.marker(latlng); |
|
31 |
map.marker.addTo(map); |
|
32 |
var hidden = $('input#' + store_position_selector); |
|
33 |
map.on('click', function(e) { |
|
34 |
map.marker.setLatLng(e.latlng); |
|
35 |
hidden.val(e.latlng.lat + ';' + e.latlng.lng); |
|
36 |
}); |
|
37 |
} |
|
38 |
if (geojson_url) { |
|
39 |
$.getJSON(geojson_url, function(data) { |
|
40 |
var geo_json = L.geoJson(data, { |
|
41 |
onEachFeature: function(feature, layer) { |
|
42 |
$map_widget.trigger('combo:map-feature-click', feature, layer); |
|
43 |
}, |
|
44 |
pointToLayer: function (feature, latlng) { |
|
45 |
var markerStyles = "background-color: "+feature.properties.colour+";"; |
|
46 |
marker = L.divIcon({iconAnchor: [0, 30], |
|
47 |
popupAnchor: [5, -45], |
|
48 |
html: '<span style="' + markerStyles + '"><i class="leaflet-marker-icon '+feature.properties.icon+'" style="color:'+feature.properties.icon_colour+'"></i></span>' |
|
49 |
}); |
|
50 |
return L.marker(latlng, {icon: marker}); |
|
51 |
} |
|
52 |
}); |
|
53 |
var bounds = geo_json.getBounds(); |
|
54 |
if (bounds.isValid()) { |
|
55 |
map.fitBounds(bounds); |
|
56 |
geo_json.addTo(map); |
|
57 |
} |
|
58 |
}); |
|
59 |
} |
|
60 |
}); |
|
61 |
}; |
|
62 |
$('div.combo-cell-map').parents('div.cell').on('combo:cellform-reloaded', function() { |
|
63 |
render_map(this); |
|
64 |
}); |
|
65 |
$('div.combo-cell-map').parents('div.cell').each(function() { |
|
66 |
$(this).trigger('combo:cellform-reloaded'); |
|
67 |
}); |
|
68 |
}); |
combo/apps/maps/templates/maps/map_cell.html | ||
---|---|---|
1 |
{% if title %}<h2>{{ title }}</h2>{% endif %} |
|
2 |
<div class="combo-cell-map" data-init-zoom="{{ initial_zoom }}" data-min-zoom="{{ min_zoom }}" data-max-zoom="{{ max_zoom }}" data-init-lat="{{ init_lat }}" data-init-lng="{{ init_lng }}" data-geojson-url="{{ geojson_url }}" data-tile-urltemplate="{{ tile_urltemplate}}" data-map-attribution="{{ map_attribution}}"> |
|
3 |
</div> |
combo/apps/maps/templates/maps/map_widget.html | ||
---|---|---|
1 |
<div class="combo-cell-map" data-init-lat="{{ init_lat }}" data-init-lng="{{ init_lng }}" data-store-position="combo-map-{{ id }}" data-tile-urltemplate="{{ tile_urltemplate}}" data-map-attribution="{{ map_attribution}}"> |
|
2 |
</div> |
|
3 | ||
4 |
<input type="{{ type }}" name="{{ name }}" value="{{ init_lat }};{{ init_lng }}" id="combo-map-{{ id }}" /> |
combo/apps/maps/urls.py | ||
---|---|---|
21 | 21 |
from .manager_views import (ManagerHomeView, LayerAddView, |
22 | 22 |
LayerEditView, LayerDeleteView) |
23 | 23 | |
24 |
from .views import GeojsonView |
|
25 | ||
24 | 26 |
maps_manager_urls = [ |
25 | 27 |
url('^$', ManagerHomeView.as_view(), name='maps-manager-homepage'), |
26 | 28 |
url('^layers/add/$', LayerAddView.as_view(), name='maps-manager-layer-add'), |
... | ... | |
33 | 35 |
urlpatterns = [ |
34 | 36 |
url(r'^manage/maps/', decorated_includes(manager_required, |
35 | 37 |
include(maps_manager_urls))), |
38 |
url(r'^ajax/mapcell/geojson/(?P<cell_id>\w+)/$', GeojsonView.as_view(), |
|
39 |
name='mapcell-geojson'), |
|
36 | 40 |
] |
combo/apps/maps/views.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2017 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import json |
|
18 | ||
19 |
from django.views.generic.base import View |
|
20 |
from django.http import HttpResponse, Http404, HttpResponseForbidden |
|
21 | ||
22 |
from .models import Map |
|
23 | ||
24 | ||
25 |
class GeojsonView(View): |
|
26 | ||
27 |
def get(self, request, *args, **kwargs): |
|
28 |
try: |
|
29 |
cell = Map.objects.get(pk=kwargs['cell_id']) |
|
30 |
except Map.DoesNotExist: |
|
31 |
raise Http404() |
|
32 |
if cell.page.is_visible(request.user) and cell.is_visible(request.user): |
|
33 |
geojson = cell.get_geojson(request) |
|
34 |
content_type = 'application/json' |
|
35 |
return HttpResponse(json.dumps(geojson), content_type=content_type) |
|
36 |
return HttpResponseForbidden() |
combo/manager/templates/combo/manager_base.html | ||
---|---|---|
3 | 3 | |
4 | 4 |
{% block css %} |
5 | 5 |
<link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/combo.manager.css"/> |
6 |
<link rel="stylesheet" type="text/css" media="all" href="{% static "xstatic/leaflet.css" %}"></script> |
|
7 |
<link rel="stylesheet" type="text/css" media="all" href="{% static "css/combo.map.css" %}"></script> |
|
6 | 8 |
{% endblock %} |
7 | 9 |
{% block page-title %}{% firstof site_title "Combo" %}{% endblock %} |
8 | 10 |
{% block site-title %}{% firstof site_title "Combo" %}{% endblock %} |
... | ... | |
30 | 32 |
<script src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script> |
31 | 33 |
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script> |
32 | 34 |
<script src="{% static "js/combo.manager.js" %}"></script> |
35 |
<script src="{% static "xstatic/leaflet.js" %}"></script> |
|
36 |
<script src="{% static "js/combo.map.js" %}"></script> |
|
33 | 37 |
{% endblock %} |
combo/settings.py | ||
---|---|---|
80 | 80 |
'combo.apps.maps', |
81 | 81 |
'haystack', |
82 | 82 |
'xstatic.pkg.chartnew_js', |
83 |
'xstatic.pkg.leaflet', |
|
83 | 84 |
) |
84 | 85 | |
85 | 86 |
INSTALLED_APPS = plugins.register_plugins_apps(INSTALLED_APPS) |
... | ... | |
283 | 284 |
# dashboard support |
284 | 285 |
COMBO_DASHBOARD_ENABLED = False |
285 | 286 | |
287 |
# default position on maps |
|
288 |
COMBO_MAP_DEFAULT_POSITION = {'lat': '48.83369263315934', |
|
289 |
'lng': '2.3233688436448574' |
|
290 |
} |
|
291 | ||
292 |
# default map tiles url |
|
293 |
COMBO_MAP_TILE_URLTEMPLATE = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' |
|
294 | ||
295 |
# default combo map attribution |
|
296 |
COMBO_MAP_ATTRIBUTION = 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' |
|
297 | ||
286 | 298 |
local_settings_file = os.environ.get('COMBO_SETTINGS_FILE', |
287 | 299 |
os.path.join(os.path.dirname(__file__), 'local_settings.py')) |
288 | 300 |
if os.path.exists(local_settings_file): |
debian/control | ||
---|---|---|
16 | 16 |
python-feedparser, |
17 | 17 |
python-django-cmsplugin-blurp, |
18 | 18 |
python-xstatic-chartnew-js, |
19 |
python-xstatic-leaflet, |
|
19 | 20 |
python-eopayment (>= 1.9), |
20 | 21 |
python-django-haystack (>= 2.4.0), |
21 | 22 |
python-sorl-thumbnail, |
requirements.txt | ||
---|---|---|
6 | 6 |
django-jsonfield |
7 | 7 |
requests |
8 | 8 |
XStatic-ChartNew.js |
9 |
XStatic-Leaflet |
|
9 | 10 |
eopayment>=1.13 |
10 | 11 |
python-dateutil |
11 | 12 |
djangorestframework>=3.3, <3.4 |
setup.py | ||
---|---|---|
111 | 111 |
'django-jsonfield', |
112 | 112 |
'requests', |
113 | 113 |
'XStatic-ChartNew.js', |
114 |
'XStatic-Leaflet', |
|
114 | 115 |
'eopayment>=1.13', |
115 | 116 |
'python-dateutil', |
116 | 117 |
'djangorestframework>=3.3, <3.4', |
tests/test_maps_cells.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
import pytest |
|
3 | ||
4 |
from django.contrib.auth.models import User |
|
5 |
from django.test.client import RequestFactory |
|
6 |
from django.template import Context |
|
7 |
from django.test import Client |
|
8 |
from django.core.urlresolvers import reverse |
|
9 |
from django.contrib.auth.models import Group |
|
10 | ||
11 |
from combo.data.models import Page |
|
12 |
from combo.apps.maps.models import MapLayer, Map |
|
13 | ||
14 |
pytestmark = pytest.mark.django_db |
|
15 | ||
16 |
client = Client() |
|
17 | ||
18 |
@pytest.fixture |
|
19 |
def user(): |
|
20 |
try: |
|
21 |
user = User.objects.get(username='admin') |
|
22 |
except User.DoesNotExist: |
|
23 |
user = User.objects.create_user('admin', email=None, password='admin') |
|
24 |
return user |
|
25 | ||
26 |
@pytest.fixture |
|
27 |
def layer(): |
|
28 |
try: |
|
29 |
layer = MapLayer.objects.get() |
|
30 |
except MapLayer.DoesNotExist: |
|
31 |
layer = MapLayer() |
|
32 |
layer.geojson_url = 'http://example.net/geojson' |
|
33 |
layer.marker_colour = 'FF0000' |
|
34 |
layer.icon = 'fa-bicycle' |
|
35 |
layer.icon_colour = '0000FF' |
|
36 |
layer.save() |
|
37 |
return layer |
|
38 | ||
39 |
def login(username='admin', password='admin'): |
|
40 |
resp = client.post('/login/', {'username': username, 'password': password}) |
|
41 |
assert resp.status_code == 302 |
|
42 | ||
43 |
def test_cell_disabled(): |
|
44 |
MapLayer.objects.all().delete() |
|
45 |
assert Map.is_enabled() is False |
|
46 | ||
47 |
def test_cell_enabled(layer): |
|
48 |
assert Map.is_enabled() is True |
|
49 | ||
50 |
def test_cell_rendering(layer): |
|
51 |
page = Page(title='xxx', slug='test_map_cell', template_name='standard') |
|
52 |
page.save() |
|
53 |
cell = Map(page=page, placeholder='content', order=0, title = 'Map with points', |
|
54 |
default_position="48.83369263315934;2.3233688436448574") |
|
55 |
cell.save() |
|
56 |
cell.layers.add(layer) |
|
57 |
context = Context({'request': RequestFactory().get('/')}) |
|
58 |
rendered = cell.render(context) |
|
59 |
assert 'data-init-zoom="13"' in rendered |
|
60 |
assert 'data-min-zoom="0"' in rendered |
|
61 |
assert 'data-max-zoom="19"' in rendered |
|
62 |
assert 'data-init-lat="48.83369263315934"' in rendered |
|
63 |
assert 'data-init-lng="2.3233688436448574"' in rendered |
|
64 |
assert 'data-geojson-url="/ajax/mapcell/geojson/1/"' in rendered |
|
65 |
resp = client.get('/test_map_cell/') |
|
66 |
assert 'xstatic/leaflet.js' in resp.content |
|
67 |
assert 'js/combo.map.js' in resp.content |
|
68 |
assert 'xstatic/leaflet.css' in resp.content |
|
69 |
assert 'css/combo.map.css' in resp.content |
|
70 | ||
71 | ||
72 |
def test_get_geojson_on_non_public_page(layer): |
|
73 |
page = Page(title='xxx', slug='new', template_name='standard', |
|
74 |
public=False) |
|
75 |
page.save() |
|
76 |
cell = Map(page=page, placeholder='content', order=0, |
|
77 |
title = 'Map with points') |
|
78 |
cell.save() |
|
79 |
cell.layers.add(layer) |
|
80 |
resp = client.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id})) |
|
81 |
assert resp.status_code == 403 |
|
82 | ||
83 |
def test_get_geojson_on_non_publik_cell(layer): |
|
84 |
page = Page(title='xxx', slug='new', template_name='standard') |
|
85 |
page.save() |
|
86 |
cell = Map(page=page, placeholder='content', order=0, public=False, |
|
87 |
title = 'Map with points') |
|
88 |
cell.save() |
|
89 |
cell.layers.add(layer) |
|
90 |
resp = client.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id})) |
|
91 |
assert resp.status_code == 403 |
|
92 | ||
93 |
def test_geojson_on_restricted_cell(layer, user): |
|
94 |
page = Page(title='xxx', slug='new', template_name='standard') |
|
95 |
page.save() |
|
96 |
group = Group.objects.create(name='map tester') |
|
97 |
cell = Map(page=page, placeholder='content', order=0, public=False) |
|
98 |
cell.title = 'Map with points' |
|
99 |
cell.save() |
|
100 |
cell.layers.add(layer) |
|
101 |
cell.groups.add(group) |
|
102 |
login() |
|
103 |
resp = client.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id})) |
|
104 |
assert resp.status_code == 403 |
|
105 |
user.groups.add(group) |
|
106 |
user.save() |
|
107 |
resp = client.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id})) |
|
0 |
- |