Adds blocked users view

also refactors the setting view
This commit is contained in:
Mouse Reeve 2021-01-26 09:56:01 -08:00
parent 3f011445e2
commit acfc865d4e
11 changed files with 273 additions and 178 deletions

View File

@ -0,0 +1,21 @@
{% extends 'preferences_layout.html' %}
{% block header %}
Blocked Users
{% endblock %}
{% block panel %}
<ul>
{% for user in request.user.blocks.all %}
<li class="is-flex">
<p>
{% include 'snippets/avatar.html' with user=user %} {% include 'snippets/username.html' with user=user %}
</p>
<p class="mr-2">
{% include 'snippets/block_button.html' with user=user %}
</p>
</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends 'preferences_layout.html' %}
{% block header %}
Change Password
{% endblock %}
{% block panel %}
<form name="edit-profile" action="/change-password/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="block">
<label class="label" for="id_password">New password:</label>
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password">
</div>
<div class="block">
<label class="label" for="id_confirm_password">Confirm password:</label>
<input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password">
</div>
<button class="button is-primary" type="submit">Change password</button>
</form>
{% endblock %}

View File

@ -1,66 +1,48 @@
{% extends 'layout.html' %} {% extends 'preferences_layout.html' %}
{% block content %} {% block header %}
<div class="block columns"> Edit Profile
<div class="column is-half"> {% endblock %}
<h1 class="title">Profile</h1>
{% if form.non_field_errors %} {% block panel %}
<p class="notification is-danger">{{ form.non_field_errors }}</p> {% if form.non_field_errors %}
{% endif %} <p class="notification is-danger">{{ form.non_field_errors }}</p>
<form name="edit-profile" action="/edit-profile/" method="post" enctype="multipart/form-data"> {% endif %}
{% csrf_token %} <form name="edit-profile" action="/edit-profile/" method="post" enctype="multipart/form-data">
<div class="block"> {% csrf_token %}
<label class="label" for="id_avatar">Avatar:</label> <div class="block">
{{ form.avatar }} <label class="label" for="id_avatar">Avatar:</label>
{% for error in form.avatar.errors %} {{ form.avatar }}
<p class="help is-danger">{{ error | escape }}</p> {% for error in form.avatar.errors %}
{% endfor %} <p class="help is-danger">{{ error | escape }}</p>
</div> {% endfor %}
<div class="block"> </div>
<label class="label" for="id_name">Display name:</label> <div class="block">
{{ form.name }} <label class="label" for="id_name">Display name:</label>
{% for error in form.name.errors %} {{ form.name }}
<p class="help is-danger">{{ error | escape }}</p> {% for error in form.name.errors %}
{% endfor %} <p class="help is-danger">{{ error | escape }}</p>
</div> {% endfor %}
<div class="block"> </div>
<label class="label" for="id_summary">Summary:</label> <div class="block">
{{ form.summary }} <label class="label" for="id_summary">Summary:</label>
{% for error in form.summary.errors %} {{ form.summary }}
<p class="help is-danger">{{ error | escape }}</p> {% for error in form.summary.errors %}
{% endfor %} <p class="help is-danger">{{ error | escape }}</p>
</div> {% endfor %}
<div class="block"> </div>
<label class="label" for="id_email">Email address:</label> <div class="block">
{{ form.email }} <label class="label" for="id_email">Email address:</label>
{% for error in form.email.errors %} {{ form.email }}
<p class="help is-danger">{{ error | escape }}</p> {% for error in form.email.errors %}
{% endfor %} <p class="help is-danger">{{ error | escape }}</p>
</div> {% endfor %}
<div class="block"> </div>
<label class="checkbox label" for="id_manually_approves_followers"> <div class="block">
Manually approve followers: <label class="checkbox label" for="id_manually_approves_followers">
{{ form.manually_approves_followers }} Manually approve followers:
</label> {{ form.manually_approves_followers }}
</div> </label>
<button class="button is-primary" type="submit">Save</button> </div>
</form> <button class="button is-primary" type="submit">Save</button>
</div> </form>
<div class="column is-half">
<div class="block">
<h2 class="title">Change password</h2>
<form name="edit-profile" action="/change-password/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="block">
<label class="label" for="id_password">New password:</label>
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password">
</div>
<div class="block">
<label class="label" for="id_confirm_password">Confirm password:</label>
<input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password">
</div>
<button class="button is-primary" type="submit">Change password</button>
</form>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,31 @@
{% extends 'layout.html' %}
{% block content %}
<header class="block column is-offset-one-quarter pl-1">
<h1 class="title">{% block header %}{% endblock %}</h1>
</header>
<div class="block columns">
<nav class="menu column is-one-quarter">
<h2 class="menu-label">Account</h2>
<ul class="menu-list">
<li>
<a href="/edit-profile"{% if '/edit-profile' in request.path %} class="is-active" aria-selected="true"{% endif %}>Profile</a>
</li>
<li>
<a href="/change-password"{% if '/change-password' in request.path %} class="is-active" aria-selected="true"{% endif %}>Change password</a>
</li>
</ul>
<h2 class="menu-label">Relationships</h2>
<ul class="menu-list">
<li>
<a href="/block"{% if '/block' in request.path %} class="is-active" aria-selected="true"{% endif %}>Blocked users</a>
</li>
</ul>
</nav>
<div class="column content">
{% block panel %}{% endblock %}
</div>
</div>
{% endblock %}

