1. Curtis Maloney
  2. gnocchi-blog

Commits

cur...@tinbrain.net  committed 4b294fe

Renamed source dir to avoid confusion

  • Participants
  • Parent commits de35bc8
  • Branches default

Comments (0)

Files changed (26)

File blog/__init__.py

Empty file removed.

File blog/admin.py

-from django.contrib import admin
-from gnocchi_blog import models
-
-register = admin.site.register
-
-class BlogPostAdmin(admin.ModelAdmin):
-    list_display = ('title', 'published', 'post_date', 'kill_date', 'posted_by', 'allow_comments', )
-    list_filter = ('posted_by', 'published',)
-    date_heirarchy = 'post_date'
-    search_fields = ('title', 'content',)
-    prepopulated_fields = {'slug': ('title',)}
-    exclude = ('posted_by',)
-    actions = ('publish_newsletter',)
-
-    def queryset(self, request):
-        qset = self.model.objects.all()
-        if not request.user.is_superuser:
-            return qset.filter(posted_by=request.user)
-        return qset
-
-    def save_model(self, request, obj, form, change):
-        if not change:
-            obj.posted_by = request.user
-        obj.save()
-
-register( models.BlogPost, BlogPostAdmin )

File blog/feeds.py

-from django.contrib.syndication.views import Feed
-from gnocchi_blog.models import BlogPost
-from taggit.models import TaggedItem
-
-class LatestBlogFeed(Feed):
-    '''Basic feed configuration
-
-    You need to derive your own class from this to set link, and you will
-    probably also want to change description and title.
-
-    See http://docs.djangoproject.com/en/1.2/ref/contrib/syndication/
-    '''
-    description = 'Blog Posts'
-    title = 'Blog'
-
-    def categories(self):
-        return TaggedItem.tags_for(BlogPost)
-
-    def items(self):
-        return BlogPost.objects.current()
-
-    def item_title(self, item):
-        return item.title
-
-    def item_author_name(self, item):
-        return item.posted_by.get_full_name()
-
-    def item_pubdate(self, item):
-        return item.post_date
-
-    def item_categories(self, item):
-        return item.tags

File blog/models.py

-from django.db import models
-from django.core.urlresolvers import reverse
-
-from taggit.managers import TaggableManager
-
-from datetime import datetime
-
-class BlogManager(models.Manager):
-    def current(self):
-        now = datetime.now()
-        return self.get_query_set().filter(
-            published=True,
-            post_date__lte=now,
-        ).exclude(
-            kill_date__lte=now
-        )
-
-class BlogPost(models.Model):
-    title = models.CharField(max_length=200)
-    slug = models.SlugField()
-    content = models.TextField(blank=True)
-
-    post_date = models.DateTimeField(default=datetime.now)
-    kill_date = models.DateTimeField(null=True, blank=True, default=None)
-    posted_by = models.ForeignKey('auth.User', blank=True, null=True)
-    published = models.BooleanField(default=False)
-
-    allow_comments = models.BooleanField(default=True)
-
-    tags = TaggableManager()
-
-    objects = BlogManager()
-
-    class Meta:
-        ordering = ('-post_date',)
-    def __unicode__(self):
-        return self.title
-    def get_absolute_url(self):
-        return reverse('blog_detail', kwargs={
-            'slug': self.slug,
-            'year': '%04d' % self.post_date.year,
-            'month': self.post_date.strftime('%b').lower(),
-            'day': '%02d' % self.post_date.day,
-        })
-

File blog/recaptcha.py

