kopia lustrzana https://github.com/rtts/django-simplecms
Tidying up!
rodzic
0e221ccf33
commit
2382fd47f0
88
README.md
88
README.md
|
@ -3,13 +3,93 @@
|
|||
*A super simple but very extensible content management system for
|
||||
Django websites.*
|
||||
|
||||
This project provides the basic building blocks of *Pages* and
|
||||
*Sections* and all the views needed to display and edit them.
|
||||
SimpleCMS provides the reusable Django app `cms` which contains
|
||||
everything you need to create websites that can be easily edited by
|
||||
end users.
|
||||
|
||||
## How does it work?
|
||||
|
||||
Contrary to 'regular' Django websites, SimpleCMS allows you to write a
|
||||
view for each *section*, rather than for each *page* on your website.
|
||||
On which pages these sections appear, and in which order, is left to
|
||||
the content editor rather than the programmer. After authenticating,
|
||||
the editor can use the {page,section}{create,update} forms to fill the
|
||||
website with various types of content.
|
||||
|
||||
Here's an example `views.py` of an app using SimpleCMS:
|
||||
|
||||
from cms.views import SectionView
|
||||
from cms.decorators import section_view
|
||||
|
||||
@section_view
|
||||
class HelloWorld(SectionView):
|
||||
verbose_name = 'Hello world section'
|
||||
fields = ['content']
|
||||
template_name = 'hello.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super()get_context_data(**kwargs)
|
||||
context['message'] = 'Hello World!'
|
||||
|
||||
And here is the contents of `hello.html`:
|
||||
|
||||
<section type="helloworld">
|
||||
<h1>{{message}}</h1>
|
||||
{{section.content}}
|
||||
</section>
|
||||
|
||||
Everytime a section needs to be rendered, your section view will be
|
||||
called by SimpleCMS and return a response which will get inserted into
|
||||
the final rendered page.
|
||||
|
||||
## The "Edit" Interface
|
||||
|
||||
Somewhat like the Django Admin site, SimpleCMS comes with its own
|
||||
editing environment, albeit much simpler and only suitable for editing
|
||||
pages and sections. After authenticating, the content editor can click
|
||||
the "edit" button on any page of the website to alter, add or
|
||||
rearrange sections.
|
||||
|
||||
For each section, the section type can be selected from a dropdown
|
||||
menu. As you can see in `views.py` above, each section type comes with
|
||||
its own list of editable fields. Client-side javascript will hide/show
|
||||
the relevant fields based on the selected section type. All sections
|
||||
are stored in the same database table.
|
||||
|
||||
## Batteries included!
|
||||
|
||||
SimpleCMS has been specifically crafted to fit my own personal needs
|
||||
when building websites for customers. Therefore it includes a variety
|
||||
of useful template tags, default *Page* and *Section* models, and all
|
||||
the other boilerplate code needed for new projects.
|
||||
|
||||
One notable inclusion is the `eval` template tag. It will pass its
|
||||
argument first through Django's templating system and then through
|
||||
Markdown, making for instance the following possible. (Disclaimer: use
|
||||
with caution!)
|
||||
|
||||
Welcome to **{% now 'Y' %}!**
|
||||
|
||||
Another useful feature is the automatic compilation of `SCSS` files to
|
||||
`CSS` files using a custom middleware.
|
||||
|
||||
## Installation
|
||||
|
||||
Use the provided helper command `simplecms` to quickly setup a new
|
||||
project:
|
||||
|
||||
pip install https://github.com/rtts/django-simplecms.git
|
||||
simplecms mysite
|
||||
$ pip install https://github.com/rtts/django-simplecms.git
|
||||
$ simplecms mysite
|
||||
|
||||
After the project files have been created, initialize the database and
|
||||
create a superuser:
|
||||
|
||||
$ cd mysite
|
||||
$ sudo su postgres -c "createuser mysite; createdb -O mysite mysite"
|
||||
$ ./manage.py migrate
|
||||
$ ./manage.py createsuperuser
|
||||
|
||||
Finally, run the development server and visit
|
||||
http://localhost:8000/login/ in your browser to log in!
|
||||
|
||||
$ ./manage.py runserver
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
test -z $1 && echo "Please provide a project name!" && exit 1
|
||||
|
||||
mkdir -p $1 && cd $1
|
||||
pip3 freeze > requirements.txt
|
||||
example_dir=$(python3 -c 'import os,example;print(os.path.dirname(example.__file__))')
|
||||
cp -r $example_dir/{project,app,manage.py} .
|
||||
sed -i s/example/$1/ project/settings.py
|
||||
|
||||
# Assume the user has sudo access to postgres
|
||||
sudo su postgres -c "createuser $1; createdb -O $1 $1" || true
|
||||
|
||||
cat << EOF > .gitignore
|
||||
*.pyc
|
||||
__pycache__/
|
||||
EOF
|
||||
|
||||
./manage.py makemigrations app
|
||||
./manage.py migrate
|
||||
./manage.py createsuperuser
|
||||
./manage.py runserver --nostatic
|
|
@ -7,4 +7,4 @@ class CmsConfig(AppConfig):
|
|||
verbose_name = _('Content Management System')
|
||||
|
||||
def ready(self):
|
||||
autodiscover_modules('cms')
|
||||
autodiscover_modules('views')
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
test -z $1 && echo "Please provide a project name!" && exit 1
|
||||
mkdir "$1"
|
||||
cd "$1"
|
||||
pip3 freeze > requirements.txt
|
||||
example_dir=$(python3 -c 'import os,example;print(os.path.dirname(example.__file__))')
|
||||
cp -r "$example_dir" "$1"
|
||||
cp "$example_dir"/../manage.py .
|
||||
sed -i "s/example/$1/" manage.py
|
||||
cat << EOF > .gitignore
|
||||
*.pyc
|
||||
__pycache__/
|
||||
EOF
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Successfully created project "$1"!
|
||||
|
||||
Things to do next:
|
||||
- create a database
|
||||
- ./manage.py migrate
|
||||
- ./manage.py createsuperuser
|
||||
- ./manage.py runserver --nostatic
|
||||
|
||||
EOF
|
|
@ -12,6 +12,6 @@ def section_model(cls):
|
|||
|
||||
def section_view(cls):
|
||||
'''Decorator to register a view for a specific section'''
|
||||
registry.views_per_type[cls.__name__.lower()] = cls
|
||||
registry.section_class.TYPES.append((cls.__name__.lower(), cls.verbose_name))
|
||||
registry.view_per_type[cls.__name__.lower()] = cls
|
||||
registry.section_types.append((cls.__name__.lower(), cls.verbose_name))
|
||||
return cls
|
||||
|
|
27
cms/forms.py
27
cms/forms.py
|
@ -11,20 +11,18 @@ class PageForm(forms.ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.label_suffix = ''
|
||||
extra = 1 if self.instance.pk else 2
|
||||
|
||||
self.formsets = [forms.inlineformset_factory(
|
||||
parent_model = registry.page_class,
|
||||
model = registry.section_class,
|
||||
form = SectionForm,
|
||||
extra=extra,
|
||||
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,
|
||||
form_kwargs={'label_suffix': self.label_suffix},
|
||||
)]
|
||||
if not self.instance.pk:
|
||||
self.formsets[0][0].empty_permitted = False
|
||||
self.formsets[0][0].empty_permitted = True
|
||||
|
||||
def is_valid(self):
|
||||
return super().is_valid() and self.formsets[0].is_valid()
|
||||
|
@ -42,9 +40,6 @@ class PageForm(forms.ModelForm):
|
|||
formset = self.formsets[0]
|
||||
formset.instance = page
|
||||
formset.save()
|
||||
if page.slug and not page.sections.exists():
|
||||
page.delete()
|
||||
return None
|
||||
return page
|
||||
|
||||
class Meta:
|
||||
|
@ -58,12 +53,8 @@ class SectionForm(forms.ModelForm):
|
|||
super().__init__(*args, **kwargs)
|
||||
self.label_suffix = ''
|
||||
self.fields['DELETE'] = forms.BooleanField(label=_('Delete'), required=False)
|
||||
extra = 1 if self.instance.pk else 2
|
||||
|
||||
# Repopulate the 'choices' attribute of the type field from
|
||||
# the child model.
|
||||
self.fields['type'].choices = self._meta.model.TYPES
|
||||
self.fields['type'].initial = self._meta.model.TYPES[0][0]
|
||||
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
|
||||
|
@ -74,7 +65,7 @@ class SectionForm(forms.ModelForm):
|
|||
parent_model=registry.section_class,
|
||||
model=field.related_model,
|
||||
fields='__all__',
|
||||
extra=extra,
|
||||
extra=1,
|
||||
)(
|
||||
instance=self.instance,
|
||||
data=self.data if self.is_bound else None,
|
||||
|
|
|
@ -1,27 +1,35 @@
|
|||
import os
|
||||
from django.conf import settings
|
||||
from django.apps import apps
|
||||
from sass import compile
|
||||
from django.conf import settings
|
||||
|
||||
def locate(filename):
|
||||
for path, dirs, files in os.walk(os.getcwd()):
|
||||
for f in files:
|
||||
if f == filename:
|
||||
return os.path.join(path, filename)
|
||||
|
||||
class SassMiddleware:
|
||||
'''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'):
|
||||
_, staticdir, appname, css_file = request.path.split('/', maxsplit=3)
|
||||
app_path = apps.get_app_config(appname).path
|
||||
sass_file = css_file[:-4]
|
||||
css_path = os.path.join(app_path, staticdir, appname, css_file)
|
||||
sass_path = os.path.join(app_path, staticdir, appname, sass_file)
|
||||
map_path = css_path + '.map'
|
||||
if os.path.exists(sass_path):
|
||||
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:
|
||||
f.write(mapping)
|
||||
css_file = request.path.rsplit('/',1)[1]
|
||||
css_path = locate(css_file)
|
||||
if css_path:
|
||||
sass_path = css_path[:-4]
|
||||
map_path = css_path + '.map'
|
||||
if os.path.exists(sass_path):
|
||||
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:
|
||||
f.write(mapping)
|
||||
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
views_per_type = {}
|
||||
page_class = None
|
||||
section_class = None
|
||||
section_types = []
|
||||
view_per_type = {}
|
||||
|
||||
def get_types():
|
||||
return section_types
|
||||
|
||||
def get_view(section, request):
|
||||
'''Instantiate the registered view of a section'''
|
||||
return views_per_type[section.type](request)
|
||||
return view_per_type[section.type](request)
|
||||
|
||||
def get_fields_per_type():
|
||||
fields_per_type = {}
|
||||
for name, view in views_per_type.items():
|
||||
for name, view in view_per_type.items():
|
||||
fields_per_type[name] = ['title', 'type', 'number'] + view.fields
|
||||
return fields_per_type
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ form.cms {
|
|||
background: $background;
|
||||
border: 0.5px solid #ccc;
|
||||
clear: both;
|
||||
margin: 0.5em 0em;
|
||||
margin: 0.25em 0em;
|
||||
padding: 0.25em 0.5em;
|
||||
padding-top: 0;
|
||||
border-radius: 0.25em;
|
||||
|
@ -60,7 +60,9 @@ form.cms {
|
|||
}
|
||||
|
||||
div.formfield {
|
||||
margin: 0.25em 0;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
@ -68,6 +70,7 @@ form.cms {
|
|||
width: 77%;
|
||||
clear: none;
|
||||
float: left;
|
||||
margin-top: 0;
|
||||
}
|
||||
&.number {
|
||||
width: 20%;
|
||||
|
@ -108,6 +111,7 @@ form.cms {
|
|||
}
|
||||
|
||||
input, select, textarea {
|
||||
height: 32px;
|
||||
background: $background;
|
||||
color: black;
|
||||
border: 0.5px solid #ccc;
|
||||
|
@ -130,6 +134,7 @@ form.cms {
|
|||
font-size: 1rem;
|
||||
height: 15em;
|
||||
line-height: 1.5;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
select {
|
||||
padding-left: 3px;
|
||||
|
|
|
@ -34,7 +34,7 @@ form.cms {
|
|||
background: #fff6;
|
||||
border: 0.5px solid #ccc;
|
||||
clear: both;
|
||||
margin: 0.5em 0em;
|
||||
margin: 0.25em 0em;
|
||||
padding: 0.25em 0.5em;
|
||||
padding-top: 0;
|
||||
border-radius: 0.25em; }
|
||||
|
@ -44,13 +44,16 @@ form.cms {
|
|||
width: 50px;
|
||||
cursor: pointer; }
|
||||
form.cms div.formfield {
|
||||
margin: 0.25em 0;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
box-sizing: border-box; }
|
||||
form.cms div.formfield.type, form.cms div.formfield.number, form.cms div.formfield.slug {
|
||||
width: 77%;
|
||||
clear: none;
|
||||
float: left; }
|
||||
float: left;
|
||||
margin-top: 0; }
|
||||
form.cms div.formfield.number {
|
||||
width: 20%;
|
||||
float: right; }
|
||||
|
@ -76,6 +79,7 @@ form.cms {
|
|||
font-size: 12px !important;
|
||||
font-weight: 400 !important; }
|
||||
form.cms input, form.cms select, form.cms textarea {
|
||||
height: 32px;
|
||||
background: #fff6;
|
||||
color: black;
|
||||
border: 0.5px solid #ccc;
|
||||
|
@ -95,7 +99,8 @@ form.cms {
|
|||
form.cms textarea {
|
||||
font-size: 1rem;
|
||||
height: 15em;
|
||||
line-height: 1.5; }
|
||||
line-height: 1.5;
|
||||
overflow-y: hidden; }
|
||||
form.cms select {
|
||||
padding-left: 3px; }
|
||||
form.cms ul.errorlist {
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
"admin.scss"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "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"
|
||||
"mappings": "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"
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
{% if form.errors %}
|
||||
<div class="global_error">
|
||||
{% trans 'Please correct the error(s) below and save again' %}
|
||||
{{form.errors}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -75,8 +76,7 @@
|
|||
function resizeTextareas() {
|
||||
let tx = document.getElementsByTagName('textarea');
|
||||
for (let i = 0; i < tx.length; i++) {
|
||||
tx[i].setAttribute('style', 'height:0;overflow-y:hidden;');
|
||||
tx[i].style.height = (tx[i].scrollHeight) + 'px';
|
||||
tx[i].style.height = (tx[i].scrollHeight) + 1 + 'px';
|
||||
tx[i].addEventListener('input', function() {
|
||||
this.style.height = (this.scrollHeight) + 1 + 'px'; // why the 1???
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.0.2 on 2020-03-22 11:11
|
||||
# Generated by Django 3.0.2 on 2020-03-22 15:45
|
||||
|
||||
import cms.models
|
||||
from django.db import migrations, models
|
||||
|
@ -31,16 +31,6 @@ class Migration(migrations.Migration):
|
|||
},
|
||||
bases=(cms.models.Numbered, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SectionImage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('image', models.ImageField(upload_to='', verbose_name='Image')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['pk'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Section',
|
||||
fields=[
|
||||
|
@ -62,4 +52,15 @@ class Migration(migrations.Migration):
|
|||
},
|
||||
bases=(cms.models.Numbered, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SectionImage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('image', models.ImageField(upload_to='', verbose_name='Image')),
|
||||
('section', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='example.Section')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['pk'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import swapper
|
||||
from django.db import migrations
|
||||
Page = swapper.load_model('cms', 'Page')
|
||||
|
||||
def add_homepage(apps, schema_editor):
|
||||
if not Page.objects.exists():
|
||||
Page(slug='', title='Homepage', number=1).save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('cms', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_homepage, migrations.RunPython.noop),
|
||||
]
|
|
@ -19,7 +19,7 @@ class Section(BaseSection):
|
|||
page = models.ForeignKey(Page, verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT)
|
||||
|
||||
class SectionImage(models.Model):
|
||||
#section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE)
|
||||
section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE)
|
||||
image = models.ImageField(_('Image'))
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -26,17 +26,13 @@ except ImportError:
|
|||
DEBUG = True
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
def read(file):
|
||||
with open(file) as f:
|
||||
return f.read()
|
||||
def write(file, content):
|
||||
with open(file, 'w') as f:
|
||||
f.write(content)
|
||||
try:
|
||||
SECRET_KEY = read(KEYFILE)
|
||||
with open(KEYFILE) as f:
|
||||
SECRET_KEY = f.read()
|
||||
except IOError:
|
||||
SECRET_KEY = ''.join(random.choice(string.printable) for x in range(50))
|
||||
write(KEYFILE, SECRET_KEY)
|
||||
with open(KEYFILE, 'w') as f:
|
||||
f.write(SECRET_KEY)
|
||||
|
||||
INSTALLED_APPS = [
|
||||
PROJECT_NAME,
|
||||
|
@ -45,12 +41,13 @@ INSTALLED_APPS = [
|
|||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'cms',
|
||||
'embed_video',
|
||||
'easy_thumbnails',
|
||||
'django_extensions',
|
||||
]
|
||||
if not DEBUG:
|
||||
INSTALLED_APPS += ['django.contrib.staticfiles']
|
||||
|
||||
MIDDLEWARE = [
|
||||
'cms.middleware.SassMiddleware',
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
{% block title %}Awesome Website{% endblock %}
|
||||
|
||||
{% block extrahead %}
|
||||
<link rel="stylesheet" href="{% static 'app/hamburgers.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'app/main1.scss.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'hamburgers.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'main1.scss.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
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 django.utils.translation import gettext_lazy as _
|
||||
|
||||
@section_view
|
||||
class Text(SectionView):
|
||||
verbose_name = _('Text')
|
||||
fields = ['title', 'content']
|
||||
template_name = 'app/sections/text.html'
|
||||
fields = ['content']
|
||||
template_name = 'text.html'
|
||||
|
||||
@section_view
|
||||
class Images(SectionView):
|
||||
verbose_name = _('Images')
|
||||
fields = ['title', 'images']
|
||||
template_name = 'app/sections/images.html'
|
||||
verbose_name = _('Image(s)')
|
||||
fields = ['images']
|
||||
template_name = 'images.html'
|
||||
|
||||
@section_view
|
||||
class Video(SectionView):
|
||||
verbose_name = _('Video')
|
||||
fields = ['title', 'video']
|
||||
template_name = 'app/sections/video.html'
|
||||
fields = ['video']
|
||||
template_name = 'video.html'
|
||||
|
||||
@section_view
|
||||
class Contact(SectionFormView):
|
||||
verbose_name = _('Contact')
|
||||
fields = ['title']
|
||||
fields = []
|
||||
form_class = ContactForm
|
||||
success_url = '/thanks/'
|
||||
template_name = 'app/sections/contact.html'
|
||||
template_name = 'contact.html'
|
4
setup.py
4
setup.py
|
@ -3,13 +3,13 @@ from setuptools import setup, find_packages
|
|||
|
||||
setup(
|
||||
name = 'django-simplecms',
|
||||
version = '3.0.0',
|
||||
version = '1.0.0',
|
||||
url = 'https://github.com/rtts/django-simplecms',
|
||||
author = 'Jaap Joris Vens',
|
||||
author_email = 'jj@rtts.eu',
|
||||
license = 'GPL3',
|
||||
packages = find_packages(),
|
||||
scripts = ['bin/simplecms'],
|
||||
scripts = ['cms/bin/simplecms'],
|
||||
include_package_data = True,
|
||||
install_requires = [
|
||||
'django',
|
||||
|
|
Ładowanie…
Reference in New Issue