~netlandish/django-wiki

942161c75fdc999426c3e5889965dfb3820fefc0 — Benjamin Bach 8 years ago 96cbc23 + 456ee71
Merge pull request #563 from benjaoming/django-1.10

Django 1.10 support and version 0.2b1
42 files changed, 204 insertions(+), 1132 deletions(-)

A .coveragerc
M .travis.yml
M Makefile
M README.rst
M docs/release_notes.rst
M pytest.ini
M setup.py
M testproject/testproject/settings/base.py
M tox.ini
M wiki/__init__.py
M wiki/conf/settings.py
M wiki/core/compat.py
M wiki/decorators.py
M wiki/forms.py
M wiki/models/__init__.py
M wiki/models/article.py
M wiki/models/pluginbase.py
M wiki/models/urlpath.py
M wiki/plugins/attachments/markdown_extensions.py
M wiki/plugins/attachments/models.py
M wiki/plugins/attachments/settings.py
D wiki/plugins/attachments/south_migrations/0001_initial.py
D wiki/plugins/attachments/south_migrations/__init__.py
M wiki/plugins/attachments/urls.py
M wiki/plugins/images/markdown_extensions.py
M wiki/plugins/images/models.py
M wiki/plugins/images/settings.py
D wiki/plugins/images/south_migrations/0001_initial.py
D wiki/plugins/images/south_migrations/__init__.py
M wiki/plugins/macros/mdx/macro.py
M wiki/plugins/notifications/models.py
M wiki/plugins/notifications/settings.py
D wiki/plugins/notifications/south_migrations/0001_initial.py
D wiki/plugins/notifications/south_migrations/__init__.py
D wiki/south_migrations/0001_initial.py
D wiki/south_migrations/__init__.py
R runtests.py => wiki/tests/settings.py -rwxr-xr-x => -rw-r--r--
M wiki/tests/test_models.py
M wiki/tests/test_urls.py
M wiki/tests/test_views.py
M wiki/tests/testdata/urls.py
M wiki/urls.py
A .coveragerc => .coveragerc +2 -0
@@ 0,0 1,2 @@
[run]
omit = */tests/*

M .travis.yml => .travis.yml +17 -10
@@ 6,25 6,33 @@
# directive.

sudo: false
dist: trusty

env: PIP_DOWNLOAD_CACHE="pip_cache"

language: python

addons:
  apt:
    sources:
      - deadsnakes
    packages:
      - python3.5
      - python3.5-dev
      - libjpeg8
      - libjpeg8-dev

# See tox.ini for env list
env:
- TOXENV=py26-django15
- TOXENV=py26-django16
- TOXENV=py27-django15
- TOXENV=py27-django16
- TOXENV=py27-django17
- TOXENV=py27-django18
- TOXENV=py27-django19
- TOXENV=py34-django15
- TOXENV=py34-django16
- TOXENV=py34-django17
- TOXENV=py27-django110
- TOXENV=py34-django18
- TOXENV=py34-django19
- TOXENV=py34-django110
- TOXENV=py35-django18
- TOXENV=py35-django19
- TOXENV=py35-django110

install:
  - pip install tox>=2.1 coveralls


@@ 37,5 45,4 @@ notifications:
    - "irc.freenode.org#django-wiki"

after_success:
  - coverage combine
  - coveralls
  - bash <(curl -s https://codecov.io/bash)

M Makefile => Makefile +1 -1
@@ 27,7 27,7 @@ lint:
	pep8 wiki

test:
	./runtests.py
	pytest

test-all:
	tox

M README.rst => README.rst +14 -6
@@ 7,8 7,8 @@ django-wiki
   :target: http://django-wiki.readthedocs.org/
.. |Build Status| image:: https://travis-ci.org/django-wiki/django-wiki.png?branch=master
   :target: https://travis-ci.org/django-wiki/django-wiki
.. |Coverage Status| image:: https://coveralls.io/repos/django-wiki/django-wiki/badge.svg?branch=master
   :target: https://coveralls.io/r/django-wiki/django-wiki?branch=master
.. |Coverage Status| image:: http://codecov.io/github/django-wiki/django-wiki/coverage.svg?branch=master
   :target: http://codecov.io/github/django-wiki/django-wiki?branch=master
.. |PyPi| image:: https://badge.fury.io/py/wiki.svg
   :target: https://pypi.python.org/pypi/wiki/
.. |Downloads| image:: https://img.shields.io/pypi/dm/wiki.svg


@@ 20,19 20,27 @@ django-wiki
Important notice
----------------

Final release 0.1 is out now. Please note that the only way to upgrade is from 0.0.24.
Dev release of 0.2 is out. Please note that the only way to upgrade is from 0.1.

**0.1** adds Django 1.7, 1.8 and 1.9 support.
 - **0.2** supports Django 1.8, 1.9, and 1.10.

**0.0.24** added experimental Django 1.7 support.
Final release 0.1 is out. Please note that the only way to upgrade is from 0.0.24.

 - **0.1** adds Django 1.7, 1.8 and 1.9 support.
 - **0.0.24** added experimental Django 1.7 support.

For upgrade instructions, please refer to the `Release
Notes <http://django-wiki.readthedocs.org/en/latest/release_notes.html#django-wiki-0-1>`__
Notes <http://django-wiki.readthedocs.org/en/latest/release_notes.html>`__


News
----

October 13, 2016
~~~~~~~~~~~~~~~~

0.2b1 released: `Release notes <http://django-wiki.readthedocs.org/en/latest/release_notes.html#django-wiki-0-2>`__

June 19, 2016
~~~~~~~~~~~~~


M docs/release_notes.rst => docs/release_notes.rst +6 -0
@@ 31,10 31,16 @@ break due to some force majeure.
django-wiki 0.2 (dev)
---------------------

 * Added Django 1.10 support #563
 * Fix memory leak in markdown extensions setting #564
 * Updated translations - Languages > 90% completed: Chinese (China), Portuguese (Brazil), Korean (Korea), French, Slovak, Spanish, Dutch, German, Russian, Finnish.
 * Taiwanese Chinese added (39% completed)

Support removed for:

 * Python 2.6
 * Django < 1.8
 * South

django-wiki 0.1.2
-----------------

M pytest.ini => pytest.ini +1 -0
@@ 3,3 3,4 @@ django_find_project = false
python_files=test_*.py
testpaths=wiki
norecursedirs=testproject .svn _build tmp* dist *.egg-info
DJANGO_SETTINGS_MODULE=wiki.tests.settings

M setup.py => setup.py +6 -46
@@ 3,8 3,6 @@
from __future__ import absolute_import, unicode_literals

import os
from sys import version_info as PYTHON_VERSION

from setuptools import find_packages, setup

from wiki import __version__


@@ 14,8 12,6 @@ from wiki import __version__
# Used for the long_description.  It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...


def get_path(fname):
    return os.path.join(os.path.dirname(__file__), fname)



@@ 25,52 21,16 @@ def read(fname):


requirements = [
    "Django>=1.5",
    "Django>=1.8",
    "Pillow",
    "django-nyt>=0.9.7.2,<1.0",
    "django-nyt>=1.0b1",
    "six",
    "django-mptt>=0.8.6,<0.9",
    "django-sekizai>=0.10",
    "sorl-thumbnail>=12,<13",
    "Markdown>=2.6,<2.7",
]


# Requirements that depend on Django version: South and sorl-thumbnail
try:
    from django import VERSION as DJANGO_VERSION
except ImportError:
    # No Django so assuming that one will get installed, but we don't know which
    # one.
    # We will assume it's a very recent one and base the requirements on that...
    requirements.append("sorl-thumbnail>=12")
    # 0.6.1 broken: https://github.com/django-mptt/django-mptt/issues/316
    requirements.append("django-mptt>=0.8")
    requirements.append("django-sekizai>=0.9")
else:
    if DJANGO_VERSION < (1, 7):
        requirements.append("South>=1.0.1")
        requirements.append("django-mptt>=0.7.1,<0.8")
        requirements.append("django-sekizai<0.9")
    elif DJANGO_VERSION < (1, 8):
        # Fixes
        # AttributeError: 'URLPath' object has no attribute 'get_deferred_fields'
        requirements.append("django-mptt>=0.7.1,<0.8")
        requirements.append("django-sekizai>=0.9")
    else:
        # Latest django-mptt only works for Django 1.8+
        requirements.append("django-mptt>=0.8,<0.9")
        requirements.append("django-sekizai>=0.9")
    if DJANGO_VERSION < (1, 5):
        # For Django 1.4, use sorl-thumbnail<11.12.1:
        # https://github.com/mariocesar/sorl-thumbnail/issues/255
        requirements.append("sorl-thumbnail<11.12.1")
    else:
        requirements.append("sorl-thumbnail>=12,<13")

if PYTHON_VERSION < (2, 7):
    # For Python 2.6, use Markdown<2.5.0, see
    # https://github.com/waylan/Python-Markdown/issues/349
    requirements.append("Markdown>2.2.0,<2.5.0")
else:
    requirements.append("Markdown>2.2.0,<2.7")

packages = find_packages()



M testproject/testproject/settings/base.py => testproject/testproject/settings/base.py +2 -8
@@ 13,7 13,6 @@ from __future__ import unicode_literals

import os

from django import VERSION
from django.core.urlresolvers import reverse_lazy




@@ 32,9 31,6 @@ DEBUG = False
ALLOWED_HOSTS = []


# Application definition


INSTALLED_APPS = [
    'django.contrib.humanize',
    'django.contrib.auth',


@@ 57,10 53,8 @@ INSTALLED_APPS = [
    'wiki.plugins.notifications',
    'mptt',
]
if VERSION < (1, 7):
    INSTALLED_APPS.append('south')
else:
    TEST_RUNNER = 'django.test.runner.DiscoverRunner'

TEST_RUNNER = 'django.test.runner.DiscoverRunner'


MIDDLEWARE_CLASSES = [

M tox.ini => tox.ini +13 -20
@@ 1,40 1,33 @@
[tox]
# Ensure you add to .travis.yml if you add here
envlist = py26-django{15,16}, {py27,py34}-django{15,16,17,18,19}
envlist = {py27,py34,py35}-django{18,19,110}

[testenv]

commands =
     {envbindir}/coverage run --source=wiki runtests.py
     {envbindir}/pytest --cov=wiki --cov-config .coveragerc

usedevelop = true

deps =
     coverage
     pytest
     pytest-django
     pytest-cov
     Pillow==2.3.0
     django-classy-tags==0.4
     six>=1.9
     mock>=2.0
     django_nyt==0.9.8
     django15: Django==1.5.12
     django16: Django==1.6.11
     django{15,16}: South==1.0.2
     django{15,16}: django-sekizai==0.7
     django{15,16}: django-mptt==0.7.2
     django{15,16}: sorl-thumbnail==11.12
     django{15,16}: pytest-django<3
     django17: Django==1.7.8
     django17: django-mptt==0.7.4
     Markdown==2.6.7
     django_nyt==1.0b1
     django18: Django==1.8.2
     django19: Django==1.9
     django{18,19}: django-mptt==0.8.0
     django{17,18,19}: django-sekizai==0.9.0
     django{17,18,19}: sorl-thumbnail==12.3
     django{17,18,19}: pytest-django>=3
     py26: importlib
     py26: Markdown==2.4.1
     py{27,34}: Markdown==2.6.5
     django110: Django==1.10.2
     django{18,19,110}: django-mptt==0.8.6
     django{18,19,110}: django-sekizai==0.10.0
     django{18,19,110}: sorl-thumbnail==12.3

basepython =
     py26: python2.6
     py27: python2.7
     py34: python3.4
     py35: python3.5

M wiki/__init__.py => wiki/__init__.py +1 -1
@@ 19,5 19,5 @@ from __future__ import unicode_literals

from wiki.core.version import get_version

VERSION = (0, 1, 3, 'alpha', 0)
VERSION = (0, 2, 0, 'beta', 1)
__version__ = get_version(VERSION)

M wiki/conf/settings.py => wiki/conf/settings.py +1 -7
@@ 1,6 1,5 @@
from __future__ import absolute_import, unicode_literals

import django
from django.conf import settings as django_settings
from django.core.files.storage import default_storage
from django.core.urlresolvers import reverse_lazy


@@ 10,7 9,6 @@ from django.utils.translation import ugettext_lazy as _
URL_CASE_SENSITIVE = getattr(django_settings, 'WIKI_URL_CASE_SENSITIVE', False)

# Non-configurable (at the moment)
APP_LABEL = 'wiki'
WIKI_LANGUAGE = 'markdown'

# The editor class to use -- maybe a 3rd party or your own...? You can always


@@ 175,11 173,7 @@ SEARCH_VIEW = getattr(
CACHE_TIMEOUT = getattr(django_settings, 'WIKI_CACHE_TIMEOUT', 600)

# Choose the Group model to use. Defaults to django's auth.Group
# This requires `django.apps` which was introduced in Django 1.7.
if django.VERSION < (1, 7):
    GROUP_MODEL = 'auth.Group'
else:
    GROUP_MODEL = getattr(django_settings, 'WIKI_GROUP_MODEL', 'auth.Group')
GROUP_MODEL = getattr(django_settings, 'WIKI_GROUP_MODEL', 'auth.Group')

###################
# SPAM PROTECTION #

M wiki/core/compat.py => wiki/core/compat.py +3 -31
@@ 2,27 2,16 @@
compatibility with several Django versions simultaneously."""
from __future__ import absolute_import, unicode_literals

from django import VERSION as DJANGO_VERSION
from django.conf import settings as django_settings
from django.db import transaction

# Django 1.5+
if DJANGO_VERSION >= (1, 5):
    USER_MODEL = getattr(django_settings, 'AUTH_USER_MODEL', 'auth.User')
else:
    USER_MODEL = 'auth.User'
USER_MODEL = getattr(django_settings, 'AUTH_USER_MODEL', 'auth.User')


def get_user_model():

    if DJANGO_VERSION >= (1, 5):
        from django.contrib.auth import get_user_model as gum
        return gum()
    else:
        from django.contrib.auth.models import User
        if 'USERNAME_FIELD' not in User.__dict__:
            User.USERNAME_FIELD = 'username'
        return User
    from django.contrib.auth import get_user_model as gum
    return gum()


# Django 1.6 transaction API, required for 1.8+


@@ 38,20 27,3 @@ try:
except AttributeError:
    atomic = nop_decorator
    transaction_commit_on_success = transaction.commit_on_success


if DJANGO_VERSION < (1, 8):
    from django.template.loader import render_to_string as django_render_to_string
    from django.template import Context, RequestContext

    # Similar to 1.8 signature, but with things we can support
    # under 1.7 and less.
    def render_to_string(template_name, context=None, request=None):
        if request is not None:
            context_instance = RequestContext(request, context)
        else:
            context_instance = Context(context)
        return django_render_to_string(template_name,
                                       context_instance=context_instance)
else:
    from django.template.loader import render_to_string  # noqa @UnusedImport

M wiki/decorators.py => wiki/decorators.py +1 -1
@@ 8,10 8,10 @@ from django.core.urlresolvers import reverse
from django.http import (HttpResponse, HttpResponseForbidden,
                         HttpResponseNotFound, HttpResponseRedirect)
from django.shortcuts import get_object_or_404, redirect
from django.template.loader import render_to_string
from django.utils.http import urlquote
from six.moves import filter
from wiki.conf import settings
from wiki.core.compat import render_to_string
from wiki.core.exceptions import NoRootURL



M wiki/forms.py => wiki/forms.py +2 -8
@@ 7,6 7,7 @@ from datetime import timedelta
from itertools import chain

from django import forms
from django.apps import apps
from django.contrib.auth.forms import UserCreationForm
from django.core.urlresolvers import Resolver404, resolve
from django.forms.widgets import HiddenInput


@@ 32,14 33,7 @@ except ImportError:


User = get_user_model()

# Backwards compatibility with Django < 1.7
try:
    from django.apps import apps
except ImportError:
    from django.contrib.auth.models import Group
else:
    Group = apps.get_model(settings.GROUP_MODEL)
Group = apps.get_model(settings.GROUP_MODEL)

# Due to deprecation of django.forms.util in Django 1.9
try:

M wiki/models/__init__.py => wiki/models/__init__.py +19 -14
@@ 1,17 1,16 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
# -*- coding: utf-8 -*-

from django import VERSION
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
import warnings
from six import string_types
from six import string_types, text_type

# TODO: Don't use wildcards
from .article import *  # noqa
from .pluginbase import *  # noqa
from .urlpath import *  # noqa
from django.utils.functional import lazy

# TODO: Should the below stuff be executed a more logical place?
# Follow Django's default_settings.py / settings.py pattern and put these


@@ 64,16 63,6 @@ if 'django_notify' in django_settings.INSTALLED_APPS:
    raise ImproperlyConfigured(
        'django-wiki: You need to change from django_notify to django_nyt in INSTALLED_APPS and your urlconfig.')

######################
# Warnings
######################


if VERSION < (1, 7):
    if 'south' not in django_settings.INSTALLED_APPS:
        warnings.warn(
            "django-wiki: No south in your INSTALLED_APPS. This is highly discouraged.")


from django.core import urlresolvers  # noqa



@@ 108,4 97,20 @@ def reverse(*args, **kwargs):
    return url

# Now we redefine reverse method
reverse_lazy = lazy(reverse, text_type)
urlresolvers.reverse = reverse
urlresolvers.reverse_lazy = reverse_lazy

# Patch up other locations of the reverse function
try:
    from django.urls import base
    from django import urls
    from django import shortcuts
    base.reverse = reverse
    base.reverse_lazy = reverse_lazy
    urls.reverse = reverse
    urls.reverse_lazy = reverse_lazy
    shortcuts.reverse = reverse
    urls.reverse_lazy = reverse_lazy
except ImportError:
    pass

M wiki/models/article.py => wiki/models/article.py +0 -3
@@ 192,7 192,6 @@ class Article(models.Model):
        return str(obj_name)

    class Meta:
        app_label = settings.APP_LABEL
        permissions = (
            ("moderate", _("Can edit all articles and lock/unlock/restore")),
            ("assign", _("Can change ownership of any article")),


@@ 253,7 252,6 @@ class ArticleForObject(models.Model):
        return "{}".format(self.article)

    class Meta:
        app_label = settings.APP_LABEL
        verbose_name = _('Article for object')
        verbose_name_plural = _('Articles for object')
        # Do not allow several objects


@@ 372,7 370,6 @@ class ArticleRevision(BaseRevisionMixin, models.Model):
        self.locked = predecessor.locked

    class Meta:
        app_label = settings.APP_LABEL
        get_latest_by = 'revision_number'
        ordering = ('created',)
        unique_together = ('article', 'revision_number')

M wiki/models/pluginbase.py => wiki/models/pluginbase.py +0 -22
@@ 24,7 24,6 @@ from __future__ import absolute_import, unicode_literals
from django.db import models
from django.db.models import signals
from django.utils.translation import ugettext_lazy as _
from wiki.conf import settings
from wiki.decorators import disable_signal_for_loaddata

from .article import ArticleRevision, BaseRevisionMixin


@@ 62,11 61,6 @@ class ArticlePlugin(models.Model):
        """Remove related contents completely, ie. media files."""
        pass

    class Meta:
        # Override this setting with app_label = '' in your extended model
        # if it lives outside the wiki app.
        app_label = settings.APP_LABEL


class ReusablePlugin(ArticlePlugin):



@@ 108,11 102,6 @@ class ReusablePlugin(ArticlePlugin):
    def can_moderate(self, user):
        return self.article.can_moderate(user) if self.article else False

    class Meta:
        # Override this setting with app_label = '' in your extended model
        # if it lives outside the wiki app.
        app_label = settings.APP_LABEL


class SimplePluginCreateError(Exception):
    pass


@@ 156,11 145,6 @@ class SimplePlugin(ArticlePlugin):
    def get_logmessage(self):
        return _("A plugin was changed")

    class Meta:
        # Override this setting with app_label = '' in your extended model
        # if it lives outside the wiki app.
        app_label = settings.APP_LABEL


class RevisionPlugin(ArticlePlugin):



@@ 208,11 192,6 @@ class RevisionPlugin(ArticlePlugin):
        if save:
            self.save()

    class Meta:
        # Override this setting with app_label = '' in your extended model
        # if it lives outside the wiki app.
        app_label = settings.APP_LABEL


class RevisionPluginRevision(BaseRevisionMixin, models.Model):



@@ 228,7 207,6 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model):
    class Meta:
        # Override this setting with app_label = '' in your extended model
        # if it lives outside the wiki app.
        app_label = settings.APP_LABEL
        get_latest_by = 'revision_number'
        ordering = ('-created',)


M wiki/models/urlpath.py => wiki/models/urlpath.py +0 -1
@@ 201,7 201,6 @@ class URLPath(MPTTModel):
        verbose_name = _('URL path')
        verbose_name_plural = _('URL paths')
        unique_together = ('site', 'parent', 'slug')
        app_label = settings.APP_LABEL

    def clean(self, *args, **kwargs):
        if self.slug and not self.parent:

M wiki/plugins/attachments/markdown_extensions.py => wiki/plugins/attachments/markdown_extensions.py +1 -1
@@ 5,7 5,7 @@ import re
import markdown
from django.contrib.auth.models import AnonymousUser
from django.core.urlresolvers import reverse
from wiki.core.compat import render_to_string
from django.template.loader import render_to_string
from wiki.core.permissions import can_read
from wiki.plugins.attachments import models


M wiki/plugins/attachments/models.py => wiki/plugins/attachments/models.py +0 -4
@@ 54,8 54,6 @@ class Attachment(ReusablePlugin):
        verbose_name_plural = _('attachments')
        # Matches label of upcoming 0.1 release
        db_table = 'wiki_attachments_attachment'
        if settings.APP_LABEL:
            app_label = settings.APP_LABEL

    def __str__(self):
        from wiki.models import Article


@@ 140,8 138,6 @@ class AttachmentRevision(BaseRevisionMixin, models.Model):
        get_latest_by = 'revision_number'
        # Matches label of upcoming 0.1 release
        db_table = 'wiki_attachments_attachmentrevision'
        if settings.APP_LABEL:
            app_label = settings.APP_LABEL

    def get_filename(self):
        """Used to retrieve the filename of a revision.

M wiki/plugins/attachments/settings.py => wiki/plugins/attachments/settings.py +3 -3
@@ 1,12 1,12 @@
from __future__ import absolute_import, unicode_literals

from django import VERSION
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
from wiki.conf import settings as wiki_settings

# This is not used in django 1.7+
APP_LABEL = 'attachments' if VERSION < (1, 7) else None
# Deprecated
APP_LABEL = None

SLUG = "attachments"

# Please see this note about support for UTF-8 files on django/apache:

D wiki/plugins/attachments/south_migrations/0001_initial.py => wiki/plugins/attachments/south_migrations/0001_initial.py +0 -151
@@ 1,151 0,0 @@
# -*- coding: utf-8 -*-
from django.db import models
from south.db import db
from south.utils import datetime_utils as datetime
from south.v2 import SchemaMigration


class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding model 'Attachment'
        db.create_table(u'wiki_attachments_attachment', (
            (u'reusableplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.ReusablePlugin'], unique=True, primary_key=True)),
            ('current_revision', self.gf('django.db.models.fields.related.OneToOneField')(blank=True, related_name=u'current_set', unique=True, null=True, to=orm['attachments.AttachmentRevision'])),
            ('original_filename', self.gf('django.db.models.fields.CharField')(max_length=256, null=True, blank=True)),
        ))
        db.send_create_signal(u'attachments', ['Attachment'])

        # Adding model 'AttachmentRevision'
        db.create_table(u'wiki_attachments_attachmentrevision', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('revision_number', self.gf('django.db.models.fields.IntegerField')()),
            ('user_message', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('automatic_log', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15, null=True, blank=True)),
            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
            ('previous_revision', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['attachments.AttachmentRevision'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('locked', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('attachment', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['attachments.Attachment'])),
            ('file', self.gf('django.db.models.fields.files.FileField')(max_length=255)),
            ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
        ))
        db.send_create_signal(u'attachments', ['AttachmentRevision'])


    def backwards(self, orm):
        # Deleting model 'Attachment'
        db.delete_table(u'wiki_attachments_attachment')

        # Deleting model 'AttachmentRevision'
        db.delete_table(u'wiki_attachments_attachmentrevision')


    models = {
        u'attachments.attachment': {
            'Meta': {'object_name': 'Attachment', 'db_table': "u'wiki_attachments_attachment'", '_ormbases': [u'wiki.ReusablePlugin']},
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'current_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['attachments.AttachmentRevision']"}),
            'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
            u'reusableplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ReusablePlugin']", 'unique': 'True', 'primary_key': 'True'})
        },
        u'attachments.attachmentrevision': {
            'Meta': {'ordering': "(u'created',)", 'object_name': 'AttachmentRevision', 'db_table': "u'wiki_attachments_attachmentrevision'"},
            'attachment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['attachments.Attachment']"}),
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['attachments.AttachmentRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        },
        u'auth.group': {
            'Meta': {'object_name': 'Group'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
        },
        u'auth.permission': {
            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        u'auth.user': {
            'Meta': {'object_name': 'User'},
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
        },
        u'contenttypes.contenttype': {
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
        },
        u'wiki.article': {
            'Meta': {'object_name': 'Article'},
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'current_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['wiki.ArticleRevision']"}),
            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_articles'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
        },
        u'wiki.articleplugin': {
            'Meta': {'object_name': 'ArticlePlugin'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
        },
        u'wiki.articlerevision': {
            'Meta': {'ordering': "(u'created',)", 'unique_together': "((u'article', u'revision_number'),)", 'object_name': 'ArticleRevision'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.ArticleRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        },
        u'wiki.reusableplugin': {
            'Meta': {'object_name': 'ReusablePlugin', '_ormbases': [u'wiki.ArticlePlugin']},
            u'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'articles': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'shared_plugins_set'", 'symmetrical': 'False', 'to': u"orm['wiki.Article']"})
        }
    }

    complete_apps = ['attachments']

D wiki/plugins/attachments/south_migrations/__init__.py => wiki/plugins/attachments/south_migrations/__init__.py +0 -0
M wiki/plugins/attachments/urls.py => wiki/plugins/attachments/urls.py +1 -5
@@ 1,7 1,6 @@
from __future__ import absolute_import, unicode_literals

from django import VERSION as DJANGO_VERSION
from django.conf.urls import patterns, url
from django.conf.urls import url
from wiki.plugins.attachments import views

urlpatterns = [


@@ 33,6 32,3 @@ urlpatterns = [
        views.AttachmentChangeRevisionView.as_view(),
        name='attachments_revision_change'),
]

if DJANGO_VERSION < (1, 8):
    urlpatterns = patterns('', *urlpatterns)

M wiki/plugins/images/markdown_extensions.py => wiki/plugins/images/markdown_extensions.py +1 -1
@@ 4,7 4,7 @@ from __future__ import absolute_import, unicode_literals
import re

import markdown
from wiki.core.compat import render_to_string
from django.template.loader import render_to_string
from wiki.plugins.images import models

IMAGE_RE = re.compile(

M wiki/plugins/images/models.py => wiki/plugins/images/models.py +0 -4
@@ 49,8 49,6 @@ class Image(RevisionPlugin):
        verbose_name = _('image')
        verbose_name_plural = _('images')
        db_table = 'wiki_images_image'  # Matches label of upcoming 0.1 release
        if settings.APP_LABEL:
            app_label = settings.APP_LABEL

    def __str__(self):
        if self.current_revision:


@@ 114,8 112,6 @@ class ImageRevision(RevisionPluginRevision):
        verbose_name_plural = _('image revisions')
        # Matches label of upcoming 0.1 release
        db_table = 'wiki_images_imagerevision'
        if settings.APP_LABEL:
            app_label = settings.APP_LABEL
        ordering = ('-created',)

    def __str__(self):

M wiki/plugins/images/settings.py => wiki/plugins/images/settings.py +2 -3
@@ 1,13 1,12 @@
from __future__ import absolute_import, unicode_literals

from django import VERSION
from django.conf import settings as django_settings
from wiki.conf import settings as wiki_settings

SLUG = 'images'

# This is deprecated in django 1.7+
APP_LABEL = 'images' if VERSION < (1, 7) else None
# Deprecated
APP_LABEL = None

# Where to store images
IMAGE_PATH = getattr(django_settings, 'WIKI_IMAGES_PATH', "wiki/images/%aid/")

D wiki/plugins/images/south_migrations/0001_initial.py => wiki/plugins/images/south_migrations/0001_initial.py +0 -142
@@ 1,142 0,0 @@
# -*- coding: utf-8 -*-
from django.db import models
from south.db import db
from south.utils import datetime_utils as datetime
from south.v2 import SchemaMigration


class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding model 'Image'
        db.create_table(u'wiki_images_image', (
            (u'revisionplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.RevisionPlugin'], unique=True, primary_key=True)),
        ))
        db.send_create_signal(u'images', ['Image'])

        # Adding model 'ImageRevision'
        db.create_table(u'wiki_images_imagerevision', (
            (u'revisionpluginrevision_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.RevisionPluginRevision'], unique=True, primary_key=True)),
            ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=2000, null=True, blank=True)),
            ('width', self.gf('django.db.models.fields.SmallIntegerField')(null=True, blank=True)),
            ('height', self.gf('django.db.models.fields.SmallIntegerField')(null=True, blank=True)),
        ))
        db.send_create_signal(u'images', ['ImageRevision'])


    def backwards(self, orm):
        # Deleting model 'Image'
        db.delete_table(u'wiki_images_image')

        # Deleting model 'ImageRevision'
        db.delete_table(u'wiki_images_image')


    models = {
        u'auth.group': {
            'Meta': {'object_name': 'Group'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
        },
        u'auth.permission': {
            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        u'auth.user': {
            'Meta': {'object_name': 'User'},
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
        },
        u'contenttypes.contenttype': {
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
        },
        u'images.image': {
            'Meta': {'object_name': 'Image', 'db_table': "u'wiki_images_image'", '_ormbases': [u'wiki.RevisionPlugin']},
            u'revisionplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.RevisionPlugin']", 'unique': 'True', 'primary_key': 'True'})
        },
        u'images.imagerevision': {
            'Meta': {'ordering': "(u'-created',)", 'object_name': 'ImageRevision', 'db_table': "u'wiki_images_image'", '_ormbases': [u'wiki.RevisionPluginRevision']},
            'height': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
            'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '2000', 'null': 'True', 'blank': 'True'}),
            u'revisionpluginrevision_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.RevisionPluginRevision']", 'unique': 'True', 'primary_key': 'True'}),
            'width': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'})
        },
        u'wiki.article': {
            'Meta': {'object_name': 'Article'},
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'current_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['wiki.ArticleRevision']"}),
            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_articles'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
        },
        u'wiki.articleplugin': {
            'Meta': {'object_name': 'ArticlePlugin'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
        },
        u'wiki.articlerevision': {
            'Meta': {'ordering': "(u'created',)", 'unique_together': "((u'article', u'revision_number'),)", 'object_name': 'ArticleRevision'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.ArticleRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        },
        u'wiki.revisionplugin': {
            'Meta': {'object_name': 'RevisionPlugin', '_ormbases': [u'wiki.ArticlePlugin']},
            u'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'plugin_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['wiki.RevisionPluginRevision']"})
        },
        u'wiki.revisionpluginrevision': {
            'Meta': {'ordering': "(u'-created',)", 'object_name': 'RevisionPluginRevision'},
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'plugin': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'revision_set'", 'to': u"orm['wiki.RevisionPlugin']"}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.RevisionPluginRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        }
    }

    complete_apps = ['images']

D wiki/plugins/images/south_migrations/__init__.py => wiki/plugins/images/south_migrations/__init__.py +0 -0
M wiki/plugins/macros/mdx/macro.py => wiki/plugins/macros/mdx/macro.py +1 -1
@@ 4,9 4,9 @@ from __future__ import absolute_import, unicode_literals
import re

import markdown
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from six import string_types
from wiki.core.compat import render_to_string
from wiki.plugins.macros import settings

# See:

M wiki/plugins/notifications/models.py => wiki/plugins/notifications/models.py +0 -2
@@ 32,8 32,6 @@ class ArticleSubscription(ArticlePlugin):
        unique_together = ('subscription', 'articleplugin_ptr')
        # Matches label of upcoming 0.1 release
        db_table = 'wiki_notifications_articlesubscription'
        if settings.APP_LABEL:
            app_label = settings.APP_LABEL


def default_url(article, urlpath=None):

M wiki/plugins/notifications/settings.py => wiki/plugins/notifications/settings.py +2 -3
@@ 1,9 1,8 @@
from __future__ import absolute_import, unicode_literals

from django import VERSION

# This is deprecated in django 1.7+
APP_LABEL = 'notifications' if VERSION < (1, 7) else None
# Deprecated
APP_LABEL = None

# Key for django_nyt - changing it will break any existing notifications.
ARTICLE_EDIT = "article_edit"

D wiki/plugins/notifications/south_migrations/0001_initial.py => wiki/plugins/notifications/south_migrations/0001_initial.py +0 -148
@@ 1,148 0,0 @@
# -*- coding: utf-8 -*-
from django.db import models
from south.db import db
from south.utils import datetime_utils as datetime
from south.v2 import SchemaMigration


class Migration(SchemaMigration):

    depends_on = (
        ("django_nyt", "0001_initial"),
    )

    def forwards(self, orm):
        # Adding model 'ArticleSubscription'
        db.create_table(u'wiki_notifications_articlesubscription', (
            (u'articleplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.ArticlePlugin'], unique=True, primary_key=True)),
            ('subscription', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['django_nyt.Subscription'], unique=True)),
        ))
        db.send_create_signal(u'notifications', ['ArticleSubscription'])

        # Adding unique constraint on 'ArticleSubscription', fields ['subscription', u'articleplugin_ptr']
        db.create_unique(u'wiki_notifications_articlesubscription', ['subscription_id', u'articleplugin_ptr_id'])


    def backwards(self, orm):
        # Removing unique constraint on 'ArticleSubscription', fields ['subscription', u'articleplugin_ptr']
        db.delete_unique(u'wiki_notifications_articlesubscription', ['subscription_id', u'articleplugin_ptr_id'])

        # Deleting model 'ArticleSubscription'
        db.delete_table(u'wiki_notifications_articlesubscription')


    models = {
        u'auth.group': {
            'Meta': {'object_name': 'Group'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
        },
        u'auth.permission': {
            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        u'auth.user': {
            'Meta': {'object_name': 'User'},
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
        },
        u'contenttypes.contenttype': {
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
        },
        u'django_nyt.notification': {
            'Meta': {'ordering': "(u'-id',)", 'object_name': 'Notification', 'db_table': "u'nyt_notification'"},
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'is_emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'is_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'message': ('django.db.models.fields.TextField', [], {}),
            'occurrences': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
            'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['django_nyt.Subscription']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
        },
        u'django_nyt.notificationtype': {
            'Meta': {'object_name': 'NotificationType', 'db_table': "u'nyt_notificationtype'"},
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'primary_key': 'True'}),
            'label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'})
        },
        u'django_nyt.settings': {
            'Meta': {'object_name': 'Settings', 'db_table': "u'nyt_settings'"},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'interval': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
        },
        u'django_nyt.subscription': {
            'Meta': {'object_name': 'Subscription', 'db_table': "u'nyt_subscription'"},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'latest': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'latest_for'", 'null': 'True', 'to': u"orm['django_nyt.Notification']"}),
            'notification_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['django_nyt.NotificationType']"}),
            'object_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
            'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'settings': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['django_nyt.Settings']"})
        },
        u'notifications.articlesubscription': {
            'Meta': {'unique_together': "((u'subscription', u'articleplugin_ptr'),)", 'object_name': 'ArticleSubscription', 'db_table': "u'wiki_notifications_articlesubscription'", '_ormbases': [u'wiki.ArticlePlugin']},
            u'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'subscription': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['django_nyt.Subscription']", 'unique': 'True'})
        },
        u'wiki.article': {
            'Meta': {'object_name': 'Article'},
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'current_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['wiki.ArticleRevision']"}),
            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_articles'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
        },
        u'wiki.articleplugin': {
            'Meta': {'object_name': 'ArticlePlugin'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
        },
        u'wiki.articlerevision': {
            'Meta': {'ordering': "(u'created',)", 'unique_together': "((u'article', u'revision_number'),)", 'object_name': 'ArticleRevision'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.ArticleRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        }
    }

    complete_apps = ['notifications']

D wiki/plugins/notifications/south_migrations/__init__.py => wiki/plugins/notifications/south_migrations/__init__.py +0 -0
D wiki/south_migrations/0001_initial.py => wiki/south_migrations/0001_initial.py +0 -307
@@ 1,307 0,0 @@
# -*- coding: utf-8 -*-
from django.db import models
from south.db import db
from south.utils import datetime_utils as datetime
from south.v2 import SchemaMigration


class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding model 'Article'
        db.create_table(u'wiki_article', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('current_revision', self.gf('django.db.models.fields.related.OneToOneField')(blank=True, related_name=u'current_set', unique=True, null=True, to=orm['wiki.ArticleRevision'])),
            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
            ('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
            ('owner', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name=u'owned_articles', null=True, on_delete=models.SET_NULL, to=orm['auth.User'])),
            ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Group'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('group_read', self.gf('django.db.models.fields.BooleanField')(default=True)),
            ('group_write', self.gf('django.db.models.fields.BooleanField')(default=True)),
            ('other_read', self.gf('django.db.models.fields.BooleanField')(default=True)),
            ('other_write', self.gf('django.db.models.fields.BooleanField')(default=True)),
        ))
        db.send_create_signal(u'wiki', ['Article'])

        # Adding model 'ArticleForObject'
        db.create_table(u'wiki_articleforobject', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])),
            ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'content_type_set_for_articleforobject', to=orm['contenttypes.ContentType'])),
            ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
            ('is_mptt', self.gf('django.db.models.fields.BooleanField')(default=False)),
        ))
        db.send_create_signal(u'wiki', ['ArticleForObject'])

        # Adding unique constraint on 'ArticleForObject', fields ['content_type', 'object_id']
        db.create_unique(u'wiki_articleforobject', ['content_type_id', 'object_id'])

        # Adding model 'ArticleRevision'
        db.create_table(u'wiki_articlerevision', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('revision_number', self.gf('django.db.models.fields.IntegerField')()),
            ('user_message', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('automatic_log', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15, null=True, blank=True)),
            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
            ('previous_revision', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.ArticleRevision'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('locked', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])),
            ('content', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('title', self.gf('django.db.models.fields.CharField')(max_length=512)),
        ))
        db.send_create_signal(u'wiki', ['ArticleRevision'])

        # Adding unique constraint on 'ArticleRevision', fields ['article', 'revision_number']
        db.create_unique(u'wiki_articlerevision', ['article_id', 'revision_number'])

        # Adding model 'URLPath'
        db.create_table(u'wiki_urlpath', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])),
            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, null=True, blank=True)),
            ('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
            ('parent', self.gf('mptt.fields.TreeForeignKey')(blank=True, related_name=u'children', null=True, to=orm['wiki.URLPath'])),
            (u'lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
            (u'rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
            (u'tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
            (u'level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
        ))
        db.send_create_signal(u'wiki', ['URLPath'])

        # Adding unique constraint on 'URLPath', fields ['site', 'parent', 'slug']
        db.create_unique(u'wiki_urlpath', ['site_id', 'parent_id', 'slug'])

        # Adding model 'ArticlePlugin'
        db.create_table(u'wiki_articleplugin', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])),
            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
        ))
        db.send_create_signal(u'wiki', ['ArticlePlugin'])

        # Adding model 'ReusablePlugin'
        db.create_table(u'wiki_reusableplugin', (
            (u'articleplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.ArticlePlugin'], unique=True, primary_key=True)),
        ))
        db.send_create_signal(u'wiki', ['ReusablePlugin'])

        # Adding M2M table for field articles on 'ReusablePlugin'
        m2m_table_name = db.shorten_name(u'wiki_reusableplugin_articles')
        db.create_table(m2m_table_name, (
            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
            ('reusableplugin', models.ForeignKey(orm[u'wiki.reusableplugin'], null=False)),
            ('article', models.ForeignKey(orm[u'wiki.article'], null=False))
        ))
        db.create_unique(m2m_table_name, ['reusableplugin_id', 'article_id'])

        # Adding model 'SimplePlugin'
        db.create_table(u'wiki_simpleplugin', (
            (u'articleplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.ArticlePlugin'], unique=True, primary_key=True)),
            ('article_revision', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.ArticleRevision'])),
        ))
        db.send_create_signal(u'wiki', ['SimplePlugin'])

        # Adding model 'RevisionPlugin'
        db.create_table(u'wiki_revisionplugin', (
            (u'articleplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.ArticlePlugin'], unique=True, primary_key=True)),
            ('current_revision', self.gf('django.db.models.fields.related.OneToOneField')(blank=True, related_name=u'plugin_set', unique=True, null=True, to=orm['wiki.RevisionPluginRevision'])),
        ))
        db.send_create_signal(u'wiki', ['RevisionPlugin'])

        # Adding model 'RevisionPluginRevision'
        db.create_table(u'wiki_revisionpluginrevision', (
            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('revision_number', self.gf('django.db.models.fields.IntegerField')()),
            ('user_message', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('automatic_log', self.gf('django.db.models.fields.TextField')(blank=True)),
            ('ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15, null=True, blank=True)),
            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
            ('previous_revision', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.RevisionPluginRevision'], null=True, on_delete=models.SET_NULL, blank=True)),
            ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('locked', self.gf('django.db.models.fields.BooleanField')(default=False)),
            ('plugin', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'revision_set', to=orm['wiki.RevisionPlugin'])),
        ))
        db.send_create_signal(u'wiki', ['RevisionPluginRevision'])


    def backwards(self, orm):
        # Removing unique constraint on 'URLPath', fields ['site', 'parent', 'slug']
        db.delete_unique(u'wiki_urlpath', ['site_id', 'parent_id', 'slug'])

        # Removing unique constraint on 'ArticleRevision', fields ['article', 'revision_number']
        db.delete_unique(u'wiki_articlerevision', ['article_id', 'revision_number'])

        # Removing unique constraint on 'ArticleForObject', fields ['content_type', 'object_id']
        db.delete_unique(u'wiki_articleforobject', ['content_type_id', 'object_id'])

        # Deleting model 'Article'
        db.delete_table(u'wiki_article')

        # Deleting model 'ArticleForObject'
        db.delete_table(u'wiki_articleforobject')

        # Deleting model 'ArticleRevision'
        db.delete_table(u'wiki_articlerevision')

        # Deleting model 'URLPath'
        db.delete_table(u'wiki_urlpath')

        # Deleting model 'ArticlePlugin'
        db.delete_table(u'wiki_articleplugin')

        # Deleting model 'ReusablePlugin'
        db.delete_table(u'wiki_reusableplugin')

        # Removing M2M table for field articles on 'ReusablePlugin'
        db.delete_table(db.shorten_name(u'wiki_reusableplugin_articles'))

        # Deleting model 'SimplePlugin'
        db.delete_table(u'wiki_simpleplugin')

        # Deleting model 'RevisionPlugin'
        db.delete_table(u'wiki_revisionplugin')

        # Deleting model 'RevisionPluginRevision'
        db.delete_table(u'wiki_revisionpluginrevision')


    models = {
        u'auth.group': {
            'Meta': {'object_name': 'Group'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
        },
        u'auth.permission': {
            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        u'auth.user': {
            'Meta': {'object_name': 'User'},
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
        },
        u'contenttypes.contenttype': {
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
        },
        u'sites.site': {
            'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"},
            'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        u'wiki.article': {
            'Meta': {'object_name': 'Article'},
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'current_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['wiki.ArticleRevision']"}),
            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_articles'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
        },
        u'wiki.articleforobject': {
            'Meta': {'unique_together': "((u'content_type', u'object_id'),)", 'object_name': 'ArticleForObject'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'content_type_set_for_articleforobject'", 'to': u"orm['contenttypes.ContentType']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'is_mptt': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
        },
        u'wiki.articleplugin': {
            'Meta': {'object_name': 'ArticlePlugin'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
        },
        u'wiki.articlerevision': {
            'Meta': {'ordering': "(u'created',)", 'unique_together': "((u'article', u'revision_number'),)", 'object_name': 'ArticleRevision'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.ArticleRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        },
        u'wiki.reusableplugin': {
            'Meta': {'object_name': 'ReusablePlugin', '_ormbases': [u'wiki.ArticlePlugin']},
            u'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'articles': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'shared_plugins_set'", 'symmetrical': 'False', 'to': u"orm['wiki.Article']"})
        },
        u'wiki.revisionplugin': {
            'Meta': {'object_name': 'RevisionPlugin', '_ormbases': [u'wiki.ArticlePlugin']},
            u'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "u'plugin_set'", 'unique': 'True', 'null': 'True', 'to': u"orm['wiki.RevisionPluginRevision']"})
        },
        u'wiki.revisionpluginrevision': {
            'Meta': {'ordering': "(u'-created',)", 'object_name': 'RevisionPluginRevision'},
            'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
            'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
            'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'plugin': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'revision_set'", 'to': u"orm['wiki.RevisionPlugin']"}),
            'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.RevisionPluginRevision']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'revision_number': ('django.db.models.fields.IntegerField', [], {}),
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
            'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
        },
        u'wiki.simpleplugin': {
            'Meta': {'object_name': 'SimplePlugin', '_ormbases': [u'wiki.ArticlePlugin']},
            'article_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.ArticleRevision']"}),
            u'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'})
        },
        u'wiki.urlpath': {
            'Meta': {'unique_together': "((u'site', u'parent', u'slug'),)", 'object_name': 'URLPath'},
            'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['wiki.Article']"}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
            u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
            'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['wiki.URLPath']"}),
            u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
            'site': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['sites.Site']"}),
            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
            u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
        }
    }

    complete_apps = ['wiki']

D wiki/south_migrations/__init__.py => wiki/south_migrations/__init__.py +0 -0
R runtests.py => wiki/tests/settings.py +57 -109
@@ 1,111 1,59 @@
#!/usr/bin/env python
from __future__ import absolute_import, unicode_literals

import sys

import django
import pytest
from django.conf import settings

# Run py.tests
# Compatibility testing patches on the py-moneyed

settings_dict = dict(
    DEBUG=True,
    AUTH_USER_MODEL='testdata.CustomUser',
    WIKI_GROUP_MODEL='testdata.CustomGroup',
    DATABASES={
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
        }
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
    }
}

DEBUG = True
AUTH_USER_MODEL = 'testdata.CustomUser'
WIKI_GROUP_MODEL = 'testdata.CustomGroup'
SITE_ID = 1
ROOT_URLCONF = 'wiki.tests.testdata.urls'
INSTALLED_APPS = [
    'wiki.tests.testdata',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.admin',
    'django.contrib.humanize',
    'django.contrib.sites',
    'django_nyt',
    'mptt',
    'sekizai',
    'sorl.thumbnail',
    'wiki',
    'wiki.plugins.attachments',
    'wiki.plugins.notifications',
    'wiki.plugins.images',
    'wiki.plugins.macros',
]
MIDDLEWARE_CLASSES = [
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
]
USE_TZ = True
SECRET_KEY = 'b^fv_)t39h%9p40)fnkfblo##jkr!$0)lkp6bpy!fi*f$4*92!'
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                "django.contrib.auth.context_processors.auth",
                "django.template.context_processors.debug",
                "django.template.context_processors.i18n",
                "django.template.context_processors.media",
                "django.template.context_processors.request",
                "django.template.context_processors.static",
                "django.template.context_processors.tz",
                "django.contrib.messages.context_processors.messages",
                "sekizai.context_processors.sekizai",
            ]
        },
    },
    SITE_ID=1,
    ROOT_URLCONF='wiki.tests.testdata.urls',
    INSTALLED_APPS=[
        'wiki.tests.testdata',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.admin',
        'django.contrib.humanize',
        'django.contrib.sites',
        'django_nyt',
        'mptt',
        'sekizai',
        'sorl.thumbnail',
        'wiki',
        'wiki.plugins.attachments',
        'wiki.plugins.notifications',
        'wiki.plugins.images',
        'wiki.plugins.macros',
    ] + (['south'] if django.VERSION < (1, 7) else []),
    MIDDLEWARE_CLASSES=[
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
    ],
    USE_TZ=True,
    SOUTH_TESTS_MIGRATE=True,
    SECRET_KEY='b^fv_)t39h%9p40)fnkfblo##jkr!$0)lkp6bpy!fi*f$4*92!',
)

TEMPLATE_CONTEXT_PROCESSORS = [
    "django.contrib.auth.context_processors.auth",
    "django.template.context_processors.debug",
    "django.template.context_processors.i18n",
    "django.template.context_processors.media",
    "django.template.context_processors.request",
    "django.template.context_processors.static",
    "django.template.context_processors.tz",
    "django.contrib.messages.context_processors.messages",
    "sekizai.context_processors.sekizai",
]

if django.VERSION < (1, 8):
    settings_dict.update(dict(
        TEMPLATE_CONTEXT_PROCESSORS=[p.replace('django.template.context_processors',
                                               'django.core.context_processors')
                                     for p in TEMPLATE_CONTEXT_PROCESSORS]
    ))
else:
    settings_dict.update(dict(
        TEMPLATES=[
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'APP_DIRS': True,
                'OPTIONS': {
                    'context_processors': TEMPLATE_CONTEXT_PROCESSORS
                },
            },
        ]
    ))

settings.configure(**settings_dict)


# If you use South for migrations, uncomment this to monkeypatch
# syncdb to get migrations to run.
if django.VERSION < (1, 7):
    from south.management.commands import patch_for_test_db_setup
    patch_for_test_db_setup()

argv = [sys.argv[0], "test", "--traceback"]

# python setup.py test calls script with just 'test'
if len(sys.argv) == 1 or sys.argv[1] == 'test':
    # Nothing following 'runtests.py':
    if django.VERSION < (1, 6):
        argv.extend(["wiki", "attachments"])
    else:
        argv.extend(["wiki.tests", "wiki.plugins.attachments.tests"])
else:
    # Allow tests to be specified:
    argv.extend(sys.argv[1:])


failures = pytest.main()

if failures:
    sys.exit(failures)

M wiki/tests/test_models.py => wiki/tests/test_models.py +5 -12
@@ 1,26 1,19 @@
from __future__ import absolute_import, unicode_literals

from django.apps import apps
from django.conf.urls import url
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.test.testcases import TestCase

from wiki.conf import settings
from wiki.managers import ArticleManager
from wiki.models import Article, ArticleRevision, URLPath
from wiki.urls import WikiURLPatterns

User = get_user_model()




# Backwards compatibility with Django < 1.7
try:
    from django.apps import apps
except ImportError:
    from django.contrib.auth.models import Group
else:
    from wiki.conf import settings
    Group = apps.get_model(settings.GROUP_MODEL)
User = get_user_model()
Group = apps.get_model(settings.GROUP_MODEL)


class WikiCustomUrlPatterns(WikiURLPatterns):

M wiki/tests/test_urls.py => wiki/tests/test_urls.py +1 -7
@@ 1,7 1,7 @@
from __future__ import absolute_import, unicode_literals

from django import VERSION as DJANGO_VERSION
from django.conf.urls import patterns, url
from django.conf.urls import url
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.test.testcases import TestCase


@@ 14,9 14,6 @@ from wiki.urls import WikiURLPatterns
User = get_user_model()





class WikiCustomUrlPatterns(WikiURLPatterns):

    def get_article_urls(self):


@@ 41,9 38,6 @@ urlpatterns = [
    url(r'', get_wiki_pattern(url_config_class=WikiCustomUrlPatterns))
]

if DJANGO_VERSION < (1, 8):
    urlpatterns = patterns('', *urlpatterns)


@wiki_override_settings(WIKI_URL_CONFIG_CLASS='wiki.tests.test_models.WikiCustomUrlPatterns',
                        ROOT_URLCONF='wiki.tests.test_urls')

M wiki/tests/test_views.py => wiki/tests/test_views.py +1 -1
@@ 3,7 3,7 @@ from __future__ import absolute_import, print_function, unicode_literals
import pprint

from django.contrib.auth import authenticate
from django.core.urlresolvers import reverse
from wiki.models import reverse

from .base import ArticleWebTestBase, WebTestBase


M wiki/tests/testdata/urls.py => wiki/tests/testdata/urls.py +1 -5
@@ 1,8 1,7 @@
from __future__ import absolute_import, unicode_literals

from django import VERSION as DJANGO_VERSION
from django.conf import settings
from django.conf.urls import include, patterns, url
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django_nyt.urls import get_pattern as get_notify_pattern


@@ 28,6 27,3 @@ urlpatterns += [
    url(r'^notify/', get_notify_pattern()),
    url(r'', get_wiki_pattern())
]

if DJANGO_VERSION < (1, 8):
    urlpatterns = patterns('', *urlpatterns)

M wiki/urls.py => wiki/urls.py +39 -44
@@ 1,7 1,6 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

from django import VERSION as DJANGO_VERSION
from django.conf.urls import include, url
from wiki.conf import settings
from wiki.core.plugins import registry


@@ 62,20 61,20 @@ class WikiURLPatterns(object):

    def get_root_urls(self):
        urlpatterns = [
            url('^$',
            url(r'^$',
                self.article_view_class.as_view(),
                name='root',
                kwargs={'path': ''}),
            url('^create-root/$',
            url(r'^create-root/$',
                article.CreateRootView.as_view(),
                name='root_create'),
            url('^missing-root/$',
            url(r'^missing-root/$',
                article.MissingRootView.as_view(),
                name='root_missing'),
            url('^_search/$',
            url(r'^_search/$',
                get_class_from_str(self.search_view_class).as_view(),
                name='search'),
            url('^_revision/diff/(?P<revision_id>\d+)/$',
            url(r'^_revision/diff/(?P<revision_id>\d+)/$',
                self.article_diff_view,
                name='diff'),
        ]


@@ 92,16 91,16 @@ class WikiURLPatterns(object):
    def get_accounts_urls(self):
        if settings.ACCOUNT_HANDLING:
            urlpatterns = [
                url('^_accounts/sign-up/$',
                url(r'^_accounts/sign-up/$',
                    self.signup_view_class.as_view(),
                    name='signup'),
                url('^_accounts/logout/$',
                url(r'^_accounts/logout/$',
                    self.logout_view_class.as_view(),
                    name='logout'),
                url('^_accounts/login/$',
                url(r'^_accounts/login/$',
                    self.login_view_class.as_view(),
                    name='login'),
                url('^_accounts/settings/$',
                url(r'^_accounts/settings/$',
                    self.profile_update_view_class.as_view(),
                    name='profile_update'),
            ]


@@ 114,14 113,14 @@ class WikiURLPatterns(object):
            # This one doesn't work because it don't know
            # where to redirect after...
            url(
                '^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$',
                r'^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$',
                self.revision_change_view_class.as_view(),
                name='change_revision'),
            url('^_revision/preview/(?P<article_id>\d+)/$',
            url(r'^_revision/preview/(?P<article_id>\d+)/$',
                self.article_preview_view_class.as_view(),
                name='preview_revision'),
            url(
                '^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$',
                r'^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$',
                self.revision_merge_view,
                name='merge_revision_preview',
                kwargs={


@@ 132,39 131,39 @@ class WikiURLPatterns(object):
    def get_article_urls(self):
        urlpatterns = [
            # Paths decided by article_ids
            url('^(?P<article_id>\d+)/$',
            url(r'^(?P<article_id>\d+)/$',
                self.article_view_class.as_view(),
                name='get'),
            url('^(?P<article_id>\d+)/delete/$',
            url(r'^(?P<article_id>\d+)/delete/$',
                self.article_delete_view_class.as_view(),
                name='delete'),
            url('^(?P<article_id>\d+)/deleted/$',
            url(r'^(?P<article_id>\d+)/deleted/$',
                self.article_deleted_view_class.as_view(),
                name='deleted'),
            url('^(?P<article_id>\d+)/edit/$',
            url(r'^(?P<article_id>\d+)/edit/$',
                self.article_edit_view_class.as_view(),
                name='edit'),
            url('^(?P<article_id>\d+)/preview/$',
            url(r'^(?P<article_id>\d+)/preview/$',
                self.article_preview_view_class.as_view(),
                name='preview'),
            url('^(?P<article_id>\d+)/history/$',
            url(r'^(?P<article_id>\d+)/history/$',
                self.article_history_view_class.as_view(),
                name='history'),
            url('^(?P<article_id>\d+)/settings/$',
            url(r'^(?P<article_id>\d+)/settings/$',
                self.article_settings_view_class.as_view(),
                name='settings'),
            url('^(?P<article_id>\d+)/source/$',
            url(r'^(?P<article_id>\d+)/source/$',
                self.article_source_view_class.as_view(),
                name='source'),
            url(
                '^(?P<article_id>\d+)/revision/change/(?P<revision_id>\d+)/$',
                r'^(?P<article_id>\d+)/revision/change/(?P<revision_id>\d+)/$',
                self.revision_change_view_class.as_view(),
                name='change_revision'),
            url(
                '^(?P<article_id>\d+)/revision/merge/(?P<revision_id>\d+)/$',
                r'^(?P<article_id>\d+)/revision/merge/(?P<revision_id>\d+)/$',
                self.revision_merge_view,
                name='merge_revision'),
            url('^(?P<article_id>\d+)/plugin/(?P<slug>\w+)/$',
            url(r'^(?P<article_id>\d+)/plugin/(?P<slug>\w+)/$',
                self.article_plugin_view_class.as_view(),
                name='plugin'),
        ]


@@ 173,46 172,46 @@ class WikiURLPatterns(object):
    def get_article_path_urls(self):
        urlpatterns = [
            # Paths decided by URLs
            url('^(?P<path>.+/|)_create/$',
            url(r'^(?P<path>.+/|)_create/$',
                self.article_create_view_class.as_view(),
                name='create'),
            url('^(?P<path>.+/|)_delete/$',
            url(r'^(?P<path>.+/|)_delete/$',
                self.article_delete_view_class.as_view(),
                name='delete'),
            url('^(?P<path>.+/|)_deleted/$',
            url(r'^(?P<path>.+/|)_deleted/$',
                self.article_deleted_view_class.as_view(),
                name='deleted'),
            url('^(?P<path>.+/|)_edit/$',
            url(r'^(?P<path>.+/|)_edit/$',
                self.article_edit_view_class.as_view(),
                name='edit'),
            url('^(?P<path>.+/|)_preview/$',
            url(r'^(?P<path>.+/|)_preview/$',
                self.article_preview_view_class.as_view(),
                name='preview'),
            url('^(?P<path>.+/|)_history/$',
            url(r'^(?P<path>.+/|)_history/$',
                self.article_history_view_class.as_view(),
                name='history'),
            url('^(?P<path>.+/|)_dir/$',
            url(r'^(?P<path>.+/|)_dir/$',
                self.article_dir_view_class.as_view(),
                name='dir'),
            url('^(?P<path>.+/|)_settings/$',
            url(r'^(?P<path>.+/|)_settings/$',
                self.article_settings_view_class.as_view(),
                name='settings'),
            url('^(?P<path>.+/|)_source/$',
            url(r'^(?P<path>.+/|)_source/$',
                self.article_source_view_class.as_view(),
                name='source'),
            url(
                '^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$',
                r'^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$',
                self.revision_change_view_class.as_view(),
                name='change_revision'),
            url(
                '^(?P<path>.+/|)_revision/merge/(?P<revision_id>\d+)/$',
                r'^(?P<path>.+/|)_revision/merge/(?P<revision_id>\d+)/$',
                self.revision_merge_view,
                name='merge_revision'),
            url('^(?P<path>.+/|)_plugin/(?P<slug>\w+)/$',
            url(r'^(?P<path>.+/|)_plugin/(?P<slug>\w+)/$',
                self.article_plugin_view_class.as_view(),
                name='plugin'),
            # This should always go last!
            url('^(?P<path>.+/|)$',
            url(r'^(?P<path>.+/|)$',
                self.article_view_class.as_view(),
                name='get'),
        ]


@@ 226,14 225,14 @@ class WikiURLPatterns(object):
            if slug:
                article_urlpatterns = plugin.urlpatterns.get('article', [])
                urlpatterns += [
                    url('^(?P<article_id>\d+)/plugin/' + slug + '/',
                    url(r'^(?P<article_id>\d+)/plugin/' + slug + '/',
                        include(article_urlpatterns)),
                    url('^(?P<path>.+/|)_plugin/' + slug + '/',
                    url(r'^(?P<path>.+/|)_plugin/' + slug + '/',
                        include(article_urlpatterns)),
                ]
                root_urlpatterns = plugin.urlpatterns.get('root', [])
                urlpatterns += [
                    url('^_plugin/' + slug + '/', include(root_urlpatterns)),
                    url(r'^_plugin/' + slug + '/', include(root_urlpatterns)),
                ]
        return urlpatterns



@@ 252,10 251,6 @@ def get_pattern(app_name="wiki", namespace="wiki", url_config_class=None):
            url_config_class = get_class_from_str(url_config_classname)
    urlpatterns = url_config_class().get_urls()

    if DJANGO_VERSION < (1, 8):
        from django.conf.urls import patterns
        urlpatterns = patterns('', *urlpatterns)

    return urlpatterns, app_name, namespace