-"""
-An easy-to-use Django forms integration of the reCaptcha service.
-v1.0.1
-
-To use, simply base your form off the ``RecaptchaForm`` class. This class adds
-a new argument that must be provided to the form, ``remote_ip``.
-
-Two settings which must be set in your project's ``settings`` module are 
-``RECAPTCHA_PUBLIC_KEY`` and ``RECAPTCHA_SECRET_KEY``, the public and private
-keys for your domain, respectively. 
-
-Following is an example of creating a basic comment form class and then
-using an instance of the form in a view::
-
-    from django import forms
-    from mysite.utils import recaptcha
-
-    class CommentForm(recaptcha.RecaptchaForm):
-        name = forms.CharField()
-        comment = forms.CharField(widget=Textarea())
-        captcha = recaptcha.RecaptchaField()
-
-    def comment(request):
-        comment_form = CommentForm(remote_ip=request.META['REMOTE_ADDR'])
-        ...
-
-If you need to use a different base form (such as ``ModelForm``), use multiple
-inheritance like so::
-
-    class MyModelForm(BaseRecaptchaForm, ModelForm):
-        ...
-"""
-
-import httplib
-from django import forms
-from django.conf import settings
-from django.utils.safestring import mark_safe
-from django.utils.http import urlencode
-
-OPTIONS_SCRIPT_HTML = u'''<script type="text/javascript">
-   var RecaptchaOptions = %r;
-</script>
-'''
-RECAPTCHA_HTML = u'''%(options)s<script type="text/javascript" src="http://api.recaptcha.net/challenge?k=%(public_key)s"></script>
-<noscript>
-   <iframe src="http://api.recaptcha.net/noscript?k=%(public_key)s"
-       height="300" width="500" frameborder="0"></iframe><br />
-   <textarea name="recaptcha_challenge_field" rows="3" cols="40">
-   </textarea>
-   <input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
-</noscript>'''
-
-
-class RecaptchaWidget(forms.Widget):
-    def __init__(self, theme=None, tabindex=None, public_key=None):
-        '''
-        From http://recaptcha.net/apidocs/captcha/client.html#look-n-feel::
-
-            theme:      'red' | 'white' | 'blackglass' | 'clean'
-
-                Defines which theme to use for reCAPTCHA.
-
-            tabindex:   any integer
-
-                Sets a tabindex for the reCAPTCHA text box. If other elements
-                in the form use a tabindex, this should be set so that
-                navigation is easier for the user.
-
-        The optional ``public_key`` argument can be used to override the
-        default use of the project-wide ``RECAPTCHA_PUBLIC_KEY`` setting.
-        '''
-        options = {}
-        if theme:
-            options['theme'] = theme
-        if tabindex:
-            options['tabindex'] = tabindex
-        self.options = options
-        self.public_key = public_key or settings.RECAPTCHA_PUBLIC_KEY
-        super(RecaptchaWidget, self).__init__()
-
-    def render(self, name, value, attrs=None):
-        args = dict(public_key=self.public_key, options='')
-        if self.options:
-            args['options'] = OPTIONS_SCRIPT_HTML % self.options
-        return mark_safe(RECAPTCHA_HTML % args)
-
-    def value_from_datadict(self, data, files, name):
-        challenge = data.get('recaptcha_challenge_field')
-        response = data.get('recaptcha_response_field')
-        return (challenge, response)
-
-    def id_for_label(self, id_):
-        return None
-
-
-class RecaptchaField(forms.Field):
-    widget = RecaptchaWidget
-    default_error_messages = {
-        'required': u'Please enter the CAPTCHA solution.',
-        'invalid': u'An incorrect CAPTCHA solution was entered.',
-        'no-remote-ip': u'CAPTCHA failed due to no visible IP address.',
-        'challenge-error': u'An error occurred with the CAPTCHA service - try '
-            'refreshing.',
-        'unknown-error': u'The CAPTCHA service returned the following error: '
-            '%(code)s.',
-    }
-
-    def __init__(self, private_key=None, *args, **kwargs):
-        """
-        The optional ``private_key`` argument can be used to override the
-        default use of the project-wide ``RECAPTCHA_SECRET_KEY`` setting.
-        """
-        self.remote_ip = None
-        self.private_key = private_key or settings.RECAPTCHA_SECRET_KEY
-        super(RecaptchaField, self).__init__(*args, **kwargs)
-
-    def clean(self, value):
-        if not self.remote_ip:
-            raise forms.ValidationError(self.error_messages['no-remote-ip'])
-        value = super(RecaptchaField, self).clean(value)
-        challenge, response = value
-        if not challenge:
-            raise forms.ValidationError(self.error_messages['challenge-error'])
-        if not response:
-            raise forms.ValidationError(self.error_messages['required'])
-        try:
-            value = validate_recaptcha(self.remote_ip, challenge, response,
-                                       self.private_key)
-        except RecaptchaError, e:
-            if e.code == 'incorrect-captcha-sol':
-                raise forms.ValidationError(self.error_messages['invalid'])
-            raise forms.ValidationError(self.error_messages['unknown-error'] %
-                                        {'code': e.code})
-        return value
-
-
-class BaseRecaptchaForm(forms.BaseForm):
-    def __init__(self, remote_ip, *args, **kwargs):
-        super(BaseRecaptchaForm, self).__init__(*args, **kwargs)
-        for field in self.fields.values():
-            if isinstance(field, RecaptchaField):
-                field.remote_ip = remote_ip
-
-
-class RecaptchaForm(BaseRecaptchaForm, forms.Form):
-    pass
-
-
-class RecaptchaError(Exception):
-    def __init__(self, code):
-        self.code = code
-
-    def __str__(self):
-        return self.code
-
-
-def validate_recaptcha(remote_ip, challenge, response, private_key):
-    assert challenge, 'No challenge was provided for reCaptcha validation'
-    # Request validation from recaptcha.net
-    params = dict(privatekey=private_key, remoteip=remote_ip,
-                  challenge=challenge, response=response)
-    params = urlencode(params)
-    headers = {"Content-type": "application/x-www-form-urlencoded",
-               "Accept": "text/plain"}
-    conn = httplib.HTTPConnection("api-verify.recaptcha.net")
-    conn.request("POST", "/verify", params, headers)
-    response = conn.getresponse()
-    if response.status == 200:
-        data = response.read()
-    else:
-        data = ''
-    conn.close()
-    # Validate based on response data
-    result = data.startswith('true')
-    if not result:
-        bits = data.split('\n', 2)
-        error_code = ''
-        if len(bits) > 1:
-            error_code = bits[1]
-        raise RecaptchaError(error_code)
-    # Return dictionary
-    return result

