Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/stable/1.3.x' into develop
Browse files Browse the repository at this point in the history
Conflicts:
	.hgignore
	README
	django/__init__.py
	django/conf/__init__.py
	django/conf/global_settings.py
	django/conf/project_template/settings.py
	django/contrib/admin/filterspecs.py
	django/contrib/admin/options.py
	django/contrib/admin/sites.py
	django/contrib/admin/templates/registration/password_reset_email.html
	django/contrib/auth/forms.py
	django/contrib/auth/management/__init__.py
	django/contrib/auth/models.py
	django/contrib/auth/tests/auth_backends.py
	django/contrib/auth/tests/forms.py
	django/contrib/auth/tests/templates/registration/password_reset_email.html
	django/contrib/auth/tests/urls.py
	django/contrib/auth/tests/views.py
	django/contrib/auth/urls.py
	django/contrib/auth/views.py
	django/contrib/comments/views/comments.py
	django/contrib/comments/views/moderation.py
	django/contrib/comments/views/utils.py
	django/contrib/markup/templatetags/markup.py
	django/contrib/markup/tests.py
	django/contrib/sites/management.py
	django/contrib/sites/tests.py
	django/contrib/staticfiles/management/commands/collectstatic.py
	django/core/cache/backends/db.py
	django/core/files/images.py
	django/core/files/uploadhandler.py
	django/core/management/commands/shell.py
	django/core/serializers/python.py
	django/core/serializers/xml_serializer.py
	django/core/validators.py
	django/db/backends/__init__.py
	django/db/backends/mysql/base.py
	django/db/backends/sqlite3/base.py
	django/db/models/base.py
	django/db/models/deletion.py
	django/db/models/fields/__init__.py
	django/db/models/fields/related.py
	django/db/models/query.py
	django/db/models/sql/compiler.py
	django/db/models/sql/query.py
	django/forms/fields.py
	django/forms/formsets.py
	django/http/__init__.py
	django/http/multipartparser.py
	django/http/utils.py
	django/templatetags/future.py
	django/test/client.py
	django/test/utils.py
	django/utils/datastructures.py
	django/utils/encoding.py
	django/utils/http.py
	django/views/i18n.py
	docs/conf.py
	docs/howto/deployment/modpython.txt
	docs/howto/deployment/modwsgi.txt
	docs/howto/initial-data.txt
	docs/howto/static-files.txt
	docs/index.txt
	docs/internals/contributing.txt
	docs/internals/deprecation.txt
	docs/internals/release-process.txt
	docs/intro/overview.txt
	docs/intro/tutorial01.txt
	docs/intro/tutorial02.txt
	docs/intro/tutorial03.txt
	docs/ref/class-based-views.txt
	docs/ref/contrib/admin/index.txt
	docs/ref/contrib/gis/create_template_postgis-debian.sh
	docs/ref/contrib/gis/db-api.txt
	docs/ref/contrib/gis/install.txt
	docs/ref/contrib/markup.txt
	docs/ref/databases.txt
	docs/ref/django-admin.txt
	docs/ref/models/options.txt
	docs/ref/request-response.txt
	docs/ref/settings.txt
	docs/ref/templates/api.txt
	docs/releases/0.95.txt
	docs/releases/0.96.txt
	docs/releases/1.0.1.txt
	docs/releases/index.txt
	docs/topics/auth.txt
	docs/topics/class-based-views.txt
	docs/topics/db/models.txt
	docs/topics/db/optimization.txt
	docs/topics/forms/formsets.txt
	docs/topics/forms/modelforms.txt
	docs/topics/http/file-uploads.txt
	docs/topics/http/urls.txt
	docs/topics/i18n/index.txt
	docs/topics/i18n/internationalization.txt
	docs/topics/logging.txt
	docs/topics/templates.txt
	docs/topics/testing.txt
	setup.py
	tests/modeltests/validators/tests.py
	tests/regressiontests/admin_filterspecs/models.py
	tests/regressiontests/admin_filterspecs/tests.py
	tests/regressiontests/admin_views/tests.py
	tests/regressiontests/app_loading/tests.py
	tests/regressiontests/comment_tests/tests/comment_view_tests.py
	tests/regressiontests/comment_tests/tests/moderation_view_tests.py
	tests/regressiontests/file_uploads/tests.py
	tests/regressiontests/file_uploads/uploadhandler.py
	tests/regressiontests/file_uploads/urls.py
	tests/regressiontests/file_uploads/views.py
	tests/regressiontests/forms/tests/formsets.py
	tests/regressiontests/formwizard/templates/forms/wizard.html
	tests/regressiontests/generic_views/dates.py
	tests/regressiontests/httpwrappers/tests.py
	tests/regressiontests/requests/tests.py
	tests/regressiontests/serializers_regress/tests.py
	tests/regressiontests/settings_tests/tests.py
	tests/regressiontests/staticfiles_tests/tests.py
	tests/regressiontests/templates/tests.py
	tests/regressiontests/utils/datastructures.py
	tests/regressiontests/utils/http.py
	tests/regressiontests/views/tests/i18n.py
  • Loading branch information
