Repository: wagtail/wagtail

Issue:
Password reset request generates email with URL "example.com"
Received:

```
Please follow the link below to reset your password
http://example.com/admin/password_reset/confirm/NA/3x7-cfc1f37209f0c04d1ee1/
```

This time `BASE_URL` _is_ configured, but as this view is from django.contrib this is perhaps due to some other missing setting. 

Related to #693 #826



Discussion:
`example.com` comes from Django's "sites" framework: https://docs.djangoproject.com/en/1.7/ref/contrib/sites/

Wagtail in general doesn't use the Django "sites" framework, but django.contrib.auth (which handles the password reset view here) does use it if available; if not, it falls back on RequestSite, which isn't particularly desirable either (it takes the domain name from the current request, which might not be directly usable e.g. if the site is behind a proxy).

The direct fix is to go to django-admin and configure the default Site record correctly; however, it's not really reasonable to expect Wagtail site administrators to do that, since Wagtail has its own partially-overlapping concept of site records and having both of them in use is a bit nasty. (In retrospect, we could probably have built Wagtail's sites mechanism on top of django.contrib.sites, but this isn't the place to reconsider that particular decision...) Instead, we'd be better off avoiding use of django.contrib.sites within Wagtail, and standardising on `BASE_URL` as used by the moderation emails.

Given that we're not enforcing the use of `BASE_URL` particularly well right now (as mentioned in #826, we don't even include it in the project template's settings file), we ought to take care to avoid breaking this for installations that _do_ have django.contrib.sites correctly configured but don't provide `BASE_URL` (in which case their moderation emails will currently be broken, but we'd rather not make it more broken). So, I think the best plan is:
- Look for a `BASE_URL` setting
- Failing that, cobble together a URL from the result of [get_current_site](https://docs.djangoproject.com/en/1.7/ref/contrib/sites/#get-current-site-shortcut) (which will use either django.contrib.sites or RequestSite, depending on what's installed)
- Push the resulting URL into the password_reset view (using `extra_context`) and use that instead of the standard protocol/domain variables.


Resolving patch:
diff --git a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
--- a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
+++ b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
@@ -134,6 +134,11 @@ def usage_count_enabled():
     return getattr(settings, 'WAGTAIL_USAGE_COUNT_ENABLED', False)
 
 
+@register.assignment_tag
+def base_url_setting():
+    return getattr(settings, 'BASE_URL', None)
+
+
 class EscapeScriptNode(template.Node):
     TAG_NAME = 'escapescript'
     SCRIPT_RE = re.compile(r'<(-*)/script>')


Test patch:
diff --git a/wagtail/wagtailadmin/tests/test_password_reset.py b/wagtail/wagtailadmin/tests/test_password_reset.py
new file mode 100644
--- /dev/null
+++ b/wagtail/wagtailadmin/tests/test_password_reset.py
@@ -0,0 +1,33 @@
+from django.test import TestCase, override_settings
+from django.core import mail
+
+from wagtail.tests.utils import WagtailTestUtils
+from wagtail.wagtailcore.models import Site
+
+
+class TestUserPasswordReset(TestCase, WagtailTestUtils):
+    fixtures = ['test.json']
+
+    # need to clear urlresolver caches before/after tests, because we override ROOT_URLCONF
+    # in some tests here
+    def setUp(self):
+        from django.core.urlresolvers import clear_url_caches
+        clear_url_caches()
+
+    def tearDown(self):
+        from django.core.urlresolvers import clear_url_caches
+        clear_url_caches()
+
+    @override_settings(ROOT_URLCONF="wagtail.wagtailadmin.urls")
+    def test_email_found_default_url(self):
+        response = self.client.post('/password_reset/', {'email': 'siteeditor@example.com'})
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertIn("testserver", mail.outbox[0].body)
+
+    @override_settings(ROOT_URLCONF="wagtail.wagtailadmin.urls", BASE_URL='http://mysite.com')
+    def test_email_found_base_url(self):
+        response = self.client.post('/password_reset/', {'email': 'siteeditor@example.com'})
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertIn("mysite.com", mail.outbox[0].body)