File blog/search_indexes.py

-
-from haystack.indexes import SearchIndex, CharField
-from haystack import site
-from gnocchi_blog.models import BlogPost
-
-class BlogPostIndex(SearchIndex):
-    text = CharField(document=True, use_template=True)
-    title = CharField(model_attr='title')
-
-site.register(BlogPost, BlogPostIndex)

File blog/sitemap.py

Empty file removed.

File blog/templates/search/indexes/blog/blogpost_text.txt

-{{ object.title }}
-{{ object.content }}
-

File blog/templatetags/__init__.py

Empty file removed.

File blog/templatetags/blog.py

-from django import template
-from gnocchi_blog.models import BlogPost
-from gnocchi_tools.template import parse_args
-
-register = template.Library()
-
-class RecentPostsNode(template.Node):
-    def __init__(self, count, varname, username=None, *tags):
-        self.count = count
-        self.varname = varname
-        self.tags = tags
-        self.username = username
-    def render(self, context):
-        tags = filter(None, [tag.resolve(context) for tag in self.tags])
-        varname = self.varname
-        if not isinstance(varname, basestring):
-            varname = varname.resolve(context)
-        username = self.username
-        if username and not isinstance(username, basestring):
-            username = username.resolve(context)
-
-        qset = BlogPost.objects.current()
-        if tags:
-            qset = qset.filter(tag__in=tags)
-        if username:
-            qset = qset.filter(posted_by__username=username)
-        context[varname] = qset
-        return ''
-
-@register.tag
-def get_recent_posts(parser, token):
-    args, kwargs, as_name = parse_args(parser, token, 'posts')
-    count = int(args.pop(0))
-    return RecentPostsNode(count, as_name, *args, **kwargs)
-
-@register.filter
-def recent_posts(value, username=None):
-    qset = BlogPost.objects.current()
-    if username:
-        qset.filter(posted_by__username=username)
-    try:
-        return qset[:int(value)]
-    except BlogPost.DoesNotExist:
-        return []
-

File blog/urls.py

-from django.conf.urls.defaults import patterns, url
-
-urlpatterns = patterns('gnocchi_blog.views',
-    url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[\w-]+)/$',
-        'blog_detail',
-        name='blog_detail',
-    ),
-    url(r'^$', 'blog_list', ),
-)
-
-

File blog/views.py

-from gnocchi_blog.models import BlogPost
-
-class BlogPostMixin(object):
-    date_field = 'post_date'
-    slug_field = 'slug'
-    username = None
-
-    def get_queryset(self):
-        qset = BlogPost.objects.current()
-        if self.username:
-            qset = qset.filter(user__username=self.username)
-        tags = self.request.GET.getlist('tags')
-        if tags:
-            qset = qset.filter(tags__in=tags)
-        return qset

File gnocchi_blog/__init__.py

Empty file added.

File gnocchi_blog/admin.py