aburgel committed Feb 20, 2013
2 parents c68fd7e + a6927d8 commit 1aa654e
Show file tree
Hide file tree
Showing 104 changed files with 1,452 additions and 365 deletions.
11 changes: 2 additions & 9 deletions .hgignore
Original file line number Original file line Diff line number Diff line change
@@ -1,13 +1,6 @@
syntax: glob syntax:glob
*~
*.tmp
*.swp
*.egg-info *.egg-info
*.pot *.pot
*.py[co] *.py[co]
docs/_build/ docs/_build/
*.orig
desktop.ini
nbproject
tests/django
build
2 changes: 1 addition & 1 deletion README
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ http://code.djangoproject.com/newticket
To get more help: To get more help:


* Join the #django channel on irc.freenode.net. Lots of helpful people * Join the #django channel on irc.freenode.net. Lots of helpful people
hang out there. Read the archives at http://botland.oebfare.com/logger/django/. hang out there. Read the archives at http://django-irc-logs.com/.


* Join the django-users mailing list, or read the archives, at * Join the django-users mailing list, or read the archives, at
http://groups.google.com/group/django-users. http://groups.google.com/group/django-users.
Expand Down
2 changes: 1 addition & 1 deletion django/__init__.py
Original file line number Original file line Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = (1, 3, 1, 'final', 0) VERSION = (1, 3, 6, 'final', 0)


def get_version(): def get_version():
version = '%s.%s' % (VERSION[0], VERSION[1]) version = '%s.%s' % (VERSION[0], VERSION[1])
Expand Down
3 changes: 3 additions & 0 deletions django/conf/__init__.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ def __setattr__(self, name, value):
if name in ("MEDIA_URL", "STATIC_URL") and value and not value.endswith('/'): if name in ("MEDIA_URL", "STATIC_URL") and value and not value.endswith('/'):
warnings.warn('If set, %s must end with a slash' % name, warnings.warn('If set, %s must end with a slash' % name,
PendingDeprecationWarning) PendingDeprecationWarning)
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, basestring):
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
"to a tuple, not a string.")
object.__setattr__(self, name, value) object.__setattr__(self, name, value)




Expand Down
4 changes: 4 additions & 0 deletions django/conf/global_settings.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
# * Receive x-headers # * Receive x-headers
INTERNAL_IPS = () INTERNAL_IPS = ()


# Hosts/domain names that are valid for this site.
# "*" matches anything, ".example.com" matches example.com and all subdomains
ALLOWED_HOSTS = ['*']

# Local time zone for this installation. All choices can be found here: # Local time zone for this installation. All choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
# systems may support all possibilities). # systems may support all possibilities).
Expand Down
4 changes: 4 additions & 0 deletions django/conf/project_template/settings.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
} }
} }


# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.3/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []

# Local time zone for this installation. Choices can be found here: # Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems. # although not all choices may be available on all operating systems.
Expand Down
9 changes: 6 additions & 3 deletions django/contrib/admin/filterspecs.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ def __init__(self, f, request, params, model, model_admin,
self.lookup_title = other_model._meta.verbose_name self.lookup_title = other_model._meta.verbose_name
else: else:
self.lookup_title = f.verbose_name # use field name self.lookup_title = f.verbose_name # use field name
rel_name = other_model._meta.pk.name if hasattr(f, 'rel'):
rel_name = f.rel.get_related_field().name
else:
rel_name = other_model._meta.pk.name
self.lookup_kwarg = '%s__%s__exact' % (self.field_path, rel_name) self.lookup_kwarg = '%s__%s__exact' % (self.field_path, rel_name)
self.lookup_kwarg_isnull = '%s__isnull' % (self.field_path) self.lookup_kwarg_isnull = '%s__isnull' % self.field_path
self.lookup_val = request.GET.get(self.lookup_kwarg, None) self.lookup_val = request.GET.get(self.lookup_kwarg, None)
self.lookup_val_isnull = request.GET.get( self.lookup_val_isnull = request.GET.get(
self.lookup_kwarg_isnull, None) self.lookup_kwarg_isnull, None)
Expand Down Expand Up @@ -176,7 +179,7 @@ def choices(self, cl):


class DateFieldFilterSpec(FilterSpec): class DateFieldFilterSpec(FilterSpec):
def __init__(self, f, request, params, model, model_admin, def __init__(self, f, request, params, model, model_admin,
field_path=None): field_path=None):
super(DateFieldFilterSpec, self).__init__(f, request, params, model, super(DateFieldFilterSpec, self).__init__(f, request, params, model,
model_admin, model_admin,
field_path=field_path) field_path=field_path)
Expand Down
20 changes: 13 additions & 7 deletions django/contrib/admin/options.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -225,18 +225,18 @@ def lookup_allowed(self, lookup, value):
# if foo has been specificially included in the lookup list; so # if foo has been specificially included in the lookup list; so
# drop __id if it is the last part. However, first we need to find # drop __id if it is the last part. However, first we need to find
# the pk attribute name. # the pk attribute name.
pk_attr_name = None rel_name = None
for part in parts[:-1]: for part in parts[:-1]:
field, _, _, _ = model._meta.get_field_by_name(part) field, _, _, _ = model._meta.get_field_by_name(part)
if hasattr(field, 'rel'): if hasattr(field, 'rel'):
model = field.rel.to model = field.rel.to
pk_attr_name = model._meta.pk.name rel_name = field.rel.get_related_field().name
elif isinstance(field, RelatedObject): elif isinstance(field, RelatedObject):
model = field.model model = field.model
pk_attr_name = model._meta.pk.name rel_name = model._meta.pk.name
else: else:
pk_attr_name = None rel_name = None
if pk_attr_name and len(parts) > 1 and parts[-1] == pk_attr_name: if rel_name and len(parts) > 1 and parts[-1] == rel_name:
parts.pop() parts.pop()


try: try:
Expand Down Expand Up @@ -1242,15 +1242,21 @@ def delete_view(self, request, object_id, extra_context=None):
def history_view(self, request, object_id, extra_context=None): def history_view(self, request, object_id, extra_context=None):
"The 'history' admin view for this model." "The 'history' admin view for this model."
from django.contrib.admin.models import LogEntry from django.contrib.admin.models import LogEntry
# First check if the user can see this history.
model = self.model model = self.model
obj = get_object_or_404(model, pk=unquote(object_id))

if not self.has_change_permission(request, obj):
raise PermissionDenied

# Then get the history for this object.
opts = model._meta opts = model._meta
app_label = opts.app_label app_label = opts.app_label
action_list = LogEntry.objects.filter( action_list = LogEntry.objects.filter(
object_id = object_id, object_id = object_id,
content_type__id__exact = ContentType.objects.get_for_model(model).id content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time') ).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=unquote(object_id))
context = { context = {
'title': _('Change history: %s') % force_unicode(obj), 'title': _('Change history: %s') % force_unicode(obj),
'action_list': action_list, 'action_list': action_list,
Expand Down
1 change: 1 addition & 0 deletions django/contrib/auth/tests/urls.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def remote_user_auth_view(request):
(r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')), (r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')),
(r'^remote_user/$', remote_user_auth_view), (r'^remote_user/$', remote_user_auth_view),
(r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')), (r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')),
(r'^admin_password_reset/$', 'django.contrib.auth.views.password_reset', dict(is_admin_site=True)),
(r'^login_required/$', login_required(password_reset)), (r'^login_required/$', login_required(password_reset)),
(r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')), (r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')),
) )
Expand Down
39 changes: 39 additions & 0 deletions django/contrib/auth/tests/views.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from django.core import mail from django.core import mail
from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import QueryDict from django.http import QueryDict


Expand Down Expand Up @@ -69,6 +70,44 @@ def test_email_found_custom_from(self):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual("staffmember@example.com", mail.outbox[0].from_email) self.assertEqual("staffmember@example.com", mail.outbox[0].from_email)


def test_admin_reset(self):
"If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override."
response = self.client.post('/admin_password_reset/',
{'email': 'staffmember@example.com'},
HTTP_HOST='adminsite.com'
)
self.assertEqual(response.status_code, 302)
self.assertEqual(len(mail.outbox), 1)
self.assertTrue("http://adminsite.com" in mail.outbox[0].body)
self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email)

