diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..5b486340f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: Python CI + +on: + push: + branches: [master] + pull_request: + branches: + - '**' + +jobs: + run_tests: + name: Tests + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-20.04] + python-version: ['3.8'] + toxenv: [django22, django30, django31] + steps: + - uses: actions/checkout@v1 + - name: setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install pip + run: pip install -r requirements/pip.txt + + - name: Install Dependencies + run: pip install -r requirements/ci.txt + + - name: Run Tests + env: + TOXENV: ${{ matrix.toxenv }} + run: tox diff --git a/.travis.yml b/.travis.yml index b8baa020e..c04bbe4bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,11 @@ language: python python: - - 3.5 - 3.8 install: - - pip install -r requirements/travis.txt -matrix: - - python: 3.5 - env: TOXENV=py35-django22 - - python: 3.8 - env: TOXENV=py38-django22 + - pip install -r requirements/ci.txt +env: + - TOXENV=py38-django22 + - TOXENV=py38-django30 + - TOXENV=py38-django31 script: - tox diff --git a/Makefile b/Makefile index 73686e09c..aac2eaff4 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,13 @@ upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade upgrade: pip install -q -r requirements/pip_tools.txt + pip-compile --upgrade --allow-unsafe --rebuild -o requirements/pip.txt requirements/pip.in pip-compile --upgrade -o requirements/pip_tools.txt requirements/pip_tools.in pip-compile --upgrade -o requirements/base.txt requirements/base.in pip-compile --upgrade -o requirements/docs.txt requirements/docs.in pip-compile --upgrade -o requirements/test.txt requirements/test.in pip-compile --upgrade -o requirements/tox.txt requirements/tox.in - pip-compile --upgrade -o requirements/travis.txt requirements/travis.in + pip-compile --upgrade -o requirements/ci.txt requirements/ci.in # Let tox control the Django version for tests grep -e "^django==" requirements/base.txt > requirements/django.txt sed '/^[dD]jango==/d' requirements/test.txt > requirements/test.tmp diff --git a/django_notify/admin.py b/django_notify/admin.py index 19eaee01e..1abc3a9d3 100644 --- a/django_notify/admin.py +++ b/django_notify/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from django_notify import models diff --git a/django_notify/decorators.py b/django_notify/decorators.py index 6d87ccca9..518881ace 100644 --- a/django_notify/decorators.py +++ b/django_notify/decorators.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - import json from django.contrib.auth.decorators import login_required diff --git a/django_notify/migrations/0001_initial.py b/django_notify/migrations/0001_initial.py index 4f9972b60..4a77c0d96 100644 --- a/django_notify/migrations/0001_initial.py +++ b/django_notify/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - import django.db.models.deletion from django.conf import settings from django.db import migrations, models diff --git a/django_notify/models.py b/django_notify/models.py index 89ffd1f57..0687bb3c1 100644 --- a/django_notify/models.py +++ b/django_notify/models.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.db import models @@ -15,9 +12,9 @@ class NotificationType(models.Model): """ Notification types are added on-the-fly by the applications adding new notifications""" - key = models.CharField(max_length=128, primary_key=True, verbose_name=_(u'unique key'), + key = models.CharField(max_length=128, primary_key=True, verbose_name=_('unique key'), unique=True) - label = models.CharField(max_length=128, verbose_name=_(u'verbose name'), + label = models.CharField(max_length=128, verbose_name=_('verbose name'), blank=True, null=True) content_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=models.CASCADE) @@ -27,23 +24,23 @@ def __unicode__(self): class Meta: app_label = 'django_notify' db_table = settings.DB_TABLE_PREFIX + '_notificationtype' - verbose_name = _(u'type') - verbose_name_plural = _(u'types') + verbose_name = _('type') + verbose_name_plural = _('types') class Settings(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) - interval = models.SmallIntegerField(choices=settings.INTERVALS, verbose_name=_(u'interval'), + interval = models.SmallIntegerField(choices=settings.INTERVALS, verbose_name=_('interval'), default=settings.INTERVALS_DEFAULT) def __unicode__(self): - return _(u"Settings for %s") % self.user.username + return _("Settings for %s") % self.user.username class Meta: app_label = 'django_notify' db_table = settings.DB_TABLE_PREFIX + '_settings' - verbose_name = _(u'settings') - verbose_name_plural = _(u'settings') + verbose_name = _('settings') + verbose_name_plural = _('settings') class Subscription(models.Model): @@ -51,7 +48,7 @@ class Subscription(models.Model): settings = models.ForeignKey(Settings, on_delete=models.CASCADE) notification_type = models.ForeignKey(NotificationType, on_delete=models.CASCADE) object_id = models.CharField(max_length=64, null=True, blank=True, - help_text=_(u'Leave this blank to subscribe to any kind of object')) + help_text=_('Leave this blank to subscribe to any kind of object')) send_emails = models.BooleanField(default=True) def __unicode__(self): @@ -60,14 +57,14 @@ def __unicode__(self): class Meta: app_label = 'django_notify' db_table = settings.DB_TABLE_PREFIX + '_subscription' - verbose_name = _(u'subscription') - verbose_name_plural = _(u'subscriptions') + verbose_name = _('subscription') + verbose_name_plural = _('subscriptions') class Notification(models.Model): subscription = models.ForeignKey(Subscription, null=True, blank=True, on_delete=models.SET_NULL) message = models.TextField() - url = models.URLField(blank=True, null=True, verbose_name=_(u'link for notification')) + url = models.URLField(blank=True, null=True, verbose_name=_('link for notification')) is_viewed = models.BooleanField(default=False) is_emailed = models.BooleanField(default=False) created = models.DateTimeField(auto_now_add=True) @@ -106,8 +103,8 @@ def __unicode__(self): class Meta: app_label = 'django_notify' db_table = settings.DB_TABLE_PREFIX + '_notification' - verbose_name = _(u'notification') - verbose_name_plural = _(u'notifications') + verbose_name = _('notification') + verbose_name_plural = _('notifications') def notify(message, key, target_object=None, url=None): @@ -133,7 +130,7 @@ def notify(message, key, target_object=None, url=None): if target_object: if not isinstance(target_object, Model): - raise TypeError(_(u"You supplied a target_object that's not an instance of a django Model.")) + raise TypeError(_("You supplied a target_object that's not an instance of a django Model.")) object_id = target_object.id else: object_id = None diff --git a/django_notify/settings.py b/django_notify/settings.py index 70dcc232b..334d42341 100644 --- a/django_notify/settings.py +++ b/django_notify/settings.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf import settings as django_settings _ = lambda x: x @@ -22,9 +20,9 @@ WEEKLY = 7*24-1 INTERVALS = getattr(django_settings, "NOTIFY_INTERVALS", - [(INSTANTLY, _(u'instantly')), - (DAILY, _(u'daily')), - (WEEKLY, _(u'weekly'))]) + [(INSTANTLY, _('instantly')), + (DAILY, _('daily')), + (WEEKLY, _('weekly'))]) INTERVALS_DEFAULT = INSTANTLY diff --git a/django_notify/tests.py b/django_notify/tests.py index 1ddecbc38..06eeec547 100644 --- a/django_notify/tests.py +++ b/django_notify/tests.py @@ -5,7 +5,6 @@ Replace this with more appropriate tests for your application. """ -from __future__ import absolute_import from django.test import TestCase diff --git a/django_notify/urls.py b/django_notify/urls.py index 03516ddb3..90c318303 100644 --- a/django_notify/urls.py +++ b/django_notify/urls.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.conf.urls import url from django_notify import views @@ -8,8 +5,8 @@ urlpatterns = [ url('^json/get/$', views.get_notifications, name='json_get', kwargs={}), url('^json/mark-read/$', views.mark_read, name='json_mark_read_base', kwargs={}), - url('^json/mark-read/(\d+)/$', views.mark_read, name='json_mark_read', kwargs={}), - url('^goto/(?P\d+)/$', views.goto, name='goto', kwargs={}), + url(r'^json/mark-read/(\d+)/$', views.mark_read, name='json_mark_read', kwargs={}), + url(r'^goto/(?P\d+)/$', views.goto, name='goto', kwargs={}), url('^goto/$', views.goto, name='goto_base', kwargs={}), ] diff --git a/django_notify/views.py b/django_notify/views.py index e4866dcf5..99fda5d8a 100644 --- a/django_notify/views.py +++ b/django_notify/views.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import - from django.contrib.auth.decorators import login_required from django.shortcuts import get_object_or_404, redirect diff --git a/docs/conf.py b/docs/conf.py index 9456640de..4e5504f41 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # django-wiki documentation build configuration file, created by # sphinx-quickstart on Mon Jul 23 16:13:51 2012. @@ -11,7 +10,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -from __future__ import absolute_import import os import sys @@ -43,8 +41,8 @@ master_doc = 'index' # General information about the project. -project = u'django-wiki' -copyright = u'2012, Benjamin Bach' +project = 'django-wiki' +copyright = '2012, Benjamin Bach' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -186,8 +184,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'django-wiki.tex', u'django-wiki Documentation', - u'Benjamin Bach', 'manual'), + ('index', 'django-wiki.tex', 'django-wiki Documentation', + 'Benjamin Bach', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -216,8 +214,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'django-wiki', u'django-wiki Documentation', - [u'Benjamin Bach'], 1) + ('index', 'django-wiki', 'django-wiki Documentation', + ['Benjamin Bach'], 1) ] # If true, show URL addresses after external links. @@ -230,8 +228,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'django-wiki', u'django-wiki Documentation', - u'Benjamin Bach', 'django-wiki', 'One line description of project.', + ('index', 'django-wiki', 'django-wiki Documentation', + 'Benjamin Bach', 'django-wiki', 'One line description of project.', 'Miscellaneous'), ] diff --git a/requirements/base.in b/requirements/base.in index bb1d13c64..8feb9227e 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -6,5 +6,4 @@ django django-mptt django-sekizai Markdown -six # Use for strings and unicode compatibility sorl-thumbnail # Required by wiki.plugins.images diff --git a/requirements/base.txt b/requirements/base.txt index 1380df0b9..71432101a 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,17 +4,36 @@ # # make upgrade # -bleach==3.1.5 # via -r requirements/base.in -django-classy-tags==1.0.0 # via django-sekizai -django-js-asset==1.2.2 # via django-mptt -django-mptt==0.11.0 # via -r requirements/base.in -django-sekizai==1.1.0 # via -r requirements/base.in -django==2.2.14 # via -c requirements/constraints.txt, -r requirements/base.in, django-classy-tags, django-mptt, django-sekizai -markdown==2.6.11 # via -c requirements/constraints.txt, -r requirements/base.in -packaging==20.4 # via bleach -pyparsing==2.4.7 # via packaging -pytz==2020.1 # via django -six==1.15.0 # via -r requirements/base.in, bleach, django-classy-tags, django-sekizai, packaging -sorl-thumbnail==12.6.3 # via -r requirements/base.in -sqlparse==0.3.1 # via django -webencodings==0.5.1 # via bleach +bleach==3.2.1 + # via -r requirements/base.in +django-classy-tags==2.0.0 + # via django-sekizai +django-js-asset==1.2.2 + # via django-mptt +django-mptt==0.11.0 + # via -r requirements/base.in +django-sekizai==2.0.0 + # via -r requirements/base.in +django==2.2.17 + # via + # -c requirements/constraints.txt + # -r requirements/base.in + # django-classy-tags + # django-mptt + # django-sekizai +markdown==3.3.3 + # via -r requirements/base.in +packaging==20.8 + # via bleach +pyparsing==2.4.7 + # via packaging +pytz==2020.5 + # via django +six==1.15.0 + # via bleach +sorl-thumbnail==12.7.0 + # via -r requirements/base.in +sqlparse==0.4.1 + # via django +webencodings==0.5.1 + # via bleach diff --git a/requirements/travis.in b/requirements/ci.in similarity index 100% rename from requirements/travis.in rename to requirements/ci.in diff --git a/requirements/ci.txt b/requirements/ci.txt new file mode 100644 index 000000000..72b43e872 --- /dev/null +++ b/requirements/ci.txt @@ -0,0 +1,50 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +appdirs==1.4.4 + # via + # -r requirements/tox.txt + # virtualenv +distlib==0.3.1 + # via + # -r requirements/tox.txt + # virtualenv +filelock==3.0.12 + # via + # -r requirements/tox.txt + # tox + # virtualenv +packaging==20.8 + # via + # -r requirements/tox.txt + # tox +pluggy==0.13.1 + # via + # -r requirements/tox.txt + # tox +py==1.10.0 + # via + # -r requirements/tox.txt + # tox +pyparsing==2.4.7 + # via + # -r requirements/tox.txt + # packaging +six==1.15.0 + # via + # -r requirements/tox.txt + # tox + # virtualenv +toml==0.10.2 + # via + # -r requirements/tox.txt + # tox +tox==3.21.1 + # via -r requirements/tox.txt +virtualenv==20.3.1 + # via + # -r requirements/tox.txt + # tox diff --git a/requirements/constraints.txt b/requirements/constraints.txt index e5853f8af..8a2cc2b0a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -12,6 +12,3 @@ # Use latest Django LTS version Django<2.3.0 - -# Use version 2.6 to avoid markdown errors -Markdown<2.7 diff --git a/requirements/django.txt b/requirements/django.txt index 3cdbc8d88..34249f7c4 100644 --- a/requirements/django.txt +++ b/requirements/django.txt @@ -1 +1 @@ -django==2.2.14 # via -c requirements/constraints.txt, -r requirements/base.in, django-classy-tags, django-mptt, django-sekizai +django==2.2.17 diff --git a/requirements/docs.txt b/requirements/docs.txt index 34bc60738..cf7b7199c 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -4,31 +4,58 @@ # # make upgrade # -alabaster==0.7.12 # via sphinx -babel==2.8.0 # via sphinx -certifi==2020.6.20 # via requests -chardet==3.0.4 # via requests -docutils==0.16 # via sphinx -edx-sphinx-theme==1.5.0 # via -r requirements/docs.in -idna==2.10 # via requests -imagesize==1.2.0 # via sphinx -jinja2==2.11.2 # via sphinx -markupsafe==1.1.1 # via jinja2 -packaging==20.4 # via sphinx -pygments==2.6.1 # via sphinx -pyparsing==2.4.7 # via packaging -pytz==2020.1 # via babel -requests==2.24.0 # via sphinx -six==1.15.0 # via edx-sphinx-theme, packaging -snowballstemmer==2.0.0 # via sphinx -sphinx==3.1.1 # via -r requirements/docs.in, edx-sphinx-theme -sphinxcontrib-applehelp==1.0.2 # via sphinx -sphinxcontrib-devhelp==1.0.2 # via sphinx -sphinxcontrib-htmlhelp==1.0.3 # via sphinx -sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.3 # via sphinx -sphinxcontrib-serializinghtml==1.1.4 # via sphinx -urllib3==1.25.9 # via requests +alabaster==0.7.12 + # via sphinx +babel==2.9.0 + # via sphinx +certifi==2020.12.5 + # via requests +chardet==4.0.0 + # via requests +docutils==0.16 + # via sphinx +edx-sphinx-theme==1.6.0 + # via -r requirements/docs.in +idna==2.10 + # via requests +imagesize==1.2.0 + # via sphinx +jinja2==2.11.2 + # via sphinx +markupsafe==1.1.1 + # via jinja2 +packaging==20.8 + # via sphinx +pygments==2.7.4 + # via sphinx +pyparsing==2.4.7 + # via packaging +pytz==2020.5 + # via babel +requests==2.25.1 + # via sphinx +six==1.15.0 + # via edx-sphinx-theme +snowballstemmer==2.0.0 + # via sphinx +sphinx==3.4.3 + # via + # -r requirements/docs.in + # edx-sphinx-theme +sphinxcontrib-applehelp==1.0.2 + # via sphinx +sphinxcontrib-devhelp==1.0.2 + # via sphinx +sphinxcontrib-htmlhelp==1.0.3 + # via sphinx +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.3 + # via sphinx +sphinxcontrib-serializinghtml==1.1.4 + # via sphinx +urllib3==1.26.2 + # via requests # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/requirements/pip.in b/requirements/pip.in new file mode 100644 index 000000000..7015e2e2f --- /dev/null +++ b/requirements/pip.in @@ -0,0 +1,3 @@ +pip +setuptools +wheel diff --git a/requirements/pip.txt b/requirements/pip.txt new file mode 100644 index 000000000..9acedf7fe --- /dev/null +++ b/requirements/pip.txt @@ -0,0 +1,14 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +wheel==0.36.2 + # via -r requirements/pip.in + +# The following packages are considered to be unsafe in a requirements file: +pip==20.3.3 + # via -r requirements/pip.in +setuptools==51.1.2 + # via -r requirements/pip.in diff --git a/requirements/pip_tools.txt b/requirements/pip_tools.txt index 279019f74..53310ee5c 100644 --- a/requirements/pip_tools.txt +++ b/requirements/pip_tools.txt @@ -4,9 +4,10 @@ # # make upgrade # -click==7.1.2 # via pip-tools -pip-tools==5.2.1 # via -r requirements/pip_tools.in -six==1.15.0 # via pip-tools +click==7.1.2 + # via pip-tools +pip-tools==5.5.0 + # via -r requirements/pip_tools.in # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/requirements/test.txt b/requirements/test.txt index eb6cdf418..1b2ca1cfa 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -4,28 +4,73 @@ # # make upgrade # -attrs==19.3.0 # via pytest -bleach==3.1.5 # via -r requirements/base.txt -coverage==5.1 # via pytest-cov -django-classy-tags==1.0.0 # via -r requirements/base.txt, django-sekizai -django-js-asset==1.2.2 # via -r requirements/base.txt, django-mptt -django-mptt==0.11.0 # via -r requirements/base.txt -django-sekizai==1.1.0 # via -r requirements/base.txt -importlib-metadata==1.7.0 # via pluggy, pytest -markdown==2.6.11 # via -c requirements/constraints.txt, -r requirements/base.txt -more-itertools==8.4.0 # via pytest -packaging==20.4 # via -r requirements/base.txt, bleach, pytest -pathlib2==2.3.5 # via pytest -pluggy==0.13.1 # via pytest -py==1.9.0 # via pytest -pyparsing==2.4.7 # via -r requirements/base.txt, packaging -pytest-cov==2.10.0 # via -r requirements/test.in -pytest-django==3.9.0 # via -r requirements/test.in -pytest==5.4.3 # via -r requirements/test.in, pytest-cov, pytest-django -pytz==2020.1 # via -r requirements/base.txt, django -six==1.15.0 # via -r requirements/base.txt, bleach, django-classy-tags, django-sekizai, packaging, pathlib2 -sorl-thumbnail==12.6.3 # via -r requirements/base.txt -sqlparse==0.3.1 # via -r requirements/base.txt, django -wcwidth==0.2.5 # via pytest -webencodings==0.5.1 # via -r requirements/base.txt, bleach -zipp==1.2.0 # via importlib-metadata +attrs==20.3.0 + # via pytest +bleach==3.2.1 + # via -r requirements/base.txt +coverage==5.3.1 + # via pytest-cov +django-classy-tags==2.0.0 + # via + # -r requirements/base.txt + # django-sekizai +django-js-asset==1.2.2 + # via + # -r requirements/base.txt + # django-mptt +django-mptt==0.11.0 + # via -r requirements/base.txt +django-sekizai==2.0.0 + # via -r requirements/base.txt + # via + # -c requirements/constraints.txt + # -r requirements/base.txt + # django-classy-tags + # django-mptt + # django-sekizai +iniconfig==1.1.1 + # via pytest +markdown==3.3.3 + # via -r requirements/base.txt +packaging==20.8 + # via + # -r requirements/base.txt + # bleach + # pytest +pluggy==0.13.1 + # via pytest +py==1.10.0 + # via pytest +pyparsing==2.4.7 + # via + # -r requirements/base.txt + # packaging +pytest-cov==2.10.1 + # via -r requirements/test.in +pytest-django==4.1.0 + # via -r requirements/test.in +pytest==6.2.1 + # via + # -r requirements/test.in + # pytest-cov + # pytest-django +pytz==2020.5 + # via + # -r requirements/base.txt + # django +six==1.15.0 + # via + # -r requirements/base.txt + # bleach +sorl-thumbnail==12.7.0 + # via -r requirements/base.txt +sqlparse==0.4.1 + # via + # -r requirements/base.txt + # django +toml==0.10.2 + # via pytest +webencodings==0.5.1 + # via + # -r requirements/base.txt + # bleach diff --git a/requirements/tox.txt b/requirements/tox.txt index 53bf2cd40..4214a5e52 100644 --- a/requirements/tox.txt +++ b/requirements/tox.txt @@ -4,17 +4,29 @@ # # make upgrade # -appdirs==1.4.4 # via virtualenv -distlib==0.3.1 # via virtualenv -filelock==3.0.12 # via tox, virtualenv -importlib-metadata==1.7.0 # via pluggy, tox, virtualenv -importlib-resources==3.0.0 # via virtualenv -packaging==20.4 # via tox -pluggy==0.13.1 # via tox -py==1.9.0 # via tox -pyparsing==2.4.7 # via packaging -six==1.15.0 # via packaging, tox, virtualenv -toml==0.10.1 # via tox -tox==3.16.1 # via -r requirements/tox.in -virtualenv==20.0.25 # via tox -zipp==1.2.0 # via importlib-metadata, importlib-resources +appdirs==1.4.4 + # via virtualenv +distlib==0.3.1 + # via virtualenv +filelock==3.0.12 + # via + # tox + # virtualenv +packaging==20.8 + # via tox +pluggy==0.13.1 + # via tox +py==1.10.0 + # via tox +pyparsing==2.4.7 + # via packaging +six==1.15.0 + # via + # tox + # virtualenv +toml==0.10.2 + # via tox +tox==3.21.1 + # via -r requirements/tox.in +virtualenv==20.3.1 + # via tox diff --git a/requirements/travis.txt b/requirements/travis.txt deleted file mode 100644 index 498f5d84d..000000000 --- a/requirements/travis.txt +++ /dev/null @@ -1,20 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# make upgrade -# -appdirs==1.4.4 # via -r requirements/tox.txt, virtualenv -distlib==0.3.1 # via -r requirements/tox.txt, virtualenv -filelock==3.0.12 # via -r requirements/tox.txt, tox, virtualenv -importlib-metadata==1.7.0 # via -r requirements/tox.txt, pluggy, tox, virtualenv -importlib-resources==3.0.0 # via -r requirements/tox.txt, virtualenv -packaging==20.4 # via -r requirements/tox.txt, tox -pluggy==0.13.1 # via -r requirements/tox.txt, tox -py==1.9.0 # via -r requirements/tox.txt, tox -pyparsing==2.4.7 # via -r requirements/tox.txt, packaging -six==1.15.0 # via -r requirements/tox.txt, packaging, tox, virtualenv -toml==0.10.1 # via -r requirements/tox.txt, tox -tox==3.16.1 # via -r requirements/tox.txt -virtualenv==20.0.25 # via -r requirements/tox.txt, tox -zipp==1.2.0 # via -r requirements/tox.txt, importlib-metadata, importlib-resources diff --git a/setup.py b/setup.py index b550659ca..dcba30e68 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - import os from setuptools import find_packages, setup @@ -55,17 +52,17 @@ def is_requirement(line): packages = find_packages() -package_data = dict( - (package_name, template_patterns) +package_data = { + package_name: template_patterns for package_name in packages -) +} setup( name="django-wiki", - version="0.1.1", + version="1.0.0", author="Benjamin Bach", author_email="benjamin@overtag.dk", - description=("A wiki system written for the Django framework."), + description="A wiki system written for the Django framework.", license="GPLv3", keywords="django wiki markdown", packages=find_packages(exclude=["testproject", "testproject.*"]), @@ -80,10 +77,11 @@ def is_requirement(line): 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.8', 'Framework :: Django', 'Framework :: Django :: 2.2', + 'Framework :: Django :: 3.0', + 'Framework :: Django :: 3.1', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development', 'Topic :: Software Development :: Libraries :: Application Frameworks', diff --git a/testproject/manage.py b/testproject/manage.py index 8c9f710b6..06c89afba 100755 --- a/testproject/manage.py +++ b/testproject/manage.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from __future__ import absolute_import import os import sys diff --git a/testproject/testproject/settings.py b/testproject/testproject/settings.py index 7a3ec92e5..d70c498d4 100644 --- a/testproject/testproject/settings.py +++ b/testproject/testproject/settings.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from os import path as os_path PROJECT_PATH = os_path.abspath(os_path.split(__file__)[0]) diff --git a/testproject/testproject/urls.py b/testproject/testproject/urls.py index 2e1cc9485..eaa47c094 100644 --- a/testproject/testproject/urls.py +++ b/testproject/testproject/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import include, url from django.conf import settings from django.contrib.staticfiles.urls import staticfiles_urlpatterns diff --git a/testproject/testproject/wsgi.py b/testproject/testproject/wsgi.py index cba768928..fc4f30d7d 100644 --- a/testproject/testproject/wsgi.py +++ b/testproject/testproject/wsgi.py @@ -13,7 +13,6 @@ framework. """ -from __future__ import absolute_import import os import sys diff --git a/testproject/vmanage.py b/testproject/vmanage.py index 2a831bb22..68fe4826a 100755 --- a/testproject/vmanage.py +++ b/testproject/vmanage.py @@ -1,5 +1,4 @@ #!virtualenv/bin/python -from __future__ import absolute_import import os import sys diff --git a/tox.ini b/tox.ini index 8533b7248..f2edeb42a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,12 @@ [tox] -envlist = {py35,py38}-django{22} +envlist = py38-django{22,30,31} [testenv] deps = django22: -r requirements/django.txt - -r{toxinidir}/requirements/test.txt + django30: Django>=3.0,<3.1 + django31: Django>=3.1,<3.2 + -r{toxinidir}/requirements/test.txt changedir={toxinidir}/testproject/ commands = pytest --cov wiki --cov django_notify diff --git a/wiki/admin.py b/wiki/admin.py index 6dcbc2d53..c8789338c 100644 --- a/wiki/admin.py +++ b/wiki/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django import forms from django.contrib import admin from django.contrib.contenttypes.admin import GenericTabularInline @@ -22,7 +20,7 @@ class Meta: fields = '__all__' def __init__(self, *args, **kwargs): - super(ArticleRevisionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) EditorClass = editors.getEditorClass() editor = editors.getEditor() self.fields['content'].widget = editor.get_admin_widget() @@ -54,7 +52,7 @@ class Meta: fields = '__all__' def __init__(self, *args, **kwargs): - super(ArticleForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self.instance.pk: revisions = models.ArticleRevision.objects.filter(article=self.instance) self.fields['current_revision'].queryset = revisions @@ -77,7 +75,7 @@ class URLPathAdmin(MPTTModelAdmin): def get_created(self, instance): return instance.article.created - get_created.short_description = _(u'created') + get_created.short_description = _('created') admin.site.register(models.URLPath, URLPathAdmin) diff --git a/wiki/apps.py b/wiki/apps.py index 51417b761..42fb43f76 100644 --- a/wiki/apps.py +++ b/wiki/apps.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from django.apps import AppConfig from django.utils.translation import ugettext_lazy as _ diff --git a/wiki/conf/__init__.py b/wiki/conf/__init__.py index 40a96afc6..e69de29bb 100644 --- a/wiki/conf/__init__.py +++ b/wiki/conf/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/wiki/conf/settings.py b/wiki/conf/settings.py index 178727b1f..e4d8c8bfe 100644 --- a/wiki/conf/settings.py +++ b/wiki/conf/settings.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.conf import settings as django_settings from django.urls import reverse_lazy diff --git a/wiki/core/__init__.py b/wiki/core/__init__.py index de5adcdf3..4a73ce8ac 100644 --- a/wiki/core/__init__.py +++ b/wiki/core/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import bleach import markdown diff --git a/wiki/core/compat.py b/wiki/core/compat.py index 8beeb5f79..ad73030e4 100644 --- a/wiki/core/compat.py +++ b/wiki/core/compat.py @@ -3,7 +3,7 @@ # so we restore that version. # When support for Django < 1.11 is dropped, we should look at using the # new template based rendering, at which point this probably won't be needed at all. -class BuildAttrsCompat(object): +class BuildAttrsCompat: def build_attrs_compat(self, extra_attrs=None, **kwargs): "Helper function for building an attribute dictionary." attrs = self.attrs.copy() diff --git a/wiki/core/diff.py b/wiki/core/diff.py index e19bdac18..4c5fd6f18 100644 --- a/wiki/core/diff.py +++ b/wiki/core/diff.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import difflib diff --git a/wiki/core/exceptions.py b/wiki/core/exceptions.py index a95264532..442b85c9d 100644 --- a/wiki/core/exceptions.py +++ b/wiki/core/exceptions.py @@ -1,4 +1,3 @@ - # If no root URL is found, we raise this... class NoRootURL(Exception): pass diff --git a/wiki/core/extensions.py b/wiki/core/extensions.py index 9fa6a9385..5d19e893c 100644 --- a/wiki/core/extensions.py +++ b/wiki/core/extensions.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from markdown.extensions import Extension from wiki.core.processors import AnchorTagProcessor diff --git a/wiki/core/http.py b/wiki/core/http.py index 3ad4160c3..c93833046 100644 --- a/wiki/core/http.py +++ b/wiki/core/http.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import mimetypes import os from datetime import datetime diff --git a/wiki/core/permissions.py b/wiki/core/permissions.py index a953194db..307869c5c 100644 --- a/wiki/core/permissions.py +++ b/wiki/core/permissions.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from wiki.conf import settings diff --git a/wiki/core/plugins/base.py b/wiki/core/plugins/base.py index eca464da6..1c4e2cbbe 100644 --- a/wiki/core/plugins/base.py +++ b/wiki/core/plugins/base.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.utils.translation import ugettext_lazy as _ @@ -13,7 +11,7 @@ plugin's models. """ -class BasePlugin(object): +class BasePlugin: """Plugins should inherit from this""" # Must fill in! slug = None @@ -38,13 +36,13 @@ class RenderMedia: js = [] css = {} -class PluginSidebarFormMixin(object): +class PluginSidebarFormMixin: def get_usermessage(self): pass -class PluginSettingsFormMixin(object): - settings_form_headline = _(u'Settings for plugin') +class PluginSettingsFormMixin: + settings_form_headline = _('Settings for plugin') settings_order = 1 settings_write_access = False diff --git a/wiki/core/plugins/loader.py b/wiki/core/plugins/loader.py index 6e61aa2a4..122acfc80 100644 --- a/wiki/core/plugins/loader.py +++ b/wiki/core/plugins/loader.py @@ -1,11 +1,9 @@ -# -*- coding: utf-8 -*- """ Credits to ojii, functions get_module and load are from: https://github.com/ojii/django-load. Thanks for the technique! """ -from __future__ import absolute_import, print_function from importlib import import_module diff --git a/wiki/core/plugins/registry.py b/wiki/core/plugins/registry.py index 6b3afb817..ecbb598f0 100644 --- a/wiki/core/plugins/registry.py +++ b/wiki/core/plugins/registry.py @@ -1,10 +1,5 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from importlib import import_module -import six - _cache = {} _settings_forms = [] _markdown_extensions = [] @@ -20,25 +15,25 @@ def register(PluginClass): raise Exception("Plugin class already registered") plugin = PluginClass() _cache[PluginClass] = plugin - + settings_form = getattr(PluginClass, 'settings_form', None) if settings_form: - if isinstance(settings_form, six.string_types): + if isinstance(settings_form, str): klassname = settings_form.split(".")[-1] modulename = ".".join(settings_form.split(".")[:-1]) form_module = import_module(modulename) settings_form = getattr(form_module, klassname) _settings_forms.append(settings_form) - - + + if getattr(PluginClass, 'article_tab', None): _article_tabs.append(plugin) - + if getattr(PluginClass, 'sidebar', None): _sidebar.append(plugin) - _markdown_extensions.extend(getattr(PluginClass, 'markdown_extensions', [])) - + _markdown_extensions.extend(getattr(PluginClass, 'markdown_extensions', [])) + def get_plugins(): """Get loaded plugins - do not call before all plugins are loaded.""" return _cache diff --git a/wiki/core/processors.py b/wiki/core/processors.py index 216c01f15..d1b00a8e6 100644 --- a/wiki/core/processors.py +++ b/wiki/core/processors.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from markdown.treeprocessors import Treeprocessor diff --git a/wiki/decorators.py b/wiki/decorators.py index e90c4634d..810f0fe1c 100644 --- a/wiki/decorators.py +++ b/wiki/decorators.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - import json from django.conf import settings as django_settings @@ -10,7 +7,6 @@ from django.template.context import RequestContext from django.template.loader import render_to_string from django.urls import reverse -from six.moves import filter from wiki.core.exceptions import NoRootURL diff --git a/wiki/editors/__init__.py b/wiki/editors/__init__.py index d1a3d88eb..a03d34e48 100644 --- a/wiki/editors/__init__.py +++ b/wiki/editors/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.urls import get_callable from wiki.conf import settings diff --git a/wiki/editors/base.py b/wiki/editors/base.py index 94687e39f..c7e589dc8 100644 --- a/wiki/editors/base.py +++ b/wiki/editors/base.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django import forms diff --git a/wiki/editors/markitup.py b/wiki/editors/markitup.py index 67c7c51ba..399ab8379 100644 --- a/wiki/editors/markitup.py +++ b/wiki/editors/markitup.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django import forms from django.forms.utils import flatatt from django.utils.encoding import force_text @@ -18,12 +16,12 @@ def __init__(self, attrs=None): 'rows': '10', 'cols': '40',} if attrs: default_attrs.update(attrs) - super(MarkItUpAdminWidget, self).__init__(default_attrs) + super().__init__(default_attrs) def render(self, name, value, attrs=None): if value is None: value = '' final_attrs = self.build_attrs_compat(attrs, name=name) - return mark_safe(u'%s' % (flatatt(final_attrs), + return mark_safe('%s' % (flatatt(final_attrs), conditional_escape(force_text(value)))) @@ -34,12 +32,12 @@ def __init__(self, attrs=None): 'rows': '10', 'cols': '40',} if attrs: default_attrs.update(attrs) - super(MarkItUpWidget, self).__init__(default_attrs) + super().__init__(default_attrs) def render(self, name, value, attrs=None, renderer=None): if value is None: value = '' final_attrs = self.build_attrs_compat(attrs, name=name) - return mark_safe(u'
%s
' % (flatatt(final_attrs), + return mark_safe('
%s
' % (flatatt(final_attrs), conditional_escape(force_text(value)))) class MarkItUp(BaseEditor): diff --git a/wiki/forms.py b/wiki/forms.py index ffd11e296..c6a6e382f 100644 --- a/wiki/forms.py +++ b/wiki/forms.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function - from itertools import chain from django import forms @@ -37,19 +34,19 @@ def check_spam(self, current_revision, request): class CreateRootForm(forms.Form): - title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.')) - content = forms.CharField(label=_(u'Type in some contents'), - help_text=_(u'This is just the initial contents of your article. After creating it, you can use more complex features like adding plugins, meta data, related articles etc...'), + title = forms.CharField(label=_('Title'), help_text=_('Initial title of the article. May be overridden with revision titles.')) + content = forms.CharField(label=_('Type in some contents'), + help_text=_('This is just the initial contents of your article. After creating it, you can use more complex features like adding plugins, meta data, related articles etc...'), required=False, widget=getEditor().get_widget()) #@UndefinedVariable class EditForm(forms.Form): - title = forms.CharField(label=_(u'Title'),) - content = forms.CharField(label=_(u'Contents'), + title = forms.CharField(label=_('Title'),) + content = forms.CharField(label=_('Contents'), required=False, widget=getEditor().get_widget()) #@UndefinedVariable - summary = forms.CharField(label=_(u'Summary'), help_text=_(u'Give a short reason for your edit, which will be stated in the revision log.'), + summary = forms.CharField(label=_('Summary'), help_text=_('Give a short reason for your edit, which will be stated in the revision log.'), required=False) current_revision = forms.IntegerField(required=False, widget=forms.HiddenInput()) @@ -87,7 +84,7 @@ def __init__(self, current_revision, *args, **kwargs): kwargs['initial'] = initial - super(EditForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_title(self): title = strip_tags(self.cleaned_data['title']) @@ -98,9 +95,9 @@ def clean(self): if self.no_clean or self.preview: return cd if not str(self.initial_revision.id) == str(self.presumed_revision): - raise forms.ValidationError(_(u'While you were editing, someone else changed the revision. Your contents have been automatically merged with the new contents. Please review the text below.')) + raise forms.ValidationError(_('While you were editing, someone else changed the revision. Your contents have been automatically merged with the new contents. Please review the text below.')) if cd['title'] == self.initial_revision.title and cd['content'] == self.initial_revision.content: - raise forms.ValidationError(_(u'No changes made. Nothing to save.')) + raise forms.ValidationError(_('No changes made. Nothing to save.')) return cd @@ -138,10 +135,10 @@ class SelectWidgetBootstrap(BuildAttrsCompat, forms.Select): """) def __init__(self, attrs={'class': 'btn-group pull-left btn-group-form'}, choices=()): self.noscript_widget = forms.Select(attrs={}, choices=choices) - super(SelectWidgetBootstrap, self).__init__(attrs, choices) + super().__init__(attrs, choices) def __setattr__(self, k, value): - super(SelectWidgetBootstrap, self).__setattr__(k, value) + super().__setattr__(k, value) if k != 'attrs': self.noscript_widget.__setattr__(k, value) @@ -162,57 +159,57 @@ def render(self, name, value, attrs=None, choices=()): """""" % {'attrs': flatatt(final_attrs), 'options':self.render_options(choices, [value]), - 'label': _(u'Select an option'), + 'label': _('Select an option'), 'name': name, 'js': SelectWidgetBootstrap.js, 'noscript': self.noscript_widget.render(name, value, {}, choices)} ] - return mark_safe(u'\n'.join(output)) + return mark_safe('\n'.join(output)) def render_option(self, selected_choices, option_value, option_label): option_value = force_text(option_value) - selected_html = (option_value in selected_choices) and u' selected="selected"' or '' - return u'
  • %s
  • ' % ( + selected_html = (option_value in selected_choices) and ' selected="selected"' or '' + return '
  • %s
  • ' % ( escape(option_value), selected_html, conditional_escape(force_text(option_label))) def render_options(self, choices, selected_choices): # Normalize to strings. - selected_choices = set([force_text(v) for v in selected_choices]) + selected_choices = {force_text(v) for v in selected_choices} output = [] for option_value, option_label in chain(self.choices, choices): if isinstance(option_label, (list, tuple)): - output.append(u'
  • ' % escape(force_text(option_value))) + output.append('
  • ' % escape(force_text(option_value))) for option in option_label: output.append(self.render_option(selected_choices, *option)) else: output.append(self.render_option(selected_choices, option_value, option_label)) - return u'\n'.join(output) + return '\n'.join(output) class TextInputPrepend(forms.TextInput): def __init__(self, *args, **kwargs): self.prepend = kwargs.pop('prepend', "") - super(TextInputPrepend, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def render(self, *args, **kwargs): - html = super(TextInputPrepend, self).render(*args, **kwargs) + html = super().render(*args, **kwargs) return mark_safe('
    %s%s
    ' % (self.prepend, html)) class CreateForm(forms.Form): def __init__(self, urlpath_parent, *args, **kwargs): - super(CreateForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.urlpath_parent = urlpath_parent - title = forms.CharField(label=_(u'Title'),) - slug = forms.SlugField(label=_(u'Slug'), help_text=_(u"This will be the address where your article can be found. Use only alphanumeric characters and - or _. Note that you cannot change the slug after creating the article."), + title = forms.CharField(label=_('Title'),) + slug = forms.SlugField(label=_('Slug'), help_text=_("This will be the address where your article can be found. Use only alphanumeric characters and - or _. Note that you cannot change the slug after creating the article."), max_length=models.URLPath.SLUG_MAX_LENGTH) - content = forms.CharField(label=_(u'Contents'), + content = forms.CharField(label=_('Contents'), required=False, widget=getEditor().get_widget()) #@UndefinedVariable - summary = forms.CharField(label=_(u'Summary'), help_text=_(u"Write a brief message for the article's history log."), + summary = forms.CharField(label=_('Summary'), help_text=_("Write a brief message for the article's history log."), required=False) def clean_title(self): @@ -222,7 +219,7 @@ def clean_title(self): def clean_slug(self): slug = self.cleaned_data['slug'] if slug.startswith("_"): - raise forms.ValidationError(_(u'A slug may not begin with an underscore.')) + raise forms.ValidationError(_('A slug may not begin with an underscore.')) if settings.URL_CASE_SENSITIVE: already_existing_slug = models.URLPath.objects.filter(slug=slug, parent=self.urlpath_parent) @@ -231,9 +228,9 @@ def clean_slug(self): if already_existing_slug: already_urlpath = already_existing_slug[0] if already_urlpath.article and already_urlpath.article.current_revision.deleted: - raise forms.ValidationError(_(u'A deleted article with slug "%s" already exists.') % already_urlpath.slug) + raise forms.ValidationError(_('A deleted article with slug "%s" already exists.') % already_urlpath.slug) else: - raise forms.ValidationError(_(u'A slug named "%s" already exists.') % already_urlpath.slug) + raise forms.ValidationError(_('A slug named "%s" already exists.') % already_urlpath.slug) return slug @@ -243,42 +240,42 @@ class DeleteForm(forms.Form): def __init__(self, *args, **kwargs): self.article = kwargs.pop('article') self.has_children = kwargs.pop('has_children') - super(DeleteForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) confirm = forms.BooleanField(required=False, - label=_(u'Yes, I am sure')) + label=_('Yes, I am sure')) purge = forms.BooleanField(widget=HiddenInput(), required=False, - label=_(u'Purge'), - help_text=_(u'Purge the article: Completely remove it (and all its contents) with no undo. Purging is a good idea if you want to free the slug such that users can create new articles in its place.')) + label=_('Purge'), + help_text=_('Purge the article: Completely remove it (and all its contents) with no undo. Purging is a good idea if you want to free the slug such that users can create new articles in its place.')) revision = forms.ModelChoiceField(models.ArticleRevision.objects.all(), widget=HiddenInput(), required=False) def clean(self): cd = self.cleaned_data if not cd['confirm']: - raise forms.ValidationError(_(u'You are not sure enough!')) + raise forms.ValidationError(_('You are not sure enough!')) if cd['revision'] != self.article.current_revision: - raise forms.ValidationError(_(u'While you tried to delete this article, it was modified. TAKE CARE!')) + raise forms.ValidationError(_('While you tried to delete this article, it was modified. TAKE CARE!')) return cd class PermissionsForm(PluginSettingsFormMixin, forms.ModelForm): - locked = forms.BooleanField(label=_(u'Lock article'), help_text=_(u'Deny all users access to edit this article.'), + locked = forms.BooleanField(label=_('Lock article'), help_text=_('Deny all users access to edit this article.'), required=False) - settings_form_headline = _(u'Permissions') + settings_form_headline = _('Permissions') settings_order = 5 settings_write_access = False - owner_username = forms.CharField(required=False, label=_(u'Owner'), - help_text=_(u'Enter the username of the owner.')) - group = forms.ModelChoiceField(models.Group.objects.all(), empty_label=_(u'(none)'), + owner_username = forms.CharField(required=False, label=_('Owner'), + help_text=_('Enter the username of the owner.')) + group = forms.ModelChoiceField(models.Group.objects.all(), empty_label=_('(none)'), required=False) if settings.USE_BOOTSTRAP_SELECT_WIDGET: group.widget= SelectWidgetBootstrap() - recursive = forms.BooleanField(label=_(u'Inherit permissions'), help_text=_(u'Check here to apply the above permissions recursively to articles under this one.'), + recursive = forms.BooleanField(label=_('Inherit permissions'), help_text=_('Check here to apply the above permissions recursively to articles under this one.'), required=False) def get_usermessage(self): @@ -293,7 +290,7 @@ def __init__(self, article, request, *args, **kwargs): self.request = request kwargs['instance'] = article kwargs['initial'] = {'locked': article.current_revision.locked} - super(PermissionsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.can_change_groups = False self.can_assign = False @@ -324,7 +321,7 @@ def clean_owner_username(self): try: user = User.objects.get(username=username) except models.User.DoesNotExist: - raise forms.ValidationError(_(u'No user with that username')) + raise forms.ValidationError(_('No user with that username')) else: user = None else: @@ -332,7 +329,7 @@ def clean_owner_username(self): return user def save(self, commit=True): - article = super(PermissionsForm, self).save(commit=False) + article = super().save(commit=False) article.owner = self.cleaned_data['owner_username'] if not self.can_change_groups: article.group = self.article.group @@ -346,14 +343,14 @@ def save(self, commit=True): revision = models.ArticleRevision() revision.inherit_predecessor(self.article) revision.set_from_request(self.request) - revision.automatic_log = _(u'Article locked for editing') + revision.automatic_log = _('Article locked for editing') revision.locked = True self.article.add_revision(revision) elif not self.cleaned_data['locked'] and article.current_revision.locked: revision = models.ArticleRevision() revision.inherit_predecessor(self.article) revision.set_from_request(self.request) - revision.automatic_log = _(u'Article unlocked for editing') + revision.automatic_log = _('Article unlocked for editing') revision.locked = False self.article.add_revision(revision) @@ -366,4 +363,4 @@ class Meta: class DirFilterForm(forms.Form): - query = forms.CharField(label=_(u'Filter'), widget=forms.TextInput(attrs={'class': 'search-query'})) + query = forms.CharField(label=_('Filter'), widget=forms.TextInput(attrs={'class': 'search-query'})) diff --git a/wiki/management/commands/wikiviz.py b/wiki/management/commands/wikiviz.py index 9e6c53563..7062d5b9e 100644 --- a/wiki/management/commands/wikiviz.py +++ b/wiki/management/commands/wikiviz.py @@ -43,7 +43,6 @@ -e, --inheritance show inheritance arrows. """ -from __future__ import absolute_import, print_function import os import sys @@ -205,7 +204,7 @@ def add_attributes(field): t = type(field).__name__ if isinstance(field, (OneToOneField, ForeignKey)): - t += " ({0})".format(field.rel.field_name) + t += f" ({field.rel.field_name})" # TODO: ManyToManyField, GenericRelation model['fields'].append({ diff --git a/wiki/managers.py b/wiki/managers.py index 1c6c0f5e2..3876c15ee 100644 --- a/wiki/managers.py +++ b/wiki/managers.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.db import models from django.db.models import Q from django.db.models.query import EmptyQuerySet, QuerySet diff --git a/wiki/middleware.py b/wiki/middleware.py index 803eb8a7c..29185f4b4 100644 --- a/wiki/middleware.py +++ b/wiki/middleware.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import importlib import threading @@ -23,7 +21,7 @@ class _RequestCache(threading.local): A thread-local for storing the per-request cache. """ def __init__(self): - super(_RequestCache, self).__init__() + super().__init__() self.data = {} self.request = None diff --git a/wiki/migrations/0001_initial.py b/wiki/migrations/0001_initial.py index 00915fcfc..8b52ac707 100644 --- a/wiki/migrations/0001_initial.py +++ b/wiki/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - import mptt.fields from django.conf import settings from django.db import migrations, models @@ -222,7 +219,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='urlpath', - unique_together=set([('site', 'parent', 'slug')]), + unique_together={('site', 'parent', 'slug')}, ), migrations.AddField( model_name='revisionpluginrevision', @@ -280,7 +277,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='articlerevision', - unique_together=set([('article', 'revision_number')]), + unique_together={('article', 'revision_number')}, ), migrations.AddField( model_name='articleplugin', @@ -290,7 +287,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='articleforobject', - unique_together=set([('content_type', 'object_id')]), + unique_together={('content_type', 'object_id')}, ), migrations.AddField( model_name='article', diff --git a/wiki/migrations/0002_remove_article_subscription.py b/wiki/migrations/0002_remove_article_subscription.py index be93ad704..55d1295c2 100644 --- a/wiki/migrations/0002_remove_article_subscription.py +++ b/wiki/migrations/0002_remove_article_subscription.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - from django.db import migrations, models diff --git a/wiki/migrations/0003_ip_address_conv.py b/wiki/migrations/0003_ip_address_conv.py index 22612fb9c..5d6e6cba1 100644 --- a/wiki/migrations/0003_ip_address_conv.py +++ b/wiki/migrations/0003_ip_address_conv.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - from django.db import migrations, models diff --git a/wiki/migrations/0004_increase_slug_size.py b/wiki/migrations/0004_increase_slug_size.py index ef5038f0d..01e491dc8 100644 --- a/wiki/migrations/0004_increase_slug_size.py +++ b/wiki/migrations/0004_increase_slug_size.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - from django.db import migrations, models diff --git a/wiki/migrations/0005_remove_attachments_and_images.py b/wiki/migrations/0005_remove_attachments_and_images.py index c44b9105d..308664e38 100644 --- a/wiki/migrations/0005_remove_attachments_and_images.py +++ b/wiki/migrations/0005_remove_attachments_and_images.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.5 on 2017-09-07 19:03 -from __future__ import absolute_import, unicode_literals from django.db import migrations diff --git a/wiki/migrations/0006_auto_20200110_1003.py b/wiki/migrations/0006_auto_20200110_1003.py index fd82ade41..4fb7a1662 100644 --- a/wiki/migrations/0006_auto_20200110_1003.py +++ b/wiki/migrations/0006_auto_20200110_1003.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.27 on 2020-01-10 10:03 -from __future__ import unicode_literals from django.db import migrations, models diff --git a/wiki/models/__init__.py b/wiki/models/__init__.py index a3e127ac3..e95e840fa 100644 --- a/wiki/models/__init__.py +++ b/wiki/models/__init__.py @@ -1,9 +1,5 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.conf import settings as django_settings from django.core.exceptions import ImproperlyConfigured -from six import string_types, text_type # TODO: Don't use wildcards from .article import * @@ -75,7 +71,7 @@ def reverse(*args, **kwargs): return the result of calling reverse._transform_url(reversed_url) for every url in the wiki namespace. """ - if args and isinstance(args[0], string_types) and args[0].startswith('wiki:'): + if args and isinstance(args[0], str) and args[0].startswith('wiki:'): url_kwargs = kwargs.get('kwargs', {}) path = url_kwargs.get('path', False) # If a path is supplied then discard the article_id @@ -94,7 +90,7 @@ def reverse(*args, **kwargs): # Now we redefine reverse method -reverse_lazy = lazy(reverse, text_type) +reverse_lazy = lazy(reverse, str) urls.reverse = reverse urls.reverse_lazy = reverse_lazy diff --git a/wiki/models/article.py b/wiki/models/article.py index 9991341a1..901e86275 100644 --- a/wiki/models/article.py +++ b/wiki/models/article.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - import bleach from django.contrib.auth.models import Group, User from django.contrib.contenttypes import fields @@ -23,27 +20,27 @@ class Article(models.Model): objects = managers.ArticleManager() current_revision = models.OneToOneField('ArticleRevision', - verbose_name=_(u'current revision'), + verbose_name=_('current revision'), blank=True, null=True, related_name='current_set', - help_text=_(u'The revision being displayed for this article. If you need to do a roll-back, simply change the value of this field.'), on_delete=models.CASCADE + help_text=_('The revision being displayed for this article. If you need to do a roll-back, simply change the value of this field.'), on_delete=models.CASCADE ) - created = models.DateTimeField(auto_now_add=True, verbose_name=_(u'created')) - modified = models.DateTimeField(auto_now=True, verbose_name=_(u'modified'), - help_text=_(u'Article properties last modified')) + created = models.DateTimeField(auto_now_add=True, verbose_name=_('created')) + modified = models.DateTimeField(auto_now=True, verbose_name=_('modified'), + help_text=_('Article properties last modified')) owner = models.ForeignKey(User, verbose_name=_('owner'), blank=True, null=True, related_name='owned_articles', - help_text=_(u'The owner of the article, usually the creator. The owner always has both read and write access.'), on_delete=models.CASCADE) + help_text=_('The owner of the article, usually the creator. The owner always has both read and write access.'), on_delete=models.CASCADE) group = models.ForeignKey(Group, verbose_name=_('group'), blank=True, null=True, - help_text=_(u'Like in a UNIX file system, permissions can be given to a user according to group membership. Groups are handled through the Django auth system.'), on_delete=models.CASCADE) + help_text=_('Like in a UNIX file system, permissions can be given to a user according to group membership. Groups are handled through the Django auth system.'), on_delete=models.CASCADE) - group_read = models.BooleanField(default=True, verbose_name=_(u'group read access')) - group_write = models.BooleanField(default=True, verbose_name=_(u'group write access')) - other_read = models.BooleanField(default=True, verbose_name=_(u'others read access')) - other_write = models.BooleanField(default=True, verbose_name=_(u'others write access')) + group_read = models.BooleanField(default=True, verbose_name=_('group read access')) + group_write = models.BooleanField(default=True, verbose_name=_('group write access')) + other_read = models.BooleanField(default=True, verbose_name=_('others read access')) + other_write = models.BooleanField(default=True, verbose_name=_('others write access')) # TODO: Do not use kwargs, it can lead to dangerous situations with bad # permission checking patterns. Also, since there are no other keywords, @@ -96,8 +93,7 @@ def can_assign(self, user): def descendant_objects(self): """NB! This generator is expensive, so use it with care!!""" for obj in self.articleforobject_set.filter(is_mptt=True): - for descendant in obj.content_object.get_descendants(): - yield descendant + yield from obj.content_object.get_descendants() def get_children(self, max_num=None, user_can_read=None, **kwargs): """NB! This generator is expensive, so use it with care!!""" @@ -174,7 +170,7 @@ def get_for_object(cls, obj): def __unicode__(self): if self.current_revision: return self.current_revision.title - return _(u'Article without content (%(id)d)') % {'id': self.id} + return _('Article without content (%(id)d)') % {'id': self.id} class Meta: permissions = ( @@ -210,8 +206,8 @@ class ArticleForObject(models.Model): is_mptt = models.BooleanField(default=False, editable=False) class Meta: - verbose_name = _(u'Article for object') - verbose_name_plural = _(u'Articles for object') + verbose_name = _('Article for object') + verbose_name_plural = _('Articles for object') # Do not allow several objects unique_together = ('content_type', 'object_id') @@ -220,7 +216,7 @@ class BaseRevisionMixin(models.Model): """This is an abstract model used as a mixin: Do not override any of the core model methods but respect the inheritor's freedom to do so itself.""" - revision_number = models.IntegerField(editable=False, verbose_name=_(u'revision number')) + revision_number = models.IntegerField(editable=False, verbose_name=_('revision number')) user_message = models.TextField(blank=True) automatic_log = models.TextField(blank=True, editable=False) @@ -246,8 +242,8 @@ class BaseRevisionMixin(models.Model): # NOTE! The semantics of these fields are not related to the revision itself # but the actual related object. If the latest revision says "deleted=True" then # the related object should be regarded as deleted. - deleted = models.BooleanField(verbose_name=_(u'deleted'), default=False) - locked = models.BooleanField(verbose_name=_(u'locked'), default=False) + deleted = models.BooleanField(verbose_name=_('deleted'), default=False) + locked = models.BooleanField(verbose_name=_('locked'), default=False) def set_from_request(self, request): if request.user.is_authenticated: @@ -266,15 +262,15 @@ class ArticleRevision(BaseRevisionMixin, models.Model): copy, do NEVER create m2m relationships.""" article = models.ForeignKey('Article', on_delete=models.CASCADE, - verbose_name=_(u'article')) + verbose_name=_('article')) # This is where the content goes, with whatever markup language is used - content = models.TextField(blank=True, verbose_name=_(u'article contents')) + content = models.TextField(blank=True, verbose_name=_('article contents')) # This title is automatically set from either the article's title or # the last used revision... - title = models.CharField(max_length=512, verbose_name=_(u'article title'), - null=False, blank=False, help_text=_(u'Each revision contains a title field that must be filled out, even if the title has not changed')) + title = models.CharField(max_length=512, verbose_name=_('article title'), + null=False, blank=False, help_text=_('Each revision contains a title field that must be filled out, even if the title has not changed')) # TODO: # Allow a revision to redirect to another *article*. This @@ -316,7 +312,7 @@ def save(self, *args, **kwargs): self.revision_number = 1 self.clean_data() - super(ArticleRevision, self).save(*args, **kwargs) + super().save(*args, **kwargs) if not self.article.current_revision: # If I'm saved from Django admin, then article.current_revision is me! diff --git a/wiki/models/pluginbase.py b/wiki/models/pluginbase.py index f78519998..d97d8ed70 100644 --- a/wiki/models/pluginbase.py +++ b/wiki/models/pluginbase.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.db import models from django.db.models import signals from django.utils.translation import ugettext_lazy as _ @@ -39,7 +36,7 @@ class ArticlePlugin(models.Model): properties in the future...""" article = models.ForeignKey(Article, on_delete=models.CASCADE, - verbose_name=_(u"article")) + verbose_name=_("article")) deleted = models.BooleanField(default=False) @@ -75,8 +72,8 @@ class ReusablePlugin(ArticlePlugin): # The article on which the plugin was originally created. # Used to apply permissions. ArticlePlugin.article.on_delete=models.SET_NULL - ArticlePlugin.article.verbose_name=_(u'original article') - ArticlePlugin.article.help_text=_(u'Permissions are inherited from this article') + ArticlePlugin.article.verbose_name=_('original article') + ArticlePlugin.article.help_text=_('Permissions are inherited from this article') ArticlePlugin.article.null = True ArticlePlugin.article.blank = True @@ -101,7 +98,7 @@ def save(self, *args, **kwargs): if articles.count() == 0: self.article = articles[0] - super(ReusablePlugin, self).save(*args, **kwargs) + super().save(*args, **kwargs) class SimplePluginCreateError(Exception): pass @@ -128,13 +125,13 @@ class YourPlugin(SimplePlugin): article_revision = models.ForeignKey(ArticleRevision, on_delete=models.CASCADE) def __init__(self, *args, **kwargs): - super(SimplePlugin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if not self.id and not 'article' in kwargs: raise SimplePluginCreateError("Keyword argument 'article' expected.") self.article = kwargs['article'] def get_logmessage(self): - return _(u"A plugin was changed") + return _("A plugin was changed") def save(self, *args, **kwargs): if not self.id: @@ -146,7 +143,7 @@ def save(self, *args, **kwargs): new_revision.save() self.article_revision = new_revision - super(SimplePlugin, self).save(*args, **kwargs) + super().save(*args, **kwargs) class RevisionPlugin(ArticlePlugin): @@ -159,9 +156,9 @@ class RevisionPlugin(ArticlePlugin): """ # The current revision of this plugin, if any! current_revision = models.OneToOneField('RevisionPluginRevision', - verbose_name=_(u'current revision'), + verbose_name=_('current revision'), blank=True, null=True, related_name='plugin_set', - help_text=_(u'The revision being displayed for this plugin.' + help_text=_('The revision being displayed for this plugin.' 'If you need to do a roll-back, simply change the value of this field.'), on_delete=models.CASCADE ) @@ -213,7 +210,7 @@ def save(self, *args, **kwargs): except RevisionPluginRevision.DoesNotExist: self.revision_number = 1 - super(RevisionPluginRevision, self).save(*args, **kwargs) + super().save(*args, **kwargs) if not self.plugin.current_revision: # If I'm saved from Django admin, then plugin.current_revision is me! diff --git a/wiki/models/urlpath.py b/wiki/models/urlpath.py index 5f717b73f..88562425b 100644 --- a/wiki/models/urlpath.py +++ b/wiki/models/urlpath.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function - import logging from django.contrib.contenttypes import fields @@ -40,13 +37,13 @@ class URLPath(MPTTModel): # Do NOT modify this field - it is updated with signals whenever ArticleForObject is changed. article = models.ForeignKey(Article, on_delete=models.CASCADE, editable=False, - verbose_name=_(u'Cache lookup value for articles')) + verbose_name=_('Cache lookup value for articles')) # The slug is constructed from course key and will in practice be much shorter then 255 characters # since course keys are capped at 65 characters in the Studio (https://openedx.atlassian.net/browse/TNL-889). SLUG_MAX_LENGTH = 255 - slug = models.SlugField(verbose_name=_(u'slug'), null=True, blank=True, + slug = models.SlugField(verbose_name=_('slug'), null=True, blank=True, max_length=SLUG_MAX_LENGTH) site = models.ForeignKey(Site, on_delete=models.CASCADE) parent = TreeForeignKey('self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) @@ -55,7 +52,7 @@ def __init__(self, *args, **kwargs): pass # Fixed in django-mptt 0.5.3 #self._tree_manager = URLPath.objects - return super(URLPath, self).__init__(*args, **kwargs) + return super().__init__(*args, **kwargs) @property def cached_ancestors(self): @@ -136,29 +133,29 @@ class MPTTMeta: def __unicode__(self): path = self.path - return path if path else ugettext(u"(root)") + return path if path else ugettext("(root)") def save(self, *args, **kwargs): - super(URLPath, self).save(*args, **kwargs) + super().save(*args, **kwargs) def delete(self, *args, **kwargs): assert not (self.parent and self.get_children()), "You cannot delete a root article with children." - super(URLPath, self).delete(*args, **kwargs) + super().delete(*args, **kwargs) class Meta: - verbose_name = _(u'URL path') - verbose_name_plural = _(u'URL paths') + verbose_name = _('URL path') + verbose_name_plural = _('URL paths') unique_together = ('site', 'parent', 'slug') def clean(self, *args, **kwargs): if self.slug and not self.parent: - raise ValidationError(_(u'Sorry but you cannot have a root article with a slug.')) + raise ValidationError(_('Sorry but you cannot have a root article with a slug.')) if not self.slug and self.parent: - raise ValidationError(_(u'A non-root note must always have a slug.')) + raise ValidationError(_('A non-root note must always have a slug.')) if not self.parent: if URLPath.objects.root_nodes().filter(site=self.site).exclude(id=self.id): - raise ValidationError(_(u'There is already a root node on %s') % self.site) - super(URLPath, self).clean(*args, **kwargs) + raise ValidationError(_('There is already a root node on %s') % self.site) + super().clean(*args, **kwargs) @classmethod def get_by_path(cls, path, select_related=False): @@ -262,10 +259,10 @@ def on_article_delete(instance, *args, **kwargs): other_read = False, other_write = False) article.add_revision(ArticleRevision( - content=_(u'Articles who lost their parents\n' + content=_('Articles who lost their parents\n' '===============================\n\n' 'The children of this article have had their parents deleted. You should probably find a new home for them.'), - title=_(u"Lost and found"))) + title=_("Lost and found"))) lost_and_found = URLPath.objects.create(slug=settings.LOST_AND_FOUND_SLUG, parent=URLPath.root(), site=site, diff --git a/wiki/plugins/attachments/__init__.py b/wiki/plugins/attachments/__init__.py index 44433dce7..fe5b55e46 100644 --- a/wiki/plugins/attachments/__init__.py +++ b/wiki/plugins/attachments/__init__.py @@ -1,3 +1 @@ -from __future__ import unicode_literals - default_app_config = 'wiki.apps.AttachmentsConfig' diff --git a/wiki/plugins/attachments/admin.py b/wiki/plugins/attachments/admin.py index 0f6ccdf50..c02aae1aa 100644 --- a/wiki/plugins/attachments/admin.py +++ b/wiki/plugins/attachments/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from wiki.plugins.attachments import models diff --git a/wiki/plugins/attachments/forms.py b/wiki/plugins/attachments/forms.py index 8163afe67..2b5db386f 100644 --- a/wiki/plugins/attachments/forms.py +++ b/wiki/plugins/attachments/forms.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django import forms from django.utils.translation import ugettext_lazy as _ @@ -9,8 +6,8 @@ class AttachmentForm(forms.ModelForm): - description = forms.CharField(label=_(u'Description'), - help_text=_(u'A short summary of what the file contains'), + description = forms.CharField(label=_('Description'), + help_text=_('A short summary of what the file contains'), required=False) class Meta: @@ -19,12 +16,12 @@ class Meta: class DeleteForm(forms.Form): """This form is both used for dereferencing and deleting attachments""" - confirm = forms.BooleanField(label=_(u'Yes I am sure...'), + confirm = forms.BooleanField(label=_('Yes I am sure...'), required=False) def clean_confirm(self): if not self.cleaned_data['confirm']: - raise forms.ValidationError(_(u'You are not sure enough!')) + raise forms.ValidationError(_('You are not sure enough!')) return True class SearchForm(forms.Form): diff --git a/wiki/plugins/attachments/markdown_extensions.py b/wiki/plugins/attachments/markdown_extensions.py index 3ccabeaf3..3767da5d8 100644 --- a/wiki/plugins/attachments/markdown_extensions.py +++ b/wiki/plugins/attachments/markdown_extensions.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import re import markdown @@ -32,9 +30,9 @@ def run(self, lines): id=attachment_id, current_revision__deleted=False) url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id, 'attachment_id':attachment.id,}) - line = line.replace(m.group(1), u"""%s""" % - (url, _(u"Click to download file"), attachment.original_filename)) + line = line.replace(m.group(1), """%s""" % + (url, _("Click to download file"), attachment.original_filename)) except models.Attachment.DoesNotExist: - line = line.replace(m.group(1), u"""Attachment with ID #%s is deleted.""" % attachment_id) + line = line.replace(m.group(1), """Attachment with ID #%s is deleted.""" % attachment_id) new_text.append(line) return new_text diff --git a/wiki/plugins/attachments/migrations/0001_initial.py b/wiki/plugins/attachments/migrations/0001_initial.py index a3d777159..aebbc234b 100644 --- a/wiki/plugins/attachments/migrations/0001_initial.py +++ b/wiki/plugins/attachments/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - from django.conf import settings from django.db import migrations, models diff --git a/wiki/plugins/attachments/models.py b/wiki/plugins/attachments/models.py index 832cc7868..74dc5862c 100644 --- a/wiki/plugins/attachments/models.py +++ b/wiki/plugins/attachments/models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -19,13 +17,13 @@ class Attachment(ReusablePlugin): objects = managers.ArticleFkManager() current_revision = models.OneToOneField('AttachmentRevision', - verbose_name=_(u'current revision'), + verbose_name=_('current revision'), blank=True, null=True, related_name='current_set', - help_text=_(u'The revision of this attachment currently in use (on all articles using the attachment)'), + help_text=_('The revision of this attachment currently in use (on all articles using the attachment)'), on_delete=models.CASCADE ) - original_filename = models.CharField(max_length=256, verbose_name=_(u'original filename'), blank=True, null=True) + original_filename = models.CharField(max_length=256, verbose_name=_('original filename'), blank=True, null=True) def can_write(self, **kwargs): user = kwargs.get('user', None) @@ -37,8 +35,8 @@ def can_delete(self, user): return self.can_write(user=user) class Meta: - verbose_name = _(u'attachment') - verbose_name_plural = _(u'attachments') + verbose_name = _('attachment') + verbose_name_plural = _('attachments') db_table = 'wiki_attachments_attachment' def __unicode__(self): @@ -80,14 +78,14 @@ class AttachmentRevision(BaseRevisionMixin, models.Model): attachment = models.ForeignKey('Attachment', on_delete=models.CASCADE) file = models.FileField(upload_to=upload_path, #@ReservedAssignment - verbose_name=_(u'file'), + verbose_name=_('file'), storage=settings.STORAGE_BACKEND) description = models.TextField(blank=True) class Meta: - verbose_name = _(u'attachment revision') - verbose_name_plural = _(u'attachment revisions') + verbose_name = _('attachment revision') + verbose_name_plural = _('attachment revisions') ordering = ('created',) get_latest_by = 'revision_number' db_table = 'wiki_attachments_attachmentrevision' @@ -128,7 +126,7 @@ def save(self, *args, **kwargs): except (AttachmentRevision.DoesNotExist, Attachment.DoesNotExist): self.revision_number = 1 - super(AttachmentRevision, self).save(*args, **kwargs) + super().save(*args, **kwargs) if not self.attachment.current_revision: # If I'm saved from Django admin, then article.current_revision is me! diff --git a/wiki/plugins/attachments/settings.py b/wiki/plugins/attachments/settings.py index e0fa297ab..d72149386 100644 --- a/wiki/plugins/attachments/settings.py +++ b/wiki/plugins/attachments/settings.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf import settings as django_settings from django.core.files.storage import default_storage diff --git a/wiki/plugins/attachments/views.py b/wiki/plugins/attachments/views.py index cc7896d0a..da151101e 100644 --- a/wiki/plugins/attachments/views.py +++ b/wiki/plugins/attachments/views.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.contrib import messages from django.db import transaction from django.db.models import Q @@ -31,7 +28,7 @@ def dispatch(self, request, article, *args, **kwargs): self.attachments = models.Attachment.objects.active().filter(articles=article) # Fixing some weird transaction issue caused by adding commit_manually to form_valid - return super(AttachmentView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def form_valid(self, form): if (self.request.user.is_anonymous and not settings.ANONYMOUS or @@ -48,11 +45,11 @@ def form_valid(self, form): attachment_revision.attachment = attachment attachment_revision.set_from_request(self.request) attachment_revision.save() - messages.success(self.request, _(u'%s was successfully added.') % attachment_revision.get_filename()) + messages.success(self.request, _('%s was successfully added.') % attachment_revision.get_filename()) except models.IllegalFileExtension as e: - messages.error(self.request, _(u'Your file could not be saved: %s') % e) + messages.error(self.request, _('Your file could not be saved: %s') % e) except Exception: - messages.error(self.request, _(u'Your file could not be saved, probably because of a permission error on the web server.')) + messages.error(self.request, _('Your file could not be saved, probably because of a permission error on the web server.')) return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id) @@ -66,7 +63,7 @@ def get_context_data(self, **kwargs): kwargs['search_form'] = forms.SearchForm() kwargs['selected_tab'] = 'attachments' kwargs['anonymous_disallowed'] = self.request.user.is_anonymous and not settings.ANONYMOUS - return super(AttachmentView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class AttachmentHistoryView(ArticleMixin, TemplateView): @@ -79,13 +76,13 @@ def dispatch(self, request, article, attachment_id, *args, **kwargs): self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) else: self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) - return super(AttachmentHistoryView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_context_data(self, **kwargs): kwargs['attachment'] = self.attachment kwargs['revisions'] = self.attachment.attachmentrevision_set.all().order_by('-revision_number') kwargs['selected_tab'] = 'attachments' - return super(AttachmentHistoryView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class AttachmentReplaceView(ArticleMixin, FormView): @@ -101,7 +98,7 @@ def dispatch(self, request, article, attachment_id, *args, **kwargs): self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) else: self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) - return super(AttachmentReplaceView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def form_valid(self, form): @@ -113,13 +110,13 @@ def form_valid(self, form): attachment_revision.save() self.attachment.current_revision = attachment_revision self.attachment.save() - messages.success(self.request, _(u'%s uploaded and replaces old attachment.') % attachment_revision.get_filename()) + messages.success(self.request, _('%s uploaded and replaces old attachment.') % attachment_revision.get_filename()) except models.IllegalFileExtension as e: - messages.error(self.request, _(u'Your file could not be saved: %s') % e) + messages.error(self.request, _('Your file could not be saved: %s') % e) return redirect("wiki:attachments_replace", attachment_id=self.attachment.id, path=self.urlpath.path, article_id=self.article.id) except Exception: - messages.error(self.request, _(u'Your file could not be saved, probably because of a permission error on the web server.')) + messages.error(self.request, _('Your file could not be saved, probably because of a permission error on the web server.')) return redirect("wiki:attachments_replace", attachment_id=self.attachment.id, path=self.urlpath.path, article_id=self.article.id) @@ -127,7 +124,7 @@ def form_valid(self, form): def get_form(self, form_class): form = FormView.get_form(self, form_class) - form.fields['file'].help_text = _(u'Your new file will automatically be renamed to match the file already present. Files with different extensions are not allowed.') + form.fields['file'].help_text = _('Your new file will automatically be renamed to match the file already present. Files with different extensions are not allowed.') return form def get_initial(self, **kwargs): @@ -138,7 +135,7 @@ def get_context_data(self, **kwargs): kwargs['form'] = self.get_form() kwargs['attachment'] = self.attachment kwargs['selected_tab'] = 'attachments' - return super(AttachmentReplaceView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class AttachmentDownloadView(ArticleMixin, View): @@ -153,7 +150,7 @@ def dispatch(self, request, article, attachment_id, *args, **kwargs): self.revision = get_object_or_404(models.AttachmentRevision, id=revision_id, attachment__articles=article) else: self.revision = self.attachment.current_revision - return super(AttachmentDownloadView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get(self, request, *args, **kwargs): if self.revision: @@ -176,12 +173,12 @@ def dispatch(self, request, article, attachment_id, revision_id, *args, **kwargs else: self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) self.revision = get_object_or_404(models.AttachmentRevision, id=revision_id, attachment__articles=article) - return super(AttachmentChangeRevisionView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def post(self, request, *args, **kwargs): self.attachment.current_revision = self.revision self.attachment.save() - messages.success(self.request, _(u'Current revision changed for %s.') % self.attachment.original_filename) + messages.success(self.request, _('Current revision changed for %s.') % self.attachment.original_filename) return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id) @@ -196,13 +193,13 @@ class AttachmentAddView(ArticleMixin, View): @method_decorator(get_article(can_write=True)) def dispatch(self, request, article, attachment_id, *args, **kwargs): self.attachment = get_object_or_404(models.Attachment.objects.active().can_write(request.user), id=attachment_id) - return super(AttachmentAddView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def post(self, request, *args, **kwargs): if self.attachment.articles.filter(id=self.article.id): self.attachment.articles.add(self.article) self.attachment.save() - messages.success(self.request, _(u'Added a reference to "%(att)s" from "%(art)s".') % + messages.success(self.request, _('Added a reference to "%(att)s" from "%(art)s".') % {'att': self.attachment.original_filename, 'art': self.article.current_revision.title}) return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id) @@ -218,7 +215,7 @@ def dispatch(self, request, article, attachment_id, *args, **kwargs): self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article) if not self.attachment.can_delete(request.user): return response_forbidden(request, article, kwargs.get('urlpath', None)) - return super(AttachmentDeleteView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def form_valid(self, form): @@ -232,10 +229,10 @@ def form_valid(self, form): revision.save() self.attachment.current_revision = revision self.attachment.save() - messages.info(self.request, _(u'The file %s was deleted.') % self.attachment.original_filename) + messages.info(self.request, _('The file %s was deleted.') % self.attachment.original_filename) else: self.attachment.articles.remove(self.article) - messages.info(self.request, _(u'This article is no longer related to the file %s.') % self.attachment.original_filename) + messages.info(self.request, _('This article is no longer related to the file %s.') % self.attachment.original_filename) return redirect("wiki:get", path=self.urlpath.path, article_id=self.article.id) @@ -244,7 +241,7 @@ def get_context_data(self, **kwargs): kwargs['selected_tab'] = 'attachments' if 'form' not in kwargs: kwargs['form'] = self.get_form() - return super(AttachmentDeleteView, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class AttachmentSearchView(ArticleMixin, ListView): @@ -256,7 +253,7 @@ class AttachmentSearchView(ArticleMixin, ListView): @method_decorator(get_article(can_write=True)) def dispatch(self, request, article, *args, **kwargs): - return super(AttachmentSearchView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_queryset(self): self.query = self.request.GET.get('query', None) diff --git a/wiki/plugins/attachments/wiki_plugin.py b/wiki/plugins/attachments/wiki_plugin.py index 4a3900590..06a4187f9 100644 --- a/wiki/plugins/attachments/wiki_plugin.py +++ b/wiki/plugins/attachments/wiki_plugin.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.conf.urls import url from django.utils.translation import ugettext_lazy as _ @@ -28,13 +25,13 @@ class AttachmentPlugin(BasePlugin): url(r'^change/(?P\d+)/revision/(?P\d+)/$', views.AttachmentChangeRevisionView.as_view(), name='attachments_revision_change'), ] - article_tab = (_(u'Attachments'), "icon-file") + article_tab = (_('Attachments'), "icon-file") article_view = views.AttachmentView().dispatch # List of notifications to construct signal handlers for. This # is handled inside the notifications plugin. notifications = [{'model': models.AttachmentRevision, - 'message': lambda obj: (_(u"A file was changed: %s") if not obj.deleted else _(u"A file was deleted: %s")) % obj.get_filename(), + 'message': lambda obj: (_("A file was changed: %s") if not obj.deleted else _("A file was deleted: %s")) % obj.get_filename(), 'key': ARTICLE_EDIT, 'created': True, 'get_article': lambda obj: obj.attachment.article} diff --git a/wiki/plugins/help/models.py b/wiki/plugins/help/models.py index dbe627f0e..71a836239 100644 --- a/wiki/plugins/help/models.py +++ b/wiki/plugins/help/models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.db import models # Create your models here. diff --git a/wiki/plugins/help/tests.py b/wiki/plugins/help/tests.py index 1ddecbc38..06eeec547 100644 --- a/wiki/plugins/help/tests.py +++ b/wiki/plugins/help/tests.py @@ -5,7 +5,6 @@ Replace this with more appropriate tests for your application. """ -from __future__ import absolute_import from django.test import TestCase diff --git a/wiki/plugins/help/wiki_plugin.py b/wiki/plugins/help/wiki_plugin.py index b56c79950..5ac4edb5e 100644 --- a/wiki/plugins/help/wiki_plugin.py +++ b/wiki/plugins/help/wiki_plugin.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.utils.translation import ugettext_lazy as _ from wiki.core.plugins import registry diff --git a/wiki/plugins/images/__init__.py b/wiki/plugins/images/__init__.py index 5486d78b8..f2ecac4de 100644 --- a/wiki/plugins/images/__init__.py +++ b/wiki/plugins/images/__init__.py @@ -1,2 +1 @@ -from __future__ import unicode_literals default_app_config = 'wiki.apps.ImagesConfig' diff --git a/wiki/plugins/images/admin.py b/wiki/plugins/images/admin.py index ed6d5a176..1456e4112 100644 --- a/wiki/plugins/images/admin.py +++ b/wiki/plugins/images/admin.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django.contrib import admin from django import forms from wiki.plugins.images import models @@ -11,7 +10,7 @@ class Meta: fields = '__all__' def __init__(self, *args, **kwargs): - super(ImageForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self.instance.pk: revisions = models.ImageRevision.objects.filter(plugin=self.instance) self.fields['current_revision'].queryset = revisions diff --git a/wiki/plugins/images/forms.py b/wiki/plugins/images/forms.py index d10b9c363..b85edbb8c 100644 --- a/wiki/plugins/images/forms.py +++ b/wiki/plugins/images/forms.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django import forms from django.utils.translation import ugettext_lazy as _ @@ -11,21 +10,21 @@ class SidebarForm(forms.ModelForm, PluginSidebarFormMixin): def __init__(self, article, request, *args, **kwargs): self.article = article self.request = request - super(SidebarForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def get_usermessage(self): - return _(u"New image %s was successfully uploaded. You can use it by selecting it from the list of available images.") % self.instance.get_filename() + return _("New image %s was successfully uploaded. You can use it by selecting it from the list of available images.") % self.instance.get_filename() def save(self, *args, **kwargs): if not self.instance.id: image = models.Image() image.article = self.article kwargs['commit'] = False - revision = super(SidebarForm, self).save(*args, **kwargs) + revision = super().save(*args, **kwargs) revision.set_from_request(self.request) image.add_revision(self.instance, save=True) return revision - return super(SidebarForm, self).save(*args, **kwargs) + return super().save(*args, **kwargs) class Meta: model = models.ImageRevision @@ -37,12 +36,12 @@ class RevisionForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.image = kwargs.pop('image') self.request = kwargs.pop('request') - super(RevisionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, *args, **kwargs): if not self.instance.id: kwargs['commit'] = False - revision = super(RevisionForm, self).save(*args, **kwargs) + revision = super().save(*args, **kwargs) revision.inherit_predecessor(self.image, skip_image_file=True) revision.set_from_request(self.request) #revision.save() @@ -57,10 +56,10 @@ class Meta: class PurgeForm(forms.Form): - confirm = forms.BooleanField(label=_(u'Are you sure?'), required=False) + confirm = forms.BooleanField(label=_('Are you sure?'), required=False) def clean_confirm(self): confirm = self.cleaned_data['confirm'] if not confirm: - raise forms.ValidationError(_(u'You are not sure enough!')) + raise forms.ValidationError(_('You are not sure enough!')) return confirm diff --git a/wiki/plugins/images/markdown_extensions.py b/wiki/plugins/images/markdown_extensions.py index 49d310909..8cca18c18 100644 --- a/wiki/plugins/images/markdown_extensions.py +++ b/wiki/plugins/images/markdown_extensions.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import markdown import re diff --git a/wiki/plugins/images/migrations/0001_initial.py b/wiki/plugins/images/migrations/0001_initial.py index 3d948851b..dd2e2ddeb 100644 --- a/wiki/plugins/images/migrations/0001_initial.py +++ b/wiki/plugins/images/migrations/0001_initial.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import from django.db import models, migrations import wiki.plugins.images.models diff --git a/wiki/plugins/images/models.py b/wiki/plugins/images/models.py index 3eefbab45..cbd1df356 100644 --- a/wiki/plugins/images/models.py +++ b/wiki/plugins/images/models.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django.conf import settings as django_settings from django.core.exceptions import ImproperlyConfigured from django.db import models @@ -41,12 +40,12 @@ def can_delete(self, user): return self.can_write(user=user) class Meta: - verbose_name = _(u'image') - verbose_name_plural = _(u'images') + verbose_name = _('image') + verbose_name_plural = _('images') db_table = 'wiki_images_image' def __unicode__(self): - title = (_(u'Image: %s') % self.current_revision.imagerevision.get_filename()) if self.current_revision else _(u'Current revision not set!!') + title = (_('Image: %s') % self.current_revision.imagerevision.get_filename()) if self.current_revision else _('Current revision not set!!') return title @@ -90,14 +89,14 @@ def inherit_predecessor(self, image, skip_image_file=False): self.image = predecessor.image self.width = predecessor.width self.height = predecessor.height - except IOError: + except OSError: self.image = None class Meta: - verbose_name = _(u'image revision') - verbose_name_plural = _(u'image revisions') + verbose_name = _('image revision') + verbose_name_plural = _('image revisions') db_table = 'wiki_images_imagerevision' ordering = ('-created',) def __unicode__(self): - return _(u'Image Revsion: %d') % self.revision_number + return _('Image Revsion: %d') % self.revision_number diff --git a/wiki/plugins/images/settings.py b/wiki/plugins/images/settings.py index 9dac168ee..7bcc27e7b 100644 --- a/wiki/plugins/images/settings.py +++ b/wiki/plugins/images/settings.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django.conf import settings as django_settings # Where to store images diff --git a/wiki/plugins/images/templatetags/wiki_images_tags.py b/wiki/plugins/images/templatetags/wiki_images_tags.py index 74bbbe1e7..dcbfec8cf 100644 --- a/wiki/plugins/images/templatetags/wiki_images_tags.py +++ b/wiki/plugins/images/templatetags/wiki_images_tags.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django import template from wiki.plugins.images import models diff --git a/wiki/plugins/images/views.py b/wiki/plugins/images/views.py index 784994a3b..dd302369e 100644 --- a/wiki/plugins/images/views.py +++ b/wiki/plugins/images/views.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django.contrib import messages from django.urls import reverse from django.shortcuts import get_object_or_404, redirect @@ -24,7 +23,7 @@ class ImageView(ArticleMixin, ListView): @method_decorator(get_article(can_read=True, not_locked=True)) def dispatch(self, request, article, *args, **kwargs): - return super(ImageView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_queryset(self): if (self.article.can_moderate(self.request.user) or @@ -140,7 +139,7 @@ def dispatch(self, request, article, *args, **kwargs): return ArticleMixin.dispatch(self, request, article, *args, **kwargs) def get_form_kwargs(self, **kwargs): - kwargs = super(RevisionAddView, self).get_form_kwargs(**kwargs) + kwargs = super().get_form_kwargs(**kwargs) kwargs['image'] = self.image kwargs['request'] = self.request return kwargs @@ -150,7 +149,7 @@ def get_context_data(self, **kwargs): # with the form instance if 'form' not in kwargs: kwargs['form'] = self.get_form() - kwargs = super(RevisionAddView, self).get_context_data(**kwargs) + kwargs = super().get_context_data(**kwargs) kwargs['image'] = self.image return kwargs diff --git a/wiki/plugins/images/wiki_plugin.py b/wiki/plugins/images/wiki_plugin.py index 4eeaa0613..3873c364a 100644 --- a/wiki/plugins/images/wiki_plugin.py +++ b/wiki/plugins/images/wiki_plugin.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import from django.conf.urls import url from django.utils.translation import ugettext_lazy as _ @@ -24,7 +22,7 @@ class ImagePlugin(BasePlugin): # is handled inside the notifications plugin. notifications = [ {'model': models.ImageRevision, - 'message': lambda obj: _(u"An image was added: %s") % obj.get_filename(), + 'message': lambda obj: _("An image was added: %s") % obj.get_filename(), 'key': ARTICLE_EDIT, 'created': False, 'ignore': lambda revision: bool(revision.previous_revision), # Ignore if there is a previous revision... the image isn't new @@ -43,11 +41,11 @@ class RenderMedia: urlpatterns = [ url('^$', views.ImageView.as_view(), name='images_index'), - url('^delete/(?P\d+)/$', views.DeleteView.as_view(), name='images_delete'), - url('^restore/(?P\d+)/$', views.DeleteView.as_view(), name='images_restore', kwargs={'restore': True}), - url('^purge/(?P\d+)/$', views.PurgeView.as_view(), name='images_purge'), - url('^(?P\d+)/revision/change/(?P\d+)/$', views.RevisionChangeView.as_view(), name='images_restore'), - url('^(?P\d+)/revision/add/$', views.RevisionAddView.as_view(), name='images_add_revision'), + url(r'^delete/(?P\d+)/$', views.DeleteView.as_view(), name='images_delete'), + url(r'^restore/(?P\d+)/$', views.DeleteView.as_view(), name='images_restore', kwargs={'restore': True}), + url(r'^purge/(?P\d+)/$', views.PurgeView.as_view(), name='images_purge'), + url(r'^(?P\d+)/revision/change/(?P\d+)/$', views.RevisionChangeView.as_view(), name='images_restore'), + url(r'^(?P\d+)/revision/add/$', views.RevisionAddView.as_view(), name='images_add_revision'), ] markdown_extensions = [ImageExtension()] diff --git a/wiki/plugins/links/mdx/djangowikilinks.py b/wiki/plugins/links/mdx/djangowikilinks.py index 9316d8e4f..77e8a9d9a 100755 --- a/wiki/plugins/links/mdx/djangowikilinks.py +++ b/wiki/plugins/links/mdx/djangowikilinks.py @@ -20,7 +20,6 @@ ''' -from __future__ import absolute_import import markdown from os import path as os_path @@ -41,16 +40,16 @@ def __init__(self, **kwargs): 'live_lookups': [True, 'If the plugin should try and match links to real articles'], 'default_level': [2, 'The level that most articles are created at. Relative links will tend to start at that level.'] } - + # Override defaults with user settings - super(WikiPathExtension, self).__init__(**kwargs) - + super().__init__(**kwargs) + def extendMarkdown(self, md, md_globals): self.md = md - + # append to end of inline patterns WIKI_RE = r'\[(?P[^\]]+?)\]\(wiki:(?P[a-zA-Z\d\./_-]*)\)' - wikiPathPattern = WikiPath(WIKI_RE, self.config, markdown_instance=md) + wikiPathPattern = WikiPath(WIKI_RE, self.config, md=md) wikiPathPattern.md = md md.inlinePatterns.add('djangowikipath', wikiPathPattern, " 0: urlpath = lookup[0] path = urlpath.get_absolute_url() else: urlpath = None path = self.config['base_url'][0] + path_from_link - + label = m.group('linkTitle') a = etree.Element('a') a.set('href', path) @@ -117,9 +116,9 @@ def handleMatch(self, m) : else: a.set('class', self.config['html_class'][0]) a.text = label - + return a - + def _getMeta(self): """ Return meta data or config data. """ base_url = self.config['base_url'][0] diff --git a/wiki/plugins/links/mdx/urlize.py b/wiki/plugins/links/mdx/urlize.py index a800f283b..68e2b591d 100644 --- a/wiki/plugins/links/mdx/urlize.py +++ b/wiki/plugins/links/mdx/urlize.py @@ -39,7 +39,6 @@ """ -from __future__ import absolute_import import markdown import re @@ -59,8 +58,8 @@ class UrlizePattern(markdown.inlinepatterns.Pattern): def __init__(self, pattern, markdown_instance=None): - markdown.inlinepatterns.Pattern.__init__(self, pattern, markdown_instance=markdown_instance) - self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, + markdown.inlinepatterns.Pattern.__init__(self, pattern, md=markdown_instance) + self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, re.DOTALL | re.UNICODE | re.IGNORECASE) """ Return a link Element given an autolink (`http://example/com`). """ @@ -69,18 +68,18 @@ def handleMatch(self, m): if url.startswith('<'): url = url[1:-1] - + text = url - + if not url.split('://')[0] in ('http','https','ftp'): if '@' in url and not '/' in url: url = 'mailto:' + url else: url = 'http://' + url - + icon = markdown.util.etree.Element("span") icon.set('class', 'icon-globe') - + span_text = markdown.util.etree.Element("span") span_text.text = markdown.util.AtomicString(" " + text) el = markdown.util.etree.Element("a") diff --git a/wiki/plugins/links/models.py b/wiki/plugins/links/models.py index 00539216d..71a836239 100644 --- a/wiki/plugins/links/models.py +++ b/wiki/plugins/links/models.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django.db import models # Create your models here. diff --git a/wiki/plugins/links/tests.py b/wiki/plugins/links/tests.py index e585808ad..501deb776 100644 --- a/wiki/plugins/links/tests.py +++ b/wiki/plugins/links/tests.py @@ -5,7 +5,6 @@ Replace this with more appropriate tests for your application. """ -from __future__ import absolute_import from django.test import TestCase diff --git a/wiki/plugins/links/views.py b/wiki/plugins/links/views.py index c955db7d4..f17578d84 100644 --- a/wiki/plugins/links/views.py +++ b/wiki/plugins/links/views.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from wiki.decorators import json_view, get_article from django.views.generic.base import View from django.utils.decorators import method_decorator diff --git a/wiki/plugins/links/wiki_plugin.py b/wiki/plugins/links/wiki_plugin.py index 3b72db0d2..a3fe719d7 100644 --- a/wiki/plugins/links/wiki_plugin.py +++ b/wiki/plugins/links/wiki_plugin.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import from django.conf.urls import url from django.utils.translation import ugettext_lazy as _ diff --git a/wiki/plugins/notifications/__init__.py b/wiki/plugins/notifications/__init__.py index 17d97fad5..94436a565 100644 --- a/wiki/plugins/notifications/__init__.py +++ b/wiki/plugins/notifications/__init__.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals default_app_config = 'wiki.apps.NotifcationsConfig' # Key for django_notify diff --git a/wiki/plugins/notifications/forms.py b/wiki/plugins/notifications/forms.py index b4121185f..d457aa2b7 100644 --- a/wiki/plugins/notifications/forms.py +++ b/wiki/plugins/notifications/forms.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from django import forms from django.utils.translation import ugettext_lazy as _ @@ -12,12 +11,12 @@ class SubscriptionForm(PluginSettingsFormMixin, forms.Form): - settings_form_headline = _(u'Notifications') + settings_form_headline = _('Notifications') settings_order = 1 settings_write_access = False - edit = forms.BooleanField(required=False, label=_(u'When this article is edited')) - edit_email = forms.BooleanField(required=False, label=_(u'Also receive emails about article edits'), + edit = forms.BooleanField(required=False, label=_('When this article is edited')) + edit_email = forms.BooleanField(required=False, label=_('Also receive emails about article edits'), widget=forms.CheckboxInput(attrs={'onclick': mark_safe("$('#id_edit').attr('checked', $(this).is(':checked'));")})) def __init__(self, article, request, *args, **kwargs): @@ -36,7 +35,7 @@ def __init__(self, article, request, *args, **kwargs): initial = {'edit': bool(self.edit_notifications), 'edit_email': bool(self.edit_notifications.filter(send_emails=True))} kwargs['initial'] = initial - super(SubscriptionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def get_usermessage(self): if self.changed_data: diff --git a/wiki/plugins/notifications/migrations/0001_initial.py b/wiki/plugins/notifications/migrations/0001_initial.py index 9f541c809..70b364467 100644 --- a/wiki/plugins/notifications/migrations/0001_initial.py +++ b/wiki/plugins/notifications/migrations/0001_initial.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import from django.db import models, migrations diff --git a/wiki/plugins/notifications/models.py b/wiki/plugins/notifications/models.py index a9bdca2af..9988f3523 100644 --- a/wiki/plugins/notifications/models.py +++ b/wiki/plugins/notifications/models.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from django.db.models import signals @@ -16,7 +14,7 @@ class ArticleSubscription(ArticlePlugin, Subscription): def __unicode__(self): - return (_(u"%(user)s subscribing to %(article)s (%(type)s)") % + return (_("%(user)s subscribing to %(article)s (%(type)s)") % {'user': self.settings.user.username, 'article': self.article.current_revision.title, 'type': self.notification_type.label}) @@ -39,13 +37,13 @@ def post_article_revision_save(instance, **kwargs): if kwargs.get('created', False): url = default_url(instance.article) if instance.deleted: - notify(_(u'Article deleted: %s') % instance.title, ARTICLE_EDIT, + notify(_('Article deleted: %s') % instance.title, ARTICLE_EDIT, target_object=instance.article, url=url) elif instance.previous_revision: - notify(_(u'Article modified: %s') % instance.title, ARTICLE_EDIT, + notify(_('Article modified: %s') % instance.title, ARTICLE_EDIT, target_object=instance.article, url=url) else: - notify(_(u'New article created: %s') % instance.title, ARTICLE_EDIT, + notify(_('New article created: %s') % instance.title, ARTICLE_EDIT, target_object=instance, url=url) diff --git a/wiki/plugins/notifications/tests.py b/wiki/plugins/notifications/tests.py index e585808ad..501deb776 100644 --- a/wiki/plugins/notifications/tests.py +++ b/wiki/plugins/notifications/tests.py @@ -5,7 +5,6 @@ Replace this with more appropriate tests for your application. """ -from __future__ import absolute_import from django.test import TestCase diff --git a/wiki/plugins/notifications/wiki_plugin.py b/wiki/plugins/notifications/wiki_plugin.py index 5f284059f..e2cf3be5d 100644 --- a/wiki/plugins/notifications/wiki_plugin.py +++ b/wiki/plugins/notifications/wiki_plugin.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from wiki.core.plugins import registry from wiki.core.plugins.base import BasePlugin diff --git a/wiki/templatetags/wiki_tags.py b/wiki/templatetags/wiki_tags.py index 91f1cdf61..54c3a256f 100644 --- a/wiki/templatetags/wiki_tags.py +++ b/wiki/templatetags/wiki_tags.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django import template from django.conf import settings as django_settings from django.contrib.contenttypes.models import ContentType diff --git a/wiki/urls.py b/wiki/urls.py index f0c4183b6..f89331299 100644 --- a/wiki/urls.py +++ b/wiki/urls.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - from django.conf.urls import include, url from wiki.conf import settings diff --git a/wiki/views/accounts.py b/wiki/views/accounts.py index 126e04114..850ccc092 100644 --- a/wiki/views/accounts.py +++ b/wiki/views/accounts.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- """This is nothing but the usual handling of django user accounts, so go ahead and replace it or disable it!""" -from __future__ import absolute_import from django.conf import settings as django_settings from django.contrib import messages @@ -25,14 +23,14 @@ class Signup(CreateView): template_name = "wiki/accounts/signup.html" def get_success_url(self, *args): - messages.success(self.request, _(u'You are now sign up... and now you can sign in!')) + messages.success(self.request, _('You are now sign up... and now you can sign in!')) return reverse("wiki:login") class Logout(View): def get(self, request, *args, **kwargs): auth_logout(request) - messages.info(request, _(u"You are no longer logged in. Bye bye!")) + messages.info(request, _("You are no longer logged in. Bye bye!")) return redirect("wiki:get", URLPath.root().path) class Login(FormView): @@ -42,7 +40,7 @@ class Login(FormView): def get_form_kwargs(self): self.request.session.set_test_cookie() - kwargs = super(Login, self).get_form_kwargs() + kwargs = super().get_form_kwargs() kwargs['request'] = self.request return kwargs @@ -57,7 +55,7 @@ def get(self, request, *args, **kwargs): def form_valid(self, form, *args, **kwargs): auth_login(self.request, form.get_user()) - messages.info(self.request, _(u"You are now logged in! Have fun!")) + messages.info(self.request, _("You are now logged in! Have fun!")) if self.request.GET.get("next", None): return redirect(self.request.GET['next']) if django_settings.LOGIN_REDIRECT_URL: diff --git a/wiki/views/article.py b/wiki/views/article.py index f9b3ba929..711fd4cef 100644 --- a/wiki/views/article.py +++ b/wiki/views/article.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - import difflib from django.contrib import messages @@ -14,7 +11,6 @@ from django.views.generic.base import TemplateView, View from django.views.generic.edit import FormView from django.views.generic.list import ListView -from six.moves import range from wiki import editors, forms, models from wiki.conf import settings @@ -32,7 +28,7 @@ class ArticleView(ArticleMixin, TemplateView): @method_decorator(get_article(can_read=True)) def dispatch(self, request, article, *args, **kwargs): - return super(ArticleView, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_context_data(self, **kwargs): kwargs['selected_tab'] = 'view' @@ -46,7 +42,7 @@ class Create(FormView, ArticleMixin): @method_decorator(get_article(can_write=True)) def dispatch(self, request, article, *args, **kwargs): - return super(Create, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_form(self, form_class=None): """ @@ -88,14 +84,14 @@ def form_valid(self, form): 'other_read': self.article.other_read, 'other_write': self.article.other_write, }) - messages.success(self.request, _(u"New article '%s' created.") % self.newpath.article.current_revision.title) + messages.success(self.request, _("New article '%s' created.") % self.newpath.article.current_revision.title) # TODO: Handle individual exceptions better and give good feedback. except Exception as e: if self.request.user.is_superuser: - messages.error(self.request, _(u"There was an error creating this article: %s") % str(e)) + messages.error(self.request, _("There was an error creating this article: %s") % str(e)) else: - messages.error(self.request, _(u"There was an error creating this article.")) + messages.error(self.request, _("There was an error creating this article.")) return redirect('wiki:get', '') url = self.get_success_url() @@ -147,13 +143,13 @@ def dispatch1(self, request, article, *args, **kwargs): else: self.cannot_delete_root = True - return super(Delete, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_initial(self): return {'revision': self.article.current_revision} def get_form(self, form_class=None): - form = super(Delete, self).get_form(form_class=form_class) + form = super().get_form(form_class=form_class) if self.article.can_moderate(self.request.user): form.fields['purge'].widget = forms.forms.CheckboxInput() return form @@ -176,7 +172,7 @@ def form_valid(self, form): cannot_delete_children = True if self.cannot_delete_root or cannot_delete_children: - messages.error(self.request, _(u'This article cannot be deleted because it has children or is a root article.')) + messages.error(self.request, _('This article cannot be deleted because it has children or is a root article.')) return redirect('wiki:get', article_id=self.article.id) @@ -187,14 +183,14 @@ def form_valid(self, form): else: self.article.delete() - messages.success(self.request, _(u'This article together with all its contents are now completely gone! Thanks!')) + messages.success(self.request, _('This article together with all its contents are now completely gone! Thanks!')) else: revision = models.ArticleRevision() revision.inherit_predecessor(self.article) revision.set_from_request(self.request) revision.deleted = True self.article.add_revision(revision) - messages.success(self.request, _(u'The article "%s" is now marked as deleted! Thanks for keeping the site free from unwanted material!') % revision.title) + messages.success(self.request, _('The article "%s" is now marked as deleted! Thanks for keeping the site free from unwanted material!') % revision.title) return self.get_success_url() def get_success_url(self): @@ -214,7 +210,7 @@ def get_context_data(self, **kwargs): kwargs['delete_children'] = self.children_slice[:20] kwargs['delete_children_more'] = len(self.children_slice) > 20 kwargs['cannot_delete_children'] = cannot_delete_children - return super(Delete, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class Edit(ArticleMixin, FormView): @@ -227,7 +223,7 @@ class Edit(ArticleMixin, FormView): def dispatch(self, request, article, *args, **kwargs): self.sidebar_plugins = plugin_registry.get_sidebar() self.sidebar = [] - return super(Edit, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_form(self, form_class=None): """ @@ -262,7 +258,7 @@ def get(self, request, *args, **kwargs): else: form = None self.sidebar.append((plugin, form)) - return super(Edit, self).get(request, *args, **kwargs) + return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): # Generate sidebar forms @@ -277,7 +273,7 @@ def post(self, request, *args, **kwargs): if usermessage: messages.success(self.request, usermessage) else: - messages.success(self.request, _(u'Your changes were saved.')) + messages.success(self.request, _('Your changes were saved.')) if self.urlpath: return redirect('wiki:edit', path=self.urlpath.path) return redirect('wiki:edit', article_id=self.article.id) @@ -287,7 +283,7 @@ def post(self, request, *args, **kwargs): else: form = None self.sidebar.append((plugin, form)) - return super(Edit, self).post(request, *args, **kwargs) + return super().post(request, *args, **kwargs) def form_valid(self, form): """Create a new article revision when the edit form is valid @@ -300,7 +296,7 @@ def form_valid(self, form): revision.deleted = False revision.set_from_request(self.request) self.article.add_revision(revision) - messages.success(self.request, _(u'A new revision of the article was successfully added.')) + messages.success(self.request, _('A new revision of the article was successfully added.')) return self.get_success_url() def get_success_url(self): @@ -318,7 +314,7 @@ def get_context_data(self, **kwargs): kwargs['editor'] = editors.getEditor() kwargs['selected_tab'] = 'edit' kwargs['sidebar'] = self.sidebar - return super(Edit, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) class Deleted(Delete): @@ -359,13 +355,13 @@ def dispatch(self, request, article, *args, **kwargs): revision.deleted = False revision.automatic_log = _('Restoring article') self.article.add_revision(revision) - messages.success(request, _(u'The article "%s" and its children are now restored.') % revision.title) + messages.success(request, _('The article "%s" and its children are now restored.') % revision.title) if self.urlpath: return redirect('wiki:get', path=self.urlpath.path) else: return redirect('wiki:get', article_id=article.id) - return super(Deleted, self).dispatch1(request, article, *args, **kwargs) + return super().dispatch1(request, article, *args, **kwargs) def get_initial(self): return {'revision': self.article.current_revision, @@ -385,7 +381,7 @@ class Source(ArticleMixin, TemplateView): @method_decorator(get_article(can_read=True)) def dispatch(self, request, article, *args, **kwargs): - return super(Source, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_context_data(self, **kwargs): kwargs['selected_tab'] = 'source' @@ -413,7 +409,7 @@ def get_context_data(self, **kwargs): @method_decorator(get_article(can_read=True)) def dispatch(self, request, article, *args, **kwargs): - return super(History, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) class Dir(ListView, ArticleMixin): @@ -431,7 +427,7 @@ def dispatch(self, request, article, *args, **kwargs): self.query = self.filter_form.cleaned_data['query'] else: self.query = None - return super(Dir, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_queryset(self): children = self.urlpath.get_children().can_read(self.request.user) @@ -478,7 +474,7 @@ class Settings(ArticleMixin, TemplateView): @method_decorator(login_required) @method_decorator(get_article(can_read=True)) def dispatch(self, request, article, *args, **kwargs): - return super(Settings, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def get_form_classes(self,): """ @@ -512,7 +508,7 @@ def post(self, *args, **kwargs): else: form = Form(self.article, self.request) self.forms.append(form) - return super(Settings, self).get(*args, **kwargs) + return super().get(*args, **kwargs) def get(self, *args, **kwargs): self.forms = [] @@ -524,7 +520,7 @@ def get(self, *args, **kwargs): for Form in self.get_form_classes(): self.forms.append(Form(new_article, self.request)) - return super(Settings, self).get(*args, **kwargs) + return super().get(*args, **kwargs) def get_success_url(self): if self.urlpath: @@ -534,7 +530,7 @@ def get_success_url(self): def get_context_data(self, **kwargs): kwargs['selected_tab'] = 'settings' kwargs['forms'] = self.forms - return super(Settings, self).get_context_data(**kwargs) + return super().get_context_data(**kwargs) # TODO: Throw in a class-based view @@ -543,7 +539,7 @@ def change_revision(request, article, revision_id=None, urlpath=None): revision = get_object_or_404(models.ArticleRevision, article=article, id=revision_id) article.current_revision = revision article.save() - messages.success(request, _(u'The article %(title)s is now set to display revision #%(revision_number)d') % { + messages.success(request, _('The article %(title)s is now set to display revision #%(revision_number)d') % { 'title': revision.title, 'revision_number': revision.revision_number, }) @@ -566,7 +562,7 @@ def dispatch(self, request, article, *args, **kwargs): self.revision = get_object_or_404(models.ArticleRevision, article=article, id=revision_id) else: self.revision = None - return super(Preview, self).dispatch(request, article, *args, **kwargs) + return super().dispatch(request, article, *args, **kwargs) def post(self, request, *args, **kwargs): edit_form = forms.EditForm(self.article.current_revision, request.POST, preview=True) @@ -574,14 +570,14 @@ def post(self, request, *args, **kwargs): self.title = edit_form.cleaned_data['title'] self.content = edit_form.cleaned_data['content'] self.preview = True - return super(Preview, self).get(request, *args, **kwargs) + return super().get(request, *args, **kwargs) def get(self, request, *args, **kwargs): if self.revision and not self.title: self.title = self.revision.title if self.revision and not self.content: self.content = self.revision.content - return super(Preview, self).get( request, *args, **kwargs) + return super().get( request, *args, **kwargs) def get_context_data(self, **kwargs): kwargs['title'] = self.title @@ -608,7 +604,7 @@ def diff(request, revision_id, other_revision_id=None): other_changes = [] if not other_revision or other_revision.title != revision.title: - other_changes.append((_(u'New title'), revision.title)) + other_changes.append((_('New title'), revision.title)) return dict(diff=list(diff), other_changes=other_changes) @@ -632,11 +628,11 @@ def merge(request, article, revision_id, urlpath=None, template_file="wiki/previ new_revision.locked = False new_revision.title=article.current_revision.title new_revision.content=content - new_revision.automatic_log = (_(u'Merge between Revision #%(r1)d and Revision #%(r2)d') % + new_revision.automatic_log = (_('Merge between Revision #%(r1)d and Revision #%(r2)d') % {'r1': revision.revision_number, 'r2': old_revision.revision_number}) article.add_revision(new_revision, save=True) - messages.success(request, _(u'A new revision was created: Merge between Revision #%(r1)d and Revision #%(r2)d') % + messages.success(request, _('A new revision was created: Merge between Revision #%(r1)d and Revision #%(r2)d') % {'r1': revision.revision_number, 'r2': old_revision.revision_number}) if urlpath: @@ -674,7 +670,7 @@ def dispatch(self, request, *args, **kwargs): # TODO: This is too dangerous... let's say there is no root.article and we end up here, # then it might cascade to delete a lot of things on an existing installation.... / benjaoming root.delete() - return super(CreateRootView, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) def form_valid(self, form): models.URLPath.create_root( @@ -685,6 +681,6 @@ def form_valid(self, form): return redirect("wiki:root") def get_context_data(self, **kwargs): - data = super(CreateRootView, self).get_context_data(**kwargs) + data = super().get_context_data(**kwargs) data['editor'] = editors.getEditor() return data diff --git a/wiki/views/mixins.py b/wiki/views/mixins.py index 45cd0cfbc..90423a9b9 100644 --- a/wiki/views/mixins.py +++ b/wiki/views/mixins.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.views.generic.base import TemplateResponseMixin from wiki.conf import settings @@ -21,7 +19,7 @@ def dispatch(self, request, article, *args, **kwargs): articles__article__current_revision__deleted=False, user_can_read=request.user): self.children_slice.append(child) - return super(ArticleMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs):