Commits

Ian Lewis committed c0fa094 Merge

Merge bootstrap branch into default

Comments (0)

Files changed (90)

Add a comment to this file

app/__init__.py

Empty file removed.

Add a comment to this file

app/blog/__init__.py

Empty file removed.

app/blog/admin.py

-#:coding=utf8:
-from django.contrib import admin
-from django.contrib.auth.models import User
-
-from blog.models import Post
-
-class PostAdmin(admin.ModelAdmin):
-    list_display = ("id", "title", "locale", "pub_date", "active")
-    list_filter = ("active","locale")
-    list_display_links = ("id", "title")
-    search_fields = ("title", "content")
-    prepopulated_fields = {"slug": ("title",)}
-
-    def get_form(self, request, obj=None, **kwargs):
-        '''
-        Overwrite get_form to select the currently logged in user as the author
-        Copied from byteflow.
-        '''
-        form = super(PostAdmin, self).get_form(request, obj, **kwargs)
-        f = form.base_fields['author']
-        f.initial = request.user.pk
-        if request.user.is_superuser:
-            f.queryset = User.objects.filter(is_staff=True)
-        else:
-            f.queryset = User.objects.filter(pk=request.user.pk)
-        return form
-
-admin.site.register(Post, PostAdmin)

app/blog/decorators.py

-#:coding=utf8:
-
-from django.http import HttpResponseRedirect
-
-def feed_redirect(view):
-    """
-    A decorator that redirects based on some old legacy
-    parameters. Some folks still subscribe to old feed
-    urls.
-    """
-    def wrapped(request, *args, **kwargs):
-        if request.GET.get("tempskin") == "_rss" or \
-           request.GET.get("tempskin") == "_rss2" or \
-           request.GET.get("tempskin") == "_atom":
-            if request.GET.get("lang") == "ja-JP" or \
-               kwargs.get("locale") == "jp":
-                return HttpResponseRedirect("http://feeds2.feedburner.com/IanLewisBlogJP")
-            else:
-                return HttpResponseRedirect("http://feeds2.feedburner.com/IanLewisBlog")
-        return view(request, *args, **kwargs)
-
-    return wrapped
-
-

app/blog/feeds.py

-#:coding=utf8:
-
-from django.contrib.syndication.feeds import Feed
-from models import Post
-
-from django.core.urlresolvers import reverse
-
-from tagging.utils import parse_tag_input
-from tagging.models import Tag, TaggedItem
-
-class LatestBlogEntries(Feed):
-    def link(self, obj):
-        if isinstance(obj, Tag):
-            return reverse('blog_tag_page', kwargs={
-                'locale':self.locale,
-                'tag': obj.name,
-            })
-        else:
-            return reverse('blog_page', kwargs={'locale':self.locale})
-
-    def items(self, obj):
-        if isinstance(obj, Tag):
-            return TaggedItem.objects.get_by_model(Post.objects.published().filter(locale=self.locale), obj)
-        else:
-            return Post.objects.published().filter(locale=self.locale)[:10]
-
-    def item_author_name(self, item):
-        return item.author.get_full_name()
-
-    def item_author_email(self, item):
-        return item.author.email
-
-    def item_author_link(self, item):
-        return reverse('main_page')
-
-    def item_pubdate(self, item):
-        return item.pub_date
-
-    def item_categories(self, item):
-        return parse_tag_input(item.tags)
-
-    def get_object(self, bits):
-        if len(bits) > 1:
-            raise Tag.DoesNotExist
-        elif len(bits) == 1:
-            return Tag.objects.get(name=bits[0])
-        else:
-            return None
-
-class LatestEnglishBlogEntries(LatestBlogEntries):
-    title="Ian Lewis' Blog"
-    description="The latest blog posts from Ian Lewis' blog"
-    locale="en"
-
-class LatestJapaneseBlogEntries(LatestBlogEntries):
-    title=u"イアンルイスのブログ"
-    description=u"イアンルイスのブログの最新エントリ"
-    locale="jp"

app/blog/models.py

-#:coding=utf8:
-
-from django.contrib.sites.models import Site
-from django.contrib.auth.models import User
-from tagging.models import *
-from django.db.models import *
-from django.utils.translation import ugettext_lazy as _
-
-from tagging.fields import TagField
-
-from datetime import datetime
-
-BLOG_LOCALES = (
-    ('jp', u'日本語'),
-    ('en', u'English'),
-)
-
-class PostManager(Manager):
-    def published(self):
-        return self.filter(pub_date__lt = datetime.now(), active=True)
-
-class Post(Model):
-    author = ForeignKey(User, verbose_name=u"author")
-    slug = SlugField(u"slug", max_length=50, unique=True, db_index=True)
-    title = TextField(u"title")
-    lead = TextField(u"lead", blank=True, null=True, default=None, max_length=600)
-    content = TextField(u"content")
-    markup_type = models.CharField(max_length=10, choices=(
-        ("html", "HTML"),
-        ("rst", "reStructuredText"),
-    ), default="html")
-    locale = CharField(u'locale', max_length=20, choices=BLOG_LOCALES, default="en", db_index=True)
-    tags = TagField()
-
-    active = BooleanField(u'published', default=False, db_index=True)
-    pub_date = DateTimeField(u'published', default=datetime.now, db_index=True)
-    create_date = DateTimeField(u'created', default=datetime.now)
-   
-    objects = PostManager()
-
-    @permalink
-    def get_absolute_url(self):
-        return ('blog_detail', (), {
-            'locale': self.locale,
-            'slug': self.slug,
-        })
-
-    @property
-    def lang(self):
-        return {
-            "en": "en",
-            "jp": "ja",
-        }.get(self.locale, "en")
-    
-    def get_full_url(self):
-        return 'http://%s%s' % (Site.objects.get_current().domain, self.get_absolute_url())
-
-    def __unicode__(self):
-        return self.title
-
-    class Meta:
-        verbose_name = _("post")
-        verbose_name_plural = _("posts")
-        ordering = ('-pub_date',)
Add a comment to this file

app/blog/templatetags/__init__.py

Empty file removed.

app/blog/templatetags/blog_tags.py

-#:coding=utf-8:
-
-import re
-import logging
-import html2text
-import HTMLParser
-
-from django.template import Library
-from django.utils.safestring import mark_safe
-
-from docutils import nodes
-from docutils.writers import html4css1
-from docutils.core import publish_parts
-from docutils.parsers.rst import directives
-
-from pygments import highlight
-from pygments.formatters import HtmlFormatter
-from pygments.lexers import get_lexer_by_name, TextLexer
-
-register = Library()
-
-logger = logging.getLogger(__name__)
-
-VARIANTS = {}
-
-def pygments_directive(name, arguments, options, content, lineno,
-                       content_offset, block_text, state, state_machine):
-    try:
-        lexer = get_lexer_by_name(arguments[0])
-    except (ValueError, IndexError):
-        # no lexer found - use the text one instead of an exception
-        lexer = TextLexer()
-    parsed = highlight(u"\n".join(content), lexer, HtmlFormatter(cssclass="highlight notranslate"))
-    return [nodes.raw("", parsed, format="html")]
-pygments_directive.arguments = (0, 1, False)
-pygments_directive.content = 1
-pygments_directive.options = dict([(key, directives.flag) for key in VARIANTS])
-
-directives.register_directive("sourcecode", pygments_directive)
-directives.register_directive("code-block", pygments_directive)
-
-def lightbox_directive(name, arguments, options, content, lineno,
-        content_offset, block_text, state, state_machine):
-    """
-    lightbox image directive
-
-    usage:
-
-    .. lightbox:: thumb_url img_url
-
-        Title
-    """
-    thumb_href = arguments[0]
-    img_href = arguments[1]
-    title = u" ".join(content)
-
-    html = '<div class="lightbox-img"><a title="%(title)s" rel="lightbox" href="%(img_href)s"><img src="%(thumb_href)s" title="%(title)s" alt=""/></a></div>' % {
-        "title": title, 
-        "img_href": img_href,
-        "thumb_href": thumb_href,
-    }
-    return [nodes.raw("", html, format="html")]
-lightbox_directive.arguments = (2, 2, False)
-lightbox_directive.content = 1
-lightbox_directive.options = dict([(key, directives.flag) for key in VARIANTS])
-
-directives.register_directive("lightbox", lightbox_directive)
-
-class HTMLWriter(html4css1.Writer):
-    def __init__(self):
-        html4css1.Writer.__init__(self)
-        self.translator_class = HTMLTranslator
-
-class HTMLTranslator(html4css1.HTMLTranslator):
-    named_tags = []
-    
-    def visit_literal(self, node):
-        # TODO: wrapping fixes.
-        self.body.append("<code>%s</code>" % node.astext())
-        raise nodes.SkipNode
-
-
-def rst_to_html(value):
-    parts = publish_parts(source=value, writer=HTMLWriter(),
-        settings_overrides={"initial_header_level": 2})
-    return parts["fragment"]
-    
-
-def to_html(obj):
-    if obj.markup_type == "html":
-        html = obj.content
-    elif obj.markup_type == "rst":
-        html = rst_to_html(obj.content)
-    return mark_safe(html)
-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)
-
-@register.filter
-def date_microformat(d):
-    '''
-        Microformat version of a date.
-        2009-02-10T02:58:00+00:00 (ideal)
-        2009-02-09T17:54:41.181868-08:00 (mine)
-    '''
-    return d.isoformat()
-
-STRIKE_REPL_RE = re.compile("< *strike *>[^<]*</ *strike *>", re.IGNORECASE)
-def html_to_text(html):
-    """
-    HTMLを plain/text に変換する
-    """
-    h = html2text.HTML2Text()
-    h.ignore_links = True
-    h.ignore_images = True
-    h.ignore_emphasis = True
-    h.hide_strikethrough = True
-    # <strike>***</strike> テキストを削る 
-    text = STRIKE_REPL_RE.sub("", html)
-    # html2text を呼び出す
-    text = h.handle(text).replace("&nbsp_place_holder;", " ")
-    # 先頭と後続の空白を削る
-    return text.strip()
-
-def abbrev(s, num=255, end="..."):
-    """
-    >>> abbrev('spamspamspam', 6)
-    'spa...'
-    >>> abbrev('spamspamspam', 12)
-    'spamspamspam'
-    >>> abbrev('blahblahblah', 13)
-    'eggseggseg...'
-    >>> abbrev('eggseggseggs', 1)
-    'e'
-    >>> abbrev('eggseggseggs', 2, '.')
-    'e.'
-    """
-    index = num - len(end)
-    if len(s) > num:
-        s = (s[:index] + end) if index > 0 else s[:num]
-    return s
-
-def to_lead(obj):
-    if obj.lead:
-        return obj.lead
-    else:
-        max_len = 300 if obj.locale == "jp" else 600
-        html = to_html(obj)
-        try:
-            return abbrev(html_to_text(html), max_len, "[...]")
-        except HTMLParser.HTMLParseError,e:
-            logger.error('HTML Parse error: "%s" for text "%s"' % (
-                e,
-                html,
-            ))
-            return ""
-         
-register.filter("to_lead", to_lead)

app/blog/urls.py

-#:coding=utf8:
-
-from django.conf.urls.defaults import *
-
-from feeds import *
-
-urlpatterns = patterns('django.views.generic.simple',
-    # Old tag url redirect
-    url(r'^(?P<locale>\w{2})/(?P<tag>[^/]+);$', 'redirect_to', {'url': '/%(locale)s/tag/%(tag)s'}, name='old_blog_tag_page'),
-)
-
-feeds = {
-    'enfeed': LatestEnglishBlogEntries,
-    'jpfeed': LatestJapaneseBlogEntries,
-}
-
-urlpatterns += patterns('blog.views',
-    url(r'^admin/blog/post/(?P<object_id>[0-9]+)/preview$', 'blog_detail_preview', name='blog_detail_preview'),
-
-    url(r'^(?P<locale>\w{2})/tag/(?P<tag>.+)$', 'tag_page', name='blog_tag_page'),
-        
-    url(r'^(?P<locale>\w{2})/(?P<slug>[^/]+)/?$', 'blog_detail', name='blog_detail'),
-    url(r'^(?P<locale>\w{2})/?$', 'blog_page', name="blog_page"),
-)
-
-urlpatterns += patterns('django.contrib.syndication.views',
-    url(r'^feed/(?P<url>.*)$', 'feed', {'feed_dict':feeds}, name='blog_feeds'),
-)

app/blog/views.py

-# Create your views here.
-from django.views.generic.list_detail import object_list,object_detail
-from django.contrib.admin.views.decorators import staff_member_required
-from django.core.urlresolvers import reverse
-from django.http import Http404
-from django.views.decorators.http import require_http_methods
-from django.views.decorators.cache import never_cache
-from models import *
-from decorators import *
-
-@never_cache
-@staff_member_required
-@require_http_methods(['GET', 'HEAD'])
-def blog_detail_preview(request, object_id):
-    object = Post.objects.get(pk=object_id)
-    defaults = {
-        "queryset": Post.objects.all(),
-        "object_id": object_id,
-        "extra_context": {"locale": object.locale},
-    }
-    return object_detail(request, **defaults)
-
-@require_http_methods(['GET', 'HEAD'])
-@feed_redirect
-def blog_page(request, locale="en"):
-    return object_list(request, 
-        Post.objects.published().filter(locale=locale),
-        extra_context={
-            "locale":locale,
-            "rss_feed_url": reverse("blog_feeds", kwargs={"url": "%sfeed" % locale}),
-        },
-    )
-
-@require_http_methods(['GET', 'HEAD'])
-def blog_detail(request, slug, locale="en"):
-    defaults = {
-        "queryset": Post.objects.published().filter(locale=locale),
-        "slug": slug,
-        "extra_context": {"locale": locale},
-    }
-    return object_detail(request, **defaults)
-
-@require_http_methods(['GET', 'HEAD'])
-def tag_page(request, tag, locale="en"):
-    from tagging.views import tagged_object_list
-    return tagged_object_list(
-        request,
-        queryset_or_model=Post.objects.published().filter(locale=locale),
-        tag=tag,
-        template_name="blog/post_list.html",
-        extra_context={
-            'locale':locale,
-            "rss_feed_url": reverse("blog_feeds", kwargs={"url": "%sfeed/%s" % (locale, tag)}),
-        },
-    )
Add a comment to this file

app/homepage/__init__.py

Empty file removed.

app/homepage/admin.py

-from django.contrib import admin
-from homepage.models import Log
-
-class LogAdmin(admin.ModelAdmin):
-    date_hierarchy = 'datetime' 
-    model = Log
-    list_display = ['datetime', 'host', 'level', 'source', 'abbrev_msg']
-    search_fields = ['source', 'msg', 'host']
-    list_filter = ['level', 'source', 'host']
-
-admin.site.register(Log, LogAdmin)

app/homepage/finders.py

-import os
-from django.contrib.staticfiles.finders import AppDirectoriesFinder
-from django.contrib.staticfiles.storage import AppStaticStorage
-
-class AppMediaStorage(AppStaticStorage):
-    source_dir = 'media'
-
-    def get_location(self, app_root):
-        """
-        Given the app root, return the location of the static files of an app,
-        by default 'static'. We special case the admin app here since it has
-        its static files in 'media'.
-        """
-        if self.app_module == 'grappelli':
-            return os.path.join(app_root, 'static')
-        return os.path.join(app_root, self.source_dir)
-
-class AppMediaDirectoriesFinder(AppDirectoriesFinder):
-    storage_class = AppMediaStorage 

app/homepage/handlers.py

