Projet

Général

Profil

0002-maps-new-MapLayerOptions-model-22639.patch

Lauréline Guérin, 11 février 2020 15:16

Télécharger (27,7 ko)

Voir les différences:

Subject: [PATCH 2/5] maps: new MapLayerOptions model (#22639)

 combo/apps/maps/forms.py                      | 13 +++-
 combo/apps/maps/manager_views.py              | 72 +++++++++++++++++--
 .../maps/migrations/0008_map_layer_options.py | 48 +++++++++++++
 .../maps/migrations/0009_map_layer_kind.py    | 35 +++++++++
 combo/apps/maps/models.py                     | 64 +++++++++++++++--
 .../templates/maps/layer_options_form.html    | 22 ++++++
 .../maps/templates/maps/map_cell_form.html    | 26 +++++++
 combo/apps/maps/urls.py                       | 17 +++--
 combo/data/models.py                          | 11 ++-
 combo/manager/static/css/combo.manager.css    | 15 ++--
 tests/test_import_export.py                   | 24 +++++--
 tests/test_maps_cells.py                      | 19 +++--
 tests/test_maps_manager.py                    | 34 ++++++++-
 13 files changed, 358 insertions(+), 42 deletions(-)
 create mode 100644 combo/apps/maps/migrations/0008_map_layer_options.py
 create mode 100644 combo/apps/maps/migrations/0009_map_layer_kind.py
 create mode 100644 combo/apps/maps/templates/maps/layer_options_form.html
 create mode 100644 combo/apps/maps/templates/maps/map_cell_form.html
combo/apps/maps/forms.py
20 20
from django.utils.translation import ugettext_lazy as _
21 21

  
22 22
from combo.data.fields import TemplatableURLField
23
from .models import MapLayer
23
from .models import MapLayer, MapLayerOptions
24 24

  
25 25

  
26 26
class IconRadioSelect(forms.RadioSelect):
......
37 37
                   'icon_colour': forms.TextInput(attrs={'type': 'color'}),
38 38
                   }
39 39

  
40

  
41 40
    def __init__(self, *args, **kwargs):
42 41
        super(MapNewLayerForm, self).__init__(*args, **kwargs)
43 42
        self.fields['icon'].choices = list(
......
59 58
        super(MapLayerForm, self).__init__(*args, **kwargs)
60 59
        self.fields['icon'].choices = list(
61 60
                sorted(self.fields['icon'].choices, key=lambda x: slugify(force_text(x[1]))))
61

  
62

  
63
class MapLayerOptionsForm(forms.ModelForm):
64
    class Meta:
65
        model = MapLayerOptions
66
        fields = ['map_layer']
67

  
68
    def __init__(self, *args, **kwargs):
69
        super(MapLayerOptionsForm, self).__init__(*args, **kwargs)
70
        self.fields['map_layer'].queryset = self.instance.map_cell.get_free_layers()
combo/apps/maps/manager_views.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
from django.core.urlresolvers import reverse_lazy
18
from django.views.generic import (TemplateView, ListView, CreateView,
19
                UpdateView, DeleteView)
17
from django.core.urlresolvers import reverse, reverse_lazy
18
from django.http import Http404
19
from django.shortcuts import get_object_or_404
20
from django.utils.translation import ugettext_lazy as _
21
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
20 22

  
23
from combo.data.models import CellBase, PageSnapshot
21 24
from .models import Map
22 25
from .models import MapLayer
23
from .forms import MapNewLayerForm, MapLayerForm
26
from .models import MapLayerOptions
27
from .forms import MapNewLayerForm, MapLayerForm, MapLayerOptionsForm
24 28

  
25 29

  
26 30
class MapLayerMixin(object):
......
49 53

  
50 54
class LayerDeleteView(MapLayerMixin, DeleteView):
51 55
    template_name = 'maps/map_layer_confirm_delete.html'
56

  
57

  
58
class MapCellAddLayer(CreateView):
59
    form_class = MapLayerOptionsForm
60
    template_name = 'maps/layer_options_form.html'
61

  
62
    def dispatch(self, request, *args, **kwargs):
63
        try:
64
            self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk'])
65
        except Map.DoesNotExist:
66
            raise Http404
67
        return super(MapCellAddLayer, self).dispatch(request, *args, **kwargs)
68

  
69
    def get_form_kwargs(self):
70
        kwargs = super(MapCellAddLayer, self).get_form_kwargs()
71
        kwargs['instance'] = MapLayerOptions(map_cell=self.cell)
72
        return kwargs
73

  
74
    def form_valid(self, form):
75
        PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
76
        return super(MapCellAddLayer, self).form_valid(form)
77

  
78
    def get_success_url(self):
79
        return '%s#cell-%s' % (
80
            reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')}),
81
            self.kwargs['cell_reference'])
82

  
83

  
84
map_cell_add_layer = MapCellAddLayer.as_view()
85

  
86

  
87
class MapCellDeleteLayer(DeleteView):
88
    template_name = 'combo/generic_confirm_delete.html'
89

  
90
    def dispatch(self, request, *args, **kwargs):
91
        try:
92
            self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk'])
93
        except Map.DoesNotExist:
94
            raise Http404
95
        self.object = get_object_or_404(
96
            MapLayerOptions,
97
            pk=kwargs['layeroptions_pk'],
98
            map_cell=self.cell)
99
        return super(MapCellDeleteLayer, self).dispatch(request, *args, **kwargs)
100

  
101
    def get_object(self, *args, **kwargs):
102
        return self.object
103

  
104
    def delete(self, request, *args, **kwargs):
105
        response = super(MapCellDeleteLayer, self).delete(request, *args, **kwargs)
106
        PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
107
        return response
108

  
109
    def get_success_url(self):
110
        return '%s#cell-%s' % (
111
            reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')}),
112
            self.kwargs['cell_reference'])
113

  
114

  
115
map_cell_delete_layer = MapCellDeleteLayer.as_view()
combo/apps/maps/migrations/0008_map_layer_options.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import migrations, models
5
import django.db.models.deletion
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('maps', '0007_auto_20180706_1345'),
12
    ]
