cciw-website / cciw / cciwmain /

Full commit
Utility functions and base classes that are common to all views etc.
import datetime
import re
import sys
import traceback

from django.conf import settings
from django.core.mail import mail_admins
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.utils.safestring import mark_safe
from django.views.generic.edit import FormView
from django.views.generic.list import ListView

from cciw.cciwmain.utils import python_to_json
import cciw.middleware.threadlocals as threadlocals

# CBV baseclass functionality
class DefaultMetaData(object):
    Mixin that provides some default metadata and other standard variables to the
    page context. Assumes the use of TemplateView.

    Also provides a mechanism for other context data:

     * 'extra_context' attribute can be stored on the class
       and will be used as a starting point for context data

     * After the instance has been initialised, 'context'
       will be available on the instance as a place to store context data.
    metadata_title = None
    metadata_description = None
    metadata_keywords = None
    extra_context = None

    def __init__(self, **kwargs):
        # Provide place for arbitrary context to get stored.
        self.context = {}
        # Merge in statically defined context on the class, in a way that won't
        # mean that the class atttribute will be mutated.
        extra_context = self.extra_context
        if extra_context is not None:

        return super(DefaultMetaData, self).__init__(**kwargs)

    def get_context_data(self, **kwargs):
        d = dict(title=self.metadata_title,
        # Allow context to overwrite
        c = super(DefaultMetaData, self).get_context_data(**kwargs)
        return c

def json_validation_request(request, form):
    Returns a JSON validation response for a form, if the request is for JSON

    if request.GET.get('format') == 'json':
        return HttpResponse(python_to_json(form.errors),
        return None

# CBV equivalent to json_validation_request
class AjaxyFormMixin(object):
    A FormView subclass that enables the returning of validation results by JSON
    if accessed with ?format=json.
    def post(self, request, *args, **kwargs):
        if request.GET.get('format', None) == 'json':
            form_class = self.get_form_class()
            form = self.get_form(form_class)
            return HttpResponse(python_to_json(form.errors),
            return super(AjaxyFormMixin, self).post(request, *args, **kwargs)

class AjaxyFormView(AjaxyFormMixin, FormView):

# CBV wrapper for feeds.handle_feed_request
class FeedHandler(object):
    Mixin that handles requests for a feed rather than HTML
    feed_class = None

    def get_feed_class(self):
        if self.feed_class is None:
            raise NotImplementedError("Attribute feed_class not defined.")
            return self.feed_class

    def is_feed_request(self):
        return self.request.GET.get('format', None) == 'atom'

    def get(self, request, *args, **kwargs):
        if self.is_feed_request():
            feed_class = self.get_feed_class()
            return feeds.handle_feed_request(self.request, feed_class, self.get_queryset())
            return super(FeedHandler, self).get(request, *args, **kwargs)

def object_list(request, queryset, extra_context=None,
                template_name='', paginate_by=None):
    # list_detail.object_list replacement with all the things we need
    class ObjectList(ListView):
        def post(self, request, *args, **kwargs):
            return self.get(request, *args, **kwargs)

        def get_context_data(self, **kwargs):
            c = super(ObjectList, self).get_context_data(**kwargs)
            return c

    return ObjectList.as_view(template_name=template_name,

_thisyear = None
_thisyear_timestamp = None

def get_thisyear():
    Get the year the website is currently on.  The website year is
    equal to the year of the last camp in the database, or the year
    afterwards if that camp is in the past.
    global _thisyear, _thisyear_timestamp
    if _thisyear is None or _thisyear_timestamp is None \
        or ( - _thisyear_timestamp).seconds > 3600:
        from cciw.cciwmain.models import Camp
        lastcamp = Camp.objects.order_by('-end_date')[0]
        if lastcamp.is_past():
            _thisyear = lastcamp.year + 1
            _thisyear = lastcamp.year
        _thisyear_timestamp =
    return _thisyear

def standard_subs(value):
    """Standard substitutions made on HTML content"""
    return value.replace('{{thisyear}}', str(get_thisyear()))\
                .replace('{{media}}', settings.MEDIA_URL)\
                .replace('{{static}}', settings.STATIC_URL)
standard_subs.is_safe = True # provided our substitutions don't introduce anything that must be escaped

def get_order_option(order_options, request, default_order_by):
    """Get the order_by parameter from the request, if the request
    contains any of the specified ordering parameters in the query string.

    order_options is a dict of query string params and the corresponding lookup argument.

    default_order_by is value to use for if there is no matching
    order query string.

    order_request = request.GET.get('order', None)
        order_by = order_options[order_request]
        order_by = default_order_by
    return order_by

def create_breadcrumb(links):
    return mark_safe(u" :: ".join(links))

def standard_processor(request):
    Processor that does standard processing of request that we need for all
    context = {}
    context['current_member'] = threadlocals.get_current_member()
    format = request.GET.get('format')
    if format is not None:
        # json or atom - we are not rendering typical pages, and don't want the
        # overhead of additional queries. This is especially important for Atom,
        # which can render many templates with separate RequestContext
        # instances.
        return context

    from cciw.sitecontent.models import MenuLink
    thisyear = get_thisyear()
    context['thisyear'] = thisyear
    assert type(request.path) is unicode
    context['homepage'] = (request.path == u"/")

    # Ugly special casing for 'thisyear' camps
    m = re.match(u'/camps/%s/(\d+)/' % unicode(thisyear),  request.path)
    if m is not None:
        request_path = u'/thisyear/%s/' % m.groups()[0]
        request_path = request.path

    # As a callable, get_links will get called automatically by the template
    # renderer *when needed*, so we avoid queries. We memoize in links_cache to
    # avoid double queries
    links_cache = []
    def get_links():
        if len(links_cache) > 0:
            return links_cache
            for l in MenuLink.objects.filter(parent_item__isnull=True, visible=True):
                l.title = standard_subs(l.title)
                l.isCurrentPage = False
                l.isCurrentSection = False
                if l.url == request_path:
                    l.isCurrentPage = True
                elif request_path.startswith(l.url) and l.url != u'/':
                    l.isCurrentSection = True
            return links_cache

    context['menulinks'] = get_links
    context['GOOGLE_ANALYTICS_ACCOUNT'] = getattr(settings, 'GOOGLE_ANALYTICS_ACCOUNT', '')
    context['PRODUCTION'] = (settings.LIVEBOX and settings.PRODUCTION)

    return context

member_username_re = re.compile(r'^[A-Za-z0-9_]{3,15}$')

def get_member_href(user_name):
    if not member_username_re.match(user_name):
        # This can get called from feeds, and we need to ensure
        # we don't generate a URL, as it will go nowhere (also causes problems
        # with the feed framework and utf-8).
        # Also, this can be called via bbcode, so we need to ensure
        # that we don't pass anything to urlresolvers.reverse that
        # will make it die.
        return u''
        return reverse('cciwmain.members.detail', kwargs={'user_name':user_name})

def get_member_link(user_name):
    user_name = user_name.strip()
    if user_name.startswith(u"'"):
        return user_name
        return mark_safe(u'<a title="Information about user \'%s\'" href="%s">%s</a>' % \
               (user_name, get_member_href(user_name), user_name))

def get_current_domain():
    return Site.objects.get_current().domain

def exception_notify_admins(subject):
    Send admins notification of an exception that occurred
    exc_info = sys.exc_info()
    message = '\n'.join(traceback.format_exception(*exc_info))
    mail_admins(subject, message, fail_silently=True)

from cciw.cciwmain import feeds