Blackify everything

main
Jaap Joris Vens 2021-07-04 01:59:48 +02:00
rodzic cd8151b2ce
commit 98de86f49d
19 zmienionych plików z 441 dodań i 327 usunięć

Wyświetl plik

@ -1,2 +1,2 @@
__version__ = "1.0.5" __version__ = "1.0.6"
default_app_config = "cms.apps.CmsConfig" default_app_config = "cms.apps.CmsConfig"

Wyświetl plik

@ -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 from pip._internal.operations import freeze as pip
import cms
import example
def main(): def main():
parser = argparse.ArgumentParser(description='SimpleCMS') parser = argparse.ArgumentParser(description="SimpleCMS")
parser.add_argument('project_name', nargs='?', default='.') parser.add_argument("project_name", nargs="?", default=".")
project_name = parser.parse_args().project_name project_name = parser.parse_args().project_name
if project_name == '.': if project_name == ".":
project_dir = os.getcwd() project_dir = os.getcwd()
project_name = os.path.basename(project_dir) project_name = os.path.basename(project_dir)
else: else:
project_dir = os.path.join(os.getcwd(), project_name) project_dir = os.path.join(os.getcwd(), project_name)
if re.match(r'^\w+$', 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 (
input(f"Do you want to create a new project in `{project_dir}` ?\033[1D")
in "Yy"
):
create_project(project_name, project_dir) create_project(project_name, project_dir)
else: else:
print(f'Invalid project name: {project_name}') print(f"Invalid project name: {project_name}")
def create_project(project_name, project_dir): def create_project(project_name, project_dir):
os.makedirs(project_dir, exist_ok=True) 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(): for line in pip.freeze():
if 'django_simplecms' in line: if "django_simplecms" in line:
line = f'django-simplecms=={cms.__version__}' line = f"django-simplecms=={cms.__version__}"
print(line, file=f) print(line, file=f)
shutil.copytree(os.path.dirname(example.__file__),os.path.join(project_dir, project_name), dirs_exist_ok=True) shutil.copytree(
with open(os.open(os.path.join(project_dir, 'manage.py'), os.O_CREAT|os.O_WRONLY, 0o755), 'w') as f: os.path.dirname(example.__file__),
print('#!/usr/bin/env python', os.path.join(project_dir, project_name),
'import os, sys', dirs_exist_ok=True,
f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')", )
'from django.core.management import execute_from_command_line', with open(
'execute_from_command_line(sys.argv)', os.open(
sep='\n', file=f) os.path.join(project_dir, "manage.py"), os.O_CREAT | os.O_WRONLY, 0o755
with open(os.path.join(project_dir, project_name, 'wsgi.py'), 'w') as f: ),
print('import os', "w",
'from django.core.wsgi import get_wsgi_application', ) as f:
f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')", print(
'application = get_wsgi_application()', "#!/usr/bin/env python",
sep='\n', file=f) "import os, sys",
with open(os.path.join(project_dir, '.gitignore'), 'w') as f: f"os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings')",
print('*.pyc\n__pycache__/', file=f) "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}"', print(
'', f'Successfully created project "{project_name}"',
'Things to do next:', "",
'- create a database', "Things to do next:",
'- ./manage.py makemigrations', "- create a database",
'- ./manage.py migrate', "- ./manage.py makemigrations",
'- ./manage.py createsuperuser', "- ./manage.py migrate",
'- ./manage.py runserver', "- ./manage.py createsuperuser",
sep='\n') "- ./manage.py runserver",
sep="\n",
)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

Wyświetl plik

@ -1,10 +1,11 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
from django.utils.module_loading import autodiscover_modules from django.utils.module_loading import autodiscover_modules
from django.utils.translation import gettext_lazy as _
class CmsConfig(AppConfig): class CmsConfig(AppConfig):
name = 'cms' name = "cms"
verbose_name = _('Content Management System') verbose_name = _("Content Management System")
def ready(self): def ready(self):
autodiscover_modules('views') autodiscover_modules("views")

Wyświetl plik