13

  
14
    operations = [
15
        migrations.SeparateDatabaseAndState(
16
            state_operations=[
17
                migrations.CreateModel(
18
                    name='MapLayerOptions',
19
                    fields=[
20
                        ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
                    ],
22
                    options={
23
                        'db_table': 'maps_map_layers',
24
                    },
25
                ),
26
                migrations.AlterField(
27
                    model_name='map',
28
                    name='layers',
29
                    field=models.ManyToManyField(blank=True, through='maps.MapLayerOptions', to='maps.MapLayer', verbose_name='Layers'),
30
                ),
31
                migrations.AddField(
32
                    model_name='maplayeroptions',
33
                    name='map_cell',
34
                    field=models.ForeignKey(db_column='map_id', on_delete=django.db.models.deletion.CASCADE, to='maps.Map'),
35
                ),
36
                migrations.AddField(
37
                    model_name='maplayeroptions',
38
                    name='map_layer',
39
                    field=models.ForeignKey(db_column='maplayer_id', verbose_name='Layer', on_delete=django.db.models.deletion.CASCADE, to='maps.MapLayer'),
40
                ),
41
                migrations.AlterUniqueTogether(
42
                    name='maplayeroptions',
43
                    unique_together=set([('map_cell', 'map_layer')]),
44
                ),
45
            ],
46
            database_operations=[]
47
        )
48
    ]
combo/apps/maps/migrations/0009_map_layer_kind.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-02-07 09:00
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('maps', '0008_map_layer_options'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='maplayer',
17
            name='kind',
18
            field=models.CharField(choices=[('tiles', 'Tiles'), ('geojson', 'GeoJSON')], default='geojson', max_length=10),
19
        ),
20
        migrations.AddField(
21
            model_name='maplayer',
22
            name='tiles_attribution',
23
            field=models.CharField(blank=True, max_length=1024, null=True, verbose_name='Attribution'),
24
        ),
25
        migrations.AddField(
26
            model_name='maplayer',
27
            name='tiles_default',
28
            field=models.BooleanField(default=False, verbose_name='Default tiles layer'),
29
        ),
30
        migrations.AddField(
31
            model_name='maplayer',
32
            name='tiles_template_url',
33
            field=models.CharField(blank=True, max_length=1024, null=True, verbose_name='Tiles URL'),
34
        ),
35
    ]
combo/apps/maps/models.py
267 267
    group_markers = models.BooleanField(_('Group markers in clusters'), default=False)
268 268
    marker_behaviour_onclick = models.CharField(_('Marker behaviour on click'), max_length=32,
269 269
                                                default='none', choices=MARKER_BEHAVIOUR_ONCLICK)
270
    layers = models.ManyToManyField(MapLayer, verbose_name=_('Layers'), blank=True)
