doc; error in sections with object; update styles and templates

This commit is contained in:
bkfox 2016-06-01 20:07:22 +02:00
parent e0a268505e
commit ad58d3c332
10 changed files with 153 additions and 81 deletions

View File

@ -79,3 +79,15 @@ website, using instance of the Website class:
4. Change templates and css styles if needed.
# Generated content
## CSS
* **.meta**: metadata of any item (author, date, info, tags...)
* **.info**: used to render extra information, usually in lists
The following classes are used for sections (on the section container) and page-wide views (on the <main> tag):
* **.section**: associated to all sections
* **.section_*class***: associated to all section, where name is the name of the classe used to generate the section;
* **.list**: for lists (sections and general list)
* **.detail**: for the detail page view

View File

@ -14,6 +14,7 @@ import bleach
from taggit.managers import TaggableManager
from aircox.cms import routes
from aircox.cms import settings
class Comment(models.Model):
@ -64,6 +65,10 @@ class Comment(models.Model):
attributes=settings.AIRCOX_CMS_BLEACH_COMMENT_ATTRS
)
def save(self, make_safe = True, *args, **kwargs):
if make_safe:
self.make_safe()
return super().save(*args, **kwargs)
class Post (models.Model):
@ -120,19 +125,23 @@ class Post (models.Model):
search_fields = [ 'title', 'content' ]
@classmethod
def children_of(cl, thread, queryset = None):
def get_with_thread(cl, thread = None, queryset = None,
thread_model = None, thread_id = None):
"""
Return children of the given thread of the cl's type. If queryset
is not given, use cl.objects as starting queryset.
Return posts of the cl's type that are children of the given thread.
"""
if not queryset:
queryset = cl.objects
thread_type = ContentType.objects.get_for_model(thread)
qs = queryset.filter(
thread_id = thread.pk,
thread_type__pk = thread_type.id
if thread:
thread_model = type(thread)
thread_id = thread.id
thread_model = ContentType.objects.get_for_model(thread_model)
return queryset.filter(
thread_id = thread_id,
thread_type__pk = thread_model.id
)
return qs
def get_comments(self):
"""
@ -176,6 +185,11 @@ class Post (models.Model):
)
self.tags = [ bleach.clean(tag, tags=[]) for tag in self.tags.all() ]
def save(self, make_safe = True, *args, **kwargs):
if make_safe:
self.make_safe()
return super().save(*args, **kwargs)
class Meta:
abstract = True

View File

@ -23,14 +23,14 @@ class Route:
url_args = [] # arguments passed from the url [ (name : regex),... ]
@classmethod
def get_queryset(cl, website, model, request, **kwargs):
def get_queryset(cl, model, request, **kwargs):
"""
Called by the view to get the queryset when it is needed
"""
pass
@classmethod
def get_object(cl, website, model, request, **kwargs):
def get_object(cl, model, request, **kwargs):
"""
Called by the view to get the object when it is needed
"""
@ -76,7 +76,7 @@ class DetailRoute(Route):
]
@classmethod
def get_object(cl, website, model, request, pk, **kwargs):
def get_object(cl, model, request, pk, **kwargs):
return model.objects.get(pk = int(pk))
@ -84,7 +84,7 @@ class AllRoute(Route):
name = 'all'
@classmethod
def get_queryset(cl, website, model, request, **kwargs):
def get_queryset(cl, model, request, **kwargs):
return model.objects.all()
@classmethod
@ -108,19 +108,30 @@ class ThreadRoute(Route):
('pk', '[0-9]+'),
]
@classmethod
def get_queryset(cl, website, model, request, thread_model, pk, **kwargs):
def get_thread(cl, model, thread_model, pk=None):
"""
Return a model if not pk, otherwise the model element of given id
"""
if type(thread_model) is str:
thread_model = website.registry.get(thread_model)
thread_model = model._website.registry.get(thread_model)
if not thread_model or not pk:
return thread_model
return thread_model.objects.get(pk=pk)
if not thread_model:
return
thread_model = ContentType.objects.get_for_model(thread_model)
return model.objects.filter(
thread_type = thread_model,
thread_id = int(pk)
)
@classmethod
def get_queryset(cl, model, request, thread_model, pk, **kwargs):
thread = cl.get_thread(model, thread_model, pk)
return model.get_with_thread(thread_model = thread, thread_id = pk)
@classmethod
def get_title(cl, model, request, thread_model, pk, **kwargs):
return _('%(name)s: %(model)s') % {
'model': model._meta.verbose_name_plural,
'name': cl.get_thread(model, thread_model, pk).title,
}
class DateRoute(Route):
@ -132,22 +143,32 @@ class DateRoute(Route):
]
@classmethod
def get_queryset(cl, website, model, request, year, month, day, **kwargs):
def get_queryset(cl, model, request, year, month, day, **kwargs):
return model.objects.filter(
date__year = int(year),
date__month = int(month),
date__day = int(day),
)
@classmethod
def get_title(cl, model, request, year, month, day, **kwargs):
return _('%(model)s of %(year)/%(month)/%(day)') % {
'model': model._meta.verbose_name_plural,
'year': year,
'month': month,
'day': day
}
class SearchRoute(Route):
name = 'search'
@classmethod
def get_queryset(cl, website, model, request, **kwargs):
def get_queryset(cl, model, request, **kwargs):
q = request.GET.get('q') or ''
qs = None
## TODO: by tag
for search_field in model.search_fields or []:
r = models.Q(**{ search_field + '__icontains': q })
if qs: qs = qs | r
@ -155,5 +176,10 @@ class SearchRoute(Route):
return model.objects.filter(qs).distinct()
## TODO: by tag
@classmethod
def get_title(cl, model, request, **kwargs):
return _('Search "%(search)s" in %(model)s') % {
'model': model._meta.verbose_name_plural,
'search': request.GET.get('q') or '',
}

View File

@ -39,8 +39,7 @@ class Section(View):
* header: header of the section
* footer: footer of the section
* object_required: if true and not object has been given, gets angry
* object: (can be persistent) related object
* force_object: (can be persistent) related object
"""
template_name = 'aircox/cms/section.html'
@ -54,6 +53,7 @@ class Section(View):
header = ''
footer = ''
object = None
force_object = None
def __init__ (self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -85,8 +85,7 @@ class Section(View):
}
def get(self, request, object=None, **kwargs):
if not self.object:
self.object = object
self.object = self.force_object or object
self.request = request
self.kwargs = kwargs
@ -146,6 +145,7 @@ class ListItem:
author = None
date = None
image = None
info = None
detail_url = None
css_class = None
@ -153,10 +153,10 @@ class ListItem:
def __init__ (self, post = None, **kwargs):
if post:
self.update_from(post)
self.update(post)
self.__dict__.update(**kwargs)
def update_from(self, post):
def update(self, post):
"""
Update empty fields using the given post
"""
@ -177,7 +177,6 @@ class List(Section):
* object_list: force an object list to be used
* url: url to the list in full page
* message_empty: message to print when list is empty (if not hiding)
* fields: fields of the items to render
* image_size: size of the images
* truncate: number of words to keep in content (0 = full content)
@ -188,7 +187,7 @@ class List(Section):
url = None
message_empty = _('nothing')
fields = [ 'date', 'time', 'image', 'title', 'content' ]
fields = [ 'date', 'time', 'image', 'title', 'content', 'info' ]
image_size = '64x64'
truncate = 16
@ -222,7 +221,9 @@ class Comments(List):
Section used to render comment form and comments list. It renders the
form and comments, and save them.
"""
css_class="comments"
title=_('Comments')
css_class='comments'
paginate_by = 0
truncate = 0
fields = [ 'date', 'time', 'author', 'content' ]
@ -259,11 +260,10 @@ class Comments(List):
"""
Forward data to this view
"""
# TODO: comment satanize
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.thread = self.object
comment.thread = object
comment.published = view.website.auto_publish_comments
comment.save()

View File

@ -4,7 +4,6 @@ body {
margin: 0;
}
.page {
display: flex;
}
@ -52,35 +51,34 @@ main .section {
/** comments **/
.comment_form label {
display: none;
}
.comment_form input:not([type=checkbox]),
.comment_form textarea {
.comments form input:not([type=checkbox]),
.comments form textarea {
display: inline-block;
width: calc(100% - 5em);
width: 100%;
max-height: 6em;
margin: 0.2em 0em;
padding: 0.2em;
}
.comment_form input[type=checkbox],
.comment_form button[type=submit] {
max-width: 4em;
.comments form input[type=checkbox],
.comments form button[type=submit] {
vertical-align:bottom;
margin: 0.2em 0em;
text-align: center;
}
.comment_form .extra {
.comments form button[type=submit] {
float: right;
}
.comments form #show_more:not(:checked) ~ .extra {
display: none;
}
.comment_form input[type="checkbox"]:checked + .extra {
display: block;
.comments label[for="show_more"] {
font-size: 0.8em;
}

View File

@ -7,18 +7,22 @@
{% if comment_form %}
{% with comment_form as form %}
{{ form.non_field_errors }}
<form action="" method="POST" class="comment_form">
<form action="" method="POST">
{% csrf_token %}
{% render_honeypot_field "hp_website" %}
<div>
{{ form.author.errors }}
{{ form.author }}
<input type="checkbox" value="1">
<div class="extra">
{{ form.email.errors }}
{{ form.email }}
{{ form.url.errors }}
{{ form.url }}
<div>
<input type="checkbox" value="1" id="show_more">
<label for="show_more">{% trans "show more options" %}</label>
<div class="extra">
{{ form.email.errors }}
{{ form.email }}
{{ form.url.errors }}
{{ form.url }}
</div>
</div>
</div>
<div>

View File

@ -17,7 +17,6 @@
<a href="{{ item.detail_url }}">
{% endif %}
{% if 'date' in list.fields or 'time' in list.fields or 'author' in list.fields %}
<div class="meta">
{% if item.date and 'date' in list.fields or 'time' in list.fields %}
<time datetime="{{ item.date }}">
@ -38,26 +37,33 @@
{{ item.author }}
</span>
{% endif %}
{% if item.info and 'info' in list.fields %}
<span class="info">
{{ item.info }}
</span>
{% endif %}
</div>
{% endif %}
{% if 'image' in list.fields and item.image %}
<img src="{% thumbnail item.image list.image_size crop %}">
{% endif %}
{% if 'title' in list.fields %}
<h2 class="title">{{ item.title }}</h2>
{% endif %}
{% if 'content' in list.fields %}
<div class="content">
{% if list.truncate %}
{{ item.content|striptags|truncatewords:list.truncate }}
{% else %}
{{ item.content|striptags }}
{% if 'title' in list.fields and item.title %}
<h2 class="title">{{ item.title }}</h2>
{% endif %}
{% if 'content' in list.fields and item.content %}
<div class="text">
{% if list.truncate %}
{{ item.content|striptags|truncatewords:list.truncate }}
{% else %}
{{ item.content|striptags }}
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
{% if item.detail_url %}
</a>
@ -72,8 +78,9 @@
{% if page_obj or list.url %}
<nav>
{% if not page_obj or embed %}
<a href="{{list.url}}" title={% trans "More elements" %}>&#8690;</a>
{% else %}
{% comment %}link to show more elements of the list{% endcomment %}
<a href="{{list.url}}">{% trans "&#8226;&#8226;&#8226;" %}</a>
{% elif page_obj.paginator.num_pages > 1 %}
{% with page_obj.paginator.num_pages as num_pages %}
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">previous</a>

View File

@ -1,5 +1,5 @@
<{{ tag }} class="{{ css_class }}"
<{{ tag }} {% if css_class %} class="{{ css_class }}" {% endif %}
{% for k, v in attrs.items %}
{{ k }} = "{{ v|addslashes }}"
{% endfor %} >
@ -15,7 +15,7 @@
{% block header %}
{% if header %}
<header>
{{ header }}
{{ header|safe }}
</header>
{% endif %}
{% endblock %}
@ -27,7 +27,7 @@
{% block footer %}
{% if footer %}
<footer>
{{ footer }}
{{ footer|safe }}
</footer>
{% endif %}
{% endblock %}

View File

@ -32,7 +32,10 @@
{{ menus.left|safe }}
{% endif %}
<main>
<main {% if css_class %} class="{{ css_class }}" {% endif %}
{% for k, v in attrs.items %}
{{ k }} = "{{ v|addslashes }}"
{% endfor %} >
{% endif %}
{% if messages %}
<ul class="messages">

View File

@ -49,7 +49,7 @@ class PostListView(PostBaseView, ListView):
"""
template_name = 'aircox/cms/list.html'
allow_empty = True
paginate_by = 3
paginate_by = 25
model = None
route = None
@ -64,11 +64,13 @@ class PostListView(PostBaseView, ListView):
def get_queryset(self):
if self.route:
qs = self.route.get_queryset(self.website, self.model, self.request,
qs = self.route.get_queryset(self.model, self.request,
**self.kwargs)
else:
qs = self.queryset or self.model.objects.all()
qs = qs.filter(published = True)
query = self.request.GET
if query.get('exclude'):
qs = qs.exclude(id = int(query['exclude']))
@ -93,11 +95,16 @@ class PostListView(PostBaseView, ListView):
context = super().get_context_data(**kwargs)
context.update(self.get_base_context(**kwargs))
title = self.title if self.title else \
self.route and self.route.get_title(self.model, self.request,
**self.kwargs)
if self.title:
title = self.title
else:
title = self.route and \
self.route.get_title(self.model, self.request,
**self.kwargs)
context['title'] = title
context['base_template'] = 'aircox/cms/website.html'
context['css_class'] = 'list'
if not self.list:
import aircox.cms.sections as sections
@ -153,6 +160,7 @@ class PostDetailView(DetailView, PostBaseView):
section.get(request = self.request, **kwargs)
for section in self.sections
])
context['css_class'] = 'detail'
return context
def post(self, request, *args, **kwargs):
@ -165,7 +173,7 @@ class PostDetailView(DetailView, PostBaseView):
self.comments = section
self.object = self.get_object()
self.comments.post(self, request, object)
self.comments.post(self, request, self.object)
return self.get(request, *args, **kwargs)