Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi-markup, internationalized models, post_published signal #16

Merged
merged 14 commits into from Jan 2, 2015
2 changes: 1 addition & 1 deletion README.rst
Expand Up @@ -11,7 +11,7 @@ internal Pinax blog app once we've made it feature complete.
Current features include:

* support for multiple channels (e.g. technical vs business)
* use of Creole as markup format
* use HTML, Creole, Markdown, reStructuredText or Textile as markup formats
* Atom feeds
* previewing of blog posts before publishing
* optional ability to announce new posts on twitter
6 changes: 4 additions & 2 deletions biblion/admin.py
Expand Up @@ -4,7 +4,7 @@

from biblion.models import Biblion, Post, Image
from biblion.forms import AdminPostForm
from biblion.utils import can_tweet
from biblion.utils.twitter import can_tweet


class ImageInline(admin.TabularInline):
Expand All @@ -18,7 +18,7 @@ class BiblionAdmin(admin.ModelAdmin):
}


class PostAdmin(admin.ModelAdmin):
class PostAdmin(admin.ModelAdmin):
list_display = ["biblion", "title", "published_flag"]
list_filter = ["biblion"]
form = AdminPostForm
Expand All @@ -27,10 +27,12 @@ class PostAdmin(admin.ModelAdmin):
"title",
"slug",
"author",
"markup_type",
"teaser",
"content",
"publish",
"publish_date",
"sites",
]
if can_tweet():
fields.append("tweet")
Expand Down
40 changes: 20 additions & 20 deletions biblion/forms.py
Expand Up @@ -3,14 +3,14 @@
from django import forms

from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import curry

from django.contrib.auth.models import User
from django.contrib.sites.models import Site

from biblion.models import Biblion, Post, Revision, Image
from biblion.settings import PARSER
from biblion.utils import can_tweet, load_path_attr, slugify_unique
from biblion.signals import post_published
from biblion.utils.twitter import can_tweet
from biblion.utils.slugify import slugify_unique


class BiblionForm(forms.ModelForm):
Expand Down Expand Up @@ -131,14 +131,12 @@ def save(self):
if Post.objects.filter(pk=post.pk, published=None).count():
if self.cleaned_data["publish"]:
post.published = datetime.datetime.now()

if self.cleaned_data["publish_date"]:
post.published = self.cleaned_data["publish_date"]

render_func = curry(load_path_attr(PARSER[0]), **PARSER[1])

post.teaser_html = render_func(self.cleaned_data["teaser"])
post.content_html = render_func(self.cleaned_data["content"])
post.teaser = self.cleaned_data["teaser"]
post.content = self.cleaned_data["content"]
post.updated = datetime.datetime.now()
post.save()

Expand Down Expand Up @@ -211,6 +209,7 @@ def __init__(self, *args, **kwargs):

if latest_revision:
# set initial data from the latest revision
self.fields["markup_type"].initial = latest_revision.markup_type
self.fields["teaser"].initial = latest_revision.teaser
self.fields["content"].initial = latest_revision.content

Expand All @@ -221,28 +220,29 @@ def __init__(self, *args, **kwargs):

def save(self):
post = super(AdminPostForm, self).save(commit=False)

if post.pk is None:
if self.cleaned_data["publish"]:
post.published = datetime.datetime.now()
# only publish the first time publish has been checked
if (post.pk is None or Post.objects.filter(pk=post.pk, published=None).count()) and self.cleaned_data["publish"]:
post.published = datetime.datetime.now()
send_published_signal = True
else:
if Post.objects.filter(pk=post.pk, published=None).count():
if self.cleaned_data["publish"]:
post.published = datetime.datetime.now()

send_published_signal = False

if self.cleaned_data["publish_date"]:
post.published = self.cleaned_data["publish_date"]

render_func = curry(load_path_attr(PARSER[0]), **PARSER[1])

post.teaser_html = render_func(self.cleaned_data["teaser"])
post.content_html = render_func(self.cleaned_data["content"])
post.markup_type = self.cleaned_data["markup_type"]
post.teaser = self.cleaned_data["teaser"]
post.content = self.cleaned_data["content"]
post.updated = datetime.datetime.now()
post.save()

if send_published_signal:
post_published.send(sender=self, pk=post.pk)

r = Revision()
r.post = post
r.title = post.title
r.markup_type = self.cleaned_data["markup_type"]
r.teaser = self.cleaned_data["teaser"]
r.content = self.cleaned_data["content"]
r.author = post.author
Expand Down
5 changes: 0 additions & 5 deletions biblion/markdown_parser.py

