forked from mirrors/bookwyrm
Merge branch 'main' into production
This commit is contained in:
commit
f6026f5ed8
13 changed files with 110 additions and 143 deletions
|
@ -1,13 +1,13 @@
|
|||
''' functionality outline for a book data connector '''
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from dateutil import parser
|
||||
import pytz
|
||||
import requests
|
||||
from requests import HTTPError
|
||||
from urllib3.exceptions import RequestError
|
||||
|
||||
from django.db import transaction
|
||||
from dateutil import parser
|
||||
import requests
|
||||
from requests import HTTPError
|
||||
|
||||
from bookwyrm import models
|
||||
|
||||
|
|
|
@ -69,7 +69,8 @@ def shared_inbox(request):
|
|||
},
|
||||
'Update': {
|
||||
'Person': handle_update_user,
|
||||
'Document': handle_update_book,
|
||||
'Edition': handle_update_book,
|
||||
'Work': handle_update_book,
|
||||
},
|
||||
}
|
||||
activity_type = activity['type']
|
||||
|
@ -337,7 +338,7 @@ def handle_update_book(activity):
|
|||
document = activity['object']
|
||||
# check if we have their copy and care about their updates
|
||||
book = models.Book.objects.select_subclasses().filter(
|
||||
remote_id=document['url'],
|
||||
remote_id=document['id'],
|
||||
sync=True,
|
||||
).first()
|
||||
if not book:
|
||||
|
|
|
@ -82,6 +82,17 @@ class UserFollowRequest(UserRelationship):
|
|||
''' following a user requires manual or automatic confirmation '''
|
||||
status = 'follow_request'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
''' make sure the follow relationship doesn't already exist '''
|
||||
try:
|
||||
UserFollows.objects.get(
|
||||
user_subject=self.user_subject,
|
||||
user_object=self.user_object
|
||||
)
|
||||
return None
|
||||
except UserFollows.DoesNotExist:
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class UserBlocks(UserRelationship):
|
||||
''' prevent another user from following you and seeing your posts '''
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% block content %}
|
||||
<div class="content-container">
|
||||
<h2>Search results</h2>
|
||||
{% for result_set in results %}
|
||||
{% if result_set.results %}
|
||||
<section>
|
||||
{% if not result_set.connector.local %}
|
||||
<h3>
|
||||
Results from <a href="{{ result_set.connector.base_url }}" target="_blank">{% if result_set.connector.name %}{{ result_set.connector.name }}{% else %}{{ result_set.connector.identifier }}{% endif %}</a>
|
||||
</h3>
|
||||
{% endif %}
|
||||
|
||||
{% for result in result_set.results %}
|
||||
<div>
|
||||
<form action="/resolve_book" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="remote_id" value="{{ result.key }}">
|
||||
<button type="submit">{{ result.title }} by {{ result.author }} ({{ result.year }})</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,17 +1,18 @@
|
|||
{% load fr_display %}
|
||||
{% with book.id|uuid as uuid %}
|
||||
{% with book|book_description as full %}
|
||||
{% if full %}
|
||||
{% with full|text_overflow as trimmed %}
|
||||
{% if trimmed != full %}
|
||||
<div>
|
||||
<input type="radio" name="show-hide-{{ book.id }}" id="show-{{ book.id }}" class="toggle-control" checked>
|
||||
<input type="radio" name="show-hide-{{ book.id }}-{{ uuid }}" id="show-{{ book.id }}-{{ uuid }}" class="toggle-control" checked>
|
||||
<blockquote class="content toggle-content hidden">{{ trimmed }}
|
||||
<label class="button is-small" for="hide-{{ book.id }}">show more</label></blockquote>
|
||||
<label class="button is-small" for="hide-{{ book.id }}-{{ uuid }}">show more</label></blockquote>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" name="show-hide-{{ book.id }}" id="hide-{{ book.id }}" class="toggle-control">
|
||||
<input type="radio" name="show-hide-{{ book.id }}-{{ uuid }}" id="hide-{{ book.id }}-{{ uuid }}" class="toggle-control">
|
||||
<blockquote class="content toggle-content hidden">{{ full }}
|
||||
<label class="button is-small" for="show-{{ book.id }}">show less</label></blockquote>
|
||||
<label class="button is-small" for="show-{{ book.id }}-{{ uuid }}">show less</label></blockquote>
|
||||
</div>
|
||||
{% else %}
|
||||
<blockquote class="content">{{ full }}
|
||||
|
@ -20,3 +21,4 @@
|
|||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
|
|
12
bookwyrm/templates/snippets/book_preview.html
Normal file
12
bookwyrm/templates/snippets/book_preview.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="media">
|
||||
<div class="media-left">
|
||||
<div>
|
||||
<a href="/book/{{ book.id }}">{% include 'snippets/book_cover.html' with book=book %}</a>
|
||||
{% include 'snippets/shelve_button.html' with book=book %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<h3 class="title is-6">{% include 'snippets/book_titleby.html' with book=book %}</h3>
|
||||
{% include 'snippets/book_description.html' with book=book %}
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +1,5 @@
|
|||
{% load fr_display %}
|
||||
{% with activity.id|uuid as uuid %}
|
||||
<div class="card-footer-item">
|
||||
{% if request.user.is_authenticated %}
|
||||
<form name="reply" action="/reply" method="post" onsubmit="return reply(event)">
|
||||
|
@ -7,7 +8,7 @@
|
|||
<input type="hidden" name="reply_parent" value="{{ activity.id }}">
|
||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||
<input type="hidden" name="privacy" value="{{ activity.privacy }}">
|
||||
<textarea name="content" placeholder="Leave a comment..." id="id_content_{{ activity.id }}" required="true"></textarea>
|
||||
<textarea name="content" placeholder="Leave a comment..." id="id_content_{{ activity.id }}-{{ uuid }}" required="true"></textarea>
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-comment">
|
||||
<span class="is-sr-only">Comment</span>
|
||||
|
@ -16,7 +17,7 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
<form name="boost" action="/boost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}">
|
||||
<form name="boost" action="/boost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
|
||||
{% csrf_token %}
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-boost">
|
||||
|
@ -24,7 +25,7 @@
|
|||
</span>
|
||||
</button>
|
||||
</form>
|
||||
<form name="unboost" action="/unboost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}">
|
||||
<form name="unboost" action="/unboost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
|
||||
{% csrf_token %}
|
||||
<button class="button is-success" type="submit">
|
||||
<span class="icon icon-boost">
|
||||
|
@ -33,7 +34,7 @@
|
|||
</button>
|
||||
</form>
|
||||
|
||||
<form name="favorite" action="/favorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}">
|
||||
<form name="favorite" action="/favorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
|
||||
{% csrf_token %}
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-heart">
|
||||
|
@ -41,7 +42,7 @@
|
|||
</span>
|
||||
</button>
|
||||
</form>
|
||||
<form name="unfavorite" action="/unfavorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}">
|
||||
<form name="unfavorite" action="/unfavorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
|
||||
{% csrf_token %}
|
||||
<button class="button is-success" type="submit">
|
||||
<span class="icon icon-heart">
|
||||
|
@ -65,3 +66,4 @@
|
|||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
|
|
|
@ -4,12 +4,25 @@
|
|||
{% if not status.deleted %}
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
{% include 'snippets/status_header.html' with status=status %}
|
||||
<div class="card-header-title">
|
||||
<p>
|
||||
{% if status.status_type == 'Boost' %}
|
||||
{% include 'snippets/avatar.html' with user=status.user %}
|
||||
{% include 'snippets/username.html' with user=status.user %}
|
||||
boosted
|
||||
</p>
|
||||
<p>
|
||||
{% include 'snippets/status_header.html' with status=status|boosted_status %}
|
||||
{% else %}
|
||||
{% include 'snippets/status_header.html' with status=status %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="card-content">
|
||||
{% if status.status_type == 'Boost' %}
|
||||
{% include 'snippets/status_content.html' with status=status.boosted_status %}
|
||||
{% include 'snippets/status_content.html' with status=status|boosted_status %}
|
||||
{% else %}
|
||||
{% include 'snippets/status_content.html' with status=status %}
|
||||
{% endif %}
|
||||
|
@ -17,7 +30,7 @@
|
|||
|
||||
<footer class="card-footer">
|
||||
{% if status.status_type == 'Boost' %}
|
||||
{% include 'snippets/interaction.html' with activity=status.boosted_status %}
|
||||
{% include 'snippets/interaction.html' with activity=status|boosted_status %}
|
||||
{% else %}
|
||||
{% include 'snippets/interaction.html' with activity=status %}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,64 +1,31 @@
|
|||
{% load fr_display %}
|
||||
<div class="block">
|
||||
{% if status.status_type == 'Review' %}
|
||||
<h3>
|
||||
{% if status.name %}{{ status.name }}<br>{% endif %}
|
||||
{% include 'snippets/stars.html' with rating=status.rating %}
|
||||
</h3>
|
||||
{% endif %}
|
||||
|
||||
<div class="media">
|
||||
{% if not hide_book and status.mention_books.count %}
|
||||
<div class="media-left">
|
||||
<div class="columns">
|
||||
{% for book in status.mention_books.all|slice:"0:4" %}
|
||||
<div class="column">
|
||||
<a href="/book/{{ book.id }}">{% include 'snippets/book_cover.html' with book=book %}</a>
|
||||
{% if status.mention_books.count > 1 %}
|
||||
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
||||
{% endif %}
|
||||
{% include 'snippets/shelve_button.html' with book=book %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if status.quote %}
|
||||
<div class="quote block">
|
||||
<blockquote>{{ status.quote }}</blockquote>
|
||||
|
||||
<p> — {% include 'snippets/book_titleby.html' with book=status.book %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not hide_book and status.book %}
|
||||
<div class="media-left">
|
||||
<div>
|
||||
<a href="/book/{{ status.book.id }}">{% include 'snippets/book_cover.html' with book=status.book %}</a>
|
||||
{% include 'snippets/shelve_button.html' with book=status.book %}
|
||||
</div>
|
||||
</div>
|
||||
{% if status.content and status.status_type != 'GeneratedNote' and status.status_type != 'Boost' %}
|
||||
<blockquote>{{ status.content | safe }}</blockquote>
|
||||
{% endif %}
|
||||
|
||||
<div class="media-content">
|
||||
{% if status.status_type == 'Review' %}
|
||||
<h3>
|
||||
{% if status.name %}{{ status.name }}<br>{% endif %}
|
||||
{% include 'snippets/stars.html' with rating=status.rating %}
|
||||
</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if status.quote %}
|
||||
<div class="quote block">
|
||||
<blockquote>{{ status.quote }}</blockquote>
|
||||
|
||||
<p> — {% include 'snippets/book_titleby.html' with book=status.book %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if status.content and status.status_type != 'GeneratedNote' and status.status_type != 'Boost' %}
|
||||
<blockquote>{{ status.content | safe }}</blockquote>
|
||||
{% endif %}
|
||||
|
||||
{% if status.mention_books.count == 1 and not status.book %}
|
||||
{% include 'snippets/book_description.html' with book=status.mention_books.first %}
|
||||
{% endif %}
|
||||
|
||||
{% if not status.content and status.book and not hide_book and status.status_type != 'Boost' %}
|
||||
{% include 'snippets/book_description.html' with book=status.book %}
|
||||
{% endif %}
|
||||
|
||||
{% if status.status_type == 'Boost' %}
|
||||
{% include 'snippets/status_content.html' with status=status|boosted_status %}
|
||||
{% endif %}
|
||||
|
||||
{% if not max_depth and status.reply_parent or status|replies %}<p><a href="{{ status.remote_id }}">Thread</a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if status.book or status.mention_books.count %}
|
||||
<div class="{% if status.status_type != 'GeneratedNote' %}box{% endif %}">
|
||||
{% if status.book %}
|
||||
{% include 'snippets/book_preview.html' with book=status.book %}
|
||||
{% elif status.mention_books.count %}
|
||||
{% include 'snippets/book_preview.html' with book=status.mention_books.first %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
{% load fr_display %}
|
||||
<div class="card-header-title">
|
||||
<p>
|
||||
{% include 'snippets/avatar.html' with user=status.user %}
|
||||
{% include 'snippets/avatar.html' with user=status.user %}
|
||||
{% include 'snippets/username.html' with user=status.user %}
|
||||
|
||||
{% include 'snippets/username.html' with user=status.user %}
|
||||
{% if status.status_type == 'GeneratedNote' %}
|
||||
{{ status.content | safe }} {% include 'snippets/book_titleby.html' with book=status.mention_books.first %}
|
||||
{% elif status.status_type == 'Boost' %}
|
||||
boosted {% include 'snippets/avatar.html' with user=status.boosted_status.user %}{% include 'snippets/username.html' with user=status.boosted_status.user possessive=True %} status
|
||||
{% elif status.status_type == 'Review' and not status.name and not status.content%}
|
||||
rated <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||
{% elif status.status_type == 'Review' %}
|
||||
reviewed <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||
{% elif status.status_type == 'Comment' %}
|
||||
commented on <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||
{% elif status.status_type == 'Quotation' %}
|
||||
quoted <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||
{% elif status.reply_parent %}
|
||||
{% with parent_status=status|parent %}
|
||||
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.remote_id }}">{% if parent_status.status_type == 'GeneratedNote' %}update{% else %}{{ parent_status.status_type | lower }}{% endif %}</a>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% if status.status_type == 'GeneratedNote' %}
|
||||
{{ status.content | safe }}
|
||||
{% elif status.status_type == 'Review' and not status.name and not status.content%}
|
||||
rated
|
||||
{% elif status.status_type == 'Review' %}
|
||||
reviewed
|
||||
{% elif status.status_type == 'Comment' %}
|
||||
commented on
|
||||
{% elif status.status_type == 'Quotation' %}
|
||||
quoted
|
||||
{% elif status.reply_parent %}
|
||||
{% with parent_status=status|parent %}
|
||||
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.remote_id }}">{% if parent_status.status_type == 'GeneratedNote' %}update{% else %}{{ parent_status.status_type | lower }}{% endif %}</a>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% if status.book %}
|
||||
<a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||
{% elif status.mention_books %}
|
||||
<a href="/book/{{ status.mention_books.first.id }}">{{ status.mention_books.first.title }}</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% block content %}
|
||||
<div class="block">
|
||||
<h2 class="title">User search results</h2>
|
||||
{% if not results %}
|
||||
<p>No results found for "{{ query }}"</p>
|
||||
{% endif %}
|
||||
{% for result in results %}
|
||||
{{ result }}
|
||||
<div class="block">
|
||||
{% include 'snippets/avatar.html' with user=result %}</h2>
|
||||
{% include 'snippets/username.html' with user=result show_full=True %}</h2>
|
||||
{% include 'snippets/follow_button.html' with user=result %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
''' template filters '''
|
||||
from uuid import uuid4
|
||||
from django import template
|
||||
|
||||
from bookwyrm import models
|
||||
|
@ -111,7 +112,6 @@ def get_book_description(book):
|
|||
''' use the work's text if the book doesn't have it '''
|
||||
return book.description or book.parent_work.description
|
||||
|
||||
|
||||
@register.filter(name='text_overflow')
|
||||
def text_overflow(text):
|
||||
''' dont' let book descriptions run for ages '''
|
||||
|
@ -127,6 +127,11 @@ def text_overflow(text):
|
|||
return trimmed + '...'
|
||||
|
||||
|
||||
@register.filter(name='uuid')
|
||||
def get_uuid(identifier):
|
||||
return '%s%s' % (identifier, uuid4())
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def shelve_button_identifier(context, book):
|
||||
''' check what shelf a user has a book on, if any '''
|
||||
|
|
|
@ -98,7 +98,7 @@ def home_tab(request, tab):
|
|||
prev_page = '/?page=%d#feed' % (page - 1)
|
||||
data = {
|
||||
'user': request.user,
|
||||
'suggested_books': suggested_books,
|
||||
'suggested_books': set(suggested_books),
|
||||
'activities': activities,
|
||||
'review_form': forms.ReviewForm(),
|
||||
'quotation_form': forms.QuotationForm(),
|
||||
|
|
Loading…
Reference in a new issue