From 9299952c581d5ed5bfcd3ff9431cf7d7e739e3b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 27 Aug 2020 15:30:07 +0200
Subject: [PATCH 3/3] manager: create page from model (#10117)

---
 combo/data/models.py   |  6 ++++-
 combo/manager/forms.py | 46 ++++++++++++++++++++++++++++++--
 combo/manager/views.py | 34 +++++++++---------------
 combo/settings.py      |  1 +
 tests/test_manager.py  | 59 +++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 120 insertions(+), 26 deletions(-)

diff --git a/combo/data/models.py b/combo/data/models.py
index aba23c00..d910536e 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -470,7 +470,7 @@ class Page(models.Model):
     def is_new(self):
         return self.creation_timestamp > timezone.now() - datetime.timedelta(days=7)
 
-    def duplicate(self, title=None):
+    def duplicate(self, title=None, parent=False):
         # clone current page
         new_page = copy.deepcopy(self)
         new_page.pk = None
@@ -484,6 +484,10 @@ class Page(models.Model):
         new_page.order = self.order + 1
         # exclude from navigation
         new_page.exclude_from_navigation = True
+        # set parent
+        if parent is not False:
+            # it can be None
+            new_page.parent = parent
         # store new page
         new_page.save()
 
diff --git a/combo/manager/forms.py b/combo/manager/forms.py
index bb8c0c26..02651c2f 100644
--- a/combo/manager/forms.py
+++ b/combo/manager/forms.py
@@ -22,6 +22,7 @@ from django.template.loader import get_template, TemplateDoesNotExist
 from django.utils.translation import ugettext_lazy as _
 
 from combo.data.models import Page
+from combo.data.models import ParentContentCell
 
 from .fields import ImageIncludingSvgField
 
@@ -30,7 +31,7 @@ def get_groups_as_choices():
     return [(x.id, x.name) for x in Group.objects.all().order_by('name')]
 
 
-def get_template_name_choices():
+def get_template_name_choices(with_models=False):
     def template_exists(template):
         try:
             get_template(settings.COMBO_PUBLIC_TEMPLATES[template].get('template'))
@@ -41,6 +42,17 @@ def get_template_name_choices():
     templates = [(x[0], x[1]['name']) for x in settings.COMBO_PUBLIC_TEMPLATES.items()]
     templates = [x for x in templates if template_exists(x[0])]
     templates.sort(key=lambda x: x[1])
+
+    if not with_models:
+        return templates
+
+    try:
+        model_root_page = Page.objects.get(slug=settings.COMBO_PUBLIC_TEMPLATES_ROOT_PAGE)
+    except Page.DoesNotExist:
+        return templates
+
+    templates.extend([('model_%s' % p.slug, p.title) for p in model_root_page.get_descendants()])
+    templates.sort(key=lambda x: x[1])
     return templates
 
 
@@ -58,10 +70,40 @@ class PageAddForm(forms.ModelForm):
         fields = ('title', 'template_name')
 
     def __init__(self, *args, **kwargs):
+        self.request = kwargs.pop('request')
+        self.parent = kwargs.pop('parent', None)
         super().__init__(*args, **kwargs)
-        self.fields['template_name'].widget = forms.Select(choices=get_template_name_choices())
+        self.fields['template_name'].widget = forms.Select(choices=get_template_name_choices(with_models=True))
         self.fields['template_name'].initial = settings.COMBO_DEFAULT_PUBLIC_TEMPLATE
 
+    def save(self, *args, **kwargs):
+        template_name = self.cleaned_data['template_name']
+        if template_name.startswith('model_'):
+            try:
+                model_page = Page.objects.get(slug=template_name[6:])
+            except Page.DoesNotExist:
+                pass
+            else:
+                # model page, create page by duplication
+                self.instance = model_page.duplicate(title=self.cleaned_data['title'], parent=self.parent)
+
+        if self.instance.pk is None:
+            # not a model page, create page from form
+            self.instance = super().save(commit=False, *args, **kwargs)
+            self.instance.parent = self.parent
+            self.instance.save()
+
+            if self.instance.slug != 'index' or self.parent:
+                for placeholder in self.instance.get_placeholders(request=self.request):
+                    if not placeholder.acquired:
+                        continue
+                    ParentContentCell.objects.create(
+                        page=self.instance,
+                        placeholder=placeholder.key,
+                        order=0)
+
+        return self.instance
+
 
 class PageEditTitleForm(forms.ModelForm):
     class Meta:
diff --git a/combo/manager/views.py b/combo/manager/views.py
index 25c06774..efb5b7d3 100644
--- a/combo/manager/views.py
+++ b/combo/manager/views.py
@@ -123,22 +123,17 @@ class PageAddView(CreateView):
     template_name = 'combo/page_add.html'
     form_class = PageAddForm
 
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['request'] = self.request
+        return kwargs
+
     def get_initial(self):
         initial = super(PageAddView, self).get_initial()
-        if not Page.objects.exists(): # first page
+        if not Page.objects.exists():  # first page
             initial['title'] = _('Home')
         return initial
 
-    def form_valid(self, form):
-        response = super(PageAddView, self).form_valid(form)
-        if self.object.slug != 'index' or self.object.parent_id:
-            for placeholder in self.object.get_placeholders(request=self.request):
-                if placeholder.acquired is True:
-                    ParentContentCell(page=self.object,
-                            placeholder=placeholder.key, order=0).save()
-
-        return response
-
     def get_success_url(self):
         return reverse('combo-manager-page-view', kwargs={'pk': self.object.id})
 
@@ -147,20 +142,15 @@ page_add = PageAddView.as_view()
 
 
 class PageAddChildView(PageAddView):
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['parent'] = get_object_or_404(Page, pk=self.kwargs['pk'])
+        return kwargs
+
     def get_initial(self):
-        # it can not be the first page, so don't try to initial the title
+        # it can not be the first page, so don't try to initialize the title
         return super(CreateView, self).get_initial()
 
-    def dispatch(self, request, *args, **kwargs):
-        self.parent = get_object_or_404(Page, pk=kwargs['pk'])
-        return super(PageAddChildView, self).dispatch(request, *args, **kwargs)
-
-    def form_valid(self, form):
-        response = super(PageAddChildView, self).form_valid(form)
-        self.object.parent = self.parent
-        self.object.save()
-        return response
-
 
 page_add_child = PageAddChildView.as_view()
 
diff --git a/combo/settings.py b/combo/settings.py
index 99e40c4c..526b35b3 100644
--- a/combo/settings.py
+++ b/combo/settings.py
@@ -218,6 +218,7 @@ COMBO_PUBLIC_TEMPLATES = {
         '_': (_('Top banner'), _('Middle column')),
     },
 }
