Add basic blogging support
authorBrett Parker <iDunno@sommitrealweird.co.uk>
Mon, 11 Feb 2008 02:26:04 +0000 (02:26 +0000)
committerBrett Parker <iDunno@sommitrealweird.co.uk>
Mon, 11 Feb 2008 02:26:04 +0000 (02:26 +0000)
13 files changed:
sommitrealweird/blog/__init__.py [new file with mode: 0644]
sommitrealweird/blog/context_processors.py [new file with mode: 0644]
sommitrealweird/blog/feeds.py [new file with mode: 0644]
sommitrealweird/blog/models.py [new file with mode: 0644]
sommitrealweird/blog/templates/blog/blog_index.html [new file with mode: 0644]
sommitrealweird/blog/templates/blog/feeds/description.html [new file with mode: 0644]
sommitrealweird/blog/templates/blog/feeds/title.html [new file with mode: 0644]
sommitrealweird/blog/templates/blog/single_entry_rst.html [new file with mode: 0644]
sommitrealweird/blog/urls.py [new file with mode: 0644]
sommitrealweird/blog/views.py [new file with mode: 0644]
sommitrealweird/settings.py
sommitrealweird/urls.py
templates/base.html

diff --git a/sommitrealweird/blog/__init__.py b/sommitrealweird/blog/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/sommitrealweird/blog/context_processors.py b/sommitrealweird/blog/context_processors.py
new file mode 100644 (file)
index 0000000..14a9216
--- /dev/null
@@ -0,0 +1,35 @@
+from django.conf import settings
+from blog.models import BlogEntry, BlogSection
+import re
+
+def content_breadcrumb(request):
+    path = request.path
+    if path[0:len(settings.BLOG_ROOT)] == settings.BLOG_ROOT:
+        breadcrumb = [{'url': u'/', 'title': u'/'}, {'url': settings.BLOG_ROOT, 'title': u'blog'},]
+
+        path = path[len(settings.BLOG_ROOT):]
+
+        parts = path.split('/')
+
+        if parts[0] == "section":
+            # Just need to add the section title to the breadcrumb
+            section = BlogSection.objects.get(slug__exact=parts[1])
+            breadcrumb.append({'url': section.get_absolute_url(), 'title': section.title})
+
+        if len(parts) == 5 and  \
+            parts[0].isdigit() and \
+            parts[1].isdigit() and \
+            parts[2].isdigit():
+                # try getting the blog entry
+                try:
+                    entry = BlogEntry.objects.get(publish_date__year=int(parts[0]), publish_date__month=int(parts[1]), publish_date__day=int(parts[2]), slug__exact=parts[3])
+                    breadcrumb.append({'url': entry.get_absolute_url(), 'title': entry.title})
+                except:
+                    breadcrumb.append({'url': 'nowhere', 'title': 'screwed'})
+                    pass
+
+        return {
+            'content_breadcrumb': breadcrumb,
+        }
+    else:
+        return {}
diff --git a/sommitrealweird/blog/feeds.py b/sommitrealweird/blog/feeds.py
new file mode 100644 (file)
index 0000000..d08456f
--- /dev/null
@@ -0,0 +1,22 @@
+from django.contrib.syndication.feeds import Feed
+from django.utils.feedgenerator import Atom1Feed
+from blog.models import BlogEntry
+from django.conf import settings
+
+class LatestBlogEntries(Feed):
+    feed_type = Atom1Feed
+    title = settings.BLOG_TITLE
+    description = u'Updates on %s' %(title,)
+    link = settings.BLOG_ROOT
+
+    title_template = 'blog/feeds/title.html'
+    description_template = 'blog/feeds/description.html'
+
+    def items(self):
+        try:
+            return BlogEntry.objects.all().order_by('-publish_date')[:20]
+        except:
+            return BlogEntry.objects.all().order_by('-publish_date')
+
+    def item_pubdate(self, obj):
+        return obj.publish_date
diff --git a/sommitrealweird/blog/models.py b/sommitrealweird/blog/models.py
new file mode 100644 (file)
index 0000000..6b0d254
--- /dev/null
@@ -0,0 +1,44 @@
+from django.db import models
+from django.conf import settings
+
+FORMAT_CHOICES = (
+    ('rst', 'reStructuredText'),
+    ('html', 'HTML'),
+)
+
+class BlogEntry(models.Model):
+    title = models.CharField(maxlength=150)
+    islive = models.BooleanField()
+    sections = models.ManyToManyField('BlogSection')
+    format = models.CharField(maxlength=10, choices=FORMAT_CHOICES)
+    slug = models.SlugField(prepopulate_from=("title",))
+    publish_date = models.DateTimeField()
+    content = models.TextField()
+
+    def __str__(self):
+        return self.__unicode__()
+
+    def __unicode__(self):
+        return u'%s - %s' %(self.publish_date.strftime('%Y-%m-%d %H:%M'), self.title)
+
+    def get_absolute_url(self):
+        return u'%s%04d/%02d/%02d/%s/' %(settings.BLOG_ROOT, self.publish_date.year, self.publish_date.month, self.publish_date.day, self.slug)
+
+    class Admin:
+        pass
+
+class BlogSection(models.Model):
+    title = models.CharField(maxlength=150)
+    slug = models.SlugField(prepopulate_from=("title",))
+
+    def __str__(self):
+        return self.__unicode__()
+
+    def __unicode__(self):
+        return u'%s' %(self.title,)
+
+    def get_absolute_url(self):
+        return u'%ssection/%s/' %(settings.BLOG_ROOT, self.slug)
+
+    class Admin:
+        pass
diff --git a/sommitrealweird/blog/templates/blog/blog_index.html b/sommitrealweird/blog/templates/blog/blog_index.html
new file mode 100644 (file)
index 0000000..8e94748
--- /dev/null
@@ -0,0 +1,46 @@
+{% extends "base.html" %}
+{% load markup %}
+
+{% block content %}
+    {% if entries %}
+        {% for entry in entries %}
+            <h1>{{ entry.title|escape }}</h1>
+            {{ entry.content|restructuredtext }}
+            <p>Posted: {{ entry.publish_date|date:"Y-m-d H:i" }} in 
+                {% for section in entry.sections.all %}
+                    {% if forloop.first %}
+                        <a href="{{ section.get_absolute_url }}">{{ section.title }}</a>{% if not forloop.last %}, {% endif %}
+                    {% else %}
+                        {% if forloop.last %}
+                            and <a href="{{ section.get_absolute_url }}">{{ section.title }}</a>
+                        {% else %}
+                            <a href="{{ section.get_abolute_url }}">{{ section.title }}</a>
+                        {% endif %}
+                    {% endif %}
+                {% endfor %}
+                <a href="{{ entry.get_absolute_url }}">permalink</a></p>
+        {% endfor %}
+        {% if paginated %}
+            <p class="paginator">Pages: 
+            {% if has_prev %}
+                <a href="?page={{ prev }}">Previous</a> |
+            {% endif %}
+            {% for paginator_page in pages %}
+                {% if forloop.first %}
+                {% else %}
+                    |
+                {% endif %}
+                {% ifequal paginator_page page %}
+                    {{ page }}
+                {% else %}
+                    <a href="?page={{ paginator_page }}">{{ paginator_page }}</a>
+                {% endifequal %}
+            {% endfor %}
+            {% if has_next %}
+                | <a href="?page={{ next }}">Next</a>
+            {% endif %}
+        {% endif %}
+    {% else %}
+        <p>We have no blog entries - that sucks!</p>
+    {% endif %}
+{% endblock %}
diff --git a/sommitrealweird/blog/templates/blog/feeds/description.html b/sommitrealweird/blog/templates/blog/feeds/description.html
new file mode 100644 (file)
index 0000000..d16eb9b
--- /dev/null
@@ -0,0 +1,3 @@
+{% load markup %}
+
+{{ obj.content|restructuredtext }}
diff --git a/sommitrealweird/blog/templates/blog/feeds/title.html b/sommitrealweird/blog/templates/blog/feeds/title.html
new file mode 100644 (file)
index 0000000..d355de5
--- /dev/null
@@ -0,0 +1 @@
+{{ obj.title }}
diff --git a/sommitrealweird/blog/templates/blog/single_entry_rst.html b/sommitrealweird/blog/templates/blog/single_entry_rst.html
new file mode 100644 (file)
index 0000000..5681e14
--- /dev/null
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+
+{% load markup %}
+
+{% block content %}
+<h1>{{ title }}</h1>
+{{ content|restructuredtext }}
+{% endblock %}
diff --git a/sommitrealweird/blog/urls.py b/sommitrealweird/blog/urls.py
new file mode 100644 (file)
index 0000000..f41ed25
--- /dev/null
@@ -0,0 +1,10 @@
+from django.conf.urls.defaults import *
+from settings import MEDIA_ROOT, MEDIA_URL
+
+urlpatterns = patterns('',
+    (r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/(?P<slug>[^/]+)(/|)$', 'blog.views.blog_view'),
+    (r'^(?P<year>[0-9]{4})(?P<month>[0-9]{2})(?P<day>[0-9]{2})-(?P<slug>[^/]+)\.html$', 'blog.views.blog_view'),
+    (r'^(?P<year>[0-9]{4})(?P<month>[0-9]{2})(?P<day>[0-9]{2})(?P<hour>[0-9]{2})(?P<minutes>[0-9]{2})-(?P<slug>[^/]+)\.html$', 'blog.views.blog_view'),
+    (r'^section/(?P<section>[^/]*)/$', 'blog.views.blog_index'),
+    (r'^$', 'blog.views.blog_index'),
+)
diff --git a/sommitrealweird/blog/views.py b/sommitrealweird/blog/views.py
new file mode 100644 (file)
index 0000000..a801cd4
--- /dev/null
@@ -0,0 +1,54 @@
+from blog.models import BlogEntry, BlogSection
+from django.http import Http404, HttpResponse
+from django.template import RequestContext, loader
+from django.core.paginator import ObjectPaginator
+from django.conf import settings
+
+def blog_index(request, section=None):
+    entries = BlogEntry.objects.all()
+    if section:
+        entries = entries.filter(sections__slug__exact=section)
+    entries = entries.order_by('-publish_date')
+    if entries:
+        paginate_by=20
+        paginator = ObjectPaginator(entries, paginate_by)
+        page = int(request.GET.get('page', 0))
+        if page > 0:
+            page = page - 1
+        paginated_entries = paginator.get_page(page)
+        pages = [i for i in range(1, paginator.pages + 1)]
+        t = loader.get_template("blog/blog_index.html")
+        c = RequestContext(request,
+            {
+                "entries": paginated_entries,
+                "title": settings.BLOG_TITLE,
+                "page": page + 1,
+                "pages": pages,
+                "paginated": 1,
+                "has_next": paginator.has_next_page(page),
+                "has_prev": paginator.has_next_page(page),
+                "next": page + 2,
+                "prev": page - 1
+            })
+        return HttpResponse(t.render(c))
+    else:
+        raise Http404
+
+def blog_view(request, year=None, month=None, day=None, hour=None, minutes=None, section=None, slug=None):
+    # work out what we've got roughly
+    if year and month and day and slug:
+        # exact blog entry (woo!)
+            year = int(year)
+            month = int(month)
+            day = int(day)
+            blog_entry = BlogEntry.objects.get(publish_date__year=year, publish_date__month=month, publish_date__day=day, slug__exact=slug)
+            template_name = "blog/single_entry_%s.html" %(blog_entry.format,)
+            t = loader.get_template(template_name)
+            c = RequestContext(request,
+                {
+                    "content": blog_entry.content,
+                    "title": blog_entry.title,
+                    "publish_date": blog_entry.publish_date
+                }
+            )
+            return HttpResponse(t.render(c))
index e198e9bca59edb443ac11e3fca2582f393c4f5ac..959a2797a1c11840d24d0027fde4ee6df0aad3c7 100644 (file)
@@ -83,6 +83,7 @@ INSTALLED_APPS = (
     'django.contrib.markup',
     'bpcms',
     'generic',
+    'blog',
 )
 
 TEMPLATE_CONTEXT_PROCESSORS = (
@@ -93,11 +94,17 @@ TEMPLATE_CONTEXT_PROCESSORS = (
     'bpcms.context_processors.content_menu',
     'bpcms.context_processors.content_submenu',
     'bpcms.context_processors.content_breadcrumb',
+    'blog.context_processors.content_breadcrumb',
 )
 
+APPEND_SLASH=False
+
 BPCMS_ROOT = '/'
 BPCMS_DISALLOWED_ROOT_DOC_NAMES = (
     'admin',
     'blog',
     'photos',
 )
+
+BLOG_ROOT = '/blog/'
+BLOG_TITLE = u'The World of SommitRealWeird.'
index 39adc87cdfb542b9fe4fb9e2f3ee985d278d366f..b709acced792c93e27330fea820be3f1576d06d8 100644 (file)
@@ -1,16 +1,23 @@
 from django.conf.urls.defaults import *
 from settings import MEDIA_ROOT, MEDIA_URL
+from blog.feeds import LatestBlogEntries
 
 handler404 = 'generic.views.render_404'
 
+feeds_dict = {
+    'blog': LatestBlogEntries,
+    }
+
 urlpatterns = patterns('',
     # Example:
     # (r'^sommitrealweird/', include('sommitrealweird.foo.urls')),
 
     # Uncomment this for admin:
     (r'^$', 'bpcms.views.document_view', {'slug': 'index'}),
+    (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT, 'show_indexes': True}),
     (r'^admin/', include('django.contrib.admin.urls')),
+    (r'^blog/', include('blog.urls')),
+    (r'^feeds/(?P<url>.*)/', 'django.contrib.syndication.views.feed', {'feed_dict': feeds_dict}),
     (r'^(?:content/|)(?P<slug>[^/]+)/$', 'bpcms.views.document_view'),
     (r'^(?:content/|)(?P<folders>.*)/(?P<slug>[^/]+)/$', 'bpcms.views.document_view'),
-    (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT, 'show_indexes': True}),
 )
index ebd630e66a246742a0fe55fc150150293aa26ba1..430b8c669211e4619b2d480ee3b289df9f4b5bd0 100644 (file)
@@ -19,7 +19,7 @@
                         <li><a href="{{ item.url }}">{{ item.title }}</a></li>
                         {% endfor %}
                         <li>Photos</li>
-                        <li>Blog</li>
+                        <li><a href="/blog/">Blog</a></li>
                     </ul>
                     {% endblock %}
                 </div>