diff --git a/hypha/apply/users/forms.py b/hypha/apply/users/forms.py index ef8005b26d..bbe5356a45 100644 --- a/hypha/apply/users/forms.py +++ b/hypha/apply/users/forms.py @@ -32,7 +32,7 @@ class PasswordlessAuthForm(forms.Form): """ email = forms.EmailField( - label=_("Email Address"), + label=_("Email address"), required=True, max_length=254, widget=forms.EmailInput(attrs={"autofocus": True, "autocomplete": "email"}), diff --git a/hypha/apply/users/services.py b/hypha/apply/users/services.py index 8762aa00c2..70d33ea326 100644 --- a/hypha/apply/users/services.py +++ b/hypha/apply/users/services.py @@ -63,7 +63,7 @@ def get_email_context(self) -> dict: def send_email_no_account_found(self, to): context = self.get_email_context() - subject = "Login attempt at {org_long_name}".format(**context) + subject = "Log in attempt at {org_long_name}".format(**context) # Force subject to a single line to avoid header-injection issues. subject = "".join(subject.splitlines()) @@ -91,7 +91,7 @@ def send_login_email(self, user): } ) - subject = "Login to {username} at {org_long_name}".format(**context) + subject = "Log in to {username} at {org_long_name}".format(**context) # Force subject to a single line to avoid header-injection issues. subject = "".join(subject.splitlines()) diff --git a/hypha/apply/users/templates/users/login.html b/hypha/apply/users/templates/users/login.html index a9f8eed607..66e4736384 100644 --- a/hypha/apply/users/templates/users/login.html +++ b/hypha/apply/users/templates/users/login.html @@ -1,11 +1,11 @@ {% extends "base-apply.html" %} -{% load i18n wagtailcore_tags %} +{% load i18n wagtailcore_tags heroicons %} -{% block title %}{% trans "Login" %}{% endblock %} +{% block title %}{% trans "Log in" %}{% endblock %} {% block body_class %}bg-white{% endblock %} {% block content %} -
+
@@ -23,10 +23,10 @@

{% trans "Two factor verification" %}

{% elif wizard.steps.current == 'backup' %}

{% trans "Two factor verification" %}

- {% blocktrans trimmed %}Please enter one of the backup codes to login to your account.{% endblocktrans %} + {% blocktrans trimmed %}Please enter one of the backup codes to log in to your account.{% endblocktrans %}

- Those codes were generated for you during 2FA setup to print or keep safe in a password manager. + {% blocktrans trimmed %}Those codes were generated for you during 2FA setup to print or keep safe in a password manager.{% endblocktrans %}

{% endif %} @@ -61,24 +61,21 @@

{% blocktrans %}Log in to {{ ORG_SHORT_NAME }}{% endblocktr {% endif %}
- - - {% if ENABLE_PUBLIC_SIGNUP %} - {% trans "Create account" %} - {% endif %} +
- {% if GOOGLE_OAUTH2 %} -
-
- {% trans "or" %} -
-
+
+
+ {% trans "or" %} +
+
- - {% endif %} +
+ {% if GOOGLE_OAUTH2 %} + {% include "includes/org_login_button.html" %} + {% endif %} + {% include "includes/passwordless_login_button.html" %} +
diff --git a/hypha/apply/users/templates/users/password_reset/form.html b/hypha/apply/users/templates/users/password_reset/form.html index 23fdbb1fcb..82ef552a61 100644 --- a/hypha/apply/users/templates/users/password_reset/form.html +++ b/hypha/apply/users/templates/users/password_reset/form.html @@ -28,14 +28,8 @@

{% trans "Forgot password?" %}

{{ form.email.label_tag }}

{{ form.email }}

- + - -
{% endblock %} diff --git a/hypha/apply/users/templates/users/passwordless_login_signup.html b/hypha/apply/users/templates/users/passwordless_login_signup.html index a8af8c41ab..40c99a60a5 100644 --- a/hypha/apply/users/templates/users/passwordless_login_signup.html +++ b/hypha/apply/users/templates/users/passwordless_login_signup.html @@ -1,7 +1,9 @@ {% extends base_template %} {% load i18n wagtailcore_tags heroicons %} -{% block title %}{% trans "Login or Signup" %}{% endblock %} +{% block title %} + {% trans "Log in" %}{% if ENABLE_PUBLIC_SIGNUP %} {% trans "or" %} {% trans "Sign up" %}{% endif %} +{% endblock %} {% block content %}
@@ -16,9 +18,9 @@

{% if ENABLE_PUBLIC_SIGNUP %} - {% blocktrans %}Login or signup to {{ ORG_SHORT_NAME }}{% endblocktrans %} + {% blocktrans %}Log in or signup to {{ ORG_SHORT_NAME }}{% endblocktrans %} {% else %} - {% blocktrans %}Login to {{ ORG_SHORT_NAME }}{% endblocktrans %} + {% blocktrans %}Log in to {{ ORG_SHORT_NAME }}{% endblocktrans %} {% endif %}

@@ -42,32 +44,21 @@

{% endif %}
- +
-
+

{% trans "or" %}
-
+
{% if GOOGLE_OAUTH2 %} - - {% blocktrans %}Log in with your {{ ORG_SHORT_NAME }} email{% endblocktrans %} - + {% include "includes/org_login_button.html" %} {% endif %} - - {% heroicon_mini "key" size=18 class="inline align-text-bottom me-1" aria_hidden=true %} - {% trans "Login with Password" %} - + {% include "includes/password_login_button.html" %}
diff --git a/hypha/apply/users/templates/users/register.html b/hypha/apply/users/templates/users/register.html deleted file mode 100644 index 1b9b08de0b..0000000000 --- a/hypha/apply/users/templates/users/register.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "base-apply.html" %} -{% load i18n wagtailcore_tags %} - -{% block title %}{% trans "Register" %}{% endblock %} -{% block body_class %}bg-white{% endblock %} - -{% block content %} -
-
-
-
- {% csrf_token %} - {% if redirect_url %} - - {% endif %} -

{% trans "Create your account" %}

- - {% if form.non_field_errors %} -
{{ form.non_field_errors.as_text }}
- {% endif %} - - {% for field in form %} - {%if field.id_for_label != 'id_is_superuser' and field.label != 'Groups' %} - {% include "forms/includes/field.html" %} - {%endif%} - {% endfor %} -
- -
-
-

- {% trans "Already have an account?" %} {% trans "Log in" %} -

-
-
- - {% if settings.users.AuthSettings.register_extra_text %} -
-
- {{ settings.users.AuthSettings.register_extra_text|richtext}} -
-
- {% endif %} -
-{% endblock %} diff --git a/hypha/apply/users/tests/test_registration.py b/hypha/apply/users/tests/test_registration.py deleted file mode 100644 index 510a2708eb..0000000000 --- a/hypha/apply/users/tests/test_registration.py +++ /dev/null @@ -1,71 +0,0 @@ -from django.conf import settings -from django.core import mail -from django.test import TestCase, override_settings -from django.urls import reverse - -from hypha.apply.funds.tests.factories import FundTypeFactory -from hypha.apply.utils.testing import make_request - - -class TestRegistration(TestCase): - @override_settings(ENABLE_PUBLIC_SIGNUP=False) - def test_registration_enabled_has_no_link(self): - response = self.client.get("/", follow=True) - self.assertNotContains(response, reverse("users:register")) - - @override_settings(ENABLE_PUBLIC_SIGNUP=True) - def test_registration_enabled_has_link(self): - response = self.client.get("/", follow=True) - self.assertContains(response, reverse("users:register")) - - @override_settings(ENABLE_PUBLIC_SIGNUP=True) - def test_registration(self): - response = self.client.post( - reverse("users:register"), - data={ - "email": "test@test.com", - "first_name": "Not used - see full_name", - "last_name": "Not used - see full_name", - }, - secure=True, - ) - assert len(mail.outbox) == 1 - assert "Activate your account on the" in mail.outbox[0].body - - assert response.status_code == 302 - assert reverse("users:register-success") in response.url - - @override_settings(ENABLE_PUBLIC_SIGNUP=True) - def test_duplicate_registration_fails(self): - response = self.client.post( - reverse("users:register"), - data={ - "email": "test@test.com", - "first_name": "Not used - see full_name", - "last_name": "Not used - see full_name", - }, - secure=True, - ) - mail.outbox.clear() - - response = self.client.post( - reverse("users:register"), - data={ - "email": "test@test.com", - "first_name": "Not used - see full_name", - "last_name": "Not used - see full_name", - }, - secure=True, - ) - - assert len(mail.outbox) == 0 - self.assertContains(response, "A user with that email already exists") - - @override_settings(FORCE_LOGIN_FOR_APPLICATION=True, ENABLE_PUBLIC_SIGNUP=False) - def test_force_login(self): - fund = FundTypeFactory() - response = fund.serve( - make_request(None, {}, method="get", site=fund.get_site()) - ) - assert response.status_code == 302 - assert response.url == reverse(settings.LOGIN_URL) + "?next=/" diff --git a/hypha/apply/users/urls.py b/hypha/apply/users/urls.py index 3759fb8202..32afa399a6 100644 --- a/hypha/apply/users/urls.py +++ b/hypha/apply/users/urls.py @@ -17,8 +17,6 @@ PasswordlessSignupView, PasswordResetConfirmView, PasswordResetView, - RegisterView, - RegistrationSuccessView, TWOFAAdminDisableView, TWOFADisableView, TWOFASetupView, @@ -39,10 +37,6 @@ ), path("login/", LoginView.as_view(), name="login"), path("logout/", auth_views.LogoutView.as_view(next_page="/"), name="logout"), - path("register/", RegisterView.as_view(), name="register"), - path( - "register-success/", RegistrationSuccessView.as_view(), name="register-success" - ), ] account_urls = [ diff --git a/hypha/apply/users/views.py b/hypha/apply/users/views.py index 041fcfc99d..430643ced2 100644 --- a/hypha/apply/users/views.py +++ b/hypha/apply/users/views.py @@ -18,7 +18,7 @@ from django.core.exceptions import PermissionDenied from django.core.signing import TimestampSigner, dumps, loads from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import Http404, get_object_or_404, redirect, render, resolve_url +from django.shortcuts import Http404, get_object_or_404, redirect, render from django.template.loader import render_to_string from django.template.response import TemplateResponse from django.urls import reverse, reverse_lazy @@ -30,7 +30,7 @@ from django.views.decorators.cache import never_cache from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import UpdateView -from django.views.generic.base import TemplateView, View +from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from django_htmx.http import HttpResponseClientRedirect from django_otp import devices_for_user @@ -56,7 +56,6 @@ from .forms import ( BecomeUserForm, CustomAuthenticationForm, - CustomUserCreationForm, Disable2FAConfirmationForm, PasswordlessAuthForm, ProfileForm, @@ -74,65 +73,6 @@ User = get_user_model() -@method_decorator( - ratelimit(key="ip", rate=settings.DEFAULT_RATE_LIMIT, method="POST"), - name="dispatch", -) -class RegisterView(View): - redirect_field_name = "next" - form = CustomUserCreationForm - - def get(self, request): - # We keep /register in the urls in order to test (where we turn on/off - # the setting per test), but when disabled, we want to pretend it doesn't - # exist va 404 - if not settings.ENABLE_PUBLIC_SIGNUP: - raise Http404 - - if request.user.is_authenticated: - return redirect(settings.LOGIN_REDIRECT_URL) - - ctx = { - "form": self.form(register_view=True), - "redirect_url": get_redirect_url(request, self.redirect_field_name), - } - return render(request, "users/register.html", ctx) - - def post(self, request): - # See comment in get() above about doing this here rather than in urls - if not settings.ENABLE_PUBLIC_SIGNUP: - raise Http404 - - form = self.form(register_view=True, data=request.POST) - context = {} - if form.is_valid(): - # If using wagtail password management - if "password1" in form.cleaned_data: - context["password"] = form.cleaned_data["password1"] - - site = Site.find_for_request(self.request) - user, created = User.objects.get_or_create_and_notify( - email=form.cleaned_data["email"], - site=site, - redirect_url=get_redirect_url(request, self.redirect_field_name), - defaults={ - "full_name": form.cleaned_data["full_name"], - }, - **context, - ) - if created: - params = {"name": user.full_name, "email": user.email} - # redirect to success page with params as query params - return HttpResponseRedirect( - resolve_url("users:register-success") + "?" + urlencode(params) - ) - return render(request, "users/register.html", {"form": form}) - - -class RegistrationSuccessView(TemplateView): - template_name = "users/register-success.html" - - @method_decorator( ratelimit(key="ip", rate=settings.DEFAULT_RATE_LIMIT, method="POST"), name="dispatch", diff --git a/hypha/static_src/sass/components/_link.scss b/hypha/static_src/sass/components/_link.scss index 9624fab830..591eb11242 100644 --- a/hypha/static_src/sass/components/_link.scss +++ b/hypha/static_src/sass/components/_link.scss @@ -9,9 +9,24 @@ @include button($color--light-blue, $color--dark-blue); display: inline-block; + // The custom width of 465px allow the buttons to scale with the input field + $input-box-max-width: 465px; + &--narrow { @include button--narrow; } + + &--login { + /* stylelint-disable-next-line media-query-no-invalid */ + @media (min-width: $input-box-max-width) { + width: 20rem; + } + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media (max-width: $input-box-max-width) { + width: 100%; + } } &--underlined { diff --git a/hypha/templates/base-apply.html b/hypha/templates/base-apply.html index 89c4cc7899..0c393448e3 100644 --- a/hypha/templates/base-apply.html +++ b/hypha/templates/base-apply.html @@ -48,12 +48,9 @@ {% heroicon_outline "bars-3" size=32 stroke_width=2 class="inline" aria_hidden="true" %} {% else %} - {% if request.path != '/auth/' %} + {% if request.path != '/auth/' and request.path != '/login/' %} {% include "includes/login_button.html" %} {% endif %} - {% if ENABLE_PUBLIC_SIGNUP and request.path == '/auth/' %} - {% include "includes/register_button.html" %} - {% endif %} {% endif %}
@@ -121,7 +118,7 @@

{% endif %} - {% if request.path != '/auth/' %} + {% if request.path != '/auth/' and request.path != '/login/' %} {% include "includes/login_button.html" %} {% endif %} @@ -129,10 +126,6 @@ {% trans "Log out" %} - {% else %} - {% if ENABLE_PUBLIC_SIGNUP and request.path != '/register/' %} - {% include "includes/register_button.html" %} - {% endif %} {% endif %}
diff --git a/hypha/templates/includes/login_button.html b/hypha/templates/includes/login_button.html index d5157d8dcf..b440bef386 100644 --- a/hypha/templates/includes/login_button.html +++ b/hypha/templates/includes/login_button.html @@ -8,7 +8,7 @@ > {% heroicon_micro "user" class="inline align-text-bottom w-4 h-4 me-1" aria_hidden=true %} - My {{ ORG_SHORT_NAME }} + {% blocktrans %}My {{ ORG_SHORT_NAME }}{% endblocktrans %} {% else %} {% heroicon_micro "user" class="inline align-text-bottom w-4 h-4 me-1" aria_hidden=true %} - {% trans "Login" %} + {% trans "Log in" %} {% if ENABLE_PUBLIC_SIGNUP %} {% trans " or Sign up" %} {% endif %} {% endif %} diff --git a/hypha/templates/includes/org_login_button.html b/hypha/templates/includes/org_login_button.html new file mode 100644 index 0000000000..e40ff3b36e --- /dev/null +++ b/hypha/templates/includes/org_login_button.html @@ -0,0 +1,8 @@ +{% load i18n heroicons %} + + {% heroicon_mini "building-office" size=18 class="inline align-text-bottom me-1" aria_hidden=true %} + {% blocktrans %}Log in with your {{ ORG_SHORT_NAME }} email{% endblocktrans %} + \ No newline at end of file diff --git a/hypha/templates/includes/password_login_button.html b/hypha/templates/includes/password_login_button.html new file mode 100644 index 0000000000..2767669213 --- /dev/null +++ b/hypha/templates/includes/password_login_button.html @@ -0,0 +1,8 @@ +{% load i18n heroicons %} + + {% heroicon_mini "key" size=18 class="inline align-text-bottom me-1" aria_hidden=true %} + {% trans "Log in with password" %} + \ No newline at end of file diff --git a/hypha/templates/includes/passwordless_login_button.html b/hypha/templates/includes/passwordless_login_button.html new file mode 100644 index 0000000000..d5d2ba78c4 --- /dev/null +++ b/hypha/templates/includes/passwordless_login_button.html @@ -0,0 +1,8 @@ +{% load i18n heroicons %} + + {% heroicon_mini "envelope" size=18 class="inline align-text-bottom me-1" aria_hidden=true %} + {% trans "Log in without password" %} + diff --git a/hypha/templates/includes/register_button.html b/hypha/templates/includes/register_button.html deleted file mode 100644 index 9134346bc4..0000000000 --- a/hypha/templates/includes/register_button.html +++ /dev/null @@ -1,4 +0,0 @@ -{% load i18n %} - - {% trans "Sign up" %} -