diff options
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | activities/views/search.py | 2 | ||||
-rw-r--r-- | takahe/urls.py | 8 | ||||
-rw-r--r-- | templates/auth/perform_reset.html (renamed from templates/auth/reset.html) | 0 | ||||
-rw-r--r-- | templates/auth/perform_reset_success.html (renamed from templates/auth/reset_success.html) | 0 | ||||
-rw-r--r-- | templates/auth/trigger_reset.html | 18 | ||||
-rw-r--r-- | templates/auth/trigger_reset_success.html | 14 | ||||
-rw-r--r-- | templates/settings/_menu.html | 2 | ||||
-rw-r--r-- | templates/settings/login_security.html | 17 | ||||
-rw-r--r-- | users/views/auth.py | 35 | ||||
-rw-r--r-- | users/views/settings.py | 27 |
11 files changed, 116 insertions, 15 deletions
@@ -69,16 +69,16 @@ the less sure I am about it. - [x] Settings subsystem - [x] Server management page - [x] Domain management page -- [ ] Email subsystem -- [ ] Signup flow -- [ ] Password change flow -- [ ] Password reset flow +- [x] Email subsystem +- [x] Signup flow +- [x] Password reset flow ### Beta - [ ] Attach images to posts - [ ] Edit posts - [ ] Delete posts +- [ ] Password change flow - [ ] Fetch remote post images locally and thumbnail - [ ] Show follow pending states - [ ] Manual approval of followers diff --git a/activities/views/search.py b/activities/views/search.py index b748348..cf62410 100644 --- a/activities/views/search.py +++ b/activities/views/search.py @@ -9,7 +9,7 @@ class Search(FormView): template_name = "activities/search.html" class form_class(forms.Form): - query = forms.CharField() + query = forms.CharField(help_text="Search for a user by @username@domain") def form_valid(self, form): query = form.cleaned_data["query"].lstrip("@").lower() diff --git a/takahe/urls.py b/takahe/urls.py index 5c0b182..0ea49d0 100644 --- a/takahe/urls.py +++ b/takahe/urls.py @@ -22,6 +22,11 @@ urlpatterns = [ name="settings", ), path( + "settings/security/", + settings.SecurityPage.as_view(), + name="settings_security", + ), + path( "settings/profile/", settings.ProfilePage.as_view(), name="settings_profile", @@ -85,7 +90,8 @@ urlpatterns = [ path("auth/login/", auth.Login.as_view(), name="login"), path("auth/logout/", auth.Logout.as_view(), name="logout"), path("auth/signup/", auth.Signup.as_view(), name="signup"), - path("auth/reset/<token>/", auth.Reset.as_view(), name="password_reset"), + path("auth/reset/", auth.TriggerReset.as_view(), name="trigger_reset"), + path("auth/reset/<token>/", auth.PerformReset.as_view(), name="password_reset"), # Identity selection path("@<handle>/activate/", identity.ActivateIdentity.as_view()), path("identity/select/", identity.SelectIdentity.as_view()), diff --git a/templates/auth/reset.html b/templates/auth/perform_reset.html index 42eced9..42eced9 100644 --- a/templates/auth/reset.html +++ b/templates/auth/perform_reset.html diff --git a/templates/auth/reset_success.html b/templates/auth/perform_reset_success.html index 001e5d7..001e5d7 100644 --- a/templates/auth/reset_success.html +++ b/templates/auth/perform_reset_success.html diff --git a/templates/auth/trigger_reset.html b/templates/auth/trigger_reset.html new file mode 100644 index 0000000..b81c380 --- /dev/null +++ b/templates/auth/trigger_reset.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block title %}Reset Password{% endblock %} + +{% block content %} + <form action="." method="POST"> + {% csrf_token %} + <fieldset> + <legend>Reset Password</legend> + {% for field in form %} + {% include "forms/_field.html" %} + {% endfor %} + </fieldset> + <div class="buttons"> + <button>Send Email</button> + </div> + </form> +{% endblock %} diff --git a/templates/auth/trigger_reset_success.html b/templates/auth/trigger_reset_success.html new file mode 100644 index 0000000..c0c1a0d --- /dev/null +++ b/templates/auth/trigger_reset_success.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title %}Password Reset Sent{% endblock %} + +{% block content %} + <form> + <fieldset> + <legend>Password Reset Sent</legend> + <p> + Please check your email at <tt>{{ email }}</tt> for the reset link. + </p> + </fieldset> + </form> +{% endblock %} diff --git a/templates/settings/_menu.html b/templates/settings/_menu.html index d85c878..cc87941 100644 --- a/templates/settings/_menu.html +++ b/templates/settings/_menu.html @@ -8,7 +8,7 @@ </a> {% if request.user.admin %} <h3>Account</h3> - <a href="#" {% if section == "login" %}class="selected"{% endif %}> + <a href="{% url "settings_security" %}" {% if section == "security" %}class="selected"{% endif %}> <i class="fa-solid fa-key"></i> Login & Security </a> <a href="/auth/logout/"> diff --git a/templates/settings/login_security.html b/templates/settings/login_security.html new file mode 100644 index 0000000..701b325 --- /dev/null +++ b/templates/settings/login_security.html @@ -0,0 +1,17 @@ +{% extends "settings/base.html" %} + +{% block subtitle %}Login & Security{% endblock %} + +{% block content %} + <form action="." method="POST"> + {% csrf_token %} + <fieldset> + <legend>Login</legend> + {% include "forms/_field.html" with field=form.email %} + </fieldset> + <fieldset> + <legend>Password</legend> + <p>To change your password, please trigger a <a href="{% url "trigger_reset" %}">password reset</a>. + </fieldset> + </form> +{% endblock %} diff --git a/users/views/auth.py b/users/views/auth.py index 7d4040b..7f51d45 100644 --- a/users/views/auth.py +++ b/users/views/auth.py @@ -44,9 +44,38 @@ class Signup(FormView): ) -class Reset(FormView): +class TriggerReset(FormView): - template_name = "auth/reset.html" + template_name = "auth/trigger_reset.html" + + class form_class(forms.Form): + + email = forms.EmailField( + help_text="We will send a reset link to this email", + ) + + def clean_email(self): + email = self.cleaned_data.get("email").lower() + if not email: + return + if not User.objects.filter(email=email).exists(): + raise forms.ValidationError("This email does not have an account") + return email + + def form_valid(self, form): + PasswordReset.create_for_user( + User.objects.get(email=form.cleaned_data["email"]) + ) + return render( + self.request, + "auth/trigger_reset_success.html", + {"email": form.cleaned_data["email"]}, + ) + + +class PerformReset(FormView): + + template_name = "auth/perform_reset.html" class form_class(forms.Form): @@ -81,7 +110,7 @@ class Reset(FormView): self.reset.delete() return render( self.request, - "auth/reset_success.html", + "auth/perform_reset_success.html", {"email": self.reset.user.email}, ) diff --git a/users/views/settings.py b/users/views/settings.py index d823676..fd138c2 100644 --- a/users/views/settings.py +++ b/users/views/settings.py @@ -126,6 +126,7 @@ class ProfilePage(FormView): """ template_name = "settings/profile.html" + extra_context = {"section": "profile"} class form_class(forms.Form): name = forms.CharField(max_length=500) @@ -150,11 +151,6 @@ class ProfilePage(FormView): "image": self.request.identity.image.url, } - def get_context_data(self): - context = super().get_context_data() - context["section"] = "profile" - return context - def form_valid(self, form): # Update identity name and summary self.request.identity.name = form.cleaned_data["name"] @@ -174,3 +170,24 @@ class ProfilePage(FormView): self.request.identity.image = image self.request.identity.save() return redirect(".") + + +@method_decorator(identity_required, name="dispatch") +class SecurityPage(FormView): + """ + Lets the identity's profile be edited + """ + + template_name = "settings/login_security.html" + extra_context = {"section": "security"} + + class form_class(forms.Form): + email = forms.EmailField( + disabled=True, + help_text="Your email address cannot be changed yet.", + ) + + def get_initial(self): + return {"email": self.request.user.email} + + template_name = "settings/login_security.html" |