-import datetime, logging
-import platform
-
-HOST = platform.uname()[1]
-
-class DatabaseHandler(logging.Handler):
-    def emit(self, record):
-        import traceback
-        from homepage.models import Log
-        
-        if hasattr(record, 'source'):
-            source = record.source
-        else:
-            source = record.name
-
-        message = record.msg
-
-        if record.exc_info:
-            exc_info = record.exc_info
-            stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
-            message = "%s\n\n%s" % (message, stack_trace)
-
-        if hasattr(record, 'request'):
-            message = "%s\n\n%s" % (message, repr(record.request))
-         
-        try:
-            Log.objects.create(source=source, level=record.levelname, msg=message, host=HOST)
-        except:
-            # squelching exceptions sucks, but 500-ing because of a logging error sucks more
-            pass

app/homepage/middleware.py

-#:coding=utf8:
-
-import re
-
-class GoogleAnalyticsStripCookieMiddleware(object):
-    strip_re = re.compile(r'(__utm.=.+?(?:; |$))')
-    def process_request(self, request):
-        try:
-            before = request.META.get('HTTP_COOKIE')
-            if before:
-                cookie = self.strip_re.sub('', request.META['HTTP_COOKIE'])
-                request.META['HTTP_COOKIE'] = cookie
-        except Exception, e:
-            from jogging import logging
-            logging.exception("Could not script analytics cookies", e, request)

app/homepage/models.py

-import datetime
-
-from django.db import models
-from django.core.exceptions import ImproperlyConfigured
-from django.conf import settings
-
-class Log(models.Model):
-    "A log message, used by jogging's DatabaseHandler"
-    datetime = models.DateTimeField(default=datetime.datetime.now)
-    level = models.CharField(max_length=128, db_index=True)
-    msg = models.TextField()
-    source = models.CharField(max_length=128, blank=True, db_index=True)
-    host = models.CharField(max_length=200, blank=True, null=True, db_index=True)
-
-    def abbrev_msg(self, maxlen=500):
-        if len(self.msg) > maxlen:
-            return u'%s ...' % self.msg[:maxlen]
-        return self.msg
-    abbrev_msg.short_description = u'abbreviated message'
Add a comment to this file

app/homepage/plugins/__init__.py

Empty file removed.

app/homepage/plugins/google.py

-#:coding=utf-8:
-
-from lifestream.plugins import FeedPlugin
-
-from datetime import datetime
-
-class GooglePlugin(FeedPlugin):
-  
-    def name(self):
-        return "Google Reader Feed"
-
-    def pre_process(self, entry):
-        super(GooglePlugin,self).pre_process(entry)
-        entry["published"] = datetime.now()

app/homepage/plugins/slideshare.py

-#!/usr/bin/env python
-#:coding=utf-8:
-#:tabSize=2:indentSize=2:noTabs=true:
-#:folding=explicit:collapseFolds=1:
-import re
-
-from lifestream.plugins import FeedPlugin
-
-class SlidesharePlugin(FeedPlugin):
-  
-  def name(self):
-    return "Slideshare Feed"
-  
-  def pre_process(self, entry):
-    super(SlidesharePlugin, self).pre_process(entry)
-
-    # Get the flash media player url from the embed tag
-    if "slideshare_embed" in entry:
-        embed_match = re.search('<param name="movie" value="([^ "]*)"', entry["slideshare_embed"])
-        entry["media_player"]["url"] = embed_match.group(1)
Add a comment to this file

app/homepage/templatetags/__init__.py

Empty file removed.

app/homepage/templatetags/utility_tags.py

-#:coding=utf8:
-import re
-
-from datetime import datetime,timedelta
-
-from django.utils.safestring import mark_safe
-from django.utils.translation import ngettext,ugettext_lazy as _
-from django.template.defaultfilters import stringfilter
-
-from django import template
-register = template.Library()
-
-@register.filter
-@stringfilter
-def truncate_chars(value, max_length):
-    try:
-        max_length = int(max_length)
-        if len(value.rstrip()) > max_length:
-            truncd_val = value[:max_length]
-            truncd_val = truncd_val.rstrip()
-            return truncd_val + "..."
-        return value
-    except:
-        return ""
-
-def stripentities(value):
-    """Strips all HTML entities"""
-    from django.utils.html import strip_entities
-    return strip_entities(value)
-stripentities.is_safe = True
-stripentities = stringfilter(stripentities)
-register.filter(stripentities)
-
-@register.filter
-def friendly_date(date, include_time=False):
-  """
-  Prints a human friendly date.
-  """
-  delta = datetime.now() - date
-   
-  if delta < timedelta(seconds=60):
-    msg = _("Just now")
-  elif delta < timedelta(seconds=60*60):
-    minutes = delta.seconds / 60
-    msg = ngettext('%(minutes)s minute ago',
-            '%(minutes)s min ago', minutes) % {
-      'minutes': minutes,
-    }
-  elif delta < timedelta(days=1):
-    hours = int(delta.seconds / 60 / 60)
-    msg = ngettext('%(hours)s hour ago',
-            '%(hours)s hours ago', hours) % {
-      'hours': hours,
-    }
-  elif delta < timedelta(days=7):
-    msg = ngettext('%(days)s day ago',
-            '%(days)s days ago', delta.days) % {
-      'days': delta.days,
-    }
-  elif delta < timedelta(days=31):
-    weeks = int(delta.days / 7)
-    msg = ngettext('%(weeks)s week ago',
-            '%(weeks)s weeks ago', weeks) % {
-      'weeks': weeks,
-    }
-  elif delta < timedelta(days=365):
-    months = int(delta.days / 31)
-    msg = ngettext('%(months)s month ago',
-            '%(months)s months ago', months) % {
-      'months': months,
-    }
-  else:
-    years = int(delta.days / 365)
-    msg = ngettext('%(years)s year ago',
-            '%(years)s years ago', years) % {
-      'years': years,
-    }
-  return mark_safe(msg)
-
-@register.filter
-def cat(value, other_value):
-    return "%s%s" % (value, other_value)
-
-@register.filter
-def urlize_twitter(text):
-    return mark_safe(
-        re.sub(r'@([a-zA-Z0-9_]*)', r'@<a href="http://twitter.com/\1">\1</a>',
-            re.sub(r'#([a-zA-Z0-9_-]*)', r'<a href="http://twitter.com/#search?q=\1">#\1</a>', text)
-        )
-    )

app/homepage/views.py

-#:coding=utf8:
-from django.http import HttpResponseRedirect 
-from django.views.generic.list_detail import object_list,object_detail
-from django.views.decorators.http import require_http_methods
-from django.core.urlresolvers import reverse
-from django.db.models import Q
-
-from tagging.views import tagged_object_list
-
-from blog.models import Post
-from lifestream.models import Item
-
-@require_http_methods(['GET', 'HEAD'])
-def main_page(request):
-  # Get latest English and Japanese post.
-  try:
-      en_post = Post.objects.published().filter(locale="en").latest("pub_date")
-  except Post.DoesNotExist:
-      en_post = None
-  try:
-      jp_post = Post.objects.published().filter(locale="jp").latest("pub_date")
-  except Post.DoesNotExist:
-      jp_post = None
-  
-  try:
-      latest_tweet = Item.objects.published()\
-                         .filter(feed__domain="twitter.com")\
-                         .latest('date')
-  except Item.DoesNotExist:
-      latest_tweet = None
-
-  return object_list(request, 
-      queryset = Item.objects.published().exclude(feed__domain="twitter.com"), 
-      template_name = "lifestream/main.html",
-      extra_context = {
-          'latest_tweet': latest_tweet,
-          "jp_post": jp_post,
-          "en_post": en_post,
-          'rss_feed_url': reverse('lifestream_feeds', kwargs={'url': "recent"}),
-      }
-  )
-
-@require_http_methods(['GET', 'HEAD'])
-def domain_page(request, domain):
-    return object_list(
-        request,
-        queryset=Item.objects.published().filter(feed__domain=domain),
-    )
-
-@require_http_methods(['GET', 'HEAD', 'POST'])
-def item_page(request, item_id):
-    return object_detail(
-        request,
-        queryset=Item.objects.published(),
-        object_id=item_id,    
-    )
-
-@require_http_methods(['GET', 'HEAD'])
-def tag_page(request, tag):
-    return tagged_object_list(
-        request,
-        queryset_or_model=Item.objects.published(),
-        tag=tag,
-        extra_context={
-            "rss_feed_url": reverse("lifestream_feeds", kwargs={"url": "tag/%s" % tag}),
-        }
-    )
-
-@require_http_methods(['GET', 'HEAD'])
-def search(request):
-    # Get unique keywords
-    raw_keywords = request.GET.get("q") or ""
-    domain = request.GET.get("domain")
-    keywords = list(set((raw_keywords).split()))
-    if keywords: 
-        queryset = Item.objects.published()
-        for keyword in keywords:
-            queryset = queryset.filter(Q(title__icontains=keyword) | Q(clean_content__icontains=keyword))
-        if domain:
-            queryset = queryset.filter(feed__domain=domain)
-    else:
-        #queryset = Item.objects.none()
-        return HttpResponseRedirect(reverse('main_page'))
-
-    return object_list(
-        request,
-        template_name='lifestream/item_search.html',
-        queryset=queryset,
-        extra_context={
-            'keywords': raw_keywords,
-        },
-    )

