Skip to content
This repository has been archived by the owner on Dec 9, 2019. It is now read-only.

'flatblock' with default content feature #9

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions flatblocks/admin.py
Expand Up @@ -4,7 +4,8 @@

class FlatBlockAdmin(admin.ModelAdmin):
ordering = ['slug', ]
list_display = ('slug', 'header')
search_fields = ('slug', 'header', 'content')
list_display = ('slug', 'header', 'site', )
list_filter = ('site', )
search_fields = ('slug', 'header', 'content', 'site__domain', 'site__name', )

admin.site.register(FlatBlock, FlatBlockAdmin)
Binary file modified flatblocks/locale/de/LC_MESSAGES/django.mo
Binary file not shown.
Binary file modified flatblocks/locale/no/LC_MESSAGES/django.mo
Binary file not shown.
Binary file modified flatblocks/locale/ru/LC_MESSAGES/django.mo
Binary file not shown.
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):
# Removing unique constraint on 'FlatBlock', fields ['slug']
db.delete_unique('flatblocks_flatblock', ['slug'])

# Adding field 'FlatBlock.site'
db.add_column('flatblocks_flatblock', 'site',
self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='flatblocks', to=orm['sites.Site']),
keep_default=False)

# Adding index on 'FlatBlock', fields ['slug']
db.create_index('flatblocks_flatblock', ['slug'])

# Adding unique constraint on 'FlatBlock', fields ['slug', 'site']
db.create_unique('flatblocks_flatblock', ['slug', 'site_id'])

def backwards(self, orm):
# Removing unique constraint on 'FlatBlock', fields ['slug', 'site']
db.delete_unique('flatblocks_flatblock', ['slug', 'site_id'])

# Removing index on 'FlatBlock', fields ['slug']
db.delete_index('flatblocks_flatblock', ['slug'])

# Deleting field 'FlatBlock.site'
db.delete_column('flatblocks_flatblock', 'site_id')

# Adding unique constraint on 'FlatBlock', fields ['slug']
db.create_unique('flatblocks_flatblock', ['slug'])

