PKkTGB demo/apps.pyfrom django.apps import AppConfig from actstream import registry from django.apps import apps get_model = apps.get_model class DemoConfig(AppConfig): name = 'demo' def ready(self): print('!!!!!!!!!!!!!!!!!!!!') registry.register(get_model('auth.User')) PK UG'4 demo/settings.py""" Django settings for demo project. For more information on this file, see https://docs.djangoproject.com/en/1.7/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.7/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '0j_#xpc8ir6o!@od4_ogc7_*b@yw2k7*h^j(x@+t1ufw&v+ajv' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True TEMPLATE_DEBUG = True ALLOWED_HOSTS = [] SITE_ID = 1 # Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.sites', 'django_comments', 'django_comments_xtd', 'compressor', 'taggit', 'modelcluster', 'wagtail.wagtailcore', 'wagtail.wagtailadmin', 'wagtail.wagtaildocs', 'wagtail.wagtailsnippets', 'wagtail.wagtailusers', 'wagtail.wagtailimages', 'wagtail.wagtailembeds', 'wagtail.wagtailsearch', 'wagtail.wagtailredirects', 'wagtail.wagtailforms', 'wagtail.wagtailsites', 'blog', ) MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wagtail.wagtailcore.middleware.SiteMiddleware', 'wagtail.wagtailredirects.middleware.RedirectMiddleware', ) ROOT_URLCONF = 'demo.urls' WSGI_APPLICATION = 'demo.wsgi.application' # Database # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Internationalization # https://docs.djangoproject.com/en/1.7/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'compressor.finders.CompressorFinder', ) WAGTAIL_SITE_NAME = 'Blog Demo' from django.conf import global_settings TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( 'django.core.context_processors.request', ) COMPRESS_PRECOMPILERS = ( ('text/x-scss', 'django_libsass.SassCompiler'), ) MEDIA_URL = '/media/' COMMENTS_APP = "django_comments_xtd" COMMENTS_XTD_MAX_THREAD_LEVEL = 2 PKw/GFQ$$ demo/urls.pyfrom django.conf.urls import patterns, include, url from django.conf.urls.static import static from django.conf import settings from django.contrib import admin from django.views.generic.base import RedirectView from wagtail.wagtailcore import urls as wagtail_urls from wagtail.wagtailadmin import urls as wagtailadmin_urls from wagtail.wagtaildocs import urls as wagtaildocs_urls from wagtail.wagtailsearch.urls import frontend as wagtailsearch_frontend_urls from wagtail.wagtailsearch.signal_handlers import register_signal_handlers as wagtailsearch_register_signal_handlers import os wagtailsearch_register_signal_handlers() urlpatterns = patterns('', url(r'^blog/', include('blog.urls', namespace="blog")), url(r'^django-admin/', include(admin.site.urls)), url(r'^admin/', include(wagtailadmin_urls)), url(r'^comments/', include('django_comments_xtd.urls')), url(r'', include(wagtail_urls)), ) if settings.DEBUG: from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += staticfiles_urlpatterns() urlpatterns += static(settings.MEDIA_URL + 'images/', document_root=os.path.join(settings.MEDIA_ROOT, 'images')) urlpatterns += patterns('', (r'^favicon\.ico$', RedirectView.as_view(url=settings.STATIC_URL + 'demo/images/favicon.ico')) ) PK0F demo/wsgi.py""" WSGI config for demo project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ """ import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo.settings") from django.core.wsgi import get_wsgi_application application = get_wsgi_application() PK UGdemo/__init__.pyPKЙZG휱8!8!blog/models.pyfrom django.core.exceptions import ValidationError from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.conf import settings from django.contrib.auth import get_user_model from django.db import models from django.db.models import Count, Q from django.shortcuts import get_object_or_404 from django.template.defaultfilters import slugify from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailcore.models import Page from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, InlinePanel, MultiFieldPanel, FieldRowPanel) from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsnippets.models import register_snippet from wagtail.wagtailsearch import index from taggit.models import TaggedItemBase, Tag from modelcluster.tags import ClusterTaggableManager from modelcluster.fields import ParentalKey import datetime COMMENTS_APP = getattr(settings, 'COMMENTS_APP', None) def get_blog_context(context): """ Get context data useful on all blog related pages """ context['authors'] = get_user_model().objects.filter( owned_pages__live=True, owned_pages__content_type__model='blogpage' ).annotate(Count('owned_pages')).order_by('-owned_pages__count') context['all_categories'] = BlogCategory.objects.all() context['root_categories'] = BlogCategory.objects.filter( parent=None, ).prefetch_related( 'children', ).annotate( blog_count=Count('blogpage'), ) return context class BlogIndexPage(Page): @property def blogs(self): # Get list of blog pages that are descendants of this page blogs = BlogPage.objects.descendant_of(self).live() blogs = blogs.order_by( '-date' ).select_related('owner').prefetch_related( 'tagged_items__tag', 'categories', 'categories__category', ) return blogs def get_context(self, request, tag=None, category=None, author=None, *args, **kwargs): context = super(BlogIndexPage, self).get_context( request, *args, **kwargs) blogs = self.blogs if tag is None: tag = request.GET.get('tag') if tag: blogs = blogs.filter(tags__slug=tag) if category is None: # Not coming from category_view in views.py if request.GET.get('category'): category = get_object_or_404( BlogCategory, slug=request.GET.get('category')) if category: if not request.GET.get('category'): category = get_object_or_404(BlogCategory, slug=category) blogs = blogs.filter(categories__category__name=category) if author: if isinstance(author, str): blogs = blogs.filter(owner__username=author) else: blogs = blogs.filter(owner_id=author) # Pagination page = request.GET.get('page') page_size = 10 if hasattr(settings, 'BLOG_PAGINATION_PER_PAGE'): page_size = settings.BLOG_PAGINATION_PER_PAGE if page_size is not None: paginator = Paginator(blogs, page_size) # Show 10 blogs per page try: blogs = paginator.page(page) except PageNotAnInteger: blogs = paginator.page(1) except EmptyPage: blogs = paginator.page(paginator.num_pages) context['blogs'] = blogs context['category'] = category context['tag'] = tag context['author'] = author context['COMMENTS_APP'] = COMMENTS_APP context = get_blog_context(context) return context class Meta: verbose_name = _('Blog index') subpage_types = ['blog.BlogPage'] @register_snippet class BlogCategory(models.Model): name = models.CharField( max_length=80, unique=True, verbose_name=_('Category Name')) slug = models.SlugField(unique=True, max_length=80) parent = models.ForeignKey( 'self', blank=True, null=True, related_name="children", help_text=_( 'Categories, unlike tags, can have a hierarchy. You might have a ' 'Jazz category, and under that have children categories for Bebop' ' and Big Band. Totally optional.') ) description = models.CharField(max_length=500, blank=True) class Meta: ordering = ['name'] verbose_name = _("Blog Category") verbose_name_plural = _("Blog Categories") panels = [ FieldPanel('name'), FieldPanel('parent'), FieldPanel('description'), ] def __str__(self): return self.name def clean(self): if self.parent: parent = self.parent if self.parent == self: raise ValidationError('Parent category cannot be self.') if parent.parent and parent.parent == self: raise ValidationError('Cannot have circular Parents.') def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) return super(BlogCategory, self).save(*args, **kwargs) class BlogCategoryBlogPage(models.Model): category = models.ForeignKey( BlogCategory, related_name="+", verbose_name=_('Category')) page = ParentalKey('BlogPage', related_name='categories') panels = [ FieldPanel('category'), ] class BlogPageTag(TaggedItemBase): content_object = ParentalKey('BlogPage', related_name='tagged_items') @register_snippet class BlogTag(Tag): class Meta: proxy = True LIMIT_AUTHOR_CHOICES = getattr(settings, 'BLOG_LIMIT_AUTHOR_CHOICES_GROUP', None) if LIMIT_AUTHOR_CHOICES: limit_author_choices = {'groups__name': LIMIT_AUTHOR_CHOICES} else: limit_author_choices = {'is_staff': True} class BlogPage(Page): body = RichTextField(verbose_name=_('body')) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) date = models.DateField( _("Post date"), default=datetime.datetime.today, help_text=_("This date may be displayed on the blog post. It is not " "used to schedule posts to go live at a later date.") ) header_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=_('Header image') ) author = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, limit_choices_to=limit_author_choices, verbose_name=_('Author'), on_delete=models.SET_NULL, related_name='author_pages', ) search_fields = Page.search_fields + ( index.SearchField('body'), ) blog_categories = models.ManyToManyField( BlogCategory, through=BlogCategoryBlogPage, blank=True) settings_panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('go_live_at'), FieldPanel('expire_at'), ], classname="label-above"), ], 'Scheduled publishing', classname="publishing"), FieldPanel('date'), FieldPanel('author'), ] def save_revision(self, *args, **kwargs): if not self.author: self.author = self.owner return super(BlogPage, self).save_revision(*args, **kwargs) def get_absolute_url(self): return self.url def get_blog_index(self): # Find closest ancestor which is a blog index return self.get_ancestors().type(BlogIndexPage).last() def get_context(self, request, *args, **kwargs): context = super(BlogPage, self).get_context(request, *args, **kwargs) context['blogs'] = self.get_blog_index().blogindexpage.blogs context = get_blog_context(context) context['COMMENTS_APP'] = COMMENTS_APP return context class Meta: verbose_name = _('Blog page') verbose_name_plural = _('Blog pages') parent_page_types = ['blog.BlogIndexPage'] BlogPage.content_panels = [ FieldPanel('title', classname="full title"), MultiFieldPanel([ FieldPanel('tags'), InlinePanel(BlogPage, 'categories', label=_("Categories")), ], heading="Tags and Categories"), ImageChooserPanel('header_image'), FieldPanel('body', classname="full"), ] PKFJss blog/views.pyfrom django.contrib.syndication.views import Feed from .models import BlogIndexPage, BlogPage, BlogCategory from django.shortcuts import get_object_or_404 def tag_view(request, tag): index = BlogIndexPage.objects.first() return index.serve(request, tag=tag) def category_view(request, category): index = BlogIndexPage.objects.first() return index.serve(request, category=category) def author_view(request, author): index = BlogIndexPage.objects.first() return index.serve(request, author=author) class LatestEntriesFeed(Feed): title = "Blog" link = "/blog/" description = "A Blog" def items(self): return BlogPage.objects.order_by('-date')[:5] def item_title(self, item): return item.title def item_description(self, item): return item.body class LatestCategoryFeed(Feed): description = "A Blog" def title(self, category): return "Blog: " + category.name def link(self, category): return "/blog/category/" + category.slug def get_object(self, request, category): return get_object_or_404(BlogCategory, slug=category) def items(self, obj): return BlogPage.objects.filter( categories__category=obj).order_by('-date')[:5] def item_title(self, item): return item.title def item_description(self, item): return item.body PKgSG7 blog/tests.pyfrom django.test import TestCase import json from django_comments_xtd.models import XtdComment from wagtail.wagtailcore.models import Page from django.contrib.auth.models import User from .models import (BlogPage, BlogTag, BlogPageTag, BlogIndexPage, BlogCategory, BlogCategoryBlogPage) from .management.commands.wordpress_to_wagtail import Command class BlogTests(TestCase): def setUp(self): home = Page.objects.get(slug='home') self.user = User.objects.create_user('test', 'test@test.test', 'pass') self.blog_index = home.add_child(instance=BlogIndexPage( title='Blog Index', slug='blog', search_description="x", owner=self.user)) def test_index(self): url = self.blog_index.url res = self.client.get(url) self.assertEqual(res.status_code, 200) blog_page = self.blog_index.add_child(instance=BlogPage( title='Blog Page', slug='blog_page1', search_description="x", owner=self.user)) url = blog_page.url res = self.client.get(url) self.assertContains(res, "Blog Page") def test_import(self): """ Tests migrate_wordpress command - the command should do the following: 1. create BlogPage objects from a given BlogIndex 2. create category and tag objects as BlogCategory, BlogTag, BlogPageBlogCategory and BlogPageTag objects The test imports from test-data.json which includes one wordpress blog post with 11 tags and 2 categories """ command = Command() command.username = None command.password = None command.should_import_comments = True command.blog_to_migrate = 'just_testing' with open('test-data.json') as test_json: posts = json.load(test_json) command.create_blog_pages(posts, self.blog_index) self.assertEquals(Page.objects.all().count(), 4) self.assertEquals(BlogPage.objects.all().count(), 1) page = BlogPage.objects.get() self.assertEqual(page.title, "My wordpress title") self.assertInHTML("Bold here", page.body) self.assertEqual(page.categories.count(), 2) self.assertEqual(page.tags.count(), 11) self.assertEqual(page.owner.id, 2) self.assertEqual(BlogCategory.objects.all().count(), 2) self.assertEqual(BlogTag.objects.all().count(), 11) self.assertEqual(BlogCategoryBlogPage.objects.all().count(), 2) self.assertEqual(BlogPageTag.objects.all().count(), 11) parent_category = BlogCategory.objects.get(slug="writing-wisdom") child_category = BlogCategory.objects.get(slug="swoon-reads") self.assertEqual(child_category.parent, parent_category) self.assertEqual(child_category.slug, "swoon-reads") self.assertEqual(parent_category.slug, "writing-wisdom") comments = XtdComment.objects.all() self.assertEqual(comments.count(), 2) parent_comment = XtdComment.objects.get(level=0) child_comment = XtdComment.objects.get(level=1) self.assertEqual(parent_comment.id, child_comment.parent_id) PKw/G3, blog/urls.pyfrom django.conf.urls import patterns, url from . import views urlpatterns = patterns('', url(r'^tag/(?P[-\w]+)/', views.tag_view, name="tag"), url(r'^category/(?P[-\w]+)/feed/$', views.LatestCategoryFeed(), name="category_feed"), url(r'^category/(?P[-\w]+)/', views.category_view, name="category"), url(r'^author/(?P[-\w]+)/', views.author_view, name="author"), ) PK ZFblog/__init__.pyPKF~*blog/migrations/0004_auto_20150427_2047.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import wagtail.wagtailcore.fields import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ ('blog', '0003_auto_20150323_2116'), ] operations = [ migrations.AlterModelOptions( name='blogcategory', options={'ordering': ['name'], 'verbose_name_plural': 'Blog Categories', 'verbose_name': 'Blog Category'}, ), migrations.AlterModelOptions( name='blogindexpage', options={'verbose_name': 'Blog index'}, ), migrations.AlterModelOptions( name='blogpage', options={'verbose_name_plural': 'Blog pages', 'verbose_name': 'Blog page'}, ), migrations.AddField( model_name='blogcategory', name='description', field=models.CharField(max_length=500, blank=True), preserve_default=True, ), migrations.AddField( model_name='blogcategory', name='parent', field=models.ForeignKey(blank=True, null=True, to='blog.BlogCategory', help_text='Categories, unlike tags, can have a hierarchy. You might have a Jazz category, and under that have children categories for Bebop and Big Band. Totally optional.'), preserve_default=True, ), migrations.AlterField( model_name='blogcategoryblogpage', name='category', field=models.ForeignKey(verbose_name='Category', related_name='+', to='blog.BlogCategory'), preserve_default=True, ), migrations.AlterField( model_name='blogpage', name='body', field=wagtail.wagtailcore.fields.RichTextField(verbose_name='body'), preserve_default=True, ), migrations.AlterField( model_name='blogpage', name='header_image', field=models.ForeignKey(to='wagtailimages.Image', verbose_name='Header image', blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+'), preserve_default=True, ), ] PKSG8A*blog/migrations/0005_auto_20151019_1121.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models from django.conf import settings import datetime def default_author(apps, schema_editor): BlogPage = apps.get_model('blog', 'BlogPage') for blog in BlogPage.objects.all(): if not blog.author: blog.author = blog.owner blog.save() class Migration(migrations.Migration): dependencies = [ ('taggit', '0002_auto_20150616_2121'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('blog', '0004_auto_20150427_2047'), ] operations = [ migrations.CreateModel( name='BlogTag', fields=[ ], options={ 'proxy': True, }, bases=('taggit.tag',), ), migrations.AddField( model_name='blogpage', name='author', field=models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True, blank=True), ), migrations.AddField( model_name='blogpage', name='blog_categories', field=models.ManyToManyField(to='blog.BlogCategory', blank=True, through='blog.BlogCategoryBlogPage'), ), migrations.AlterField( model_name='blogcategory', name='parent', field=models.ForeignKey(to='blog.BlogCategory', help_text='Categories, unlike tags, can have a hierarchy. You might have a Jazz category, and under that have children categories for Bebop and Big Band. Totally optional.', related_name='children', null=True, blank=True), ), migrations.AlterField( model_name='blogpage', name='date', field=models.DateField(default=datetime.datetime.today, help_text='This date may be displayed on the blog post. It is not used to schedule posts to go live at a later date.', verbose_name='Post date'), ), migrations.RunPython(default_author), ] PK ZFFGGblog/migrations/0001_initial.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import wagtail.wagtailcore.fields import django.db.models.deletion import modelcluster.fields import modelcluster.tags class Migration(migrations.Migration): dependencies = [ ('wagtailcore', '0010_change_page_owner_to_null_on_delete'), ('taggit', '0001_initial'), ('wagtailimages', '0005_make_filter_spec_unique'), ] operations = [ migrations.CreateModel( name='BlogCategory', fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), ('name', models.CharField(unique=True, max_length=80, verbose_name='Category Name')), ('slug', models.SlugField(unique=True, max_length=80)), ], options={ 'ordering': ['name'], }, bases=(models.Model,), ), migrations.CreateModel( name='BlogCategoryBlogPage', fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), ('sort_order', models.IntegerField(null=True, blank=True, editable=False)), ('category', models.ForeignKey(related_name='+', to='blog.BlogCategory')), ], options={ 'abstract': False, 'ordering': ['sort_order'], }, bases=(models.Model,), ), migrations.CreateModel( name='BlogIndexPage', fields=[ ('page_ptr', models.OneToOneField(serialize=False, to='wagtailcore.Page', primary_key=True, auto_created=True, parent_link=True)), ], options={ 'abstract': False, }, bases=('wagtailcore.page',), ), migrations.CreateModel( name='BlogPage', fields=[ ('page_ptr', models.OneToOneField(serialize=False, to='wagtailcore.Page', primary_key=True, auto_created=True, parent_link=True)), ('body', wagtail.wagtailcore.fields.RichTextField()), ('header_image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image', blank=True)), ], options={ 'abstract': False, }, bases=('wagtailcore.page',), ), migrations.CreateModel( name='BlogPageTag', fields=[ ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), ('content_object', modelcluster.fields.ParentalKey(related_name='tagged_items', to='blog.BlogPage')), ('tag', models.ForeignKey(related_name='blog_blogpagetag_items', to='taggit.Tag')), ], options={ 'abstract': False, }, bases=(models.Model,), ), migrations.AddField( model_name='blogpage', name='tags', field=modelcluster.tags.ClusterTaggableManager(help_text='A comma-separated list of tags.', verbose_name='Tags', to='taggit.Tag', blank=True, through='blog.BlogPageTag'), preserve_default=True, ), migrations.AddField( model_name='blogcategoryblogpage', name='page', field=modelcluster.fields.ParentalKey(related_name='categories', to='blog.BlogPage'), preserve_default=True, ), ] PKZFEa&*blog/migrations/0002_auto_20150226_2305.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import datetime class Migration(migrations.Migration): dependencies = [ ('blog', '0001_initial'), ] operations = [ migrations.AlterModelOptions( name='blogcategory', options={'ordering': ['name'], 'verbose_name_plural': 'Blog Categories'}, ), migrations.AddField( model_name='blogpage', name='date', field=models.DateField(verbose_name='Post date', default=datetime.datetime(2015, 2, 26, 23, 5, 30, 771014)), preserve_default=False, ), ] PKwFԷ\*blog/migrations/0003_auto_20150323_2116.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ('blog', '0002_auto_20150226_2305'), ] operations = [ migrations.AlterModelOptions( name='blogcategoryblogpage', options={}, ), migrations.RemoveField( model_name='blogcategoryblogpage', name='sort_order', ), ] PK ZFblog/migrations/__init__.pyPKFq1blog/templates/base.html