@ -1,17 +1,20 @@
from cms import registry from cms import registry
def page_model(cls): def page_model(cls):
'''Decorator to register the Page model''' """Decorator to register the Page model"""
registry.page_class = cls registry.page_class = cls
return cls return cls
def section_model(cls): def section_model(cls):
'''Decorator to register the Section model''' """Decorator to register the Section model"""
registry.section_class = cls registry.section_class = cls
return cls return cls
def section_view(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.view_per_type[cls.__name__.lower()] = cls
registry.section_types.append((cls.__name__.lower(), cls.verbose_name)) registry.section_types.append((cls.__name__.lower(), cls.verbose_name))
return cls return cls

Wyświetl plik

@ -1,27 +1,28 @@
from django import forms from django import forms
from django.conf import settings 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.core.mail import EmailMessage
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import registry from . import registry
class PageForm(forms.ModelForm): class PageForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.label_suffix = '' self.label_suffix = ""
self.formsets = [forms.inlineformset_factory( self.formsets = [
parent_model=registry.page_class, forms.inlineformset_factory(
model=registry.section_class, parent_model=registry.page_class,
form=SectionForm, model=registry.section_class,
extra=1, form=SectionForm,
)( extra=1,
data=self.data if self.is_bound else None, )(
files=self.files if self.is_bound else None, data=self.data if self.is_bound else None,
instance=self.instance, files=self.files if self.is_bound else None,
)] instance=self.instance,
)
]
self.formsets[0][0].empty_permitted = True self.formsets[0][0].empty_permitted = True
def is_valid(self): def is_valid(self):
@ -44,17 +45,18 @@ class PageForm(forms.ModelForm):
class Meta: class Meta:
model = registry.page_class model = registry.page_class
fields = '__all__' fields = "__all__"
class SectionForm(forms.ModelForm): class SectionForm(forms.ModelForm):
type = forms.ChoiceField() type = forms.ChoiceField()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.label_suffix = '' self.label_suffix = ""
self.fields['DELETE'] = forms.BooleanField(label=_('Delete'), required=False) self.fields["DELETE"] = forms.BooleanField(label=_("Delete"), required=False)
self.fields['type'].choices = registry.get_types() self.fields["type"].choices = registry.get_types()
self.fields['type'].initial = registry.get_types()[0][0] self.fields["type"].initial = registry.get_types()[0][0]
# Populate the 'formsets' attribute if the Section was # Populate the 'formsets' attribute if the Section was
# extendend with one_to_many fields # extendend with one_to_many fields
@ -64,14 +66,14 @@ class SectionForm(forms.ModelForm):
formset = forms.inlineformset_factory( formset = forms.inlineformset_factory(
parent_model=registry.section_class, parent_model=registry.section_class,
model=field.related_model, model=field.related_model,
fields='__all__', fields="__all__",
extra=1, extra=1,
)( )(
instance=self.instance, instance=self.instance,
data=self.data if self.is_bound else None, data=self.data if self.is_bound else None,
files=self.files if self.is_bound else None, files=self.files if self.is_bound else None,
prefix=f'{self.prefix}-{field.name}', prefix=f"{self.prefix}-{field.name}",
form_kwargs={'label_suffix': self.label_suffix}, form_kwargs={"label_suffix": self.label_suffix},
) )
formset.name = field.name formset.name = field.name
self.formsets.append(formset) self.formsets.append(formset)
@ -79,19 +81,19 @@ class SectionForm(forms.ModelForm):
def is_valid(self): def is_valid(self):
result = super().is_valid() result = super().is_valid()
for formset in self.formsets: for formset in self.formsets:
result = result and formset.is_valid() # AND result = result and formset.is_valid() # AND
return result return result
def has_changed(self): def has_changed(self):
result = super().has_changed() result = super().has_changed()
for formset in self.formsets: for formset in self.formsets:
result = result or formset.has_changed() # OR result = result or formset.has_changed() # OR
return result return result
def save(self, commit=True): def save(self, commit=True):
section = super().save(commit=commit) section = super().save(commit=commit)
if self.cleaned_data['DELETE']: if self.cleaned_data["DELETE"]:
section.delete() section.delete()
if section.page.slug and not section.page.sections.exists(): if section.page.slug and not section.page.sections.exists():
section.page.delete() section.page.delete()
@ -106,25 +108,28 @@ class SectionForm(forms.ModelForm):
class Meta: class Meta:
model = registry.section_class model = registry.section_class
exclude = ['page'] exclude = ["page"]
class ContactForm(forms.Form): class ContactForm(forms.Form):
sender = forms.EmailField(label=_('Your email address')) sender = forms.EmailField(label=_("Your email address"))
spam_protection = forms.CharField(label=_('Your message'), widget=forms.Textarea()) spam_protection = forms.CharField(label=_("Your message"), widget=forms.Textarea())
message = forms.CharField(label=_('Your message'), widget=forms.Textarea(), initial='Hi there!') message = forms.CharField(
label=_("Your message"), widget=forms.Textarea(), initial="Hi there!"
)
def save(self): def save(self):
body = self.cleaned_data.get('spam_protection') body = self.cleaned_data.get("spam_protection")
if len(body.split()) < 7: if len(body.split()) < 7:
return return
spamcheck = self.cleaned_data.get('message') spamcheck = self.cleaned_data.get("message")
if spamcheck != 'Hi there!': if spamcheck != "Hi there!":
return return
email = EmailMessage( email = EmailMessage(
to = settings.DEFAULT_TO_EMAIL, to=settings.DEFAULT_TO_EMAIL,
body = body, body=body,
subject = _('Contact form'), subject=_("Contact form"),
headers = {'Reply-To': self.cleaned_data.get('sender')}, headers={"Reply-To": self.cleaned_data.get("sender")},
) )
email.send() email.send()

Wyświetl plik

@ -1,7 +1,9 @@
import os import os
from sass import compile
from django.conf import settings from django.conf import settings
from django.middleware import cache from django.middleware import cache
from sass import compile
def locate(filename): def locate(filename):
for path, dirs, files in os.walk(os.getcwd(), followlinks=True): for path, dirs, files in os.walk(os.getcwd(), followlinks=True):
@ -9,38 +11,44 @@ def locate(filename):
if f == filename: if f == filename:
yield os.path.join(path, filename) yield os.path.join(path, filename)
class FetchFromCacheMiddleware(cache.FetchFromCacheMiddleware): 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 requests that have a `sessionid` cookie. This should be the
Django default, IMHO. Django default, IMHO.
''' """
def process_request(self, request): def process_request(self, request):
if 'sessionid' not in request.COOKIES: if "sessionid" not in request.COOKIES:
return super().process_request(request) return super().process_request(request)
class SassMiddleware: 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. tries to compile the corresponding SCSS file.
''' """
def __init__(self, get_response): def __init__(self, get_response):
self.get_response = get_response self.get_response = get_response
def __call__(self, request): def __call__(self, request):
if settings.DEBUG and request.path.endswith('.css'): if settings.DEBUG and request.path.endswith(".css"):
css_file = request.path.rsplit('/',1)[1] css_file = request.path.rsplit("/", 1)[1]
sass_file = css_file[:-4] sass_file = css_file[:-4]
sass_paths = locate(sass_file) sass_paths = locate(sass_file)
for sass_path in list(sass_paths): for sass_path in list(sass_paths):
if os.path.exists(sass_path): if os.path.exists(sass_path):
css_path = sass_path + '.css' css_path = sass_path + ".css"
map_path = css_path + '.map' map_path = css_path + ".map"
css = compile(filename=sass_path, output_style='nested') css = compile(filename=sass_path, output_style="nested")
css, mapping = compile(filename=sass_path, source_map_filename=map_path) css, mapping = compile(
with open(css_path, 'w') as f: filename=sass_path, source_map_filename=map_path
)
with open(css_path, "w") as f:
f.write(css) f.write(css)
with open(map_path, 'w') as f: with open(map_path, "w") as f:
f.write(mapping) f.write(mapping)
response = self.get_response(request) response = self.get_response(request)

Wyświetl plik

@ -10,6 +10,8 @@ from . import fields, mixins
class Model(models.Model): class Model(models.Model):
"""Felt cute, might delete later.""" """Felt cute, might delete later."""
id = models.BigAutoField(primary_key=True)
class Meta: class Meta:
abstract = True abstract = True

Wyświetl plik

@ -3,15 +3,17 @@ section_class = None
section_types = [] section_types = []
view_per_type = {} view_per_type = {}
def get_types(): def get_types():
return section_types return section_types
def get_view(section, request): def get_view(section, request):
return view_per_type[section.type](request) return view_per_type[section.type](request)
def get_fields_per_type(): def get_fields_per_type():
fields_per_type = {} fields_per_type = {}
for name, view in view_per_type.items(): 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 return fields_per_type

Wyświetl plik

@ -1,18 +1,17 @@
from markdown import markdown as md
from django import template from django import template
from django.shortcuts import reverse from django.shortcuts import reverse
from django.utils.safestring import mark_safe 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 from cms import registry
MARKDOWN_EXTENSIONS = ['extra', 'smarty'] MARKDOWN_EXTENSIONS = ["extra", "smarty"]
register = template.Library() register = template.Library()
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def eval(context, expr): def eval(context, expr):
'''USE WITH CAUTION!!! """USE WITH CAUTION!!!
This template tag runs its argument through Django's templating This template tag runs its argument through Django's templating
system using the current context, placing all power into the system using the current context, placing all power into the
@ -20,49 +19,61 @@ def eval(context, expr):
Also, it applies Markdown. Also, it applies Markdown.
''' """
result = template.Template(expr).render(context) result = template.Template(expr).render(context)
return mark_safe(md(result, extensions=MARKDOWN_EXTENSIONS)) return mark_safe(md(result, extensions=MARKDOWN_EXTENSIONS))
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def editsection(context, inner): def editsection(context, inner):
'''Renders a simple link to edit the current section''' """Renders a simple link to edit the current section"""
section = context['section'] section = context["section"]
user = context['request'].user user = context["request"].user
app_label = section._meta.app_label app_label = section._meta.app_label
model_name = section._meta.model_name 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 slug = section.page.slug
number = section.number 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'<a class="edit section" href="{url}">{inner}</a>') return mark_safe(f'<a class="edit section" href="{url}">{inner}</a>')
return '' return ""
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
def editpage(context, inner): def editpage(context, inner):
'''Renders a simple link to edit the current page''' """Renders a simple link to edit the current page"""
page = context['page'] page = context["page"]
user = context['request'].user user = context["request"].user
app_label = page._meta.app_label app_label = page._meta.app_label
model_name = page._meta.model_name 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 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'<a class="edit page" href="{url}">{inner}</a>') return mark_safe(f'<a class="edit page" href="{url}">{inner}</a>')
return '' return ""
@register.tag('include_section')
@register.tag("include_section")
def do_include(parser, token): def do_include(parser, token):
'''Renders the section with its own context''' """Renders the section with its own context"""
_, section = token.split_contents() _, section = token.split_contents()
return IncludeSectionNode(section) return IncludeSectionNode(section)
class IncludeSectionNode(template.Node): class IncludeSectionNode(template.Node):
def __init__(self, section): def __init__(self, section):
self.section = template.Variable(section) self.section = template.Variable(section)
self.csrf_token = template.Variable('csrf_token') self.csrf_token = template.Variable("csrf_token")
self.request = template.Variable('request') self.request = template.Variable("request")
self.perms = template.Variable('perms') self.perms = template.Variable("perms")
super().__init__() super().__init__()
def render(self, context): def render(self, context):
@ -73,13 +84,13 @@ class IncludeSectionNode(template.Node):
view = registry.get_view(section, request) view = registry.get_view(section, request)
initial_context = { initial_context = {
'csrf_token': csrf_token, "csrf_token": csrf_token,
'section': section, "section": section,
'request': request, "request": request,
'perms': perms, "perms": perms,
} }
if hasattr(section, 'invalid_form'): if hasattr(section, "invalid_form"):
initial_context['form'] = section.invalid_form initial_context["form"] = section.invalid_form
section_context = view.get_context_data(**initial_context) section_context = view.get_context_data(**initial_context)
t = context.template.engine.get_template(view.template_name) t = context.template.engine.get_template(view.template_name)

Wyświetl plik

@ -1,15 +1,23 @@
from django.urls import path 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 = [ urlpatterns = [
path('new/', CreatePage.as_view(), name='createpage'), path("new/", CreatePage.as_view(), name="createpage"),
path('edit/', UpdatePage.as_view(), kwargs={'slug': ''}, name='updatepage'), path("edit/", UpdatePage.as_view(), kwargs={"slug": ""}, name="updatepage"),
path('edit/<int:number>/', UpdateSection.as_view(), kwargs={'slug': ''}, name='updatesection'), path(
path('<slug:slug>/edit/', UpdatePage.as_view(), name='updatepage'), "edit/<int:number>/",
path('<slug:slug>/edit/new/', CreateSection.as_view(), name='createsection'), UpdateSection.as_view(),
path('<slug:slug>/edit/<int:number>/', UpdateSection.as_view(), name='updatesection'), kwargs={"slug": ""},
path('', PageView.as_view(), name='page'), name="updatesection",
path('<slug:slug>/', PageView.as_view(), name='page'), ),
path("<slug:slug>/edit/", UpdatePage.as_view(), name="updatepage"),
path("<slug:slug>/edit/new/", CreateSection.as_view(), name="createsection"),
path(
"<slug:slug>/edit/<int:number>/", UpdateSection.as_view(), name="updatesection"
),
path("", PageView.as_view(), name="page"),
path("<slug:slug>/", PageView.as_view(), name="page"),
] ]

Wyświetl plik

@ -1,18 +1,20 @@
import json import json
from django.contrib.auth.mixins import UserPassesTestMixin
from django.http import Http404, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.generic import base, detail, edit
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache
from django.contrib.auth.mixins import UserPassesTestMixin from django.views.generic import base, detail, edit
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from . import registry from . import registry
from .forms import PageForm, SectionForm from .forms import PageForm, SectionForm
class SectionView: class SectionView:
'''Generic section view''' """Generic section view"""
template_name = 'cms/sections/section.html'
template_name = "cms/sections/section.html"
def __init__(self, request): def __init__(self, request):
self.request = request self.request = request
@ -20,14 +22,16 @@ class SectionView:
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
return kwargs return kwargs
class SectionFormView(SectionView): class SectionFormView(SectionView):
'''Generic section with associated form''' """Generic section with associated form"""
form_class = None form_class = None
success_url = None success_url = None
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
if 'form' not in kwargs: if "form" not in kwargs:
kwargs['form'] = self.get_form() kwargs["form"] = self.get_form()
return kwargs return kwargs
def form_valid(self, form): def form_valid(self, form):
@ -37,56 +41,62 @@ class SectionFormView(SectionView):
def get_form_kwargs(self): def get_form_kwargs(self):
return {} return {}
def get_form(self, method='get'): def get_form(self, method="get"):
form_class = self.form_class form_class = self.form_class
kwargs = self.get_form_kwargs() kwargs = self.get_form_kwargs()
if method == 'post': if method == "post":
kwargs.update({ kwargs.update(
'data': self.request.POST, {
'files': self.request.FILES, "data": self.request.POST,
}) "files": self.request.FILES,
}
)
return form_class(**kwargs) 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): class PageView(detail.DetailView):
'''Supply a default argument for slug''' """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) super().setup(*args, slug=slug, **kwargs)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
'''Instantiate section views and render final response''' """Instantiate section views and render final response"""
try: try:
page = self.object = self.get_object() page = self.object = self.get_object()
except Http404: except Http404:
app_label = registry.page_class._meta.app_label app_label = registry.page_class._meta.app_label
model_name = registry.page_class._meta.model_name model_name = registry.page_class._meta.model_name
if self.kwargs['slug'] == '': if self.kwargs["slug"] == "":
page = registry.page_class(title='Homepage', slug='') page = registry.page_class(title="Homepage", slug="")
page.save() page.save()
self.object = page self.object = page
# Special case: Don't serve 404 pages to authorized users, # Special case: Don't serve 404 pages to authorized users,
# but redirect to the edit page form with the slug pre-filled # but redirect to the edit page form with the slug pre-filled
elif (self.request.user.has_perm(f'{app_label}.change_{model_name}')): elif self.request.user.has_perm(f"{app_label}.change_{model_name}"):
return redirect('cms:updatepage', slug=self.kwargs['slug']) return redirect("cms:updatepage", slug=self.kwargs["slug"])
else: else:
raise raise
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
sections = page.sections.all() sections = page.sections.all()
context.update({ context.update(
'page': page, {
'sections': sections, "page": page,
}) "sections": sections,
}
)
return self.render_to_response(context) return self.render_to_response(context)
def post(self, request, **kwargs): def post(self, request, **kwargs):
'''Call the post() method of the correct section view''' """Call the post() method of the correct section view"""
try: try:
pk = int(self.request.POST.get('section')) pk = int(self.request.POST.get("section"))
except: except Exception:
return HttpResponseBadRequest() return HttpResponseBadRequest()
page = self.object = self.get_object() page = self.object = self.get_object()
@ -95,65 +105,73 @@ class PageView(detail.DetailView):
for section in sections: for section in sections:
if section.pk == pk: if section.pk == pk:
view = registry.get_view(section, request) view = registry.get_view(section, request)
form = view.get_form(method='post') form = view.get_form(method="post")
if form.is_valid(): if form.is_valid():
return view.form_valid(form) return view.form_valid(form)
section.invalid_form = form section.invalid_form = form
context.update({ context.update(
'page': page, {
'sections': sections, "page": page,
}) "sections": sections,
}
)
return self.render_to_response(context) return self.render_to_response(context)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
pages = registry.page_class.objects.filter(menu=True) pages = registry.page_class.objects.filter(menu=True)
context.update({ context.update(
'pages': pages, {
}) "pages": pages,
}
)
return context return context
@method_decorator(never_cache, name='dispatch')
class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View): @method_decorator(never_cache, name="dispatch")
'''Base view with nested forms for editing the page and all its sections''' 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 model = registry.page_class
form_class = PageForm form_class = PageForm
template_name = 'cms/edit.html' template_name = "cms/edit.html"
def test_func(self): 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 app_label = registry.page_class._meta.app_label
model_name = registry.page_class._meta.model_name 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): 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() kwargs = super().get_form_kwargs()
if 'slug' in self.kwargs: if "slug" in self.kwargs:
kwargs.update({'initial': {'slug': self.kwargs['slug']}}) kwargs.update({"initial": {"slug": self.kwargs["slug"]}})
return kwargs return kwargs
def get_context_data(self, **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 = 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 return context
def get_object(self): def get_object(self):
'''Prevent 404 by serving the new object form''' """Prevent 404 by serving the new object form"""
try: try:
return super().get_object() return super().get_object()
except Http404: except Http404:
return None return None
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
'''Handle GET requests''' """Handle GET requests"""
self.object = self.get_object() self.object = self.get_object()
return self.render_to_response(self.get_context_data(**kwargs)) return self.render_to_response(self.get_context_data(**kwargs))
def post(self, *args, **kwargs): def post(self, *args, **kwargs):
'''Handle POST requests''' """Handle POST requests"""
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
@ -161,51 +179,59 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
page = form.save() page = form.save()
if page: if page:
return HttpResponseRedirect(page.get_absolute_url()) return HttpResponseRedirect(page.get_absolute_url())
return HttpResponseRedirect('/') return HttpResponseRedirect("/")
return self.render_to_response(self.get_context_data(form=form, **kwargs)) return self.render_to_response(self.get_context_data(form=form, **kwargs))
class CreatePage(EditPage): class CreatePage(EditPage):
'''View for creating new pages''' """View for creating new pages"""
def get_object(self): def get_object(self):
return registry.page_class() return registry.page_class()
class UpdatePage(EditPage):
'''View for editing existing pages'''
@method_decorator(never_cache, name='dispatch') class UpdatePage(EditPage):
class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View): """View for editing existing pages"""
@method_decorator(never_cache, name="dispatch")
class EditSection(
UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View
):
model = registry.section_class model = registry.section_class
form_class = SectionForm form_class = SectionForm
template_name = 'cms/edit.html' template_name = "cms/edit.html"
def test_func(self): 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 app_label = registry.section_class._meta.app_label
model_name = registry.section_class._meta.model_name 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): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
kwargs.update({ kwargs.update(
'prefix': 'section', {
}) "prefix": "section",
}
)
return kwargs return kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**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 return context
def get_object(self, queryset=None): def get_object(self, queryset=None):
try: 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: except registry.page_class.DoesNotExist:
raise Http404() raise Http404()
return self.get_section() return self.get_section()
def get_section(self): def get_section(self):
try: try:
section = self.page.sections.get(number=self.kwargs['number']) section = self.page.sections.get(number=self.kwargs["number"])
except self.page.sections.DoesNotExist: except self.page.sections.DoesNotExist:
raise Http404() raise Http404()
return section return section
@ -225,12 +251,14 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
elif self.page.sections.exists(): elif self.page.sections.exists():
return HttpResponseRedirect(self.page.get_absolute_url()) return HttpResponseRedirect(self.page.get_absolute_url())
else: else:
return HttpResponseRedirect('/') return HttpResponseRedirect("/")
return self.render_to_response(self.get_context_data(form=form, **kwargs)) return self.render_to_response(self.get_context_data(form=form, **kwargs))
class CreateSection(EditSection): class CreateSection(EditSection):
def get_section(self): def get_section(self):
return registry.section_class(page=self.page) return registry.section_class(page=self.page)
class UpdateSection(EditSection): class UpdateSection(EditSection):
pass pass

Wyświetl plik

@ -1,26 +1,33 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from cms.models import BasePage, BaseSection
from cms.decorators import page_model, section_model from cms.decorators import page_model, section_model
from cms.models import BasePage, BaseSection
@page_model @page_model
class Page(BasePage): class Page(BasePage):
'''Add custom fields here. Already existing fields: title, slug, """Add custom fields here. Already existing fields: title, slug,
number, menu number, menu
''' """
@section_model @section_model
class Section(BaseSection): 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 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): class SectionImage(models.Model):
section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE) section = models.ForeignKey(
image = models.ImageField(_('Image')) Section, related_name="images", on_delete=models.CASCADE
)
image = models.ImageField(_("Image"))
class Meta: class Meta:
ordering = ['pk'] ordering = ["pk"]

