diff --git a/.dockerignore b/.dockerignore
index 2db1e98f..b0f77342 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -6,7 +6,6 @@
.gitignore
.next/
.stuff/
-.travis.yml
.venv/
Dockerfile
dist/
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..d73cefbe
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,73 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+
+jobs:
+ linting:
+ name: Linting
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ with:
+ python-version: 3.9
+ - uses: pre-commit/action@v2.0.3
+
+ tests:
+ name: Python ${{ matrix.python-version }}
+ runs-on: ubuntu-latest
+
+ strategy:
+ max-parallel: 5
+ matrix:
+ python-version:
+ - "3.6"
+ - "3.7"
+ - "3.8"
+ - "3.9"
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Upgrade packaging tools
+ run: python -m pip install --upgrade pip setuptools virtualenv
+ - name: Install dependencies
+ run: python -m pip install --upgrade tox
+ - name: Run tox targets for ${{ matrix.python-version }}
+ run: |
+ ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}")
+ TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox
+ - uses: actions/upload-artifact@master
+ with:
+ name: coverage-files
+ path: ./.coverage.*
+
+ coverage:
+ name: Reporting Coverage
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize')
+ needs: [tests]
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ with:
+ python-version: 3.9
+ - uses: actions/download-artifact@v3
+ with:
+ name: coverage-files
+ path: .
+ - name: Combine coverage reports
+ run: |
+ python -m pip install coverage
+ make coverage-lcov
+ - name: Upload coverage report
+ uses: coverallsapp/github-action@1.1.3
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ path-to-lcov: "./coverage.info"
diff --git a/.gitignore b/.gitignore
index 302584e6..9480ba9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
*.egg
*.egg-info
*.eggs/
-.coverage
+.coverage*
.idea
.DS_Store
.tox
@@ -22,4 +22,4 @@ example/db.sqlite3
*~
\#*\#
.#*.*
-*_flymake*
\ No newline at end of file
+*_flymake*
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..9bebd9f4
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,81 @@
+default_language_version:
+ python: python3.9
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.1.0
+ hooks:
+ - id: check-case-conflict
+ - id: check-merge-conflict
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - id: debug-statements
+ - id: detect-private-key
+ - repo: https://github.com/asottile/pyupgrade
+ rev: v2.31.0
+ hooks:
+ - id: pyupgrade
+ args:
+ - --py36-plus
+ - repo: https://github.com/myint/autoflake
+ rev: v1.4
+ hooks:
+ - id: autoflake
+ args:
+ - --in-place
+ - --remove-all-unused-imports
+ - --ignore-init-module-imports
+ - repo: https://github.com/pycqa/isort
+ rev: 5.10.1
+ hooks:
+ - id: isort
+ - repo: https://github.com/psf/black
+ rev: 22.3.0
+ hooks:
+ - id: black
+ - repo: https://github.com/asottile/blacken-docs
+ rev: v1.12.1
+ hooks:
+ - id: blacken-docs
+ additional_dependencies: [black==22.3.0]
+ - repo: https://gitlab.com/pycqa/flake8
+ rev: 3.9.2
+ hooks:
+ - id: flake8
+ additional_dependencies:
+ - flake8-bugbear
+ - flake8-comprehensions
+ - flake8-tidy-imports
+ - repo: https://github.com/sirosen/check-jsonschema
+ rev: 0.11.0
+ hooks:
+ - id: check-github-workflows
+# - repo: https://github.com/mgedmin/check-manifest
+# rev: "0.47"
+# hooks:
+# - id: check-manifest
+# # See https://github.com/mgedmin/check-manifest/issues/141
+# args: ["--no-build-isolation"]
+# Unsure how to solve this failing
+
+exclude: |
+ (?x)(
+ /(
+ \.eggs
+ | \.git
+ | \.hg
+ | \.mypy_cache
+ | \.pytest_cache
+ | \.nox
+ | \.tox
+ | \.venv
+ | _build
+ | buck-out
+ | build
+ | dist
+ )/
+ | ^(
+ example
+ | scripts
+ | tests/project/files
+ )/
+ )
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 22105dcb..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,93 +0,0 @@
-language: python
-sudo: false
-
-python: 3.5
-
-addons:
- apt_packages:
- - sqlite3
-
-env:
- # NOTE: On Travis web page define private STASH_URL, STASH_TOKEN, STASH_SECRET.
- global:
- - STASH_BOX=$TRAVIS_REPO_SLUG:$TRAVIS_BUILD_NUMBER:$STASH_SECRET
- matrix:
- - TOXENV=py27-django15
- - TOXENV=py27-django16
- - TOXENV=py27-django17
- - TOXENV=py27-django18
- - TOXENV=py27-django19
- - TOXENV=py27-django110
- - TOXENV=py27-django111
-
- - TOXENV=py35-django18
- - TOXENV=py35-django19
- - TOXENV=py35-django110
- - TOXENV=py35-django111
- - TOXENV=py35-django20
- - TOXENV=py35-django21
-
- - TOXENV=lint
- - TOXENV=coverage
-
-matrix:
- include:
- - env: TOXENV=py35-django22
- python: 3.5
- sudo: true
- dist: xenial
-
- - env: TOXENV=py36-django111
- python: 3.6
- sudo: true
- dist: xenial
- - env: TOXENV=py36-django20
- python: 3.6
- sudo: true
- dist: xenial
- - env: TOXENV=py36-django21
- python: 3.6
- sudo: true
- dist: xenial
- - env: TOXENV=py36-django22
- python: 3.6
- sudo: true
- dist: xenial
-
- - env: TOXENV=py37-django111
- python: 3.7
- sudo: true
- dist: xenial
- - env: TOXENV=py37-django20
- python: 3.7
- sudo: true
- dist: xenial
- - env: TOXENV=py37-django21
- python: 3.7
- sudo: true
- dist: xenial
- - env: TOXENV=py37-django22
- python: 3.7
- sudo: true
- dist: xenial
-
-install:
- - pip install --upgrade pip
- - pip install tox stasher
-
-script:
- - if [[ $TOXENV =~ coverage ]] && [ -n "$STASH_SECRET" ]; then
- stash pull $STASH_BOX -wc $(TOXENV= tox -l | wc -l);
- fi
- - if [[ ! $TOXENV =~ coverage ]] || [ -n "$STASH_SECRET" ]; then
- tox;
- fi
- - if [[ $TOXENV =~ py ]] && [ -n "$STASH_SECRET" ]; then
- stash push $STASH_BOX $(ls .coverage.*);
- fi
-
-after_success:
- - if [[ $TOXENV =~ coverage ]]; then
- pip install --quiet python-coveralls;
- coveralls --ignore-errors;
- fi
diff --git a/Makefile b/Makefile
index e8f72e44..a2824ad0 100644
--- a/Makefile
+++ b/Makefile
@@ -18,9 +18,14 @@ coverage:
coverage combine || true
coverage report
+.PHONY: coverage-lcov
+coverage-lcov:
+ coverage combine || true
+ coverage lcov -o coverage.info
+
.PHONY: lint
lint:
- flake8 djedi
+ pre-commit run --all-files
.PHONY: install
install:
diff --git a/README.md b/README.md
index 96cc0d0d..79ee8abb 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Django content management as it should be.
-[](https://travis-ci.org/5monkeys/djedi-cms)
+[](https://github.com/5monkeys/djedi-cms/actions)
[](https://coveralls.io/github/5monkeys/djedi-cms?branch=master)
[](https://pypi.python.org/pypi/djedi-cms/)
[](https://pypi.python.org/pypi/djedi-cms/)
@@ -28,11 +28,11 @@ Example settings for Django 2.0:
INSTALLED_APPS = (
# ...
- 'djedi',
+ "djedi",
)
MIDDLEWARE = [
- 'djedi.middleware.translation.DjediTranslationMiddleware',
+ "djedi.middleware.translation.DjediTranslationMiddleware",
# ...
]
```
@@ -49,7 +49,7 @@ $ django-admin.py migrate djedi
# urls.py
urlpatterns = [
- path('admin/', admin.site.urls),
+ path("admin/", admin.site.urls),
]
```
diff --git a/djedi-react/.dockerignore b/djedi-react/.dockerignore
index 318b0633..53681a1b 100644
--- a/djedi-react/.dockerignore
+++ b/djedi-react/.dockerignore
@@ -6,7 +6,6 @@
.gitignore
.next/
.stuff/
-.travis.yml
Dockerfile
dist/
docker-compose.yml
diff --git a/djedi-react/.npm-upgrade.json b/djedi-react/.npm-upgrade.json
index ce8e0899..5e944594 100644
--- a/djedi-react/.npm-upgrade.json
+++ b/djedi-react/.npm-upgrade.json
@@ -9,4 +9,4 @@
"reason": "Not compatible with newer versions"
}
}
-}
\ No newline at end of file
+}
diff --git a/djedi-react/test/Node.test.js b/djedi-react/test/Node.test.js
index 633ce081..903b194a 100644
--- a/djedi-react/test/Node.test.js
+++ b/djedi-react/test/Node.test.js
@@ -319,7 +319,7 @@ test("it handles nodes with null value in response", async () => {
-
+
`);
});
@@ -773,7 +773,7 @@ Network error",
-
+
diff --git a/djedi/__init__.py b/djedi/__init__.py
index 073c180f..81c35d34 100644
--- a/djedi/__init__.py
+++ b/djedi/__init__.py
@@ -1,5 +1,4 @@
-# coding=utf-8
-VERSION = (1, 3, 3, 'final', 0)
+VERSION = (1, 3, 3, "final", 0)
def get_version(version=None):
@@ -7,7 +6,7 @@ def get_version(version=None):
if version is None:
version = VERSION
assert len(version) == 5
- assert version[3] in ('alpha', 'beta', 'rc', 'final')
+ assert version[3] in ("alpha", "beta", "rc", "final")
# Now build the two parts of the version number:
# main = X.Y[.Z]
@@ -15,11 +14,11 @@ def get_version(version=None):
# | {a|b|c}N - for alpha, beta and rc releases
parts = 2 if version[2] == 0 else 3
- main = '.'.join(str(x) for x in version[:parts])
+ main = ".".join(str(x) for x in version[:parts])
- sub = ''
- if version[3] != 'final':
- mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
+ sub = ""
+ if version[3] != "final":
+ mapping = {"alpha": "a", "beta": "b", "rc": "c"}
sub = mapping[version[3]] + str(version[4])
return main + sub
@@ -30,33 +29,44 @@ def get_version(version=None):
def configure():
from django.conf import settings as django_settings
+
from cio.conf import settings
# Djedi default config
- config = dict(
- ENVIRONMENT={
- 'default': {
- 'i18n': django_settings.LANGUAGE_CODE,
- 'l10n': getattr(django_settings, 'ROOT_URLCONF', 'local').split('.', 1)[0],
- 'g11n': 'global'
+ config = {
+ "ENVIRONMENT": {
+ "default": {
+ "i18n": django_settings.LANGUAGE_CODE,
+ "l10n": getattr(django_settings, "ROOT_URLCONF", "local").split(".", 1)[
+ 0
+ ],
+ "g11n": "global",
}
},
- CACHE='djedi.backends.django.cache.Backend',
- STORAGE='djedi.backends.django.db.Backend',
- PLUGINS=[
- 'cio.plugins.txt.TextPlugin',
- 'cio.plugins.md.MarkdownPlugin',
- 'djedi.plugins.img.ImagePlugin'
+ "CACHE": "djedi.backends.django.cache.Backend",
+ "STORAGE": "djedi.backends.django.db.Backend",
+ "PLUGINS": [
+ "cio.plugins.txt.TextPlugin",
+ "cio.plugins.md.MarkdownPlugin",
+ "djedi.plugins.img.ImagePlugin",
],
- THEME='darth'
- )
+ "THEME": "darth",
+ }
# Update config with global djedi django settings
- config.update(getattr(django_settings, 'DJEDI', {}))
+ config.update(getattr(django_settings, "DJEDI", {}))
# Overwrite config with prefixed variables from django settings
- for setting in ('ENVIRONMENT', 'CACHE', 'STORAGE', 'PIPELINE', 'PLUGINS', 'THEME', 'XSS_DOMAIN'):
- conf = getattr(django_settings, 'DJEDI_%s' % setting, None)
+ for setting in (
+ "ENVIRONMENT",
+ "CACHE",
+ "STORAGE",
+ "PIPELINE",
+ "PLUGINS",
+ "THEME",
+ "XSS_DOMAIN",
+ ):
+ conf = getattr(django_settings, "DJEDI_%s" % setting, None)
if conf is not None:
config[setting] = conf
diff --git a/djedi/admin/__init__.py b/djedi/admin/__init__.py
index c259f3a9..094c2b2a 100644
--- a/djedi/admin/__init__.py
+++ b/djedi/admin/__init__.py
@@ -1,23 +1,32 @@
from django.contrib import admin
from django.db.models import Model
from django.template.defaultfilters import pluralize
+
from djedi.admin import cms
def register(admin_class):
name = admin_class.verbose_name
- name_plural = getattr(admin_class, 'verbose_name_plural', pluralize(name))
+ name_plural = getattr(admin_class, "verbose_name_plural", pluralize(name))
- model = type(name, (Model,), {
- '__module__': __name__,
- 'Meta': type('Meta', (object,), dict(
- managed=False,
- abstract=True,
- app_label='djedi',
- verbose_name=name,
- verbose_name_plural=name_plural
- ))
- })
+ model = type(
+ name,
+ (Model,),
+ {
+ "__module__": __name__,
+ "Meta": type(
+ "Meta",
+ (object,),
+ {
+ "managed": False,
+ "abstract": True,
+ "app_label": "djedi",
+ "verbose_name": name,
+ "verbose_name_plural": name_plural,
+ },
+ ),
+ },
+ )
admin.site._registry[model] = admin_class(model, admin.site)
diff --git a/djedi/admin/api.py b/djedi/admin/api.py
index 223853fb..e15f4735 100644
--- a/djedi/admin/api.py
+++ b/djedi/admin/api.py
@@ -1,8 +1,8 @@
from collections import defaultdict
-from djedi.plugins.base import DjediPlugin
from django.core.exceptions import PermissionDenied
-from django.http import HttpResponse, Http404, HttpResponseBadRequest
+from django.http import Http404, HttpResponse, HttpResponseBadRequest
+from django.template.response import TemplateResponse
from django.utils.http import urlunquote
from django.views.decorators.cache import never_cache
from django.views.decorators.clickjacking import xframe_options_exempt
@@ -14,21 +14,20 @@
from cio.plugins.exceptions import UnknownPlugin
from cio.utils.uri import URI
-from .exceptions import InvalidNodeData
-from .mixins import JSONResponseMixin, DjediContextMixin
-from ..compat import TemplateResponse
from .. import auth
+from ..plugins.base import DjediPlugin
+from .exceptions import InvalidNodeData
+from .mixins import DjediContextMixin, JSONResponseMixin
class APIView(View):
-
@csrf_exempt
def dispatch(self, request, *args, **kwargs):
if not auth.has_permission(request):
raise PermissionDenied
try:
- return super(APIView, self).dispatch(request, *args, **kwargs)
+ return super().dispatch(request, *args, **kwargs)
except Http404:
raise
except Exception as e:
@@ -50,17 +49,19 @@ def get_post_data(self, request):
if isinstance(value, list) and len(value) <= 1:
value = value[0] if value else None
- prefix, _, field = param.partition('[')
+ prefix, _, field = param.partition("[")
if field:
field = field[:-1]
try:
data[prefix][field] = value
except TypeError:
- raise InvalidNodeData('Got both reserved parameter "data" and plugin specific parameters.')
+ raise InvalidNodeData(
+ 'Got both reserved parameter "data" and plugin specific parameters.'
+ )
else:
data[prefix] = value
- return data['data'], data['meta']
+ return data["data"], data["meta"]
def decode_uri(self, uri):
decoded = urlunquote(uri)
@@ -71,12 +72,11 @@ def decode_uri(self, uri):
return decoded
- def render_to_response(self, content=u''):
+ def render_to_response(self, content=""):
return HttpResponse(content)
class NodeApi(JSONResponseMixin, APIView):
-
@never_cache
def get(self, request, uri):
"""
@@ -91,10 +91,7 @@ def get(self, request, uri):
if node.content is None:
raise Http404
- return self.render_to_json({
- 'uri': node.uri,
- 'content': node.content
- })
+ return self.render_to_json({"uri": node.uri, "content": node.content})
def post(self, request, uri):
"""
@@ -105,7 +102,7 @@ def post(self, request, uri):
"""
uri = self.decode_uri(uri)
data, meta = self.get_post_data(request)
- meta['author'] = auth.get_username(request)
+ meta["author"] = auth.get_username(request)
node = cio.set(uri, data, publish=False, **meta)
return self.render_to_json(node)
@@ -123,7 +120,6 @@ def delete(self, request, uri):
class PublishApi(JSONResponseMixin, APIView):
-
def put(self, request, uri):
"""
Publish versioned uri.
@@ -141,7 +137,6 @@ def put(self, request, uri):
class RevisionsApi(JSONResponseMixin, APIView):
-
def get(self, request, uri):
"""
List uri revisions.
@@ -151,12 +146,12 @@ def get(self, request, uri):
"""
uri = self.decode_uri(uri)
revisions = cio.revisions(uri)
- revisions = [list(revision) for revision in revisions] # Convert tuples to lists
+ # Convert tuples to lists
+ revisions = [list(revision) for revision in revisions]
return self.render_to_json(revisions)
class LoadApi(JSONResponseMixin, APIView):
-
@never_cache
def get(self, request, uri):
"""
@@ -171,7 +166,6 @@ def get(self, request, uri):
class RenderApi(APIView):
-
def post(self, request, ext):
"""
Render data for plugin and return text response.
@@ -188,7 +182,6 @@ def post(self, request, ext):
class NodeEditor(JSONResponseMixin, DjediContextMixin, APIView):
-
@never_cache
@xframe_options_exempt
def get(self, request, uri):
@@ -210,11 +203,11 @@ def get(self, request, uri):
def post(self, request, uri):
uri = self.decode_uri(uri)
data, meta = self.get_post_data(request)
- meta['author'] = auth.get_username(request)
+ meta["author"] = auth.get_username(request)
node = cio.set(uri, data, publish=False, **meta)
context = cio.load(node.uri)
- context['content'] = node.content
+ context["content"] = node.content
if request.is_ajax():
return self.render_to_json(context)
@@ -222,7 +215,12 @@ def post(self, request, uri):
return self.render_plugin(request, context)
def render_plugin(self, request, context):
- return TemplateResponse(request, [
- 'djedi/plugins/%s/editor.html' % context['uri'].ext,
- 'djedi/plugins/base/editor.html'
- ], self.get_context_data(**context))
+ return TemplateResponse(
+ request,
+ [
+ "djedi/plugins/%s/editor.html" % context["uri"].ext,
+ "djedi/plugins/base/editor.html",
+ ],
+ self.get_context_data(**context),
+ using="django",
+ )
diff --git a/djedi/admin/cms.py b/djedi/admin/cms.py
index 61749164..6fdb01b6 100644
--- a/djedi/admin/cms.py
+++ b/djedi/admin/cms.py
@@ -1,23 +1,26 @@
+from django.conf.urls import include, url
from django.contrib.admin import ModelAdmin
from django.core.exceptions import PermissionDenied
+from django.shortcuts import render
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import View
from ..auth import has_permission
-from ..compat import include, patterns, render, url
from .mixins import DjediContextMixin
class Admin(ModelAdmin):
- verbose_name = 'CMS'
+ verbose_name = "CMS"
verbose_name_plural = verbose_name
def get_urls(self):
- return patterns(
- url(r'^', include('djedi.admin.urls', namespace='djedi')),
- url(r'', lambda: None, name='djedi_cms_changelist') # Placeholder to show change link to CMS in admin
- )
+ return [
+ url(r"^", include("djedi.admin.urls", namespace="djedi")),
+ url(
+ r"", lambda: None, name="djedi_cms_changelist"
+ ), # Placeholder to show change link to CMS in admin
+ ]
def has_change_permission(self, request, obj=None):
return has_permission(request)
@@ -35,10 +38,11 @@ def has_module_permission(self, request):
class DjediCMS(DjediContextMixin, View):
-
@xframe_options_exempt
def get(self, request):
if has_permission(request):
- return render(request, 'djedi/cms/cms.html', self.get_context_data())
+ return render(
+ request, "djedi/cms/cms.html", self.get_context_data(), using="django"
+ )
else:
raise PermissionDenied
diff --git a/djedi/admin/mixins.py b/djedi/admin/mixins.py
index e10a4cfb..71c0af05 100644
--- a/djedi/admin/mixins.py
+++ b/djedi/admin/mixins.py
@@ -1,26 +1,27 @@
import simplejson as json
from django.conf import settings as django_settings
from django.http import HttpResponse
-from cio.conf import settings
+
import djedi
+from cio.conf import settings
# TODO: Switch simplejson to ujson or other?
-class JSONResponseMixin(object):
+class JSONResponseMixin:
"""
A mixin that can be used to render a JSON response.
"""
+
response_class = HttpResponse
def render_to_json(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
- response_kwargs['content_type'] = 'application/json'
+ response_kwargs["content_type"] = "application/json"
return self.response_class(
- self.convert_context_to_json(context),
- **response_kwargs
+ self.convert_context_to_json(context), **response_kwargs
)
def convert_context_to_json(self, context):
@@ -28,15 +29,14 @@ def convert_context_to_json(self, context):
return json.dumps(context, indent=4, for_json=True)
-class DjediContextMixin(object):
-
+class DjediContextMixin:
def get_context_data(self, **context):
theme = settings.THEME
- if '/' not in theme:
- theme = '{static}djedi/themes/{theme}/theme.css'.format(static=django_settings.STATIC_URL, theme=theme)
+ if "/" not in theme:
+ theme = f"{django_settings.STATIC_URL}djedi/themes/{theme}/theme.css"
- context['THEME'] = theme
- context['VERSION'] = djedi.__version__
+ context["THEME"] = theme
+ context["VERSION"] = djedi.__version__
return context
diff --git a/djedi/admin/urls.py b/djedi/admin/urls.py
index eac7dfd1..229b954a 100644
--- a/djedi/admin/urls.py
+++ b/djedi/admin/urls.py
@@ -1,16 +1,17 @@
-from ..compat import include, patterns, url
+from django.conf.urls import include, url
+
from .api import LoadApi, NodeApi, NodeEditor, PublishApi, RenderApi, RevisionsApi
from .cms import DjediCMS
-app_name = 'djedi'
+app_name = "djedi"
-urlpatterns = patterns(
- url(r'^$', DjediCMS.as_view(), name='cms'),
- url(r'^node/(?P.+)/editor$', NodeEditor.as_view(), name='cms.editor'),
- url(r'^node/(?P.+)/load$', LoadApi.as_view(), name='api.load'),
- url(r'^node/(?P.+)/publish$', PublishApi.as_view(), name='api.publish'),
- url(r'^node/(?P.+)/revisions$', RevisionsApi.as_view(), name='api.revisions'),
- url(r'^node/(?P.+)$', NodeApi.as_view(), name='api'),
- url(r'^plugin/(?P\w+)$', RenderApi.as_view(), name='api.render'),
- url(r'^api/', include('djedi.rest.urls', namespace='rest'))
-)
+urlpatterns = [
+ url(r"^$", DjediCMS.as_view(), name="cms"),
+ url(r"^node/(?P.+)/editor$", NodeEditor.as_view(), name="cms.editor"),
+ url(r"^node/(?P.+)/load$", LoadApi.as_view(), name="api.load"),
+ url(r"^node/(?P.+)/publish$", PublishApi.as_view(), name="api.publish"),
+ url(r"^node/(?P.+)/revisions$", RevisionsApi.as_view(), name="api.revisions"),
+ url(r"^node/(?P.+)$", NodeApi.as_view(), name="api"),
+ url(r"^plugin/(?P\w+)$", RenderApi.as_view(), name="api.render"),
+ url(r"^api/", include("djedi.rest.urls", namespace="rest")),
+]
diff --git a/djedi/auth/__init__.py b/djedi/auth/__init__.py
index 74576b2b..726dc97e 100644
--- a/djedi/auth/__init__.py
+++ b/djedi/auth/__init__.py
@@ -4,23 +4,25 @@
def has_permission(request):
- user = getattr(request, 'user', None)
+ user = getattr(request, "user", None)
if user:
if user.is_superuser:
return True
- if user.is_staff and user.groups.filter(name__iexact='djedi').exists():
+ if user.is_staff and user.groups.filter(name__iexact="djedi").exists():
return True
else:
- _log.warning("Request does not have `user` attribute. Make sure that "
- "Djedi middleware is used after AuthenticationMiddleware")
+ _log.warning(
+ "Request does not have `user` attribute. Make sure that "
+ "Djedi middleware is used after AuthenticationMiddleware"
+ )
return False
def get_username(request):
user = request.user
- if hasattr(user, 'get_username'):
+ if hasattr(user, "get_username"):
return user.get_username()
else:
return user.username
diff --git a/djedi/backends/django/cache/backend.py b/djedi/backends/django/cache/backend.py
index 22a7208b..1d9fea3a 100644
--- a/djedi/backends/django/cache/backend.py
+++ b/djedi/backends/django/cache/backend.py
@@ -1,23 +1,20 @@
-import six
-from django.core.cache import InvalidCacheBackendError
-from djedi.utils.encoding import smart_str, smart_unicode
-from cio.backends.base import CacheBackend
+from django.core.cache import InvalidCacheBackendError, caches
from django.core.cache.backends.locmem import LocMemCache
+from django.utils.encoding import smart_bytes, smart_text
-from djedi.compat import get_cache
+from cio.backends.base import CacheBackend
class DjangoCacheBackend(CacheBackend):
-
def __init__(self, **config):
"""
Get cache backend. Look for djedi specific cache first, then fallback on default
"""
- super(DjangoCacheBackend, self).__init__(**config)
+ super().__init__(**config)
try:
- cache_name = self.config.get('NAME', 'djedi')
- cache = get_cache(cache_name)
+ cache_name = self.config.get("NAME", "djedi")
+ cache = caches[cache_name]
except (InvalidCacheBackendError, ValueError):
from django.core.cache import cache
@@ -33,10 +30,12 @@ def _get_many(self, keys):
return self._cache.get_many(keys)
def _set(self, key, value):
- self._cache.set(key, value, timeout=None) # TODO: Fix eternal timeout like viewlet
+ # TODO: Fix eternal timeout like viewlet
+ self._cache.set(key, value, timeout=None)
def _set_many(self, data):
- self._cache.set_many(data, timeout=None) # TODO: Fix eternal timeout like viewlet
+ # TODO: Fix eternal timeout like viewlet
+ self._cache.set_many(data, timeout=None)
def _delete(self, key):
self._cache.delete(key)
@@ -50,31 +49,30 @@ def _encode_content(self, uri, content):
"""
if content is None:
content = self.NONE
- return smart_str('|'.join([six.text_type(uri), content]))
+ return smart_bytes("|".join([str(uri), content]))
def _decode_content(self, content):
"""
Split node string to uri and content and convert back to unicode.
"""
- content = smart_unicode(content)
- uri, _, content = content.partition(u'|')
+ content = smart_text(content)
+ uri, _, content = content.partition("|")
if content == self.NONE:
content = None
return uri or None, content
class DebugLocMemCache(LocMemCache):
-
def __init__(self, *args, **kwargs):
self.calls = 0
self.hits = 0
self.misses = 0
self.sets = 0
- super(DebugLocMemCache, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def get(self, key, default=None, version=None, **kwargs):
- result = super(DebugLocMemCache, self).get(key, default=default, version=version)
- if kwargs.get('count', True):
+ result = super().get(key, default=default, version=version)
+ if kwargs.get("count", True):
self.calls += 1
if result is None:
self.misses += 1
@@ -91,16 +89,16 @@ def get_many(self, keys, version=None):
hits = len(d)
self.calls += 1
self.hits += hits
- self.misses += (len(keys) - hits)
+ self.misses += len(keys) - hits
return d
def set(self, *args, **kwargs):
- super(DebugLocMemCache, self).set(*args, **kwargs)
+ super().set(*args, **kwargs)
self.calls += 1
self.sets += 1
def set_many(self, data, *args, **kwargs):
- result = super(DebugLocMemCache, self).set_many(data, *args, **kwargs)
+ result = super().set_many(data, *args, **kwargs)
self.calls -= len(data) # Remove calls from set()
self.calls += 1
return result
diff --git a/djedi/backends/django/db/backend.py b/djedi/backends/django/db/backend.py
index 85c77f1b..52e3a837 100644
--- a/djedi/backends/django/db/backend.py
+++ b/djedi/backends/django/db/backend.py
@@ -1,6 +1,7 @@
import logging
from django.db import IntegrityError
+
from cio.backends.base import DatabaseBackend
from cio.backends.exceptions import NodeDoesNotExist, PersistenceError
@@ -12,15 +13,17 @@
class DjangoModelStorageBackend(DatabaseBackend):
- scheme = 'db'
+ scheme = "db"
def __init__(self, **config):
- super(DjangoModelStorageBackend, self).__init__(**config)
+ super().__init__(**config)
def get_many(self, uris):
- storage_keys = dict((self._build_key(uri), uri) for uri in uris)
+ storage_keys = {self._build_key(uri): uri for uri in uris}
stored_nodes = Node.objects.filter(key__in=storage_keys.keys())
- stored_nodes = stored_nodes.values_list('key', 'content', 'plugin', 'version', 'is_published', 'meta')
+ stored_nodes = stored_nodes.values_list(
+ "key", "content", "plugin", "version", "is_published", "meta"
+ )
# Filter matching nodes
nodes = {}
@@ -34,9 +37,9 @@ def get_many(self, uris):
if (uri.version == version) or (is_published and not uri.version):
meta = self._decode_meta(meta, is_published=is_published)
nodes[uri] = {
- 'uri': uri.clone(ext=plugin, version=version),
- 'content': content,
- 'meta': meta
+ "uri": uri.clone(ext=plugin, version=version),
+ "content": content,
+ "meta": meta,
}
return nodes
@@ -47,7 +50,9 @@ def publish(self, uri, **meta):
if not node.is_published:
# Assign version number
if not node.version.isdigit():
- revisions = Node.objects.filter(key=node.key).values_list('version', flat=True)
+ revisions = Node.objects.filter(key=node.key).values_list(
+ "version", flat=True
+ )
version = self._get_next_version(revisions)
node.version = version
@@ -63,9 +68,12 @@ def publish(self, uri, **meta):
def get_revisions(self, uri):
key = self._build_key(uri)
- nodes = Node.objects.filter(key=key).order_by('date_created')
- revisions = nodes.values_list('plugin', 'version', 'is_published')
- return [(key.clone(ext=plugin, version=version), is_published) for plugin, version, is_published in revisions]
+ nodes = Node.objects.filter(key=key).order_by("date_created")
+ revisions = nodes.values_list("plugin", "version", "is_published")
+ return [
+ (key.clone(ext=plugin, version=version), is_published)
+ for plugin, version, is_published in revisions
+ ]
def _get(self, uri):
key = self._build_key(uri)
@@ -92,10 +100,10 @@ def _create(self, uri, content, **meta):
plugin=uri.ext,
version=uri.version,
is_published=False,
- meta=meta
+ meta=meta,
)
except IntegrityError as e:
- raise PersistenceError('Failed to create node for uri "%s"; %s' % (uri, e))
+ raise PersistenceError(f'Failed to create node for uri "{uri}"; {e}')
def _update(self, uri, content, **meta):
node = self._get(uri)
@@ -115,9 +123,9 @@ def _delete(self, node):
def _serialize(self, uri, node):
meta = self._decode_meta(node.meta, is_published=node.is_published)
return {
- 'uri': uri.clone(ext=node.plugin, version=node.version),
- 'content': node.content,
- 'meta': meta
+ "uri": uri.clone(ext=node.plugin, version=node.version),
+ "content": node.content,
+ "meta": meta,
}
def _update_meta(self, node, meta):
diff --git a/djedi/backends/django/db/models.py b/djedi/backends/django/db/models.py
index 4541776a..63cfe7cb 100644
--- a/djedi/backends/django/db/models.py
+++ b/djedi/backends/django/db/models.py
@@ -12,5 +12,5 @@ class Node(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
- app_label = u'djedi'
- db_table = 'djedi_node'
+ app_label = "djedi"
+ db_table = "djedi_node"
diff --git a/djedi/compat.py b/djedi/compat.py
deleted file mode 100644
index 8f38c43e..00000000
--- a/djedi/compat.py
+++ /dev/null
@@ -1,111 +0,0 @@
-import django
-
-from functools import partial
-from sys import version_info
-
-from django.shortcuts import render
-from django.template.loader import render_to_string
-from django.template.response import TemplateResponse as BaseTemplateResponse
-
-
-if django.VERSION < (1, 6):
- from django.conf.urls.defaults import include, url
-else:
- from django.conf.urls import include, url
-
-
-if django.VERSION < (2, 0):
- from django.core.urlresolvers import reverse, NoReverseMatch
-else:
- from django.urls import reverse, NoReverseMatch
-
-
-def patterns(*urls):
- if django.VERSION < (1, 6):
- from django.conf.urls.defaults import patterns
- return patterns('', *urls)
- elif django.VERSION < (1, 10):
- from django.conf.urls import patterns
- return patterns('', *urls)
- return list(urls)
-
-
-if django.VERSION >= (1, 9):
- if django.VERSION < (2, 0):
- from django.template.library import parse_bits
- else:
- def parse_bits(parser, bits, params, varargs, varkw, defaults,
- takes_context, name):
- from django.template import library
- return library.parse_bits(
- parser=parser, bits=bits, params=params, varargs=varargs,
- varkw=varkw, defaults=defaults, kwonly=(), kwonly_defaults=(),
- takes_context=takes_context, name=name)
-
- def generic_tag_compiler(parser, token, params, varargs, varkw, defaults,
- name, takes_context, node_class):
- """
- Returns a template.Node subclass.
-
- This got inlined into django.template.library since Django since 1.9, this here
- is a copypasta replacement:
- https://github.com/django/django/blob/stable/1.8.x/django/template/base.py#L1089
- """
- bits = token.split_contents()[1:]
- args, kwargs = parse_bits(parser, bits, params, varargs, varkw,
- defaults, takes_context, name)
- return node_class(takes_context, args, kwargs)
-else:
- from django.template.base import parse_bits
- from django.template.base import generic_tag_compiler # noqa
-
-
-if django.VERSION >= (1, 8):
- # Always use the Django template engine on Django 1.8.
- render_to_string = partial(render_to_string, using='django')
- render = partial(render, using='django')
-
- class TemplateResponse(BaseTemplateResponse):
- def __init__(self, *args, **kwargs):
- kwargs['using'] = 'django'
- super(TemplateResponse, self).__init__(*args, **kwargs)
-else:
- TemplateResponse = BaseTemplateResponse
-
-
-if django.VERSION < (1, 9):
- from django.core.cache import get_cache
-else:
- from django.core.cache import caches
-
- def get_cache(name):
- return caches[name]
-
-
-if version_info < (3,):
- from inspect import getargspec
-else:
- from collections import namedtuple
- from inspect import getfullargspec
-
- ArgSpec = namedtuple('ArgSpec', ['args', 'varargs', 'keywords', 'defaults'])
-
- def getargspec(func):
- spec = getfullargspec(func)
- return ArgSpec(
- args=spec.args,
- varargs=spec.varargs,
- keywords=spec.varkw,
- defaults=spec.defaults,
- )
-
-__all__ = ['render_to_string',
- 'render',
- 'patterns',
- 'include',
- 'url',
- 'reverse',
- 'NoReverseMatch',
- 'generic_tag_compiler',
- 'parse_bits',
- 'get_cache']
diff --git a/djedi/middleware/__init__.py b/djedi/middleware/__init__.py
index 8b6d5e28..43ad010e 100644
--- a/djedi/middleware/__init__.py
+++ b/djedi/middleware/__init__.py
@@ -2,8 +2,7 @@
from cio.pipeline import pipeline
-class DjediMiddleware(object):
-
+class DjediMiddleware:
def __init__(self, get_response=None):
self.get_response = get_response
@@ -12,8 +11,7 @@ def __call__(self, request):
if not response:
try:
response = self.get_response(request)
- response = self.process_response(request=request,
- response=response)
+ response = self.process_response(request=request, response=response)
except Exception as e:
self.process_exception(request=request, exception=e)
raise
diff --git a/djedi/middleware/admin.py b/djedi/middleware/admin.py
index fbcfa488..f74e2363 100644
--- a/djedi/middleware/admin.py
+++ b/djedi/middleware/admin.py
@@ -3,7 +3,6 @@
class DjediAdminMiddleware(DjediMiddleware, AdminPanelMixin):
-
def process_response(self, request, response):
self.inject_admin_panel(request, response)
- return super(DjediAdminMiddleware, self).process_response(request, response)
+ return super().process_response(request, response)
diff --git a/djedi/middleware/mixins.py b/djedi/middleware/mixins.py
index 6dfb3738..833d8edb 100644
--- a/djedi/middleware/mixins.py
+++ b/djedi/middleware/mixins.py
@@ -1,71 +1,70 @@
import logging
-import cio
-
from django.core.exceptions import ImproperlyConfigured
+from django.urls import NoReverseMatch, reverse
from django.utils import translation
+import cio
from cio.conf import settings
from cio.pipeline import pipeline
from djedi.auth import has_permission
-from djedi.compat import reverse, NoReverseMatch
from djedi.utils.templates import render_embed
_log = logging.getLogger(__name__)
-class TranslationMixin(object):
-
+class TranslationMixin:
def activate_language(self):
# Activate current django translation
language = translation.get_language()
cio.env.push_state(i18n=language)
-class AdminPanelMixin(object):
-
+class AdminPanelMixin:
def inject_admin_panel(self, request, response):
# Do not inject admin panel on gzipped responses
- if 'gzip' in response.get('Content-Encoding', ''):
- _log.debug('gzip detected, not injecting panel.')
+ if "gzip" in response.get("Content-Encoding", ""):
+ _log.debug("gzip detected, not injecting panel.")
return
# Only inject admin panel in html pages
- content_type = response.get('Content-Type', '').split(';')[0]
- if content_type not in ('text/html', 'application/xhtml+xml'):
- _log.debug('Non-HTML Content-Type detected, not injecting')
+ content_type = response.get("Content-Type", "").split(";")[0]
+ if content_type not in ("text/html", "application/xhtml+xml"):
+ _log.debug("Non-HTML Content-Type detected, not injecting")
return
# Do not inject admin panel in admin
try:
- admin_prefix = reverse('admin:index')
+ admin_prefix = reverse("admin:index")
except NoReverseMatch:
_log.debug(
'No reverse match for "admin:index", can\'t detect '
- 'django-admin pages'
+ "django-admin pages"
)
else:
if request.path.startswith(admin_prefix):
_log.debug(
- 'admin page detected, not injecting panel. admin_prefix=%r',
- admin_prefix
+ "admin page detected, not injecting panel. admin_prefix=%r",
+ admin_prefix,
)
return
try:
- djedi_cms_url = reverse('admin:djedi:cms')
+ djedi_cms_url = reverse("admin:djedi:cms")
except NoReverseMatch:
- raise ImproperlyConfigured('Could not find djedi in your url conf, '
- 'enable django admin or include '
- 'djedi.urls within the admin namespace.')
+ raise ImproperlyConfigured(
+ "Could not find djedi in your url conf, "
+ "enable django admin or include "
+ "djedi.urls within the admin namespace."
+ )
else:
if request.path.startswith(djedi_cms_url):
- _log.debug('djedi page detected, not injecting panel')
+ _log.debug("djedi page detected, not injecting panel")
return
# Validate user permissions
if not has_permission(request):
- _log.debug('insufficient permissions, not injecting.')
+ _log.debug("insufficient permissions, not injecting.")
return
embed = self.render_cms()
@@ -82,20 +81,20 @@ def get_requested_uri(node):
)
return uri
- defaults = dict(
- (get_requested_uri(node), node.initial)
- for node in pipeline.history.list('get')
- )
+ defaults = {
+ get_requested_uri(node): node.initial
+ for node in pipeline.history.list("get")
+ }
return render_embed(nodes=defaults)
def body_append(self, response, html):
- idx = response.content.lower().rfind(b'