### Password reset
In order to enable the password reset feature you will need to setup an SMTP backend, see
-[`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST)
+[`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST). If your installation does not have
+[`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) set, the reset link included in emails will use the server host.
## Workflows
or django_settings.EMAIL_HOST_USER != "",
"DISABLE_REGULAR_LOGIN": django_settings.DISABLE_REGULAR_LOGIN,
"ACCOUNT_ALLOW_SIGNUPS": django_settings.ACCOUNT_ALLOW_SIGNUPS,
+ "domain": getattr(django_settings, "PAPERLESS_URL", request.get_host()),
}
--- /dev/null
+{% load i18n %}{% autoescape off %}{% blocktrans with site_name="Paperless-ngx" %}Hello from {{ site_name }}!{% endblocktrans %}
+
+{% block content %}{% endblock content %}
+
+{% blocktrans with site_name="Paperless-ngx" site_domain=settings.domain %}Thank you for using {{ site_name }}!
+{{ site_domain }}{% endblocktrans %}
+{% endautoescape %}
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-02-26 00:10-0800\n"
+"POT-Creation-Date: 2024-02-26 13:34-0800\n"
"PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n"
"Language-Team: English\n"
msgid "Invalid variable detected."
msgstr ""
+#: documents/templates/account/email/base_message.txt:1
+#, python-format
+msgid "Hello from %(site_name)s!"
+msgstr ""
+
+#: documents/templates/account/email/base_message.txt:5
+#, python-format
+msgid ""
+"Thank you for using %(site_name)s!\n"
+"%(site_domain)s"
+msgstr ""
+
#: documents/templates/account/login.html:5
msgid "Paperless-ngx sign in"
msgstr ""
msgid "paperless application settings"
msgstr ""
-#: paperless/settings.py:644
+#: paperless/settings.py:656
msgid "English (US)"
msgstr ""
-#: paperless/settings.py:645
+#: paperless/settings.py:657
msgid "Arabic"
msgstr ""
-#: paperless/settings.py:646
+#: paperless/settings.py:658
msgid "Afrikaans"
msgstr ""
-#: paperless/settings.py:647
+#: paperless/settings.py:659
msgid "Belarusian"
msgstr ""
-#: paperless/settings.py:648
+#: paperless/settings.py:660
msgid "Bulgarian"
msgstr ""
-#: paperless/settings.py:649
+#: paperless/settings.py:661
msgid "Catalan"
msgstr ""
-#: paperless/settings.py:650
+#: paperless/settings.py:662
msgid "Czech"
msgstr ""
-#: paperless/settings.py:651
+#: paperless/settings.py:663
msgid "Danish"
msgstr ""
-#: paperless/settings.py:652
+#: paperless/settings.py:664
msgid "German"
msgstr ""
-#: paperless/settings.py:653
+#: paperless/settings.py:665
msgid "Greek"
msgstr ""
-#: paperless/settings.py:654
+#: paperless/settings.py:666
msgid "English (GB)"
msgstr ""
-#: paperless/settings.py:655
+#: paperless/settings.py:667
msgid "Spanish"
msgstr ""
-#: paperless/settings.py:656
+#: paperless/settings.py:668
msgid "Finnish"
msgstr ""
-#: paperless/settings.py:657
+#: paperless/settings.py:669
msgid "French"
msgstr ""
-#: paperless/settings.py:658
+#: paperless/settings.py:670
msgid "Hungarian"
msgstr ""
-#: paperless/settings.py:659
+#: paperless/settings.py:671
msgid "Italian"
msgstr ""
-#: paperless/settings.py:660
+#: paperless/settings.py:672
msgid "Japanese"
msgstr ""
-#: paperless/settings.py:661
+#: paperless/settings.py:673
msgid "Luxembourgish"
msgstr ""
-#: paperless/settings.py:662
+#: paperless/settings.py:674
msgid "Norwegian"
msgstr ""
-#: paperless/settings.py:663
+#: paperless/settings.py:675
msgid "Dutch"
msgstr ""
-#: paperless/settings.py:664
+#: paperless/settings.py:676
msgid "Polish"
msgstr ""
-#: paperless/settings.py:665
+#: paperless/settings.py:677
msgid "Portuguese (Brazil)"
msgstr ""
-#: paperless/settings.py:666
+#: paperless/settings.py:678
msgid "Portuguese"
msgstr ""
-#: paperless/settings.py:667
+#: paperless/settings.py:679
msgid "Romanian"
msgstr ""
-#: paperless/settings.py:668
+#: paperless/settings.py:680
msgid "Russian"
msgstr ""
-#: paperless/settings.py:669
+#: paperless/settings.py:681
msgid "Slovak"
msgstr ""
-#: paperless/settings.py:670
+#: paperless/settings.py:682
msgid "Slovenian"
msgstr ""
-#: paperless/settings.py:671
+#: paperless/settings.py:683
msgid "Serbian"
msgstr ""
-#: paperless/settings.py:672
+#: paperless/settings.py:684
msgid "Swedish"
msgstr ""
-#: paperless/settings.py:673
+#: paperless/settings.py:685
msgid "Turkish"
msgstr ""
-#: paperless/settings.py:674
+#: paperless/settings.py:686
msgid "Ukrainian"
msgstr ""
-#: paperless/settings.py:675
+#: paperless/settings.py:687
msgid "Chinese Simplified"
msgstr ""
+from urllib.parse import quote
+
from allauth.account.adapter import DefaultAccountAdapter
from allauth.core import context
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
return url_has_allowed_host_and_scheme(url, allowed_hosts=allowed_hosts)
+ def get_reset_password_from_key_url(self, key):
+ """
+ Return the URL to reset a password e.g. in reset email.
+ """
+ if settings.PAPERLESS_URL is None:
+ return super().get_reset_password_from_key_url(key)
+ else:
+ path = reverse(
+ "account_reset_password_from_key",
+ kwargs={"uidb36": "UID", "key": "KEY"},
+ )
+ path = path.replace("UID-KEY", quote(key))
+ return settings.PAPERLESS_URL + path
+
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def is_open_for_signup(self, request, sociallogin):
os.getenv("PAPERLESS_SOCIALACCOUNT_PROVIDERS", "{}"),
)
+ACCOUNT_EMAIL_SUBJECT_PREFIX = "[Paperless-ngx] "
+
DISABLE_REGULAR_LOGIN = __get_boolean("PAPERLESS_DISABLE_REGULAR_LOGIN")
AUTO_LOGIN_USERNAME = os.getenv("PAPERLESS_AUTO_LOGIN_USERNAME")
CORS_ALLOWED_ORIGINS.append("http://localhost:4200")
ALLOWED_HOSTS = __get_list("PAPERLESS_ALLOWED_HOSTS", ["*"])
-
-_paperless_url = os.getenv("PAPERLESS_URL")
-if _paperless_url:
- _paperless_uri = urlparse(_paperless_url)
- CSRF_TRUSTED_ORIGINS.append(_paperless_url)
- CORS_ALLOWED_ORIGINS.append(_paperless_url)
-
if ["*"] != ALLOWED_HOSTS:
# always allow localhost. Necessary e.g. for healthcheck in docker.
ALLOWED_HOSTS.append("localhost")
- if _paperless_url:
- ALLOWED_HOSTS.append(_paperless_uri.hostname)
+
+
+def _parse_paperless_url():
+ global CSRF_TRUSTED_ORIGINS, CORS_ALLOWED_ORIGINS, ALLOWED_HOSTS
+ url = os.getenv("PAPERLESS_URL")
+ if url:
+ CSRF_TRUSTED_ORIGINS.append(url)
+ CORS_ALLOWED_ORIGINS.append(url)
+ ALLOWED_HOSTS.append(urlparse(url).hostname)
+
+ return url
+
+
+PAPERLESS_URL = _parse_paperless_url()
# For use with trusted proxies
TRUSTED_PROXIES = __get_list("PAPERLESS_TRUSTED_PROXIES")
EMAIL_USE_TLS: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_TLS")
EMAIL_USE_SSL: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_SSL")
EMAIL_SUBJECT_PREFIX: Final[str] = "[Paperless-ngx] "
+if DEBUG: # pragma: no cover
+ EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
+ EMAIL_FILE_PATH = BASE_DIR / "sent_emails"
with self.assertRaises(ValidationError):
adapter.pre_authenticate(request)
+ def test_get_reset_password_from_key_url(self):
+ request = HttpRequest()
+ request.get_host = mock.Mock(return_value="foo.org")
+ with context.request_context(request):
+ adapter = get_adapter()
+
+ # Test when PAPERLESS_URL is None
+ expected_url = f"https://foo.org{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
+ self.assertEqual(
+ adapter.get_reset_password_from_key_url("UID-KEY"),
+ expected_url,
+ )
+
+ # Test when PAPERLESS_URL is not None
+ with override_settings(PAPERLESS_URL="https://bar.com"):
+ expected_url = f"https://bar.com{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
+ self.assertEqual(
+ adapter.get_reset_password_from_key_url("UID-KEY"),
+ expected_url,
+ )
+
class TestCustomSocialAccountAdapter(TestCase):
def test_is_open_for_signup(self):
from paperless.settings import _parse_beat_schedule
from paperless.settings import _parse_db_settings
from paperless.settings import _parse_ignore_dates
+from paperless.settings import _parse_paperless_url
from paperless.settings import _parse_redis_url
from paperless.settings import default_threads_per_worker
},
databases["sqlite"]["OPTIONS"],
)
+
+
+class TestPaperlessURLSettings(TestCase):
+ def test_paperless_url(self):
+ """
+ GIVEN:
+ - PAPERLESS_URL is set
+ WHEN:
+ - The URL is parsed
+ THEN:
+ - The URL is returned and present in related settings
+ """
+ with mock.patch.dict(
+ os.environ,
+ {
+ "PAPERLESS_URL": "https://example.com",
+ },
+ ):
+ url = _parse_paperless_url()
+ self.assertEqual("https://example.com", url)
+ from django.conf import settings
+
+ self.assertIn(url, settings.CSRF_TRUSTED_ORIGINS)
+ self.assertIn(url, settings.CORS_ALLOWED_ORIGINS)