app/manage.py

-#!/usr/bin/env python
-from django.core.management import execute_manager
-import imp
-import os
-import sys
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
-try:
-    imp.find_module('settings') # Assumed to be in the same directory.
-except ImportError:
-    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
-    sys.exit(1)
-
-import settings
-
-if __name__ == "__main__":
-    execute_manager(settings)

app/redirects.py

-#:coding=utf8:
-
-from django.conf.urls.defaults import *
-from django.utils.http import urlquote
-from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponseGone
-
-def redirect_to(request, url, permanent=True, **kwargs):
-    """
-    A copy of the redirect_to view from django.views.generic.simple
-    Copied since redirect_to doesn't work for urls that contain non-ascii 
-    keyword arguments.
-    """
-    if url is not None:
-        klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect
-        quoted_kwargs = {}
-        for k,v in kwargs.iteritems():
-            quoted_kwargs[k] = urlquote(v)
-        return klass(url % quoted_kwargs)
-    else:
-        return HttpResponseGone()
-
-urlpatterns = patterns('',
-    url(r'^page/(?P<page>\d+)/?$', redirect_to, {'url': '/?page=%(page)s'}, name="lifestream_page_redirect"),
-    url(r'^projects(?:/.*)?$', redirect_to, {'url': '/'}, name="projects_redirect"),
-    #url(r'^en/about(?:/.*)?$', redirect_to, {'url': '/'}, name="en_about_redirect"),
-    url(r'^jp/about(?:/.*)?$', redirect_to, {'url': '/en/about/'}, name="jp_about_redirect"),
-    url(r'^jp/aboutjp$', redirect_to, {'url': '/en/about/'}, name="jp_about_redirect2"),
-
-    url(r'^feed/?$', redirect_to, {'url': '/feeds/recent'}),
-
-    # Legacy urls for the blog
-    url(r'^index.php/(?P<locale>\w{2})/?$', redirect_to, {'url': '/%(locale)s/' }),
-    url(r'^index.php$', redirect_to, {'url': '/en/'}),
-    url(r'^index.php/(?P<locale>\w{2})/(?P<slug>[^/]+)/?$', redirect_to, {'url': '/%(locale)s/%(slug)s'}),
-
-    url(r'^en/(?P<tag_name>.+)\;', redirect_to, {'url': '/en/tag/%(tag_name)s'}),
-    url(r'^jp/(?P<tag_name>.+)\;', redirect_to, {'url': '/jp/tag/%(tag_name)s'}),
-)

app/settings.py

-# Django settings for homepage project.
-
-import os
-import sys
-import posixpath
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ROOT_PATH = os.path.dirname(__file__)
-PROJECT_PATH = os.path.dirname(ROOT_PATH)
-
-ADMINS = (
-    #('', ''),
-)
-
-MANAGERS = ADMINS
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': 'djangodb.sqlite',
-        'USER': '',
-        'PASSWORD': '',
-        'HOST': '',
-        'PORT': '',
-    }
-}
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'Asia/Tokyo'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-SITE_MEDIA_ROOT = os.path.join(PROJECT_PATH, 'site_media')
-MEDIA_ROOT = os.path.join(SITE_MEDIA_ROOT, 'media')
-STATIC_ROOT = os.path.join(SITE_MEDIA_ROOT, 'static')
-FILEBROWSER_DIRECTORY = ''
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash if there is a path component (optional in other cases).
-# Examples: "http://media.lawrence.com", "http://example.com/media/"
-SITE_MEDIA_URL = '/'
-MEDIA_URL = posixpath.join(SITE_MEDIA_URL, 'media/')
-STATIC_URL = posixpath.join(SITE_MEDIA_URL, 'static/')
-FILEBROWSER_URL_FILEBROWSER_MEDIA = posixpath.join(STATIC_URL, 'filebrowser/')
-
-STATICFILES_DIRS = (
-    os.path.join(PROJECT_PATH, 'static'),
-)
-
-STATICFILES_MEDIA_DIRNAMES = (
-    'media',
-    'static',
-)
-
-STATICFILES_FINDERS = (
-    "django.contrib.staticfiles.finders.FileSystemFinder",
-    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
-    "homepage.finders.AppMediaDirectoriesFinder",
-)
-
-# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
-# trailing slash.
-# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = posixpath.join(STATIC_URL, 'admin/')
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = 't%o@7x0*zc^r*4@=@*ky=m%_^its#b)t0f9m%fu88(vpt*&8-t'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.load_template_source',
-    'django.template.loaders.app_directories.load_template_source',
-#     'django.template.loaders.eggs.load_template_source',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.core.context_processors.auth",
-    "django.core.context_processors.debug",
-    "django.core.context_processors.i18n",
-    "django.core.context_processors.media",
-    "django.core.context_processors.static",
-    "django.core.context_processors.request",
-)
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'pagination.middleware.PaginationMiddleware',
-    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
-    'django.middleware.transaction.TransactionMiddleware',
-)
-
-ROOT_URLCONF = 'urls'
-
-TEMPLATE_DIRS = (
-    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-    os.path.join(PROJECT_PATH, 'templates'),
-)
-
-INSTALLED_APPS = (
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.comments',
-    'django.contrib.flatpages',
-    'django.contrib.staticfiles',
-    'filebrowser',
-    'south',
-    #'hgwebproxy',
-    'homepage',
-    'pagination',
-    'lifestream',
-    'blog',
-    'tagging',
-    'disqus',
-)
-
-# Need this to get around a bugs in HttpResponseRedirect
-# for non-ascii urls and flatpages
-APPEND_SLASH=False
-
-# django-lifestream
-LIFESTREAM_PLUGINS = (
-  ('lifestream.plugins.FeedPlugin', 'Generic Feed'),
-  ('lifestream.plugins.twitter.TwitterPlugin', 'Twitter Plugin'),
-  ('lifestream.plugins.youtube.YoutubePlugin', 'Youtube Plugin'),
-  ('lifestream.plugins.flickr.FlickrPlugin', 'Flickr Plugin'),
-  ('homepage.plugins.google.GooglePlugin', 'Google Plugin'),
-  ('homepage.plugins.slideshare.SlidesharePlugin', 'Slideshare Plugin'),
-  # ('youtube', 'Youtube'),
-  # ('vimeo', 'Vimeo'),
-  # ('lastfm', 'last.fm'),
-)
-
-LIFESTREAM_FEED_TIMEOUT = 10
-
-# django-tagging
-FORCE_LOWERCASE_TAGS=True
-
-# django-pagination
-PAGINATION_DEFAULT_PAGINATION = 9
-PAGINATION_INVALID_PAGE_RAISES_404 = True 
-PAGINATION_DEFAULT_WINDOW = 3
-
-# django-disqus
-DISQUS_API_KEY = ''
-DISQUS_WEBSITE_SHORTNAME = ''
-
-# logging
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': True,
-    'formatters': {
-        'verbose': {
-            'format': '[%(asctime)s][%(name)s] %(levelname)s %(message)s',
-            'datefmt': "%Y-%m-%d %H:%M:%S",
-        },
-        'simple': {
-            'format': '%(levelname)s %(message)s'
-        },
-    },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'class': 'django.utils.log.AdminEmailHandler'
-        },
-        'null': {
-            'level':'DEBUG',
-            'class':'django.utils.log.NullHandler',
-        },
-        'stderr': {
-            'level': 'ERROR',
-            'formatter': 'verbose',
-            'class':'logging.StreamHandler',
-            'stream': sys.stderr,
-        },
-        'stdout': {
-            'level': 'INFO',
-            'formatter': 'verbose',
-            'class': 'logging.StreamHandler', 
-            'stream': sys.stdout,
-        },
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['mail_admins'],
-            'propagate': False,
-            'level':'WARNING',
-        },
-    }
-}
-
-#HGPROXY_REPO_LIST_REQUIRES_LOGIN = True
-#HGPROXY_STATIC_URL = '/hgstatic/' 
-
-INTERNAL_IPS = (
-    '127.0.0.1',        
-)
-
-SOUTH_MIGRATION_MODULES = {
-    "jogging": "migrations.jogging",
-    "homepage": "migrations.homepage",
-    "blog": "migrations.blog",
-    #"hgwebproxy": "migrations.hgwebproxy",
-    "lifestream": "migrations.lifestream",
-    "tagging": "migrations.tagging",
-}
-
-#try:
-#    import feedparser
-#    feedparser._debug = DEBUG
-#except ImportError:
-#    pass

app/urls.py

-from django.conf.urls.defaults import *
-
-# Uncomment the next two lines to enable the admin:
-from django.contrib import admin
-from django.core.urlresolvers import reverse
-from django.conf import settings
-
-from blog import urls as blog_urls
-from lifestream.rss import RecentItemsFeed
-from lifestream.models import Lifestream,Item
-from tagging.models import Tag,TaggedItem
-
-import redirects
-
-admin.autodiscover()
-
-urlpatterns = redirects.urlpatterns
-
-urlpatterns += patterns('',
-    (r'^admin/filebrowser/', include('filebrowser.urls')),
-    # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
-    # to INSTALLED_APPS to enable admin documentation:
-    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
-    
-    # Uncomment the next line to enable the admin:
-    (r'^admin/', include(admin.site.urls)),
- 
-    (r'accounts/', include('django.contrib.auth.urls')),
-    #(r'^hg/', include('hgwebproxy.urls')),
-)
-
-urlpatterns += blog_urls.urlpatterns
-
-urlpatterns += patterns('',
-    url(r'^$', 'homepage.views.main_page', name='main_page'), 
-    url(r'^items/tag/(?P<tag>.+)$', 'homepage.views.tag_page', name='tag_page'),
-
-    url(r'^items/view/(?P<item_id>\d+)$', 'homepage.views.item_page', name='lifestream_item_page'),
-    url(r'^items/site/(?P<domain>.+)$', 'homepage.views.domain_page', name='lifestream_domain_page'),
-
-    url(r'^items/search$', 'homepage.views.search', name='lifestream_item_search'),
-)
-
-class HomepageRecentItemsFeed(RecentItemsFeed):
-
-    def link(self, obj):
-        return reverse('main_page')
-
-    def item_link(self, item):
-        return reverse('lifestream_item_page', kwargs={
-            'item_id': item.id,
-        })
-
-    def get_object(self, bits):
-        return Lifestream.objects.get(pk=1)
-
-class TaggedItemsFeed(RecentItemsFeed):
-
-    def link(self, obj):
-        return reverse('tag_page', kwargs={'tag': obj.name })
-
-    def item_link(self, item):
-        return reverse('lifestream_item_page', kwargs={
-            'item_id': item.id,
-        })
-
-    def items(self, obj):
-        return TaggedItem.objects.get_by_model(Item.objects.published(), obj)
-
-    def get_object(self, bits):
-        if len(bits) != 1:
-            raise Tag.DoesNotExist
-        return Tag.objects.get(name=bits[0])
-
-feeds = {
-    'recent': HomepageRecentItemsFeed,
-    'tag': TaggedItemsFeed,
-}
-
-urlpatterns += patterns('',
-    url(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds }, name='lifestream_feeds'), 
-)
-
-if settings.DEBUG:
-    from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-    urlpatterns += staticfiles_urlpatterns()
-    urlpatterns += patterns('',
-        (r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], 'django.views.static.serve',
-            {'document_root': settings.MEDIA_ROOT}),
-    )

Empty file added.

Add a comment to this file

homepage/blog/__init__.py

Empty file added.

homepage/blog/admin.py

+#:coding=utf8:
+
+from django.contrib import admin
+from django.contrib.auth.models import User
+
+from homepage.blog.models import Post
+
+class PostAdmin(admin.ModelAdmin):
+    list_display = ("id", "title", "locale", "pub_date", "active")
+    list_filter = ("active","locale")
+    list_display_links = ("id", "title")
+    search_fields = ("title", "content")
+    prepopulated_fields = {"slug": ("title",)}
+
+    def get_form(self, request, obj=None, **kwargs):
+        '''
+        Overwrite get_form to select the currently logged in user as the author
+        Copied from byteflow.
+        '''
+        form = super(PostAdmin, self).get_form(request, obj, **kwargs)
+        f = form.base_fields['author']
+        f.initial = request.user.pk
+        if request.user.is_superuser:
+            f.queryset = User.objects.filter(is_staff=True)
+        else:
+            f.queryset = User.objects.filter(pk=request.user.pk)
+        return form
+
+admin.site.register(Post, PostAdmin)

homepage/blog/decorators.py

+#:coding=utf8:
+
+from django.http import HttpResponseRedirect
+
+def feed_redirect(view):
+    """
+    A decorator that redirects based on some old legacy
+    parameters. Some folks still subscribe to old feed
+    urls.
+    """
+    def wrapped(request, *args, **kwargs):
+        if request.GET.get("tempskin") == "_rss" or \
+           request.GET.get("tempskin") == "_rss2" or \
+           request.GET.get("tempskin") == "_atom":
+            if request.GET.get("lang") == "ja-JP" or \
+               kwargs.get("locale") == "jp":
+                return HttpResponseRedirect("http://feeds2.feedburner.com/IanLewisBlogJP")
+            else:
+                return HttpResponseRedirect("http://feeds2.feedburner.com/IanLewisBlog")
+        return view(request, *args, **kwargs)
+
+    return wrapped
+
+

homepage/blog/feeds.py

+#:coding=utf8:
+
+from django.contrib.syndication.views import Feed
+from django.core.urlresolvers import reverse
+
+from tagging.utils import parse_tag_input
+from tagging.models import Tag, TaggedItem
+
+from .models import Post
+
+class LatestBlogEntries(Feed):
+    def link(self, obj):
+        if isinstance(obj, Tag):
+            return reverse('blog_tag_page', kwargs={
+                'locale':self.locale,
+                'tag': obj.name,
+            })
+        else:
+            return reverse('blog_page', kwargs={'locale':self.locale})
+
+    def items(self, obj):
+        if isinstance(obj, Tag):
+            return TaggedItem.objects.get_by_model(Post.objects.published().filter(locale=self.locale), obj)
+        else:
+            return Post.objects.published().filter(locale=self.locale)[:10]
+
+    def item_author_name(self, item):
+        return item.author.get_full_name()
+
+    def item_author_email(self, item):
+        return item.author.email
+
+    def item_author_link(self, item):
+        return reverse('main_page')
+
+    def item_pubdate(self, item):
+        return item.pub_date
+
+    def item_categories(self, item):
+        return parse_tag_input(item.tags)
+
+    def get_object(self, request, tag=None):
+        if tag:
+            return Tag.objects.get(name=tag)
+        else:
+            return None
+
+class LatestEnglishBlogEntries(LatestBlogEntries):
+    title="Ian Lewis' Blog"
+    description="The latest blog posts from Ian Lewis' blog"
+    locale="en"
+
+class LatestJapaneseBlogEntries(LatestBlogEntries):
+    title=u"イアンルイスのブログ"
+    description=u"イアンルイスのブログの最新エントリ"
+    locale="jp"

