Skip to content

Commit dd8a863

Browse files
committed
stacks initial
1 parent c9502de commit dd8a863

File tree

14 files changed

+317
-0
lines changed

14 files changed

+317
-0
lines changed

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ recursive-include cms/templates *
77
recursive-include cms/static *
88
recursive-include cms/plugins *
99
recursive-include menus/templates *
10+
recursive-include stacks/templates *
1011
recursive-include docs *
1112
recursive-exclude * *.pyc

docs/getting_started/tutorial.rst

+2
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ This includes django CMS itself as well as its dependenices and
6767
other highly recommended applications/libraries:
6868

6969
* ``'cms'``, django CMS itself
70+
* ``'stacks'``, for reusable content
7071
* ``'mptt'``, utilities for implementing a modified pre-order traversal tree
7172
* ``'menus'``, helper for model independent hierarchical website navigation
7273
* ``'south'``, intelligent schema and data migrations
7374
* ``'sekizai'``, for javascript and css management
7475

76+
7577
Also add any (or all) of the following plugins, depending on your needs:
7678

7779
* ``'cms.plugins.file'``

stacks/__init__.py

Whitespace-only changes.

stacks/admin.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.contrib import admin
2+
from stacks.models import Stack
3+
from cms.admin.placeholderadmin import PlaceholderAdmin
4+
5+
6+
class StackAdmin(PlaceholderAdmin):
7+
list_display = ('name', 'code',)
8+
search_fields = ('name', 'code',)
9+
10+
11+
admin.site.register(Stack, StackAdmin)

stacks/cms_plugins.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from django.utils.safestring import mark_safe
2+
from cms.plugin_pool import plugin_pool
3+
from cms.plugin_base import CMSPluginBase
4+
from django.utils.translation import ugettext_lazy as _
5+
from cms.plugin_rendering import render_plugins
6+
from stacks.fields import StackSearchField
7+
from stacks.models import StackLink
8+
from cms.plugins.utils import get_plugins
9+
10+
11+
class StackPlugin(CMSPluginBase):
12+
model = StackLink
13+
name = _("Stack")
14+
render_template = "cms/plugins/stacks.html"
15+
admin_preview = False
16+
17+
def render(self, context, instance, placeholder):
18+
# TODO: once we drop 2.3.x support we can just use the "render_plugin" templatetag
19+
# instead of rendering html here.
20+
plugins = get_plugins(context['request'], instance.stack.content)
21+
processors = ()
22+
html_content = mark_safe(u"".join(render_plugins(plugins, context, placeholder, processors)))
23+
context.update({
24+
'instance': instance,
25+
'placeholder': placeholder,
26+
'content': html_content,
27+
})
28+
return context
29+
30+
def formfield_for_dbfield(self, db_field, request=None, **kwargs):
31+
if db_field.name == "stack":
32+
return StackSearchField(**kwargs)
33+
return super(StackPlugin, self).formfield_for_dbfield(db_field, request=request, **kwargs)
34+
35+
36+
plugin_pool.register_plugin(StackPlugin)

