Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions coderedcms/admin_urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from django.urls import include, path, re_path
from wagtailimportexport import urls as wagtailimportexport_urls
from wagtail.admin import urls as wagtailadmin_urls
from coderedcms.views import import_pages_from_csv_file
from coderedcms.views import import_index, import_pages_from_csv_file


urlpatterns = [
path('codered/import-export/',
import_index, name="import_index"),
path('codered/import-export/import_from_csv/',
import_pages_from_csv_file, name="import_from_csv"),
re_path(r'', include(wagtailadmin_urls)),
re_path(r'', include(wagtailimportexport_urls)),
]
21 changes: 9 additions & 12 deletions coderedcms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"""
import csv
import os
import re
from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models
from django.http import HttpResponse
Expand Down Expand Up @@ -172,16 +172,13 @@ class SearchForm(forms.Form):
def get_page_model_choices():
"""
Returns a list of tuples of all creatable Codered pages
in the format of ("Custom Codered Page", "CustomCoderedPage")
in the format of (app_label:model, "Verbose Name")
"""
from coderedcms.models import get_page_models
return (
(
page.__name__,
re.sub(
r'((?<=[a-z])[A-Z]|(?<!\A)[A-Z](?=[a-z]))',
r' \1',
page.__name__
)
) for page in get_page_models() if page.is_creatable
)

rval = []
for page in get_page_models():
if page.is_creatable:
ct = ContentType.objects.get_for_model(page)
rval.append((f"{ct.app_label}:{ct.model}", ct.name))
return rval
72 changes: 64 additions & 8 deletions coderedcms/importexport.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,73 @@
"""
This code is largely copied or extended upon the now defunct
``wagtailimportexport`` package.

In the future we may want to build a more robust import/exporter for CSV files,
or simply deprecate all of this functionality.

