manager: export page and subpages (#44667)
This commit is contained in:
parent
685ad0376c
commit
95943cf340
|
@ -321,7 +321,7 @@ class Page(models.Model):
|
|||
return None
|
||||
|
||||
@classmethod
|
||||
def get_as_reordered_flat_hierarchy(cls, object_list):
|
||||
def get_as_reordered_flat_hierarchy(cls, object_list, root_page=None):
|
||||
reordered = []
|
||||
parenting = collections.defaultdict(list)
|
||||
for page in object_list:
|
||||
|
@ -329,7 +329,7 @@ class Page(models.Model):
|
|||
def fill_list(object_sublist, level=0, parent=None):
|
||||
for page in object_sublist:
|
||||
parent_id = parent.pk if parent else None
|
||||
if page.parent_id == parent_id:
|
||||
if page.parent_id == parent_id or page == root_page and parent is None:
|
||||
page.level = level
|
||||
reordered.append(page)
|
||||
if page.id in parenting:
|
||||
|
|
|
@ -34,6 +34,10 @@ class PageDuplicateForm(forms.Form):
|
|||
title = forms.CharField(label=_('New title'), max_length=150, required=False)
|
||||
|
||||
|
||||
class PageExportForm(forms.Form):
|
||||
with_subpages = forms.BooleanField(label=_('Also export subpages'), required=False)
|
||||
|
||||
|
||||
class PageEditTitleForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Page
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "combo/manager_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans "Export Page" %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Export" %}</button>
|
||||
<a class="cancel" href="{% url 'combo-manager-page-view' pk=page.pk %}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -10,7 +10,7 @@
|
|||
<a class="action-see-online" href="{{ object.get_online_url }}">{% trans 'See online' %}</a>
|
||||
<ul class="extra-actions-menu">
|
||||
<li><a class="action-history" href="{% url 'combo-manager-page-history' pk=object.id %}">{% trans 'History' %}</a></li>
|
||||
<li><a class="action-export" download href="{% url 'combo-manager-page-export' pk=object.id %}">{% trans 'Export' %}</a></li>
|
||||
<li><a {% if page_has_subpages %}rel="popup" data-autoclose-dialog="true" {% endif %}class="action-export" href="{% url 'combo-manager-page-export' pk=object.id %}">{% trans 'Export' %}</a></li>
|
||||
<li><a class="action-add-child" rel="popup" href="{% url 'combo-manager-page-add-child' pk=object.id %}">{% trans 'Add a child page' %}</a></li>
|
||||
<li><a rel="popup" class="action-duplicate" href="{% url 'combo-manager-page-duplicate' pk=object.id %}">{% trans 'Duplicate' %}</a></li>
|
||||
<li><a class="action-delete" rel="popup" href="{% url 'combo-manager-page-delete' pk=object.id %}">{% trans 'Delete' %}</a></li>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import hashlib
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
from operator import attrgetter
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -43,7 +43,7 @@ from combo import plugins
|
|||
from .forms import (PageEditTitleForm, PageVisibilityForm, SiteImportForm,
|
||||
PageEditRedirectionForm, PageSelectTemplateForm, PageEditSlugForm,
|
||||
PageEditPictureForm, PageEditIncludeInNavigationForm,
|
||||
PageEditDescriptionForm, CellVisibilityForm, PageDuplicateForm)
|
||||
PageEditDescriptionForm, CellVisibilityForm, PageDuplicateForm, PageExportForm)
|
||||
|
||||
|
||||
class HomepageView(ListView):
|
||||
|
@ -287,6 +287,7 @@ class PageView(DetailView):
|
|||
del cell_type_groups['data']
|
||||
context['cell_type_groups'] = list(cell_type_groups.items())
|
||||
context['cell_type_groups'].sort(key=lambda x: x[0])
|
||||
context['page_has_subpages'] = self.object.get_children().exists()
|
||||
|
||||
cells = CellBase.get_cells(page=self.object, prefetch_validity_info=True)
|
||||
self.object.prefetched_cells = cells
|
||||
|
@ -369,17 +370,49 @@ class PageDeleteView(DeleteView):
|
|||
def get_success_url(self):
|
||||
return reverse('combo-manager-homepage')
|
||||
|
||||
|
||||
page_delete = PageDeleteView.as_view()
|
||||
|
||||
|
||||
class PageExportView(DetailView):
|
||||
model = Page
|
||||
class PageExportView(FormView):
|
||||
form_class = PageExportForm
|
||||
template_name = 'combo/page_export.html'
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
def get_object(self):
|
||||
return get_object_or_404(Page, pk=self.kwargs['pk'])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page'] = self.get_object()
|
||||
return context
|
||||
|
||||
def export(self, with_children=False):
|
||||
instance = self.get_object()
|
||||
response = HttpResponse(content_type='application/json')
|
||||
json.dump({'pages': [self.object.get_serialized_page()]}, response, indent=2)
|
||||
today = datetime.date.today()
|
||||
if with_children:
|
||||
pages = instance.get_descendants_and_me()
|
||||
ordered_pages = Page.get_as_reordered_flat_hierarchy(pages, root_page=instance)
|
||||
filename_part = '{}_and_subpages'.format(instance.slug)
|
||||
else:
|
||||
ordered_pages = [instance]
|
||||
filename_part = instance.slug
|
||||
response['Content-Disposition'] = 'attachment; filename="export_page_{}_{}.json"'.format(
|
||||
filename_part, today.strftime('%Y%m%d')
|
||||
)
|
||||
json.dump({'pages': [p.get_serialized_page() for p in ordered_pages]}, response, indent=2)
|
||||
return response
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
subpages = self.get_object().get_children()
|
||||
if not subpages.exists():
|
||||
return self.export()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
return self.export(with_children=form.cleaned_data['with_subpages'])
|
||||
|
||||
|
||||
page_export = PageExportView.as_view()
|
||||
|
||||
|
||||
|
|
|
@ -406,7 +406,7 @@ def test_edit_page_num_queries(settings, app, admin_user):
|
|||
app.get('/manage/pages/%s/' % page.pk) # load once to populate caches
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
app.get('/manage/pages/%s/' % page.pk)
|
||||
assert len(ctx.captured_queries) == 31
|
||||
assert len(ctx.captured_queries) == 32
|
||||
|
||||
|
||||
def test_delete_page(app, admin_user):
|
||||
|
@ -421,6 +421,7 @@ def test_delete_page(app, admin_user):
|
|||
assert resp.location.endswith('/manage/')
|
||||
assert Page.objects.count() == 0
|
||||
|
||||
|
||||
def test_delete_page_keep_child(app, admin_user):
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='One', slug='one', template_name='standard')
|
||||
|
@ -440,6 +441,7 @@ def test_delete_page_keep_child(app, admin_user):
|
|||
assert Page.objects.count() == 1
|
||||
assert Page.objects.get(id=page2.id) == page2
|
||||
|
||||
|
||||
def test_delete_page_and_subpage(app, admin_user):
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='One', slug='one', template_name='standard')
|
||||
|
@ -458,6 +460,7 @@ def test_delete_page_and_subpage(app, admin_user):
|
|||
assert resp.location.endswith('/manage/')
|
||||
assert Page.objects.count() == 0
|
||||
|
||||
|
||||
def test_page_reorder(app, admin_user):
|
||||
Page.objects.all().delete()
|
||||
page1 = Page(title='One', slug='one', parent=None, order=0, template_name='standard')
|
||||
|
@ -531,17 +534,66 @@ def test_page_reorder(app, admin_user):
|
|||
assert Page.objects.get(id=page3.id).parent_id == page1.id
|
||||
|
||||
|
||||
def test_export_page(app, admin_user):
|
||||
def test_export_page(freezer, app, admin_user):
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='One', slug='one', template_name='standard')
|
||||
page.save()
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pages/%s/' % page.id)
|
||||
freezer.move_to('2020-07-16')
|
||||
resp = resp.click('Export')
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
assert resp.headers['content-disposition'] == 'attachment; filename="export_page_one_20200716.json"'
|
||||
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
|
||||
|
||||
|
||||
def test_export_page_without_child(freezer, app, admin_user):
|
||||
page = Page.objects.create(title='One', slug='one', template_name='standard')
|
||||
Page.objects.create(title='Two', slug='two', parent=page, template_name='standard')
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/pages/%s/' % page.pk)
|
||||
freezer.move_to('2020-07-16')
|
||||
resp = resp.click('Export')
|
||||
resp.form['with_subpages'].value = False
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
assert resp.headers['content-disposition'] == 'attachment; filename="export_page_one_20200716.json"'
|
||||
assert len(resp.json['pages']) == 1
|
||||
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
|
||||
|
||||
|
||||
def test_export_page_and_child(freezer, app, admin_user):
|
||||
page1 = Page.objects.create(title='One', slug='one', template_name='standard')
|
||||
page2 = Page.objects.create(title='Two', slug='two', parent=page1, template_name='standard')
|
||||
Page.objects.create(title='Three', slug='three', parent=page2, template_name='standard')
|
||||
Page.objects.create(title='Four', slug='four', parent=page1, template_name='standard')
|
||||
freezer.move_to('2020-07-16')
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/pages/%s/' % page1.pk)
|
||||
resp = resp.click('Export')
|
||||
resp.form['with_subpages'].value = True
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
assert resp.headers['content-disposition'] == 'attachment; filename="export_page_one_and_subpages_20200716.json"'
|
||||
assert len(resp.json['pages']) == 4
|
||||
assert resp.json['pages'][0].get('fields').get('slug') == 'one'
|
||||
assert resp.json['pages'][1].get('fields').get('slug') == 'two'
|
||||
assert resp.json['pages'][2].get('fields').get('slug') == 'three'
|
||||
assert resp.json['pages'][3].get('fields').get('slug') == 'four'
|
||||
|
||||
resp = app.get('/manage/pages/%s/' % page2.pk)
|
||||
resp = resp.click('Export')
|
||||
resp.form['with_subpages'].value = True
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
assert resp.headers['content-disposition'] == 'attachment; filename="export_page_two_and_subpages_20200716.json"'
|
||||
assert len(resp.json['pages']) == 2
|
||||
assert resp.json['pages'][0].get('fields').get('slug') == 'two'
|
||||
assert resp.json['pages'][1].get('fields').get('slug') == 'three'
|
||||
|
||||
|
||||
def test_export_page_order():
|
||||
Page.objects.all().delete()
|
||||
page1 = Page.objects.create(title='One', slug='one', template_name='standard')
|
||||
|
|
Loading…
Reference in New Issue