270
    layers = models.ManyToManyField(
271
        MapLayer,
272
        through='MapLayerOptions',
273
        verbose_name=_('Layers'),
274
        blank=True
275
    )
271 276

  
272 277
    template_name = 'maps/map_cell.html'
278
    manager_form_template = 'maps/map_cell_form.html'
273 279

  
274 280
    class Meta:
275 281
        verbose_name = _('Map')
......
287 293

  
288 294
    def get_default_form_class(self):
289 295
        fields = ('title', 'initial_state', 'initial_zoom', 'min_zoom',
290
                  'max_zoom', 'group_markers', 'marker_behaviour_onclick', 'layers')
291
        widgets = {'layers': forms.widgets.CheckboxSelectMultiple}
292
        return forms.models.modelform_factory(self.__class__, fields=fields,
293
                                             widgets=widgets)
296
                  'max_zoom', 'group_markers', 'marker_behaviour_onclick')
297
        return forms.models.modelform_factory(self.__class__, fields=fields)
294 298

  
295 299
    def get_geojson(self, request):
296 300
        geojson = {'type': 'FeatureCollection', 'features': []}
......
302 306

  
303 307
    @classmethod
304 308
    def is_enabled(cls):
305
        return MapLayer.objects.count() > 0
309
        return MapLayer.objects.exists()
306 310

  
307 311
    def get_cell_extra_context(self, context):
308 312
        ctx = super(Map, self).get_cell_extra_context(context)
......
322 326
        ctx['marker_behaviour_onclick'] = self.marker_behaviour_onclick
323 327
        return ctx
324 328

  
329
    def get_free_layers(self):
330
        used_layers = MapLayerOptions.objects.filter(map_cell=self).values('map_layer')
331
        return MapLayer.objects.exclude(pk__in=used_layers)
332

  
333
    def export_subobjects(self):
334
        return {'layers': [x.get_as_serialized_object() for x in MapLayerOptions.objects.filter(map_cell=self)]}
335

  
336
    @classmethod
337
    def prepare_serialized_data(cls, cell_data):
338
        # ensure compatibility with old exports
339
        if 'layers' in cell_data['fields']:
340
            layers = cell_data['fields'].pop('layers')
341
            cell_data['layers'] = [
342
                {
343
                    'fields': {'map_layer': layer},
344
                    'model': 'maps.maplayeroptions'
345
                } for layer in layers
346
            ]
347
        return cell_data
348

  
349
    def import_subobjects(self, cell_json):
350
        if 'layers' not in cell_json:
351
            return
352
        for layer in cell_json['layers']:
353
            layer['fields']['map_cell'] = self.pk
354
        for layer in serializers.deserialize('json', json.dumps(cell_json['layers'])):
355
            layer.save()
356

  
325 357
    def duplicate_m2m(self, new_cell):
326 358
        # set layers
327
        new_cell.layers.set(self.layers.all())
359
        for layer in self.layers.all():
360
            MapLayerOptions.objects.create(map_cell=new_cell, map_layer=layer)
361

  
362

  
363
class MapLayerOptions(models.Model):
364
    map_cell = models.ForeignKey(Map, on_delete=models.CASCADE, db_column='map_id')
365
    map_layer = models.ForeignKey(MapLayer, verbose_name=_('Layer'), on_delete=models.CASCADE, db_column='maplayer_id')
366

  
367
    class Meta:
368
        db_table = 'maps_map_layers'
369
        unique_together = ('map_cell', 'map_layer')
370

  
371
    def get_as_serialized_object(self):
372
        serialized_options = json.loads(
373
            serializers.serialize('json', [self], use_natural_foreign_keys=True, use_natural_primary_keys=True)
374
        )[0]
375
        del serialized_options['fields']['map_cell']
376
        del serialized_options['pk']
377
        return serialized_options
combo/apps/maps/templates/maps/layer_options_form.html
1
{% extends "combo/manager_base.html" %}
2
{% load i18n %}
3

  
4
{% block appbar %}
5
{% if form.instance.pk %}
6
<h2>{% trans "Edit layer" %}</h2>
7
{% else %}
8
<h2>{% trans "New layer" %}</h2>
9
{% endif %}
10
{% endblock %}
11

  
12
{% block content %}
13

  
14
<form method="post" enctype="multipart/form-data">
15
  {% csrf_token %}
16
  {{ form.as_p }}
17
  <div class="buttons">
18
    <button class="submit-button">{% trans "Save" %}</button>
19
    <a class="cancel" href="{% url 'combo-manager-page-view' pk=form.instance.map_cell.page_id %}">{% trans 'Cancel' %}</a>