See: https://github.com/torchbox/wagtail-import-export/
"""
import csv
import copy

from django import forms
from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.db import transaction

from django.db import models, transaction
from django.utils.translation import ugettext as _
from modelcluster.models import get_all_child_relations
from wagtail.admin.widgets import AdminPageChooser
from wagtail.core.models import Page

from wagtailimportexport.forms import ImportFromFileForm
from wagtailimportexport.importing import update_page_references

from coderedcms.forms import get_page_model_choices


class ImportPagesFromCSVFileForm(ImportFromFileForm):
class ImportPagesFromCSVFileForm(forms.Form):
"""
Mostly copied from:
https://github.com/torchbox/wagtail-import-export/blob/master/wagtailimportexport/forms.py#L29
with addition of ``page_type``.
"""
page_type = forms.ChoiceField(choices=get_page_model_choices)

file = forms.FileField(label=_("File to import"))

parent_page = forms.ModelChoiceField(
queryset=Page.objects.all(),
widget=AdminPageChooser(can_choose_root=True, show_edit_link=False),
label=_("Destination parent page"),
help_text=_("Imported pages will be created as children of this page.")
)


def update_page_references(model, pages_by_original_id):
"""
Copied from:
https://github.com/torchbox/wagtail-import-export/blob/master/wagtailimportexport/importing.py#L67
"""
for field in model._meta.get_fields():
if isinstance(field, models.ForeignKey) and issubclass(field.related_model, Page):
linked_page_id = getattr(model, field.attname)
try:
# see if the linked page is one of the ones we're importing
linked_page = pages_by_original_id[linked_page_id]
except KeyError:
# any references to pages outside of the import should be left unchanged
continue

# update fk to the linked page's new ID
setattr(model, field.attname, linked_page.id)

# update references within inline child models, including the ParentalKey pointing back
# to the page
for rel in get_all_child_relations(model):
for child in getattr(model, rel.get_accessor_name()).all():
# reset the child model's PK so that it will be inserted as a new record
# rather than updating an existing one
child.pk = None
# update page references on the child model, including the ParentalKey
update_page_references(child, pages_by_original_id)


@transaction.atomic()
def import_pages(import_data, parent_page):
Expand Down Expand Up @@ -69,7 +120,7 @@ def import_pages(import_data, parent_page):
strict_fks=False
)
base_page = pages_by_original_id[specific_page.id]
specific_page.page_ptr = base_page
specific_page.base_page_ptr = base_page
specific_page.__dict__.update(base_page.__dict__)
specific_page.content_type = ContentType.objects.get_for_model(model)
update_page_references(specific_page, pages_by_original_id)
Expand All @@ -80,7 +131,12 @@ def import_pages(import_data, parent_page):

def convert_csv_to_json(csv_file, page_type):
pages_json = {"pages": []}
default_page_data = {"app_label": "website", "content": {"pk": None}, "model": page_type}
app_label, klass = page_type.split(":")
default_page_data = {
"app_label": app_label,
"content": {"pk": None},
"model": klass,
}

pages_csv_dict = csv.DictReader(csv_file)
for row in pages_csv_dict:
Expand Down
11 changes: 0 additions & 11 deletions coderedcms/models/page_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,17 +554,6 @@ def body_preview(self):
preview = body[:200] + "..." if len(body) > 200 else body
return mark_safe(preview)

@property
def page_ptr(self):
"""
Overwrite of `page_ptr` to make it compatible with wagtailimportexport.
"""
return self.base_page_ptr

@page_ptr.setter
def page_ptr(self, value):
self.base_page_ptr = value


class CoderedArticlePage(CoderedWebPage):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
'modelcluster',
'taggit',
'wagtailcache',
'wagtailimportexport',
'wagtailseo',

# Wagtail
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
'modelcluster',
'taggit',
'wagtailcache',
'wagtailimportexport',
'wagtailseo',

# Wagtail
Expand Down
11 changes: 0 additions & 11 deletions coderedcms/templates/wagtailimportexport/export_to_file.html

This file was deleted.

11 changes: 0 additions & 11 deletions coderedcms/templates/wagtailimportexport/import_from_api.html

This file was deleted.

12 changes: 11 additions & 1 deletion coderedcms/templates/wagtailimportexport/import_from_csv.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@
{% include "wagtailadmin/shared/header.html" with title=title_str icon="download" %}

<div class="nice-padding">
<form action="{% url 'import_from_csv' %}" enctype="multipart/form-data" method="POST" novalidate>
<form action="{% url 'import_from_csv' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
<ul class="fields">
{% for field in form %}
{% include "wagtailadmin/shared/field_as_li.html" %}
{% endfor %}
</ul>

<br>
<p>
<b>{% trans "IMPORTANT:" %}</b>
{% trans "CSV file must be in the correct format before importing." %}
<a href="https://docs.coderedcorp.com/wagtail-crx/features/import_export.html">
{% trans "Read the importing guide." %}
</a>
</p>
<br>

<input type="submit" value="{% trans 'Import' %}" class="button">
</form>
</div>
Expand Down
11 changes: 0 additions & 11 deletions coderedcms/templates/wagtailimportexport/import_from_file.html

This file was deleted.

15 changes: 2 additions & 13 deletions coderedcms/templates/wagtailimportexport/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,11 @@
{% load i18n %}
{% block titletag %}{% blocktrans %}Import / export pages{% endblocktrans %}{% endblock %}
{% block content %}
{% trans "Import / export pages" as title_str %}
{% trans "Import pages" as title_str %}
{% include "wagtailadmin/shared/header.html" with title=title_str icon="download" %}

<div class="nice-padding">
<h3>JSON</h3>
<ul>
<li><a href="{% url 'wagtailimportexport_admin:import_from_file' %}">{% trans "Import from JSON file" %}</a></li>
<li><a href="{% url 'wagtailimportexport_admin:export_to_file' %}">{% trans "Export to JSON file" %}</a></li>
</ul>
<h3>CSV</h3>
<ul>
<li><a href="{% url 'import_from_csv' %}">{% trans "Import from CSV file" %}</a></li>
</ul>
<h3>API</h3>
<ul>
<li><a href="{% url 'wagtailimportexport_admin:import_from_api' %}">{% trans "Import from API" %}</a></li>
</ul>
<p><a href="{% url 'import_from_csv' %}">{% trans "Import from CSV file" %}</a></p>
</div>
{% endblock %}
1 change: 0 additions & 1 deletion coderedcms/tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
'modelcluster',
'taggit',
'wagtailcache',
'wagtailimportexport',
'wagtailseo',

# Wagtail
Expand Down
8 changes: 8 additions & 0 deletions coderedcms/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ def event_get_calendar_events(request):
)


@login_required
def import_index(request):
"""
Landing page to replace wagtailimportexport.
"""
return render(request, 'wagtailimportexport/index.html')


@login_required
def import_pages_from_csv_file(request):
"""
Expand Down
15 changes: 15 additions & 0 deletions coderedcms/wagtail_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.urls import reverse
from django.utils.html import format_html, mark_safe
from django.utils.translation import gettext_lazy as _
from wagtail.admin.menu import MenuItem
from wagtail.core import hooks
from wagtail.core.models import UserPagePermissionsProxy, get_page_models
from wagtailcache.cache import clear_cache
Expand Down Expand Up @@ -123,6 +124,20 @@ def serve_document_directly(document, request):
return response