models = {
'flatblocks.flatblock': {
'Meta': {'unique_together': "(('slug', 'site'),)", 'object_name': 'FlatBlock'},
'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'header': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flatblocks'", 'to': "orm['sites.Site']"}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}

complete_apps = ['flatblocks']
15 changes: 10 additions & 5 deletions flatblocks/models.py
@@ -1,3 +1,4 @@
from django.contrib.sites.models import Site
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
Expand All @@ -11,14 +12,22 @@ class FlatBlock(models.Model):
basically a piece of content with a given name (slug) and an optional
title (header) which you can, for example, use in a sidebar of a website.
"""
slug = models.CharField(max_length=255, unique=True,
slug = models.CharField(max_length=255, db_index=True,
verbose_name=_('Slug'),
help_text=_("A unique name used for reference in the templates"))
header = models.CharField(blank=True, null=True, max_length=255,
verbose_name=_('Header'),
help_text=_("An optional header for this content"))
content = models.TextField(verbose_name=_('Content'), blank=True,
null=True)
site = models.ForeignKey(Site, related_name='flatblocks', verbose_name=_('Site'))

class Meta:
verbose_name = _('Flat block')
verbose_name_plural = _('Flat blocks')
unique_together = (
('slug', 'site', ),
)

def __unicode__(self):
return u"%s" % (self.slug,)
Expand All @@ -27,7 +36,3 @@ def save(self, *args, **kwargs):
super(FlatBlock, self).save(*args, **kwargs)
# Now also invalidate the cache used in the templatetag
cache.delete('%s%s' % (CACHE_PREFIX, self.slug, ))

class Meta:
verbose_name = _('Flat block')
verbose_name_plural = _('Flat blocks')
8 changes: 8 additions & 0 deletions flatblocks/settings.py
@@ -1,5 +1,13 @@
from django.conf import settings
from django.core.cache import cache

CACHE_PREFIX = getattr(settings, 'FLATBLOCKS_CACHE_PREFIX', 'flatblocks_')
AUTOCREATE_STATIC_BLOCKS = getattr(settings,
'FLATBLOCKS_AUTOCREATE_STATIC_BLOCKS', False)

STRICT_DEFAULT_CHECK = getattr(settings,
'FLATBLOCKS_STRICT_DEFAULT_CHECK', False)
STRICT_DEFAULT_CHECK_UPDATE = getattr(settings,
'FLATBLOCKS_STRICT_DEFAULT_CHECK_UPDATE', False)

CACHE_TIMEOUT = getattr(settings, 'FLATBLOCKS_CACHE_TIMEOUT', cache.default_timeout)
159 changes: 140 additions & 19 deletions flatblocks/templatetags/flatblock_tags.py
Expand Up @@ -42,19 +42,21 @@
"""

from django import template
from django.template import loader
from django.db import models
from django.contrib.sites.models import Site
from django.core.cache import cache
# from django.db import models
from django.template import loader
from django.template import debug as template_debug

from flatblocks import settings
from flatblocks.models import FlatBlock

import logging


register = template.Library()
logger = logging.getLogger(__name__)

FlatBlock = models.get_model('flatblocks', 'flatblock')

class BasicFlatBlockWrapper(object):
def prepare(self, parser, token):
Expand All @@ -73,6 +75,16 @@ def prepare(self, parser, token):
self.cache_time = 0
self.tpl_name = None
tag_name, self.slug, args = tokens[0], tokens[1], tokens[2:]

try:
# Split the arguments in two sections, the "core" ones
# and the ones for default content feature
with_index = args.index('with-default')
default_args = args[with_index:]
args = args[:with_index]
except ValueError:
default_args = []

num_args = len(args)
if num_args == 0:
# Only the block name was specified
Expand All @@ -90,6 +102,39 @@ def prepare(self, parser, token):
self.tpl_name = args[2]
else:
raise template.TemplateSyntaxError, "%r tag should have between 1 and 4 arguments" % (tokens[0],)

if len(default_args) > 0:
# If we got arguments for default content, or, at least, the
# 'with-default' token was specified, parse until the end of
# the closing tag, and keep the parsed nodelist for later
# rendering
end_tag_name = 'end_%s' % tag_name
self.inner_nodelist = parser.parse((end_tag_name, ))
parser.delete_first_token()

if len(default_args) > 2:
raise template.TemplateSyntaxError(
u'Too many arguments for this block header')

# If an argument was specified after 'with-default', it is used
# as the flatblock's header

if len(default_args) == 2:
self.default_header = default_args[1]
if self.default_header[0] == self.default_header[-1] and \
self.default_header[0] in ('"', "'"):
self.default_header = self.default_header[1:-1]
self.default_header_is_variable = False
else:
self.default_header_is_variable = True
else:
self.default_header = None
self.default_header_is_variable = False
else:
self.default_header = None
self.default_header_is_variable = False
self.inner_nodelist = None

# Check to see if the slug is properly double/single quoted
if not (self.slug[0] == self.slug[-1] and self.slug[0] in ('"', "'")):
self.is_variable = True
Expand All @@ -108,19 +153,29 @@ def __call__(self, parser, token):
self.prepare(parser, token)
return FlatBlockNode(self.slug, self.is_variable, self.cache_time,
template_name=self.tpl_name,
tpl_is_variable=self.tpl_is_variable)
tpl_is_variable=self.tpl_is_variable,
default_header=self.default_header,
default_header_is_variable=self.default_header_is_variable,
default_content=self.inner_nodelist)

class PlainFlatBlockWrapper(BasicFlatBlockWrapper):
def __call__(self, parser, token):
self.prepare(parser, token)
return FlatBlockNode(self.slug, self.is_variable, self.cache_time, False)
return FlatBlockNode(
self.slug, self.is_variable, self.cache_time, False,
default_header=self.default_header,
default_header_is_variable=self.default_header_is_variable,
default_content=self.inner_nodelist,
)

do_get_flatblock = BasicFlatBlockWrapper()
do_plain_flatblock = PlainFlatBlockWrapper()

class FlatBlockNode(template.Node):
def __init__(self, slug, is_variable, cache_time=0, with_template=True,
template_name=None, tpl_is_variable=False):
template_name=None, tpl_is_variable=False,
default_header=None, default_header_is_variable=None,
default_content=None):
if template_name is None:
self.template_name = 'flatblocks/flatblock.html'
else:
Expand All @@ -133,58 +188,124 @@ def __init__(self, slug, is_variable, cache_time=0, with_template=True,
self.cache_time = cache_time
self.with_template = with_template

self.default_header_is_variable = default_header_is_variable
self.default_header = template.Variable(default_header)\
if default_header_is_variable \
else default_header
self.default_content = default_content

def render(self, context):
current_site = Site.objects.get_current()
if self.is_variable:
real_slug = template.Variable(self.slug).resolve(context)
else:
real_slug = self.slug

if isinstance(self.template_name, template.Variable):
real_template = self.template_name.resolve(context)
else:
real_template = self.template_name

if isinstance(self.default_header, template.Variable):
real_default_header = self.default_header.resolve(context)
else:
real_default_header = self.default_header

if isinstance(self.default_content,
(template.NodeList, template_debug.DebugNodeList)):
real_default_contents = self.default_content.render(context)
else:
real_default_contents = self.default_content

# Eventually we want to pass the whole context to the template so that
# users have the maximum of flexibility of what to do in there.
if self.with_template:
new_ctx = template.Context({})
new_ctx = template.Context()
new_ctx.update(context)
else:
new_ctx = None

try:
flatblock = None
if self.cache_time != 0:
cache_key = settings.CACHE_PREFIX + real_slug
flatblock = cache.get(cache_key)

if flatblock is None:
flatblock_created = False

# if flatblock's slug is hard-coded in template then it is
# safe and convenient to auto-create block if it doesn't exist.
# This behaviour can be configured using the
# FLATBLOCKS_AUTOCREATE_STATIC_BLOCKS setting
if self.is_variable or not settings.AUTOCREATE_STATIC_BLOCKS:
flatblock = FlatBlock.objects.get(slug=real_slug)
flatblock = FlatBlock.objects.get(slug=real_slug, site=current_site)
else:
flatblock, _ = FlatBlock.objects.get_or_create(
slug=real_slug,
defaults = {'content': real_slug}
)
# try:
# flatblock = FlatBlock.objects.get(slug=real_slug, site=current_site)
# except FlatBlock.DoesNotExist:
# flatblock = FlatBlock.objects.create(
# slug=real_slug,
# content=real_default_contents or real_slug,
# header=real_default_header,
# site=current_site
# )
# flatblock.save()
# flatblock_created = True
flatblock, flatblock_created = FlatBlock.objects.get_or_create(
slug=real_slug, site=current_site, defaults={
'content': real_default_contents or real_slug,
'header': real_default_header,
}
)

# If the flatblock exists, but its fields are empty, and
# the STRICT_DEFAULT_CHECK is True, then update the fields
# with the default contents.
flatblock_updated = False
if not flatblock_created and settings.STRICT_DEFAULT_CHECK:
if not flatblock.header and not real_default_header is None:
flatblock.header = real_default_header
flatblock_updated = True
if not flatblock.content and self.default_content:
flatblock.content = real_default_contents or real_slug
flatblock_updated = True

if flatblock_updated and settings.STRICT_DEFAULT_CHECK_UPDATE:
flatblock.save()

if self.cache_time != 0:
if self.cache_time is None or self.cache_time == 'None':
logger.debug("Caching %s for the cache's default timeout"
% (real_slug,))
cache.set(cache_key, flatblock)
cache.set(cache_key, flatblock, settings.CACHE_TIMEOUT)
else:
logger.debug("Caching %s for %s seconds" % (real_slug,
str(self.cache_time)))
cache.set(cache_key, flatblock, int(self.cache_time))
else:
logger.debug("Don't cache %s" % (real_slug,))

if self.with_template:
tmpl = loader.get_template(real_template)
new_ctx.update({'flatblock':flatblock})
return tmpl.render(new_ctx)
else:
return flatblock.content
return self.flatblock_output(real_template, flatblock, new_ctx)
except FlatBlock.DoesNotExist:
if real_default_contents:
flatblock = FlatBlock(
slug=real_slug,
content=real_default_contents,
header=real_default_header,
site=current_site,
)
return self.flatblock_output(real_template, flatblock, new_ctx)
return ''

def flatblock_output(self, template_name, flatblock, context=None):
if not self.with_template:
return flatblock.content
tmpl = loader.get_template(template_name)
context = context or {}
context.update({'flatblock': flatblock, })
return tmpl.render(template.Context(context))


register.tag('flatblock', do_get_flatblock)
register.tag('plain_flatblock', do_plain_flatblock)