def test_poisoned_http_host(self):
"Poisoned HTTP_HOST headers can't be used for reset emails"
# This attack is based on the way browsers handle URLs. The colon
# should be used to separate the port, but if the URL contains an @,
# the colon is interpreted as part of a username for login purposes,
# making 'evil.com' the request domain. Since HTTP_HOST is used to
# produce a meaningful reset URL, we need to be certain that the
# HTTP_HOST header isn't poisoned. This is done as a check when get_host()
# is invoked, but we check here as a practical consequence.
def test_host_poisoning():
self.client.post('/password_reset/',
{'email': 'staffmember@example.com'},
HTTP_HOST='www.example:dr.frankenstein@evil.tld'
)
self.assertRaises(SuspiciousOperation, test_host_poisoning)
self.assertEqual(len(mail.outbox), 0)

def test_poisoned_http_host_admin_site(self):
"Poisoned HTTP_HOST headers can't be used for reset emails on admin views"
def test_host_poisoning():
self.client.post('/admin_password_reset/',
{'email': 'staffmember@example.com'},
HTTP_HOST='www.example:dr.frankenstein@evil.tld'
)
self.assertRaises(SuspiciousOperation, test_host_poisoning)
self.assertEqual(len(mail.outbox), 0)

def _test_confirm_start(self): def _test_confirm_start(self):
# Start by creating the email # Start by creating the email
response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
Expand Down
52 changes: 23 additions & 29 deletions django/contrib/auth/views.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.http import HttpResponseRedirect, QueryDict from django.http import HttpResponseRedirect, QueryDict
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.utils.http import urlsafe_base64_decode from django.utils.http import urlsafe_base64_decode, is_safe_url
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
Expand Down Expand Up @@ -33,18 +33,11 @@ def login(request, template_name='registration/login.html',
if request.method == "POST": if request.method == "POST":
form = authentication_form(data=request.POST) form = authentication_form(data=request.POST)
if form.is_valid(): if form.is_valid():
netloc = urlparse.urlparse(redirect_to)[1] # Ensure the user-originating redirection url is safe.

if not is_safe_url(url=redirect_to, host=request.get_host()):
# Use default setting if redirect_to is empty
if not redirect_to:
redirect_to = settings.LOGIN_REDIRECT_URL

# Security check -- don't allow redirection to a different
# host.
elif netloc and netloc != request.get_host():
redirect_to = settings.LOGIN_REDIRECT_URL redirect_to = settings.LOGIN_REDIRECT_URL


# Okay, security checks complete. Log the user in. # Okay, security check complete. Log the user in.
auth_login(request, form.get_user()) auth_login(request, form.get_user())


if request.session.test_cookie_worked(): if request.session.test_cookie_worked():
Expand Down Expand Up @@ -76,26 +69,27 @@ def logout(request, next_page=None,
Logs out the user and displays 'You are logged out' message. Logs out the user and displays 'You are logged out' message.
""" """
auth_logout(request) auth_logout(request)
redirect_to = request.REQUEST.get(redirect_field_name, '')
if redirect_to: if redirect_field_name in request.REQUEST:
netloc = urlparse.urlparse(redirect_to)[1] next_page = request.REQUEST[redirect_field_name]
# Security check -- don't allow redirection to a different host. # Security check -- don't allow redirection to a different host.
if not (netloc and netloc != request.get_host()): if not is_safe_url(url=next_page, host=request.get_host()):
return HttpResponseRedirect(redirect_to) next_page = request.path


if next_page is None: if next_page:
current_site = get_current_site(request)
context = {
'site': current_site,
'site_name': current_site.name,
'title': _('Logged out')
}
context.update(extra_context or {})
return render_to_response(template_name, context,
context_instance=RequestContext(request, current_app=current_app))
else:
# Redirect to this page until the session has been cleared. # Redirect to this page until the session has been cleared.
return HttpResponseRedirect(next_page or request.path) return HttpResponseRedirect(next_page)

current_site = get_current_site(request)
context = {
'site': current_site,
'site_name': current_site.name,
'title': _('Logged out')
}
if extra_context is not None:
context.update(extra_context)
return render_to_response(template_name, context,
context_instance=RequestContext(request, current_app=current_app))


def logout_then_login(request, login_url=None, current_app=None, extra_context=None): def logout_then_login(request, login_url=None, current_app=None, extra_context=None):
""" """
Expand Down Expand Up @@ -151,7 +145,7 @@ def password_reset(request, is_admin_site=False,
'request': request, 'request': request,
} }
if is_admin_site: if is_admin_site:
opts = dict(opts, domain_override=request.META['HTTP_HOST']) opts = dict(opts, domain_override=request.get_host())
form.save(**opts) form.save(**opts)
return HttpResponseRedirect(post_reset_redirect) return HttpResponseRedirect(post_reset_redirect)
else: else:
Expand Down
11 changes: 4 additions & 7 deletions django/contrib/comments/views/comments.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ def post_comment(request, next=None, using=None):
if not data.get('email', ''): if not data.get('email', ''):
data["email"] = request.user.email data["email"] = request.user.email


# Check to see if the POST data overrides the view's next argument.
next = data.get("next", next)

# Look up the object we're trying to comment about # Look up the object we're trying to comment about
ctype = data.get("content_type") ctype = data.get("content_type")
object_pk = data.get("object_pk") object_pk = data.get("object_pk")
Expand Down Expand Up @@ -94,9 +91,9 @@ def post_comment(request, next=None, using=None):
] ]
return render_to_response( return render_to_response(
template_list, { template_list, {
"comment" : form.data.get("comment", ""), "comment": form.data.get("comment", ""),
"form" : form, "form": form,
"next": next, "next": data.get("next", next),
}, },
RequestContext(request, {}) RequestContext(request, {})
) )
Expand Down Expand Up @@ -127,7 +124,7 @@ def post_comment(request, next=None, using=None):
request = request request = request
) )