This file was deleted.

119 changes: 76 additions & 43 deletions biblion/models.py
@@ -1,4 +1,4 @@
# -*- coding: utf8 -*-
# -*- coding: utf-8 -*-
import urllib2

from datetime import datetime
Expand All @@ -7,7 +7,9 @@
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.core.urlresolvers import reverse
from django.utils import formats
from django.utils import simplejson as json
from django.utils.translation import ugettext as _

from django.contrib.auth.models import User
from django.contrib.sites.models import Site
Expand All @@ -18,17 +20,21 @@
twitter = None

from biblion.managers import PostManager
from biblion.utils import can_tweet
from biblion.utils.twitter import can_tweet


class Biblion(models.Model):

title = models.CharField(max_length=128)
subtitle = models.CharField(max_length=256, null=True, blank=True)
slug = models.SlugField(unique=True)
description = models.TextField()
logo = models.FileField(upload_to="biblion_biblion_logo")
sites = models.ManyToManyField(Site)
title = models.CharField(_("title"), max_length=128)
subtitle = models.CharField(_("subtitle"), max_length=256, blank=True)
slug = models.SlugField(_("slug"), unique=True)
description = models.TextField(_("description"))
logo = models.FileField(_("logo"), upload_to="biblion_biblion_logo")
sites = models.ManyToManyField(Site, verbose_name=_("list of sites"))

class Meta:
verbose_name = _("biblion")
verbose_name_plural = _("biblia")

def __unicode__(self):
return unicode(self.title)
Expand All @@ -39,30 +45,44 @@ def get_absolute_url(self):

class BiblionContributor(models.Model):

biblion = models.ForeignKey(Biblion)
user = models.ForeignKey(User)
role = models.CharField(max_length=25)
biblion = models.ForeignKey(Biblion, verbose_name=_("biblion"))
user = models.ForeignKey(User, related_name="contributors", verbose_name=_("user"))
role = models.CharField(_("role"), max_length=25)

class Meta:
verbose_name = _("biblion contributor")
verbose_name_plural = _("biblion contributors")


class Post(models.Model):

biblion = models.ForeignKey(Biblion, related_name="posts")
sites = models.ManyToManyField(Site)
biblion = models.ForeignKey(Biblion, related_name="posts", verbose_name=_("biblion"))
sites = models.ManyToManyField(Site, verbose_name=_("list of sites"))

title = models.CharField(_("title"), max_length=90)
slug = models.SlugField(_("slug"))
author = models.ForeignKey(User, related_name="posts", verbose_name=_("author"))

title = models.CharField(max_length=90)
slug = models.SlugField()
author = models.ForeignKey(User, related_name="posts")
markup_types = [
_("HTML"),
_("Creole"),
_("Markdown"),
_("reStructuredText"),
_("Textile"),
]
MARKUP_CHOICES = zip(range(1, 1 + len(markup_types)), markup_types)
markup_type = models.IntegerField(_("markup type"), choices=MARKUP_CHOICES, default=1)

teaser_html = models.TextField(editable=False)
content_html = models.TextField(editable=False)
teaser = models.TextField(_("teaser"), editable=False)
content = models.TextField(_("content"), editable=False)

tweet_text = models.CharField(max_length=140, editable=False)
tweet_text = models.CharField(_("tweet text"), max_length=140, editable=False)

created = models.DateTimeField(default=datetime.now, editable=False) # when first revision was created
updated = models.DateTimeField(null=True, blank=True, editable=False) # when last revision was create (even if not published)
published = models.DateTimeField(null=True, blank=True, editable=False) # when last published
created = models.DateTimeField(_("created"), default=datetime.now, editable=False) # when first revision was created
updated = models.DateTimeField(_("updated"), null=True, blank=True, editable=False) # when last revision was created (even if not published)
published = models.DateTimeField(_("published"), null=True, blank=True, editable=False) # when last published

view_count = models.IntegerField(default=0, editable=False)
view_count = models.IntegerField(_("view count"), default=0, editable=False)

def rev(self, rev_id):
return self.revisions.get(pk=rev_id)
Expand All @@ -79,6 +99,8 @@ def latest(self):
return None

class Meta:
verbose_name = _("post")
verbose_name_plural = _("posts")
ordering = ("-published",)
get_latest_by = "published"

