1. David Chambers
  2. Mango


Mango / views.py

# -*- coding: utf-8 -*-

from django.core.mail import EmailMultiAlternatives
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponseServerError
from django.template.loader import render_to_string
from django.utils.http import urlencode

import mango.settings
from mango.exceptions import EmptySettingError
from mango.forms import CommentForm, ContactForm
from mango.main import Category, Document, Index
if mango.settings.SUBSCRIPTIONS:
    from mango.models import Subscription
from mango.templatetags.mango import convert
from mango.utils import (html_response, logger, primary_author_email,
                         replace, slugify, text_response)

def archives(request):
    return html_response('archives', request)

def category(request, category):
    return html_response('category', request, {'category': category})

def contact(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 = [primary_author_email()]
            except EmptySettingError, error:
                return HttpResponseServerError(error.html())

            headers = {'Reply-To': sender}
            if form.cleaned_data['cc_sender']:
                headers['Cc'] = sender  # `cc` argument added in Django 1.3

            msg = EmailMultiAlternatives(subject, message, sender, recipients, headers=headers)
            msg.attach_alternative(replace(convert(message)), 'text/html')
            return HttpResponseRedirect(reverse(message_sent))
        form = ContactForm()

    return html_response('contact', request, {'form': form})

def index(request):
    if mango.settings.PAGING:
        return page(request, number=1)
        return html_response('index', request)

def message_sent(request):
    return html_response('contact', request)

def page(request, number, count=mango.settings.DISPLAY_COUNT):
    # canonicalize: /page/1/ -> /
    if request.path == reverse(page, args=(1,)):
        return HttpResponseRedirect(reverse(index))

    number = int(number)
    offset = (number - 1) * count
    posts = Index.get().descendants()
    documents = posts[offset:offset+count]
    if not documents:
        return page_not_found(request)

    prev_url = next_url = None
    if offset > 0:
        prev_url = reverse(page, args=(number - 1,))
    if offset + count < len(posts):
        next_url = reverse(page, args=(number + 1,))

    return html_response('paged', request, {'documents': documents,
            'next_url': next_url, 'number': number, 'prev_url': prev_url,
            'total': (len(posts) + count - 1) / count})

def page_not_found(request):
    return html_response('404', request, status_code=404)

def post(request, path):
    path_ = path
    path = u'%s%s/' % (reverse(index), path)
    match = Index.get().find_match(path)
    if not isinstance(match, (Category, Document)):
        return page_not_found(request)

    if path != match.identifier():
        return HttpResponseRedirect(match.permalink())

    if isinstance(match, Category):
        return category(request, match)

    document = match  # we've confirmed that the we're to serve a Document
    thread = None
    if mango.settings.DISQUS:
        thread = document._thread()

    comment = request.session.pop('comment', None) if thread else None
    if comment is not None:
        comment = render_to_string('comment.html', {'comment': comment})

    while True:  # avoids excessive indentation
        form = CommentForm()
        if request.method != 'POST': break

        form = CommentForm(request.POST, request=request)
        if not form.is_valid(): break

        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']

        # don't assume that `SUBSCRIPTIONS` is True
        if subscribe and mango.settings.SUBSCRIPTIONS:
            subscriptions = Subscription.objects.filter(
                    subscriber_email=author_email, url=document.permalink())
            if not subscriptions:
                subscription = Subscription(subscriber_name=author_name,
                        subscriber_email=author_email, url=document.permalink())
            logger.debug('%s is %s subscribed to comments on %s' % (author_name,
                    'already' if subscriptions else 'now', document.title_text))

        if not thread: break

        from mango import disqus
            # send request to Disqus
            comment = disqus.post.create(
        except disqus.APIError, error:
            logger.warning('Disqus API error: %s' % error)

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

        # send e-mail notification
        post_url = request.build_absolute_uri(reverse(post, args=(path_,)))
        params = {
            'api_key': disqus.api_key,
            'post_id': comment.id,
            'thread_id': thread.id,
            'url': post_url,
        links = {
            'approve':  ('api_key', 'post_id'),
            'close':    ('api_key', 'thread_id'),
            'delete':   ('api_key', 'post_id'),
            'spam':     ('api_key', 'post_id'),
        author = u'%s <%s>' % (author_name, author_email)
        subject = u'[%s] Comment: "%s"' % (mango.settings.SITE_TITLE, document.title_text)
        context = {
            'comment': message,
            'commenter': author,
            'urls': dict([('post', post_url)] + [(action, u'%s?%s' % (
                    request.build_absolute_uri(reverse('mango.handlers.moderate', args=(action,))),
                    urlencode([(k, params[k]) for k in keys])))
                    for action, keys in links.items()]),
            recipient = primary_author_email()
        except EmptySettingError, error:
            return HttpResponseServerError(error.html())

        msg = EmailMultiAlternatives(subject, render_to_string('email/moderator.text', context),
                to=[recipient], headers={'Reply-To': author})
        msg.attach_alternative(render_to_string('email/moderator.html', context), 'text/html')

        return HttpResponseRedirect(reverse('mango.handlers.redirect', args=(path_, comment.id)))

    return html_response(document.type, request, {
        'comments': document.comments(),
        'document': document,
        'form': form,
        'new_comment': comment,
        'thread': thread,

def search(request):
    query = request.GET.get('query', '').strip().lower()
    # convert query to a list of search terms
    terms = []
    for index, fragment in enumerate(query.split('"')):
        if index % 2:  # every second fragment is quoted
            for word in fragment.split():

    # determine which documents match the search terms
    pages, posts = [], []
    for document in Index.get().descendants(include_pages=True):
        comments = [comment.message for comment in document.comments()]
        # every term must appear in the document or in its comment thread
        if (all(term in document.source.lower() or
            any(term in comment for comment in comments)
            for term in terms)):
            if document.type == 'page':

    return html_response('searchresults', request,
            {'results': {'pages': pages, 'posts': posts}, 'terms': terms})

def tagged_as(request, tag):
    tag = slugify(tag)
    documents = []
    for document in Index.get().descendants(include_pages=True):
        if tag in [slugify(t) for t in document.meta.get('tags', [])]:
    return html_response('tag', request, {'documents': documents, 'tag': tag})

def tags(request):
    return html_response('tags', request)

def view_source(request, path):
    path = u'%s%s/' % (reverse(index), path)
    match = Index.get().find_match(path)

    if isinstance(match, Category):
        category = match
        if category.source is not None:
            return text_response(category.source)
            return HttpResponseRedirect(category.permalink())

    if isinstance(match, Document):
        document = match
        if path == document.identifier():
            return text_response(document.source)
            return HttpResponseRedirect('%s%s' % (document.permalink(),

    return page_not_found(request)