homepage/blog/models.py

+#:coding=utf8:
+
+from django.contrib.sites.models import Site
+from django.contrib.auth.models import User
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from tagging.fields import TagField
+
+from datetime import datetime
+
+BLOG_LOCALES = (
+    ('jp', u'日本語'),
+    ('en', u'English'),
+)
+
+class PostManager(models.Manager):
+    def published(self):
+        return self.filter(pub_date__lt = datetime.now(), active=True)
+
+class Post(models.Model):
+    author = models.ForeignKey(User, verbose_name=u"author")
+    slug = models.SlugField(u"slug", max_length=50, unique=True, db_index=True)
+    title = models.TextField(u"title")
+    lead = models.TextField(u"lead", blank=True, null=True, default=None, max_length=600)
+    content = models.TextField(u"content")
+    markup_type = models.CharField(max_length=10, choices=(
+        ("html", "HTML"),
+        ("rst", "reStructuredText"),
+    ), default="html")
+    locale = models.CharField(u'locale', max_length=20, choices=BLOG_LOCALES, default="en", db_index=True)
+    tags = TagField()
+
+    active = models.BooleanField(u'published', default=False, db_index=True)
+    pub_date = models.DateTimeField(u'published', default=datetime.now, db_index=True)
+    create_date = models.DateTimeField(u'created', default=datetime.now)
+   
+    objects = PostManager()
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ('blog_detail', (), {
+            'locale': self.locale,
+            'slug': self.slug,
+        })
+
+    @property
+    def lang(self):
+        return {
+            "en": "en",
+            "jp": "ja",
+        }.get(self.locale, "en")
+    
+    def get_full_url(self):
+        return 'http://%s%s' % (Site.objects.get_current().domain, self.get_absolute_url())
+
+    def __unicode__(self):
+        return self.title
+
+    class Meta:
+        verbose_name = _("post")
+        verbose_name_plural = _("posts")
+        ordering = ('-pub_date',)
Add a comment to this file

homepage/blog/templatetags/__init__.py

Empty file added.

homepage/blog/templatetags/blog_tags.py

+#:coding=utf-8:
+
+import re
+import logging
+import html2text
+import HTMLParser
+
+from django.template import Library
+from django.utils.safestring import mark_safe
+
+from docutils import nodes
+from docutils.writers import html4css1
+from docutils.core import publish_parts
+from docutils.parsers.rst import directives
+
+from pygments import highlight
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import get_lexer_by_name, TextLexer
+
+register = Library()
+
+logger = logging.getLogger(__name__)
+
+VARIANTS = {}
+
+def pygments_directive(name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+    try:
+        lexer = get_lexer_by_name(arguments[0])
+    except (ValueError, IndexError):
+        # no lexer found - use the text one instead of an exception
+        lexer = TextLexer()
+    parsed = highlight(u"\n".join(content), lexer, HtmlFormatter(cssclass="highlight notranslate"))
+    return [nodes.raw("", parsed, format="html")]
+pygments_directive.arguments = (0, 1, False)
+pygments_directive.content = 1
+pygments_directive.options = dict([(key, directives.flag) for key in VARIANTS])
+
+directives.register_directive("sourcecode", pygments_directive)
+directives.register_directive("code-block", pygments_directive)
+
+def lightbox_directive(name, arguments, options, content, lineno,
+        content_offset, block_text, state, state_machine):
+    """
+    lightbox image directive
+
+    usage:
+
+    .. lightbox:: thumb_url img_url
+
+        Title
+    """
+    thumb_href = arguments[0]
+    img_href = arguments[1]
+    title = u" ".join(content)
+
+    html = '<div class="lightbox-img"><a title="%(title)s" rel="lightbox" href="%(img_href)s"><img src="%(thumb_href)s" title="%(title)s" alt=""/></a></div>' % {
+        "title": title, 
+        "img_href": img_href,
+        "thumb_href": thumb_href,
+    }
+    return [nodes.raw("", html, format="html")]
+lightbox_directive.arguments = (2, 2, False)
+lightbox_directive.content = 1
+lightbox_directive.options = dict([(key, directives.flag) for key in VARIANTS])
+
+directives.register_directive("lightbox", lightbox_directive)
+
+class HTMLWriter(html4css1.Writer):
+    def __init__(self):
+        html4css1.Writer.__init__(self)
+        self.translator_class = HTMLTranslator
+
+class HTMLTranslator(html4css1.HTMLTranslator):
+    named_tags = []
+    
+    def visit_literal(self, node):
+        # TODO: wrapping fixes.
+        self.body.append("<code>%s</code>" % node.astext())
+        raise nodes.SkipNode
+
+
+def rst_to_html(value):
+    parts = publish_parts(source=value, writer=HTMLWriter(),
+        settings_overrides={"initial_header_level": 2})
+    return parts["fragment"]
+    
+
+def to_html(obj):
+    if obj.markup_type == "html":
+        html = obj.content
+    elif obj.markup_type == "rst":
+        html = rst_to_html(obj.content)
+    return mark_safe(html)
+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)
+
+@register.filter
+def date_microformat(d):
+    '''
+        Microformat version of a date.
+        2009-02-10T02:58:00+00:00 (ideal)
+        2009-02-09T17:54:41.181868-08:00 (mine)
+    '''
+    return d.isoformat()
+
+STRIKE_REPL_RE = re.compile("< *strike *>[^<]*</ *strike *>", re.IGNORECASE)
+def html_to_text(html):
+    """
+    HTMLを plain/text に変換する
+    """
+    h = html2text.HTML2Text()
+    h.ignore_links = True
+    h.ignore_images = True
+    h.ignore_emphasis = True
+    h.hide_strikethrough = True
+    # <strike>***</strike> テキストを削る 
+    text = STRIKE_REPL_RE.sub("", html)
+    # html2text を呼び出す
+    text = h.handle(text).replace("&nbsp_place_holder;", " ")
+    # 先頭と後続の空白を削る
+    return text.strip()
+
+def abbrev(s, num=255, end="..."):
+    """
+    >>> abbrev('spamspamspam', 6)
+    'spa...'
+    >>> abbrev('spamspamspam', 12)
+    'spamspamspam'
+    >>> abbrev('blahblahblah', 13)
+    'eggseggseg...'
+    >>> abbrev('eggseggseggs', 1)
+    'e'
+    >>> abbrev('eggseggseggs', 2, '.')
+    'e.'
+    """
+    index = num - len(end)
+    if len(s) > num:
+        s = (s[:index] + end) if index > 0 else s[:num]
+    return s
+
+def to_lead(obj, max_len=None):
+    if obj.lead:
+        lead = obj.lead
+    elif obj.content:
+        html = to_html(obj)
+        try:
+            lead = html_to_text(html)
+        except HTMLParser.HTMLParseError,e:
+            logger.error('HTML Parse error: "%s" for text "%s"' % (
+                e,
+                html,
+            ))
+            return ""
+
+    if not max_len:
+        max_len = 300 if obj.locale == "jp" else 600
+    return abbrev(lead, max_len, "[...]")
+         
+register.filter("to_lead", to_lead)

homepage/blog/urls.py