Wyświetl plik

@ -5,95 +5,95 @@ import sys
PROJECT_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__))) PROJECT_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
DEBUG = 'runserver' in sys.argv DEBUG = "runserver" in sys.argv
KEYFILE = f'/tmp/{PROJECT_NAME}.secret' KEYFILE = f"/tmp/{PROJECT_NAME}.secret"
ADMINS = [('JJ Vens', 'jj@rtts.eu')] ADMINS = [("JJ Vens", "jj@rtts.eu")]
DEFAULT_FROM_EMAIL = 'noreply@rtts.eu' DEFAULT_FROM_EMAIL = "noreply@rtts.eu"
DEFAULT_TO_EMAIL = ['jj@rtts.eu'] DEFAULT_TO_EMAIL = ["jj@rtts.eu"]
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ["*"]
ROOT_URLCONF = PROJECT_NAME + '.urls' ROOT_URLCONF = PROJECT_NAME + ".urls"
WSGI_APPLICATION = PROJECT_NAME + '.wsgi.application' WSGI_APPLICATION = PROJECT_NAME + ".wsgi.application"
LANGUAGE_CODE = 'nl' DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
TIME_ZONE = 'Europe/Amsterdam' LANGUAGE_CODE = "nl"
TIME_ZONE = "Europe/Amsterdam"
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True
USE_TZ = True USE_TZ = True
STATIC_URL = '/static/' STATIC_URL = "/static/"
STATIC_ROOT = '/srv/' + PROJECT_NAME + '/static' STATIC_ROOT = "/srv/" + PROJECT_NAME + "/static"
MEDIA_URL = '/media/' MEDIA_URL = "/media/"
MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media' MEDIA_ROOT = "/srv/" + PROJECT_NAME + "/media"
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = "/"
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
if DEBUG: if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
CACHE_MIDDLEWARE_SECONDS = 0 CACHE_MIDDLEWARE_SECONDS = 0
try: try:
with open(KEYFILE) as f: with open(KEYFILE) as f:
SECRET_KEY = f.read() SECRET_KEY = f.read()
except IOError: except IOError:
SECRET_KEY = ''.join(random.choice(string.printable) for x in range(50)) SECRET_KEY = "".join(random.choice(string.printable) for x in range(50))
with open(KEYFILE, 'w') as f: with open(KEYFILE, "w") as f:
f.write(SECRET_KEY) f.write(SECRET_KEY)
INSTALLED_APPS = [ INSTALLED_APPS = [
PROJECT_NAME, PROJECT_NAME,
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'cms', "cms",
'embed_video', "embed_video",
'easy_thumbnails', "easy_thumbnails",
'django_extensions', "django_extensions",
] ]
if not DEBUG: if not DEBUG:
INSTALLED_APPS += ['django.contrib.staticfiles'] INSTALLED_APPS += ["django.contrib.staticfiles"]
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware', "django.middleware.cache.UpdateCacheMiddleware",
'cms.middleware.SassMiddleware', "cms.middleware.SassMiddleware",
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
'tidy.middleware.TidyMiddleware', "tidy.middleware.TidyMiddleware",
'cms.middleware.FetchFromCacheMiddleware', "cms.middleware.FetchFromCacheMiddleware",
] ]
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [], "DIRS": [],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ],
}, },
}, },
] ]
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.postgresql', "ENGINE": "django.db.backends.postgresql",
'USER': PROJECT_NAME, "USER": PROJECT_NAME,
'NAME': PROJECT_NAME, "NAME": PROJECT_NAME,
} }
} }
CACHES = { CACHES = {
'default': { "default": {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', "BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
'LOCATION': '127.0.0.1:11211', "LOCATION": "127.0.0.1:11211",
'KEY_PREFIX': PROJECT_NAME, "KEY_PREFIX": PROJECT_NAME,
} }
} }