20
  </div>
21
</form>
22
{% endblock %}
combo/apps/maps/templates/maps/map_cell_form.html
1
{% extends "combo/cell_form.html" %}
2
{% load i18n %}
3

  
4
{% block cell-form %}
5
{{ form.as_p }}
6
{% with cell.maplayeroptions_set.all as options %}
7
{% if options %}
8
<p><label>{% trans "Layers:" %}</label></p>
9
<div>
10
  <ul class="objects-list list-of-layers">
11
    {% for option in options %}
12
    <li>
13
      <span>{{ option.map_layer }}</span>
14
      <a rel="popup" title="{% trans "Delete" %}" class="link-action-icon delete" href="{% url 'maps-manager-cell-delete-layer' page_pk=page.pk cell_reference=cell.get_reference layeroptions_pk=option.pk %}">{% trans "Delete" %}</a>
15
    </li>
16
    {% endfor %}
17
 </ul>
18
</div>
19
{% endif %}
20
{% endwith %}
21
{% if cell.get_free_layers.exists %}
22
<div class="buttons">
23
    <a rel="popup" href="{% url 'maps-manager-cell-add-layer' page_pk=page.pk cell_reference=cell.get_reference %}">{% trans "Add a layer" %}</a>
24
</div>
25
{% endif %}
26
{% endblock %}
combo/apps/maps/urls.py
18 18

  
19 19
from combo.urls_utils import decorated_includes, manager_required
20 20

  
21
from .manager_views import (ManagerHomeView, LayerAddView,
22
                LayerEditView, LayerDeleteView)