Expand Down Expand Up @@ -107,13 +129,13 @@ def as_tweet(self):
def tweet(self):
if can_tweet():
account = twitter.Api(
username = settings.TWITTER_USERNAME,
password = settings.TWITTER_PASSWORD,
username=settings.TWITTER_USERNAME,
password=settings.TWITTER_PASSWORD,
)
account.PostUpdate(self.as_tweet())
else:
raise ImproperlyConfigured("Unable to send tweet due to either "
"missing python-twitter or required settings.")
raise ImproperlyConfigured(_("Unable to send tweet due to either \
missing python-twitter or required settings."))

def save(self, **kwargs):
self.updated_at = datetime.now()
Expand Down Expand Up @@ -147,22 +169,29 @@ def inc_views(self):

class Revision(models.Model):

post = models.ForeignKey(Post, related_name="revisions")
post = models.ForeignKey(Post, related_name="revisions", verbose_name=_("post"))

title = models.CharField(max_length=90)
teaser = models.TextField()
title = models.CharField(_("title"), max_length=90)

content = models.TextField()
markup_type = models.IntegerField(_("markup type"))
teaser = models.TextField(_("teaser"))
content = models.TextField(_("content"))

author = models.ForeignKey(User, related_name="post_revisions")
author = models.ForeignKey(User, related_name="post revisions", verbose_name=_("author"))

updated = models.DateTimeField(default=datetime.now)
published = models.DateTimeField(null=True, blank=True)
updated = models.DateTimeField(_("updated"), default=datetime.now)
published = models.DateTimeField(_("published"), null=True, blank=True)

view_count = models.IntegerField(default=0, editable=False)
view_count = models.IntegerField(_("view count"), default=0, editable=False)

class Meta:
verbose_name = _("revision")
verbose_name_plural = _("revisions")

def __unicode__(self):
return 'Revision %s for %s' % (self.updated.strftime('%Y%m%d-%H%M'), self.post.slug)
return _("Revision %(datetime)s for %(slug)s") % {
"datetime": formats.localize("%Y%m%d-%H%M"),
"slug": self.post.slug}

def inc_views(self):
self.view_count += 1
Expand All @@ -173,19 +202,23 @@ class Image(models.Model):

post = models.ForeignKey(Post, related_name="images", blank=True, null=True)

image_path = models.ImageField(upload_to="images/%Y/%m/%d")
url = models.CharField(max_length=150, blank=True)
image_path = models.ImageField(_("image path"), upload_to="images/%Y/%m/%d")
url = models.CharField(_("url"), max_length=150, blank=True)

timestamp = models.DateTimeField(default=datetime.now, editable=False)
timestamp = models.DateTimeField(_("timestamp"), default=datetime.now, editable=False)

class Meta:
verbose_name = _("image")
verbose_name_plural = _("images")

def __unicode__(self):
if self.pk is not None:
return "{{ %d }}" % self.pk
return u"{{ %d }}" % self.pk
else:
return "deleted image"
return _("deleted image")


class FeedHit(models.Model):

request_data = models.TextField()
created = models.DateTimeField(default=datetime.now)
request_data = models.TextField(_("request data"))
created = models.DateTimeField(_("created"), default=datetime.now)
4 changes: 0 additions & 4 deletions biblion/settings.py

This file was deleted.

4 changes: 4 additions & 0 deletions biblion/signals.py
@@ -0,0 +1,4 @@
from django.dispatch import Signal


post_published = Signal(providing_args=["pk"])
11 changes: 7 additions & 4 deletions biblion/templates/biblion/atom_entry.xml
@@ -1,3 +1,6 @@
{% load biblion_tags %}


<entry xml:base="http://{{ current_site.domain }}/">
<id>http://{{ current_site.domain }}{{ entry.get_absolute_url }}</id>
<title>{{ entry.title }}</title>
Expand All @@ -12,14 +15,14 @@

<summary type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
{{ entry.teaser_html|safe }}
{{ entry.teaser|to_html }}
</div>
</summary>

<content type="xhtml" xml:lang="en">
<div xmlns="http://www.w3.org/1999/xhtml">
{{ entry.teaser_html|safe }}
{{ entry.content_html|safe }}
{{ entry.teaser|to_html }}
{{ entry.content|to_html }}
</div>
</content>
</entry>
</entry>
Empty file.
19 changes: 19 additions & 0 deletions biblion/templatetags/biblion_tags.py
@@ -0,0 +1,19 @@
from django import template

from biblion.utils.code_hilite import to_html


register = template.Library()


register.filter("to_html", to_html)


def show_post_brief(context, post):
return {
"post": post,
"last": context["forloop"]["last"],
"can_edit": context["user"].is_staff,
}

register.inclusion_tag("blog/post_brief.html", takes_context=True)(show_post_brief)