View File

@ -1,11 +1,11 @@
{% if not user in request.user.blocks.all %} {% if not user in request.user.blocks.all %}
<form name="blocks" method="post" action="/block/{{ user.id }}"> <form name="blocks" method="post" action="/block/{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-danger is-light is-small is-fullwidth" type="submit">Block</button> <button class="button is-danger is-light is-small {{ class }}" type="submit">Block</button>
</form> </form>
{% else %} {% else %}
<form name="unblocks" method="post" action="/unblock/{{ user.id }}"> <form name="unblocks" method="post" action="/unblock/{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-small is-fullwidth" type="submit">Un-block</button> <button class="button is-small {{ class }}" type="submit">Un-block</button>
</form> </form>
{% endif %} {% endif %}

View File

@ -42,83 +42,6 @@ class AuthenticationViews(TestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
def test_password_reset_request(self):
''' there are so many views, this just makes sure it LOADS '''
view = views.PasswordResetRequest.as_view()
request = self.factory.get('')
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
self.assertEqual(result.template_name, 'password_reset_request.html')
self.assertEqual(result.status_code, 200)
def test_password_reset_request_post(self):
''' send 'em an email '''
request = self.factory.post('', {'email': 'aa@bb.ccc'})
view = views.PasswordResetRequest.as_view()
resp = view(request)
self.assertEqual(resp.status_code, 302)
request = self.factory.post('', {'email': 'mouse@mouse.com'})
with patch('bookwyrm.emailing.send_email.delay'):
resp = view(request)
self.assertEqual(resp.template_name, 'password_reset_request.html')
self.assertEqual(
models.PasswordReset.objects.get().user, self.local_user)
def test_password_reset(self):
''' there are so many views, this just makes sure it LOADS '''
view = views.PasswordReset.as_view()
code = models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.get('')
request.user = self.anonymous_user
result = view(request, code.code)
self.assertIsInstance(result, TemplateResponse)
self.assertEqual(result.template_name, 'password_reset.html')
self.assertEqual(result.status_code, 200)
def test_password_reset_post(self):
''' reset from code '''
view = views.PasswordReset.as_view()
code = models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hi'
})
with patch('bookwyrm.views.password.login'):
resp = view(request, code.code)
self.assertEqual(resp.status_code, 302)
self.assertFalse(models.PasswordReset.objects.exists())
def test_password_reset_wrong_code(self):
''' reset from code '''
view = views.PasswordReset.as_view()
models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hi'
})
resp = view(request, 'jhgdkfjgdf')
self.assertEqual(resp.template_name, 'password_reset.html')
self.assertTrue(models.PasswordReset.objects.exists())
def test_password_reset_mismatch(self):
''' reset from code '''
view = views.PasswordReset.as_view()
code = models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hihi'
})
resp = view(request, code.code)
self.assertEqual(resp.template_name, 'password_reset.html')
self.assertTrue(models.PasswordReset.objects.exists())
def test_register(self): def test_register(self):
''' create a user ''' ''' create a user '''
view = views.Register.as_view() view = views.Register.as_view()
@ -274,29 +197,3 @@ class AuthenticationViews(TestCase):
with self.assertRaises(Http404): with self.assertRaises(Http404):
response = view(request) response = view(request)
self.assertEqual(models.User.objects.count(), 2) self.assertEqual(models.User.objects.count(), 2)
def test_password_change(self):
''' change password '''
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hi'
})
request.user = self.local_user
with patch('bookwyrm.views.password.login'):
view(request)
self.assertNotEqual(self.local_user.password, password_hash)
def test_password_change_mismatch(self):
''' change password '''
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hihi'
})
request.user = self.local_user
view(request)
self.assertEqual(self.local_user.password, password_hash)

