mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-10-31 22:19:00 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
877c90b087
22 changed files with 2416 additions and 1546 deletions
|
@ -37,9 +37,13 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
||||||
""" set the identifier """
|
""" set the identifier """
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
if not self.identifier:
|
if not self.identifier:
|
||||||
slug = re.sub(r"[^\w]", "", self.name).lower()
|
self.identifier = self.get_identifier()
|
||||||
self.identifier = "%s-%d" % (slug, self.id)
|
super().save(*args, **kwargs, broadcast=False)
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
def get_identifier(self):
|
||||||
|
""" custom-shelf-123 for the url """
|
||||||
|
slug = re.sub(r"[^\w]", "", self.name).lower()
|
||||||
|
return "{:s}-{:d}".format(slug, self.id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def collection_queryset(self):
|
def collection_queryset(self):
|
||||||
|
@ -49,7 +53,8 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
||||||
def get_remote_id(self):
|
def get_remote_id(self):
|
||||||
""" shelf identifier instead of id """
|
""" shelf identifier instead of id """
|
||||||
base_path = self.user.remote_id
|
base_path = self.user.remote_id
|
||||||
return "%s/shelf/%s" % (base_path, self.identifier)
|
identifier = self.identifier or self.get_identifier()
|
||||||
|
return "%s/books/%s" % (base_path, identifier)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
""" user/shelf unqiueness """
|
""" user/shelf unqiueness """
|
||||||
|
|
|
@ -165,32 +165,11 @@
|
||||||
{% include 'snippets/readthrough.html' with readthrough=readthrough %}
|
{% include 'snippets/readthrough.html' with readthrough=readthrough %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<section class="box">
|
<section class="box">
|
||||||
{% include 'snippets/create_status.html' with book=book hide_cover=True %}
|
{% include 'snippets/create_status.html' with book=book hide_cover=True %}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="block">
|
|
||||||
<form name="tag" action="/tag/" method="post">
|
|
||||||
<label for="tags" class="is-3">{% trans "Tags" %}</label>
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
|
||||||
<input id="tags" class="input" type="text" name="name">
|
|
||||||
<button class="button" type="submit">{% trans "Add tag" %}</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="block">
|
|
||||||
<div class="field is-grouped is-grouped-multiline">
|
|
||||||
{% for tag in tags %}
|
|
||||||
{% include 'snippets/tag.html' with book=book tag=tag user_tags=user_tags %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-one-fifth">
|
<div class="column is-one-fifth">
|
||||||
{% if book.subjects %}
|
{% if book.subjects %}
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<a href="{% url 'user-shelves' request.user.localname %}" class="navbar-item">
|
<a href="{% url 'user-shelves' request.user.localname %}" class="navbar-item">
|
||||||
{% trans "Your shelves" %}
|
{% trans "Your books" %}
|
||||||
</a>
|
</a>
|
||||||
<a href="/#feed" class="navbar-item">
|
<a href="/#feed" class="navbar-item">
|
||||||
{% trans "Feed" %}
|
{% trans "Feed" %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends 'components/dropdown.html' %}
|
{% extends 'components/dropdown.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block dropdown-trigger %}
|
{% block dropdown-trigger %}
|
||||||
<span>{% trans "Change shelf" %}</span>
|
<span>{% trans "Move book" %}</span>
|
||||||
<span class="icon icon-arrow-down" aria-hidden="true"></span>
|
<span class="icon icon-arrow-down" aria-hidden="true"></span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
<input type="hidden" name="shelf" value="{{ current.id }}">
|
<input type="hidden" name="shelf" value="{{ current.id }}">
|
||||||
<button class="button is-fullwidth is-small is-danger is-light" type="submit">{% trans "Unshelve" %}</button>
|
<button class="button is-fullwidth is-small is-danger is-light" type="submit">{% trans "Remove" %}</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
|
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
|
||||||
<input type="hidden" name="shelf" value="{{ active_shelf.shelf.id }}">
|
<input type="hidden" name="shelf" value="{{ active_shelf.shelf.id }}">
|
||||||
<button class="button is-fullwidth is-small is-danger is-light" type="submit">{% trans "Unshelve" %}</button>
|
<button class="button is-fullwidth is-small is-danger is-light" type="submit">{% blocktrans with name=active_shelf.shelf.name %}Remove from {{ name }}{% endblocktrans %}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
6
bookwyrm/templates/user/books_header.html
Normal file
6
bookwyrm/templates/user/books_header.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% if is_self %}
|
||||||
|
{% trans "Your books" %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with username=user.display_name %}{{ username }}'s books{% endblocktrans %}
|
||||||
|
{% endif %}
|
|
@ -3,14 +3,14 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% include 'user/books_header.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<header class="columns">
|
<header class="columns">
|
||||||
<h1 class="title">
|
<h1 class="title">
|
||||||
{% if is_self %}
|
{% include 'user/books_header.html' %}
|
||||||
{% trans "Your Shelves" %}
|
|
||||||
{% else %}
|
|
||||||
{% blocktrans with username=user.display_name %}{{ username }}: Shelves{% endblocktrans %}
|
|
||||||
{% endif %}
|
|
||||||
</h1>
|
</h1>
|
||||||
</header>
|
</header>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,10 +20,13 @@
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
|
<li class="{% if shelf.identifier == 'all' %}is-active{% endif %}">
|
||||||
|
<a href="{% url 'user-shelves' user|username %}"{% if shelf.identifier == 'all' %} aria-current="page"{% endif %}>{% trans "All books" %}</a>
|
||||||
|
</li>
|
||||||
{% for shelf_tab in shelves %}
|
{% for shelf_tab in shelves %}
|
||||||
<li class="{% if shelf_tab.identifier == shelf.identifier %}is-active{% endif %}">
|
<li class="{% if shelf_tab.identifier == shelf.identifier %}is-active{% endif %}">
|
||||||
<a href="/user/{{ user | username }}/shelf/{{ shelf_tab.identifier }}"{% if shelf_tab.identifier == shelf.identifier %} aria-current="page"{% endif %}>{% if shelf_tab.identifier == 'to-read' %}{% trans "To Read" %}{% elif shelf_tab.identifier == 'reading' %}{% trans "Currently Reading" %}{% elif shelf_tab.identifier == 'read' %}{% trans "Read" %}{% else %}{{ shelf_tab.name }}{% endif %}</a>
|
<a href="{{ shelf_tab.local_path }}"{% if shelf_tab.identifier == shelf.identifier %} aria-current="page"{% endif %}>{% if shelf_tab.identifier == 'to-read' %}{% trans "To Read" %}{% elif shelf_tab.identifier == 'reading' %}{% trans "Currently Reading" %}{% elif shelf_tab.identifier == 'read' %}{% trans "Read" %}{% else %}{{ shelf_tab.name }}{% endif %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,7 +53,7 @@
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
{% if is_self %}
|
{% if is_self and shelf.id %}
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
{% trans "Edit shelf" as button_text %}
|
{% trans "Edit shelf" as button_text %}
|
||||||
{% include 'snippets/toggle/open_button.html' with text=button_text icon="pencil" controls_text="edit-shelf-form" focus="edit-shelf-form-header" %}
|
{% include 'snippets/toggle/open_button.html' with text=button_text icon="pencil" controls_text="edit-shelf-form" focus="edit-shelf-form-header" %}
|
||||||
|
@ -81,7 +84,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% for book in books %}
|
{% for book in books %}
|
||||||
{% with book=book.book %}
|
|
||||||
<tr class="book-preview">
|
<tr class="book-preview">
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a>
|
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a>
|
||||||
|
@ -113,13 +115,12 @@
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% trans "This shelf is empty." %}</p>
|
<p>{% trans "This shelf is empty." %}</p>
|
||||||
{% if shelf.editable %}
|
{% if shelf.id and shelf.editable %}
|
||||||
<form name="delete-shelf" action="/delete-shelf/{{ shelf.id }}" method="post">
|
<form name="delete-shelf" action="/delete-shelf/{{ shelf.id }}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends 'user/user_layout.html' %}
|
{% extends 'user/user_layout.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load bookwyrm_tags %}
|
||||||
|
|
||||||
{% block title %}{{ user.display_name }}{% endblock %}
|
{% block title %}{{ user.display_name }}{% endblock %}
|
||||||
|
|
||||||
|
@ -23,12 +24,14 @@
|
||||||
{% block panel %}
|
{% block panel %}
|
||||||
{% if user.bookwyrm_user %}
|
{% if user.bookwyrm_user %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<h2 class="title">{% trans "Shelves" %}</h2>
|
<h2 class="title">
|
||||||
|
{% include 'user/books_header.html' %}
|
||||||
|
</h2>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
{% for shelf in shelves %}
|
{% for shelf in shelves %}
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<h3>{{ shelf.name }}
|
<h3>{{ shelf.name }}
|
||||||
{% if shelf.size > 3 %}<small>(<a href="{{ shelf.local_path }}">{% blocktrans with size=shelf.size %}See all {{ size }}{% endblocktrans %}</a>)</small>{% endif %}</h3>
|
{% if shelf.size > 3 %}<small>(<a href="{{ shelf.local_path }}">{% blocktrans with size=shelf.size %}View all {{ size }}{% endblocktrans %}</a>)</small>{% endif %}</h3>
|
||||||
<div class="is-mobile field is-grouped">
|
<div class="is-mobile field is-grouped">
|
||||||
{% for book in shelf.books %}
|
{% for book in shelf.books %}
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
@ -41,7 +44,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<small><a href="{{ user.local_path }}/shelves">{% blocktrans %}See all {{ shelf_count }} shelves{% endblocktrans %}</a></small>
|
<small><a href="{% url 'user-shelves' user|username %}">{% trans "View all books" %}</a></small>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% with user|username as username %}
|
{% with user|username as username %}
|
||||||
{% if 'user/'|add:username|add:'/shelf' not in request.path and 'user/'|add:username|add:'/shelves' not in request.path %}
|
{% if 'user/'|add:username|add:'/books' not in request.path and 'user/'|add:username|add:'/shelf' not in request.path %}
|
||||||
<nav class="tabs">
|
<nav class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
{% url 'user-feed' user|username as url %}
|
{% url 'user-feed' user|username as url %}
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
{% if user.shelf_set.exists %}
|
{% if user.shelf_set.exists %}
|
||||||
{% url 'user-shelves' user|username as url %}
|
{% url 'user-shelves' user|username as url %}
|
||||||
<li{% if url in request.path %} class="is-active"{% endif %}>
|
<li{% if url in request.path %} class="is-active"{% endif %}>
|
||||||
<a href="{{ url }}">{% trans "Shelves" %}</a>
|
<a href="{{ url }}">{% trans "Books" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -27,7 +27,7 @@ class Shelf(TestCase):
|
||||||
shelf = models.Shelf.objects.create(
|
shelf = models.Shelf.objects.create(
|
||||||
name="Test Shelf", identifier="test-shelf", user=self.local_user
|
name="Test Shelf", identifier="test-shelf", user=self.local_user
|
||||||
)
|
)
|
||||||
expected_id = "https://%s/user/mouse/shelf/test-shelf" % settings.DOMAIN
|
expected_id = "https://%s/user/mouse/books/test-shelf" % settings.DOMAIN
|
||||||
self.assertEqual(shelf.get_remote_id(), expected_id)
|
self.assertEqual(shelf.get_remote_id(), expected_id)
|
||||||
models.Shelf.broadcast = real_broadcast
|
models.Shelf.broadcast = real_broadcast
|
||||||
|
|
||||||
|
|
|
@ -138,16 +138,8 @@ urlpatterns = [
|
||||||
views.Following.as_view(),
|
views.Following.as_view(),
|
||||||
name="user-following",
|
name="user-following",
|
||||||
),
|
),
|
||||||
re_path(r"%s/shelves/?$" % user_path, views.user_shelves_page, name="user-shelves"),
|
|
||||||
re_path(r"%s/lists/?$" % user_path, views.UserLists.as_view(), name="user-lists"),
|
|
||||||
# goals
|
|
||||||
re_path(
|
|
||||||
r"%s/goal/(?P<year>\d{4})/?$" % user_path,
|
|
||||||
views.Goal.as_view(),
|
|
||||||
name="user-goal",
|
|
||||||
),
|
|
||||||
re_path(r"^hide-goal/?$", views.hide_goal, name="hide-goal"),
|
|
||||||
# lists
|
# lists
|
||||||
|
re_path(r"%s/lists/?$" % user_path, views.UserLists.as_view(), name="user-lists"),
|
||||||
re_path(r"^list/?$", views.Lists.as_view(), name="lists"),
|
re_path(r"^list/?$", views.Lists.as_view(), name="lists"),
|
||||||
re_path(r"^list/(?P<list_id>\d+)(.json)?/?$", views.List.as_view(), name="list"),
|
re_path(r"^list/(?P<list_id>\d+)(.json)?/?$", views.List.as_view(), name="list"),
|
||||||
re_path(r"^list/add-book/?$", views.list.add_book, name="list-add-book"),
|
re_path(r"^list/add-book/?$", views.list.add_book, name="list-add-book"),
|
||||||
|
@ -159,6 +151,29 @@ urlpatterns = [
|
||||||
re_path(
|
re_path(
|
||||||
r"^list/(?P<list_id>\d+)/curate/?$", views.Curate.as_view(), name="list-curate"
|
r"^list/(?P<list_id>\d+)/curate/?$", views.Curate.as_view(), name="list-curate"
|
||||||
),
|
),
|
||||||
|
# Uyser books
|
||||||
|
re_path(r"%s/books/?$" % user_path, views.Shelf.as_view(), name="user-shelves"),
|
||||||
|
re_path(
|
||||||
|
r"^%s/(helf|books)/(?P<shelf_identifier>[\w-]+)(.json)?/?$" % user_path,
|
||||||
|
views.Shelf.as_view(),
|
||||||
|
name="shelf",
|
||||||
|
),
|
||||||
|
re_path(
|
||||||
|
r"^%s/(books|shelf)/(?P<shelf_identifier>[\w-]+)(.json)?/?$" % local_user_path,
|
||||||
|
views.Shelf.as_view(),
|
||||||
|
name="shelf",
|
||||||
|
),
|
||||||
|
re_path(r"^create-shelf/?$", views.create_shelf, name="shelf-create"),
|
||||||
|
re_path(r"^delete-shelf/(?P<shelf_id>\d+)?$", views.delete_shelf),
|
||||||
|
re_path(r"^shelve/?$", views.shelve),
|
||||||
|
re_path(r"^unshelve/?$", views.unshelve),
|
||||||
|
# goals
|
||||||
|
re_path(
|
||||||
|
r"%s/goal/(?P<year>\d{4})/?$" % user_path,
|
||||||
|
views.Goal.as_view(),
|
||||||
|
name="user-goal",
|
||||||
|
),
|
||||||
|
re_path(r"^hide-goal/?$", views.hide_goal, name="hide-goal"),
|
||||||
# preferences
|
# preferences
|
||||||
re_path(r"^preferences/profile/?$", views.EditUser.as_view(), name="prefs-profile"),
|
re_path(r"^preferences/profile/?$", views.EditUser.as_view(), name="prefs-profile"),
|
||||||
re_path(r"^preferences/password/?$", views.ChangePassword.as_view()),
|
re_path(r"^preferences/password/?$", views.ChangePassword.as_view()),
|
||||||
|
@ -199,20 +214,6 @@ urlpatterns = [
|
||||||
re_path(r"^tag/(?P<tag_id>.+)/?$", views.Tag.as_view()),
|
re_path(r"^tag/(?P<tag_id>.+)/?$", views.Tag.as_view()),
|
||||||
re_path(r"^tag/?$", views.AddTag.as_view()),
|
re_path(r"^tag/?$", views.AddTag.as_view()),
|
||||||
re_path(r"^untag/?$", views.RemoveTag.as_view()),
|
re_path(r"^untag/?$", views.RemoveTag.as_view()),
|
||||||
# shelf
|
|
||||||
re_path(
|
|
||||||
r"^%s/shelf/(?P<shelf_identifier>[\w-]+)(.json)?/?$" % user_path,
|
|
||||||
views.Shelf.as_view(),
|
|
||||||
name="shelf",
|
|
||||||
),
|
|
||||||
re_path(
|
|
||||||
r"^%s/shelf/(?P<shelf_identifier>[\w-]+)(.json)?/?$" % local_user_path,
|
|
||||||
views.Shelf.as_view(),
|
|
||||||
),
|
|
||||||
re_path(r"^create-shelf/?$", views.create_shelf, name="shelf-create"),
|
|
||||||
re_path(r"^delete-shelf/(?P<shelf_id>\d+)?$", views.delete_shelf),
|
|
||||||
re_path(r"^shelve/?$", views.shelve),
|
|
||||||
re_path(r"^unshelve/?$", views.unshelve),
|
|
||||||
# reading progress
|
# reading progress
|
||||||
re_path(r"^edit-readthrough/?$", views.edit_readthrough, name="edit-readthrough"),
|
re_path(r"^edit-readthrough/?$", views.edit_readthrough, name="edit-readthrough"),
|
||||||
re_path(r"^delete-readthrough/?$", views.delete_readthrough),
|
re_path(r"^delete-readthrough/?$", views.delete_readthrough),
|
||||||
|
|
|
@ -27,7 +27,7 @@ from .rss_feed import RssFeed
|
||||||
from .password import PasswordResetRequest, PasswordReset, ChangePassword
|
from .password import PasswordResetRequest, PasswordReset, ChangePassword
|
||||||
from .search import Search
|
from .search import Search
|
||||||
from .shelf import Shelf
|
from .shelf import Shelf
|
||||||
from .shelf import user_shelves_page, create_shelf, delete_shelf
|
from .shelf import create_shelf, delete_shelf
|
||||||
from .shelf import shelve, unshelve
|
from .shelf import shelve, unshelve
|
||||||
from .site import Site
|
from .site import Site
|
||||||
from .status import CreateStatus, DeleteStatus
|
from .status import CreateStatus, DeleteStatus
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
""" shelf views"""
|
""" shelf views"""
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
|
@ -6,6 +8,7 @@ from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||||
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.template.response import TemplateResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
|
@ -13,14 +16,14 @@ from bookwyrm import forms, models
|
||||||
from bookwyrm.activitypub import ActivitypubResponse
|
from bookwyrm.activitypub import ActivitypubResponse
|
||||||
from bookwyrm.settings import PAGE_LENGTH
|
from bookwyrm.settings import PAGE_LENGTH
|
||||||
from .helpers import is_api_request, get_edition, get_user_from_username
|
from .helpers import is_api_request, get_edition, get_user_from_username
|
||||||
from .helpers import handle_reading_status
|
from .helpers import handle_reading_status, privacy_filter, object_visible_to_user
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable= no-self-use
|
# pylint: disable= no-self-use
|
||||||
class Shelf(View):
|
class Shelf(View):
|
||||||
""" shelf page """
|
""" shelf page """
|
||||||
|
|
||||||
def get(self, request, username, shelf_identifier):
|
def get(self, request, username, shelf_identifier=None):
|
||||||
""" display a shelf """
|
""" display a shelf """
|
||||||
try:
|
try:
|
||||||
user = get_user_from_username(request.user, username)
|
user = get_user_from_username(request.user, username)
|
||||||
|
@ -32,35 +35,30 @@ class Shelf(View):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
|
shelves = privacy_filter(request.user, user.shelf_set)
|
||||||
|
|
||||||
|
# get the shelf and make sure the logged in user should be able to see it
|
||||||
if shelf_identifier:
|
if shelf_identifier:
|
||||||
shelf = user.shelf_set.get(identifier=shelf_identifier)
|
shelf = user.shelf_set.get(identifier=shelf_identifier)
|
||||||
|
if not object_visible_to_user(request.user, shelf):
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
# this is a constructed "all books" view, with a fake "shelf" obj
|
||||||
else:
|
else:
|
||||||
shelf = user.shelf_set.first()
|
FakeShelf = namedtuple(
|
||||||
|
"Shelf", ("identifier", "name", "user", "books", "privacy")
|
||||||
|
)
|
||||||
|
books = models.Edition.objects.filter(
|
||||||
|
shelfbook__shelf__in=shelves.all()
|
||||||
|
).distinct()
|
||||||
|
shelf = FakeShelf("all", _("All books"), user, books, "public")
|
||||||
|
|
||||||
is_self = request.user == user
|
is_self = request.user == user
|
||||||
|
|
||||||
shelves = user.shelf_set
|
|
||||||
if not is_self:
|
|
||||||
follower = user.followers.filter(id=request.user.id).exists()
|
|
||||||
# make sure the user has permission to view the shelf
|
|
||||||
if shelf.privacy == "direct" or (
|
|
||||||
shelf.privacy == "followers" and not follower
|
|
||||||
):
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
# only show other shelves that should be visible
|
|
||||||
if follower:
|
|
||||||
shelves = shelves.filter(privacy__in=["public", "followers"])
|
|
||||||
else:
|
|
||||||
shelves = shelves.filter(privacy="public")
|
|
||||||
|
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return ActivitypubResponse(shelf.to_activity(**request.GET))
|
return ActivitypubResponse(shelf.to_activity(**request.GET))
|
||||||
|
|
||||||
paginated = Paginator(
|
paginated = Paginator(
|
||||||
models.ShelfBook.objects.filter(user=user, shelf=shelf)
|
shelf.books.order_by("-updated_date").all(),
|
||||||
.order_by("-updated_date")
|
|
||||||
.all(),
|
|
||||||
PAGE_LENGTH,
|
PAGE_LENGTH,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -95,11 +93,6 @@ class Shelf(View):
|
||||||
return redirect(shelf.local_path)
|
return redirect(shelf.local_path)
|
||||||
|
|
||||||
|
|
||||||
def user_shelves_page(request, username):
|
|
||||||
""" default shelf """
|
|
||||||
return Shelf.as_view()(request, username, None)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_POST
|
@require_POST
|
||||||
def create_shelf(request):
|
def create_shelf(request):
|
||||||
|
@ -176,7 +169,8 @@ def shelve(request):
|
||||||
models.ShelfBook.objects.create(
|
models.ShelfBook.objects.create(
|
||||||
book=book, shelf=desired_shelf, user=request.user
|
book=book, shelf=desired_shelf, user=request.user
|
||||||
)
|
)
|
||||||
# The book is already on this shelf. Might be good to alert, or reject the action?
|
# The book is already on this shelf.
|
||||||
|
# Might be good to alert, or reject the action?
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
return redirect(request.headers.get("Referer", "/"))
|
return redirect(request.headers.get("Referer", "/"))
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue