~netlandish/django-wiki

3843e51ed280f2da6d18bdd30be250916a570ab7 — Benjamin Bach 3 years ago 13b6b8c
Reorder and lint import statements, add pre-commit hook for reordering
77 files changed, 290 insertions(+), 189 deletions(-)

M .pre-commit-config.yaml
M docs/conf.py
M setup.py
M src/wiki/__init__.py
M src/wiki/admin.py
M src/wiki/core/markdown/__init__.py
M src/wiki/core/markdown/mdx/codehilite.py
M src/wiki/decorators.py
M src/wiki/forms_account_handling.py
M src/wiki/managers.py
M src/wiki/migrations/0001_initial.py
M src/wiki/migrations/0003_mptt_upgrade.py
M src/wiki/models/__init__.py
M src/wiki/models/article.py
M src/wiki/models/pluginbase.py
M src/wiki/models/urlpath.py
M src/wiki/plugins/attachments/forms.py
M src/wiki/plugins/attachments/migrations/0001_initial.py
M src/wiki/plugins/attachments/migrations/0002_auto_20151118_1816.py
M src/wiki/plugins/attachments/models.py
M src/wiki/plugins/attachments/views.py
M src/wiki/plugins/attachments/wiki_plugin.py
M src/wiki/plugins/editsection/views.py
M src/wiki/plugins/editsection/wiki_plugin.py
M src/wiki/plugins/globalhistory/wiki_plugin.py
M src/wiki/plugins/images/forms.py
M src/wiki/plugins/images/markdown_extensions.py
M src/wiki/plugins/images/migrations/0001_initial.py
M src/wiki/plugins/images/migrations/0002_auto_20151118_1811.py
M src/wiki/plugins/images/models.py
M src/wiki/plugins/images/templatetags/wiki_images_tags.py
M src/wiki/plugins/images/views.py
M src/wiki/plugins/images/wiki_plugin.py
M src/wiki/plugins/links/wiki_plugin.py
M src/wiki/plugins/macros/mdx/toc.py
M src/wiki/plugins/macros/mdx/wikilinks.py
M src/wiki/plugins/notifications/forms.py
M src/wiki/plugins/notifications/migrations/0001_initial.py
M src/wiki/plugins/notifications/migrations/0002_auto_20151118_1811.py
M src/wiki/plugins/notifications/views.py
M src/wiki/plugins/notifications/wiki_plugin.py
M src/wiki/plugins/redlinks/mdx/redlinks.py
M src/wiki/sites.py
M src/wiki/urls.py
M src/wiki/views/accounts.py
M src/wiki/views/article.py
M testproject/testproject/settings/base.py
M testproject/testproject/settings/local.py
M testproject/testproject/urls.py
M testproject/testproject/wsgi.py
M tests/base.py
M tests/core/test_accounts.py
M tests/core/test_basic.py
M tests/core/test_checks.py
M tests/core/test_forms.py
M tests/core/test_managers.py
M tests/core/test_models.py
M tests/core/test_sites.py
M tests/core/test_template_filters.py
M tests/core/test_template_tags.py
M tests/core/test_urls.py
M tests/core/test_views.py
M tests/plugins/attachments/test_commands.py
M tests/plugins/attachments/test_models.py
M tests/plugins/attachments/test_views.py
M tests/plugins/editsection/test_editsection.py
M tests/plugins/globalhistory/test_globalhistory.py
M tests/plugins/images/test_markdown.py
M tests/plugins/images/test_views.py
M tests/plugins/links/test_urlize.py
M tests/plugins/macros/test_links.py
M tests/plugins/macros/test_macro.py
M tests/plugins/notifications/test_forms.py
M tests/plugins/notifications/test_views.py
M tests/plugins/redlinks/test_redlinks.py
M tests/testdata/migrations/0001_initial.py
M tests/testdata/urls.py
M .pre-commit-config.yaml => .pre-commit-config.yaml +5 -0
@@ 16,3 16,8 @@ repos:
    hooks:
    - id: black
      language_version: python3

  - repo: https://github.com/asottile/reorder_python_imports
    rev: v2.3.0
    hooks:
    -   id: reorder-python-imports

M docs/conf.py => docs/conf.py +2 -9
@@ 1,13 1,12 @@
import inspect
import os
import sys
from datetime import datetime

import bleach

import django
from django.utils.encoding import force_text

from datetime import datetime

#
# django-wiki documentation build configuration file, created by
# sphinx-quickstart on Mon Jul 23 16:13:51 2012.


@@ 19,20 18,14 @@ from datetime import datetime
#
# All configuration values have a default; values that are commented out
# serve to show the default.


# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'

import django

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.

M setup.py => setup.py +2 -2
@@ 1,11 1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
from glob import glob

from setuptools import find_packages, setup
from setuptools import find_packages
from setuptools import setup

sys.path.append(os.path.join(os.path.dirname(__file__), "src"))


M src/wiki/__init__.py => src/wiki/__init__.py +0 -2
@@ 13,8 13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with django-wiki. If not, see <http://www.gnu.org/licenses/>.


from wiki.core.version import get_version

default_app_config = "wiki.apps.WikiConfig"

M src/wiki/admin.py => src/wiki/admin.py +2 -1
@@ 4,7 4,8 @@ from django.contrib.contenttypes.admin import GenericTabularInline
from django.utils.translation import gettext_lazy as _
from mptt.admin import MPTTModelAdmin

from . import editors, models
from . import editors
from . import models


class ArticleObjectAdmin(GenericTabularInline):

M src/wiki/core/markdown/__init__.py => src/wiki/core/markdown/__init__.py +5 -1
@@ 39,7 39,11 @@ class ArticleMarkdown(markdown.Markdown):
            attrs.update(plugin_registry.get_html_attributes().items())

            html = bleach.clean(
                html, tags=tags, attributes=attrs, styles=settings.MARKDOWN_HTML_STYLES, strip=True
                html,
                tags=tags,
                attributes=attrs,
                styles=settings.MARKDOWN_HTML_STYLES,
                strip=True,
            )
        return html


M src/wiki/core/markdown/mdx/codehilite.py => src/wiki/core/markdown/mdx/codehilite.py +2 -1
@@ 1,7 1,8 @@
import logging
import re

from markdown.extensions.codehilite import CodeHilite, CodeHiliteExtension
from markdown.extensions.codehilite import CodeHilite
from markdown.extensions.codehilite import CodeHiliteExtension
from markdown.preprocessors import Preprocessor
from markdown.treeprocessors import Treeprocessor


M src/wiki/decorators.py => src/wiki/decorators.py +5 -6
@@ 1,11 1,10 @@
from functools import wraps

from django.http import (
    HttpResponseForbidden,
    HttpResponseNotFound,
    HttpResponseRedirect,
)
from django.shortcuts import get_object_or_404, redirect
from django.http import HttpResponseForbidden
from django.http import HttpResponseNotFound
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.http import urlquote

M src/wiki/forms_account_handling.py => src/wiki/forms_account_handling.py +2 -1
@@ 6,7 6,8 @@ from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields import CharField, EmailField
from django.db.models.fields import CharField
from django.db.models.fields import EmailField
from django.utils.translation import gettext_lazy as _
from wiki.conf import settings


M src/wiki/managers.py => src/wiki/managers.py +4 -2
@@ 1,6 1,8 @@
from django.db import models
from django.db.models import Count, Q
from django.db.models.query import EmptyQuerySet, QuerySet
from django.db.models import Count
from django.db.models import Q
from django.db.models.query import EmptyQuerySet
from django.db.models.query import QuerySet
from mptt.managers import TreeManager



M src/wiki/migrations/0001_initial.py => src/wiki/migrations/0001_initial.py +2 -1
@@ 1,7 1,8 @@
import django.db.models.deletion
import mptt.fields
from django.conf import settings
from django.db import migrations, models
from django.db import migrations
from django.db import models
from django.db.models.fields import GenericIPAddressField as IPAddressField
from wiki.conf.settings import GROUP_MODEL


M src/wiki/migrations/0003_mptt_upgrade.py => src/wiki/migrations/0003_mptt_upgrade.py +2 -2
@@ 1,8 1,8 @@
# Upgrades fields changed in django-mptt
# See: https://github.com/django-mptt/django-mptt/pull/578
# Generated by Django 2.2.7 on 2020-02-06 20:36

from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M src/wiki/models/__init__.py => src/wiki/models/__init__.py +2 -1
@@ 1,4 1,5 @@
from django import shortcuts, urls
from django import shortcuts
from django import urls
from django.urls import base
from django.utils.functional import lazy


M src/wiki/models/article.py => src/wiki/models/article.py +3 -1
@@ 4,7 4,9 @@ from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
from django.db import models
from django.db.models.fields import GenericIPAddressField as IPAddressField
from django.db.models.signals import post_save, pre_delete, pre_save
from django.db.models.signals import post_save
from django.db.models.signals import pre_delete
from django.db.models.signals import pre_save
from django.urls import reverse
from django.utils import translation
from django.utils.safestring import mark_safe

M src/wiki/models/pluginbase.py => src/wiki/models/pluginbase.py +2 -1
@@ 23,7 23,8 @@ from django.db.models import signals
from django.utils.translation import gettext_lazy as _
from wiki.decorators import disable_signal_for_loaddata

from .article import ArticleRevision, BaseRevisionMixin
from .article import ArticleRevision
from .article import BaseRevisionMixin

