Repository: django-cms/django-cms

Issue:
bad redirect url when trying access 'login required' page
I found painful issue when `LOGIN_URL` contains query params.
For example in my settings.py a have:

```
    LOGIN_URL = '/accounts/?signin'
```

When I try accessing to private page I got redirect like `/accounts/?signin?next=/path_to_private_page/`

Small code changes in `cms/views.py` solves the problem:
was:

```
    93. tup = settings.LOGIN_URL , "next", path
    94. return HttpResponseRedirect('%s?%s=%s' % tup)
```

was now:

```
    93. parse_res = list(urlparse(settings.LOGIN_URL))
    94. params = parse_qsl(parse_res[4], True)  # 4 is query string
    95. params.append(('next', path))
    96. parse_res[4] = urlencode(params)
    97. url = unquote(urlunparse(parse_res))
    98. return HttpResponseRedirect(url)
```

after this changes redirect url is `/accounts/?signin=&next=/path_to_private_page/`



Discussion:
Django's auth views handle the same problem in a almost exactly the same way: [see trunk of redirect_to_login](https://code.djangoproject.com/browser/django/trunk/django/contrib/auth/views.py?rev=16454#L111), so the proposed fix should be fairly battle tested by now.

Edit: in fact, why not just call out to `contrib.auth.views.redirect_to_login()`? That view doesn't seem especially tied to the default auth implementation.

okay, better solution when using django 1.3.1:

```
    93. return redirect_to_login(path, settings.LOGIN_URL)
```

but not sure that other django versions have the exactly the same code in `redirect_to_login` func.


Resolving patch:
diff --git a/cms/views.py b/cms/views.py
--- a/cms/views.py
+++ b/cms/views.py
@@ -13,7 +13,7 @@
 from django.http import Http404, HttpResponseRedirect
 from django.shortcuts import render_to_response
 from django.template.context import RequestContext
-from django.utils import translation
+from django.contrib.auth.views import redirect_to_login
 from django.utils.http import urlquote
 
 def _handle_no_page(request, slug):
@@ -131,9 +131,7 @@ def details(request, slug):
 
     # permission checks
     if page.login_required and not request.user.is_authenticated():
-        path = urlquote(request.get_full_path())
-        tup = settings.LOGIN_URL, "next", path
-        return HttpResponseRedirect('%s?%s=%s' % tup)
+        return redirect_to_login(urlquote(request.get_full_path()), settings.LOGIN_URL)
 
     template_name = get_template_from_request(request, page, no_current_page=True)
     # fill the context 


Test patch:
diff --git a/cms/tests/views.py b/cms/tests/views.py
--- a/cms/tests/views.py
+++ b/cms/tests/views.py
@@ -116,11 +116,12 @@ def test_login_required(self):
         response = details(request, '')
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response['Location'], '%s?next=/en/' % settings.LOGIN_URL)
-        with SettingsOverride(USE_I18N=False):
+        with SettingsOverride(USE_I18N=False, LOGIN_URL='/accounts/?signin'):
             request = self.get_request('/')
             response = details(request, '')
             self.assertEqual(response.status_code, 302)
-            self.assertEqual(response['Location'], '%s?next=/' % settings.LOGIN_URL)
+            self.assertTrue(response['Location'].find("?signin?next") == -1)
+            self.assertEqual(response['Location'], '%s=&next=/' % settings.LOGIN_URL)
 
     def test_edit_permission(self):
         page = create_page("page", "nav_playground.html", "en", published=True)