View file
+from django.contrib import admin
+from gnocchi_blog import models, forms
+
+register = admin.site.register
+
+class BlogPostAdmin(admin.ModelAdmin):
+    list_display = ('title', 'published', 'post_date', 'kill_date', 'posted_by', 'allow_comments', )
+    list_filter = ('posted_by', 'published',)
+    date_heirarchy = 'post_date'
+    search_fields = ('title', 'content',)
+    prepopulated_fields = {'slug': ('title',)}
+    exclude = ('posted_by',)
+    actions = ('publish_newsletter',)
+
+    form = forms.BlogAdminForm
+
+    def queryset(self, request):
+        qset = self.model.objects.all()
+        if not request.user.is_superuser:
+            return qset.filter(posted_by=request.user)
+        return qset
+
+    def save_model(self, request, obj, form, change):
+        if not change:
+            obj.posted_by = request.user
+        obj.save()
+
+register( models.BlogPost, BlogPostAdmin )

File gnocchi_blog/feeds.py

View file
+from django.contrib.syndication.views import Feed
+from gnocchi_blog.models import BlogPost
+from taggit.models import TaggedItem
+
+class LatestBlogFeed(Feed):
+    '''Basic feed configuration
+
+    You need to derive your own class from this to set link, and you will
+    probably also want to change description and title.
+
+    See http://docs.djangoproject.com/en/1.2/ref/contrib/syndication/
+    '''
+    description = 'Blog Posts'
+    title = 'Blog'
+
+    def categories(self):
+        return TaggedItem.tags_for(BlogPost)
+
+    def items(self):
+        return BlogPost.objects.current()
+
+    def item_title(self, item):
+        return item.title
+
+    def item_author_name(self, item):
+        return item.posted_by.get_full_name()
+
+    def item_pubdate(self, item):
+        return item.post_date
+
+    def item_categories(self, item):
+        return item.tags

File gnocchi_blog/forms.py

View file
+from django import forms
+from gnocchi_blog.models import BlogPost
+
+class BlogAdminForm(forms.ModelForm):
+    auto = forms.BooleanField('Auto-tag?', default=False,
+        help_text="Automatically scan content for tags"
+    )
+    class Meta:
+        model = BlogPost

File gnocchi_blog/models.py

View file
+from django.db import models
+from django.core.urlresolvers import reverse
+
+from taggit.managers import TaggableManager
+
+from datetime import datetime
+
+class BlogManager(models.Manager):
+    def current(self):
+        now = datetime.now()
+        return self.get_query_set().filter(
+            published=True,
+            post_date__lte=now,
+        ).exclude(
+            kill_date__lte=now
+        )
+
+class BlogPost(models.Model):
+    title = models.CharField(max_length=200)
+    slug = models.SlugField()
+    content = models.TextField(blank=True)
+
+    post_date = models.DateTimeField(default=datetime.now)
+    kill_date = models.DateTimeField(null=True, blank=True, default=None)
+    posted_by = models.ForeignKey('auth.User', blank=True, null=True)
+    published = models.BooleanField(default=False)
+
+    allow_comments = models.BooleanField(default=True)
+
+    tags = TaggableManager()
+
+    objects = BlogManager()
+
+    class Meta:
+        ordering = ('-post_date',)
+    def __unicode__(self):
+        return self.title
+    def get_absolute_url(self):
+        return reverse('blog_detail', kwargs={
+            'slug': self.slug,
+            'year': '%04d' % self.post_date.year,
+            'month': self.post_date.strftime('%b').lower(),
+            'day': '%02d' % self.post_date.day,
+        })
+

File gnocchi_blog/recaptcha.py