+#:coding=utf8:
+
+from django.conf.urls.defaults import patterns, url
+
+from homepage.blog.feeds import (
+    LatestEnglishBlogEntries,
+    LatestJapaneseBlogEntries,
+)
+
+urlpatterns = patterns('django.views.generic.simple',
+    # Old tag url redirect
+    url(r'^(?P<locale>\w{2})/(?P<tag>[^/]+);$', 'redirect_to', {'url': '/%(locale)s/tag/%(tag)s'}, name='old_blog_tag_page'),
+)
+
+urlpatterns += patterns('homepage.blog.views',
+    url(r'^admin/blog/post/(?P<object_id>[0-9]+)/preview$', 'blog_detail_preview', name='blog_detail_preview'),
+
+    url(r'^(?P<locale>\w{2})/tag/(?P<tag>.+)$', 'tag_page', name='blog_tag_page'),
+        
+    url(r'^(?P<locale>\w{2})/(?P<slug>[^/]+)/?$', 'blog_detail', name='blog_detail'),
+    url(r'^(?P<locale>\w{2})/?$', 'blog_page', name="blog_page"),
+)
+
+urlpatterns += patterns('',
+    url(r'^feed/enfeed/$', LatestEnglishBlogEntries(), name='blog_feed_en'),
+    url(r'^feed/enfeed/(?P<tag>.+)$', LatestEnglishBlogEntries(), name='blog_feed_en_tag'),
+    url(r'^feed/jpfeed/$', LatestJapaneseBlogEntries(), name='blog_feed_jp'),
+    url(r'^feed/jpfeed/(?P<tag>.+)$', LatestJapaneseBlogEntries(), name='blog_feed_jp_tag'),
+)

homepage/blog/views.py

+#:coding=utf8:
+
+from django.views.generic.list_detail import object_list,object_detail
+from django.contrib.admin.views.decorators import staff_member_required
+from django.core.urlresolvers import reverse
+from django.views.decorators.http import require_http_methods
+from django.views.decorators.cache import never_cache
+
+from tagging.views import tagged_object_list
+
+from .models import Post
+from .decorators import feed_redirect
+
+@never_cache
+@staff_member_required
+@require_http_methods(['GET', 'HEAD'])
+def blog_detail_preview(request, object_id):
+    object = Post.objects.get(pk=object_id)
+    defaults = {
+        "queryset": Post.objects.all(),
+        "object_id": object_id,
+        "extra_context": {"locale": object.locale},
+    }
+    return object_detail(request, **defaults)
+
+@require_http_methods(['GET', 'HEAD'])
+@feed_redirect
+def blog_page(request, locale="en"):
+    return object_list(request, 
+        Post.objects.published().filter(locale=locale),
+        extra_context={
+            "locale":locale,
+            "rss_feed_url": reverse("blog_feed_%s" % locale),
+        },
+    )
+
+@require_http_methods(['GET', 'HEAD'])
+def blog_detail(request, slug, locale="en"):
+    defaults = {
+        "queryset": Post.objects.published().filter(locale=locale),
+        "slug": slug,
+        "extra_context": {"locale": locale},
+    }
+    return object_detail(request, **defaults)
+
+@require_http_methods(['GET', 'HEAD'])
+def tag_page(request, tag, locale="en"):
+    return tagged_object_list(
+        request,
+        queryset_or_model=Post.objects.published().filter(locale=locale),
+        tag=tag,
+        template_name="blog/post_list.html",
+        extra_context={
+            'locale':locale,
+            "rss_feed_url": reverse("blog_feed_%s_tag" % locale, kwargs={"tag": tag}),
+        },
+    )
Add a comment to this file

homepage/core/__init__.py

Empty file added.

homepage/core/context_processors.py

+#:coding=utf-8:
+
+from django.conf import settings
+
+def debug(request):
+    return {
+        'debug': settings.DEBUG,
+    }

homepage/core/finders.py

+#:coding=utf-8:
+
+import os
+from django.contrib.staticfiles.finders import AppDirectoriesFinder
+from django.contrib.staticfiles.storage import AppStaticStorage
+
+class AppMediaStorage(AppStaticStorage):
+    source_dir = 'media'
+
+    def get_location(self, app_root):
+        """
+        Given the app root, return the location of the static files of an app,
+        by default 'static'. We special case the admin app here since it has
+        its static files in 'media'.
+        """
+        if self.app_module == 'grappelli':
+            return os.path.join(app_root, 'static')
+        return os.path.join(app_root, self.source_dir)
+
+class AppMediaDirectoriesFinder(AppDirectoriesFinder):
+    storage_class = AppMediaStorage 

homepage/core/middleware.py

+#:coding=utf8:
+
+import re
+
+class GoogleAnalyticsStripCookieMiddleware(object):
+    strip_re = re.compile(r'(__utm.=.+?(?:; |$))')
+    def process_request(self, request):
+        try:
+            before = request.META.get('HTTP_COOKIE')
+            if before:
+                cookie = self.strip_re.sub('', request.META['HTTP_COOKIE'])
+                request.META['HTTP_COOKIE'] = cookie
+        except Exception, e:
+            from jogging import logging
+            logging.exception("Could not script analytics cookies", e, request)

homepage/core/models.py

+import datetime
+
+from django.db import models
+from django.core.exceptions import ImproperlyConfigured
+from django.conf import settings
+
+class Log(models.Model):
+    "A log message, used by jogging's DatabaseHandler"
+    datetime = models.DateTimeField(default=datetime.datetime.now)
+    level = models.CharField(max_length=128, db_index=True)
+    msg = models.TextField()
+    source = models.CharField(max_length=128, blank=True, db_index=True)
+    host = models.CharField(max_length=200, blank=True, null=True, db_index=True)
+
+    def abbrev_msg(self, maxlen=500):
+        if len(self.msg) > maxlen:
+            return u'%s ...' % self.msg[:maxlen]
+        return self.msg
+    abbrev_msg.short_description = u'abbreviated message'
Add a comment to this file

homepage/core/templatetags/__init__.py

Empty file added.

homepage/core/templatetags/utility_tags.py

+#:coding=utf8:
+import re
+
+from datetime import datetime,timedelta
+
+from django.utils.safestring import mark_safe
+from django.utils.translation import ngettext,ugettext_lazy as _
+from django.template.defaultfilters import stringfilter
+from django import template
+
+from homepage.core.utils import batch
+
+register = template.Library()
+
+register.filter(batch)
+
+@register.filter
+@stringfilter
+def truncate_chars(value, max_length):
+    try:
+        max_length = int(max_length)
+        if len(value.rstrip()) > max_length:
+            truncd_val = value[:max_length]
+            truncd_val = truncd_val.rstrip()
+            return truncd_val + "..."
+        return value
+    except:
+        return ""
+
+def stripentities(value):
+    """Strips all HTML entities"""
+    from django.utils.html import strip_entities
+    return strip_entities(value)
+stripentities.is_safe = True
+stripentities = stringfilter(stripentities)
+register.filter(stripentities)
+
+@register.filter
+def friendly_date(date, include_time=False):
+  """
+  Prints a human friendly date.
+  """
+  delta = datetime.now() - date
+   
+  if delta < timedelta(seconds=60):
+    msg = _("Just now")
+  elif delta < timedelta(seconds=60*60):
+    minutes = delta.seconds / 60
+    msg = ngettext('%(minutes)s minute ago',
+            '%(minutes)s min ago', minutes) % {
+      'minutes': minutes,
+    }
+  elif delta < timedelta(days=1):
+    hours = int(delta.seconds / 60 / 60)
+    msg = ngettext('%(hours)s hour ago',
+            '%(hours)s hours ago', hours) % {
+      'hours': hours,
+    }
+  elif delta < timedelta(days=7):
+    msg = ngettext('%(days)s day ago',
+            '%(days)s days ago', delta.days) % {
+      'days': delta.days,
+    }
+  elif delta < timedelta(days=31):
+    weeks = int(delta.days / 7)
+    msg = ngettext('%(weeks)s week ago',
+            '%(weeks)s weeks ago', weeks) % {
+      'weeks': weeks,
+    }
+  elif delta < timedelta(days=365):
+    months = int(delta.days / 31)
+    msg = ngettext('%(months)s month ago',
+            '%(months)s months ago', months) % {
+      'months': months,
+    }
+  else:
+    years = int(delta.days / 365)
+    msg = ngettext('%(years)s year ago',
+            '%(years)s years ago', years) % {
+      'years': years,
+    }
+  return mark_safe(msg)
+
+@register.filter
+def cat(value, other_value):
+    return "%s%s" % (value, other_value)
+
+@register.filter
+def urlize_twitter(text):
+    return mark_safe(
+        re.sub(r'@([a-zA-Z0-9_]*)', r'@<a href="http://twitter.com/\1">\1</a>',
+            re.sub(r'#([a-zA-Z0-9_-]*)', r'<a href="http://twitter.com/#search?q=\1">#\1</a>', text)
+        )
+    )

