diff --git a/sites/scrapy.org/scrapyorg/blog/__init__.py b/sites/scrapy.org/scrapyorg/blog/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/sites/scrapy.org/scrapyorg/blog/admin.py b/sites/scrapy.org/scrapyorg/blog/admin.py
new file mode 100644
index 000000000..0b1568c9d
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/admin.py
@@ -0,0 +1,17 @@
+from django.contrib import admin
+from scrapyorg.blog.models import *
+
+
+class CategoryAdmin(admin.ModelAdmin):
+ prepopulated_fields = {'slug': ('title',)}
+
+admin.site.register(Category, CategoryAdmin)
+
+
+class PostAdmin(admin.ModelAdmin):
+ list_display = ('title', 'publish', 'status')
+ list_filter = ('publish', 'categories', 'status')
+ search_fields = ('title', 'body')
+ prepopulated_fields = {'slug': ('title',)}
+
+admin.site.register(Post, PostAdmin)
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/feeds.py b/sites/scrapy.org/scrapyorg/blog/feeds.py
new file mode 100644
index 000000000..2cd11cb28
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/feeds.py
@@ -0,0 +1,42 @@
+from django.contrib.syndication.feeds import FeedDoesNotExist
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.sites.models import Site
+from django.contrib.syndication.feeds import Feed
+from django.core.urlresolvers import reverse
+from scrapyorg.blog.models import Post, Category
+
+
+class BlogPostsFeed(Feed):
+ _site = Site.objects.get_current()
+ title = '%s feed' % _site.name
+ description = '%s posts feed.' % _site.name
+
+ def link(self):
+ return reverse('blog_index')
+
+ def items(self):
+ return Post.objects.published()[:10]
+
+ def item_pubdate(self, obj):
+ return obj.publish
+
+
+class BlogPostsByCategory(Feed):
+ _site = Site.objects.get_current()
+ title = '%s posts category feed' % _site.name
+
+ def get_object(self, bits):
+ if len(bits) != 1:
+ raise ObjectDoesNotExist
+ return Category.objects.get(slug__exact=bits[0])
+
+ def link(self, obj):
+ if not obj:
+ raise FeedDoesNotExist
+ return obj.get_absolute_url()
+
+ def description(self, obj):
+ return "Posts recently categorized as %s" % obj.title
+
+ def items(self, obj):
+ return obj.post_set.published()[:10]
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/managers.py b/sites/scrapy.org/scrapyorg/blog/managers.py
new file mode 100644
index 000000000..d5bcdb005
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/managers.py
@@ -0,0 +1,9 @@
+from django.db.models import Manager
+import datetime
+
+
+class PublicManager(Manager):
+ """Returns published posts that are not in the future."""
+
+ def published(self):
+ return self.get_query_set().filter(status__gte=2, publish__lte=datetime.datetime.now())
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/models.py b/sites/scrapy.org/scrapyorg/blog/models.py
new file mode 100644
index 000000000..24c6e448d
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/models.py
@@ -0,0 +1,81 @@
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from django.db.models import permalink
+from django.contrib.auth.models import User
+#from tagging.fields import TagField
+from scrapyorg.blog.managers import PublicManager
+
+#import tagging
+
+
+class Category(models.Model):
+ """Category model."""
+ title = models.CharField(_('title'), max_length=100)
+ slug = models.SlugField(_('slug'), unique=True)
+
+ class Meta:
+ verbose_name = _('category')
+ verbose_name_plural = _('categories')
+ db_table = 'blog_categories'
+ ordering = ('title',)
+
+ class Admin:
+ pass
+
+ def __unicode__(self):
+ return u'%s' % self.title
+
+ @permalink
+ def get_absolute_url(self):
+ return ('blog_category_detail', None, {'slug': self.slug})
+
+
+class Post(models.Model):
+ """Post model."""
+ STATUS_CHOICES = (
+ (1, _('Draft')),
+ (2, _('Public')),
+ )
+ title = models.CharField(_('title'), max_length=200)
+ slug = models.SlugField(_('slug'), unique_for_date='publish')
+ author = models.ForeignKey(User, blank=True, null=True)
+ body = models.TextField(_('body'))
+ tease = models.TextField(_('tease'), blank=True)
+ status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=2)
+ allow_comments = models.BooleanField(_('allow comments'), default=True)
+ publish = models.DateTimeField(_('publish'))
+ created = models.DateTimeField(_('created'), auto_now_add=True)
+ modified = models.DateTimeField(_('modified'), auto_now=True)
+ categories = models.ManyToManyField(Category, blank=True)
+# tags = TagField()
+ objects = PublicManager()
+
+ class Meta:
+ verbose_name = _('post')
+ verbose_name_plural = _('posts')
+ db_table = 'blog_posts'
+ ordering = ('-publish',)
+ get_latest_by = 'publish'
+
+ class Admin:
+ list_display = ('title', 'publish', 'status')
+ list_filter = ('publish', 'categories', 'status')
+ search_fields = ('title', 'body')
+
+ def __unicode__(self):
+ return u'%s' % self.title
+
+ @permalink
+ def get_absolute_url(self):
+ return ('blog_detail', None, {
+ 'year': self.publish.year,
+ 'month': self.publish.strftime('%b').lower(),
+ 'day': self.publish.day,
+ 'slug': self.slug
+ })
+
+ def get_previous_post(self):
+ return self.get_previous_by_publish(status__gte=2)
+
+ def get_next_post(self):
+ return self.get_next_by_publish(status__gte=2)
diff --git a/sites/scrapy.org/scrapyorg/blog/sitemap.py b/sites/scrapy.org/scrapyorg/blog/sitemap.py
new file mode 100644
index 000000000..7b1ed9d25
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/sitemap.py
@@ -0,0 +1,13 @@
+from django.contrib.sitemaps import Sitemap
+from scrapyorg.blog.models import Post
+
+
+class BlogSitemap(Sitemap):
+ changefreq = "never"
+ priority = 0.5
+
+ def items(self):
+ return Post.objects.published()
+
+ def lastmod(self, obj):
+ return obj.publish
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/base.html b/sites/scrapy.org/scrapyorg/blog/templates/base.html
new file mode 100644
index 000000000..880d8a996
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/base.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ {% block title %}{% endblock %}
+
+
+
+ {% block body %}
+
+ {% block content_title %}{% endblock %}
+
+
+ {% block content %}{% endblock %}
+
+ {% endblock %}
+
+
+
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/base_blog.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/base_blog.html
new file mode 100644
index 000000000..48a039f7d
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/base_blog.html
@@ -0,0 +1,5 @@
+{% extends "base.html" %}
+oaihdasasihjsd
+
+
+{% block body_class %}blog{% endblock %}
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/category_detail.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/category_detail.html
new file mode 100644
index 000000000..23a257976
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/category_detail.html
@@ -0,0 +1,25 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Posts for {{ category.title }}{% endblock %}
+{% block body_class %}{{ block.super }} category_detail{% endblock %}
+{% block body_id %}category_{{ category.id }}{% endblock %}
+
+
+{% block content_title %}
+ Posts for {{ category.title }}
+{% endblock %}
+
+
+{% block content %}
+ {% load markup %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/category_list.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/category_list.html
new file mode 100644
index 000000000..6ec01e46b
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/category_list.html
@@ -0,0 +1,20 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post categories{% endblock %}
+{% block body_class %}{{ block.super }} category_list{% endblock %}
+
+
+{% block content_title %}
+ Post categories
+{% endblock %}
+
+
+{% block content %}
+ {% load markup %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_day.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_day.html
new file mode 100644
index 000000000..b9d9ab97f
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_day.html
@@ -0,0 +1,23 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive for {{ day|date:"d F Y" }}{% endblock %}
+{% block body_class %}{{ block.super }} post_archive_day{% endblock %}
+
+
+{% block content_title %}
+ Post archive for {{ day|date:"d F Y" }}
+{% endblock %}
+
+
+{% block content %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_month.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_month.html
new file mode 100644
index 000000000..947a1cc7d
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_month.html
@@ -0,0 +1,23 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive for {{ month|date:"F Y" }}{% endblock %}
+{% block body_class %}{{ block.super }} post_archive_month{% endblock %}
+
+
+{% block content_title %}
+ Post archive for {{ month|date:"F Y" }}
+{% endblock %}
+
+
+{% block content %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_year.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_year.html
new file mode 100644
index 000000000..f0255220b
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_archive_year.html
@@ -0,0 +1,21 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive for {{ year }}{% endblock %}
+{% block body_class %}{{ block.super }} post_archive_year{% endblock %}
+
+
+{% block content_title %}
+ Post archive for {{ year }}
+{% endblock %}
+
+
+{% block content %}
+ {% load markup %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/post_detail.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_detail.html
new file mode 100644
index 000000000..a4247dcc4
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_detail.html
@@ -0,0 +1,67 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}{{ object.title }}{% endblock %}
+{% block body_class %}{{ block.super }} post_detail{% endblock %}
+{% block body_id %}post_{{ object.id }}{% endblock %}
+
+
+{% block content_title %}
+ {{ object.title }}
+
+ {% if object.get_previous_by_publish %}
+ « {{ object.get_previous_post }}
+ {% endif %}
+ {% if object.get_next_by_publish %}
+ | {{ object.get_next_post }} »
+ {% endif %}
+
+{% endblock %}
+
+
+{% block content %}
+ {% load blog markup comments tagging_tags %}
+
+ {{ object.publish|date:"j F Y" }}
+
+
+ {{ object.body|markdown:"safe" }}
+
+
+ {% tags_for_object object as tag_list %}
+ {% if tag_list %}
+ Related tags:
+ {% for tag in tag_list %}
+ {{ tag }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+ {% endif %}
+
+ {% get_comment_list for object as comment_list %}
+ {% if comment_list %}
+
+ {% endif %}
+ {% if object.allow_comments %}
+ {% render_comment_form for object %}
+ {% else %}
+
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/post_list.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_list.html
new file mode 100644
index 000000000..399b1f0e6
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_list.html
@@ -0,0 +1,35 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive{% endblock %}
+{% block body_class %}{{ block.super }} post_list{% endblock %}
+
+
+{% block content_title %}
+ Post archive
+{% endblock %}
+
+
+{% block content %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+
+ {% if is_paginated %}
+
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/blog/post_search.html b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_search.html
new file mode 100644
index 000000000..2884333f8
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/blog/post_search.html
@@ -0,0 +1,37 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post search{% endblock %}
+{% block body_class %}{{ block.super }} post_search{% endblock %}
+
+
+{% block content_title %}
+ Search
+{% endblock %}
+
+
+{% block content %}
+
+
+ {% if message %}
+ {{ message }}
+ {% endif %}
+
+ {% if object_list %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+
+ {% endfor %}
+
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/feeds/posts_description.html b/sites/scrapy.org/scrapyorg/blog/templates/feeds/posts_description.html
new file mode 100644
index 000000000..99216e812
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/feeds/posts_description.html
@@ -0,0 +1 @@
+{{ obj.tease }}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates/feeds/posts_title.html b/sites/scrapy.org/scrapyorg/blog/templates/feeds/posts_title.html
new file mode 100644
index 000000000..7899fce3e
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates/feeds/posts_title.html
@@ -0,0 +1 @@
+{{ obj.title }}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates_backup/admin/blog/post/change_form.html b/sites/scrapy.org/scrapyorg/blog/templates_backup/admin/blog/post/change_form.html
new file mode 100644
index 000000000..08c034b77
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates_backup/admin/blog/post/change_form.html
@@ -0,0 +1,56 @@
+{% extends "admin/change_form.html" %}
+
+{% block extrahead %}
+ {% load adminmedia inlines %}
+ {{ block.super }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templates_backup/inlines/default.html b/sites/scrapy.org/scrapyorg/blog/templates_backup/inlines/default.html
new file mode 100644
index 000000000..5510ba952
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templates_backup/inlines/default.html
@@ -0,0 +1,7 @@
+{% if object %}
+ {{ object }}
+{% else %}
+ {% for object in object_list %}
+ {{ object }}
+ {% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/templatetags/__init__.py b/sites/scrapy.org/scrapyorg/blog/templatetags/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/sites/scrapy.org/scrapyorg/blog/templatetags/blog.py b/sites/scrapy.org/scrapyorg/blog/templatetags/blog.py
new file mode 100644
index 000000000..e80918cb3
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/templatetags/blog.py
@@ -0,0 +1,103 @@
+from django import template
+from django.conf import settings
+from django.db import models
+
+import re
+
+Post = models.get_model('blog', 'post')
+Category = models.get_model('blog', 'category')
+
+register = template.Library()
+
+
+class LatestPosts(template.Node):
+ def __init__(self, limit, var_name):
+ self.limit = limit
+ self.var_name = var_name
+
+ def render(self, context):
+ posts = Post.objects.published()[:int(self.limit)]
+ if posts and (int(self.limit) == 1):
+ context[self.var_name] = posts[0]
+ else:
+ context[self.var_name] = posts
+ return ''
+
+@register.tag
+def get_latest_posts(parser, token):
+ """
+ Gets any number of latest posts and stores them in a varable.
+
+ Syntax::
+
+ {% get_latest_posts [limit] as [var_name] %}
+
+ Example usage::
+
+ {% get_latest_posts 10 as latest_post_list %}
+ """
+ try:
+ tag_name, arg = token.contents.split(None, 1)
+ except ValueError:
+ raise template.TemplateSyntaxError, "%s tag requires arguments" % token.contents.split()[0]
+ m = re.search(r'(.*?) as (\w+)', arg)
+ if not m:
+ raise template.TemplateSyntaxError, "%s tag had invalid arguments" % tag_name
+ format_string, var_name = m.groups()
+ return LatestPosts(format_string, var_name)
+
+
+class BlogCategories(template.Node):
+ def __init__(self, var_name):
+ self.var_name = var_name
+
+ def render(self, context):
+ categories = Category.objects.all()
+ context[self.var_name] = categories
+ return ''
+
+@register.tag
+def get_blog_categories(parser, token):
+ """
+ Gets all blog categories.
+
+ Syntax::
+
+ {% get_blog_categories as [var_name] %}
+
+ Example usage::
+
+ {% get_blog_categories as category_list %}
+ """
+ try:
+ tag_name, arg = token.contents.split(None, 1)
+ except ValueError:
+ raise template.TemplateSyntaxError, "%s tag requires arguments" % token.contents.split()[0]
+ m = re.search(r'as (\w+)', arg)
+ if not m:
+ raise template.TemplateSyntaxError, "%s tag had invalid arguments" % tag_name
+ var_name = m.groups()[0]
+ return BlogCategories(var_name)
+
+
+@register.filter
+def get_links(value):
+ """
+ Extracts links from a ``Post`` body and returns a list.
+
+ Template Syntax::
+
+ {{ post.body|markdown:"safe"|get_links }}
+
+ """
+ try:
+ try:
+ from BeautifulSoup import BeautifulSoup
+ except ImportError:
+ from beautifulsoup import BeautifulSoup
+ soup = BeautifulSoup(value)
+ return soup.findAll('a')
+ except ImportError:
+ if settings.DEBUG:
+ raise template.TemplateSyntaxError, "Error in 'get_links' filter: BeautifulSoup isn't installed."
+ return value
diff --git a/sites/scrapy.org/scrapyorg/blog/tests.py b/sites/scrapy.org/scrapyorg/blog/tests.py
new file mode 100644
index 000000000..1dd87451d
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/tests.py
@@ -0,0 +1,66 @@
+"""
+>>> from django.test import Client
+>>> from scrapyorg.blog.models import Post, Category
+>>> import datetime
+>>> from django.core.urlresolvers import reverse
+>>> client = Client()
+
+>>> category = Category(title='Django', slug='django')
+>>> category.save()
+>>> category2 = Category(title='Rails', slug='rails')
+>>> category2.save()
+
+>>> post = Post(title='DJ Ango', slug='dj-ango', body='Yo DJ! Turn that music up!', status=2, publish=datetime.datetime(2008,5,5,16,20))
+>>> post.save()
+
+>>> post2 = Post(title='Where my grails at?', slug='where', body='I Can haz Holy plez?', status=2, publish=datetime.datetime(2008,4,2,11,11))
+>>> post2.save()
+
+>>> post.categories.add(category)
+>>> post2.categories.add(category2)
+
+>>> response = client.get(reverse('blog_index'))
+>>> response.context[-1]['object_list']
+[, ]
+>>> response.status_code
+200
+
+>>> response = client.get(reverse('blog_category_list'))
+>>> response.context[-1]['object_list']
+[, ]
+>>> response.status_code
+200
+
+>>> response = client.get(category.get_absolute_url())
+>>> response.context[-1]['object_list']
+[]
+>>> response.status_code
+200
+
+>>> response = client.get(post.get_absolute_url())
+>>> response.context[-1]['object']
+
+>>> response.status_code
+200
+
+>>> response = client.get(reverse('blog_search'), {'q': 'DJ'})
+>>> response.context[-1]['object_list']
+[]
+>>> response.status_code
+200
+>>> response = client.get(reverse('blog_search'), {'q': 'Holy'})
+>>> response.context[-1]['object_list']
+[]
+>>> response.status_code
+200
+>>> response = client.get(reverse('blog_search'), {'q': ''})
+>>> response.context[-1]['message']
+'Search term was too vague. Please try again.'
+
+>>> response = client.get(reverse('blog_detail', args=[2008, 'apr', 2, 'where']))
+>>> response.context[-1]['object']
+
+>>> response.status_code
+200
+"""
+
diff --git a/sites/scrapy.org/scrapyorg/blog/urls.py b/sites/scrapy.org/scrapyorg/blog/urls.py
new file mode 100644
index 000000000..53aa0f404
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/urls.py
@@ -0,0 +1,41 @@
+from django.conf.urls.defaults import *
+from scrapyorg.blog import views as blog_views
+
+
+urlpatterns = patterns('',
+ url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/(?P[-\w]+)/$',
+ view=blog_views.post_detail,
+ name='blog_detail'),
+
+ url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/$',
+ view=blog_views.post_archive_day,
+ name='blog_archive_day'),
+
+ url(r'^(?P\d{4})/(?P\w{3})/$',
+ view=blog_views.post_archive_month,
+ name='blog_archive_month'),
+
+ url(r'^(?P\d{4})/$',
+ view=blog_views.post_archive_year,
+ name='blog_archive_year'),
+
+ url(r'^categories/(?P[-\w]+)/$',
+ view=blog_views.category_detail,
+ name='blog_category_detail'),
+
+ url (r'^categories/$',
+ view=blog_views.category_list,
+ name='blog_category_list'),
+
+ url (r'^search/$',
+ view=blog_views.search,
+ name='blog_search'),
+
+ url(r'^page/(?P\w)/$',
+ view=blog_views.post_list,
+ name='blog_index_paginated'),
+
+ url(r'^$',
+ view=blog_views.post_list,
+ name='blog_index'),
+)
\ No newline at end of file
diff --git a/sites/scrapy.org/scrapyorg/blog/views.py b/sites/scrapy.org/scrapyorg/blog/views.py
new file mode 100644
index 000000000..ad65484b5
--- /dev/null
+++ b/sites/scrapy.org/scrapyorg/blog/views.py
@@ -0,0 +1,160 @@
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+from django.http import Http404
+from django.views.generic import date_based, list_detail
+from django.db.models import Q
+from scrapyorg.blog.models import *
+
+import datetime
+import re
+
+
+def post_list(request, page=0, **kwargs):
+ return list_detail.object_list(
+ request,
+ queryset = Post.objects.published(),
+ paginate_by = 20,
+ page = page,
+ **kwargs
+ )
+post_list.__doc__ = list_detail.object_list.__doc__
+
+
+def post_archive_year(request, year, **kwargs):
+ return date_based.archive_year(
+ request,
+ year = year,
+ date_field = 'publish',
+ queryset = Post.objects.published(),
+ make_object_list = True,
+ **kwargs
+ )
+post_archive_year.__doc__ = date_based.archive_year.__doc__
+
+
+def post_archive_month(request, year, month, **kwargs):
+ return date_based.archive_month(
+ request,
+ year = year,
+ month = month,
+ date_field = 'publish',
+ queryset = Post.objects.published(),
+ **kwargs
+ )
+post_archive_month.__doc__ = date_based.archive_month.__doc__
+
+
+def post_archive_day(request, year, month, day, **kwargs):
+ return date_based.archive_day(
+ request,
+ year = year,
+ month = month,
+ day = day,
+ date_field = 'publish',
+ queryset = Post.objects.published(),
+ **kwargs
+ )
+post_archive_day.__doc__ = date_based.archive_day.__doc__
+
+
+def post_detail(request, slug, year, month, day, **kwargs):
+ return date_based.object_detail(
+ request,
+ year = year,
+ month = month,
+ day = day,
+ date_field = 'publish',
+ slug = slug,
+ queryset = Post.objects.published(),
+ **kwargs
+ )
+post_detail.__doc__ = date_based.object_detail.__doc__
+
+
+def category_list(request, template_name = 'blog/category_list.html', **kwargs):
+ """
+ Category list
+
+ Template: ``blog/category_list.html``
+ Context:
+ object_list
+ List of categories.
+ """
+ return list_detail.object_list(
+ request,
+ queryset = Category.objects.all(),
+ template_name = template_name,
+ **kwargs
+ )
+
+def category_detail(request, slug, template_name = 'blog/category_detail.html', **kwargs):
+ """
+ Category detail
+
+ Template: ``blog/category_detail.html``
+ Context:
+ object_list
+ List of posts specific to the given category.
+ category
+ Given category.
+ """
+ category = get_object_or_404(Category, slug__iexact=slug)
+
+ return list_detail.object_list(
+ request,
+ queryset = category.post_set.published(),
+ extra_context = {'category': category},
+ template_name = template_name,
+ **kwargs
+ )
+
+
+# Stop Words courtesy of http://www.dcs.gla.ac.uk/idom/ir_resources/linguistic_utils/stop_words
+STOP_WORDS = r"""\b(a|about|above|across|after|afterwards|again|against|all|almost|alone|along|already|also|
+although|always|am|among|amongst|amoungst|amount|an|and|another|any|anyhow|anyone|anything|anyway|anywhere|are|
+around|as|at|back|be|became|because|become|becomes|becoming|been|before|beforehand|behind|being|below|beside|
+besides|between|beyond|bill|both|bottom|but|by|call|can|cannot|cant|co|computer|con|could|couldnt|cry|de|describe|
+detail|do|done|down|due|during|each|eg|eight|either|eleven|else|elsewhere|empty|enough|etc|even|ever|every|everyone|
+everything|everywhere|except|few|fifteen|fify|fill|find|fire|first|five|for|former|formerly|forty|found|four|from|
+front|full|further|get|give|go|had|has|hasnt|have|he|hence|her|here|hereafter|hereby|herein|hereupon|hers|herself|
+him|himself|his|how|however|hundred|i|ie|if|in|inc|indeed|interest|into|is|it|its|itself|keep|last|latter|latterly|
+least|less|ltd|made|many|may|me|meanwhile|might|mill|mine|more|moreover|most|mostly|move|much|must|my|myself|name|
+namely|neither|never|nevertheless|next|nine|no|nobody|none|noone|nor|not|nothing|now|nowhere|of|off|often|on|once|
+one|only|onto|or|other|others|otherwise|our|ours|ourselves|out|over|own|part|per|perhaps|please|put|rather|re|same|
+see|seem|seemed|seeming|seems|serious|several|she|should|show|side|since|sincere|six|sixty|so|some|somehow|someone|
+something|sometime|sometimes|somewhere|still|such|system|take|ten|than|that|the|their|them|themselves|then|thence|
+there|thereafter|thereby|therefore|therein|thereupon|these|they|thick|thin|third|this|those|though|three|through|
+throughout|thru|thus|to|together|too|top|toward|towards|twelve|twenty|two|un|under|until|up|upon|us|very|via|was|
+we|well|were|what|whatever|when|whence|whenever|where|whereafter|whereas|whereby|wherein|whereupon|wherever|whether|
+which|while|whither|who|whoever|whole|whom|whose|why|will|with|within|without|would|yet|you|your|yours|yourself|
+yourselves)\b"""
+
+
+def search(request, template_name='blog/post_search.html'):
+ """
+ Search for blog posts.
+
+ This template will allow you to setup a simple search form that will try to return results based on
+ given search strings. The queries will be put through a stop words filter to remove words like
+ 'the', 'a', or 'have' to help imporve the result set.
+
+ Template: ``blog/post_search.html``
+ Context:
+ object_list
+ List of blog posts that match given search term(s).
+ search_term
+ Given search term.
+ """
+ context = {}
+ if request.GET:
+ stop_word_list = re.compile(STOP_WORDS, re.IGNORECASE)
+ search_term = '%s' % request.GET['q']
+ cleaned_search_term = stop_word_list.sub('', search_term)
+ cleaned_search_term = cleaned_search_term.strip()
+ if len(cleaned_search_term) != 0:
+ post_list = Post.objects.published().filter(Q(body__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term) | Q(categories__title__icontains=cleaned_search_term))
+ context = {'object_list': post_list, 'search_term':search_term}
+ else:
+ message = 'Search term was too vague. Please try again.'
+ context = {'message':message}
+ return render_to_response(template_name, context, context_instance=RequestContext(request))
diff --git a/sites/scrapy.org/scrapyorg/settings.py b/sites/scrapy.org/scrapyorg/settings.py
index bd9bd45f1..817b820b7 100644
--- a/sites/scrapy.org/scrapyorg/settings.py
+++ b/sites/scrapy.org/scrapyorg/settings.py
@@ -77,9 +77,11 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.markup',
+ 'django.contrib.comments',
'scrapyorg.article',
'scrapyorg.download',
'scrapyorg.docs',
+ 'scrapyorg.blog',
)
DOC_PICKLE_ROOT = os.path.join(PROJECT_ROOT, 'docs', 'pickle')
diff --git a/sites/scrapy.org/scrapyorg/urls.py b/sites/scrapy.org/scrapyorg/urls.py
index c401bdadb..c61fe0d13 100644
--- a/sites/scrapy.org/scrapyorg/urls.py
+++ b/sites/scrapy.org/scrapyorg/urls.py
@@ -13,6 +13,8 @@ urlpatterns = patterns('',
# docs
url(r"^docs/", include("scrapyorg.docs.urls")),
+ # blog
+ url(r"^blog/", include("scrapyorg.blog.urls")),
)
diff --git a/sites/scrapy.org/templates/blog/base_blog.html b/sites/scrapy.org/templates/blog/base_blog.html
new file mode 100644
index 000000000..48a039f7d
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/base_blog.html
@@ -0,0 +1,5 @@
+{% extends "base.html" %}
+oaihdasasihjsd
+
+
+{% block body_class %}blog{% endblock %}
diff --git a/sites/scrapy.org/templates/blog/category_detail.html b/sites/scrapy.org/templates/blog/category_detail.html
new file mode 100644
index 000000000..880c37515
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/category_detail.html
@@ -0,0 +1,25 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Posts for {{ category.title }}{% endblock %}
+{% block body_class %}{{ block.super }} category_detail{% endblock %}
+{% block body_id %}category_{{ category.id }}{% endblock %}
+
+
+{% block main-content_title %}
+ Posts for {{ category.title }}
+{% endblock %}
+
+
+{% block main-content %}
+ {% load markup %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/templates/blog/category_list.html b/sites/scrapy.org/templates/blog/category_list.html
new file mode 100644
index 000000000..d136b4bd1
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/category_list.html
@@ -0,0 +1,20 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post categories{% endblock %}
+{% block body_class %}{{ block.super }} category_list{% endblock %}
+
+
+{% block main-content_title %}
+ Post categories
+{% endblock %}
+
+
+{% block main-content %}
+ {% load markup %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/templates/blog/post_archive_day.html b/sites/scrapy.org/templates/blog/post_archive_day.html
new file mode 100644
index 000000000..4c95f8cf0
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/post_archive_day.html
@@ -0,0 +1,23 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive for {{ day|date:"d F Y" }}{% endblock %}
+{% block body_class %}{{ block.super }} post_archive_day{% endblock %}
+
+
+{% block main-content_title %}
+ Post archive for {{ day|date:"d F Y" }}
+{% endblock %}
+
+
+{% block main-content %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/templates/blog/post_archive_month.html b/sites/scrapy.org/templates/blog/post_archive_month.html
new file mode 100644
index 000000000..45c4c53e4
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/post_archive_month.html
@@ -0,0 +1,23 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive for {{ month|date:"F Y" }}{% endblock %}
+{% block body_class %}{{ block.super }} post_archive_month{% endblock %}
+
+
+{% block main-content_title %}
+ Post archive for {{ month|date:"F Y" }}
+{% endblock %}
+
+
+{% block main-content %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/templates/blog/post_archive_year.html b/sites/scrapy.org/templates/blog/post_archive_year.html
new file mode 100644
index 000000000..1a176ef9d
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/post_archive_year.html
@@ -0,0 +1,21 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive for {{ year }}{% endblock %}
+{% block body_class %}{{ block.super }} post_archive_year{% endblock %}
+
+
+{% block main-content_title %}
+ Post archive for {{ year }}
+{% endblock %}
+
+
+{% block main-content %}
+ {% load markup %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/sites/scrapy.org/templates/blog/post_detail.html b/sites/scrapy.org/templates/blog/post_detail.html
new file mode 100644
index 000000000..15bcbb2c1
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/post_detail.html
@@ -0,0 +1,72 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}{{ object.title }}{% endblock %}
+{% block body_class %}{{ block.super }} post_detail{% endblock %}
+{% block body_id %}post_{{ object.id }}{% endblock %}
+
+
+{% block main-content_title %}
+ {{ object.title }}
+
+ {% if object.get_previous_by_publish %}
+ « {{ object.get_previous_post }}
+ {% endif %}
+ {% if object.get_next_by_publish %}
+ | {{ object.get_next_post }} »
+ {% endif %}
+
+{% endblock %}
+
+
+{% block main-content %}
+ {% load blog markup comments %}
+ {% comment %}
+ {% load tagging_tags %}
+ {% endcomment %}
+
+ {{ object.publish|date:"j F Y" }}
+
+
+ {{ object.body|markdown:"safe" }}
+
+
+ {% comment %}
+ {% tags_for_object object as tag_list %}
+ {% if tag_list %}
+ Related tags:
+ {% for tag in tag_list %}
+ {{ tag }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+ {% endif %}
+ {% endcomment %}
+
+ {% get_comment_list for object as comment_list %}
+ {% if comment_list %}
+
+ {% endif %}
+ {% if object.allow_comments %}
+ {% render_comment_form for object %}
+ {% else %}
+
+ {% endif %}
+{% endblock %}
diff --git a/sites/scrapy.org/templates/blog/post_list.html b/sites/scrapy.org/templates/blog/post_list.html
new file mode 100644
index 000000000..5f8d8c1cd
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/post_list.html
@@ -0,0 +1,35 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post archive{% endblock %}
+{% block body_class %}{{ block.super }} post_list{% endblock %}
+
+
+{% block main-content_title %}
+ Post archive
+{% endblock %}
+
+
+{% block main-content %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+ {% endfor %}
+
+
+ {% if is_paginated %}
+
+ {% endif %}
+{% endblock %}
diff --git a/sites/scrapy.org/templates/blog/post_search.html b/sites/scrapy.org/templates/blog/post_search.html
new file mode 100644
index 000000000..271000351
--- /dev/null
+++ b/sites/scrapy.org/templates/blog/post_search.html
@@ -0,0 +1,37 @@
+{% extends "blog/base_blog.html" %}
+
+
+{% block title %}Post search{% endblock %}
+{% block body_class %}{{ block.super }} post_search{% endblock %}
+
+
+{% block main-content_title %}
+ Search
+{% endblock %}
+
+
+{% block main-content %}
+
+
+ {% if message %}
+ {{ message }}
+ {% endif %}
+
+ {% if object_list %}
+
+ {% for post in object_list %}
+
+
+
{{ post.publish|date:"Y F d" }}
+
{{ post.tease }}
+
+
+ {% endfor %}
+
+ {% endif %}
+{% endblock %}
\ No newline at end of file
Comments
+ {% for comment in comment_list %} + {% if comment.is_public %} ++ {{ forloop.counter }} + {% if comment.user_url %}{{ comment.user_name }}{% else %}{{ comment.user_name }}{% endif %} says... +
+ {{ comment.comment|urlizetrunc:"60"|markdown:"safe" }} +Posted at {{ comment.submit_date|date:"P" }} on {{ comment.submit_date|date:"F j, Y" }}
+