Merge branch 'cms-features'
* cms-features: update theme and dua update cms/boxes to pycon parity update debug toolbar adding in basic nav get object or 404 on page objects move cms slug down to root url remove menu add side nav with sibling pages rename block add sibling pages to context add django-mptt to requirements remove mptt driven menu hook up django reversion adding menu context processor and hooking up template add ordering to menus and pages adding prototype cms app with pages and menu
This commit is contained in:
commit
0430f166fe
34 changed files with 632 additions and 5 deletions
|
@ -23,4 +23,9 @@ django_compressor==1.2a1
|
|||
-e git+git://github.com/pinax/pinax-theme-bootstrap-account.git@8fc34ba4309fc1edd12e5836cc1398c3f9597e6d#egg=pinax-theme-bootstrap-account
|
||||
-e git+git://github.com/pinax/django-user-accounts.git@fd08c676ae71d0b9d7353ddee123deb751d6ee15#egg=django-user-accounts
|
||||
|
||||
django-forms-bootstrap==2.0.3.post1
|
||||
django-mptt==0.5.2
|
||||
django-taggit==0.9.3
|
||||
django-reversion==1.6.1
|
||||
django-markitup==1.0.0
|
||||
markdown==2.1.1
|
||||
django-sitetree==0.6
|
||||
|
|
0
symposion/boxes/__init__.py
Normal file
0
symposion/boxes/__init__.py
Normal file
6
symposion/boxes/admin.py
Normal file
6
symposion/boxes/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from symposion.boxes.models import Box
|
||||
|
||||
|
||||
admin.site.register(Box)
|
20
symposion/boxes/authorization.py
Normal file
20
symposion/boxes/authorization.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from django.conf import settings
|
||||
|
||||
from symposion.boxes.utils import load_path_attr
|
||||
|
||||
|
||||
def default_can_edit(request, *args, **kwargs):
|
||||
"""
|
||||
This is meant to be overridden in your project per domain specific
|
||||
requirements.
|
||||
"""
|
||||
return request.user.is_staff or request.user.is_superuser
|
||||
|
||||
|
||||
def load_can_edit():
|
||||
import_path = getattr(settings, "BOXES_CAN_EDIT_CALLABLE", None)
|
||||
|
||||
if import_path is None:
|
||||
return default_can_edit
|
||||
|
||||
return load_path_attr(import_path)
|
10
symposion/boxes/forms.py
Normal file
10
symposion/boxes/forms.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from django import forms
|
||||
|
||||
from symposion.boxes.models import Box
|
||||
|
||||
|
||||
class BoxForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Box
|
||||
fields = ["content"]
|
22
symposion/boxes/models.py
Normal file
22
symposion/boxes/models.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import datetime
|
||||
|
||||
from django.db import models
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from markitup.fields import MarkupField
|
||||
|
||||
|
||||
class Box(models.Model):
|
||||
|
||||
label = models.CharField(max_length=100, db_index=True)
|
||||
content = MarkupField(blank=True)
|
||||
|
||||
created_by = models.ForeignKey(User, related_name="boxes")
|
||||
last_updated_by = models.ForeignKey(User, related_name="updated_boxes")
|
||||
|
||||
def __unicode__(self):
|
||||
return self.label
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "boxes"
|
0
symposion/boxes/templatetags/__init__.py
Normal file
0
symposion/boxes/templatetags/__init__.py
Normal file
41
symposion/boxes/templatetags/boxes_tags.py
Normal file
41
symposion/boxes/templatetags/boxes_tags.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from django import template
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template.defaulttags import kwarg_re
|
||||
|
||||
from symposion.boxes.models import Box
|
||||
from symposion.boxes.forms import BoxForm
|
||||
from symposion.boxes.authorization import load_can_edit
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag("boxes/box.html", takes_context=True)
|
||||
def box(context, label, show_edit=True, *args, **kwargs):
|
||||
|
||||
request = context["request"]
|
||||
can_edit = load_can_edit()(request, *args, **kwargs)
|
||||
|
||||
try:
|
||||
box = Box.objects.get(label=label)
|
||||
except Box.DoesNotExist:
|
||||
box = None
|
||||
|
||||
if can_edit and show_edit:
|
||||
form = BoxForm(instance=box, prefix=label)
|
||||
form_action = reverse("box_edit", args=[label])
|
||||
else:
|
||||
form = None
|
||||
form_action = None
|
||||
|
||||
return {
|
||||
"request": request,
|
||||
"label": label,
|
||||
"box": box,
|
||||
"form": form,
|
||||
"form_action": form_action,
|
||||
}
|
6
symposion/boxes/urls.py
Normal file
6
symposion/boxes/urls.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.conf.urls.defaults import url, patterns
|
||||
|
||||
|
||||
urlpatterns = patterns("symposion.boxes.views",
|
||||
url(r"^([-\w]+)/edit/$", "box_edit", name="box_edit"),
|
||||
)
|
19
symposion/boxes/utils.py
Normal file
19
symposion/boxes/utils.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from django.core.exceptions import ImproperlyConfigured
|
||||
try:
|
||||
from django.utils.importlib import import_module
|
||||
except ImportError:
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
def load_path_attr(path):
|
||||
i = path.rfind(".")
|
||||
module, attr = path[:i], path[i+1:]
|
||||
try:
|
||||
mod = import_module(module)
|
||||
except ImportError, e:
|
||||
raise ImproperlyConfigured("Error importing %s: '%s'" % (module, e))
|
||||
try:
|
||||
attr = getattr(mod, attr)
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured("Module '%s' does not define a '%s'" % (module, attr))
|
||||
return attr
|
45
symposion/boxes/views.py
Normal file
45
symposion/boxes/views.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from django.http import HttpResponseForbidden
|
||||
from django.shortcuts import redirect
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from symposion.boxes.authorization import load_can_edit
|
||||
from symposion.boxes.forms import BoxForm
|
||||
from symposion.boxes.models import Box
|
||||
|
||||
|
||||
# @@@ problem with this is that the box_edit.html and box_create.html won't have domain objects in context
|
||||
def get_auth_vars(request):
|
||||
auth_vars = {}
|
||||
if request.method == "POST":
|
||||
keys = [k for k in request.POST.keys() if k.startswith("boxes_auth_")]
|
||||
for key in keys:
|
||||
auth_vars[key.replace("boxes_auth_", "")] = request.POST.get(key)
|
||||
auth_vars["user"] = request.user
|
||||
return auth_vars
|
||||
|
||||
|
||||
@require_POST
|
||||
def box_edit(request, label):
|
||||
|
||||
if not load_can_edit()(request, **get_auth_vars(request)):
|
||||
return HttpResponseForbidden()
|
||||
|
||||
next = request.GET.get("next")
|
||||
|
||||
try:
|
||||
box = Box.objects.get(label=label)
|
||||
except Box.DoesNotExist:
|
||||
box = None
|
||||
|
||||
form = BoxForm(request.POST, instance=box, prefix=label)
|
||||
|
||||
if form.is_valid():
|
||||
if box is None:
|
||||
box = form.save(commit=False)
|
||||
box.label = label
|
||||
box.created_by = request.user
|
||||
box.last_updated_by = request.user
|
||||
box.save()
|
||||
else:
|
||||
form.save()
|
||||
return redirect(next)
|
0
symposion/cms/__init__.py
Normal file
0
symposion/cms/__init__.py
Normal file
6
symposion/cms/admin.py
Normal file
6
symposion/cms/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Page
|
||||
|
||||
|
||||
admin.site.register(Page)
|
16
symposion/cms/forms.py
Normal file
16
symposion/cms/forms.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from django import forms
|
||||
|
||||
from markitup.widgets import MarkItUpWidget
|
||||
|
||||
from .models import Page
|
||||
|
||||
|
||||
class PageForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Page
|
||||
fields = ["title", "body", "path"]
|
||||
widgets = {
|
||||
"body": MarkItUpWidget(),
|
||||
"path": forms.HiddenInput(),
|
||||
}
|
9
symposion/cms/managers.py
Normal file
9
symposion/cms/managers.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
|
||||
class PublishedPageManager(models.Manager):
|
||||
|
||||
def get_query_set(self):
|
||||
qs = super(PublishedPageManager, self).get_query_set()
|
||||
return qs.filter(publish_date__lte=datetime.now())
|
53
symposion/cms/models.py
Normal file
53
symposion/cms/models.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from markitup.fields import MarkupField
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
import reversion
|
||||
|
||||
from .managers import PublishedPageManager
|
||||
|
||||
|
||||
class Page(models.Model):
|
||||
|
||||
STATUS_CHOICES = (
|
||||
(1, _("Draft")),
|
||||
(2, _("Public")),
|
||||
)
|
||||
|
||||
title = models.CharField(max_length=100)
|
||||
path = models.CharField(max_length=100, unique=True)
|
||||
body = MarkupField()
|
||||
status = models.IntegerField(choices=STATUS_CHOICES, default=2)
|
||||
publish_date = models.DateTimeField(default=datetime.now)
|
||||
created = models.DateTimeField(editable=False, default=datetime.now)
|
||||
updated = models.DateTimeField(editable=False, default=datetime.now)
|
||||
tags = TaggableManager(blank=True)
|
||||
|
||||
published = PublishedPageManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
@models.permalink
|
||||
def get_absolute_url(self):
|
||||
return ("cms_page", [self.path])
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.updated = datetime.now()
|
||||
super(Page, self).save(*args, **kwargs)
|
||||
|
||||
def clean_fields(self, exclude=None):
|
||||
super(Page, self).clean_fields(exclude)
|
||||
if not re.match(settings.SYMPOSION_PAGE_REGEX, self.path):
|
||||
raise ValidationError({"path": [_("Path can only contain letters, numbers and hyphens and end with /"),]})
|
||||
|
||||
|
||||
reversion.register(Page)
|
8
symposion/cms/urls.py
Normal file
8
symposion/cms/urls.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.conf.urls.defaults import url, patterns
|
||||
|
||||
PAGE_RE = r"(([\w-]{1,})(/[\w-]{1,})*)/"
|
||||
|
||||
urlpatterns = patterns("symposion.cms.views",
|
||||
url(r"^(?P<path>%s)_edit/$" % PAGE_RE, "page_edit", name="cms_page_edit"),
|
||||
url(r"^(?P<path>%s)$" % PAGE_RE, "page", name="cms_page"),
|
||||
)
|
59
symposion/cms/views.py
Normal file
59
symposion/cms/views.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
from django.http import Http404
|
||||
from django.shortcuts import render, redirect
|
||||
from django.template import RequestContext
|
||||
|
||||
from .models import Page
|
||||
from .forms import PageForm
|
||||
|
||||
|
||||
def can_edit(user):
|
||||
if user.is_staff or user.is_superuser:
|
||||
return True
|
||||
if user.has_perm("cms.change_page"):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def page(request, path):
|
||||
|
||||
editable = can_edit(request.user)
|
||||
try:
|
||||
page = Page.published.get(path=path)
|
||||
except Page.DoesNotExist:
|
||||
if editable:
|
||||
return redirect("cms_page_edit", path=path)
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
return render(request, "cms/page_detail.html", {
|
||||
"page": page,
|
||||
"editable": editable,
|
||||
})
|
||||
|
||||
|
||||
def page_edit(request, path):
|
||||
|
||||
if not can_edit(request.user):
|
||||
raise Http404
|
||||
|
||||
try:
|
||||
page = Page.published.get(path=path)
|
||||
except Page.DoesNotExist:
|
||||
page = None
|
||||
|
||||
if request.method == "POST":
|
||||
form = PageForm(request.POST, instance=page)
|
||||
if form.is_valid():
|
||||
page = form.save(commit=False)
|
||||
page.path = path
|
||||
page.save()
|
||||
return redirect(page)
|
||||
else:
|
||||
print form.errors
|
||||
else:
|
||||
form = PageForm(instance=page, initial={"path": path})
|
||||
|
||||
return render(request, "cms/page_edit.html", {
|
||||
"path": path,
|
||||
"form": form
|
||||
})
|
|
@ -106,6 +106,8 @@ MIDDLEWARE_CLASSES = [
|
|||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django_openid.consumer.SessionConsumer",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.transaction.TransactionMiddleware",
|
||||
"reversion.middleware.RevisionMiddleware",
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
]
|
||||
|
||||
|
@ -151,7 +153,12 @@ INSTALLED_APPS = [
|
|||
"django_openid",
|
||||
"timezones",
|
||||
"metron",
|
||||
"markitup",
|
||||
"taggit",
|
||||
"mptt",
|
||||
"reversion",
|
||||
"easy_thumbnails",
|
||||
"sitetree",
|
||||
|
||||
# Pinax
|
||||
"account",
|
||||
|
@ -160,6 +167,8 @@ INSTALLED_APPS = [
|
|||
"symposion.about",
|
||||
"symposion.sponsorship",
|
||||
"symposion.conference",
|
||||
"symposion.cms",
|
||||
"symposion.boxes",
|
||||
]
|
||||
|
||||
FIXTURE_DIRS = [
|
||||
|
@ -192,8 +201,14 @@ DEBUG_TOOLBAR_CONFIG = {
|
|||
"INTERCEPT_REDIRECTS": False,
|
||||
}
|
||||
|
||||
MARKITUP_FILTER = ("markdown.markdown", {"safe_mode": True})
|
||||
MARKITUP_SET = "markitup/sets/markdown"
|
||||
MARKITUP_SKIN = "markitup/skins/simple"
|
||||
|
||||
CONFERENCE_ID = 1
|
||||
|
||||
SYMPOSION_PAGE_REGEX = r"(([\w-]{1,})(/[\w-]{1,})*)/"
|
||||
|
||||
# local_settings.py can be used to override environment-specific settings
|
||||
# like database and email that differ between development and production.
|
||||
try:
|
||||
|
|
31
symposion/templates/boxes/box.html
Normal file
31
symposion/templates/boxes/box.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% load markitup_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% if form %}
|
||||
<div id="edit_{{ label }}" class="modal fade hide">
|
||||
<form id="edit_form_{{ label }}" accept-charset="UTF-8" class="modal-form" method="POST" action="{{ form_action }}?next={{ request.path }}">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3>{% trans "Editing content:" %} {{ label }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% csrf_token %}
|
||||
{{ form.content }}
|
||||
|
||||
{% markitup_editor form.content.auto_id %}
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn" data-dismiss="modal">Close</a>
|
||||
<button type="submit" class="btn btn-primary">Save changes</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="content_{{ label }}" class="content-box {% if form %}editable{% endif %}">
|
||||
{% if form %}
|
||||
<a href="#edit_{{ label }}" data-toggle="modal" class="btn edit-toggle"><i class="icon-pencil"></i></a>
|
||||
{% endif %}
|
||||
{{ box.content|safe }}
|
||||
</div>
|
22
symposion/templates/cms/page_detail.html
Normal file
22
symposion/templates/cms/page_detail.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "site_base.html" %}
|
||||
|
||||
{% load sitetree %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block body_class %}cms-page{% endblock %}
|
||||
|
||||
{% block head_title %}{{ page.title }}{% endblock %}
|
||||
|
||||
{% block page_title %}
|
||||
{{ page.title }}
|
||||
{% if editable %}
|
||||
<div class="pull-right">
|
||||
<a href="{% url cms_page_edit page.path %}" class="btn"><i class="icon-pencil icon-large"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block breadcrumbs %}{% sitetree_breadcrumbs from "main" %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{{ page.body }}
|
||||
{% endblock %}
|
22
symposion/templates/cms/page_edit.html
Normal file
22
symposion/templates/cms/page_edit.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "site_base.html" %}
|
||||
|
||||
{% load sitetree %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap_tags %}
|
||||
|
||||
{% block body_class %}cms-page{% endblock %}
|
||||
|
||||
{% block head_title %}Create Page{% endblock %}
|
||||
|
||||
{% block page_title %}{% trans "Edit page at:" %} {{ path }}{% endblock %}
|
||||
{% block breadcrumbs %}{% sitetree_breadcrumbs from "main" %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form method="POST" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|as_bootstrap }}
|
||||
<div class="form-actions">
|
||||
<input class="btn btn-primary" type="submit" value="Save" />
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -2,16 +2,44 @@
|
|||
|
||||
{% load metron_tags %}
|
||||
{% load i18n %}
|
||||
{% load sitetree %}
|
||||
{% load markitup_tags %}
|
||||
{% load static %}
|
||||
|
||||
{% block extra_head_base %}
|
||||
<link href="{{ STATIC_URL }}img/favicon.ico" rel="shortcut icon" />
|
||||
<script src="{% block jquery_src %}{% static "pinax/js/jquery.js" %}{% endblock %}"></script>
|
||||
{% markitup_media "no-jquery" %}
|
||||
{% block extra_head %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
{% sitetree_menu from "main" include "trunk" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body_base %}
|
||||
<div class="container">
|
||||
{% include "_messages.html" %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{% sitetree_breadcrumbs from "main" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_title %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
{% include "_footer.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_body_base %}
|
||||
{% block script_base %}
|
||||
{% analytics %}
|
||||
{% block extra_body %}{% endblock %}
|
||||
{% endblock %}
|
||||
<script src="{% static "bootstrap/js/bootstrap.js" %}"></script>
|
||||
<script src="{% static "pinax/js/theme.js" %}"></script>
|
||||
{% block extra_script %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
17
symposion/templates/sitetree/breadcrumbs.html
Normal file
17
symposion/templates/sitetree/breadcrumbs.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% load sitetree %}
|
||||
{% if sitetree_items %}
|
||||
<ul class="breadcrumb">
|
||||
{% for item in sitetree_items %}
|
||||
{% if not forloop.last %}
|
||||
<li>
|
||||
<a href="{% sitetree_url for item %}" {% if item.hint %}title="{{ item.hint }}"{% endif %}>{{ item.title_resolved }}</a>
|
||||
<span class="divider">/</span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="active">{{ item.title_resolved }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
||||
{% endif %}
|
16
symposion/templates/sitetree/menu.html
Normal file
16
symposion/templates/sitetree/menu.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
{% load sitetree %}
|
||||
<ul class="nav">
|
||||
{% for item in sitetree_items %}
|
||||
<li class="{{ item.is_current|yesno:"active ," }}{% if item.has_children %}dropdown{% endif %}">
|
||||
{% if item.has_children %}
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" title="{{ item.hint|default:"" }}">
|
||||
{{ item.title_resolved }}
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
{% sitetree_children of item for menu template "sitetree/submenu.html" %}
|
||||
{% else %}
|
||||
<a href="{% sitetree_url for item %}" title="{{ item.hint|default:"" }}">{{ item.title_resolved }}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
8
symposion/templates/sitetree/submenu.html
Normal file
8
symposion/templates/sitetree/submenu.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% load sitetree %}
|
||||
<ul class="dropdown-menu">
|
||||
{% for item in sitetree_items %}
|
||||
<li>
|
||||
<a href="{% sitetree_url for item %}" title="{{ item.hint|default:"" }}">{{ item.title_resolved }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
15
symposion/templates/sitetree/tree.html
Normal file
15
symposion/templates/sitetree/tree.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% load sitetree %}
|
||||
{% if sitetree_items %}
|
||||
<ul>
|
||||
{% for item in sitetree_items %}
|
||||
{% if item.insitetree %}
|
||||
<li>
|
||||
<a href="{% sitetree_url for item %}" {% if item.hint %}title="{{ item.hint }}"{% endif %}>{{ item.title_resolved }}</a>
|
||||
{% if item.has_children %}
|
||||
{% sitetree_children of item for sitetree template "sitetree/tree.html" %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
|
@ -9,6 +9,7 @@ admin.autodiscover()
|
|||
|
||||
# from pinax.apps.account.openid_consumer import PinaxConsumer
|
||||
|
||||
WIKI_SLUG = r"(([\w-]{2,})(/[\w-]{2,})*)"
|
||||
|
||||
urlpatterns = patterns("",
|
||||
url(r"^$", direct_to_template, {
|
||||
|
@ -18,7 +19,12 @@ urlpatterns = patterns("",
|
|||
url(r"^about/", include("symposion.about.urls")),
|
||||
url(r"^account/", include("account.urls")),
|
||||
# url(r"^openid/", include(PinaxConsumer().urls)),
|
||||
|
||||
url(r"^boxes/", include("symposion.boxes.urls")),
|
||||
url(r"^markitup/", include("markitup.urls")),
|
||||
|
||||
url(r"^", include("symposion.cms.urls")),
|
||||
)
|
||||
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
0
symposion_project/apps/cms/__init__.py
Normal file
0
symposion_project/apps/cms/__init__.py
Normal file
12
symposion_project/apps/cms/admin.py
Normal file
12
symposion_project/apps/cms/admin.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from mptt.admin import MPTTModelAdmin
|
||||
|
||||
from cms.models import Page
|
||||
|
||||
|
||||
class PageAdmin(MPTTModelAdmin):
|
||||
prepopulated_fields = {"slug": ("title",)}
|
||||
list_display = ("title", "published", "status")
|
||||
|
||||
admin.site.register(Page, PageAdmin)
|
65
symposion_project/apps/cms/models.py
Normal file
65
symposion_project/apps/cms/models.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from markitup.fields import MarkupField
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
from mptt.utils import drilldown_tree_for_node
|
||||
|
||||
import reversion
|
||||
|
||||
|
||||
class ContentBase(models.Model):
|
||||
|
||||
STATUS_CHOICES = (
|
||||
(1, _("Draft")),
|
||||
(2, _("Public")),
|
||||
)
|
||||
|
||||
title = models.CharField(max_length=100)
|
||||
slug = models.CharField(max_length=100, blank=True, null=True)
|
||||
body = MarkupField()
|
||||
|
||||
tags = TaggableManager(blank=True)
|
||||
|
||||
status = models.IntegerField(choices=STATUS_CHOICES, default=2)
|
||||
published = models.DateTimeField(default=datetime.now)
|
||||
created = models.DateTimeField(editable=False, default=datetime.now)
|
||||
updated = models.DateTimeField(editable=False, default=datetime.now)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class Page(MPTTModel, ContentBase):
|
||||
|
||||
parent = TreeForeignKey("self", null=True, blank=True, related_name="children")
|
||||
ordering = models.PositiveIntegerField(default=1)
|
||||
path = models.TextField(blank=True, editable=False)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
def save(self, calculate_path=True, *args, **kwargs):
|
||||
super(Page, self).save(*args, **kwargs)
|
||||
if calculate_path:
|
||||
self.calculate_path()
|
||||
|
||||
def calculate_path(self):
|
||||
self.path = ""
|
||||
for page in drilldown_tree_for_node(self):
|
||||
if page == self:
|
||||
self.path += page.slug
|
||||
break
|
||||
else:
|
||||
self.path += "%s/" % page.slug
|
||||
self.save(calculate_path=False)
|
||||
|
||||
class MPTTMeta:
|
||||
order_insertion_by = ["ordering", "title"]
|
||||
|
||||
reversion.register(Page)
|
15
symposion_project/apps/cms/views.py
Normal file
15
symposion_project/apps/cms/views.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
|
||||
from cms.models import Page
|
||||
|
||||
|
||||
def page(request, slug):
|
||||
|
||||
page = get_object_or_404(Page, path=slug)
|
||||
siblings = page.get_siblings(include_self=True)
|
||||
|
||||
return render_to_response("cms/page_detail.html", {
|
||||
"page": page,
|
||||
"siblings": siblings,
|
||||
}, context_instance=RequestContext(request))
|
19
symposion_project/templates/cms/page_detail.html
Normal file
19
symposion_project/templates/cms/page_detail.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "subnav_base.html" %}
|
||||
|
||||
{% block subnav %}
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">{{ page.parent }}</li>
|
||||
{% for sibling in siblings %}
|
||||
{% if sibling == page %}
|
||||
<li>{{ sibling }}</li>
|
||||
{% else %}
|
||||
<li><a href="{% url cms_page sibling.path %}">{{ sibling }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ page.title }}</h1>
|
||||
{{ page.body }}
|
||||
{% endblock %}
|
11
symposion_project/templates/sitetree/menu.html
Normal file
11
symposion_project/templates/sitetree/menu.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% load sitetree %}
|
||||
<ul class="nav">
|
||||
{% for item in sitetree_items %}
|
||||
<li class="{{ item.is_current|yesno:"active ," }}">
|
||||
<a href="{% sitetree_url for item %}" title="{{ item.hint|default:"" }}">{{ item.title_resolved }}</a>
|
||||
{% if item.has_children %}
|
||||
{% sitetree_children of item for menu template "sitetree/menu.html" %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
Loading…
Reference in a new issue