homepage/core/utils.py

+#:coding=utf-8:
+
+from itertools import islice, chain
+
+def batch(iterable, size):
+    sourceiter = iter(iterable)
+    while True:
+        batchiter = islice(sourceiter, size)
+        yield chain([batchiter.next()], list(batchiter))

homepage/core/views.py

+#:coding=utf8:
+
+from django.http import HttpResponseRedirect 
+#from django.views.generic.list_detail import object_list, object_detail
+from django.views.decorators.http import require_http_methods
+from django.shortcuts import render
+
+from homepage.blog.models import Post
+
+@require_http_methods(['GET', 'HEAD'])
+def main_page(request):
+    en_posts = Post.objects.published().filter(locale="en").order_by("-pub_date")
+    jp_posts = Post.objects.published().filter(locale="jp").order_by("-pub_date")
+    
+    return render(request, "index.html", {
+        #'latest_tweet': latest_tweet,
+        "jp_posts": jp_posts,
+        "en_posts": en_posts,
+    })

homepage/redirects.py

+#:coding=utf8:
+
+from django.conf.urls.defaults import patterns, url
+from django.utils.http import urlquote
+from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponseGone
+
+def redirect_to(request, url, permanent=True, **kwargs):
+    """
+    A copy of the redirect_to view from django.views.generic.simple
+    Copied since redirect_to doesn't work for urls that contain non-ascii 
+    keyword arguments.
+    """
+    if url is not None:
+        klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect
+        quoted_kwargs = {}
+        for k,v in kwargs.iteritems():
+            quoted_kwargs[k] = urlquote(v)
+        return klass(url % quoted_kwargs)
+    else:
+        return HttpResponseGone()
+
+urlpatterns = patterns('',
+    url(r'^page/(?P<page>\d+)/?$', redirect_to, {'url': '/?page=%(page)s'}, name="lifestream_page_redirect"),
+    url(r'^projects(?:/.*)?$', redirect_to, {'url': '/'}, name="projects_redirect"),
+    #url(r'^en/about(?:/.*)?$', redirect_to, {'url': '/'}, name="en_about_redirect"),
+    url(r'^jp/about(?:/.*)?$', redirect_to, {'url': '/en/about/'}, name="jp_about_redirect"),
+    url(r'^jp/aboutjp$', redirect_to, {'url': '/en/about/'}, name="jp_about_redirect2"),
+
+    url(r'^feed/?$', redirect_to, {'url': '/feeds/recent'}),
+
+    # Legacy urls for the blog
+    url(r'^index.php/(?P<locale>\w{2})/?$', redirect_to, {'url': '/%(locale)s/' }),
+    url(r'^index.php$', redirect_to, {'url': '/en/'}),
+    url(r'^index.php/(?P<locale>\w{2})/(?P<slug>[^/]+)/?$', redirect_to, {'url': '/%(locale)s/%(slug)s'}),
+
+    url(r'^en/(?P<tag_name>.+)\;', redirect_to, {'url': '/en/tag/%(tag_name)s'}),
+    url(r'^jp/(?P<tag_name>.+)\;', redirect_to, {'url': '/jp/tag/%(tag_name)s'}),
+
+    # Old lifestream urls. 
+    url(r'^items/', redirect_to, {'url': '/'}),
+)

homepage/settings.py

+# Django settings for homepage project.
+
+import os
+import sys
+import posixpath
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ROOT_PATH = os.path.dirname(__file__)
+PROJECT_PATH = os.path.dirname(ROOT_PATH)
+
+ADMINS = (
+    #('', ''),
+)
+
+MANAGERS = ADMINS
+
+#DATABASES = {
+#    'default': {
+#        'ENGINE': 'django.db.backends.sqlite3',
+#        'NAME': 'djangodb.sqlite',
+#        'USER': '',
+#        'PASSWORD': '',
+#        'HOST': '',
+#        'PORT': '',
+#    }
+#}
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': 'ianlewis_hp',
+        'USER': 'root',
+        'PASSWORD': '',
+        'HOST': '',
+        'PORT': '',
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Asia/Tokyo'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+SITE_MEDIA_ROOT = os.path.join(PROJECT_PATH, 'site_media')
+MEDIA_ROOT = os.path.join(SITE_MEDIA_ROOT, 'media')
+STATIC_ROOT = os.path.join(SITE_MEDIA_ROOT, 'static')
+FILEBROWSER_DIRECTORY = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+SITE_MEDIA_URL = '/'
+MEDIA_URL = posixpath.join(SITE_MEDIA_URL, 'media/')
+STATIC_URL = posixpath.join(SITE_MEDIA_URL, 'static/')
+FILEBROWSER_URL_FILEBROWSER_MEDIA = posixpath.join(STATIC_URL, 'filebrowser/')
+
+STATICFILES_DIRS = (
+    os.path.join(PROJECT_PATH, 'static'),
+)
+
+STATICFILES_MEDIA_DIRNAMES = (
+    'media',
+    'static',
+)
+
+STATICFILES_FINDERS = (
+    "django.contrib.staticfiles.finders.FileSystemFinder",
+    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
+    "homepage.core.finders.AppMediaDirectoriesFinder",
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 't%o@7x0*zc^r*4@=@*ky=m%_^its#b)t0f9m%fu88(vpt*&8-t'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    "django.contrib.auth.context_processors.auth",
+    'django.contrib.messages.context_processors.messages',
+    "django.core.context_processors.i18n",
+    "django.core.context_processors.media",
+    "django.core.context_processors.static",
+    "django.core.context_processors.request",
+    "homepage.core.context_processors.debug",
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.transaction.TransactionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'pagination.middleware.PaginationMiddleware',
+    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
+)
+
+ROOT_URLCONF = 'homepage.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+    os.path.join(PROJECT_PATH, 'templates'),
+)
+
+INSTALLED_APPS = (
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.comments',
+    'django.contrib.flatpages',
+    'django.contrib.staticfiles',
+
+    # Third party
+    'south',
+    'filebrowser',
+    'pagination',
+    'tagging',
+    'disqus',
+
+    # app
+    'homepage.core',
+    'homepage.blog',
+)
+
+# Need this to get around a bugs in HttpResponseRedirect
+# for non-ascii urls and flatpages
+APPEND_SLASH=False
+
+# django-tagging
+FORCE_LOWERCASE_TAGS=True
+
+# django-pagination
+PAGINATION_DEFAULT_PAGINATION = 9
+PAGINATION_INVALID_PAGE_RAISES_404 = True 
+PAGINATION_DEFAULT_WINDOW = 3
+
+# django-disqus
+DISQUS_API_KEY = ''
+DISQUS_WEBSITE_SHORTNAME = ''
+
+# logging
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'formatters': {
+        'verbose': {
+            'format': '[%(asctime)s][%(name)s] %(levelname)s %(message)s',
+            'datefmt': "%Y-%m-%d %H:%M:%S",
+        },
+        'simple': {
+            'format': '%(levelname)s %(message)s'
+        },
+    },
+    'handlers': {
+        'mail_admins': {