Wyświetl plik

@ -1,16 +1,20 @@
from django.conf import settings 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.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.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() admin.site.site_header = admin.site.site_title = settings.PROJECT_NAME.replace(
urlpatterns = staticfiles_urlpatterns() + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) "_", " "
).title()
urlpatterns = staticfiles_urlpatterns() + static(
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
)
urlpatterns += [ urlpatterns += [
path('admin/', admin.site.urls), path("admin/", admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')), path("accounts/", include("django.contrib.auth.urls")),
path('login/', RedirectView.as_view(url='/accounts/login/')), path("login/", RedirectView.as_view(url="/accounts/login/")),
path('logout/', RedirectView.as_view(url='/accounts/logout/')), path("logout/", RedirectView.as_view(url="/accounts/logout/")),
path('', include('cms.urls', namespace='cms')), path("", include("cms.urls", namespace="cms")),
] ]

Wyświetl plik

@ -1,30 +1,35 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from cms.views import SectionView, SectionFormView
from cms.decorators import section_view from cms.decorators import section_view
from cms.forms import ContactForm from cms.forms import ContactForm
from cms.views import SectionFormView, SectionView
@section_view @section_view
class Text(SectionView): class Text(SectionView):
verbose_name = _('Text') verbose_name = _("Text")
fields = ['content'] fields = ["content"]
template_name = 'text.html' template_name = "text.html"
@section_view @section_view
class Images(SectionView): class Images(SectionView):
verbose_name = _('Image(s)') verbose_name = _("Image(s)")
fields = ['images'] fields = ["images"]
template_name = 'images.html' template_name = "images.html"
@section_view @section_view
class Video(SectionView): class Video(SectionView):
verbose_name = _('Video') verbose_name = _("Video")
fields = ['video'] fields = ["video"]
template_name = 'video.html' template_name = "video.html"
@section_view @section_view
class Contact(SectionFormView): class Contact(SectionFormView):
verbose_name = _('Contact') verbose_name = _("Contact")
fields = [] fields = []
form_class = ContactForm form_class = ContactForm
success_url = '/thanks/' success_url = "/thanks/"
template_name = 'contact.html' template_name = "contact.html"

Wyświetl plik

@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application 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() application = get_wsgi_application()

Wyświetl plik

@ -2,8 +2,8 @@
import os import os
import sys import sys
if __name__ == '__main__': if __name__ == "__main__":
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

2
pyproject.toml 100644
Wyświetl plik

@ -0,0 +1,2 @@
[tool.isort]
line_length = 88

Wyświetl plik

@ -1,39 +1,40 @@
#!/usr/bin/env python #!/usr/bin/env python
import cms from setuptools import find_packages, setup
from setuptools import setup, find_packages
with open('README.md', 'r') as fh: import cms
with open("README.md", "r") as fh:
long_description = fh.read() long_description = fh.read()
setup( setup(
name = 'django-simplecms', name="django-simplecms",
description = 'Simple Django CMS', description="Simple Django CMS",
version = cms.__version__, version=cms.__version__,
author = 'Jaap Joris Vens', author="Jaap Joris Vens",
author_email = 'jj+cms@rtts.eu', author_email="jj+cms@rtts.eu",
url = 'https://github.com/rtts/django-simplecms', url="https://github.com/rtts/django-simplecms",
long_description = long_description, long_description=long_description,
long_description_content_type = 'text/markdown', long_description_content_type="text/markdown",
packages = find_packages(), packages=find_packages(),
entry_points = { entry_points={
'console_scripts': ['simplecms=cms.__main__:main'], "console_scripts": ["simplecms=cms.__main__:main"],
}, },
include_package_data = True, include_package_data=True,
classifiers = [ classifiers=[
'Programming Language :: Python :: 3', "Programming Language :: Python :: 3",
'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
'Operating System :: OS Independent', "Operating System :: OS Independent",
], ],
python_requires = '>=3.8', python_requires=">=3.8",
install_requires = [ install_requires=[
'django', "django",
'django-extensions', "django-extensions",
'django-embed-video', "django-embed-video",
'django-tidy', "django-tidy",
'easy-thumbnails', "easy-thumbnails",
'libsass', "libsass",
'markdown', "markdown",
'psycopg2', "psycopg2",
'pylibmc', "pylibmc",
], ],
) )