stacks/fields.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# -*- coding: utf-8 -*-
2+
from django_select2.fields import AutoModelSelect2Field
3+
from stacks.models import Stack
4+
5+
6+
class StackSearchField(AutoModelSelect2Field):
7+
search_fields = ('name__icontains', 'code__icontains',)
8+
queryset = Stack.objects
9+
10+
def security_check(self, request, *args, **kwargs):
11+
user = request.user
12+
if user and not user.is_anonymous() and user.is_staff and user.has_perm('djangocms_stack.change_stack'):
13+
return True
14+
return False
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SOME DESCRIPTIVE TITLE.
2+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3+
# This file is distributed under the same license as the PACKAGE package.
4+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5+
#
6+
#, fuzzy
7+
msgid ""
8+
msgstr ""
9+
"Project-Id-Version: PACKAGE VERSION\n"
10+
"Report-Msgid-Bugs-To: \n"
11+
"POT-Creation-Date: 2012-12-10 11:31+0100\n"
12+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14+
"Language-Team: LANGUAGE <[email protected]>\n"
15+
"Language: \n"
16+
"MIME-Version: 1.0\n"
17+
"Content-Type: text/plain; charset=UTF-8\n"
18+
"Content-Transfer-Encoding: 8bit\n"
19+
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
20+
21+
#: cms_plugins.py:12
22+
msgid "Stack"
23+
msgstr ""
24+
25+
#: models.py:10
26+
msgid "stack name"
27+
msgstr ""
28+
29+
#: models.py:11
30+
msgid "Descriptive name to identify this stack. Not displayed to users."
31+
msgstr ""
32+
33+
#: models.py:13
34+
msgid "stack code"
35+
msgstr ""
36+
37+
#: models.py:14
38+
msgid "To render the stack in templates."
39+
msgstr ""
40+
41+
#: models.py:16
42+
msgid "stack content"
43+
msgstr ""
44+
45+
#: models.py:19 models.py:32
46+
msgid "stack"
47+
msgstr ""
48+
49+
#: models.py:20
50+
msgid "stacks"
51+
msgstr ""
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# SOME DESCRIPTIVE TITLE.
2+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3+
# This file is distributed under the same license as the PACKAGE package.
4+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5+
#
6+
#, fuzzy
7+
msgid ""
8+
msgstr ""
9+
"Project-Id-Version: PACKAGE VERSION\n"
10+
"Report-Msgid-Bugs-To: \n"
11+
"POT-Creation-Date: 2012-12-10 11:31+0100\n"
12+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14+
"Language-Team: LANGUAGE <[email protected]>\n"
15+
"Language: \n"
16+
"MIME-Version: 1.0\n"
17+
"Content-Type: text/plain; charset=UTF-8\n"
18+
"Content-Transfer-Encoding: 8bit\n"
19+
20+
#: cms_plugins.py:12
21+
msgid "Stack"
22+
msgstr ""
23+
24+
#: models.py:10
25+
msgid "stack name"
26+
msgstr ""
27+
28+
#: models.py:11
29+
msgid "Descriptive name to identify this stack. Not displayed to users."
30+
msgstr ""
31+
32+
#: models.py:13
33+
msgid "stack code"
34+
msgstr ""
35+
36+
#: models.py:14
37+
msgid "To render the stack in templates."
38+
msgstr ""
39+
40+
#: models.py:16
41+
msgid "stack content"
42+
msgstr ""
43+
44+
#: models.py:19 models.py:32
45+
msgid "stack"
46+
msgstr ""
47+
48+
#: models.py:20
49+
msgid "stacks"
50+
msgstr ""

stacks/migrations/0001_initial.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# -*- coding: utf-8 -*-
2+
import datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Adding model 'Stack'
12+
db.create_table('stacks_stack', (
13+
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14+
('name', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True)),
15+
('code', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, blank=True)),
16+
('content', self.gf('django.db.models.fields.related.ForeignKey')(related_name='stacks_contents', null=True, to=orm['cms.Placeholder'])),
17+
))
18+
db.send_create_signal('stacks', ['Stack'])
19+
20+
# Adding model 'StackLink'
21+
db.create_table('cmsplugin_stacklink', (
22+
('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
23+
('stack', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['stacks.Stack'])),
24+
))
25+
db.send_create_signal('stacks', ['StackLink'])
26+
27+
28+
def backwards(self, orm):
29+
# Deleting model 'Stack'
30+
db.delete_table('stacks_stack')
31+
32+
# Deleting model 'StackLink'
33+
db.delete_table('cmsplugin_stacklink')
34+
35+
36+
models = {
37+
'cms.cmsplugin': {
38+
'Meta': {'object_name': 'CMSPlugin'},
39+
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
40+
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 12, 10, 0, 0)'}),
41+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42+
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
43+
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
44+
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
45+
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
46+
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
47+
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
48+
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
49+
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
50+
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
51+
},
52+
'cms.placeholder': {
53+
'Meta': {'object_name': 'Placeholder'},
54+
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
55+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56+
'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
57+
},
58+
'stacks.stack': {
59+
'Meta': {'object_name': 'Stack'},
60+
'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'blank': 'True'}),
61+
'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'stacks_contents'", 'null': 'True', 'to': "orm['cms.Placeholder']"}),
62+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
63+
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
64+
},
65+
'stacks.stacklink': {
66+
'Meta': {'object_name': 'StackLink', 'db_table': "'cmsplugin_stacklink'", '_ormbases': ['cms.CMSPlugin']},
67+
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
68+
'stack': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stacks.Stack']"})
69+
}
70+
}
71+
72+
complete_apps = ['stacks']

