diff --git a/combo/apps/maps/models.py b/combo/apps/maps/models.py index 86fed184..82b6825d 100644 --- a/combo/apps/maps/models.py +++ b/combo/apps/maps/models.py @@ -319,3 +319,7 @@ class Map(CellBase): ctx['group_markers'] = self.group_markers ctx['marker_behaviour_onclick'] = self.marker_behaviour_onclick return ctx + + def duplicate_m2m(self, new_cell): + # set layers + new_cell.layers.set(self.layers.all()) diff --git a/combo/data/models.py b/combo/data/models.py index 728f0faf..4d086c81 100644 --- a/combo/data/models.py +++ b/combo/data/models.py @@ -436,6 +436,27 @@ class Page(models.Model): cells = CellBase.get_cells(page_id=self.id) return max([self.last_update_timestamp] + [x.last_update_timestamp for x in cells]) + def duplicate(self): + # clone current page + new_page = copy.deepcopy(self) + new_page.pk = None + # set title + new_page.title = _('Copy of %s') % self.title + # reset snapshot + new_page.snapshot = None + # set order + new_page.order = self.order + 1 + # store new page + new_page.save() + + # set groups + new_page.groups.set(self.groups.all()) + + for cell in self.get_cells(): + cell.duplicate(page_target=new_page) + + return new_page + class PageSnapshot(models.Model): page = models.ForeignKey(Page, on_delete=models.SET_NULL, null=True) @@ -737,6 +758,24 @@ class CellBase(six.with_metaclass(CellMeta, models.Model)): def import_subobjects(self, cell_json): pass + def duplicate(self, page_target=None): + # clone current cell + new_cell = copy.deepcopy(self) + new_cell.pk = None + # set page + new_cell.page = page_target or self.page + # store new cell + new_cell.save() + + # set groups + new_cell.groups.set(self.groups.all()) + + if hasattr(self, 'duplicate_m2m'): + self.duplicate_m2m(new_cell) + + return new_cell + + @register_cell_class class TextCell(CellBase): text = RichTextField(_('Text'), blank=True, null=True) diff --git a/combo/manager/templates/combo/page_view.html b/combo/manager/templates/combo/page_view.html index 87e7bbe1..fc8dbec2 100644 --- a/combo/manager/templates/combo/page_view.html +++ b/combo/manager/templates/combo/page_view.html @@ -12,6 +12,7 @@
  • {% trans 'history' %}
  • {% trans 'export' %}
  • {% trans 'add a child page' %}
  • +
  • {% trans 'Duplicate' %}
  • {% trans 'delete' %}
  • diff --git a/combo/manager/urls.py b/combo/manager/urls.py index a99f99ec..b56f79be 100644 --- a/combo/manager/urls.py +++ b/combo/manager/urls.py @@ -54,6 +54,8 @@ urlpatterns = [ url(r'^pages/(?P\w+)/export$', views.page_export, name='combo-manager-page-export'), url(r'^pages/(?P\w+)/add/$', views.page_add_child, name='combo-manager-page-add-child'), + url(r'^pages/(?P\w+)/duplicate$', views.page_duplicate, + name='combo-manager-page-duplicate'), url(r'^pages/(?P\w+)/history$', views.page_history, name='combo-manager-page-history'), url(r'^pages/(?P\w+)/history/(?P\w+)/$', views.snapshot_restore, diff --git a/combo/manager/views.py b/combo/manager/views.py index bad9199d..c2f375c0 100644 --- a/combo/manager/views.py +++ b/combo/manager/views.py @@ -325,6 +325,19 @@ class PageExportView(DetailView): page_export = PageExportView.as_view() +class PageDuplicateView(RedirectView): + permanent = False + + def get_redirect_url(self, pk): + page = Page.objects.get(pk=pk) + new_page = page.duplicate() + messages.info(self.request, _('Page %s has been duplicated.') % page.title) + return reverse('combo-manager-page-view', kwargs={'pk': new_page.pk}) + + +page_duplicate = PageDuplicateView.as_view() + + class PageHistoryView(ListView): model = PageSnapshot template_name = 'combo/page_history.html' diff --git a/tests/test_manager.py b/tests/test_manager.py index 7316612a..1860de32 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -440,6 +440,19 @@ def test_site_export_import(app, admin_user): resp = resp.form.submit() assert 'File is not in the expected JSON format.' in resp.text + +def test_duplicate_page(app, admin_user): + page = Page.objects.create(title='One', slug='one', template_name='standard') + TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=0) + + app = login(app) + resp = app.get('/manage/pages/%s/' % page.pk) + resp = resp.click('Duplicate') + new_page = Page.objects.latest('pk') + assert resp.status_int == 302 + assert resp.location.endswith('/manage/pages/%s/' % new_page.pk) + + def test_add_edit_cell(app, admin_user): Page.objects.all().delete() page = Page(title='One', slug='one', template_name='standard') diff --git a/tests/test_maps_cells.py b/tests/test_maps_cells.py index d74570a1..368b010a 100644 --- a/tests/test_maps_cells.py +++ b/tests/test_maps_cells.py @@ -392,3 +392,13 @@ def test_get_geojson_properties(app, layer, user): features = json.loads(resp.text)['features'] assert len(features[0]['properties']['display_fields']) == 1 assert features[0]['properties']['layer']['properties'] == ['id'] + + +def test_duplicate(layer): + page = Page.objects.create(title='xxx', slug='new', template_name='standard') + cell = Map.objects.create(page=page, placeholder='content', order=0, public=True, title='Map') + layer.save() + cell.layers.add(layer) + + new_cell = cell.duplicate() + assert list(new_cell.layers.all()) == [layer] diff --git a/tests/test_pages.py b/tests/test_pages.py index aa701bd7..77020e4f 100644 --- a/tests/test_pages.py +++ b/tests/test_pages.py @@ -9,7 +9,7 @@ from django.test import override_settings from django.test.client import RequestFactory from django.utils.six import StringIO from django.utils.timezone import now -from combo.data.models import Page, CellBase, TextCell, LinkCell +from combo.data.models import Page, PageSnapshot, CellBase, TextCell, LinkCell from combo.data.management.commands.import_site import Command as ImportSiteCommand from combo.data.management.commands.export_site import Command as ExportSiteCommand from combo.manager.forms import PageVisibilityForm @@ -192,6 +192,52 @@ def test_import_export_pages_with_links(): assert CellBase.get_cells(page_id=new_page_1.id)[0].link_page_id == new_page_2.id assert CellBase.get_cells(page_id=new_page_2.id)[0].link_page_id == new_page_1.id + +def test_duplicate_page(): + group1 = Group.objects.create(name='foobar') + group2 = Group.objects.create(name='fooblah') + + page = Page.objects.create( + title='foo', + slug='foo', + description="Foo's page") + page.groups.set([group1, group2]) + snapshot = PageSnapshot.objects.create(page=page) + page.snapshot = snapshot + page.save() + + cell1 = TextCell.objects.create(page=page, text='foo1', order=0, placeholder='content') + cell1.groups.set([group1, group2]) + cell2 = TextCell.objects.create(page=page, text='foo2', order=1, placeholder='content') + + new_page = page.duplicate() + assert new_page.pk != page.pk + assert new_page.title == 'Copy of foo' + assert new_page.slug == page.slug + assert new_page.description == page.description + assert new_page.parent is None + assert new_page.snapshot is None + assert list(new_page.groups.all()) == [group1, group2] + assert len(new_page.get_cells()) == 2 + + new_cell1 = TextCell.objects.get(page=new_page, text='foo1') + new_cell2 = TextCell.objects.get(page=new_page, text='foo2') + assert new_cell1.pk != cell1.pk + assert new_cell1.text == cell1.text + assert new_cell1.placeholder == cell1.placeholder + assert list(new_cell1.groups.all()) == [group1, group2] + assert new_cell2.pk != cell2.pk + assert new_cell2.text == cell2.text + assert new_cell2.placeholder == cell2.placeholder + assert list(new_cell2.groups.all()) == [] + + parent = Page.objects.create() + page.parent = parent + page.save() + new_page = page.duplicate() + assert new_page.parent == parent + + def test_next_previous(): Page.objects.all().delete() page = Page()