class ImportExportMenuItem(MenuItem):
def is_shown(self, request):
return request.user.is_superuser


@hooks.register('register_settings_menu_item')
def register_import_export_menu_item():
return ImportExportMenuItem(
_('Import'),
reverse('import_index'),
classnames='icon icon-download',
)


class CoderedSubmissionAdmin(SubmissionAdmin):

def __init__(self, parent=None):
Expand Down
27 changes: 18 additions & 9 deletions docs/features/import_export.rst
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
Import/Export
=============

``wagtail-import-export`` is included in the CMS. You can find documentation for it
`here <https://github.com/torchbox/wagtail-import-export>`_. In addition to the JSON
import/export functionality that the package includes, we have added the ability to create
pages by importing CSV files.
CRX includes the ability to import pages from CSV files. This is useful for
example in a store locator, where a list of stores needs to be uploaded
periodically.

To start an import, go to **Settings > Import**

In the CSV each row will be a new page and each column header will correspond to an attribute
of that page. On the import CSV page, you will select where you want the pages to live and what
page type they should be created as. A use case for this functionality would be if your site needs
to add several hundred locations as pages. These locations come from a CSV dump from some report
generating software. Your CSV could look something like this::
generating software. Your CSV could look something like this:

.. code-block:: text

title address latitude longitude
Store 1 123 Street 20.909 -15.32
Store 2 456 Avenue 34.223 87.2331
title , address , latitude , longitude
Store 1 , 123 Street , 20.909 , -15.32
Store 2 , 456 Avenue , 34.223 , 87.2331
...
...

``title``, ``address``, ``latitude``, ``longitude`` are all fields on your Page model that you will
be importing as.

.. note::
.. important::

Your CSV file must be encoded as ASCII or UTF-8.
UTF-8-BOM will cause an error.

.. versionchanged:: 0.24

In version 0.24, the ability to import/export pages from JSON was removed.
The Import/Export link in the side menu was also moved to Settings > Import.
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
'icalendar==4.0.*',
'wagtail==2.16.*',
'wagtail-cache==1.*',
'wagtail-import-export>=0.2,<0.3',
'wagtail-seo==1.*',
],
entry_points={
Expand Down
1 change: 0 additions & 1 deletion tutorial/mysite/mysite/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
'modelcluster',
'taggit',
'wagtailcache',
'wagtailimportexport',
'wagtailseo',

# Wagtail
Expand Down