Source

Mango / views.py

Full commit
#!/usr/bin/python
# -*- coding: utf-8 -*-

import datetime
import hashlib
import os
import re
import urllib

import disqus

from django.core.cache import cache
from django.core.mail import EmailMultiAlternatives, send_mail
from django.core.urlresolvers import reverse
from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template.loader import render_to_string

from mango import settings, utils
from mango.forms import CommentForm, ContactForm
from mango.models import RE
from mango.settings import *
from mango.templatetags.mango_extras import slugify

def redirect(request, path):
    return HttpResponseRedirect(reverse('mango.views.post', args=[path]))

def context_defaults(request):
    path_to_css, css = getattr(settings, 'CSS', (None, []))
    path_to_js, js = getattr(settings, 'JS', (None, []))
    posts, pages = utils.get_documents()
    return {
        'archives': utils.archives(),
        'posts': posts,
        'pages': pages,
        '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],
        'now': datetime.datetime.now(),
    }

def index(request):
    return render_to_response('index.dhtml', context_defaults(request))

def archives(request):
    return render_to_response('archives.dhtml', context_defaults(request))

def tags(request):
    tags = {}
    posts, pages = utils.get_documents()
    for document in posts + pages:
        for tag in document.meta.get('tags', []):
            tags[tag] = tags.get(tag, 0) + 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, pages = utils.get_documents()
    return render_to_response('tag.dhtml', dict({
                'tag': tag,
                'tagged': [doc for doc in posts + pages if tag in [slugify(t) for t in doc.meta.get('tags', [])]],
            }, **context_defaults(request)))

