mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2025-01-03 05:48:44 +00:00
Moves user views to class view
This commit is contained in:
parent
8693895bc6
commit
85d01d5df0
11 changed files with 316 additions and 269 deletions
|
@ -82,7 +82,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/user-edit" class="navbar-item">
|
<a href="/edit-profile" class="navbar-item">
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% if is_self %}
|
{% if is_self %}
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<a href="/user-edit/">
|
<a href="/edit-profile">
|
||||||
<span class="icon icon-pencil">
|
<span class="icon icon-pencil">
|
||||||
<span class="is-sr-only">Edit profile</span>
|
<span class="is-sr-only">Edit profile</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -221,83 +221,6 @@ class Views(TestCase):
|
||||||
response.context_data['user_results'][0], self.local_user)
|
response.context_data['user_results'][0], self.local_user)
|
||||||
|
|
||||||
|
|
||||||
def test_import_page(self):
|
|
||||||
''' there are so many views, this just makes sure it LOADS '''
|
|
||||||
request = self.factory.get('')
|
|
||||||
request.user = self.local_user
|
|
||||||
result = views.import_page(request)
|
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
|
||||||
self.assertEqual(result.template_name, 'import.html')
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
def test_import_status(self):
|
|
||||||
''' there are so many views, this just makes sure it LOADS '''
|
|
||||||
import_job = models.ImportJob.objects.create(user=self.local_user)
|
|
||||||
request = self.factory.get('')
|
|
||||||
request.user = self.local_user
|
|
||||||
with patch('bookwyrm.tasks.app.AsyncResult') as async_result:
|
|
||||||
async_result.return_value = []
|
|
||||||
result = views.import_status(request, import_job.id)
|
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
|
||||||
self.assertEqual(result.template_name, 'import_status.html')
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_page(self):
|
|
||||||
''' there are so many views, this just makes sure it LOADS '''
|
|
||||||
request = self.factory.get('')
|
|
||||||
request.user = self.local_user
|
|
||||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
|
||||||
is_api.return_value = False
|
|
||||||
result = views.user_page(request, 'mouse')
|
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
|
||||||
self.assertEqual(result.template_name, 'user.html')
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
|
||||||
is_api.return_value = True
|
|
||||||
result = views.user_page(request, 'mouse')
|
|
||||||
self.assertIsInstance(result, ActivitypubResponse)
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
def test_followers_page(self):
|
|
||||||
''' there are so many views, this just makes sure it LOADS '''
|
|
||||||
request = self.factory.get('')
|
|
||||||
request.user = self.local_user
|
|
||||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
|
||||||
is_api.return_value = False
|
|
||||||
result = views.followers_page(request, 'mouse')
|
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
|
||||||
self.assertEqual(result.template_name, 'followers.html')
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
|
||||||
is_api.return_value = True
|
|
||||||
result = views.followers_page(request, 'mouse')
|
|
||||||
self.assertIsInstance(result, ActivitypubResponse)
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
def test_following_page(self):
|
|
||||||
''' there are so many views, this just makes sure it LOADS '''
|
|
||||||
request = self.factory.get('')
|
|
||||||
request.user = self.local_user
|
|
||||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
|
||||||
is_api.return_value = False
|
|
||||||
result = views.following_page(request, 'mouse')
|
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
|
||||||
self.assertEqual(result.template_name, 'following.html')
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
|
||||||
is_api.return_value = True
|
|
||||||
result = views.following_page(request, 'mouse')
|
|
||||||
self.assertIsInstance(result, ActivitypubResponse)
|
|
||||||
self.assertEqual(result.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
def test_status_page(self):
|
def test_status_page(self):
|
||||||
''' there are so many views, this just makes sure it LOADS '''
|
''' there are so many views, this just makes sure it LOADS '''
|
||||||
status = models.Status.objects.create(
|
status = models.Status.objects.create(
|
||||||
|
|
99
bookwyrm/tests/views/test_user.py
Normal file
99
bookwyrm/tests/views/test_user.py
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
''' test for app action functionality '''
|
||||||
|
from unittest.mock import patch
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.client import RequestFactory
|
||||||
|
|
||||||
|
from bookwyrm import forms, models, views
|
||||||
|
from bookwyrm.activitypub import ActivitypubResponse
|
||||||
|
|
||||||
|
|
||||||
|
class UserViews(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.mouse', 'password',
|
||||||
|
local=True, localname='mouse')
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_page(self):
|
||||||
|
''' there are so many views, this just makes sure it LOADS '''
|
||||||
|
view = views.User.as_view()
|
||||||
|
request = self.factory.get('')
|
||||||
|
request.user = self.local_user
|
||||||
|
with patch('bookwyrm.views.user.is_api_request') as is_api:
|
||||||
|
is_api.return_value = False
|
||||||
|
result = view(request, 'mouse')
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
self.assertEqual(result.template_name, 'user.html')
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
with patch('bookwyrm.views.user.is_api_request') as is_api:
|
||||||
|
is_api.return_value = True
|
||||||
|
result = view(request, 'mouse')
|
||||||
|
self.assertIsInstance(result, ActivitypubResponse)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
def test_followers_page(self):
|
||||||
|
''' there are so many views, this just makes sure it LOADS '''
|
||||||
|
view = views.Followers.as_view()
|
||||||
|
request = self.factory.get('')
|
||||||
|
request.user = self.local_user
|
||||||
|
with patch('bookwyrm.views.user.is_api_request') as is_api:
|
||||||
|
is_api.return_value = False
|
||||||
|
result = view(request, 'mouse')
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
self.assertEqual(result.template_name, 'followers.html')
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
with patch('bookwyrm.views.user.is_api_request') as is_api:
|
||||||
|
is_api.return_value = True
|
||||||
|
result = view(request, 'mouse')
|
||||||
|
self.assertIsInstance(result, ActivitypubResponse)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
def test_following_page(self):
|
||||||
|
''' there are so many views, this just makes sure it LOADS '''
|
||||||
|
view = views.Following.as_view()
|
||||||
|
request = self.factory.get('')
|
||||||
|
request.user = self.local_user
|
||||||
|
with patch('bookwyrm.views.user.is_api_request') as is_api:
|
||||||
|
is_api.return_value = False
|
||||||
|
result = view(request, 'mouse')
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
self.assertEqual(result.template_name, 'following.html')
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
with patch('bookwyrm.views.user.is_api_request') as is_api:
|
||||||
|
is_api.return_value = True
|
||||||
|
result = view(request, 'mouse')
|
||||||
|
self.assertIsInstance(result, ActivitypubResponse)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
def test_edit_profile_page(self):
|
||||||
|
''' there are so many views, this just makes sure it LOADS '''
|
||||||
|
view = views.EditUser.as_view()
|
||||||
|
request = self.factory.get('')
|
||||||
|
request.user = self.local_user
|
||||||
|
result = view(request)
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
self.assertEqual(result.template_name, 'edit_user.html')
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
def test_edit_user(self):
|
||||||
|
''' use a form to update a user '''
|
||||||
|
view = views.EditUser.as_view()
|
||||||
|
form = forms.EditUserForm(instance=self.local_user)
|
||||||
|
form.data['name'] = 'New Name'
|
||||||
|
request = self.factory.post('', form.data)
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||||
|
view(request)
|
||||||
|
self.assertEqual(self.local_user.name, 'New Name')
|
|
@ -69,16 +69,14 @@ urlpatterns = [
|
||||||
re_path(r'^import/?$', views.Import.as_view()),
|
re_path(r'^import/?$', views.Import.as_view()),
|
||||||
re_path(r'^import/(\d+)/?$', views.ImportStatus.as_view()),
|
re_path(r'^import/(\d+)/?$', views.ImportStatus.as_view()),
|
||||||
|
|
||||||
re_path(r'^user-edit/?$', vviews.edit_profile_page),
|
|
||||||
|
|
||||||
# should return a ui view or activitypub json blob as requested
|
|
||||||
# users
|
# users
|
||||||
re_path(r'%s/?$' % user_path, vviews.user_page),
|
re_path(r'%s/?$' % user_path, views.User.as_view()),
|
||||||
re_path(r'%s\.json$' % local_user_path, vviews.user_page),
|
re_path(r'%s\.json$' % user_path, views.User.as_view()),
|
||||||
re_path(r'%s/?$' % local_user_path, vviews.user_page),
|
re_path(r'%s/shelves/?$' % user_path, vviews.user_shelves_page),
|
||||||
re_path(r'%s/shelves/?$' % local_user_path, vviews.user_shelves_page),
|
re_path(r'%s/followers(.json)?/?$' % user_path, views.Followers.as_view()),
|
||||||
re_path(r'%s/followers(.json)?/?$' % local_user_path, vviews.followers_page),
|
re_path(r'%s/following(.json)?/?$' % user_path, views.Following.as_view()),
|
||||||
re_path(r'%s/following(.json)?/?$' % local_user_path, vviews.following_page),
|
re_path(r'^edit-profile/?$', views.EditUser.as_view()),
|
||||||
|
|
||||||
# statuses
|
# statuses
|
||||||
re_path(r'%s(.json)?/?$' % status_path, vviews.status_page),
|
re_path(r'%s(.json)?/?$' % status_path, vviews.status_page),
|
||||||
|
@ -102,7 +100,6 @@ urlpatterns = [
|
||||||
re_path(r'^search/?$', vviews.search),
|
re_path(r'^search/?$', vviews.search),
|
||||||
|
|
||||||
# internal action endpoints
|
# internal action endpoints
|
||||||
re_path(r'^edit-profile/?$', actions.edit_profile),
|
|
||||||
|
|
||||||
re_path(r'^resolve-book/?$', actions.resolve_book),
|
re_path(r'^resolve-book/?$', actions.resolve_book),
|
||||||
re_path(r'^edit-book/(?P<book_id>\d+)/?$', actions.edit_book),
|
re_path(r'^edit-book/(?P<book_id>\d+)/?$', actions.edit_book),
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
''' views for actions you can take in the application '''
|
''' views for actions you can take in the application '''
|
||||||
from io import BytesIO
|
|
||||||
from uuid import uuid4
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
from dateutil.parser import ParserError
|
from dateutil.parser import ParserError
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
@ -21,50 +16,6 @@ from bookwyrm.broadcast import broadcast
|
||||||
from bookwyrm.vviews import get_user_from_username, get_edition
|
from bookwyrm.vviews import get_user_from_username, get_edition
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@require_POST
|
|
||||||
def edit_profile(request):
|
|
||||||
''' les get fancy with images '''
|
|
||||||
form = forms.EditUserForm(
|
|
||||||
request.POST, request.FILES, instance=request.user)
|
|
||||||
if not form.is_valid():
|
|
||||||
data = {'form': form, 'user': request.user}
|
|
||||||
return TemplateResponse(request, 'edit_user.html', data)
|
|
||||||
|
|
||||||
user = form.save(commit=False)
|
|
||||||
|
|
||||||
if 'avatar' in form.files:
|
|
||||||
# crop and resize avatar upload
|
|
||||||
image = Image.open(form.files['avatar'])
|
|
||||||
target_size = 120
|
|
||||||
width, height = image.size
|
|
||||||
thumbnail_scale = height / (width / target_size) if height > width \
|
|
||||||
else width / (height / target_size)
|
|
||||||
image.thumbnail([thumbnail_scale, thumbnail_scale])
|
|
||||||
width, height = image.size
|
|
||||||
|
|
||||||
width_diff = width - target_size
|
|
||||||
height_diff = height - target_size
|
|
||||||
cropped = image.crop((
|
|
||||||
int(width_diff / 2),
|
|
||||||
int(height_diff / 2),
|
|
||||||
int(width - (width_diff / 2)),
|
|
||||||
int(height - (height_diff / 2))
|
|
||||||
))
|
|
||||||
output = BytesIO()
|
|
||||||
cropped.save(output, format=image.format)
|
|
||||||
ContentFile(output.getvalue())
|
|
||||||
|
|
||||||
# set the name to a hash
|
|
||||||
extension = form.files['avatar'].name.split('.')[-1]
|
|
||||||
filename = '%s.%s' % (uuid4(), extension)
|
|
||||||
user.avatar.save(filename, ContentFile(output.getvalue()))
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
broadcast(user, user.to_update_activity(user))
|
|
||||||
return redirect('/user/%s' % request.user.localname)
|
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
def resolve_book(request):
|
def resolve_book(request):
|
||||||
''' figure out the local path to a book from a remote_id '''
|
''' figure out the local path to a book from a remote_id '''
|
||||||
|
|
|
@ -6,3 +6,4 @@ from .landing import About, Home, Feed, Discover
|
||||||
from .notifications import Notifications
|
from .notifications import Notifications
|
||||||
from .direct_message import DirectMessage
|
from .direct_message import DirectMessage
|
||||||
from .import_data import Import, ImportStatus
|
from .import_data import Import, ImportStatus
|
||||||
|
from .user import User, EditUser, Followers, Following
|
||||||
|
|
|
@ -2,6 +2,21 @@
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
def get_user_from_username(username):
|
||||||
|
''' helper function to resolve a localname or a username to a user '''
|
||||||
|
# raises DoesNotExist if user is now found
|
||||||
|
try:
|
||||||
|
return models.User.objects.get(localname=username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return models.User.objects.get(username=username)
|
||||||
|
|
||||||
|
|
||||||
|
def is_api_request(request):
|
||||||
|
''' check whether a request is asking for html or data '''
|
||||||
|
return 'json' in request.headers.get('Accept') or \
|
||||||
|
request.path[-5:] == '.json'
|
||||||
|
|
||||||
|
|
||||||
def get_activity_feed(
|
def get_activity_feed(
|
||||||
user, privacy, local_only=False, following_only=False,
|
user, privacy, local_only=False, following_only=False,
|
||||||
queryset=models.Status.objects):
|
queryset=models.Status.objects):
|
||||||
|
|
|
@ -94,7 +94,7 @@ class ChangePassword(View):
|
||||||
confirm_password = request.POST.get('confirm-password')
|
confirm_password = request.POST.get('confirm-password')
|
||||||
|
|
||||||
if new_password != confirm_password:
|
if new_password != confirm_password:
|
||||||
return redirect('/user-edit')
|
return redirect('/edit-profile')
|
||||||
|
|
||||||
request.user.set_password(new_password)
|
request.user.set_password(new_password)
|
||||||
request.user.save()
|
request.user.save()
|
||||||
|
|
192
bookwyrm/views/user.py
Normal file
192
bookwyrm/views/user.py
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
''' non-interactive pages '''
|
||||||
|
from io import BytesIO
|
||||||
|
from uuid import uuid4
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
from django.http import HttpResponseNotFound
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
from bookwyrm import forms, models
|
||||||
|
from bookwyrm.activitypub import ActivitypubResponse
|
||||||
|
from bookwyrm.broadcast import broadcast
|
||||||
|
from bookwyrm.settings import PAGE_LENGTH
|
||||||
|
from .helpers import get_activity_feed, get_user_from_username, is_api_request
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable= no-self-use
|
||||||
|
class User(View):
|
||||||
|
''' user profile page '''
|
||||||
|
def get(self, request, username):
|
||||||
|
''' profile page for a user '''
|
||||||
|
try:
|
||||||
|
user = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
# we have a json request
|
||||||
|
return ActivitypubResponse(user.to_activity())
|
||||||
|
# otherwise we're at a UI view
|
||||||
|
|
||||||
|
try:
|
||||||
|
page = int(request.GET.get('page', 1))
|
||||||
|
except ValueError:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
shelf_preview = []
|
||||||
|
|
||||||
|
# only show other shelves that should be visible
|
||||||
|
shelves = user.shelf_set
|
||||||
|
is_self = request.user.id == user.id
|
||||||
|
if not is_self:
|
||||||
|
follower = user.followers.filter(id=request.user.id).exists()
|
||||||
|
if follower:
|
||||||
|
shelves = shelves.filter(privacy__in=['public', 'followers'])
|
||||||
|
else:
|
||||||
|
shelves = shelves.filter(privacy='public')
|
||||||
|
|
||||||
|
for user_shelf in shelves.all():
|
||||||
|
if not user_shelf.books.count():
|
||||||
|
continue
|
||||||
|
shelf_preview.append({
|
||||||
|
'name': user_shelf.name,
|
||||||
|
'local_path': user_shelf.local_path,
|
||||||
|
'books': user_shelf.books.all()[:3],
|
||||||
|
'size': user_shelf.books.count(),
|
||||||
|
})
|
||||||
|
if len(shelf_preview) > 2:
|
||||||
|
break
|
||||||
|
|
||||||
|
# user's posts
|
||||||
|
activities = get_activity_feed(
|
||||||
|
request.user,
|
||||||
|
['public', 'unlisted', 'followers'],
|
||||||
|
queryset=models.Status.objects.filter(user=user)
|
||||||
|
)
|
||||||
|
paginated = Paginator(activities, PAGE_LENGTH)
|
||||||
|
activity_page = paginated.page(page)
|
||||||
|
|
||||||
|
prev_page = next_page = None
|
||||||
|
if activity_page.has_next():
|
||||||
|
next_page = '/user/%s/?page=%d' % \
|
||||||
|
(username, activity_page.next_page_number())
|
||||||
|
if activity_page.has_previous():
|
||||||
|
prev_page = '/user/%s/?page=%d' % \
|
||||||
|
(username, activity_page.previous_page_number())
|
||||||
|
data = {
|
||||||
|
'title': user.name,
|
||||||
|
'user': user,
|
||||||
|
'is_self': is_self,
|
||||||
|
'shelves': shelf_preview,
|
||||||
|
'shelf_count': shelves.count(),
|
||||||
|
'activities': activity_page.object_list,
|
||||||
|
'next': next_page,
|
||||||
|
'prev': prev_page,
|
||||||
|
}
|
||||||
|
|
||||||
|
return TemplateResponse(request, 'user.html', data)
|
||||||
|
|
||||||
|
class Followers(View):
|
||||||
|
''' list of followers view '''
|
||||||
|
def get(self, request, username):
|
||||||
|
''' list of followers '''
|
||||||
|
try:
|
||||||
|
user = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
return ActivitypubResponse(
|
||||||
|
user.to_followers_activity(**request.GET))
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'title': '%s: followers' % user.name,
|
||||||
|
'user': user,
|
||||||
|
'is_self': request.user.id == user.id,
|
||||||
|
'followers': user.followers.all(),
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, 'followers.html', data)
|
||||||
|
|
||||||
|
class Following(View):
|
||||||
|
''' list of following view '''
|
||||||
|
def get(self, request, username):
|
||||||
|
''' list of followers '''
|
||||||
|
try:
|
||||||
|
user = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
return ActivitypubResponse(
|
||||||
|
user.to_following_activity(**request.GET))
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'title': '%s: following' % user.name,
|
||||||
|
'user': user,
|
||||||
|
'is_self': request.user.id == user.id,
|
||||||
|
'following': user.following.all(),
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, 'following.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class EditUser(View):
|
||||||
|
''' edit user view '''
|
||||||
|
def get(self, request):
|
||||||
|
''' profile page for a user '''
|
||||||
|
user = request.user
|
||||||
|
|
||||||
|
form = forms.EditUserForm(instance=request.user)
|
||||||
|
data = {
|
||||||
|
'title': 'Edit profile',
|
||||||
|
'form': form,
|
||||||
|
'user': user,
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, 'edit_user.html', data)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
''' les get fancy with images '''
|
||||||
|
form = forms.EditUserForm(
|
||||||
|
request.POST, request.FILES, instance=request.user)
|
||||||
|
if not form.is_valid():
|
||||||
|
data = {'form': form, 'user': request.user}
|
||||||
|
return TemplateResponse(request, 'edit_user.html', data)
|
||||||
|
|
||||||
|
user = form.save(commit=False)
|
||||||
|
|
||||||
|
if 'avatar' in form.files:
|
||||||
|
# crop and resize avatar upload
|
||||||
|
image = Image.open(form.files['avatar'])
|
||||||
|
target_size = 120
|
||||||
|
width, height = image.size
|
||||||
|
thumbnail_scale = height / (width / target_size) if height > width \
|
||||||
|
else width / (height / target_size)
|
||||||
|
image.thumbnail([thumbnail_scale, thumbnail_scale])
|
||||||
|
width, height = image.size
|
||||||
|
|
||||||
|
width_diff = width - target_size
|
||||||
|
height_diff = height - target_size
|
||||||
|
cropped = image.crop((
|
||||||
|
int(width_diff / 2),
|
||||||
|
int(height_diff / 2),
|
||||||
|
int(width - (width_diff / 2)),
|
||||||
|
int(height - (height_diff / 2))
|
||||||
|
))
|
||||||
|
output = BytesIO()
|
||||||
|
cropped.save(output, format=image.format)
|
||||||
|
ContentFile(output.getvalue())
|
||||||
|
|
||||||
|
# set the name to a hash
|
||||||
|
extension = form.files['avatar'].name.split('.')[-1]
|
||||||
|
filename = '%s.%s' % (uuid4(), extension)
|
||||||
|
user.avatar.save(filename, ContentFile(output.getvalue()))
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
broadcast(user, user.to_update_activity(user))
|
||||||
|
return redirect('/user/%s' % request.user.localname)
|
|
@ -157,121 +157,6 @@ def search(request):
|
||||||
return TemplateResponse(request, 'search_results.html', data)
|
return TemplateResponse(request, 'search_results.html', data)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_GET
|
|
||||||
def user_page(request, username):
|
|
||||||
''' profile page for a user '''
|
|
||||||
try:
|
|
||||||
user = get_user_from_username(username)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
if is_api_request(request):
|
|
||||||
# we have a json request
|
|
||||||
return ActivitypubResponse(user.to_activity())
|
|
||||||
# otherwise we're at a UI view
|
|
||||||
|
|
||||||
try:
|
|
||||||
page = int(request.GET.get('page', 1))
|
|
||||||
except ValueError:
|
|
||||||
page = 1
|
|
||||||
|
|
||||||
shelf_preview = []
|
|
||||||
|
|
||||||
# only show other shelves that should be visible
|
|
||||||
shelves = user.shelf_set
|
|
||||||
is_self = request.user.id == user.id
|
|
||||||
if not is_self:
|
|
||||||
follower = user.followers.filter(id=request.user.id).exists()
|
|
||||||
if follower:
|
|
||||||
shelves = shelves.filter(privacy__in=['public', 'followers'])
|
|
||||||
else:
|
|
||||||
shelves = shelves.filter(privacy='public')
|
|
||||||
|
|
||||||
for user_shelf in shelves.all():
|
|
||||||
if not user_shelf.books.count():
|
|
||||||
continue
|
|
||||||
shelf_preview.append({
|
|
||||||
'name': user_shelf.name,
|
|
||||||
'local_path': user_shelf.local_path,
|
|
||||||
'books': user_shelf.books.all()[:3],
|
|
||||||
'size': user_shelf.books.count(),
|
|
||||||
})
|
|
||||||
if len(shelf_preview) > 2:
|
|
||||||
break
|
|
||||||
|
|
||||||
# user's posts
|
|
||||||
activities = get_activity_feed(
|
|
||||||
request.user,
|
|
||||||
['public', 'unlisted', 'followers'],
|
|
||||||
queryset=models.Status.objects.filter(user=user)
|
|
||||||
)
|
|
||||||
paginated = Paginator(activities, PAGE_LENGTH)
|
|
||||||
activity_page = paginated.page(page)
|
|
||||||
|
|
||||||
prev_page = next_page = None
|
|
||||||
if activity_page.has_next():
|
|
||||||
next_page = '/user/%s/?page=%d' % \
|
|
||||||
(username, activity_page.next_page_number())
|
|
||||||
if activity_page.has_previous():
|
|
||||||
prev_page = '/user/%s/?page=%d' % \
|
|
||||||
(username, activity_page.previous_page_number())
|
|
||||||
data = {
|
|
||||||
'title': user.name,
|
|
||||||
'user': user,
|
|
||||||
'is_self': is_self,
|
|
||||||
'shelves': shelf_preview,
|
|
||||||
'shelf_count': shelves.count(),
|
|
||||||
'activities': activity_page.object_list,
|
|
||||||
'next': next_page,
|
|
||||||
'prev': prev_page,
|
|
||||||
}
|
|
||||||
|
|
||||||
return TemplateResponse(request, 'user.html', data)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_GET
|
|
||||||
def followers_page(request, username):
|
|
||||||
''' list of followers '''
|
|
||||||
try:
|
|
||||||
user = get_user_from_username(username)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
if is_api_request(request):
|
|
||||||
return ActivitypubResponse(user.to_followers_activity(**request.GET))
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'title': '%s: followers' % user.name,
|
|
||||||
'user': user,
|
|
||||||
'is_self': request.user.id == user.id,
|
|
||||||
'followers': user.followers.all(),
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, 'followers.html', data)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_GET
|
|
||||||
def following_page(request, username):
|
|
||||||
''' list of followers '''
|
|
||||||
try:
|
|
||||||
user = get_user_from_username(username)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
if is_api_request(request):
|
|
||||||
return ActivitypubResponse(user.to_following_activity(**request.GET))
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'title': '%s: following' % user.name,
|
|
||||||
'user': user,
|
|
||||||
'is_self': request.user.id == user.id,
|
|
||||||
'following': user.following.all(),
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, 'following.html', data)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@require_GET
|
@require_GET
|
||||||
def status_page(request, username, status_id):
|
def status_page(request, username, status_id):
|
||||||
|
@ -327,22 +212,6 @@ def replies_page(request, username, status_id):
|
||||||
|
|
||||||
return ActivitypubResponse(status.to_replies(**request.GET))
|
return ActivitypubResponse(status.to_replies(**request.GET))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@require_GET
|
|
||||||
def edit_profile_page(request):
|
|
||||||
''' profile page for a user '''
|
|
||||||
user = request.user
|
|
||||||
|
|
||||||
form = forms.EditUserForm(instance=request.user)
|
|
||||||
data = {
|
|
||||||
'title': 'Edit profile',
|
|
||||||
'form': form,
|
|
||||||
'user': user,
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, 'edit_user.html', data)
|
|
||||||
|
|
||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
def book_page(request, book_id):
|
def book_page(request, book_id):
|
||||||
''' info about a book '''
|
''' info about a book '''
|
||||||
|
|
Loading…
Reference in a new issue