21
from . import manager_views
23 22

  
24 23
from .views import GeojsonView
25 24

  
26 25
maps_manager_urls = [
27
    url('^$', ManagerHomeView.as_view(), name='maps-manager-homepage'),
28
    url('^layers/add/$', LayerAddView.as_view(), name='maps-manager-layer-add'),
29
    url(r'^layers/(?P<slug>[\w-]+)/edit/$', LayerEditView.as_view(),
26
    url('^$', manager_views.ManagerHomeView.as_view(), name='maps-manager-homepage'),
27
    url('^layers/add/$', manager_views.LayerAddView.as_view(), name='maps-manager-layer-add'),
28
    url(r'^layers/(?P<slug>[\w-]+)/edit/$', manager_views.LayerEditView.as_view(),
30 29
        name='maps-manager-layer-edit'),
31
    url(r'^layers/(?P<slug>[\w-]+)/delete/$', LayerDeleteView.as_view(),
30
    url(r'^layers/(?P<slug>[\w-]+)/delete/$', manager_views.LayerDeleteView.as_view(),
32 31
        name='maps-manager-layer-delete'),
32
    url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/add-layer/$',
33
        manager_views.map_cell_add_layer,
34
        name='maps-manager-cell-add-layer'),
35
    url(r'^pages/(?P<page_pk>\d+)/cell/(?P<cell_reference>[\w_-]+)/layer/(?P<layeroptions_pk>\d+)/delete/$',
36
        manager_views.map_cell_delete_layer,
37
        name='maps-manager-cell-delete-layer'),
33 38
]
34 39

  
35 40
urlpatterns = [
combo/data/models.py
411 411
    @classmethod
412 412
    def load_serialized_cells(cls, cells):
413 413
        # load new cells
414
        for index, cell in enumerate(serializers.deserialize('json', json.dumps(cells))):
414
        for cell_data in cells:
415
            model = apps.get_model(cell_data['model'])
416
            cell_data = model.prepare_serialized_data(cell_data)
417
            cell = list(serializers.deserialize('json', json.dumps([cell_data])))[0]
415 418
            cell.save()
416 419
            # will populate cached_* attributes
417 420
            cell.object.save()
418
            cell.object.import_subobjects(cells[index])
421
            cell.object.import_subobjects(cell_data)
419 422

  
420 423
    @classmethod
421 424
    def load_serialized_pages(cls, json_site):
......
774 777
    def get_external_links_data(self):
775 778
        return []
776 779

  
780
    @classmethod
781
    def prepare_serialized_data(cls, cell_data):
782
        return cell_data
783

  
777 784
    def export_subobjects(self):
778 785
        return {}
779 786

  
combo/manager/static/css/combo.manager.css
485 485
	padding-left: 0;
486 486
}
487 487

  
488
ul.objects-list.list-of-links li a.link-action-icon {
488
ul.objects-list.list-of-links li a.link-action-icon,
489
ul.objects-list.list-of-layers li a.link-action-icon {
489 490
	height: 100%;
490 491
	position: absolute;
491 492
	right: 0;
......
499 500
	line-height: 3em;
500 501
}
501 502

  
502
ul.objects-list.list-of-links li a.link-action-icon::before {
503
ul.objects-list.list-of-links li a.link-action-icon::before,
504
ul.objects-list.list-of-layers li a.link-action-icon::before {
503 505
	font-family: FontAwesome;
504 506
	text-indent: 0px;
505 507
	text-align: center;
......
507 509
	width: 100%;
508 510
}
509 511

  
510
ul.objects-list.list-of-links li a.link-action-icon.delete::before {
512
ul.objects-list.list-of-links li a.link-action-icon.delete::before,
513
ul.objects-list.list-of-layers li a.link-action-icon.delete::before {
511 514
	content: "\f057"; /* remove-sign */
512 515
}
513 516

  
514
ul.objects-list.list-of-links li a.link-action-icon.edit {
517
ul.objects-list.list-of-links li a.link-action-icon.edit,
518
ul.objects-list.list-of-layers li a.link-action-icon.edit {
515 519
	right: 3em;
516 520
}
517 521

  
518
ul.objects-list.list-of-links li a.link-action-icon.edit::before {
522
ul.objects-list.list-of-links li a.link-action-icon.edit::before,
523
ul.objects-list.list-of-layers li a.link-action-icon.edit::before {
519 524
	content: "\f044";
520 525
}
tests/test_import_export.py
16 16

  
17 17
from combo.apps.assets.models import Asset
18 18
from combo.apps.gallery.models import Image, GalleryCell
19
from combo.apps.maps.models import MapLayer, Map
19
from combo.apps.maps.models import MapLayer, Map, MapLayerOptions
20 20
from combo.apps.pwa.models import PwaSettings, PwaNavigationEntry
21 21
from combo.data.models import Page, TextCell
22 22
from combo.data.utils import export_site, import_site, MissingGroups
......
123 123
    import_site(data={}, if_empty=True)
124 124
    assert MapLayer.objects.count() == 2
125 125

  
126

  
126 127
def test_import_export_map_cells(app, some_data, some_map_layers):
127 128
    page = Page.objects.get(slug='one')
128 129
    cell = Map(page=page, order=0, placeholder='content')
129 130
    cell.save()
130
    cell.layers.add(MapLayer.objects.get(slug='foo'))
131
    cell.save()
131
    MapLayerOptions.objects.create(map_cell=cell, map_layer=MapLayer.objects.get(slug='foo'))
132 132
    site_export = get_output_of_command('export_site')
133 133
    import_site(data={}, clean=True)
134 134
    assert Map.objects.count() == 0
135
    assert MapLayer.objects.count() == 0
135 136

  
136
    import_site(data=json.loads(site_export), clean=True)
137
    site_data = json.loads(site_export)
138
    import_site(data=site_data, clean=True)
137 139
    assert Map.objects.count() == 1
140
    assert MapLayer.objects.filter(slug='foo').exists() is True
138 141
    assert Map.objects.all()[0].layers.all()[0].slug == 'foo'
139 142

  
143
    # test old export format
144
    import_site(data={}, clean=True)
145
    assert Map.objects.count() == 0
146
    assert MapLayer.objects.count() == 0
147

  
148
    del site_data['pages'][0]['cells'][0]['layers']
149
    site_data['pages'][0]['cells'][0]['fields']['layers'] = [['foo']]
150
    import_site(data=site_data, clean=True)
151
    assert Map.objects.count() == 1
152
    assert MapLayer.objects.filter(slug='foo').exists() is True
153
    assert Map.objects.all()[0].layers.all()[0].slug == 'foo'
154

  
155

  
140 156
def test_group_restrictions_import_export(app, some_data):
141 157
    group = Group(name='A Group')
142 158
    group.save()
tests/test_maps_cells.py
9 9
from django.contrib.auth.models import Group
10 10

  
11 11
from combo.data.models import Page
12
from combo.apps.maps.models import MapLayer, Map
12
from combo.apps.maps.models import MapLayer, Map, MapLayerOptions
13 13

  
14 14
from .test_manager import login
15 15

  
......
123 123
    page.save()
124 124
    cell = Map(page=page, placeholder='content', order=0, title='Map with points')
125 125
    cell.save()
126
    cell.layers.add(layer)
126
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
127 127
    context = {'request': RequestFactory().get('/')}
128 128
    rendered = cell.render(context)
129 129
    assert 'data-init-zoom="13"' in rendered
......
152 152
    cell = Map(page=page, placeholder='content', order=0,
153 153
                   title='Map with points')
154 154
    cell.save()
155
    cell.layers.add(layer)
155
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
156 156
    app.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id}), status=403)
157 157

  
158 158
def test_get_geojson_on_non_publik_cell(app, layer):
......
161 161
    cell = Map(page=page, placeholder='content', order=0, public=False,
162 162
                   title='Map with points')
163 163
    cell.save()
164
    cell.layers.add(layer)
164
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
165 165
    app.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id}), status=403)