View File

@ -0,0 +1,136 @@
''' test for app action functionality '''
from unittest.mock import patch
from django.contrib.auth.models import AnonymousUser
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import models, views
class PasswordViews(TestCase):
''' view user and edit profile '''
def setUp(self):
''' we need basic test data and mocks '''
self.factory = RequestFactory()
self.local_user = models.User.objects.create_user(
'mouse@local.com', 'mouse@mouse.com', 'password',
local=True, localname='mouse')
self.anonymous_user = AnonymousUser
self.anonymous_user.is_authenticated = False
def test_password_reset_request(self):
''' there are so many views, this just makes sure it LOADS '''
view = views.PasswordResetRequest.as_view()
request = self.factory.get('')
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
self.assertEqual(result.template_name, 'password_reset_request.html')
self.assertEqual(result.status_code, 200)
def test_password_reset_request_post(self):
''' send 'em an email '''
request = self.factory.post('', {'email': 'aa@bb.ccc'})
view = views.PasswordResetRequest.as_view()
resp = view(request)
self.assertEqual(resp.status_code, 302)
request = self.factory.post('', {'email': 'mouse@mouse.com'})
with patch('bookwyrm.emailing.send_email.delay'):
resp = view(request)
self.assertEqual(resp.template_name, 'password_reset_request.html')
self.assertEqual(
models.PasswordReset.objects.get().user, self.local_user)
def test_password_reset(self):
''' there are so many views, this just makes sure it LOADS '''
view = views.PasswordReset.as_view()
code = models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.get('')
request.user = self.anonymous_user
result = view(request, code.code)
self.assertIsInstance(result, TemplateResponse)
self.assertEqual(result.template_name, 'password_reset.html')
self.assertEqual(result.status_code, 200)
def test_password_reset_post(self):
''' reset from code '''
view = views.PasswordReset.as_view()
code = models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hi'
})
with patch('bookwyrm.views.password.login'):
resp = view(request, code.code)
self.assertEqual(resp.status_code, 302)
self.assertFalse(models.PasswordReset.objects.exists())
def test_password_reset_wrong_code(self):
''' reset from code '''
view = views.PasswordReset.as_view()
models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hi'
})
resp = view(request, 'jhgdkfjgdf')
self.assertEqual(resp.template_name, 'password_reset.html')
self.assertTrue(models.PasswordReset.objects.exists())
def test_password_reset_mismatch(self):
''' reset from code '''
view = views.PasswordReset.as_view()
code = models.PasswordReset.objects.create(user=self.local_user)
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hihi'
})
resp = view(request, code.code)
self.assertEqual(resp.template_name, 'password_reset.html')
self.assertTrue(models.PasswordReset.objects.exists())
def test_password_change_get(self):
''' there are so many views, this just makes sure it LOADS '''
view = views.ChangePassword.as_view()
request = self.factory.get('')
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
self.assertEqual(result.template_name, 'change_password.html')
self.assertEqual(result.status_code, 200)
def test_password_change(self):
''' change password '''
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hi'
})
request.user = self.local_user
with patch('bookwyrm.views.password.login'):
view(request)
self.assertNotEqual(self.local_user.password, password_hash)
def test_password_change_mismatch(self):
''' change password '''
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post('', {
'password': 'hi',
'confirm-password': 'hihi'
})
request.user = self.local_user
view(request)
self.assertEqual(self.local_user.password, password_hash)