View file
+"""
+An easy-to-use Django forms integration of the reCaptcha service.
+v1.0.1
+
+To use, simply base your form off the ``RecaptchaForm`` class. This class adds
+a new argument that must be provided to the form, ``remote_ip``.
+
+Two settings which must be set in your project's ``settings`` module are 
+``RECAPTCHA_PUBLIC_KEY`` and ``RECAPTCHA_SECRET_KEY``, the public and private
+keys for your domain, respectively. 
+
+Following is an example of creating a basic comment form class and then
+using an instance of the form in a view::
+
+    from django import forms
+    from mysite.utils import recaptcha
+
+    class CommentForm(recaptcha.RecaptchaForm):
+        name = forms.CharField()
+        comment = forms.CharField(widget=Textarea())
+        captcha = recaptcha.RecaptchaField()
+
+    def comment(request):
+        comment_form = CommentForm(remote_ip=request.META['REMOTE_ADDR'])
+        ...
+
+If you need to use a different base form (such as ``ModelForm``), use multiple
+inheritance like so::
+
+    class MyModelForm(BaseRecaptchaForm, ModelForm):
+        ...
+"""
+
+import httplib
+from django import forms
+from django.conf import settings
+from django.utils.safestring import mark_safe
+from django.utils.http import urlencode
+
+OPTIONS_SCRIPT_HTML = u'''<script type="text/javascript">
+   var RecaptchaOptions = %r;
+</script>
+'''
+RECAPTCHA_HTML = u'''%(options)s<script type="text/javascript" src="http://api.recaptcha.net/challenge?k=%(public_key)s"></script>
+<noscript>
+   <iframe src="http://api.recaptcha.net/noscript?k=%(public_key)s"
+       height="300" width="500" frameborder="0"></iframe><br />
+   <textarea name="recaptcha_challenge_field" rows="3" cols="40">
+   </textarea>
+   <input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
+</noscript>'''
+
+
+class RecaptchaWidget(forms.Widget):
+    def __init__(self, theme=None, tabindex=None, public_key=None):
+        '''
+        From http://recaptcha.net/apidocs/captcha/client.html#look-n-feel::
+
+            theme:      'red' | 'white' | 'blackglass' | 'clean'
+
+                Defines which theme to use for reCAPTCHA.
+
+            tabindex:   any integer
+
+                Sets a tabindex for the reCAPTCHA text box. If other elements
+                in the form use a tabindex, this should be set so that
+                navigation is easier for the user.
+
+        The optional ``public_key`` argument can be used to override the
+        default use of the project-wide ``RECAPTCHA_PUBLIC_KEY`` setting.
+        '''
+        options = {}
+        if theme:
+            options['theme'] = theme
+        if tabindex:
+            options['tabindex'] = tabindex
+        self.options = options
+        self.public_key = public_key or settings.RECAPTCHA_PUBLIC_KEY
+        super(RecaptchaWidget, self).__init__()
+
+    def render(self, name, value, attrs=None):
+        args = dict(public_key=self.public_key, options='')
+        if self.options:
+            args['options'] = OPTIONS_SCRIPT_HTML % self.options
+        return mark_safe(RECAPTCHA_HTML % args)
+
+    def value_from_datadict(self, data, files, name):
+        challenge = data.get('recaptcha_challenge_field')
+        response = data.get('recaptcha_response_field')
+        return (challenge, response)
+
+    def id_for_label(self, id_):
+        return None
+
+
+class RecaptchaField(forms.Field):
+    widget = RecaptchaWidget
+    default_error_messages = {
+        'required': u'Please enter the CAPTCHA solution.',
+        'invalid': u'An incorrect CAPTCHA solution was entered.',
+        'no-remote-ip': u'CAPTCHA failed due to no visible IP address.',
+        'challenge-error': u'An error occurred with the CAPTCHA service - try '
+            'refreshing.',
+        'unknown-error': u'The CAPTCHA service returned the following error: '
+            '%(code)s.',
+    }
+
+    def __init__(self, private_key=None, *args, **kwargs):
+        """
+        The optional ``private_key`` argument can be used to override the
+        default use of the project-wide ``RECAPTCHA_SECRET_KEY`` setting.
+        """
+        self.remote_ip = None
+        self.private_key = private_key or settings.RECAPTCHA_SECRET_KEY
+        super(RecaptchaField, self).__init__(*args, **kwargs)
+
+    def clean(self, value):
+        if not self.remote_ip:
+            raise forms.ValidationError(self.error_messages['no-remote-ip'])
+        value = super(RecaptchaField, self).clean(value)
+        challenge, response = value
+        if not challenge:
+            raise forms.ValidationError(self.error_messages['challenge-error'])
+        if not response:
+            raise forms.ValidationError(self.error_messages['required'])
+        try:
+            value = validate_recaptcha(self.remote_ip, challenge, response,
+                                       self.private_key)
+        except RecaptchaError, e:
+            if e.code == 'incorrect-captcha-sol':
+                raise forms.ValidationError(self.error_messages['invalid'])
+            raise forms.ValidationError(self.error_messages['unknown-error'] %
+                                        {'code': e.code})
+        return value
+
+
+class BaseRecaptchaForm(forms.BaseForm):
+    def __init__(self, remote_ip, *args, **kwargs):
+        super(BaseRecaptchaForm, self).__init__(*args, **kwargs)
+        for field in self.fields.values():
+            if isinstance(field, RecaptchaField):
+                field.remote_ip = remote_ip
+
+
+class RecaptchaForm(BaseRecaptchaForm, forms.Form):
+    pass
+
+
+class RecaptchaError(Exception):
+    def __init__(self, code):
+        self.code = code
+
+    def __str__(self):
+        return self.code
+
+
+def validate_recaptcha(remote_ip, challenge, response, private_key):
+    assert challenge, 'No challenge was provided for reCaptcha validation'
+    # Request validation from recaptcha.net
+    params = dict(privatekey=private_key, remoteip=remote_ip,
+                  challenge=challenge, response=response)
+    params = urlencode(params)
+    headers = {"Content-type": "application/x-www-form-urlencoded",
+               "Accept": "text/plain"}
+    conn = httplib.HTTPConnection("api-verify.recaptcha.net")
+    conn.request("POST", "/verify", params, headers)
+    response = conn.getresponse()
+    if response.status == 200:
+        data = response.read()
+    else:
+        data = ''
+    conn.close()
+    # Validate based on response data
+    result = data.startswith('true')
+    if not result:
+        bits = data.split('\n', 2)
+        error_code = ''
+        if len(bits) > 1:
+            error_code = bits[1]
+        raise RecaptchaError(error_code)
+    # Return dictionary
+    return result

