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' %}
{% block content %}
<div class="block columns">
<div class="column is-half">
<h1 class="title">Profile</h1>
{% if form.non_field_errors %}
<p class="notification is-danger">{{ form.non_field_errors }}</p>
{% endif %}
<form name="edit-profile" action="/edit-profile/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="block">
<label class="label" for="id_avatar">Avatar:</label>
{{ form.avatar }}
{% for error in form.avatar.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="label" for="id_name">Display name:</label>
{{ form.name }}
{% for error in form.name.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="label" for="id_summary">Summary:</label>
{{ form.summary }}
{% for error in form.summary.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="label" for="id_email">Email address:</label>
{{ form.email }}
{% for error in form.email.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="checkbox label" for="id_manually_approves_followers">
Manually approve followers:
{{ form.manually_approves_followers }}
</label>
</div>
<button class="button is-primary" type="submit">Save</button>
</form>
</div>
<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>
{% extends 'preferences_layout.html' %}
{% block header %}
Edit Profile
{% endblock %}
{% block panel %}
{% if form.non_field_errors %}
<p class="notification is-danger">{{ form.non_field_errors }}</p>
{% endif %}
<form name="edit-profile" action="/edit-profile/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="block">
<label class="label" for="id_avatar">Avatar:</label>
{{ form.avatar }}
{% for error in form.avatar.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="label" for="id_name">Display name:</label>
{{ form.name }}
{% for error in form.name.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="label" for="id_summary">Summary:</label>
{{ form.summary }}
{% for error in form.summary.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="label" for="id_email">Email address:</label>
{{ form.email }}
{% for error in form.email.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<label class="checkbox label" for="id_manually_approves_followers">
Manually approve followers:
{{ form.manually_approves_followers }}
</label>
</div>
<button class="button is-primary" type="submit">Save</button>
</form>
{% 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 %}
<form name="blocks" method="post" action="/block/{{ user.id }}">
{% 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>
{% else %}
<form name="unblocks" method="post" action="/unblock/{{ user.id }}">
{% 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>
{% endif %}

View File

@ -42,83 +42,6 @@ class AuthenticationViews(TestCase):
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):
''' create a user '''
view = views.Register.as_view()
@ -274,29 +197,3 @@ class AuthenticationViews(TestCase):
with self.assertRaises(Http404):
response = view(request)
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/(?P<code>[A-Za-z0-9]+)/?$',
views.PasswordReset.as_view()),
re_path(r'^change-password/?$', views.ChangePassword),
re_path(r'^change-password/?$', views.ChangePassword.as_view()),
# invites
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'^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()),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -1,6 +1,7 @@
''' views for actions you can take in the application '''
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
@ -13,6 +14,8 @@ class Block(View):
''' blocking users '''
def get(self, request):
''' list of blocked users? '''
return TemplateResponse(
request, 'blocks.html', {'title': 'Blocked Users'})
def post(self, request, user_id):
''' block a user '''
@ -26,4 +29,4 @@ class Block(View):
privacy='direct',
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')
class ChangePassword(View):
''' 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):
''' allow a user to change their password '''
new_password = request.POST.get('password')

View File

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