expanded range of supported versions of django and added patches for csrf_token
This commit is contained in:
parent
8ae8fcdc85
commit
96a9863db2
|
@ -6,7 +6,17 @@ basic actions on behalf of the forum application
|
|||
"""
|
||||
import os
|
||||
import smtplib
|
||||
import sys
|
||||
import logging
|
||||
from askbot import patches
|
||||
from askbot.deployment.assertions import assert_package_compatibility
|
||||
|
||||
VERSION = (0, 6, 75)
|
||||
|
||||
#necessary for interoperability of django and coffin
|
||||
assert_package_compatibility()
|
||||
patches.patch_django()
|
||||
patches.patch_coffin()#must go after django
|
||||
|
||||
def get_install_directory():
|
||||
"""returns path to directory
|
||||
|
@ -15,8 +25,9 @@ def get_install_directory():
|
|||
"""
|
||||
return os.path.dirname(__file__)
|
||||
|
||||
|
||||
def get_version():
|
||||
"""returns version of the askbot app
|
||||
this version is meaningful for pypi only
|
||||
"""
|
||||
return '0.6.74'
|
||||
return '.'.join([str(subversion) for subversion in VERSION])
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
"""assertions regarding deployment of askbot
|
||||
todo: move here stuff from startup_procedures.py
|
||||
|
||||
the reason - some assertions need to be run in askbot/__init__
|
||||
as opposed to startup_procedures.py - which are executed in the
|
||||
beginning of the models module
|
||||
"""
|
||||
from askbot.deployment import package_utils
|
||||
from askbot.exceptions import DeploymentError
|
||||
|
||||
def assert_package_compatibility():
|
||||
"""raises an exception if any known incompatibilities
|
||||
are found
|
||||
"""
|
||||
(django_major, django_minor, django_micro) = package_utils.get_django_version()
|
||||
if django_major < 1:
|
||||
raise DeploymentError('Django version < 1.0 is not supported by askbot')
|
||||
|
||||
coffin_version = package_utils.get_coffin_version()
|
||||
if coffin_version == (0, 3, 0) and django_major == 1 and django_minor > 1:
|
||||
raise DeploymentError(
|
||||
'Coffin package version 0.3 is not compatible '
|
||||
'with the current version of Django, please upgrade '
|
||||
'coffin to at least 0.3.3'
|
||||
)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"""utilities that determine versions of packages
|
||||
that are part of askbot
|
||||
|
||||
versions of all packages are normalized to three-tuples
|
||||
of integers (missing zeroes added)
|
||||
"""
|
||||
import coffin
|
||||
import django
|
||||
|
||||
def get_coffin_version():
|
||||
"""Returns version of Coffin package
|
||||
as a three integer value tuple
|
||||
"""
|
||||
version = coffin.__version__
|
||||
if len(version) == 2:
|
||||
micro_version = 0
|
||||
elif len(version) == 3:
|
||||
micro_version = version[2]
|
||||
else:
|
||||
raise ValueError('unsupported version of coffin %s' % '.'.join(version))
|
||||
major_version = version[0]
|
||||
minor_version = version[1]
|
||||
return (major_version, minor_version, micro_version)
|
||||
|
||||
def get_django_version():
|
||||
"""returns three-tuple for the version
|
||||
of django"""
|
||||
return django.VERSION[:3]
|
|
@ -45,7 +45,7 @@ from django.utils.html import escape
|
|||
from django.utils.translation import ugettext as _
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.core.mail import send_mail
|
||||
from askbot.skins.loaders import ENV
|
||||
from askbot.skins.loaders import render_into_skin
|
||||
|
||||
from askbot.deps.openid.consumer.consumer import Consumer, \
|
||||
SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
|
||||
|
@ -554,9 +554,7 @@ def show_signin_view(
|
|||
data['major_login_providers'] = major_login_providers.values()
|
||||
data['minor_login_providers'] = minor_login_providers.values()
|
||||
|
||||
template = ENV.get_template('authopenid/signin.html')
|
||||
context = RequestContext(request, data)
|
||||
return HttpResponse(template.render(context))
|
||||
return render_into_skin('authopenid/signin.html', data, request)
|
||||
|
||||
@login_required
|
||||
def delete_login_method(request):
|
||||
|
@ -813,7 +811,6 @@ def register(request, login_provider_name=None, user_identifier=None):
|
|||
provider_logo = providers[login_provider_name]
|
||||
|
||||
logging.debug('printing authopenid/complete.html output')
|
||||
template = ENV.get_template('authopenid/complete.html')
|
||||
data = {
|
||||
'openid_register_form': register_form,
|
||||
'email_feeds_form': email_feeds_form,
|
||||
|
@ -823,8 +820,7 @@ def register(request, login_provider_name=None, user_identifier=None):
|
|||
'login_type':'openid',
|
||||
'gravatar_faq_url':reverse('faq') + '#gravatar',
|
||||
}
|
||||
context = RequestContext(request, data)
|
||||
return HttpResponse(template.render(context))
|
||||
return render_into_skin('authopenid/complete.html', data, request)
|
||||
|
||||
def signin_failure(request, message):
|
||||
"""
|
||||
|
@ -895,7 +891,7 @@ def signup_with_password(request):
|
|||
|
||||
# send email
|
||||
#subject = _("Welcome email subject line")
|
||||
#message_template = ENV.get_template(
|
||||
#message_template = get_emplate(
|
||||
# 'authopenid/confirm_email.txt'
|
||||
#)
|
||||
#message_context = Context({
|
||||
|
@ -933,9 +929,11 @@ def signup_with_password(request):
|
|||
'minor_login_providers': minor_login_providers.values(),
|
||||
'login_form': login_form
|
||||
}
|
||||
template = ENV.get_template('authopenid/signup_with_password.html')
|
||||
context = RequestContext(request, context_data)
|
||||
return HttpResponse(template.render(context))
|
||||
return render_into_skin(
|
||||
'authopenid/signup_with_password.html',
|
||||
context_data,
|
||||
request
|
||||
)
|
||||
#what if request is not posted?
|
||||
|
||||
@login_required
|
||||
|
@ -1003,15 +1001,14 @@ def _send_email_key(user):
|
|||
to user's email address
|
||||
"""
|
||||
subject = _("Recover your %(site)s account") % {'site': askbot_settings.APP_SHORT_NAME}
|
||||
message_template = ENV.get_template('authopenid/email_validation.txt')
|
||||
import settings
|
||||
message_context = Context({
|
||||
'validation_link': askbot_settings.APP_URL + reverse(
|
||||
'user_account_recover',
|
||||
kwargs={'key':user.email_key}
|
||||
)
|
||||
})
|
||||
message = message_template.render(message_context)
|
||||
data = {
|
||||
'validation_link': askbot_settings.APP_URL + \
|
||||
reverse(
|
||||
'user_account_recover',
|
||||
kwargs={'key':user.email_key}
|
||||
)
|
||||
}
|
||||
message = render_into_skin('authopenid/email_validation.txt', data)
|
||||
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
|
||||
|
||||
def send_new_email_key(user,nomessage=False):
|
||||
|
@ -1037,14 +1034,16 @@ def send_email_key(request):
|
|||
"""
|
||||
if askbot_settings.EMAIL_VALIDATION == True:
|
||||
if request.user.email_isvalid:
|
||||
template = ENV.get_template('authopenid/changeemail.html')
|
||||
data = {
|
||||
'email': request.user.email,
|
||||
'action_type': 'key_not_sent',
|
||||
'change_link': reverse('user_changeemail')
|
||||
}
|
||||
context = RequestContext(request, data)
|
||||
return HttpResponse(template.render(context))
|
||||
return render_into_skin(
|
||||
'authopenid/changeemail.html',
|
||||
data,
|
||||
request
|
||||
)
|
||||
else:
|
||||
send_new_email_key(request.user)
|
||||
return validation_email_sent(request)
|
||||
|
@ -1107,14 +1106,12 @@ def validation_email_sent(request):
|
|||
set to True bolean value, basically dead now"""
|
||||
assert(askbot_settings.EMAIL_VALIDATION == True)
|
||||
logging.debug('')
|
||||
template = ENV.get_template('authopenid/changeemail.html')
|
||||
data = {
|
||||
'email': request.user.email,
|
||||
'change_email_url': reverse('user_changeemail'),
|
||||
'action_type': 'validate'
|
||||
}
|
||||
context = RequestContext(request, data)
|
||||
return HttpResponse(template.render(context))
|
||||
return render_into_skin('authopenid/changeemail.html', data, request)
|
||||
|
||||
def verifyemail(request,id=None,key=None):
|
||||
"""
|
||||
|
@ -1129,10 +1126,12 @@ def verifyemail(request,id=None,key=None):
|
|||
user.email_isvalid = True
|
||||
clear_email_validation_message(user)
|
||||
user.save()
|
||||
template = ENV.get_template('authopenid/changeemail.html')
|
||||
data = {'action_type': 'validation_complete'}
|
||||
context = RequestContext(request, data)
|
||||
return HttpResponse(template.render(context))
|
||||
return render_into_skin(
|
||||
'authopenid/changeemail.html',
|
||||
data,
|
||||
request
|
||||
)
|
||||
else:
|
||||
logging.error('hmm, no user found for email validation message - foul play?')
|
||||
raise Http404
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
from django.core import exceptions
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
class DeploymentError(exceptions.ImproperlyConfigured):
|
||||
"""raised when there is some error with deployment"""
|
||||
pass
|
||||
|
||||
class LoginRequired(exceptions.PermissionDenied):
|
||||
"""raised when an operation required a logged
|
||||
in user"""
|
||||
|
|
|
@ -52,6 +52,6 @@ class QuestionsPageSizeMiddleware(object):
|
|||
#500.html needs RequestContext, while handler500 only receives Context
|
||||
#need to log some more details about the request
|
||||
logging.critical(utils.http.get_request_info(request))
|
||||
from askbot.skins.loaders import ENV
|
||||
template = ENV.get_template('500.jinja.html')
|
||||
from askbot.skins.loaders import get_template
|
||||
template = get_template('500.jinja.html', request)
|
||||
return HttpResponse(template.render(RequestContext(request)))
|
||||
|
|
|
@ -2010,8 +2010,8 @@ def send_instant_notifications_about_activity_in_post(
|
|||
if update_activity.activity_type not in acceptable_types:
|
||||
return
|
||||
|
||||
from askbot.skins.loaders import ENV
|
||||
template = ENV.get_template('instant_notification.html')
|
||||
from askbot.skins.loaders import get_template
|
||||
template = get_template('instant_notification.html')
|
||||
|
||||
update_type_map = const.RESPONSE_ACTIVITY_TYPE_MAP_FOR_TEMPLATES
|
||||
update_type = update_type_map[update_activity.activity_type]
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
"""module for monkey patching that is
|
||||
necessary for interoperability of different
|
||||
versions of various components used in askbot
|
||||
"""
|
||||
import django
|
||||
from askbot.patches import django_patches
|
||||
from askbot.deployment import package_utils
|
||||
|
||||
def patch_django():
|
||||
"""earlier versions of Django do not have
|
||||
csrf token and function called import_library
|
||||
(the latter is needed by coffin)
|
||||
"""
|
||||
(major, minor, micro) = package_utils.get_django_version()
|
||||
if major == 1 and minor < 2:
|
||||
django_patches.add_import_library_function()
|
||||
django_patches.add_csrf_protection()
|
||||
|
||||
def patch_coffin():
|
||||
"""coffin before version 0.3.4
|
||||
does not have csrf_token template tag.
|
||||
This patch must be applied after the django patches
|
||||
"""
|
||||
from askbot.patches import coffin_patches
|
||||
|
||||
(major, minor, micro) = package_utils.get_coffin_version()
|
||||
if major == 0 and minor == 3 and micro < 4:
|
||||
coffin_patches.add_csrf_token_tag()
|
|
@ -0,0 +1,34 @@
|
|||
"""patches for the coffin module"""
|
||||
from jinja2 import nodes
|
||||
from jinja2 import Markup
|
||||
from jinja2.ext import Extension
|
||||
|
||||
class CsrfTokenExtension(Extension):
|
||||
"""Jinja2-version of the ``csrf_token`` tag.
|
||||
|
||||
Adapted from a snippet by Jason Green:
|
||||
http://www.djangosnippets.org/snippets/1847/
|
||||
|
||||
This tag is a bit stricter than the Django tag in that it doesn't
|
||||
simply ignore any invalid arguments passed in.
|
||||
"""
|
||||
|
||||
tags = set(['csrf_token'])
|
||||
|
||||
def parse(self, parser):
|
||||
lineno = parser.stream.next().lineno
|
||||
return nodes.Output([
|
||||
self.call_method('_render', [nodes.Name('csrf_token', 'load')])
|
||||
]).set_lineno(lineno)
|
||||
|
||||
def _render(self, csrf_token):
|
||||
from django.template.defaulttags import CsrfTokenNode
|
||||
return Markup(CsrfTokenNode().render({'csrf_token': csrf_token}))
|
||||
|
||||
def add_csrf_token_tag():
|
||||
"""adds csrf token tag to the default library"""
|
||||
import coffin.template.defaulttags
|
||||
coffin.template.defaulttags.CsrfTokenExtension = CsrfTokenExtension
|
||||
csrf_token = CsrfTokenExtension
|
||||
coffin.template.defaulttags.csrf_token = csrf_token
|
||||
coffin.template.defaulttags.register.tag(csrf_token)
|
|
@ -0,0 +1,327 @@
|
|||
"""a module for patching django"""
|
||||
import imp
|
||||
import os
|
||||
import sys
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.functional import lazy
|
||||
from django.template import Node
|
||||
|
||||
def module_has_submodule(package, module_name):
|
||||
"""See if 'module' is in 'package'."""
|
||||
name = ".".join([package.__name__, module_name])
|
||||
if name in sys.modules:
|
||||
return True
|
||||
for finder in sys.meta_path:
|
||||
if finder.find_module(name):
|
||||
return True
|
||||
for entry in package.__path__: # No __path__, then not a package.
|
||||
try:
|
||||
# Try the cached finder.
|
||||
finder = sys.path_importer_cache[entry]
|
||||
if finder is None:
|
||||
# Implicit import machinery should be used.
|
||||
try:
|
||||
file_, _, _ = imp.find_module(module_name, [entry])
|
||||
if file_:
|
||||
file_.close()
|
||||
return True
|
||||
except ImportError:
|
||||
continue
|
||||
# Else see if the finder knows of a loader.
|
||||
elif finder.find_module(name):
|
||||
return True
|
||||
else:
|
||||
continue
|
||||
except KeyError:
|
||||
# No cached finder, so try and make one.
|
||||
for hook in sys.path_hooks:
|
||||
try:
|
||||
finder = hook(entry)
|
||||
# XXX Could cache in sys.path_importer_cache
|
||||
if finder.find_module(name):
|
||||
return True
|
||||
else:
|
||||
# Once a finder is found, stop the search.
|
||||
break
|
||||
except ImportError:
|
||||
# Continue the search for a finder.
|
||||
continue
|
||||
else:
|
||||
# No finder found.
|
||||
# Try the implicit import machinery if searching a directory.
|
||||
if os.path.isdir(entry):
|
||||
try:
|
||||
file_, _, _ = imp.find_module(module_name, [entry])
|
||||
if file_:
|
||||
file_.close()
|
||||
return True
|
||||
except ImportError:
|
||||
pass
|
||||
# XXX Could insert None or NullImporter
|
||||
else:
|
||||
# Exhausted the search, so the module cannot be found.
|
||||
return False
|
||||
|
||||
class CsrfTokenNode(Node):
|
||||
def render(self, context):
|
||||
csrf_token = context.get('csrf_token', None)
|
||||
if csrf_token:
|
||||
if csrf_token == 'NOTPROVIDED':
|
||||
return mark_safe(u"")
|
||||
else:
|
||||
return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % csrf_token)
|
||||
else:
|
||||
# It's very probable that the token is missing because of
|
||||
# misconfiguration, so we raise a warning
|
||||
from django.conf import settings
|
||||
if settings.DEBUG:
|
||||
import warnings
|
||||
warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.")
|
||||
return u''
|
||||
|
||||
def get_token(request):
|
||||
"""
|
||||
Returns the the CSRF token required for a POST form.
|
||||
A side effect of calling this function is to make the the csrf_protect
|
||||
decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
|
||||
header to the outgoing response. For this reason, you may need to use this
|
||||
function lazily, as is done by the csrf context processor.
|
||||
"""
|
||||
request.META["CSRF_COOKIE_USED"] = True
|
||||
return request.META.get("CSRF_COOKIE", None)
|
||||
|
||||
def csrf(request):
|
||||
"""
|
||||
Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if
|
||||
it has not been provided by either a view decorator or the middleware
|
||||
"""
|
||||
def _get_val():
|
||||
token = get_token(request)
|
||||
if token is None:
|
||||
# In order to be able to provide debugging info in the
|
||||
# case of misconfiguration, we use a sentinel value
|
||||
# instead of returning an empty dict.
|
||||
return 'NOTPROVIDED'
|
||||
else:
|
||||
return token
|
||||
_get_val = lazy(_get_val, str)
|
||||
return {'csrf_token': _get_val() }
|
||||
|
||||
"""
|
||||
Cross Site Request Forgery Middleware.
|
||||
This module provides a middleware that implements protection
|
||||
against request forgeries from other sites.
|
||||
"""
|
||||
import itertools
|
||||
import re
|
||||
import random
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import get_callable
|
||||
from django.utils.hashcompat import md5_constructor
|
||||
from django.utils.safestring import mark_safe
|
||||
_POST_FORM_RE = \
|
||||
re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
|
||||
_HTML_TYPES = ('text/html', 'application/xhtml+xml')
|
||||
# Use the system (hardware-based) random number generator if it exists.
|
||||
if hasattr(random, 'SystemRandom'):
|
||||
randrange = random.SystemRandom().randrange
|
||||
else:
|
||||
randrange = random.randrange
|
||||
_MAX_CSRF_KEY = 18446744073709551616L # 2 << 63
|
||||
def _get_failure_view():
|
||||
"""
|
||||
Returns the view to be used for CSRF rejections
|
||||
"""
|
||||
return get_callable(settings.CSRF_FAILURE_VIEW)
|
||||
|
||||
def _get_new_csrf_key():
|
||||
return md5_constructor("%s%s"
|
||||
% (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
|
||||
|
||||
def _make_legacy_session_token(session_id):
|
||||
return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
|
||||
|
||||
class CsrfViewMiddleware(object):
|
||||
"""
|
||||
Middleware that requires a present and correct csrfmiddlewaretoken
|
||||
for POST requests that have a CSRF cookie, and sets an outgoing
|
||||
CSRF cookie.
|
||||
This middleware should be used in conjunction with the csrf_token template
|
||||
tag.
|
||||
"""
|
||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||
if getattr(callback, 'csrf_exempt', False):
|
||||
return None
|
||||
if getattr(request, 'csrf_processing_done', False):
|
||||
return None
|
||||
reject = lambda s: _get_failure_view()(request, reason=s)
|
||||
def accept():
|
||||
# Avoid checking the request twice by adding a custom attribute to
|
||||
# request. This will be relevant when both decorator and middleware
|
||||
# are used.
|
||||
request.csrf_processing_done = True
|
||||
return None
|
||||
# If the user doesn't have a CSRF cookie, generate one and store it in the
|
||||
# request, so it's available to the view. We'll store it in a cookie when
|
||||
# we reach the response.
|
||||
try:
|
||||
request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME]
|
||||
cookie_is_new = False
|
||||
except KeyError:
|
||||
# No cookie, so create one. This will be sent with the next
|
||||
# response.
|
||||
request.META["CSRF_COOKIE"] = _get_new_csrf_key()
|
||||
# Set a flag to allow us to fall back and allow the session id in
|
||||
# place of a CSRF cookie for this request only.
|
||||
cookie_is_new = True
|
||||
if request.method == 'POST':
|
||||
if getattr(request, '_dont_enforce_csrf_checks', False):
|
||||
# Mechanism to turn off CSRF checks for test suite. It comes after
|
||||
# the creation of CSRF cookies, so that everything else continues to
|
||||
# work exactly the same (e.g. cookies are sent etc), but before the
|
||||
# any branches that call reject()
|
||||
return accept()
|
||||
if request.is_ajax():
|
||||
# .is_ajax() is based on the presence of X-Requested-With. In
|
||||
# the context of a browser, this can only be sent if using
|
||||
# XmlHttpRequest. Browsers implement careful policies for
|
||||
# XmlHttpRequest:
|
||||
#
|
||||
# * Normally, only same-domain requests are allowed.
|
||||
#
|
||||
# * Some browsers (e.g. Firefox 3.5 and later) relax this
|
||||
# carefully:
|
||||
#
|
||||
# * if it is a 'simple' GET or POST request (which can
|
||||
# include no custom headers), it is allowed to be cross
|
||||
# domain. These requests will not be recognized as AJAX.
|
||||
#
|
||||
# * if a 'preflight' check with the server confirms that the
|
||||
# server is expecting and allows the request, cross domain
|
||||
# requests even with custom headers are allowed. These
|
||||
# requests will be recognized as AJAX, but can only get
|
||||
# through when the developer has specifically opted in to
|
||||
# allowing the cross-domain POST request.
|
||||
#
|
||||
# So in all cases, it is safe to allow these requests through.
|
||||
return accept()
|
||||
if request.is_secure():
|
||||
# Strict referer checking for HTTPS
|
||||
referer = request.META.get('HTTP_REFERER')
|
||||
if referer is None:
|
||||
return reject("Referer checking failed - no Referer.")
|
||||
# The following check ensures that the referer is HTTPS,
|
||||
# the domains match and the ports match. This might be too strict.
|
||||
good_referer = 'https://%s/' % request.get_host()
|
||||
if not referer.startswith(good_referer):
|
||||
return reject("Referer checking failed - %s does not match %s." %
|
||||
(referer, good_referer))
|
||||
# If the user didn't already have a CSRF cookie, then fall back to
|
||||
# the Django 1.1 method (hash of session ID), so a request is not
|
||||
# rejected if the form was sent to the user before upgrading to the
|
||||
# Django 1.2 method (session independent nonce)
|
||||
if cookie_is_new:
|
||||
try:
|
||||
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
|
||||
csrf_token = _make_legacy_session_token(session_id)
|
||||
except KeyError:
|
||||
# No CSRF cookie and no session cookie. For POST requests,
|
||||
# we insist on a CSRF cookie, and in this way we can avoid
|
||||
# all CSRF attacks, including login CSRF.
|
||||
return reject("No CSRF or session cookie.")
|
||||
else:
|
||||
csrf_token = request.META["CSRF_COOKIE"]
|
||||
# check incoming token
|
||||
request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
|
||||
if request_csrf_token != csrf_token:
|
||||
if cookie_is_new:
|
||||
# probably a problem setting the CSRF cookie
|
||||
return reject("CSRF cookie not set.")
|
||||
else:
|
||||
return reject("CSRF token missing or incorrect.")
|
||||
return accept()
|
||||
def process_response(self, request, response):
|
||||
if getattr(response, 'csrf_processing_done', False):
|
||||
return response
|
||||
# If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
|
||||
# never called, probaby because a request middleware returned a response
|
||||
# (for example, contrib.auth redirecting to a login page).
|
||||
if request.META.get("CSRF_COOKIE") is None:
|
||||
return response
|
||||
if not request.META.get("CSRF_COOKIE_USED", False):
|
||||
return response
|
||||
# Set the CSRF cookie even if it's already set, so we renew the expiry timer.
|
||||
response.set_cookie(settings.CSRF_COOKIE_NAME,
|
||||
request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
|
||||
domain=settings.CSRF_COOKIE_DOMAIN)
|
||||
# Content varies with the CSRF cookie, so set the Vary header.
|
||||
from django.utils.cache import patch_vary_headers
|
||||
patch_vary_headers(response, ('Cookie',))
|
||||
response.csrf_processing_done = True
|
||||
return response
|
||||
|
||||
from django.utils.decorators import decorator_from_middleware
|
||||
from functools import wraps
|
||||
|
||||
csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
|
||||
csrf_protect.__name__ = "csrf_protect"
|
||||
csrf_protect.__doc__ = """
|
||||
This decorator adds CSRF protection in exactly the same way as
|
||||
CsrfViewMiddleware, but it can be used on a per view basis. Using both, or
|
||||
using the decorator multiple times, is harmless and efficient.
|
||||
"""
|
||||
|
||||
def add_import_library_function():
|
||||
|
||||
#this definition is copy/pasted from django 1.2 source code
|
||||
#it is necessary to make Coffin library happy
|
||||
from django.utils.importlib import import_module
|
||||
class InvalidTemplateLibrary(Exception):
|
||||
pass
|
||||
|
||||
def import_library(taglib_module):
|
||||
"""Load a template tag library module.
|
||||
Verifies that the library contains a 'register' attribute, and
|
||||
returns that attribute as the representation of the library
|
||||
"""
|
||||
app_path, taglib = taglib_module.rsplit('.',1)
|
||||
app_module = import_module(app_path)
|
||||
try:
|
||||
mod = import_module(taglib_module)
|
||||
except ImportError, e:
|
||||
# If the ImportError is because the taglib submodule does not exist, that's not
|
||||
# an error that should be raised. If the submodule exists and raised an ImportError
|
||||
# on the attempt to load it, that we want to raise.
|
||||
if not module_has_submodule(app_module, taglib):
|
||||
return None
|
||||
else:
|
||||
raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % (taglib_module, e))
|
||||
try:
|
||||
return mod.register
|
||||
except AttributeError:
|
||||
raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module)
|
||||
|
||||
import django.template
|
||||
django.template.import_library = import_library
|
||||
|
||||
def add_csrf_protection():
|
||||
"""adds csrf_token template tag to django
|
||||
Must be used if version of django is < 1.2
|
||||
|
||||
Also adds csrf function to the context processor
|
||||
and the csrf_protect decorator for the views
|
||||
"""
|
||||
import django.template.defaulttags
|
||||
def csrf_token(parser, token):
|
||||
return CsrfTokenNode()
|
||||
django.template.defaulttags.CsrfTokenNode = CsrfTokenNode
|
||||
django.template.defaulttags.register.tag(csrf_token)
|
||||
|
||||
#add csrf context processor
|
||||
import django.core.context_processors
|
||||
django.core.context_processors.csrf = csrf
|
||||
|
||||
#add csrf_protect decorator
|
||||
import django.views.decorators
|
||||
django.views.decorators.csrf = imp.new_module('csrf')
|
||||
django.views.decorators.csrf.csrf_protect = csrf_protect
|
|
@ -1,5 +1,5 @@
|
|||
{% load extra_tags %}
|
||||
{% include_jinja "404.jinja.html" %}
|
||||
{% include_jinja "404.jinja.html" request %}
|
||||
{% comment %}
|
||||
this one has to be a django template because of use of default hander404
|
||||
{% endcomment %}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% load extra_tags %}
|
||||
{% include_jinja "500.jinja.html" %}
|
||||
{% include_jinja "500.jinja.html" request %}
|
||||
{% comment %}this template must be django
|
||||
because of the use of default handler500
|
||||
{% endcomment %}
|
||||
|
|
|
@ -3,14 +3,17 @@ from django.template.loaders import filesystem
|
|||
from django.template import RequestContext
|
||||
from django.http import HttpResponse
|
||||
from django.utils import translation
|
||||
from askbot.conf import settings as askbot_settings
|
||||
from django.conf import settings as django_settings
|
||||
from coffin.common import CoffinEnvironment
|
||||
from jinja2 import loaders as jinja_loaders
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from jinja2.utils import open_if_exists
|
||||
from askbot.conf import settings as askbot_settings
|
||||
from askbot.skins import utils
|
||||
|
||||
from coffin import template
|
||||
template.add_to_builtins('askbot.templatetags.extra_filters_jinja')
|
||||
|
||||
#module for skinning askbot
|
||||
#via ASKBOT_DEFAULT_SKIN configureation variable (not django setting)
|
||||
|
||||
|
@ -103,29 +106,26 @@ class SkinEnvironment(CoffinEnvironment):
|
|||
return '<link href="%s" rel="stylesheet" type="text/css" />' % url
|
||||
return ''
|
||||
|
||||
ENV = SkinEnvironment(
|
||||
autoescape=False,
|
||||
extensions=['jinja2.ext.i18n'],
|
||||
skin = askbot_settings.ASKBOT_DEFAULT_SKIN
|
||||
#loader = SkinLoader()
|
||||
)
|
||||
ENV.set_language(django_settings.LANGUAGE_CODE)
|
||||
|
||||
def load_skins():
|
||||
skins = dict()
|
||||
for skin_name in utils.get_available_skins():
|
||||
skins[skin_name] = SkinEnvironment(skin = skin_name)
|
||||
skins[skin_name] = SkinEnvironment(
|
||||
skin = skin_name,
|
||||
extensions=['jinja2.ext.i18n',]
|
||||
)
|
||||
skins[skin_name].set_language(django_settings.LANGUAGE_CODE)
|
||||
#from askbot.templatetags import extra_filters_jinja as filters
|
||||
#skins[skin_name].filters['media'] = filters.media
|
||||
return skins
|
||||
|
||||
SKINS = load_skins()
|
||||
|
||||
def get_skin(request):
|
||||
def get_skin(request = None):
|
||||
"""retreives the skin environment
|
||||
for a given request (request var is not used at this time)"""
|
||||
return SKINS[askbot_settings.ASKBOT_DEFAULT_SKIN]
|
||||
|
||||
def get_template(template, request):
|
||||
def get_template(template, request = None):
|
||||
"""retreives template for the skin
|
||||
request variable will be used in the future to set
|
||||
template according to the user preference or admins preference
|
||||
|
|
|
@ -3,6 +3,8 @@ in the beginning of models/__init__.py
|
|||
|
||||
the purpose of this module is to validate deployment of askbot
|
||||
|
||||
question: why not run these from askbot/__init__.py?
|
||||
|
||||
the main function is run_startup_tests
|
||||
"""
|
||||
from django.db import transaction
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
|
|||
from django.core.urlresolvers import reverse
|
||||
from askbot.utils import functions
|
||||
from askbot.utils.slug import slugify
|
||||
from askbot.skins.loaders import ENV
|
||||
from askbot.skins.loaders import get_template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -117,10 +117,12 @@ def cnprog_paginator(context):
|
|||
|
||||
class IncludeJinja(template.Node):
|
||||
"""http://www.mellowmorning.com/2010/08/24/"""
|
||||
def __init__(self, filename):
|
||||
def __init__(self, filename, request_var):
|
||||
self.filename = filename
|
||||
self.request_var = template.Variable(request_var)
|
||||
def render(self, context):
|
||||
jinja_template = ENV.get_template(self.filename)
|
||||
request = self.request_var.resolve(context)
|
||||
jinja_template = get_template(self.filename, request)
|
||||
return jinja_template.render(context)
|
||||
|
||||
@register.tag
|
||||
|
@ -128,11 +130,12 @@ def include_jinja(parser, token):
|
|||
bits = token.contents.split()
|
||||
|
||||
#Check if a filename was given
|
||||
if len(bits) != 2:
|
||||
if len(bits) != 3:
|
||||
error_message = '%r tag requires the name of the ' + \
|
||||
'template to be included included'
|
||||
'template and the request variable'
|
||||
raise template.TemplateSyntaxError(error_message % bits[0])
|
||||
filename = bits[1]
|
||||
request_var = bits[2]
|
||||
|
||||
#Remove quotes or raise error
|
||||
if filename[0] in ('"', "'") and filename[-1] == filename[0]:
|
||||
|
@ -140,4 +143,4 @@ def include_jinja(parser, token):
|
|||
else:
|
||||
raise template.TemplateSyntaxError('file name must be quoted')
|
||||
|
||||
return IncludeJinja(filename)
|
||||
return IncludeJinja(filename, request_var)
|
||||
|
|
|
@ -1,23 +1,30 @@
|
|||
from django.test import TestCase, signals
|
||||
from jinja2.environment import Template as Jinja2Template
|
||||
from django.test import TestCase
|
||||
from django.test import signals
|
||||
from django.template import defaultfilters
|
||||
from django.core.urlresolvers import reverse
|
||||
import coffin
|
||||
import coffin.template
|
||||
from askbot import models
|
||||
from askbot.utils.slug import slugify
|
||||
from askbot.deployment import package_utils
|
||||
import sys
|
||||
|
||||
#note - this code can be run only once
|
||||
ORIG_JINJA2_RENDERER = Jinja2Template.render
|
||||
def instrumented_render(template_object, *args, **kwargs):
|
||||
context = dict(*args, **kwargs)
|
||||
signals.template_rendered.send(
|
||||
sender=template_object,
|
||||
template=template_object,
|
||||
context=context
|
||||
)
|
||||
return ORIG_JINJA2_RENDERER(template_object, *args, **kwargs)
|
||||
Jinja2Template.render = instrumented_render
|
||||
def patch_jinja2():
|
||||
from jinja2 import Template
|
||||
ORIG_JINJA2_RENDERER = Template.render
|
||||
def instrumented_render(template_object, *args, **kwargs):
|
||||
context = dict(*args, **kwargs)
|
||||
signals.template_rendered.send(
|
||||
sender=template_object,
|
||||
template=template_object,
|
||||
context=context
|
||||
)
|
||||
return ORIG_JINJA2_RENDERER(template_object, *args, **kwargs)
|
||||
Template.render = instrumented_render
|
||||
|
||||
(CMAJOR, CMINOR, CMICRO) = package_utils.get_coffin_version()
|
||||
if CMAJOR == 0 and CMINOR == 3 and CMICRO < 4:
|
||||
patch_jinja2()
|
||||
|
||||
class PageLoadTestCase(TestCase):
|
||||
def try_url(
|
||||
|
|
|
@ -63,6 +63,7 @@ def questions(request):
|
|||
List of Questions, Tagged questions, and Unanswered questions.
|
||||
matching search query or user selection
|
||||
"""
|
||||
#before = datetime.datetime.now()
|
||||
#don't allow to post to this view
|
||||
if request.method == 'POST':
|
||||
raise Http404
|
||||
|
|
10
setup.py
10
setup.py
|
@ -7,9 +7,9 @@ import sys
|
|||
#you might want to install django-debug-toolbar as well
|
||||
|
||||
install_requires = [
|
||||
'django==1.1.2',
|
||||
'django>=1.1.2',
|
||||
'Jinja2',
|
||||
'Coffin==0.3.0',
|
||||
'Coffin>=0.3',
|
||||
'South>=0.7.1',
|
||||
'oauth2',
|
||||
'recaptcha-client',
|
||||
|
@ -24,7 +24,9 @@ install_requires = [
|
|||
'django-kombu==0.9.2',
|
||||
]
|
||||
|
||||
import askbot
|
||||
#todo: have a dirty version retriever that
|
||||
#parses it out from askbot/__init__.py but does not
|
||||
#import it as there are issues
|
||||
|
||||
WIN_PLATFORMS = ('win32', 'cygwin',)
|
||||
if sys.platform not in WIN_PLATFORMS:
|
||||
|
@ -32,7 +34,7 @@ if sys.platform not in WIN_PLATFORMS:
|
|||
|
||||
setup(
|
||||
name = "askbot",
|
||||
version = askbot.get_version(),
|
||||
version = '0.6.75',#remember to manually set this correctly
|
||||
description = 'Question and Answer forum, like StackOverflow, written in python and Django',
|
||||
packages = find_packages(),
|
||||
author = 'Evgeny.Fadeev',
|
||||
|
|
Reference in New Issue