166 166

  
167 167
def test_geojson_on_restricted_cell(app, layer, user):
......
171 171
    cell = Map(page=page, placeholder='content', order=0, public=False)
172 172
    cell.title = 'Map with points'
173 173
    cell.save()
174
    cell.layers.add(layer)
174
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
175 175
    cell.groups.add(group)
176 176
    login(app)
177 177
    app.get(reverse('mapcell-geojson', kwargs={'cell_id': cell.id}), status=403)
......
193 193
    cell.save()
194 194
    layer.geojson_url = 'http://example.org/geojson?t1'
195 195
    layer.save()
196
    cell.layers.add(layer)
196
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
197 197

  
198 198
    # check cache duration
199 199
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
......
307 307
    layer2.icon = 'fa-bicycle'
308 308
    layer2.icon_colour = '0000FF'
309 309
    layer2.save()
310
    cell.layers.add(layer2)
310
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer2)
311 311

  
312 312
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
313 313
        requests_get.return_value = mock.Mock(
......
338 338
    cell.title = 'Map'
339 339
    cell.save()
340 340
    layer.save()
341
    cell.layers.add(layer)
341
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
342 342

  
343 343
    with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get:
344 344
        layer.geojson_url = 'http://example.org/geojson?t1'
......
397 397
def test_duplicate(layer):
398 398
    page = Page.objects.create(title='xxx', slug='new', template_name='standard')
399 399
    cell = Map.objects.create(page=page, placeholder='content', order=0, public=True, title='Map')
400
    layer.save()
401
    cell.layers.add(layer)
400
    MapLayerOptions.objects.create(map_cell=cell, map_layer=layer)
402 401

  
403 402
    new_cell = cell.duplicate()
404 403
    assert list(new_cell.layers.all()) == [layer]
tests/test_maps_manager.py
3 3
import pytest
4 4
import mock
5 5

  
6
from django.contrib.auth.models import User
7

  
8 6
from combo.apps.maps.models import Map
9 7
from combo.apps.maps.models import MapLayer
8
from combo.apps.maps.models import MapLayerOptions
10 9
from combo.data.models import Page
11 10

  
12 11
pytestmark = pytest.mark.django_db
......
124 123
        assert item['properties']['layer']['label'] == 'Test'
125 124
        assert item['properties']['layer']['colour'] == '#FFFFFF'
126 125
        assert item['properties']['layer']['icon_colour'] == '#FFFFFF'
126

  
127

  
128
def test_add_delete_layer(app, admin_user):
129
    layer = MapLayer.objects.create(
130
        label='bicycles',
131
        geojson_url='http://example.org/geojson',
132
    )
133
    page = Page.objects.create(title='One', slug='one', template_name='standard')
134
    cell = Map.objects.create(page=page, placeholder='content', order=0, public=True, title='Map')
135
    app = login(app)
136
    resp = app.get('/manage/pages/%s/' % page.pk)
137

  
138
    assert list(cell.get_free_layers()) == [layer]
139
    resp = resp.click(href='.*/add-layer/$')
140
    resp.forms[0]['map_layer'] = layer.pk
141
    resp = resp.forms[0].submit()
142
    assert resp.status_int == 302
143
    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
144
    assert MapLayerOptions.objects.count() == 1
145
    options = MapLayerOptions.objects.get()
146
    assert options.map_cell == cell
147
    assert options.map_layer == layer
148

  
149
    resp = resp.follow()
150
    assert list(cell.get_free_layers()) == []
151
    assert '/add-layer/$' not in resp.text
152
    resp = resp.click(href='.*/layer/%s/delete/$' % options.pk)
153
    resp = resp.forms[0].submit()
154
    assert resp.status_int == 302
155
    assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
156
    assert MapLayerOptions.objects.count() == 0
127
-