+COMBO_PUBLIC_TEMPLATES_ROOT_PAGE = 'modeles'
 
 # extra variables for templates
 TEMPLATE_VARS = {}
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 7468d2fa..e904d967 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -84,6 +84,23 @@ def test_add_page(app, admin_user):
     assert page.template_name == 'standard'  # default template
     assert page.exclude_from_navigation is True
 
+    model_root_page = Page.objects.create(slug='modeles', title='Models')
+    model_a = Page.objects.create(slug='model-a', title='Model A', template_name='two-columns', parent=model_root_page)
+    TextCell.objects.create(page=model_a, placeholder='content', text='Foobar', order=1)
+
+    resp = app.get('/manage/pages/add/')
+    resp.forms[0]['title'].value = 'Page A'
+    resp.forms[0]['template_name'].value = 'model_model-a'
+    resp = resp.forms[0].submit()
+    new_page = Page.objects.latest('pk')
+    assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
+    assert new_page.slug == 'page-a'
+    assert new_page.title == 'Page A'
+    assert new_page.template_name == 'two-columns'
+    assert new_page.parent is None
+    assert TextCell.objects.filter(page=new_page).count() == 1
+    assert ParentContentCell.objects.filter(page=new_page).count() == 0
+
 
 def test_add_second_page(app, admin_user):
     Page.objects.all().delete()
@@ -115,6 +132,23 @@ def test_add_child_page(app, admin_user):
     app.get('/manage/pages/%s/add/' % child.pk, status=200)
     app.get('/manage/pages/42/add/', status=404)
 
+    model_root_page = Page.objects.create(slug='modeles', title='Models')
+    model_a = Page.objects.create(slug='model-a', title='Model A', template_name='two-columns', parent=model_root_page)
+    TextCell.objects.create(page=model_a, placeholder='content', text='Foobar', order=1)
+
+    resp = app.get('/manage/pages/%s/add/' % page.pk)
+    resp.forms[0]['title'].value = 'Page A'
+    resp.forms[0]['template_name'].value = 'model_model-a'
+    resp = resp.forms[0].submit()
+    new_page = Page.objects.latest('pk')
+    assert resp.location.endswith('/manage/pages/%s/' % new_page.pk)
+    assert new_page.slug == 'page-a'
+    assert new_page.title == 'Page A'
+    assert new_page.template_name == 'two-columns'
+    assert new_page.parent == page
+    assert TextCell.objects.filter(page=new_page).count() == 1
+    assert ParentContentCell.objects.filter(page=new_page).count() == 0
+
 
 def test_page_add_forms(settings):
     settings.COMBO_PUBLIC_TEMPLATES = {
@@ -132,10 +166,29 @@ def test_page_add_forms(settings):
         },
     }
     settings.COMBO_DEFAULT_PUBLIC_TEMPLATE = 'one'
-    form = PageAddForm()
+    request = RequestFactory().get('/')
+    form = PageAddForm(request=request)
     assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
     assert form['template_name'].initial == 'one'
 
+    model_root_page = Page.objects.create(slug='modeles', title='Models')
+    form = PageAddForm(request=request)
+    assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
+    model_a = Page.objects.create(slug='model-a', title='Model A', parent=model_root_page)
+    Page.objects.create(slug='model-aa', title='Model AA', parent=model_a)
+    Page.objects.create(slug='model-b', title='Model B', parent=model_root_page)
+    normal_page = Page.objects.create(slug='models', title='Wrong Models')
+    Page.objects.create(slug='model-c', title='Not a model (wrong parent)')
+    Page.objects.create(slug='model-d', title='Again not a model (wrong parent)', parent=normal_page)
+    form = PageAddForm(request=request)
+    assert form.fields['template_name'].widget.choices == [
+        ('two', 'AAA'),
+        ('one', 'BBB'),
+        ('model_model-a', 'Model A'),
+        ('model_model-aa', 'Model AA'),
+        ('model_model-b', 'Model B')
+    ]
+
 
 def test_edit_page(app, admin_user):
     Page.objects.all().delete()
@@ -209,6 +262,10 @@ def test_page_edit_template_form(settings):
         },
     }
     settings.COMBO_DEFAULT_PUBLIC_TEMPLATE = 'one'
+    # model pages ignored
+    model_root_page = Page.objects.create(slug='modeles')
+    Page.objects.create(slug='model-a', title='Model A', parent=model_root_page)
+
     form = PageSelectTemplateForm(instance=page)
     assert form.fields['template_name'].widget.choices == [('two', 'AAA'), ('one', 'BBB')]
     assert form['template_name'].initial == 'two'
-- 
2.28.0