Wagtail blog

This base.html template is from wagtail demo. You probably want to override it.

{% block content %} {% endblock %} PKIxF(4blog/templates/blog/blog_post_comments_xtd_link.html{% load wagtailcore_tags comments %} {% get_comment_count for blog as comment_count %}

{{ comment_count }} comment{{ comment_count|pluralize }} {{ comment_count|pluralize:"has,have" }} been posted.

PKSGA A "blog/templates/blog/blog_post.html{% load wagtailcore_tags static %}

{{ blog.title }}

{% if blog.date %}
{{ blog.date|date:"F jS, Y" }} {{ blog.author }}
{% endif %} {% if include_context == "index_page" %}
{{ blog.body|richtext|truncatewords_html:70 }} Read the rest of this entry »
{% else %}
{{ blog.body|richtext }}
{% endif %}
{% if blog.tags.all %} Tags: {% for tag in blog.tags.all %} {{ tag }}{% if not forloop.last %}, {% endif %} {% endfor %} {% endif %}
{% if blog.categories.all %} Posted in {% for cat in blog.categories.all %} {{ cat.category.name }}{% if not forloop.last %}, {% endif %} {% endfor %} | {% endif %}
{% if COMMENTS_APP == 'django_comments_xtd' %} {% include 'blog/blog_post_comments_xtd_link.html' with blog=blog %} {% elif False %} No Comments {% endif %}
PKxF~"blog/templates/blog/blog_page.html{% extends "base.html" %} {% load wagtailcore_tags %} {% block content %} {% include 'blog/blog_post.html' with blog=self %} {% if COMMENTS_APP == 'django_comments_xtd' %} {% include 'blog/blog_post_comments_xtd.html' with blog=self %} {% endif %} {% endblock %} PKxFd[[/blog/templates/blog/blog_post_comments_xtd.html{% load comments %} {% render_comment_list for self %} {% render_comment_form for self %} PKԸZFƲ(blog/templates/blog/blog_index_page.html{% extends "base.html" %} {% load wagtailcore_tags static %} {% block content %} {% if category %}

{{ category }}

{% endif %} {% if tag %}

Posts tagged with '{{ tag }}'

{% endif %} {% for rl in self.related_links.all %}

{{ rl.title }}: {{ rl.link_page }}

{% endfor %} {% if blogs %}
    {% for blog in blogs %}
  • {% include 'blog/blog_post.html' with include_context="index_page" %}
  • {% endfor %}
    {% if blogs.has_next %} {% endif %} {% if blogs.has_previous %} {% endif %}
{% else %}

There are currently no blog posts

{% endif %} {% endblock %} PK4{Fblog/management/__init__.pyPK}/Gfv8v80blog/management/commands/wordpress_to_wagtail.pyfrom django.core.management.base import BaseCommand, CommandError from django.core.files import File from django.contrib.auth import get_user_model User = get_user_model() from django.contrib.auth.models import User from django_comments_xtd.models import XtdComment from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site from django_comments_xtd.models import MaxThreadLevelExceededException from base64 import b64encode from blog.models import BlogPage import urllib.request import os import json import requests from datetime import datetime try: import html except ImportError: # 2.x import HTMLParser html = HTMLParser.HTMLParser() from bs4 import BeautifulSoup from blog.models import (BlogTag, BlogPageTag, BlogIndexPage, BlogCategory, BlogCategoryBlogPage) from wagtail.wagtailimages.models import Image class Command(BaseCommand): """ This is a management command to migrate a Wordpress site to Wagtail. Two arguments should be used - the site to be migrated and the site it is being migrated to. Users will first need to make sure the WP REST API(WP API) plugin is installed on the self-hosted Wordpress site to migrate. Next users will need to create a BlogIndex object in this GUI. This will be used as a parent object for the child blog page objects. """ def add_arguments(self, parser): """have to add this to use args in django 1.8""" parser.add_argument('blog_to_migrate', help="Base url of wordpress instance") parser.add_argument('blog_index', help="Title of blog index page to attach blogs") parser.add_argument('username', default=False, help='Username for basic Auth') parser.add_argument('password', default=False, help='Password for basic Auth') parser.add_argument('--import-comments', action="store_true", help="import Wordpress comments to Django Xtd") def handle(self, *args, **options): """gets data from WordPress site""" if 'username' in options: self.username = options['username'] else: self.username = None if 'password' in options: self.password = options['password'] else: self.password = None self.blog_to_migrate = options['blog_to_migrate'] try: blog_index = BlogIndexPage.objects.get( title__icontains=options['blog_index']) except BlogIndexPage.DoesNotExist: raise CommandError("Have you created an index yet?") if self.blog_to_migrate == "just_testing": with open('test-data.json') as test_json: posts = json.load(test_json) else: posts = self.get_posts_data(self.blog_to_migrate) self.should_import_comments = options.get('import_comments') self.create_blog_pages(posts, blog_index) def convert_html_entities(self, text, *args, **options): """converts html symbols so they show up correctly in wagtail""" return html.unescape(text) def clean_data(self, data): # I have no idea what this junk is garbage = data.split("[")[0] data = data.strip(garbage) for bad_data in ['8db4ac', '\r\n', '\r\n0']: data = data.strip(bad_data) return data def get_posts_data( self, blog, id=None, get_comments=False, *args, **options ): if self.blog_to_migrate == "just_testing": with open('test-data-comments.json') as test_json: return json.load(test_json) self.url = blog headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', } if self.username and self.password: auth = b64encode( str.encode('{}:{}'.format(self.username, self.password))) headers['Authorization'] = 'Basic {}'.format(auth) if self.url.startswith('http://'): base_url = self.url else: base_url = ''.join(('http://', self.url)) posts_url = ''.join((base_url, '/wp-json/posts')) comments_url = ''.join((posts_url, '/%s/comments')) % id if get_comments is True: comments_url = ''.join((posts_url, '/%s/comments')) % id fetched_comments = requests.get(comments_url) comments_data = fetched_comments.text comments_data = self.clean_data(comments_data) return json.loads(comments_data) else: fetched_posts = requests.get(posts_url + '?filter[posts_per_page]=-1', headers=headers) data = fetched_posts.text data = self.clean_data(data) return json.loads(data) def create_images_from_urls_in_content(self, body): """create Image objects and transfer image files to media root""" soup = BeautifulSoup(body, "html5lib") for img in soup.findAll('img'): old_url = img['src'] if 'width' in img: width = img['width'] if 'height' in img: height = img['height'] else: width = 100 height = 100 path, file_ = os.path.split(img['src']) if not img['src']: continue # Blank image try: remote_image = urllib.request.urlretrieve(img['src']) except (urllib.error.HTTPError, urllib.error.URLError, UnicodeEncodeError): print("Unable to import " + img['src']) continue image = Image(title=file_, width=width, height=height) try: image.file.save(file_, File(open(remote_image[0], 'rb'))) image.save() new_url = image.file.url body = body.replace(old_url, new_url) body = self.convert_html_entities(body) except TypeError: print("Unable to import image {}".format(remote_image[0])) return body def create_user(self, author): username = author['username'] first_name = author['first_name'] last_name = author['last_name'] try: user = User.objects.get(username=username) except User.DoesNotExist: user = User.objects.create_user( username=username, first_name=first_name, last_name=last_name) return user def create_comment( self, blog_post_type, blog_post_id, comment_text, date ): new_comment = XtdComment.objects.get_or_create( site_id=self.site_id, content_type=blog_post_type, object_pk=blog_post_id, comment=comment_text, submit_date=date, )[0] return new_comment def lookup_comment_by_wordpress_id(self, comment_id, comments): """ Returns Django comment object with this wordpress id """ for comment in comments: if comment.wordpress_id == comment_id: return comment def import_comments(self, post_id, slug, *args, **options): try: mysite = Site.objects.get_current() self.site_id = mysite.id except Site.DoesNotExist: print('site does not exist') return comments = self.get_posts_data( self.blog_to_migrate, post_id, get_comments=True) imported_comments = [] for comment in comments: try: blog_post = BlogPage.objects.get(slug=slug) blog_post_type = ContentType.objects.get_for_model(blog_post) except BlogPage.DoesNotExist: print('cannot find this blog post') continue comment_text = self.convert_html_entities(comment.get('content')) date = datetime.strptime(comment.get('date'), '%Y-%m-%dT%H:%M:%S') status = comment.get('status') if status != 'approved': continue comment_author = comment.get('author') new_comment = self.create_comment( blog_post_type, blog_post.pk, comment_text, date) new_comment.wordpress_id = comment.get('ID') new_comment.parent_wordpress_id = comment.get('parent') if type(comment_author) is int: pass else: if 'username' in comment_author: user_name = comment['author']['username'] user_url = comment['author']['URL'] try: current_user = User.objects.get(username=user_name) new_comment.user = current_user except User.DoesNotExist: pass new_comment.user_name = user_name new_comment.user_url = user_url new_comment.save() imported_comments.append(new_comment) # Now assign parent comments for comment in imported_comments: if comment.parent_wordpress_id != "0": for sub_comment in imported_comments: if sub_comment.wordpress_id == comment.parent_wordpress_id: comment.parent_id = sub_comment.id try: comment._calculate_thread_data() comment.save() except MaxThreadLevelExceededException: print("Warning, max thread level exceeded on {}".format(comment.id)) break def create_categories_and_tags(self, page, categories): categories_for_blog_entry = [] tags_for_blog_entry = [] for records in categories.values(): if records[0]['taxonomy'] == 'post_tag': for record in records: tag_name = record['name'] tag_slug = record['slug'] new_tag = BlogTag.objects.get_or_create( name=tag_name, slug=tag_slug)[0] tags_for_blog_entry.append(new_tag) if records[0]['taxonomy'] == 'category': for record in records: category_name = record['name'] category_slug = record['slug'] new_category = BlogCategory.objects.get_or_create( name=category_name, slug=category_slug)[0] if record['parent'] is not None: parent_category = BlogCategory.objects.get_or_create( name=record['parent']['name'])[0] parent_category.slug = record['parent']['slug'] parent_category.save() parent = parent_category new_category.parent = parent else: parent = None categories_for_blog_entry.append(new_category) new_category.save() # loop through list of BlogCategory and BlogTag objects and create # BlogCategoryBlogPages(bcbp) for each category and BlogPageTag objects # for each tag for this blog page for category in categories_for_blog_entry: BlogCategoryBlogPage.objects.get_or_create( category=category, page=page)[0] for tag in tags_for_blog_entry: BlogPageTag.objects.get_or_create( tag=tag, content_object=page)[0] def create_blog_pages(self, posts, blog_index, *args, **options): """create Blog post entries from wordpress data""" for post in posts: print(post.get('slug')) post_id = post.get('ID') title = post.get('title') if title: new_title = self.convert_html_entities(title) title = new_title slug = post.get('slug') description = post.get('description') if description: description = self.convert_html_entities(description) body = post.get('content') # get image info from content and create image objects body = self.create_images_from_urls_in_content(body) # author/user data author = post.get('author') user = self.create_user(author) categories = post.get('terms') # format the date date = post.get('date')[:10] try: new_entry = BlogPage.objects.get(slug=slug) new_entry.title = title new_entry.body = body new_entry.owner = user new_entry.save() except BlogPage.DoesNotExist: new_entry = blog_index.add_child(instance=BlogPage( title=title, slug=slug, search_description="description", date=date, body=body, owner=user)) featured_image = post.get('featured_image') if featured_image is not None: title = post['featured_image']['title'] source = post['featured_image']['source'] path, file_ = os.path.split(source) source = source.replace('stage.swoon', 'swoon') try: remote_image = urllib.request.urlretrieve(source) width = 640 height = 290 header_image = Image(title=title, width=width, height=height) header_image.file.save( file_, File(open(remote_image[0], 'rb'))) header_image.save() except UnicodeEncodeError: header_image = None print('unable to set header image {}'.format(source)) else: header_image = None new_entry.header_image = header_image new_entry.save() self.create_categories_and_tags(new_entry, categories) if self.should_import_comments: self.import_comments(post_id, slug) PK4{F$blog/management/commands/__init__.pyPKFQU  'blog/locale/pt_BR/LC_MESSAGES/django.po# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-04-12 16:14-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: models.py:66 msgid "Blog index" msgstr "Blogs" #: models.py:72 msgid "Category Name" msgstr "Nome da Categoria" #: models.py:77 msgid "Blog Category" msgstr "Categoria de Blog" #: models.py:78 msgid "Blog Categories" msgstr "Categorias de Blog" #: models.py:90 msgid "Category" msgstr "Categoria" #: models.py:102 msgid "body" msgstr "conteúdo" #: models.py:104 msgid "Post date" msgstr "Data da postagem" #: models.py:111 msgid "Header image" msgstr "Imagem principal" #: models.py:132 msgid "Blog page" msgstr "Página de Blog" #: models.py:133 msgid "Blog pages" msgstr "Páginas de Blog" #: models.py:141 msgid "Categories" msgstr "Categorias" PKF>(6'blog/locale/pt_BR/LC_MESSAGES/django.mo | ! 1 ? J T _j s  j-3C T _i{    Blog CategoriesBlog CategoryBlog indexBlog pageBlog pagesCategoriesCategoryCategory NameHeader imagePost datebodyProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2015-04-12 16:14-0300 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Categorias de BlogCategoria de BlogBlogsPágina de BlogPáginas de BlogCategoriasCategoriaNome da CategoriaImagem principalData da postagemconteúdoPKIZG^- ,wagtail_blog-1.6.3.dist-info/DESCRIPTION.rstUNKNOWN PKIZGkHkk*wagtail_blog-1.6.3.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Django", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License"], "extensions": {"python.details": {"contacts": [{"email": "david@thelabnyc.com", "name": "David Burke", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/thelabnyc/wagtail_blog"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["django", "wagtail", "blog"], "license": "Apache License", "metadata_version": "2.0", "name": "wagtail-blog", "run_requires": [{"requires": ["wagtail (>=1.0.0)"]}], "summary": "A wordpress like blog app implemented in wagtail", "version": "1.6.3"}PKIZG(={H *wagtail_blog-1.6.3.dist-info/top_level.txtblog demo PKIZG}\\"wagtail_blog-1.6.3.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKIZGI%wagtail_blog-1.6.3.dist-info/METADATAMetadata-Version: 2.0 Name: wagtail-blog Version: 1.6.3 Summary: A wordpress like blog app implemented in wagtail Home-page: https://github.com/thelabnyc/wagtail_blog Author: David Burke Author-email: david@thelabnyc.com License: Apache License Keywords: django wagtail blog Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Requires-Dist: wagtail (>=1.0.0) UNKNOWN PKIZGΠ  #wagtail_blog-1.6.3.dist-info/RECORDblog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 blog/models.py,sha256=Jc6qh2_tilQZhwamr7G7UvYAuHGo9k97_mmEv5cBEK8,8504 blog/tests.py,sha256=H_OfrwfjL6hjI524XROW-5UJEOXx8I1IrWUAEHi5jQY,3219 blog/urls.py,sha256=hWYISaXjNugJII_T9XkZmSfW6aLjBEUthtVe2RKqnZk,416 blog/views.py,sha256=cRGds26xP0ZGUUi7glTo4IzFSW4yyLUSqteUVWS6J1s,1395 blog/locale/pt_BR/LC_MESSAGES/django.mo,sha256=K6Ojyv3_mlPiZsJVlXKgYOBzI47zxM3i9SgXdmuY_C8,935 blog/locale/pt_BR/LC_MESSAGES/django.po,sha256=MVdweeFceDzdTTNmpYkoffQnlfRfOuErp3YSeFrC-i4,1289 blog/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 blog/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 blog/management/commands/wordpress_to_wagtail.py,sha256=1r7KsGNMCDxDFNrvHvUehehJYtk2JU7Ww5JKAv1jPCw,14454 blog/migrations/0001_initial.py,sha256=jdyQ1jxB3hCJLubDNwpbMSj0KIXL0yVUqSB6gU2GxbU,3655 blog/migrations/0002_auto_20150226_2305.py,sha256=ozUf_CCTUTW2uqGJghmMjAk_3gIct5anMd8wWUIcBJo,676 blog/migrations/0003_auto_20150323_2116.py,sha256=BhAd24J8ZxmQGITh48p6ZqfyqyhtxUEUwXFZZ3s5etU,481 blog/migrations/0004_auto_20150427_2047.py,sha256=BRE0wHqdZvbyHrIlj5FhwmVH5Dn8k3U_foCwIl8RF54,2223 blog/migrations/0005_auto_20151019_1121.py,sha256=I9dKKdv4ClZsUyk8NVAEhvV6okuypOSk2AuV98rFctI,2011 blog/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 blog/templates/base.html,sha256=xtl5fCHVS-HF5ER3ynITU3qIzydUsbR8reuR6nLBDkI,190 blog/templates/blog/blog_index_page.html,sha256=ZG_GSoB_oRCPJbkDiAHQnRwSS_buCYgmMmp-CzVXtec,1419 blog/templates/blog/blog_page.html,sha256=NoGUPMPFS94cfZh4NRKXlL_KWeb4Bi2HQOl2cGkAYfc,285 blog/templates/blog/blog_post.html,sha256=U830guP22cWyMOHnpCWoBREKEW2jzIleboMG4anaSTw,2369 blog/templates/blog/blog_post_comments_xtd.html,sha256=4pQZ74VmcZcAa8gI0uiJu3wXPvbkRQaNpHLkADh39bA,91 blog/templates/blog/blog_post_comments_xtd_link.html,sha256=HJeDzuZCHh-P01SA_c-cA2g5PjreDSEr3uYKSN79eFE,272 demo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 demo/apps.py,sha256=AmrUdZZgowh5nZ4l4O2fg6NSHtnlWu29yNWQJRNQFA4,281 demo/settings.py,sha256=MGFZJGYjwI7qskA2jsoBCm-9PG6pDP6_iy8-3t6EoPg,3238 demo/urls.py,sha256=0Nk6xbwuJkco1uJ42WzcziP34hFl_AMl0a72slsPpTM,1316 demo/wsgi.py,sha256=illrTGUBvGRaAtHTAu0qBZbfzS-fSLBpHKtjVXzsNlM,383 wagtail_blog-1.6.3.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 wagtail_blog-1.6.3.dist-info/METADATA,sha256=FWxdeXXwv_XKTkk5mxYMRf7pbTc2pHnNsS5uudNcE44,705 wagtail_blog-1.6.3.dist-info/RECORD,, wagtail_blog-1.6.3.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 wagtail_blog-1.6.3.dist-info/metadata.json,sha256=0U9m2MppCws7g-IGE21xj5L-6CEUiqyV6KiWEJhV6W0,875 wagtail_blog-1.6.3.dist-info/top_level.txt,sha256=0duHW0I9dA8wh5RjD44SgsiVCgMXfA1C6BBdkip_kDU,10 PKkTGB demo/apps.pyPK UG'4 Cdemo/settings.pyPKw/GFQ$$ demo/urls.pyPK0F edemo/wsgi.pyPK UGdemo/__init__.pyPKЙZG휱8!8!<blog/models.pyPKFJss 6blog/views.pyPKgSG7 ><blog/tests.pyPKw/G3, Hblog/urls.pyPK ZFJblog/__init__.pyPKF~*Jblog/migrations/0004_auto_20150427_2047.pyPKSG8A*Sblog/migrations/0005_auto_20151019_1121.pyPK ZFFGG\blog/migrations/0001_initial.pyPKZFEa&*jblog/migrations/0002_auto_20150226_2305.pyPKwFԷ\*~mblog/migrations/0003_auto_20150323_2116.pyPK ZFoblog/migrations/__init__.pyPKFq1oblog/templates/base.htmlPKIxF(4pblog/templates/blog/blog_post_comments_xtd_link.htmlPKSGA A "6rblog/templates/blog/blog_post.htmlPKxF~"{blog/templates/blog/blog_page.htmlPKxFd[[/}blog/templates/blog/blog_post_comments_xtd.htmlPKԸZFƲ(}blog/templates/blog/blog_index_page.htmlPK4{Fblog/management/__init__.pyPK}/Gfv8v80ƃblog/management/commands/wordpress_to_wagtail.pyPK4{F$blog/management/commands/__init__.pyPKFQU  '̼blog/locale/pt_BR/LC_MESSAGES/django.poPKF>(6'blog/locale/pt_BR/LC_MESSAGES/django.moPKIZG^- ,wagtail_blog-1.6.3.dist-info/DESCRIPTION.rstPKIZGkHkk*Zwagtail_blog-1.6.3.dist-info/metadata.jsonPKIZG(={H * wagtail_blog-1.6.3.dist-info/top_level.txtPKIZG}\\"_wagtail_blog-1.6.3.dist-info/WHEELPKIZGI%wagtail_blog-1.6.3.dist-info/METADATAPKIZGΠ  #wagtail_blog-1.6.3.dist-info/RECORDPK!! [