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

            try:
                recipients = [primary_author_email()]
            except EmptySettingError, error:
                logger.error(error.message)
                return HttpResponseServerError(error.html())

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

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

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

def index(request):
    if mango.settings.PAGING:
        return page(request, number=1)
    else:
        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())
                subscription.save()
            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
        try:
            # send request to Disqus
            comment = disqus.post.create(
                    thread=thread.id,
                    message=message,
                    author_name=author_name,
                    author_email=author_email,
                    author_url=author_url,
                    ip_address=request.META['REMOTE_ADDR'])
        except disqus.APIError, error:
            logger.warning('Disqus API error: %s' % error)
            break

        # 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()]),
        }
        try:
            recipient = primary_author_email()
        except EmptySettingError, error:
            logger.error(error.message)
            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')
        msg.send(fail_silently=False)

        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
            terms.append(fragment)
        else:
            for word in fragment.split():
                terms.append(word)

    # 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':
                pages.append(document)
            else:
                posts.append(document)

    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', [])]:
            documents.append(document)
    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)
        else:
            return HttpResponseRedirect(category.permalink())

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

    return page_not_found(request)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.