Files @ ff16f71df6e8
Branch filter:

Location: symposion_app/pinaxcon/monkey_patch.py

Christopher Neugebauer
September website changes - Pre-registration (#66)

* Factors rendering of external links into its own template. Automagical!

* Adds wagtail templates for keynotes on the front page

* Migrates to Wagtail 1.6

* Migrates content pages to be a streamfield. Flexibility++.

* Fixes editing of ContentPage bodies

* Adds floating images to content pages

* Fixes the layout of floating images

* Adds anchor links to content pages.

* Adds presentation link to the keynote speaker block model

* LCA-ifies the schedule list

* Refactors cms_pages/content_page into a new base template

* cms_pages/content_page now derives from abstract_content_page

* news_index_page now derives from abstract_content_page

* news_page now derives from abstract_content_page.html

* utility_page now uses the content_page base template

* Factors out _right_floating_image.html

* Themes the presentation detail page

* Themes the speaker profile page.

* Themes the schedule list page.

* Minor work on schedule_conference.html

* Themes schedule_detail.html

* Replaces cradle.svg

* Adds a background image to the schedule pages

* Adds libravatar fallback for speaker profile images

* Adds new background images (must update colophon)

* Adds some magic so that we can have slightly different presentation backgrounds for different pages.

* Adds the sponsor block to the bottom of the page.

* Adds sponsor logos to footer.

* All migrations are now in this tree

* Fixes wagtail migrations

* Adjusts presentation_detail to allow for miniconfs (i.e. no target audience)

* Adds unpublishing to presentation detail

* Adds ScheduleHeaderParagraph, which allows us to add some text to the header of schedule pages.

* Adds NamedHeaderParagraph. It allows us to store header paragraphs in the CMS.

* Date formatting in the schedule

* First step for fixing images

* Allows us to automagically load the colophon

* Adds “publish changes” button.

* Can login with email address
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from functools import wraps


class MonkeyPatchMiddleware(object):
    ''' Ensures that our monkey patching only gets called after it is safe to do so.'''

    def process_request(self, request):
        do_monkey_patch()


def do_monkey_patch():
    patch_speaker_profile_form()
    patch_mail_to_send_bcc()
    fix_sitetree_check_access_500s()
    never_cache_login_page()

    # Remove this function from existence
    global do_monkey_patch
    do_monkey_patch = lambda: None


def patch_speaker_profile_form():
    ''' Replaces textarea widgets with markdown editors. '''

    import widgets
    from symposion.speakers.forms import SpeakerForm

    fields = SpeakerForm.base_fields
    fields["biography"].widget = widgets.AceMarkdownEditor()
    fields["experience"].widget = widgets.AceMarkdownEditor()
    fields["accessibility"].widget = widgets.AceMarkdownEditor()


def patch_mail_to_send_bcc():
    ''' Patches django.core.mail's message classes to send a BCC e-mail to
    the default BCC e-mail address. '''

    from django.core.mail import message

    ARG = "bcc"

    if hasattr(settings, "ENVELOPE_BCC_LIST"):
        bcc_email = settings.ENVELOPE_BCC_LIST
    else:
        return  # We don't need to do this patch.

    def bcc_arg_position(f):
        ''' Returns the position of 'bcc' in the argument list to f, or None if
        there is no such argument. '''
        co = f.__code__
        # The first co_argcount members of co_varnames are argument variables
        for i, argname in enumerate(co.co_varnames[:co.co_argcount]):
            if argname == ARG:
                return i
        else:
            return None

    def bcc_is_provided_positionally(f, a):
        ''' Returns true if 'bcc' is provided as a positional argument to f,
        when it is called with the argument list `a`. '''

        return bcc_arg_position(f) < len(a)

    def patch_bcc_positional(f, a):
        ''' Returns a copy of `a`, but with the bcc argument patched to include
        our BCC list. '''

        pos = bcc_arg_position(f)
        bcc = a[pos]

        if bcc is not None:
            bcc = list(bcc)
        else:
            bcc = []

        bcc += bcc_email

        return tuple(a[:pos] + (bcc,) + a[pos + 1:])


    def patch_bcc_keyword(f, k):
        ''' Adds our BCC list to the BCC list in the keyword arguments, and
        returns the new version of the keyword arguments.

        Arguments:
            f (callable): The function that we're patching. It should have an
                argument called bcc.
            k (dict): A dictionary of kwargs to be provided to EmailMessage.
            It will be modified to add the BCC list specified in
            settings.ENVELOPE_BCC_LIST, if provided.
        '''

        if ARG in k:
            bcc = list(k[ARG])
            del k[ARG]
        else:
            bcc = []
        bcc += list(bcc_email)
        k[ARG] = bcc

        return k

    to_wrap = message.EmailMessage.__init__
    
    @wraps(to_wrap)
    def email_message__init__(*a, **k):

        if bcc_is_provided_positionally(to_wrap, a):
            a = patch_bcc_positional(to_wrap, a)
        else:
            k = patch_bcc_keyword(to_wrap, k)

        return to_wrap(*a, **k)

    message.EmailMessage.__init__ = email_message__init__

    # Do not need to wrap EmailMultiAlternatives because it is a subclass of
    # EmailMessage.


def fix_sitetree_check_access_500s():
    ''' django-sitetree has a bug: https://github.com/idlesign/django-sitetree/pull/167/files
    -- it swallows the cause of all 500 errors. This swallows KeyErrors from
    the failing function. '''

    from sitetree.sitetreeapp import SiteTree

    old_check_access = SiteTree.check_access

    @wraps(SiteTree.check_access)
    def check_access(self, *a, **k):
        try:
            return old_check_access(self, *a, **k)
        except KeyError:
            return False

    SiteTree.check_access = check_access

def never_cache_login_page():
    from django.views.decorators.cache import never_cache
    from account.views import LoginView
    LoginView.get = never_cache(LoginView.get)