Skip to content

Commit

Permalink
Initial commit of stipped down code and templates
Browse files Browse the repository at this point in the history
  • Loading branch information
dcwatson committed Aug 21, 2012
1 parent bbcf405 commit 2488852
Show file tree
Hide file tree
Showing 31 changed files with 1,031 additions and 3 deletions.
13 changes: 10 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.py[co]
*.db

# Packages
*.egg
Expand All @@ -20,8 +21,14 @@ pip-log.txt
.coverage
.tox

#Translations
# Translations
*.mo

#Mr Developer
.mr.developer.cfg
# OSX/Windows cruft
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
Thumbs.db
22 changes: 22 additions & 0 deletions initial_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"pk": 1,
"model": "sites.site",
"fields": {
"domain": "http://localhost:8000",
"name": "Leaves"
}
},
{
"pk": 1,
"model": "leaves.preferences",
"fields": {
"default_comment_status": "pending",
"stream_count": 10,
"analytics_id": "",
"site": 1,
"feed_count": 10,
"homepage": "blog-index"
}
}
]
Empty file added leaves/__init__.py
Empty file.
104 changes: 104 additions & 0 deletions leaves/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
from django.contrib.sites.models import Site
from leaves.models import Tag, Comment, Attachment, Redirect, Preferences, Page

PUBLISHING_OPTIONS = (_('Publishing Options'), {
'fields': ('author', 'author_name', 'status', 'date_published', 'date_expires', 'tags'),
})

ADVANCED_PUBLISHING_OPTIONS = (_('Advanced Publishing Options'), {
'classes': ('collapse',),
'fields': ('sites', 'show_in_stream', 'allow_comments', 'password', 'custom_url', 'summary_template',
'page_template'),
})

SITEMAP_OPTIONS = (_('Sitemap Options'), {
'classes': ('collapse',),
'fields': ('changefreq', 'priority'),
})

TRANSLATION_OPTIONS = (_('Translation Options'), {
'classes': ('collapse',),
'fields': ('language', 'translation_of', 'translator_name'),
})

class AttachmentInline (admin.TabularInline):
model = Attachment
extra = 1

class LeafAdmin (admin.ModelAdmin):
list_display = ('__unicode__', 'language', 'author_name', 'status', 'date_published', 'date_expires',
'date_created', 'date_modified', 'show_in_stream', 'url')
list_filter = ('author', 'sites', 'status', 'show_in_stream', 'language')
list_select_related = True
date_hierarchy = 'date_published'
inlines = (AttachmentInline,)

def url(self, obj):
if obj.custom_url:
return obj.custom_url
elif hasattr(obj, 'get_absolute_url'):
return obj.get_absolute_url()
return ''
url.short_description = 'URL'