__all__ = [
    "ArticlePlugin",

M src/wiki/models/urlpath.py => src/wiki/models/urlpath.py +11 -5
@@ 5,17 5,23 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.db import models, transaction
from django.db.models.signals import post_save, pre_delete
from django.db import models
from django.db import transaction
from django.db.models.signals import post_save
from django.db.models.signals import pre_delete
from django.urls import reverse
from django.utils.translation import gettext, gettext_lazy as _
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel
from wiki import managers
from wiki.conf import settings
from wiki.core.exceptions import MultipleRootURLs, NoRootURL
from wiki.core.exceptions import MultipleRootURLs
from wiki.core.exceptions import NoRootURL
from wiki.decorators import disable_signal_for_loaddata
from wiki.models.article import Article, ArticleForObject, ArticleRevision
from wiki.models.article import Article
from wiki.models.article import ArticleForObject
from wiki.models.article import ArticleRevision

__all__ = [
    "URLPath",

M src/wiki/plugins/attachments/forms.py => src/wiki/plugins/attachments/forms.py +2 -1
@@ 3,7 3,8 @@ import zipfile

from django import forms
from django.core.files.uploadedfile import File
from django.utils.translation import gettext, gettext_lazy as _
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from wiki.core.permissions import can_moderate
from wiki.plugins.attachments import models
from wiki.plugins.attachments.models import IllegalFileExtension

M src/wiki/plugins/attachments/migrations/0001_initial.py => src/wiki/plugins/attachments/migrations/0001_initial.py +2 -1
@@ 1,7 1,8 @@
import django.db.models.deletion
import wiki.plugins.attachments.models
from django.conf import settings
from django.db import migrations, models
from django.db import migrations
from django.db import models
from django.db.models.fields import GenericIPAddressField as IPAddressField



M src/wiki/plugins/attachments/migrations/0002_auto_20151118_1816.py => src/wiki/plugins/attachments/migrations/0002_auto_20151118_1816.py +2 -1
@@ 1,4 1,5 @@
from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M src/wiki/plugins/attachments/models.py => src/wiki/plugins/attachments/models.py +2 -1
@@ 3,7 3,8 @@ import os
from django.conf import settings as django_settings
from django.db import models
from django.db.models import signals
from django.utils.translation import gettext, gettext_lazy as _
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from wiki import managers
from wiki.decorators import disable_signal_for_loaddata
from wiki.models.article import BaseRevisionMixin

M src/wiki/plugins/attachments/views.py => src/wiki/plugins/attachments/views.py +13 -5
@@ 1,15 1,23 @@
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.http import Http404
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views.generic import FormView, ListView, TemplateView, View
from django.views.generic import FormView
from django.views.generic import ListView
from django.views.generic import TemplateView
from django.views.generic import View
from wiki.core.http import send_file
from wiki.core.paginator import WikiPaginator
from wiki.decorators import get_article, response_forbidden
from wiki.plugins.attachments import forms, models, settings
from wiki.decorators import get_article
from wiki.decorators import response_forbidden
from wiki.plugins.attachments import forms
from wiki.plugins.attachments import models
from wiki.plugins.attachments import settings
from wiki.views.mixins import ArticleMixin



M src/wiki/plugins/attachments/wiki_plugin.py => src/wiki/plugins/attachments/wiki_plugin.py +5 -2
@@ 1,8 1,11 @@
from django.urls import include, re_path
from django.urls import include
from django.urls import re_path
from django.utils.translation import gettext as _
from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin
from wiki.plugins.attachments import models, settings, views
from wiki.plugins.attachments import models
from wiki.plugins.attachments import settings
from wiki.plugins.attachments import views
from wiki.plugins.attachments.markdown_extensions import AttachmentExtension
from wiki.plugins.notifications.settings import ARTICLE_EDIT
from wiki.plugins.notifications.util import truncate_title

M src/wiki/plugins/editsection/views.py => src/wiki/plugins/editsection/views.py +2 -1
@@ 1,7 1,8 @@
import re

from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy
from wiki import models

M src/wiki/plugins/editsection/wiki_plugin.py => src/wiki/plugins/editsection/wiki_plugin.py +2 -1
@@ 3,7 3,8 @@ from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin
from wiki.plugins.editsection.markdown_extensions import EditSectionExtension

from . import settings, views
from . import settings
from . import views


class EditSectionPlugin(BasePlugin):

M src/wiki/plugins/globalhistory/wiki_plugin.py => src/wiki/plugins/globalhistory/wiki_plugin.py +2 -1
@@ 2,7 2,8 @@ from django.urls import re_path
from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin

from . import settings, views
from . import settings
from . import views


class GlobalHistoryPlugin(BasePlugin):

M src/wiki/plugins/images/forms.py => src/wiki/plugins/images/forms.py +2 -1
@@ 1,5 1,6 @@
from django import forms
from django.utils.translation import gettext, gettext_lazy as _
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from wiki.core.plugins.base import PluginSidebarFormMixin
from wiki.plugins.images import models


M src/wiki/plugins/images/markdown_extensions.py => src/wiki/plugins/images/markdown_extensions.py +2 -1
@@ 1,6 1,7 @@
import markdown
from django.template.loader import render_to_string
from wiki.plugins.images import models, settings
from wiki.plugins.images import models
from wiki.plugins.images import settings

IMAGE_RE = (
    r"(?:(?im)"

M src/wiki/plugins/images/migrations/0001_initial.py => src/wiki/plugins/images/migrations/0001_initial.py +2 -1
@@ 1,5 1,6 @@
import wiki.plugins.images.models
from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M src/wiki/plugins/images/migrations/0002_auto_20151118_1811.py => src/wiki/plugins/images/migrations/0002_auto_20151118_1811.py +2 -1
@@ 1,4 1,5 @@
from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M src/wiki/plugins/images/models.py => src/wiki/plugins/images/models.py +4 -2
@@ 3,8 3,10 @@ import os.path
from django.conf import settings as django_settings
from django.db import models
from django.db.models import signals
from django.utils.translation import gettext, gettext_lazy as _
from wiki.models.pluginbase import RevisionPlugin, RevisionPluginRevision
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from wiki.models.pluginbase import RevisionPlugin
from wiki.models.pluginbase import RevisionPluginRevision

from . import settings


M src/wiki/plugins/images/templatetags/wiki_images_tags.py => src/wiki/plugins/images/templatetags/wiki_images_tags.py +2 -1
@@ 1,5 1,6 @@
from django import template
from wiki.plugins.images import models, settings
from wiki.plugins.images import models
from wiki.plugins.images import settings

register = template.Library()


M src/wiki/plugins/images/views.py => src/wiki/plugins/images/views.py +7 -3
@@ 1,16 1,20 @@
import logging

from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views.generic import FormView, ListView, RedirectView
from django.views.generic import FormView
from django.views.generic import ListView
from django.views.generic import RedirectView
from wiki.conf import settings as wiki_settings
from wiki.core.paginator import WikiPaginator
from wiki.decorators import get_article
from wiki.models.pluginbase import RevisionPluginRevision
from wiki.plugins.images import forms, models
from wiki.plugins.images import forms
from wiki.plugins.images import models
from wiki.views.mixins import ArticleMixin

logger = logging.getLogger(__name__)

M src/wiki/plugins/images/wiki_plugin.py => src/wiki/plugins/images/wiki_plugin.py +4 -1
@@ 2,7 2,10 @@ from django.urls import re_path
from django.utils.translation import gettext as _
from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin
from wiki.plugins.images import forms, models, settings, views
from wiki.plugins.images import forms
from wiki.plugins.images import models
from wiki.plugins.images import settings
from wiki.plugins.images import views
from wiki.plugins.images.markdown_extensions import ImageExtension
from wiki.plugins.notifications.settings import ARTICLE_EDIT
from wiki.plugins.notifications.util import truncate_title

M src/wiki/plugins/links/wiki_plugin.py => src/wiki/plugins/links/wiki_plugin.py +4 -2
@@ 1,8 1,10 @@
from django.urls import re_path, reverse_lazy
from django.urls import re_path
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin
from wiki.plugins.links import settings, views
from wiki.plugins.links import settings
from wiki.plugins.links import views
from wiki.plugins.links.mdx.djangowikilinks import WikiPathExtension
from wiki.plugins.links.mdx.urlize import makeExtension as urlize_makeExtension


M src/wiki/plugins/macros/mdx/toc.py => src/wiki/plugins/macros/mdx/toc.py +3 -1
@@ 1,6 1,8 @@
import re

from markdown.extensions.toc import TocExtension, TocTreeprocessor, slugify
from markdown.extensions.toc import slugify
from markdown.extensions.toc import TocExtension
from markdown.extensions.toc import TocTreeprocessor
from wiki.plugins.macros import settings

HEADER_ID_PREFIX = "wiki-toc-"

M src/wiki/plugins/macros/mdx/wikilinks.py => src/wiki/plugins/macros/mdx/wikilinks.py +2 -1
@@ 5,7 5,8 @@ import re

import markdown
from django.urls import reverse
from markdown.extensions import Extension, wikilinks
from markdown.extensions import Extension
from markdown.extensions import wikilinks


def build_url(label, base, end, md):

M src/wiki/plugins/notifications/forms.py => src/wiki/plugins/notifications/forms.py +7 -3
@@ 1,9 1,13 @@
from django import forms
from django.contrib.contenttypes.models import ContentType
from django.forms.models import BaseModelFormSet, modelformset_factory
from django.forms.models import BaseModelFormSet
from django.forms.models import modelformset_factory
from django.utils.safestring import mark_safe
from django.utils.translation import gettext, gettext_lazy as _
from django_nyt.models import NotificationType, Settings, Subscription
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from django_nyt.models import NotificationType
from django_nyt.models import Settings
from django_nyt.models import Subscription
from wiki.core.plugins.base import PluginSettingsFormMixin
from wiki.plugins.notifications import models
from wiki.plugins.notifications.settings import ARTICLE_EDIT

M src/wiki/plugins/notifications/migrations/0001_initial.py => src/wiki/plugins/notifications/migrations/0001_initial.py +2 -1
@@ 1,4 1,5 @@
from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M src/wiki/plugins/notifications/migrations/0002_auto_20151118_1811.py => src/wiki/plugins/notifications/migrations/0002_auto_20151118_1811.py +2 -1
@@ 1,4 1,5 @@
from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M src/wiki/plugins/notifications/views.py => src/wiki/plugins/notifications/views.py +2 -1
@@ 5,7 5,8 @@ from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views.generic import FormView

from . import forms, models
from . import forms
from . import models


class NotificationSettings(FormView):

M src/wiki/plugins/notifications/wiki_plugin.py => src/wiki/plugins/notifications/wiki_plugin.py +2 -1
@@ 2,7 2,8 @@ from django.urls import re_path
from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin

from . import settings, views
from . import settings
from . import views


class NotifyPlugin(BasePlugin):

M src/wiki/plugins/redlinks/mdx/redlinks.py => src/wiki/plugins/redlinks/mdx/redlinks.py +2 -1
@@ 1,5 1,6 @@
import html
from urllib.parse import urljoin, urlparse
from urllib.parse import urljoin
from urllib.parse import urlparse

from markdown.extensions import Extension
from markdown.postprocessors import AndSubstitutePostprocessor

M src/wiki/sites.py => src/wiki/sites.py +2 -1
@@ 1,5 1,6 @@
from django.apps import apps
from django.urls import include, re_path
from django.urls import include
from django.urls import re_path
from django.utils.functional import LazyObject
from django.utils.module_loading import import_string
from wiki.conf import settings

M src/wiki/urls.py => src/wiki/urls.py +5 -2
@@ 1,9 1,12 @@
from django.urls import include, re_path
from django.urls import include
from django.urls import re_path
from django.utils.module_loading import import_string
from wiki import sites
from wiki.conf import settings
from wiki.core.plugins import registry
from wiki.views import accounts, article, deleted_list
from wiki.views import accounts
from wiki.views import article
from wiki.views import deleted_list

urlpatterns = [
    re_path(r"^", sites.site.urls),

M src/wiki/views/accounts.py => src/wiki/views/accounts.py +10 -8
@@ 8,19 8,21 @@ settings.WIKI_SIGNUP_URL = '/your/signup/url'
SETTINGS.LOGIN_URL
SETTINGS.LOGOUT_URL
"""

from django.conf import settings as django_settings
from django.contrib import messages
from django.contrib.auth import (
    get_user_model,
    login as auth_login,
    logout as auth_logout,
)
from django.contrib.auth import get_user_model
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.forms import AuthenticationForm
from django.shortcuts import get_object_or_404, redirect, render
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext as _
from django.views.generic import CreateView, FormView, UpdateView, View
from django.views.generic import CreateView
from django.views.generic import FormView
from django.views.generic import UpdateView
from django.views.generic import View
from wiki import forms
from wiki.conf import settings


M src/wiki/views/article.py => src/wiki/views/article.py +14 -11
@@ 7,20 7,23 @@ from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.db.models import Q
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _, ngettext
from django.utils.translation import gettext as _
from django.utils.translation import ngettext
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import (
    DetailView,
    FormView,
    ListView,
    RedirectView,
    TemplateView,
    View,
)
from wiki import editors, forms, models
from django.views.generic import DetailView
from django.views.generic import FormView
from django.views.generic import ListView
from django.views.generic import RedirectView
from django.views.generic import TemplateView
from django.views.generic import View
from wiki import editors
from wiki import forms
from wiki import models
from wiki.conf import settings
from wiki.core import permissions
from wiki.core.diff import simple_merge

M testproject/testproject/settings/base.py => testproject/testproject/settings/base.py +0 -1
@@ 7,7 7,6 @@ https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""

import os

from django.urls import reverse_lazy

M testproject/testproject/settings/local.py => testproject/testproject/settings/local.py +0 -2
@@ 1,6 1,4 @@
# Add your own changes here -- but do not push to remote!!
# After changing the file, from root of repository execute:

# git update-index --assume-unchanged testproject/testproject/settings/local.py

from .dev import *  # noqa @UnusedWildImport

M testproject/testproject/urls.py => testproject/testproject/urls.py +4 -4
@@ 2,8 2,9 @@ from django.conf import settings
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.http.response import HttpResponse
from django.urls import include
from django.urls import re_path
from django.views.static import serve as static_serve
from django.urls import include, re_path

admin.autodiscover()



@@ 25,12 26,11 @@ if settings.DEBUG:
if settings.DEBUG:
    try:
        import debug_toolbar
        urlpatterns = [
            re_path('__debug__/', include(debug_toolbar.urls)),

        urlpatterns = [
            re_path("__debug__/", include(debug_toolbar.urls)),
            # For django versions before 2.0:
            # url(r'^__debug__/', include(debug_toolbar.urls)),

        ] + urlpatterns
    except ImportError as ie:
        pass

M testproject/testproject/wsgi.py => testproject/testproject/wsgi.py +2 -1
@@ 16,10 16,11 @@ framework.
import os
import sys

from django.core.wsgi import get_wsgi_application

# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application

PROJECT_PATH = os.path.abspath(os.path.split(__file__)[0])
PROJECT_PARENT = os.path.abspath(os.path.split(PROJECT_PATH)[0])

M tests/base.py => tests/base.py +4 -2
@@ 3,8 3,10 @@ import unittest

import django_functest
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.template import Context, Template
from django.test import TestCase, override_settings
from django.template import Context
from django.template import Template
from django.test import override_settings
from django.test import TestCase
from django.urls import reverse
from wiki.models import URLPath


M tests/core/test_accounts.py => tests/core/test_accounts.py +10 -10
@@ 6,15 6,13 @@ from django.shortcuts import resolve_url
from wiki.conf import settings as wiki_settings
from wiki.models import reverse

from ..base import (
    SUPERUSER1_PASSWORD,
    SUPERUSER1_USERNAME,
    ArticleWebTestUtils,
    DjangoClientTestBase,
    RequireRootArticleMixin,
    TestBase,
    wiki_override_settings,
)
from ..base import ArticleWebTestUtils
from ..base import DjangoClientTestBase
from ..base import RequireRootArticleMixin
from ..base import SUPERUSER1_PASSWORD
from ..base import SUPERUSER1_USERNAME
from ..base import TestBase
from ..base import wiki_override_settings
from ..testdata.models import CustomUser




@@ 38,7 36,9 @@ class AccountUpdateTest(

        # save a new revision
        response = self.client.post(resolve_url("wiki:profile_update"), example_data)
        self.assertContains(response, "Passwords don", status_code=200)  # Django 2/3 output different escaped versions of single quote in don't
        self.assertContains(
            response, "Passwords don", status_code=200
        )  # Django 2/3 output different escaped versions of single quote in don't

        # Now check that we don't succeed with unmatching passwords
        example_data = {

M tests/core/test_basic.py => tests/core/test_basic.py +3 -1
@@ 5,7 5,9 @@ from django.test import TestCase
from wiki.conf import settings as wiki_settings
from wiki.core.http import send_file
from wiki.forms import Group
from wiki.models import Article, ArticleRevision, URLPath
from wiki.models import Article
from wiki.models import ArticleRevision
from wiki.models import URLPath

from ..base import wiki_override_settings
from ..testdata.models import CustomGroup

M tests/core/test_checks.py => tests/core/test_checks.py +6 -7
@@ 1,14 1,13 @@
import copy

from django.conf import settings
from django.core.checks import Error, registry
from django.core.checks import Error
from django.core.checks import registry
from django.test import TestCase
from wiki.checks import (
    FIELDS_IN_CUSTOM_USER_MODEL,
    REQUIRED_CONTEXT_PROCESSORS,
    REQUIRED_INSTALLED_APPS,
    Tags,
)
from wiki.checks import FIELDS_IN_CUSTOM_USER_MODEL
from wiki.checks import REQUIRED_CONTEXT_PROCESSORS
from wiki.checks import REQUIRED_INSTALLED_APPS
from wiki.checks import Tags

from ..base import wiki_override_settings


M tests/core/test_forms.py => tests/core/test_forms.py +5 -2
@@ 1,7 1,10 @@
from django.test import TestCase
from django.utils.translation import gettext
from tests.base import DjangoClientTestBase, RequireRootArticleMixin
from wiki.forms import DeleteForm, UserCreationForm
from wiki.forms import DeleteForm
from wiki.forms import UserCreationForm

from tests.base import DjangoClientTestBase
from tests.base import RequireRootArticleMixin


class DeleteFormTests(RequireRootArticleMixin, DjangoClientTestBase):

M tests/core/test_managers.py => tests/core/test_managers.py +2 -1
@@ 4,7 4,8 @@ because the pattern of building them is different from Django
1.5 to 1.6 to 1.7 so there will be 3 patterns in play at the
same time.
"""
from wiki.models import Article, URLPath
from wiki.models import Article
from wiki.models import URLPath
from wiki.plugins.attachments.models import Attachment

from ..base import ArticleTestBase

M tests/core/test_models.py => tests/core/test_models.py +3 -1
@@ 5,7 5,9 @@ from django.test.testcases import TestCase
from django.urls import re_path
from wiki.conf import settings
from wiki.managers import ArticleManager
from wiki.models import Article, ArticleRevision, URLPath
from wiki.models import Article
from wiki.models import ArticleRevision
from wiki.models import URLPath
from wiki.urls import WikiURLPatterns

User = get_user_model()

M tests/core/test_sites.py => tests/core/test_sites.py +6 -3
@@ 2,10 2,13 @@ from importlib import reload

from django.contrib.sites.models import Site
from django.test.testcases import TestCase
from django.urls import include, re_path
from wiki import sites, urls
from django.urls import include
from django.urls import re_path
from wiki import sites
from wiki import urls
from wiki.apps import WikiConfig
from wiki.models import Article, URLPath
from wiki.models import Article
from wiki.models import URLPath

from ..base import wiki_override_settings


M tests/core/test_template_filters.py => tests/core/test_template_filters.py +11 -11
@@ 1,15 1,15 @@
from django.contrib.auth import get_user_model
from wiki.models import Article, ArticleRevision
from wiki.templatetags.wiki_tags import (
    can_delete,
    can_moderate,
    can_read,
    can_write,
    get_content_snippet,
    is_locked,
)

from ..base import TemplateTestCase, wiki_override_settings
from wiki.models import Article
from wiki.models import ArticleRevision
from wiki.templatetags.wiki_tags import can_delete
from wiki.templatetags.wiki_tags import can_moderate
from wiki.templatetags.wiki_tags import can_read
from wiki.templatetags.wiki_tags import can_write
from wiki.templatetags.wiki_tags import get_content_snippet
from wiki.templatetags.wiki_tags import is_locked

from ..base import TemplateTestCase
from ..base import wiki_override_settings

User = get_user_model()


M tests/core/test_template_tags.py => tests/core/test_template_tags.py +7 -7
@@ 6,13 6,13 @@ from django.contrib.contenttypes.models import ContentType
from django.http import HttpRequest
from wiki.conf import settings
from wiki.forms import CreateRootForm
from wiki.models import Article, ArticleForObject, ArticleRevision
from wiki.templatetags.wiki_tags import (
    article_for_object,
    login_url,
    wiki_form,
    wiki_render,
)
from wiki.models import Article
from wiki.models import ArticleForObject
from wiki.models import ArticleRevision
from wiki.templatetags.wiki_tags import article_for_object
from wiki.templatetags.wiki_tags import login_url
from wiki.templatetags.wiki_tags import wiki_form
from wiki.templatetags.wiki_tags import wiki_render

from ..base import TemplateTestCase


M tests/core/test_urls.py => tests/core/test_urls.py +6 -3
@@ 1,8 1,11 @@
from django.contrib.sites.models import Site
from django.test.testcases import TestCase
from django.urls import include, re_path
from wiki.models import Article, URLPath
from wiki.urls import WikiURLPatterns, get_pattern as get_wiki_pattern
from django.urls import include
from django.urls import re_path
from wiki.models import Article
from wiki.models import URLPath
from wiki.urls import get_pattern as get_wiki_pattern
from wiki.urls import WikiURLPatterns

from ..base import wiki_override_settings


M tests/core/test_views.py => tests/core/test_views.py +13 -12
@@ 7,19 7,20 @@ from django.test import override_settings
from django.utils import translation
from django.utils.html import escape
from django_functest import FuncBaseMixin
from tests.testdata.models import CustomGroup
from wiki import models
from wiki.forms import PermissionsForm, validate_slug_numbers
from wiki.models import ArticleRevision, URLPath, reverse

from ..base import (
    SUPERUSER1_USERNAME,
    ArticleWebTestUtils,
    DjangoClientTestBase,
    RequireRootArticleMixin,
    SeleniumBase,
    WebTestBase,
)
from wiki.forms import PermissionsForm
from wiki.forms import validate_slug_numbers
from wiki.models import ArticleRevision
from wiki.models import reverse
from wiki.models import URLPath

from ..base import ArticleWebTestUtils
from ..base import DjangoClientTestBase
from ..base import RequireRootArticleMixin
from ..base import SeleniumBase
from ..base import SUPERUSER1_USERNAME
from ..base import WebTestBase
from tests.testdata.models import CustomGroup


class RootArticleViewTestsBase(FuncBaseMixin):

M tests/plugins/attachments/test_commands.py => tests/plugins/attachments/test_commands.py +2 -1
@@ 1,10 1,11 @@
import os
import tempfile

from tests.core.test_commands import TestManagementCommands
from wiki.models import URLPath
from wiki.plugins.attachments import models

from tests.core.test_commands import TestManagementCommands


class TestAttachmentManagementCommands(TestManagementCommands):
    """

M tests/plugins/attachments/test_models.py => tests/plugins/attachments/test_models.py +5 -2
@@ 1,5 1,8 @@
from tests.base import RequireRootArticleMixin, TestBase
from wiki.plugins.attachments.models import Attachment, AttachmentRevision
from wiki.plugins.attachments.models import Attachment
from wiki.plugins.attachments.models import AttachmentRevision

from tests.base import RequireRootArticleMixin
from tests.base import TestBase


class AttachmentRevisionTests(RequireRootArticleMixin, TestBase):

M tests/plugins/attachments/test_views.py => tests/plugins/attachments/test_views.py +3 -1
@@ 4,7 4,9 @@ from django.core.files.uploadedfile import InMemoryUploadedFile
from django.urls import reverse
from wiki.models import URLPath

from ...base import ArticleWebTestUtils, DjangoClientTestBase, RequireRootArticleMixin
from ...base import ArticleWebTestUtils
from ...base import DjangoClientTestBase
from ...base import RequireRootArticleMixin


class AttachmentTests(

M tests/plugins/editsection/test_editsection.py => tests/plugins/editsection/test_editsection.py +3 -1
@@ 2,7 2,9 @@ from django.urls import reverse
from django_functest import FuncBaseMixin
from wiki.models import URLPath

from ...base import DjangoClientTestBase, RequireRootArticleMixin, WebTestBase
from ...base import DjangoClientTestBase
from ...base import RequireRootArticleMixin
from ...base import WebTestBase

TEST_CONTENT = (
    "Title 1\n"

M tests/plugins/globalhistory/test_globalhistory.py => tests/plugins/globalhistory/test_globalhistory.py +3 -1
@@ 2,7 2,9 @@ from django.urls import reverse
from django.utils import translation
from wiki.models import URLPath

from ...base import ArticleWebTestUtils, DjangoClientTestBase, RequireRootArticleMixin
from ...base import ArticleWebTestUtils
from ...base import DjangoClientTestBase
from ...base import RequireRootArticleMixin


class GlobalhistoryTests(

M tests/plugins/images/test_markdown.py => tests/plugins/images/test_markdown.py +3 -1
@@ 2,10 2,12 @@ import base64
from io import BytesIO

from django.core.files.uploadedfile import InMemoryUploadedFile
from tests.base import RequireRootArticleMixin, TestBase
from wiki.core import markdown
from wiki.plugins.images import models

from tests.base import RequireRootArticleMixin
from tests.base import TestBase


class ImageMarkdownTests(RequireRootArticleMixin, TestBase):
    def setUp(self):

M tests/plugins/images/test_views.py => tests/plugins/images/test_views.py +4 -6
@@ 10,12 10,10 @@ from wiki.models import URLPath
from wiki.plugins.images import models
from wiki.plugins.images.wiki_plugin import ImagePlugin

from ...base import (
    ArticleWebTestUtils,
    DjangoClientTestBase,
    RequireRootArticleMixin,
    wiki_override_settings,
)
from ...base import ArticleWebTestUtils
from ...base import DjangoClientTestBase
from ...base import RequireRootArticleMixin
from ...base import wiki_override_settings


class ImageTests(RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase):

M tests/plugins/links/test_urlize.py => tests/plugins/links/test_urlize.py +2 -1
@@ 2,7 2,8 @@ import html

import markdown
import pytest
from wiki.plugins.links.mdx.urlize import UrlizeExtension, makeExtension
from wiki.plugins.links.mdx.urlize import makeExtension
from wiki.plugins.links.mdx.urlize import UrlizeExtension

# Template accepts two strings - href value and link text value.
EXPECTED_LINK_TEMPLATE = (

M tests/plugins/macros/test_links.py => tests/plugins/macros/test_links.py +3 -1
@@ 1,6 1,8 @@
from tests.base import RequireRootArticleMixin, TestBase
from wiki.core import markdown

from tests.base import RequireRootArticleMixin
from tests.base import TestBase


class WikiLinksTests(RequireRootArticleMixin, TestBase):
    def test_wikilink(self):

M tests/plugins/macros/test_macro.py => tests/plugins/macros/test_macro.py +3 -1
@@ 1,6 1,8 @@
from tests.base import RequireRootArticleMixin, TestBase
from wiki.core import markdown

from tests.base import RequireRootArticleMixin
from tests.base import TestBase


class MacroTests(RequireRootArticleMixin, TestBase):
    def test_article_list(self):

M tests/plugins/notifications/test_forms.py => tests/plugins/notifications/test_forms.py +1 -1
@@ 1,8 1,8 @@
from django.test import TestCase
from django_nyt.forms import SettingsForm
from wiki.plugins.notifications.forms import SettingsFormSet

from tests.base import RequireSuperuserMixin
from wiki.plugins.notifications.forms import SettingsFormSet


class SettingsFormTests(RequireSuperuserMixin, TestCase):

M tests/plugins/notifications/test_views.py => tests/plugins/notifications/test_views.py +4 -5
@@ 1,10 1,9 @@
from django.shortcuts import resolve_url
from django_nyt.models import Settings
from tests.base import (
    ArticleWebTestUtils,
    DjangoClientTestBase,
    RequireRootArticleMixin,
)

from tests.base import ArticleWebTestUtils
from tests.base import DjangoClientTestBase
from tests.base import RequireRootArticleMixin


class NotificationSettingsTests(

M tests/plugins/redlinks/test_redlinks.py => tests/plugins/redlinks/test_redlinks.py +3 -1
@@ 1,7 1,9 @@
from tests.base import RequireRootArticleMixin, TestBase
from wiki.core import markdown
from wiki.models import URLPath

from tests.base import RequireRootArticleMixin
from tests.base import TestBase


class RedlinksTests(RequireRootArticleMixin, TestBase):
    def setUp(self):

M tests/testdata/migrations/0001_initial.py => tests/testdata/migrations/0001_initial.py +2 -2
@@ 1,9 1,9 @@
# Generated by Django 2.2.1 on 2019-07-03 19:03

import django.contrib.auth.models
import django.contrib.auth.validators
import django.utils.timezone
from django.db import migrations, models
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

M tests/testdata/urls.py => tests/testdata/urls.py +2 -1
@@ 1,7 1,8 @@
from django.conf import settings
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, re_path
from django.urls import include
from django.urls import re_path

urlpatterns = [
    re_path(r"^admin/doc/", include("django.contrib.admindocs.urls")),