def post(request, path, view_source=False):
    fragments = path.split('/')
    fragments.reverse()

    files = []
    directories = []
    for dirpath, dirnames, filenames in os.walk(UNIX_PATH_TO_POSTS):
        for filename in filenames:
            head, tail = os.path.split(os.path.join(dirpath, filename))
            unix_path = u'/'.join(head.split(os.sep)).replace(UNIX_PATH_TO_POSTS, '')
            files.append((os.path.join(*unix_path.split('/')), tail))
        for dirname in dirnames:
            unix_path = u'/'.join(os.path.join(dirpath, dirname).split(os.sep)).replace(
                    UNIX_PATH_TO_POSTS, '')
            directories.append(os.path.join(*unix_path.split('/')))

    filepath = None
    is_short = False
    for head, tail in files:
        match = re.match(RE['alias=>canon'], os.path.splitext(tail)[0])
        if not filepath and fragments[0] in match.groupdict().values():
            filepath = tail
            if fragments[0] == match.group('alias'):
                is_short = True
            for fragment in fragments[1:]:
                head, tail = os.path.split(head)
                match = re.match(RE['alias=>canon'], tail.lower())
                if fragment in match.groupdict().values():
                    filepath = os.path.join(tail, filepath)
                    if not is_short and fragment == match.group('alias'):
                        is_short = True
                else:
                    filepath = None
                    is_short = False
                    break

    if not filepath:
        for directory in directories:
            head = directory
            if not filepath:
                for fragment in fragments:
                    head, tail = os.path.split(head)
                    match = re.match(RE['alias=>canon'], tail.lower())
                    if fragment in match.groupdict().values():
                        filepath = os.path.join(tail, filepath) if filepath else tail
                        if fragment == match.group('alias'):
                            is_short = True
                    else:
                        filepath = None
                        is_short = False
                        break

    if filepath:
        filepath = os.path.join(PATH_TO_POSTS, filepath)

    if not filepath or not os.path.exists(filepath):
        raise Http404

    if os.path.isdir(filepath):
        if view_source:
            return HttpResponsePermanentRedirect('../')

        fragments = [u'']
        head, tail = os.path.split(filepath)
        while tail:
            match = re.match(RE['alias=>canon'], tail)
            fragments.insert(1, os.path.join(tail, filepath) if filepath else tail)
            head, tail = os.path.split(head)
        filepath = os.path.join(*fragments)

        if is_short:
            fragments = [u'']
            for fragment in filepath.split(os.sep):
                if fragment:
                    match = re.match(RE['alias=>canon'], fragment)
                    fragments.append(match.group('canon'))
            return HttpResponseRedirect(u'/'.join(fragments).replace(
                    UNIX_PATH_TO_POSTS, ''))

        match = re.match(RE['alias=>canon'], os.path.split(filepath)[1])
        category_posts, category_pages = utils.get_documents(filepath)
        return render_to_response('category.dhtml', dict({
            'name': match.group('canon'),
            'category_posts': category_posts,
            'category_pages': category_pages,
        }, **context_defaults(request)))

    document = utils.get_document(filepath)

    if is_short:
        url = document.urls['canon']['abs']
        if view_source:
            url += 'm/'
        return HttpResponseRedirect(url)

    if os.path.islink(filepath): # symbolic link
        return HttpResponseRedirect(document.urls['canon']['rel'])
    elif view_source: # Return the plain text of the Markdown page.
        return HttpResponse(document.body, content_type='text/plain; charset=utf-8')

    # Parse the Markdown and return it through the Django template.
    form = CommentForm()
    thread = None
    comment = request.session.get('comment')
    if comment:
        del request.session['comment']
        comment = render_to_string('comment.dhtml', {'comment': comment})

    comments = []
    if DSQ and FORUM:
        try:
            # This call is only here for legacy and migration support, it will never return a value on Mango made threads
            thread = DSQ.get_thread_by_url(FORUM, document.urls['canon']['abs'])
            if not thread:
                thread = DSQ.thread_by_identifier(FORUM, document.title, document.urls['canon']['rel'])['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']

                    try:
                        ip_address = urllib.urlopen('http://www.whatismyip.com/automation/n09230945.asp').readlines()[0]
                    except:
                        ip_address = None

                    # send request to Disqus
                    comment = DSQ.create_post(FORUM, thread, message=message, ip_address=ip_address,
                            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, document.title)
                    context = {
                        'commenter': author,
                        'comment': message,
                        'base_url': BASE_URL,
                        'path': path,
                        'api_key': hashlib.sha1(DISQUS['api_key']).hexdigest(),
                        'post_id': comment.id,
                        'thread_id': thread.id,
                    }
                    msg = EmailMultiAlternatives(subject, render_to_string('email.dtext', context),
                            to=[utils.primary_author_email()], headers={'Reply-To': author})
                    msg.attach_alternative(render_to_string('email.dhtml', context), 'text/html')
                    msg.send(fail_silently=False)

                    return HttpResponseRedirect('redirect/')

            for c in DSQ.get_thread_posts(FORUM, thread, limit=9999, exclude='killed'):
                if c.has_been_moderated:
                    comments.append(c)
            if DISQUS.get('sort') == 'oldest_first':
                comments.sort(key=lambda comment: comment.created_at)
        except disqus.APIError:
            pass

    return render_to_response('%s.dhtml' % document.type, dict({
                'document': document,
                'comments': comments,
                'form': form,
                'new_comment': comment,
                'thread': thread,
            }, **context_defaults(request)))

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']:
                recipients.append(sender)

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

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

def moderate(request, action):
    if not DSQ:
        return HttpResponseServerError('Invalid DISQUS settings.')

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

    post_id = request.GET.get('post_id')

    if action == 'close':
        if not FORUM:
            return HttpResponseServerError('Invalid DISQUS settings.')

        thread_id = request.GET.get('thread_id')
        if not thread_id:
            return HttpResponseBadRequest('Invalid thread id.')

        thread = None
        for thread in DSQ.get_thread_list(FORUM, limit=9999):
            if thread.id == thread_id:
                break
            thread = None
            
        if not thread:
            return HttpResponseBadRequest('Invalid thread id.')

        DSQ.update_thread(FORUM, thread, allow_comments=False)
        message = 'Thread closed.'

    elif action == 'approve':
        DSQ.moderate_post(post_id, 'approve')
        message = 'Comment approved.'
    else:
        DSQ.moderate_post(post_id, 'kill')
        message = 'Comment deleted.'
        if action == 'spam':
            DSQ.moderate_post(post_id, 'spam')
            message = 'Comment marked as spam and deleted.'

    return HttpResponse(message, content_type='text/plain; charset=utf-8')