return next_redirect(data, next, comment_done, c=comment._get_pk_val()) return next_redirect(request, next, comment_done, c=comment._get_pk_val())


comment_done = confirmation_view( comment_done = confirmation_view(
template = "comments/posted.html", template = "comments/posted.html",
Expand Down
7 changes: 4 additions & 3 deletions django/contrib/comments/views/moderation.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from django.contrib.comments import signals from django.contrib.comments import signals
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect



@csrf_protect @csrf_protect
@login_required @login_required
def flag(request, comment_id, next=None): def flag(request, comment_id, next=None):
Expand All @@ -23,7 +24,7 @@ def flag(request, comment_id, next=None):
# Flag on POST # Flag on POST
if request.method == 'POST': if request.method == 'POST':
perform_flag(request, comment) perform_flag(request, comment)
return next_redirect(request.POST.copy(), next, flag_done, c=comment.pk) return next_redirect(request, next, flag_done, c=comment.pk)


# Render a form on GET # Render a form on GET
else: else:
Expand All @@ -50,7 +51,7 @@ def delete(request, comment_id, next=None):
if request.method == 'POST': if request.method == 'POST':
# Flag the comment as deleted instead of actually deleting it. # Flag the comment as deleted instead of actually deleting it.
perform_delete(request, comment) perform_delete(request, comment)
return next_redirect(request.POST.copy(), next, delete_done, c=comment.pk) return next_redirect(request, next, delete_done, c=comment.pk)


# Render a form on GET # Render a form on GET
else: else:
Expand All @@ -77,7 +78,7 @@ def approve(request, comment_id, next=None):
if request.method == 'POST': if request.method == 'POST':
# Flag the comment as approved. # Flag the comment as approved.
perform_approve(request, comment) perform_approve(request, comment)
return next_redirect(request.POST.copy(), next, approve_done, c=comment.pk) return next_redirect(request, next, approve_done, c=comment.pk)


# Render a form on GET # Render a form on GET
else: else:
Expand Down
10 changes: 6 additions & 4 deletions django/contrib/comments/views/utils.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@


import urllib import urllib
import textwrap import textwrap
from django.http import HttpResponseRedirect
from django.core import urlresolvers from django.core import urlresolvers
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.contrib import comments from django.contrib import comments
from django.utils.http import is_safe_url


def next_redirect(data, default, default_view, **get_kwargs): def next_redirect(request, default, default_view, **get_kwargs):
""" """
Handle the "where should I go next?" part of comment views. Handle the "where should I go next?" part of comment views.
Expand All @@ -21,9 +22,10 @@ def next_redirect(data, default, default_view, **get_kwargs):
Returns an ``HttpResponseRedirect``. Returns an ``HttpResponseRedirect``.
""" """
next = data.get("next", default) next = request.POST.get('next', default)
if next is None: if not is_safe_url(url=next, host=request.get_host()):
next = urlresolvers.reverse(default_view) next = urlresolvers.reverse(default_view)

if get_kwargs: if get_kwargs:
if '#' in next: if '#' in next:
tmp = next.rsplit('#', 1) tmp = next.rsplit('#', 1)
Expand Down
Loading

0 comments on commit 1aa654e

Please sign in to comment.