def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'author_name' and 'request' in kwargs:
kwargs['initial'] = kwargs['request'].user.get_full_name().strip()
return super(LeafAdmin, self).formfield_for_dbfield(db_field, **kwargs)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'author':
if not request.user.is_superuser:
kwargs['queryset'] = User.objects.filter(pk=request.user.pk)
kwargs['initial'] = request.user.pk
return super(LeafAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'sites':
kwargs['initial'] = Site.objects.all()
return super(LeafAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)

class CommentAdmin (admin.ModelAdmin):
list_display = ('leaf', 'title', 'author_name', 'email', 'status', 'date_posted')
list_filter = ('date_posted', 'status', 'leaf__sites')
date_hierarchy = 'date_posted'
search_fields = ('title', 'comment', 'author_name', 'email')
actions = ('approve_comments',)
ordering = ('-date_posted',)

def approve_comments(self, request, queryset):
update_count = queryset.update(status='published')
term = 'comment was' if update_count == 1 else 'comments were'
self.message_user(request, '%s %s successfully approved.' % (update_count, term))
approve_comments.short_description = 'Mark selected comments as published'

class TagAdmin (admin.ModelAdmin):
list_display = ('name', 'slug')
prepopulated_fields = {'slug': ('name',)}

class RedirectAdmin (admin.ModelAdmin):
list_display = ('old_path', 'new_path', 'site', 'redirect_type')
list_filter = ('site', 'redirect_type')

class PreferencesAdmin (admin.ModelAdmin):
list_display = ('site', 'homepage', 'stream_count', 'feed_count', 'analytics_id', 'default_comment_status')

class PageAdmin (LeafAdmin):
fieldsets = (
(None, {
'fields': ('title', 'slug', 'summary', 'content'),
}),
PUBLISHING_OPTIONS,
ADVANCED_PUBLISHING_OPTIONS,
SITEMAP_OPTIONS,
TRANSLATION_OPTIONS,
)
prepopulated_fields = {'slug': ('title',)}

admin.site.register(Tag, TagAdmin)
admin.site.register(Comment, CommentAdmin)
admin.site.register(Redirect, RedirectAdmin)
admin.site.register(Preferences, PreferencesAdmin)
admin.site.register(Page, PageAdmin)
Empty file added leaves/blog/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions leaves/blog/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
from leaves.admin import LeafAdmin, PUBLISHING_OPTIONS, ADVANCED_PUBLISHING_OPTIONS, SITEMAP_OPTIONS, \
TRANSLATION_OPTIONS
from leaves.blog.models import Post

class PostAdmin (LeafAdmin):
fieldsets = (
(None, {
'fields': ('title', 'slug', 'summary', 'content'),
}),
PUBLISHING_OPTIONS,
ADVANCED_PUBLISHING_OPTIONS,
SITEMAP_OPTIONS,
TRANSLATION_OPTIONS,
)
prepopulated_fields = {'slug': ('title',)}

admin.site.register(Post, PostAdmin)
27 changes: 27 additions & 0 deletions leaves/blog/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from leaves.models import Leaf
from leaves.managers import LeafManager

class Post (Leaf):
title = models.CharField(_('title'), max_length=200)
slug = models.SlugField(_('slug'), unique=True)
summary = models.TextField(_('summary'), blank=True)
content = models.TextField(_('content'))

objects = LeafManager()

def __unicode__(self):
return self.title
__unicode__.admin_order_field = 'title'

def natural_key(self):
return (self.slug,)

@models.permalink
def get_absolute_url(self):
return ('blog-post', (), {
'year': str(self.date_published.year),
'month': '%02d' % self.date_published.month,
'slug': self.slug,
})
9 changes: 9 additions & 0 deletions leaves/blog/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.conf.urls import patterns, include, url
from leaves.utils import homepage

urlpatterns = patterns('',
url(r'^$', 'leaves.blog.views.index', name='blog-index'),
url(r'^(?P<year>\d+)/(?P<month>\d+)/(?P<slug>[^/]+)/$', 'leaves.blog.views.post', name='blog-post'),
)

homepage.register('blog-index', 'Recent Blog Posts')
16 changes: 16 additions & 0 deletions leaves/blog/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.shortcuts import render, get_object_or_404
from leaves.blog.models import Post
from leaves.utils import get_page

def index(request):
qs = Post.objects.stream(site=request.site, user=request.user)
return render(request, 'blog/index.html', {
'page': get_page(request, qs, request.site.preferences.stream_count),
})

def post(request, year, month, slug):
qs = Post.objects.stream(site=request.site, user=request.user)
post = get_object_or_404(qs, date_published__year=year, date_published__month=month, slug__iexact=slug)
return render(request, post.get_page_templates(), {
'post': post,
})
46 changes: 46 additions & 0 deletions leaves/managers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.db import models
from django.utils import timezone

class NaturalKeyManager (models.Manager):
"""
"""

def __init__(self, *field_names):
self.field_names = tuple(field_names) or ('slug',)
super(NaturalKeyManager, self).__init__()

def get_by_natural_key(self, *keys):
if len(self.field_names) != len(keys):
raise ValueError('Could not get %s by natural key: %s' % (self.model.__class__, keys))
params = dict(zip(self.field_names, keys))
return self.get(**params)

class LeafManager (NaturalKeyManager):
"""
"""

def get_query_set(self):
qs = super(LeafManager, self).get_query_set()
# Get all the non-null ForeignKey fields.
related = [f.name for f in self.model._meta.fields if isinstance(f, models.ForeignKey) and not f.null]
if self.model.__name__ == 'Leaf':
# Now get all the subclasses of Leaf to select the reverse relation of the OneToOneFields.
related += [cls.__name__.lower() for cls in self.model.__subclasses__()]
return qs.select_related(*related)

def published(self, site=None, user=None, asof=None):
if asof is None:
asof = timezone.now()
exp = models.Q(date_expires__isnull=True) | models.Q(date_expires__gt=asof)
q = models.Q(status='published') & models.Q(date_published__lte=asof) & exp
if site:
q &= models.Q(sites__in=(site,))
# Show the user's leaves in the published stream, even if they aren't. This is particularly useful for the
# admin's "Show on site" feature.
if user and user.is_authenticated():
q |= models.Q(author=user)
return self.get_query_set().filter(q)

def stream(self, site=None, user=None, asof=None):
qs = self.published(site=site, user=user, asof=asof)
return qs.filter(show_in_stream=True)
83 changes: 83 additions & 0 deletions leaves/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import resolve
from django.contrib.sites.models import Site
from django.utils.encoding import iri_to_uri
from django.conf import settings
from django import http
from leaves.models import Leaf, Redirect
import threading

request_context = threading.local()

class LeavesSiteMiddleware (object):

def process_request(self, request):
host = request.get_host().lower().split(':')[0]
# In order to support subdomains, we need to check all the sites
# to find the most specific.
all_sites = {}
default_site = None
for s in Site.objects.select_related('preferences'):
all_sites[s.domain.lower()] = s
if s.pk == settings.SITE_ID:
default_site = s
# If all else fails, fall back to using SITE_ID.
request.site = default_site
if host in all_sites:
request.site = all_sites[host]
else:
# Find the most specific match, determined by match suffix length.
best_match = None
match_len = 0
for domain, site in all_sites.items():
if host.endswith(domain) and len(domain) > match_len:
best_match = site
match_len = len(domain)
if best_match:
request.site = best_match
request_context.site = request.site

def process_response(self, request, response):
if hasattr(request_context, 'site'):
del request_context.site
return response

class LeavesFallbackMiddleware (object):

def process_response(self, request, response):
if response.status_code != 404:
return response
try:
url = request.path_info
if not url.endswith('/') and settings.APPEND_SLASH:
return http.HttpResponseRedirect('%s/' % request.path)
if not url.startswith('/'):
url = '/' + url
leaf = get_object_or_404(Leaf, custom_url=url)
leaf = leaf.resolve()
# We need to try to find the view that normally renders this Leaf
# type. Since the URL is custom, we fall back to trying
# get_absolute_url on the Leaf subclass, which should return a URL
# mapped in the plugin's urlconf.
match = resolve(leaf.get_absolute_url())
r = match.func(request, *match.args, **match.kwargs)
# If the response is a TemplateResponse, we need to bake it.
if hasattr(r, 'render'):
r.render()
return r
except http.Http404:
# If we still haven't found anything, check the redirects.
try:
url = request.get_full_path()
r = Redirect.objects.get(old_path=url, site=request.site)
if not r.new_path:
return http.HttpResponseGone()
resp = http.HttpResponse(status=r.redirect_type)
resp['Location'] = iri_to_uri(r.new_path)
return resp
except Redirect.DoesNotExist:
return response
except:
if settings.DEBUG:
raise
return response
Loading

0 comments on commit 2488852

Please sign in to comment.