diff --git a/cms/__init__.py b/cms/__init__.py
index d1c3568..6172cbf 100644
--- a/cms/__init__.py
+++ b/cms/__init__.py
@@ -1,2 +1,2 @@
-__version__ = "1.0.5"
+__version__ = "1.0.6"
default_app_config = "cms.apps.CmsConfig"
diff --git a/cms/__main__.py b/cms/__main__.py
index 3052380..15af440 100644
--- a/cms/__main__.py
+++ b/cms/__main__.py
@@ -1,58 +1,85 @@
-import os, re, argparse, shutil, example, cms
+import argparse
+import os
+import re
+import shutil
+
from pip._internal.operations import freeze as pip
+import cms
+import example
+
def main():
- parser = argparse.ArgumentParser(description='SimpleCMS')
- parser.add_argument('project_name', nargs='?', default='.')
+ parser = argparse.ArgumentParser(description="SimpleCMS")
+ parser.add_argument("project_name", nargs="?", default=".")
project_name = parser.parse_args().project_name
- if project_name == '.':
+ if project_name == ".":
project_dir = os.getcwd()
project_name = os.path.basename(project_dir)
else:
project_dir = os.path.join(os.getcwd(), project_name)
- if re.match(r'^\w+$', project_name):
- if input(f'Do you want to create a new project in `{project_dir}` ?\033[1D') in 'Yy':
+ if re.match(r"^\w+$", project_name):
+ if (
+ input(f"Do you want to create a new project in `{project_dir}` ?\033[1D")
+ in "Yy"
+ ):
create_project(project_name, project_dir)
else:
- print(f'Invalid project name: {project_name}')
+ print(f"Invalid project name: {project_name}")
def create_project(project_name, project_dir):
os.makedirs(project_dir, exist_ok=True)
- with open(os.path.join(project_dir, 'requirements.txt'), 'w') as f:
+ with open(os.path.join(project_dir, "requirements.txt"), "w") as f:
for line in pip.freeze():
- if 'django_simplecms' in line:
- line = f'django-simplecms=={cms.__version__}'
+ if "django_simplecms" in line:
+ line = f"django-simplecms=={cms.__version__}"
print(line, file=f)
- shutil.copytree(os.path.dirname(example.__file__),os.path.join(project_dir, project_name), dirs_exist_ok=True)
- with open(os.open(os.path.join(project_dir, 'manage.py'), os.O_CREAT|os.O_WRONLY, 0o755), 'w') as f:
- print('#!/usr/bin/env python',
- 'import os, sys',
- f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
- 'from django.core.management import execute_from_command_line',
- 'execute_from_command_line(sys.argv)',
- sep='\n', file=f)
- with open(os.path.join(project_dir, project_name, 'wsgi.py'), 'w') as f:
- print('import os',
- 'from django.core.wsgi import get_wsgi_application',
- f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
- 'application = get_wsgi_application()',
- sep='\n', file=f)
- with open(os.path.join(project_dir, '.gitignore'), 'w') as f:
- print('*.pyc\n__pycache__/', file=f)
+ shutil.copytree(
+ os.path.dirname(example.__file__),
+ os.path.join(project_dir, project_name),
+ dirs_exist_ok=True,
+ )
+ with open(
+ os.open(
+ os.path.join(project_dir, "manage.py"), os.O_CREAT | os.O_WRONLY, 0o755
+ ),
+ "w",
+ ) as f:
+ print(
+ "#!/usr/bin/env python",
+ "import os, sys",
+ f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
+ "from django.core.management import execute_from_command_line",
+ "execute_from_command_line(sys.argv)",
+ sep="\n",
+ file=f,
+ )
+ with open(os.path.join(project_dir, project_name, "wsgi.py"), "w") as f:
+ print(
+ "import os",
+ "from django.core.wsgi import get_wsgi_application",
+ f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
+ "application = get_wsgi_application()",
+ sep="\n",
+ file=f,
+ )
+ with open(os.path.join(project_dir, ".gitignore"), "w") as f:
+ print("*.pyc\n__pycache__/", file=f)
- print(f'Successfully created project "{project_name}"',
- '',
- 'Things to do next:',
- '- create a database',
- '- ./manage.py makemigrations',
- '- ./manage.py migrate',
- '- ./manage.py createsuperuser',
- '- ./manage.py runserver',
- sep='\n')
+ print(
+ f'Successfully created project "{project_name}"',
+ "",
+ "Things to do next:",
+ "- create a database",
+ "- ./manage.py makemigrations",
+ "- ./manage.py migrate",
+ "- ./manage.py createsuperuser",
+ "- ./manage.py runserver",
+ sep="\n",
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/cms/apps.py b/cms/apps.py
index a7614d9..9a96878 100644
--- a/cms/apps.py
+++ b/cms/apps.py
@@ -1,10 +1,11 @@
from django.apps import AppConfig
-from django.utils.translation import gettext_lazy as _
from django.utils.module_loading import autodiscover_modules
+from django.utils.translation import gettext_lazy as _
+
class CmsConfig(AppConfig):
- name = 'cms'
- verbose_name = _('Content Management System')
+ name = "cms"
+ verbose_name = _("Content Management System")
def ready(self):
- autodiscover_modules('views')
+ autodiscover_modules("views")
diff --git a/cms/decorators.py b/cms/decorators.py
index 590e4ca..d787f2c 100644
--- a/cms/decorators.py
+++ b/cms/decorators.py
@@ -1,17 +1,20 @@
from cms import registry
+
def page_model(cls):
- '''Decorator to register the Page model'''
+ """Decorator to register the Page model"""
registry.page_class = cls
return cls
+
def section_model(cls):
- '''Decorator to register the Section model'''
+ """Decorator to register the Section model"""
registry.section_class = cls
return cls
+
def section_view(cls):
- '''Decorator to register a view for a specific section'''
+ """Decorator to register a view for a specific section"""
registry.view_per_type[cls.__name__.lower()] = cls
registry.section_types.append((cls.__name__.lower(), cls.verbose_name))
return cls
diff --git a/cms/forms.py b/cms/forms.py
index 8d40608..00a7503 100644
--- a/cms/forms.py
+++ b/cms/forms.py
@@ -1,27 +1,28 @@
from django import forms
from django.conf import settings
-from django.db.models import Prefetch
-from django.contrib.contenttypes.models import ContentType
from django.core.mail import EmailMessage
from django.utils.translation import gettext_lazy as _
from . import registry
+
class PageForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.label_suffix = ''
+ self.label_suffix = ""
- self.formsets = [forms.inlineformset_factory(
- parent_model=registry.page_class,
- model=registry.section_class,
- form=SectionForm,
- extra=1,
- )(
- data=self.data if self.is_bound else None,
- files=self.files if self.is_bound else None,
- instance=self.instance,
- )]
+ self.formsets = [
+ forms.inlineformset_factory(
+ parent_model=registry.page_class,
+ model=registry.section_class,
+ form=SectionForm,
+ extra=1,
+ )(
+ data=self.data if self.is_bound else None,
+ files=self.files if self.is_bound else None,
+ instance=self.instance,
+ )
+ ]
self.formsets[0][0].empty_permitted = True
def is_valid(self):
@@ -44,17 +45,18 @@ class PageForm(forms.ModelForm):
class Meta:
model = registry.page_class
- fields = '__all__'
+ fields = "__all__"
+
class SectionForm(forms.ModelForm):
type = forms.ChoiceField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.label_suffix = ''
- self.fields['DELETE'] = forms.BooleanField(label=_('Delete'), required=False)
- self.fields['type'].choices = registry.get_types()
- self.fields['type'].initial = registry.get_types()[0][0]
+ self.label_suffix = ""
+ self.fields["DELETE"] = forms.BooleanField(label=_("Delete"), required=False)
+ self.fields["type"].choices = registry.get_types()
+ self.fields["type"].initial = registry.get_types()[0][0]
# Populate the 'formsets' attribute if the Section was
# extendend with one_to_many fields
@@ -64,14 +66,14 @@ class SectionForm(forms.ModelForm):
formset = forms.inlineformset_factory(
parent_model=registry.section_class,
model=field.related_model,
- fields='__all__',
+ fields="__all__",
extra=1,
)(
instance=self.instance,
data=self.data if self.is_bound else None,
files=self.files if self.is_bound else None,
- prefix=f'{self.prefix}-{field.name}',
- form_kwargs={'label_suffix': self.label_suffix},
+ prefix=f"{self.prefix}-{field.name}",
+ form_kwargs={"label_suffix": self.label_suffix},
)
formset.name = field.name
self.formsets.append(formset)
@@ -79,19 +81,19 @@ class SectionForm(forms.ModelForm):
def is_valid(self):
result = super().is_valid()
for formset in self.formsets:
- result = result and formset.is_valid() # AND
+ result = result and formset.is_valid() # AND
return result
def has_changed(self):
result = super().has_changed()
for formset in self.formsets:
- result = result or formset.has_changed() # OR
+ result = result or formset.has_changed() # OR
return result
def save(self, commit=True):
section = super().save(commit=commit)
- if self.cleaned_data['DELETE']:
+ if self.cleaned_data["DELETE"]:
section.delete()
if section.page.slug and not section.page.sections.exists():
section.page.delete()
@@ -106,25 +108,28 @@ class SectionForm(forms.ModelForm):
class Meta:
model = registry.section_class
- exclude = ['page']
+ exclude = ["page"]
+
class ContactForm(forms.Form):
- sender = forms.EmailField(label=_('Your email address'))
- spam_protection = forms.CharField(label=_('Your message'), widget=forms.Textarea())
- message = forms.CharField(label=_('Your message'), widget=forms.Textarea(), initial='Hi there!')
+ sender = forms.EmailField(label=_("Your email address"))
+ spam_protection = forms.CharField(label=_("Your message"), widget=forms.Textarea())
+ message = forms.CharField(
+ label=_("Your message"), widget=forms.Textarea(), initial="Hi there!"
+ )
def save(self):
- body = self.cleaned_data.get('spam_protection')
+ body = self.cleaned_data.get("spam_protection")
if len(body.split()) < 7:
return
- spamcheck = self.cleaned_data.get('message')
- if spamcheck != 'Hi there!':
+ spamcheck = self.cleaned_data.get("message")
+ if spamcheck != "Hi there!":
return
email = EmailMessage(
- to = settings.DEFAULT_TO_EMAIL,
- body = body,
- subject = _('Contact form'),
- headers = {'Reply-To': self.cleaned_data.get('sender')},
+ to=settings.DEFAULT_TO_EMAIL,
+ body=body,
+ subject=_("Contact form"),
+ headers={"Reply-To": self.cleaned_data.get("sender")},
)
email.send()
diff --git a/cms/middleware.py b/cms/middleware.py
index 912a7e2..e294962 100644
--- a/cms/middleware.py
+++ b/cms/middleware.py
@@ -1,7 +1,9 @@
import os
-from sass import compile
+
from django.conf import settings
from django.middleware import cache
+from sass import compile
+
def locate(filename):
for path, dirs, files in os.walk(os.getcwd(), followlinks=True):
@@ -9,38 +11,44 @@ def locate(filename):
if f == filename:
yield os.path.join(path, filename)
+
class FetchFromCacheMiddleware(cache.FetchFromCacheMiddleware):
- '''Minor change to the original middleware that prevents caching of
+ """Minor change to the original middleware that prevents caching of
requests that have a `sessionid` cookie. This should be the
Django default, IMHO.
- '''
+ """
+
def process_request(self, request):
- if 'sessionid' not in request.COOKIES:
+ if "sessionid" not in request.COOKIES:
return super().process_request(request)
+
class SassMiddleware:
- '''Simple SASS middleware that intercepts requests for .css files and
+ """Simple SASS middleware that intercepts requests for .css files and
tries to compile the corresponding SCSS file.
- '''
+ """
+
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
- if settings.DEBUG and request.path.endswith('.css'):
- css_file = request.path.rsplit('/',1)[1]
+ if settings.DEBUG and request.path.endswith(".css"):
+ css_file = request.path.rsplit("/", 1)[1]
sass_file = css_file[:-4]
sass_paths = locate(sass_file)
for sass_path in list(sass_paths):
if os.path.exists(sass_path):
- css_path = sass_path + '.css'
- map_path = css_path + '.map'
- css = compile(filename=sass_path, output_style='nested')
- css, mapping = compile(filename=sass_path, source_map_filename=map_path)
- with open(css_path, 'w') as f:
+ css_path = sass_path + ".css"
+ map_path = css_path + ".map"
+ css = compile(filename=sass_path, output_style="nested")
+ css, mapping = compile(
+ filename=sass_path, source_map_filename=map_path
+ )
+ with open(css_path, "w") as f:
f.write(css)
- with open(map_path, 'w') as f:
+ with open(map_path, "w") as f:
f.write(mapping)
response = self.get_response(request)
diff --git a/cms/models.py b/cms/models.py
index f645d09..d63c0b3 100644
--- a/cms/models.py
+++ b/cms/models.py
@@ -10,6 +10,8 @@ from . import fields, mixins
class Model(models.Model):
"""Felt cute, might delete later."""
+ id = models.BigAutoField(primary_key=True)
+
class Meta:
abstract = True
diff --git a/cms/registry.py b/cms/registry.py
index a6826d6..b0814fd 100644
--- a/cms/registry.py
+++ b/cms/registry.py
@@ -3,15 +3,17 @@ section_class = None
section_types = []
view_per_type = {}
+
def get_types():
return section_types
+
def get_view(section, request):
return view_per_type[section.type](request)
+
def get_fields_per_type():
fields_per_type = {}
for name, view in view_per_type.items():
- fields_per_type[name] = ['title', 'type', 'number'] + view.fields
+ fields_per_type[name] = ["title", "type", "number"] + view.fields
return fields_per_type
-
diff --git a/cms/templatetags/cms.py b/cms/templatetags/cms.py
index 9212337..708f7ea 100644
--- a/cms/templatetags/cms.py
+++ b/cms/templatetags/cms.py
@@ -1,18 +1,17 @@
-from markdown import markdown as md
-
from django import template
from django.shortcuts import reverse
from django.utils.safestring import mark_safe
-from django.utils.translation import gettext_lazy as _
+from markdown import markdown as md
from cms import registry
-MARKDOWN_EXTENSIONS = ['extra', 'smarty']
+MARKDOWN_EXTENSIONS = ["extra", "smarty"]
register = template.Library()
+
@register.simple_tag(takes_context=True)
def eval(context, expr):
- '''USE WITH CAUTION!!!
+ """USE WITH CAUTION!!!
This template tag runs its argument through Django's templating
system using the current context, placing all power into the
@@ -20,49 +19,61 @@ def eval(context, expr):
Also, it applies Markdown.
- '''
+ """
result = template.Template(expr).render(context)
return mark_safe(md(result, extensions=MARKDOWN_EXTENSIONS))
+
@register.simple_tag(takes_context=True)
def editsection(context, inner):
- '''Renders a simple link to edit the current section'''
- section = context['section']
- user = context['request'].user
+ """Renders a simple link to edit the current section"""
+ section = context["section"]
+ user = context["request"].user
app_label = section._meta.app_label
model_name = section._meta.model_name
- if user.has_perm(f'{app_label}.change_{model_name}'):
+ if user.has_perm(f"{app_label}.change_{model_name}"):
slug = section.page.slug
number = section.number
- url = reverse('cms:updatesection', args=[slug, number]) if slug else reverse('cms:updatesection', args=[number])
+ url = (
+ reverse("cms:updatesection", args=[slug, number])
+ if slug
+ else reverse("cms:updatesection", args=[number])
+ )
return mark_safe(f'{inner}')
- return ''
+ return ""
+
@register.simple_tag(takes_context=True)
def editpage(context, inner):
- '''Renders a simple link to edit the current page'''
- page = context['page']
- user = context['request'].user
+ """Renders a simple link to edit the current page"""
+ page = context["page"]
+ user = context["request"].user
app_label = page._meta.app_label
model_name = page._meta.model_name
- if user.has_perm(f'{app_label}.change_{model_name}'):
+ if user.has_perm(f"{app_label}.change_{model_name}"):
slug = page.slug
- url = reverse('cms:updatepage', args=[slug]) if slug else reverse('cms:updatepage')
+ url = (
+ reverse("cms:updatepage", args=[slug])
+ if slug
+ else reverse("cms:updatepage")
+ )
return mark_safe(f'{inner}')
- return ''
+ return ""
-@register.tag('include_section')
+
+@register.tag("include_section")
def do_include(parser, token):
- '''Renders the section with its own context'''
+ """Renders the section with its own context"""
_, section = token.split_contents()
return IncludeSectionNode(section)
+
class IncludeSectionNode(template.Node):
def __init__(self, section):
self.section = template.Variable(section)
- self.csrf_token = template.Variable('csrf_token')
- self.request = template.Variable('request')
- self.perms = template.Variable('perms')
+ self.csrf_token = template.Variable("csrf_token")
+ self.request = template.Variable("request")
+ self.perms = template.Variable("perms")
super().__init__()
def render(self, context):
@@ -73,13 +84,13 @@ class IncludeSectionNode(template.Node):
view = registry.get_view(section, request)
initial_context = {
- 'csrf_token': csrf_token,
- 'section': section,
- 'request': request,
- 'perms': perms,
+ "csrf_token": csrf_token,
+ "section": section,
+ "request": request,
+ "perms": perms,
}
- if hasattr(section, 'invalid_form'):
- initial_context['form'] = section.invalid_form
+ if hasattr(section, "invalid_form"):
+ initial_context["form"] = section.invalid_form
section_context = view.get_context_data(**initial_context)
t = context.template.engine.get_template(view.template_name)
diff --git a/cms/urls.py b/cms/urls.py
index 4113786..c2a0930 100644
--- a/cms/urls.py
+++ b/cms/urls.py
@@ -1,15 +1,23 @@
from django.urls import path
-from .views import PageView, CreatePage, UpdatePage, CreateSection, UpdateSection
-app_name = 'cms'
+from .views import CreatePage, CreateSection, PageView, UpdatePage, UpdateSection
+
+app_name = "cms"
urlpatterns = [
- path('new/', CreatePage.as_view(), name='createpage'),
- path('edit/', UpdatePage.as_view(), kwargs={'slug': ''}, name='updatepage'),
- path('edit//', UpdateSection.as_view(), kwargs={'slug': ''}, name='updatesection'),
- path('/edit/', UpdatePage.as_view(), name='updatepage'),
- path('/edit/new/', CreateSection.as_view(), name='createsection'),
- path('/edit//', UpdateSection.as_view(), name='updatesection'),
- path('', PageView.as_view(), name='page'),
- path('/', PageView.as_view(), name='page'),
+ path("new/", CreatePage.as_view(), name="createpage"),
+ path("edit/", UpdatePage.as_view(), kwargs={"slug": ""}, name="updatepage"),
+ path(
+ "edit//",
+ UpdateSection.as_view(),
+ kwargs={"slug": ""},
+ name="updatesection",
+ ),
+ path("/edit/", UpdatePage.as_view(), name="updatepage"),
+ path("/edit/new/", CreateSection.as_view(), name="createsection"),
+ path(
+ "/edit//", UpdateSection.as_view(), name="updatesection"
+ ),
+ path("", PageView.as_view(), name="page"),
+ path("/", PageView.as_view(), name="page"),
]
diff --git a/cms/views.py b/cms/views.py
index b2068c8..b60b58e 100644
--- a/cms/views.py
+++ b/cms/views.py
@@ -1,18 +1,20 @@
import json
+from django.contrib.auth.mixins import UserPassesTestMixin
+from django.http import Http404, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import redirect
-from django.views.generic import base, detail, edit
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
-from django.contrib.auth.mixins import UserPassesTestMixin
-from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
+from django.views.generic import base, detail, edit
from . import registry
from .forms import PageForm, SectionForm
+
class SectionView:
- '''Generic section view'''
- template_name = 'cms/sections/section.html'
+ """Generic section view"""
+
+ template_name = "cms/sections/section.html"
def __init__(self, request):
self.request = request
@@ -20,14 +22,16 @@ class SectionView:
def get_context_data(self, **kwargs):
return kwargs
+
class SectionFormView(SectionView):
- '''Generic section with associated form'''
+ """Generic section with associated form"""
+
form_class = None
success_url = None
def get_context_data(self, **kwargs):
- if 'form' not in kwargs:
- kwargs['form'] = self.get_form()
+ if "form" not in kwargs:
+ kwargs["form"] = self.get_form()
return kwargs
def form_valid(self, form):
@@ -37,56 +41,62 @@ class SectionFormView(SectionView):
def get_form_kwargs(self):
return {}
- def get_form(self, method='get'):
+ def get_form(self, method="get"):
form_class = self.form_class
kwargs = self.get_form_kwargs()
- if method == 'post':
- kwargs.update({
- 'data': self.request.POST,
- 'files': self.request.FILES,
- })
+ if method == "post":
+ kwargs.update(
+ {
+ "data": self.request.POST,
+ "files": self.request.FILES,
+ }
+ )
return form_class(**kwargs)
-class PageView(detail.DetailView):
- '''View of a page with heterogeneous sections'''
- model = registry.page_class
- template_name = 'cms/page.html'
- def setup(self, *args, slug='', **kwargs):
- '''Supply a default argument for slug'''
+class PageView(detail.DetailView):
+ """View of a page with heterogeneous sections"""
+
+ model = registry.page_class
+ template_name = "cms/page.html"
+
+ def setup(self, *args, slug="", **kwargs):
+ """Supply a default argument for slug"""
super().setup(*args, slug=slug, **kwargs)
def get(self, request, *args, **kwargs):
- '''Instantiate section views and render final response'''
+ """Instantiate section views and render final response"""
try:
page = self.object = self.get_object()
except Http404:
app_label = registry.page_class._meta.app_label
model_name = registry.page_class._meta.model_name
- if self.kwargs['slug'] == '':
- page = registry.page_class(title='Homepage', slug='')
+ if self.kwargs["slug"] == "":
+ page = registry.page_class(title="Homepage", slug="")
page.save()
self.object = page
# Special case: Don't serve 404 pages to authorized users,
# but redirect to the edit page form with the slug pre-filled
- elif (self.request.user.has_perm(f'{app_label}.change_{model_name}')):
- return redirect('cms:updatepage', slug=self.kwargs['slug'])
+ elif self.request.user.has_perm(f"{app_label}.change_{model_name}"):
+ return redirect("cms:updatepage", slug=self.kwargs["slug"])
else:
raise
context = self.get_context_data(**kwargs)
sections = page.sections.all()
- context.update({
- 'page': page,
- 'sections': sections,
- })
+ context.update(
+ {
+ "page": page,
+ "sections": sections,
+ }
+ )
return self.render_to_response(context)
def post(self, request, **kwargs):
- '''Call the post() method of the correct section view'''
+ """Call the post() method of the correct section view"""
try:
- pk = int(self.request.POST.get('section'))
- except:
+ pk = int(self.request.POST.get("section"))
+ except Exception:
return HttpResponseBadRequest()
page = self.object = self.get_object()
@@ -95,65 +105,73 @@ class PageView(detail.DetailView):
for section in sections:
if section.pk == pk:
view = registry.get_view(section, request)
- form = view.get_form(method='post')
+ form = view.get_form(method="post")
if form.is_valid():
return view.form_valid(form)
section.invalid_form = form
- context.update({
- 'page': page,
- 'sections': sections,
- })
+ context.update(
+ {
+ "page": page,
+ "sections": sections,
+ }
+ )
return self.render_to_response(context)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
pages = registry.page_class.objects.filter(menu=True)
- context.update({
- 'pages': pages,
- })
+ context.update(
+ {
+ "pages": pages,
+ }
+ )
return context
-@method_decorator(never_cache, name='dispatch')
-class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
- '''Base view with nested forms for editing the page and all its sections'''
+
+@method_decorator(never_cache, name="dispatch")
+class EditPage(
+ UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View
+):
+ """Base view with nested forms for editing the page and all its sections"""
+
model = registry.page_class
form_class = PageForm
- template_name = 'cms/edit.html'
+ template_name = "cms/edit.html"
def test_func(self):
- '''Only allow users with the correct permissions'''
+ """Only allow users with the correct permissions"""
app_label = registry.page_class._meta.app_label
model_name = registry.page_class._meta.model_name
- return self.request.user.has_perm(f'{app_label}.change_{model_name}')
+ return self.request.user.has_perm(f"{app_label}.change_{model_name}")
def get_form_kwargs(self):
- '''Set the default slug to the current URL for new pages'''
+ """Set the default slug to the current URL for new pages"""
kwargs = super().get_form_kwargs()
- if 'slug' in self.kwargs:
- kwargs.update({'initial': {'slug': self.kwargs['slug']}})
+ if "slug" in self.kwargs:
+ kwargs.update({"initial": {"slug": self.kwargs["slug"]}})
return kwargs
def get_context_data(self, **kwargs):
- '''Populate the fields_per_type dict for use in javascript'''
+ """Populate the fields_per_type dict for use in javascript"""
context = super().get_context_data(**kwargs)
- context['fields_per_type'] = json.dumps(registry.get_fields_per_type())
+ context["fields_per_type"] = json.dumps(registry.get_fields_per_type())
return context
def get_object(self):
- '''Prevent 404 by serving the new object form'''
+ """Prevent 404 by serving the new object form"""
try:
return super().get_object()
except Http404:
return None
def get(self, *args, **kwargs):
- '''Handle GET requests'''
+ """Handle GET requests"""
self.object = self.get_object()
return self.render_to_response(self.get_context_data(**kwargs))
def post(self, *args, **kwargs):
- '''Handle POST requests'''
+ """Handle POST requests"""
self.object = self.get_object()
form = self.get_form()
@@ -161,51 +179,59 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
page = form.save()
if page:
return HttpResponseRedirect(page.get_absolute_url())
- return HttpResponseRedirect('/')
+ return HttpResponseRedirect("/")
return self.render_to_response(self.get_context_data(form=form, **kwargs))
+
class CreatePage(EditPage):
- '''View for creating new pages'''
+ """View for creating new pages"""
+
def get_object(self):
return registry.page_class()
-class UpdatePage(EditPage):
- '''View for editing existing pages'''
-@method_decorator(never_cache, name='dispatch')
-class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
+class UpdatePage(EditPage):
+ """View for editing existing pages"""
+
+
+@method_decorator(never_cache, name="dispatch")
+class EditSection(
+ UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View
+):
model = registry.section_class
form_class = SectionForm
- template_name = 'cms/edit.html'
+ template_name = "cms/edit.html"
def test_func(self):
- '''Only allow users with the correct permissions'''
+ """Only allow users with the correct permissions"""
app_label = registry.section_class._meta.app_label
model_name = registry.section_class._meta.model_name
- return self.request.user.has_perm(f'{app_label}.change_{model_name}')
+ return self.request.user.has_perm(f"{app_label}.change_{model_name}")
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
- kwargs.update({
- 'prefix': 'section',
- })
+ kwargs.update(
+ {
+ "prefix": "section",
+ }
+ )
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['fields_per_type'] = json.dumps(registry.get_fields_per_type())
+ context["fields_per_type"] = json.dumps(registry.get_fields_per_type())
return context
def get_object(self, queryset=None):
try:
- self.page = registry.page_class.objects.get(slug=self.kwargs['slug'])
+ self.page = registry.page_class.objects.get(slug=self.kwargs["slug"])
except registry.page_class.DoesNotExist:
raise Http404()
return self.get_section()
def get_section(self):
try:
- section = self.page.sections.get(number=self.kwargs['number'])
+ section = self.page.sections.get(number=self.kwargs["number"])
except self.page.sections.DoesNotExist:
raise Http404()
return section
@@ -225,12 +251,14 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
elif self.page.sections.exists():
return HttpResponseRedirect(self.page.get_absolute_url())
else:
- return HttpResponseRedirect('/')
+ return HttpResponseRedirect("/")
return self.render_to_response(self.get_context_data(form=form, **kwargs))
+
class CreateSection(EditSection):
def get_section(self):
return registry.section_class(page=self.page)
+
class UpdateSection(EditSection):
pass
diff --git a/example/models.py b/example/models.py
index 50848c0..0707768 100644
--- a/example/models.py
+++ b/example/models.py
@@ -1,26 +1,33 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
-from cms.models import BasePage, BaseSection
+
from cms.decorators import page_model, section_model
+from cms.models import BasePage, BaseSection
+
@page_model
class Page(BasePage):
- '''Add custom fields here. Already existing fields: title, slug,
+ """Add custom fields here. Already existing fields: title, slug,
number, menu
- '''
+ """
+
@section_model
class Section(BaseSection):
- '''Add custom fields here. Already existing fields: title, type,
+ """Add custom fields here. Already existing fields: title, type,
number, content, image, video, href
- '''
- page = models.ForeignKey(Page, related_name='sections', on_delete=models.PROTECT)
+ """
+
+ page = models.ForeignKey(Page, related_name="sections", on_delete=models.PROTECT)
+
class SectionImage(models.Model):
- section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE)
- image = models.ImageField(_('Image'))
+ section = models.ForeignKey(
+ Section, related_name="images", on_delete=models.CASCADE
+ )
+ image = models.ImageField(_("Image"))
class Meta:
- ordering = ['pk']
+ ordering = ["pk"]
diff --git a/example/settings.py b/example/settings.py
index a4a8e63..d987395 100644
--- a/example/settings.py
+++ b/example/settings.py
@@ -5,95 +5,95 @@ import sys
PROJECT_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
-DEBUG = 'runserver' in sys.argv
-KEYFILE = f'/tmp/{PROJECT_NAME}.secret'
-ADMINS = [('JJ Vens', 'jj@rtts.eu')]
-DEFAULT_FROM_EMAIL = 'noreply@rtts.eu'
-DEFAULT_TO_EMAIL = ['jj@rtts.eu']
-ALLOWED_HOSTS = ['*']
-ROOT_URLCONF = PROJECT_NAME + '.urls'
-WSGI_APPLICATION = PROJECT_NAME + '.wsgi.application'
-LANGUAGE_CODE = 'nl'
-TIME_ZONE = 'Europe/Amsterdam'
+DEBUG = "runserver" in sys.argv
+KEYFILE = f"/tmp/{PROJECT_NAME}.secret"
+ADMINS = [("JJ Vens", "jj@rtts.eu")]
+DEFAULT_FROM_EMAIL = "noreply@rtts.eu"
+DEFAULT_TO_EMAIL = ["jj@rtts.eu"]
+ALLOWED_HOSTS = ["*"]
+ROOT_URLCONF = PROJECT_NAME + ".urls"
+WSGI_APPLICATION = PROJECT_NAME + ".wsgi.application"
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+LANGUAGE_CODE = "nl"
+TIME_ZONE = "Europe/Amsterdam"
USE_I18N = True
USE_L10N = True
USE_TZ = True
-STATIC_URL = '/static/'
-STATIC_ROOT = '/srv/' + PROJECT_NAME + '/static'
-MEDIA_URL = '/media/'
-MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media'
-LOGIN_REDIRECT_URL = '/'
-LOGOUT_REDIRECT_URL = '/'
-DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
+STATIC_URL = "/static/"
+STATIC_ROOT = "/srv/" + PROJECT_NAME + "/static"
+MEDIA_URL = "/media/"
+MEDIA_ROOT = "/srv/" + PROJECT_NAME + "/media"
+LOGIN_REDIRECT_URL = "/"
+LOGOUT_REDIRECT_URL = "/"
if DEBUG:
- EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
CACHE_MIDDLEWARE_SECONDS = 0
try:
with open(KEYFILE) as f:
SECRET_KEY = f.read()
except IOError:
- SECRET_KEY = ''.join(random.choice(string.printable) for x in range(50))
- with open(KEYFILE, 'w') as f:
+ SECRET_KEY = "".join(random.choice(string.printable) for x in range(50))
+ with open(KEYFILE, "w") as f:
f.write(SECRET_KEY)
INSTALLED_APPS = [
PROJECT_NAME,
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'cms',
- 'embed_video',
- 'easy_thumbnails',
- 'django_extensions',
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "cms",
+ "embed_video",
+ "easy_thumbnails",
+ "django_extensions",
]
if not DEBUG:
- INSTALLED_APPS += ['django.contrib.staticfiles']
+ INSTALLED_APPS += ["django.contrib.staticfiles"]
MIDDLEWARE = [
- 'django.middleware.cache.UpdateCacheMiddleware',
- 'cms.middleware.SassMiddleware',
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'tidy.middleware.TidyMiddleware',
- 'cms.middleware.FetchFromCacheMiddleware',
+ "django.middleware.cache.UpdateCacheMiddleware",
+ "cms.middleware.SassMiddleware",
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
+ "tidy.middleware.TidyMiddleware",
+ "cms.middleware.FetchFromCacheMiddleware",
]
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.debug",
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
],
},
},
]
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.postgresql',
- 'USER': PROJECT_NAME,
- 'NAME': PROJECT_NAME,
+ "default": {
+ "ENGINE": "django.db.backends.postgresql",
+ "USER": PROJECT_NAME,
+ "NAME": PROJECT_NAME,
}
}
CACHES = {
- 'default': {
- 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
- 'LOCATION': '127.0.0.1:11211',
- 'KEY_PREFIX': PROJECT_NAME,
+ "default": {
+ "BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
+ "LOCATION": "127.0.0.1:11211",
+ "KEY_PREFIX": PROJECT_NAME,
}
}
diff --git a/example/urls.py b/example/urls.py
index be77a41..49d9e51 100644
--- a/example/urls.py
+++ b/example/urls.py
@@ -1,16 +1,20 @@
from django.conf import settings
-from django.contrib import admin
-from django.urls import path, include
from django.conf.urls.static import static
-from django.views.generic import RedirectView
+from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import include, path
+from django.views.generic import RedirectView
-admin.site.site_header = admin.site.site_title = settings.PROJECT_NAME.replace('_', ' ').title()
-urlpatterns = staticfiles_urlpatterns() + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+admin.site.site_header = admin.site.site_title = settings.PROJECT_NAME.replace(
+ "_", " "
+).title()
+urlpatterns = staticfiles_urlpatterns() + static(
+ settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
+)
urlpatterns += [
- path('admin/', admin.site.urls),
- path('accounts/', include('django.contrib.auth.urls')),
- path('login/', RedirectView.as_view(url='/accounts/login/')),
- path('logout/', RedirectView.as_view(url='/accounts/logout/')),
- path('', include('cms.urls', namespace='cms')),
+ path("admin/", admin.site.urls),
+ path("accounts/", include("django.contrib.auth.urls")),
+ path("login/", RedirectView.as_view(url="/accounts/login/")),
+ path("logout/", RedirectView.as_view(url="/accounts/logout/")),
+ path("", include("cms.urls", namespace="cms")),
]
diff --git a/example/views.py b/example/views.py
index e891d71..995e394 100644
--- a/example/views.py
+++ b/example/views.py
@@ -1,30 +1,35 @@
from django.utils.translation import gettext_lazy as _
-from cms.views import SectionView, SectionFormView
+
from cms.decorators import section_view
from cms.forms import ContactForm
+from cms.views import SectionFormView, SectionView
+
@section_view
class Text(SectionView):
- verbose_name = _('Text')
- fields = ['content']
- template_name = 'text.html'
+ verbose_name = _("Text")
+ fields = ["content"]
+ template_name = "text.html"
+
@section_view
class Images(SectionView):
- verbose_name = _('Image(s)')
- fields = ['images']
- template_name = 'images.html'
+ verbose_name = _("Image(s)")
+ fields = ["images"]
+ template_name = "images.html"
+
@section_view
class Video(SectionView):
- verbose_name = _('Video')
- fields = ['video']
- template_name = 'video.html'
+ verbose_name = _("Video")
+ fields = ["video"]
+ template_name = "video.html"
+
@section_view
class Contact(SectionFormView):
- verbose_name = _('Contact')
+ verbose_name = _("Contact")
fields = []
form_class = ContactForm
- success_url = '/thanks/'
- template_name = 'contact.html'
+ success_url = "/thanks/"
+ template_name = "contact.html"
diff --git a/example/wsgi.py b/example/wsgi.py
index 805686a..5fb93d6 100644
--- a/example/wsgi.py
+++ b/example/wsgi.py
@@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
application = get_wsgi_application()
diff --git a/manage.py b/manage.py
index f0f029c..6bfa2c4 100755
--- a/manage.py
+++ b/manage.py
@@ -2,8 +2,8 @@
import os
import sys
-if __name__ == '__main__':
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings')
+if __name__ == "__main__":
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..1a17557
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,2 @@
+[tool.isort]
+line_length = 88
diff --git a/setup.py b/setup.py
index f1f1168..a17767d 100755
--- a/setup.py
+++ b/setup.py
@@ -1,39 +1,40 @@
#!/usr/bin/env python
-import cms
-from setuptools import setup, find_packages
+from setuptools import find_packages, setup
-with open('README.md', 'r') as fh:
+import cms
+
+with open("README.md", "r") as fh:
long_description = fh.read()
setup(
- name = 'django-simplecms',
- description = 'Simple Django CMS',
- version = cms.__version__,
- author = 'Jaap Joris Vens',
- author_email = 'jj+cms@rtts.eu',
- url = 'https://github.com/rtts/django-simplecms',
- long_description = long_description,
- long_description_content_type = 'text/markdown',
- packages = find_packages(),
- entry_points = {
- 'console_scripts': ['simplecms=cms.__main__:main'],
+ name="django-simplecms",
+ description="Simple Django CMS",
+ version=cms.__version__,
+ author="Jaap Joris Vens",
+ author_email="jj+cms@rtts.eu",
+ url="https://github.com/rtts/django-simplecms",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ packages=find_packages(),
+ entry_points={
+ "console_scripts": ["simplecms=cms.__main__:main"],
},
- include_package_data = True,
- classifiers = [
- 'Programming Language :: Python :: 3',
- 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
- 'Operating System :: OS Independent',
+ include_package_data=True,
+ classifiers=[
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
+ "Operating System :: OS Independent",
],
- python_requires = '>=3.8',
- install_requires = [
- 'django',
- 'django-extensions',
- 'django-embed-video',
- 'django-tidy',
- 'easy-thumbnails',
- 'libsass',
- 'markdown',
- 'psycopg2',
- 'pylibmc',
+ python_requires=">=3.8",
+ install_requires=[
+ "django",
+ "django-extensions",
+ "django-embed-video",
+ "django-tidy",
+ "easy-thumbnails",
+ "libsass",
+ "markdown",
+ "psycopg2",
+ "pylibmc",
],
)