View File

@ -47,7 +47,7 @@ urlpatterns = [
re_path(r'^password-reset/?$', views.PasswordResetRequest.as_view()), re_path(r'^password-reset/?$', views.PasswordResetRequest.as_view()),
re_path(r'^password-reset/(?P<code>[A-Za-z0-9]+)/?$', re_path(r'^password-reset/(?P<code>[A-Za-z0-9]+)/?$',
views.PasswordReset.as_view()), views.PasswordReset.as_view()),
re_path(r'^change-password/?$', views.ChangePassword), re_path(r'^change-password/?$', views.ChangePassword.as_view()),
# invites # invites
re_path(r'^invite/?$', views.ManageInvites.as_view()), re_path(r'^invite/?$', views.ManageInvites.as_view()),
@ -137,5 +137,6 @@ urlpatterns = [
re_path(r'^accept-follow-request/?$', views.accept_follow_request), re_path(r'^accept-follow-request/?$', views.accept_follow_request),
re_path(r'^delete-follow-request/?$', views.delete_follow_request), re_path(r'^delete-follow-request/?$', views.delete_follow_request),
re_path(r'^block/?$', views.Block.as_view()),
re_path(r'^block/(?P<user_id>\d+)/?$', views.Block.as_view()), re_path(r'^block/(?P<user_id>\d+)/?$', views.Block.as_view()),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -1,6 +1,7 @@
''' views for actions you can take in the application ''' ''' views for actions you can take in the application '''
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import View from django.views import View
@ -13,6 +14,8 @@ class Block(View):
''' blocking users ''' ''' blocking users '''
def get(self, request): def get(self, request):
''' list of blocked users? ''' ''' list of blocked users? '''
return TemplateResponse(
request, 'blocks.html', {'title': 'Blocked Users'})
def post(self, request, user_id): def post(self, request, user_id):
''' block a user ''' ''' block a user '''
@ -26,4 +29,4 @@ class Block(View):
privacy='direct', privacy='direct',
direct_recipients=[to_block] direct_recipients=[to_block]
) )
return redirect('/blocks') return redirect('/block')

View File

@ -88,6 +88,14 @@ class PasswordReset(View):
@method_decorator(login_required, name='dispatch') @method_decorator(login_required, name='dispatch')
class ChangePassword(View): class ChangePassword(View):
''' change password as logged in user ''' ''' change password as logged in user '''
def get(self, request):
''' change password page '''
data = {
'title': 'Change Password',
'user': request.user,
}
return TemplateResponse(request, 'change_password.html', data)
def post(self, request): def post(self, request):
''' allow a user to change their password ''' ''' allow a user to change their password '''
new_password = request.POST.get('password') new_password = request.POST.get('password')

View File

@ -147,14 +147,11 @@ class Following(View):
class EditUser(View): class EditUser(View):
''' edit user view ''' ''' edit user view '''
def get(self, request): def get(self, request):
''' profile page for a user ''' ''' edit profile page for a user '''
user = request.user
form = forms.EditUserForm(instance=request.user)
data = { data = {
'title': 'Edit profile', 'title': 'Edit profile',
'form': form, 'form': forms.EditUserForm(instance=request.user),
'user': user, 'user': request.user,
} }
return TemplateResponse(request, 'edit_user.html', data) return TemplateResponse(request, 'edit_user.html', data)