Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
python-version: "3.11"
- uses: pre-commit/action@v2.0.3

tests:
Expand All @@ -25,10 +25,11 @@ jobs:
max-parallel: 5
matrix:
python-version:
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
# - "3.12" # Enable when we get rid of setuptools

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -57,7 +58,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
python-version: "3.11"
- uses: actions/download-artifact@v3
with:
name: coverage-files
Expand Down
28 changes: 14 additions & 14 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
default_language_version:
python: python3.9
python: python3.11
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v4.5.0
hooks:
- id: check-case-conflict
- id: check-merge-conflict
Expand All @@ -11,42 +11,42 @@ repos:
- id: debug-statements
- id: detect-private-key
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.0
rev: v3.15.0
hooks:
- id: pyupgrade
args:
- --py36-plus
- repo: https://github.com/myint/autoflake
rev: v1.4
- --py38-plus
- repo: https://github.com/pycqa/autoflake
rev: v2.2.1
hooks:
- id: autoflake
args:
- --in-place
- --remove-all-unused-imports
- --ignore-init-module-imports
- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/asottile/blacken-docs
rev: v1.12.1
- repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0
hooks:
- id: blacken-docs
additional_dependencies: [black==22.3.0]
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
additional_dependencies: [black==23.9.1]
- repo: https://github.com/pycqa/flake8
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies:
- flake8-bugbear
- flake8-comprehensions
- flake8-tidy-imports
- repo: https://github.com/sirosen/check-jsonschema
rev: 0.11.0
rev: 0.27.0
hooks:
- id: check-github-workflows
# - repo: https://github.com/mgedmin/check-manifest
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.6
FROM python:3.10

# Install system dependencies
RUN apt-get update && apt-get install -y gettext && \
Expand Down
1 change: 1 addition & 0 deletions djedi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def configure():
"cio.plugins.txt.TextPlugin",
"cio.plugins.md.MarkdownPlugin",
"djedi.plugins.img.ImagePlugin",
"djedi.plugins.list.ListPlugin",
],
"THEME": "darth",
}
Expand Down
50 changes: 32 additions & 18 deletions djedi/admin/api.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from collections import defaultdict
from urllib.parse import unquote

from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponse, HttpResponseBadRequest
from django.template.response import TemplateResponse
from django.utils.http import urlunquote
from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View

import cio
from cio.node import Node
from cio.plugins import plugins
from cio.plugins.exceptions import UnknownPlugin
from cio.utils.uri import URI
Expand All @@ -21,7 +24,7 @@


class APIView(View):
@csrf_exempt
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
if not auth.has_permission(request):
raise PermissionDenied
Expand Down Expand Up @@ -64,7 +67,7 @@ def get_post_data(self, request):
return data["data"], data["meta"]

def decode_uri(self, uri):
decoded = urlunquote(uri)
decoded = unquote(uri)

# If uri got decoded then recursive try more times until nothing more can be decoded
if decoded != uri:
Expand All @@ -77,7 +80,7 @@ def render_to_response(self, content=""):


class NodeApi(JSONResponseMixin, APIView):
@never_cache
@method_decorator(never_cache)
def get(self, request, uri):
"""
Return published node or specified version.
Expand Down Expand Up @@ -152,7 +155,7 @@ def get(self, request, uri):


class LoadApi(JSONResponseMixin, APIView):
@never_cache
@method_decorator(never_cache)
def get(self, request, uri):
"""
Load raw node source from storage.
Expand All @@ -173,33 +176,31 @@ def post(self, request, ext):
try:
plugin = plugins.get(ext)
data, meta = self.get_post_data(request)
data = plugin.load(data)
uri = URI(ext=ext)
node = Node(uri=uri, content=data)
data = plugin.load_node(node)
node.content = data
except UnknownPlugin:
raise Http404
else:
content = plugin.render(data)
content = plugin.render_node(node, data)
return self.render_to_response(content)


class NodeEditor(JSONResponseMixin, DjediContextMixin, APIView):
@never_cache
@xframe_options_exempt
@method_decorator(never_cache)
@method_decorator(xframe_options_exempt)
def get(self, request, uri):
try:
uri = self.decode_uri(uri)
uri = URI(uri)
plugin = plugins.resolve(uri)
plugin_context = self.get_context_data(uri=uri)

if isinstance(plugin, DjediPlugin):
plugin_context = plugin.get_editor_context(**plugin_context)
plugin_context = self.get_plugin_context(request, uri)

except UnknownPlugin:
raise Http404
else:
return self.render_plugin(request, plugin_context)

@never_cache
@method_decorator(never_cache)
def post(self, request, uri):
uri = self.decode_uri(uri)
data, meta = self.get_post_data(request)
Expand All @@ -208,17 +209,30 @@ def post(self, request, uri):

context = cio.load(node.uri)
context["content"] = node.content
context.update(self.get_plugin_context(request, context["uri"]))

if request.is_ajax():
# is_ajax call?
if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":
return self.render_to_json(context)
else:
return self.render_plugin(request, context)

def get_plugin_context(self, request, uri):
uri = URI(uri)
plugin = plugins.resolve(uri)

context = {"uri": uri, "plugin": uri.ext}
if isinstance(plugin, DjediPlugin):
context = plugin.get_editor_context(request, **context)