stacks/migrations/__init__.py

Whitespace-only changes.

stacks/models.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import uuid
2+
from django.db import models
3+
from cms.models.fields import PlaceholderField
4+
from django.utils.translation import ugettext_lazy as _
5+
from cms.models.pluginmodel import CMSPlugin
6+
7+
8+
class Stack(models.Model):
9+
name = models.CharField(
10+
verbose_name=_(u'stack name'), max_length=255, blank=True, default='',
11+
help_text=_(u'Descriptive name to identify this stack. Not displayed to users.'))
12+
code = models.CharField(
13+
verbose_name=_(u'stack code'), max_length=255, unique=True, blank=True,
14+
help_text=_(u'To render the stack in templates.'))
15+
content = PlaceholderField(
16+
slotname=u'stack_content', verbose_name=_(u'stack content'), related_name='stacks_contents')
17+
18+
class Meta:
19+
verbose_name = _(u'stack')
20+
verbose_name_plural = _(u'stacks')
21+
22+
def __unicode__(self):
23+
return self.name
24+
25+
def clean(self):
26+
# TODO: check for clashes if the random code is already taken
27+
if not self.code:
28+
self.code = u'stack-%s' % uuid.uuid4()
29+
30+
31+
class StackLink(CMSPlugin):
32+
stack = models.ForeignKey(Stack, verbose_name=_(u'stack'))
33+
34+
def __unicode__(self):
35+
return self.stack.name
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{ content }}

stacks/templatetags/__init__.py

Whitespace-only changes.

stacks/templatetags/stack_tags.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from classytags.arguments import Argument
2+
from classytags.core import Tag, Options
3+
from django import template
4+
from django.utils.safestring import mark_safe
5+
from cms.plugin_rendering import render_plugins
6+
from cms.plugins.utils import get_plugins
7+
from stacks import models as stack_models
8+
from stacks.models import Stack
9+
10+
register = template.Library()
11+
12+
13+
class StackNode(Tag):
14+
name = 'stack'
15+
options = Options(
16+
Argument('code', required=True),
17+
'as',
18+
Argument('varname', required=False, resolve=False)
19+
)
20+
21+
def render_tag(self, context, code, varname):
22+
# TODO: language override (the reason this is not implemented, is that language selection is buried way
23+
# down somewhere in some method called in render_plugins. There it gets extracted from the request
24+
# and a language in request.GET always overrides everything.)
25+
if not code:
26+
# an empty string was passed in or the variable is not available in the context
27+
return ''
28+
# TODO: caching?
29+
if isinstance(code, Stack):
30+
stack = code
31+
else:
32+
stack, __ = stack_models.Stack.objects.get_or_create(code=code, defaults={'name': code})
33+
# TODO: once we drop 2.3.x support we can just use the "render_plugin" templatetag
34+
# instead of rendering html here.
35+
placeholder = stack.content
36+
plugins = get_plugins(context['request'], placeholder)
37+
processors = ()
38+
rendered_placeholder = mark_safe("".join(render_plugins(plugins, context, placeholder, processors)))
39+
if varname:
40+
context[varname] = rendered_placeholder
41+
rendered_placeholder = u''
42+
return rendered_placeholder
43+
44+
register.tag(StackNode)

0 commit comments

Comments
 (0)