Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2020-11-02 11:55:06 -08:00
commit f6026f5ed8
13 changed files with 110 additions and 143 deletions

View file

@ -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

View file

@ -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:

View file

@ -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 '''

View file

@ -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 %}

View file

@ -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 %}

View 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>

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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> &mdash; {% 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> &mdash; {% 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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 '''

View file

@ -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(),