File gnocchi_blog/search_indexes.py

View file
+
+from haystack.indexes import SearchIndex, CharField
+from haystack import site
+from gnocchi_blog.models import BlogPost
+
+class BlogPostIndex(SearchIndex):
+    text = CharField(document=True, use_template=True)
+    title = CharField(model_attr='title')
+
+site.register(BlogPost, BlogPostIndex)

File gnocchi_blog/sitemap.py

Empty file added.

File gnocchi_blog/templates/search/indexes/blog/blogpost_text.txt

View file
+{{ object.title }}
+{{ object.content }}
+

File gnocchi_blog/templatetags/__init__.py

Empty file added.

File gnocchi_blog/templatetags/blog.py

View file
+from django import template
+from gnocchi_blog.models import BlogPost
+from gnocchi_tools.template import parse_args
+
+register = template.Library()
+
+class RecentPostsNode(template.Node):
+    def __init__(self, count, varname, username=None, *tags):
+        self.count = count
+        self.varname = varname
+        self.tags = tags
+        self.username = username
+    def render(self, context):
+        tags = filter(None, [tag.resolve(context) for tag in self.tags])
+        varname = self.varname
+        if not isinstance(varname, basestring):
+            varname = varname.resolve(context)
+        username = self.username
+        if username and not isinstance(username, basestring):
+            username = username.resolve(context)
+
+        qset = BlogPost.objects.current()
+        if tags:
+            qset = qset.filter(tag__in=tags)
+        if username:
+            qset = qset.filter(posted_by__username=username)
+        context[varname] = qset
+        return ''
+
+@register.tag
+def get_recent_posts(parser, token):
+    args, kwargs, as_name = parse_args(parser, token, 'posts')
+    count = int(args.pop(0))
+    return RecentPostsNode(count, as_name, *args, **kwargs)
+
+@register.filter
+def recent_posts(value, username=None):
+    qset = BlogPost.objects.current()
+    if username:
+        qset.filter(posted_by__username=username)
+    try:
+        return qset[:int(value)]
+    except BlogPost.DoesNotExist:
+        return []
+

File gnocchi_blog/urls.py

View file
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('gnocchi_blog.views',
+    url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[\w-]+)/$',
+        'blog_detail',
+        name='blog_detail',
+    ),
+    url(r'^$', 'blog_list', ),
+)
+
+

File gnocchi_blog/views.py

View file
+from gnocchi_blog.models import BlogPost
+
+class BlogPostMixin(object):
+    date_field = 'post_date'
+    slug_field = 'slug'
+    username = None
+
+    def get_queryset(self):
+        qset = BlogPost.objects.current()
+        if self.username:
+            qset = qset.filter(user__username=self.username)
+        tags = self.request.GET.getlist('tags')
+        if tags:
+            qset = qset.filter(tags__in=tags)
+        return qset

File setup.py

View file
     url = 'http://bitbucket.org/funkybob/gnocchi-blog/',
     keywords = [ 'django', 'blog', ],
     packages = find_packages(),
-    package_dir = {
-        'gnocchi_blog': 'blog',
-    },
     zip_safe = False,
     classifiers = [
         'Environment :: Web Environment',