Mango /

Full commit
# -*- coding: utf-8 -*-

import datetime
import hashlib
import os
import re

import disqus

from django.core.mail import EmailMultiAlternatives, send_mail
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import Context, loader
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
from mango import settings, utils
from mango.forms import CommentForm, ContactForm
from mango.settings import *
from mango.templatetags.mango_extras import slugify
from mango.utils import convert_html_chars, RE

    'approve': {'do': 'Approve comment', 'done': 'Comment approved.'},
    'delete': {'do': 'Delete comment', 'done': 'Comment deleted.'},
    'spam': {'do': 'Mark comment as spam', 'done': 'Comment marked as spam.'},

def redirect(request, path):
    return HttpResponseRedirect('/%s/' % path)

def context_defaults(request):
    path_to_css, css = settings.__dict__.get('CSS', (None, []))
    path_to_js, js = settings.__dict__.get('JS', (None, []))
    return {
        'request': request,
        'settings': settings,
        'stylesheets': [dict(media=media, href=path_to_css.lstrip('.')+filename) for media, stylesheets in css for filename in stylesheets],
        'scripts': [dict(src=path_to_js.lstrip('.')+filename) for filename in js],

def index(request):
    return render_to_response('index.dhtml', dict({
                'posts': [post for year, month, posts in utils.posts() for post in posts],
            }, **context_defaults(request)))

def archives(request):
    return render_to_response('archives.dhtml', dict({'posts': utils.posts()}, **context_defaults(request)))

def tags(request):
    tags = {}
    for year, month, these_posts in utils.posts():
        for post in these_posts:
            for tag in post['meta'].get('tags', []):
                if tag in tags:
                    tags[tag] += 1
                    tags[tag] = 1
    tags = [(key, value) for key, value in sorted(tags.items(), key=lambda pair: pair[0].lower())]
    return render_to_response('tags.dhtml', dict({'tags': tags}, **context_defaults(request)))

def tagged_as(request, tag):
    tag = slugify(tag)
    posts = []
    for year, month, these_posts in utils.posts():
        for post in these_posts:
            if tag in [slugify(t) for t in post['meta'].get('tags', [])]:
    return render_to_response('index.dhtml', dict({'posts': posts}, **context_defaults(request)))

#TODO - this name should be changed as it now is also an index page, should this be combined with the index post?
def post(request, path, view_source=False):
    def render_post(filepath):
        context = utils.parse_markdown(utils.get_contents(filepath))
        form = CommentForm()
        thread = None
        comment = request.session.get('comment')
        if comment:
            del request.session['comment']
            t = loader.get_template('comment.dhtml')
            comment = t.render(Context({'comment': comment}))

        def get_forum(forums):
            shortname = DISQUS['shortname']
            for forum in forums:
                if forum.shortname == shortname:
                    return forum
            raise disqus.APIError

        comments = []
        #TODO - This will need to be updated, I don't currently have a DISQUS account so can't test, but I've changed the identifier
        if settings.__dict__.get('DISQUS'):
                dsq = disqus.DisqusService(DISQUS_API_VERSION)
                forum = get_forum(dsq.get_forum_list())
                thread = dsq.get_thread_by_url(forum, BASE_URL.rstrip('/') + canon_url)
                if not thread:
                    thread = dsq.thread_by_identifier(forum, context['meta']['title'], canon_url)['thread']

                if request.method == 'POST':
                    form = CommentForm(request.POST)
                    if form.is_valid():
                        author_name = form.cleaned_data['author_name']
                        author_email = form.cleaned_data['author_email']
                        author_url = form.cleaned_data['author_url']
                        message = form.cleaned_data['message']
                        subscribe = form.cleaned_data['subscribe']

                        # send request to Disqus
                        comment = dsq.create_post(forum=forum, thread=thread, message=message,
                                author_name=author_name, author_email=author_email, author_url=author_url)

                        # store comment so that it can be displayed to the author even if withheld for moderation
                        request.session['comment'] = comment

                        # send e-mail notification
                        author = u'%s <%s>' % (author_name, author_email)
                        subject = u'[%s] Comment: "%s"' % (SITE_TITLE.decode('utf-8'), context['meta']['title'])

                        query_string = u'api_key=%s&post_id=%s' % (

                        text_content = u'%s wrote:\n\n%s' % (author, message)
                        html_content = u'<p>%s wrote:</p>' % convert_html_chars(author)
                        html_content += u'<blockquote>%s</blockquote><ul>' % re.sub(r'([ \t]*\n){2,}', '</p><p>',
                        for action, messages in sorted(MESSAGES.items()):
                            text_content += u'\n\n* %s: %s/moderate/%s?%s' % (
                                    messages['do'], BASE_URL.rstrip('/'), action, query_string)
                            html_content += u'<li><a href="%s/moderate/%s?%s">%s</a></li>' % (
                                    BASE_URL.rstrip('/'), action, convert_html_chars(query_string), messages['do'])
                        html_content += u'</ul>'

                        msg = EmailMultiAlternatives(subject, text_content, author, [utils.primary_author_email()])
                        msg.attach_alternative(html_content, 'text/html')

                        return HttpResponseRedirect('redirect/')

                for c in dsq.get_thread_posts(forum, thread, limit=0, exclude='killed'):
                    if c.has_been_moderated:
                if DISQUS.get('sort') == 'oldest_first':
                    comments.sort(key=lambda comment: comment.created_at)
            except disqus.APIError:

        return render_to_string('post.dhtml', dict(context, **dict({
                    'comments': comments,
                    'form': form,
                    'new_comment': comment,
                    'short_url': short_url,
                    'thread': thread,
                    'type': 'post' if 'datetime' in context['meta'] else 'page',
                }, **context_defaults(request))))

    filepath = os.path.join(os.sep, *utils.absolute_path_to_posts().split('/'))
    is_short = False
    fragments = path.split('/')
    last_index = len(fragments) - 1
    for index, fragment in enumerate(fragments):
        found = False
        for entry in os.listdir(filepath):
            name = entry
            if index == last_index:
                name, ext = os.path.splitext(entry)
            match = re.match(RE['alias=>canon'], name)
            if fragment in match.groupdict().values():
                found = True
                if fragment =='alias'):
                    is_short = True
                filepath = os.path.join(filepath, entry)
        if not found:
            raise Http404

    short_url, canon_url = utils.post_urls(filepath)

    if is_short:
        if view_source:
            canon_url += 'm/'
        return HttpResponseRedirect(canon_url)

    if os.path.isdir(filepath):
        #TODO - if view_source is true what do we do here?
        #TODO - caching
        html = render_to_response('index.dhtml', dict({
            'posts': [post for year, month, posts in utils.posts(filepath) for post in posts],
        }, **context_defaults(request)))
        return HttpResponse(html)

    if view_source: # Return the plain text of the Markdown page.
        text = utils.parse_markdown(text=utils.get_contents(filepath), plaintext=True)
        return HttpResponse(text, content_type='text/plain; charset=utf-8')

    if os.path.islink(filepath): # symbolic link
        short_url, canon_url = utils.post_urls(os.path.realpath(filepath))
        return HttpResponseRedirect(canon_url)

    # Parse the Markdown and return it through the Django template.
    value = render_post(filepath)
    if isinstance(value, HttpResponseRedirect):
        return value

    return HttpResponse(value)

def contact(request, message_sent=False):
    if message_sent:
        return render_to_response('contact.dhtml', context_defaults(request))

    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']
            sender = u'%s <%s>' % (form.cleaned_data['sender_name'], form.cleaned_data['sender_email'])

            recipients = [utils.primary_author_email()]
            if form.cleaned_data['cc_sender']:

            send_mail(subject, message, sender, recipients, fail_silently=False)
            return HttpResponseRedirect('thanks/')
        form = ContactForm()

    return render_to_response('contact.dhtml', dict({'form': form}, **context_defaults(request)))

def moderate(request, action):
    if request.GET.get('api_key') != hashlib.sha1(DISQUS['api_key']).hexdigest():
        return HttpResponseBadRequest('Invalid API key.')

    dsq = disqus.DisqusService(DISQUS_API_VERSION)
    dsq.moderate_post(request.GET.get('post_id'), 'kill' if action == 'delete' else action)
    return HttpResponse(MESSAGES[action]['done'], content_type='text/plain; charset=utf-8')