context["uri"] = mark_safe(context["uri"])
return context

def render_plugin(self, request, context):
return TemplateResponse(
request,
[
"djedi/plugins/%s/editor.html" % context["uri"].ext,
"djedi/plugins/%s/editor.html" % context["plugin"],
"djedi/plugins/base/editor.html",
],
self.get_context_data(**context),
Expand Down
7 changes: 3 additions & 4 deletions djedi/admin/cms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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.urls import include, re_path
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import View

Expand All @@ -10,14 +10,13 @@


class Admin(ModelAdmin):

verbose_name = "CMS"
verbose_name_plural = verbose_name

def get_urls(self):
return [
url(r"^", include("djedi.admin.urls", namespace="djedi")),
url(
re_path(r"^", include("djedi.admin.urls", namespace="djedi")),
re_path(
r"", lambda: None, name="djedi_cms_changelist"
), # Placeholder to show change link to CMS in admin
]
Expand Down
3 changes: 3 additions & 0 deletions djedi/admin/mixins.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import simplejson as json
from django.conf import settings as django_settings
from django.http import HttpResponse
from django.utils.safestring import mark_safe

import djedi
from cio.conf import settings
from cio.plugins import plugins

# TODO: Switch simplejson to ujson or other?

Expand Down Expand Up @@ -38,5 +40,6 @@ def get_context_data(self, **context):

context["THEME"] = theme
context["VERSION"] = djedi.__version__
context["PLUGINS"] = mark_safe(json.dumps(list(plugins.plugins.keys())))

return context
20 changes: 11 additions & 9 deletions djedi/admin/urls.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
from django.conf.urls import include, url
from django.urls import include, re_path

from .api import LoadApi, NodeApi, NodeEditor, PublishApi, RenderApi, RevisionsApi
from .cms import DjediCMS

app_name = "djedi"

urlpatterns = [
url(r"^$", DjediCMS.as_view(), name="cms"),
url(r"^node/(?P<uri>.+)/editor$", NodeEditor.as_view(), name="cms.editor"),
url(r"^node/(?P<uri>.+)/load$", LoadApi.as_view(), name="api.load"),
url(r"^node/(?P<uri>.+)/publish$", PublishApi.as_view(), name="api.publish"),
url(r"^node/(?P<uri>.+)/revisions$", RevisionsApi.as_view(), name="api.revisions"),
url(r"^node/(?P<uri>.+)$", NodeApi.as_view(), name="api"),
url(r"^plugin/(?P<ext>\w+)$", RenderApi.as_view(), name="api.render"),
url(r"^api/", include("djedi.rest.urls", namespace="rest")),
re_path(r"^$", DjediCMS.as_view(), name="cms"),
re_path(r"^node/(?P<uri>.+)/editor$", NodeEditor.as_view(), name="cms.editor"),
re_path(r"^node/(?P<uri>.+)/load$", LoadApi.as_view(), name="api.load"),
re_path(r"^node/(?P<uri>.+)/publish$", PublishApi.as_view(), name="api.publish"),
re_path(
r"^node/(?P<uri>.+)/revisions$", RevisionsApi.as_view(), name="api.revisions"
),
re_path(r"^node/(?P<uri>.+)$", NodeApi.as_view(), name="api"),
re_path(r"^plugin/(?P<ext>\w+)$", RenderApi.as_view(), name="api.render"),
re_path(r"^api/", include("djedi.rest.urls", namespace="rest")),
]
7 changes: 7 additions & 0 deletions djedi/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.apps import AppConfig


class DjediConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "djedi"
verbose_name = "Djedi CMS"
4 changes: 2 additions & 2 deletions djedi/backends/django/cache/backend.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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 django.utils.encoding import smart_bytes, smart_str

from cio.backends.base import CacheBackend

Expand Down Expand Up @@ -55,7 +55,7 @@ def _decode_content(self, content):
"""
Split node string to uri and content and convert back to unicode.
"""
content = smart_text(content)
content = smart_str(content)
uri, _, content = content.partition("|")
if content == self.NONE:
content = None
Expand Down
2 changes: 0 additions & 2 deletions djedi/backends/django/db/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@


class DjangoModelStorageBackend(DatabaseBackend):

scheme = "db"

def __init__(self, **config):
Expand All @@ -32,7 +31,6 @@ def get_many(self, uris):

# Assert requested plugin matches
if uri.ext in (None, plugin):

# Assert version matches or node is published
if (uri.version == version) or (is_published and not uri.version):
meta = self._decode_meta(meta, is_published=is_published)
Expand Down
2 changes: 0 additions & 2 deletions djedi/backends/django/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@


class Node(models.Model):

key = models.CharField(max_length=255, db_index=True)
content = models.TextField(blank=True)
plugin = models.CharField(max_length=8)
Expand All @@ -12,5 +11,4 @@ class Node(models.Model):
date_created = models.DateTimeField(auto_now_add=True)

class Meta:
app_label = "djedi"
db_table = "djedi_node"
1 change: 0 additions & 1 deletion djedi/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@


class Migration(migrations.Migration):

dependencies = []

operations = [
Expand Down
1 change: 0 additions & 1 deletion djedi/migrations/0002_auto_20190722_1447.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class Migration(migrations.Migration):

dependencies = [
("djedi", "0001_initial"),
]
Expand Down
Loading