forked from mirrors/bookwyrm
Merge branch 'main' into production
This commit is contained in:
commit
9ad369203f
5 changed files with 175 additions and 152 deletions
|
@ -359,6 +359,10 @@ class CollectionItemMixin(ActivitypubMixin):
|
||||||
|
|
||||||
activity_serializer = activitypub.CollectionItem
|
activity_serializer = activitypub.CollectionItem
|
||||||
|
|
||||||
|
def broadcast(self, activity, sender, software="bookwyrm"):
|
||||||
|
""" only send book collection updates to other bookwyrm instances """
|
||||||
|
super().broadcast(activity, sender, software=software)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def privacy(self):
|
def privacy(self):
|
||||||
""" inherit the privacy of the list, or direct if pending """
|
""" inherit the privacy of the list, or direct if pending """
|
||||||
|
@ -371,6 +375,9 @@ class CollectionItemMixin(ActivitypubMixin):
|
||||||
def recipients(self):
|
def recipients(self):
|
||||||
""" the owner of the list is a direct recipient """
|
""" the owner of the list is a direct recipient """
|
||||||
collection_field = getattr(self, self.collection_field)
|
collection_field = getattr(self, self.collection_field)
|
||||||
|
if collection_field.user.local:
|
||||||
|
# don't broadcast to yourself
|
||||||
|
return []
|
||||||
return [collection_field.user]
|
return [collection_field.user]
|
||||||
|
|
||||||
def save(self, *args, broadcast=True, **kwargs):
|
def save(self, *args, broadcast=True, **kwargs):
|
||||||
|
|
|
@ -3,6 +3,12 @@ html {
|
||||||
scroll-padding-top: 20%;
|
scroll-padding-top: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,165 +22,169 @@
|
||||||
<meta name="twitter:image:alt" content="BookWyrm Logo">
|
<meta name="twitter:image:alt" content="BookWyrm Logo">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar container" aria-label="main navigation">
|
<nav class="navbar" aria-label="main navigation">
|
||||||
<div class="navbar-brand">
|
<div class="container">
|
||||||
<a class="navbar-item" href="/">
|
<div class="navbar-brand">
|
||||||
<img class="image logo" src="{% if site.logo_small %}/images/{{ site.logo_small }}{% else %}/static/images/logo-small.png{% endif %}" alt="Home page">
|
<a class="navbar-item" href="/">
|
||||||
</a>
|
<img class="image logo" src="{% if site.logo_small %}/images/{{ site.logo_small }}{% else %}/static/images/logo-small.png{% endif %}" alt="Home page">
|
||||||
<form class="navbar-item column" action="/search/">
|
|
||||||
<div class="field has-addons">
|
|
||||||
<div class="control">
|
|
||||||
<input aria-label="{% trans 'Search for a book or user' %}" id="search-input" class="input" type="text" name="q" placeholder="{% trans 'Search for a book or user' %}" value="{{ query }}">
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<button class="button" type="submit">
|
|
||||||
<span class="icon icon-search" title="{% trans 'Search' %}">
|
|
||||||
<span class="is-sr-only">{% trans "Search" %}</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div role="button" tabindex="0" class="navbar-burger pulldown-menu" data-controls="main-nav" aria-expanded="false">
|
|
||||||
<div class="navbar-item mt-3">
|
|
||||||
<div class="icon icon-dots-three-vertical" title="{% trans 'Main navigation menu' %}">
|
|
||||||
<span class="is-sr-only">{% trans "Main navigation menu" %}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar-menu" id="main-nav">
|
|
||||||
<div class="navbar-start">
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<a href="{% url 'user-shelves' request.user.localname %}" class="navbar-item">
|
|
||||||
{% trans "Your books" %}
|
|
||||||
</a>
|
</a>
|
||||||
<a href="/#feed" class="navbar-item">
|
<form class="navbar-item column" action="/search/">
|
||||||
{% trans "Feed" %}
|
<div class="field has-addons">
|
||||||
</a>
|
<div class="control">
|
||||||
<a href="{% url 'lists' %}" class="navbar-item">
|
<input aria-label="{% trans 'Search for a book or user' %}" id="search-input" class="input" type="text" name="q" placeholder="{% trans 'Search for a book or user' %}" value="{{ query }}">
|
||||||
{% trans "Lists" %}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar-end">
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
|
||||||
<a
|
|
||||||
href="{{ request.user.local_path }}"
|
|
||||||
class="navbar-link pulldown-menu"
|
|
||||||
role="button"
|
|
||||||
aria-expanded="false"
|
|
||||||
tabindex="0"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-controls="navbar-dropdown"
|
|
||||||
>
|
|
||||||
{% include 'snippets/avatar.html' with user=request.user %}
|
|
||||||
<span class="ml-2">{{ request.user.display_name }}</span>
|
|
||||||
</a>
|
|
||||||
<ul class="navbar-dropdown" id="navbar-dropdown">
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'direct-messages' %}" class="navbar-item">
|
|
||||||
{% trans "Direct Messages" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'directory' %}" class="navbar-item">
|
|
||||||
{% trans 'Directory' %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/import" class="navbar-item">
|
|
||||||
{% trans 'Import Books' %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/preferences/profile" class="navbar-item">
|
|
||||||
{% trans 'Settings' %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% if perms.bookwyrm.create_invites or perms.moderate_users %}
|
|
||||||
<li class="navbar-divider" role="presentation"></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.bookwyrm.create_invites %}
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'settings-invite-requests' %}" class="navbar-item">
|
|
||||||
{% trans 'Invites' %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.bookwyrm.moderate_users %}
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'settings-users' %}" class="navbar-item">
|
|
||||||
{% trans 'Admin' %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
<li class="navbar-divider" role="presentation"></li>
|
|
||||||
<li>
|
|
||||||
<a href="/logout" class="navbar-item">
|
|
||||||
{% trans 'Log out' %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="navbar-item">
|
|
||||||
<a href="/notifications" class="tags has-addons">
|
|
||||||
<span class="tag is-medium">
|
|
||||||
<span class="icon icon-bell" title="{% trans 'Notifications' %}">
|
|
||||||
<span class="is-sr-only">{% trans "Notifications" %}</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span class="{% if not request.user|notification_count %}is-hidden {% endif %}tag is-danger is-medium transition-x" data-poll-wrapper>
|
|
||||||
<span data-poll="notifications">{{ request.user | notification_count }}</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="navbar-item">
|
|
||||||
{% if request.path != '/login' and request.path != '/login/' %}
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column">
|
|
||||||
<form name="login" method="post" action="/login">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="columns is-variable is-1">
|
|
||||||
<div class="column">
|
|
||||||
<label class="is-sr-only" for="id_localname">{% trans "Username:" %}</label>
|
|
||||||
<input type="text" name="localname" maxlength="150" class="input" required="" id="id_localname" placeholder="{% trans 'username' %}">
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<label class="is-sr-only" for="id_password">{% trans "Username:" %}</label>
|
|
||||||
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password" placeholder="{% trans 'password' %}">
|
|
||||||
<p class="help"><a href="/password-reset">{% trans "Forgot your password?" %}</a></p>
|
|
||||||
</div>
|
|
||||||
<div class="column is-narrow">
|
|
||||||
<button class="button is-primary" type="submit">{% trans "Log in" %}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{% if site.allow_registration and request.path != '' and request.path != '/' %}
|
<div class="control">
|
||||||
<div class="column is-narrow">
|
<button class="button" type="submit">
|
||||||
<a href="/" class="button is-link">
|
<span class="icon icon-search" title="{% trans 'Search' %}">
|
||||||
{% trans "Join" %}
|
<span class="is-sr-only">{% trans "Search" %}</span>
|
||||||
</a>
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div role="button" tabindex="0" class="navbar-burger pulldown-menu" data-controls="main-nav" aria-expanded="false">
|
||||||
|
<div class="navbar-item mt-3">
|
||||||
|
<div class="icon icon-dots-three-vertical" title="{% trans 'Main navigation menu' %}">
|
||||||
|
<span class="is-sr-only">{% trans "Main navigation menu" %}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-menu" id="main-nav">
|
||||||
|
<div class="navbar-start">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<a href="{% url 'user-shelves' request.user.localname %}" class="navbar-item">
|
||||||
|
{% trans "Your books" %}
|
||||||
|
</a>
|
||||||
|
<a href="/#feed" class="navbar-item">
|
||||||
|
{% trans "Feed" %}
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'lists' %}" class="navbar-item">
|
||||||
|
{% trans "Lists" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-end">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a
|
||||||
|
href="{{ request.user.local_path }}"
|
||||||
|
class="navbar-link pulldown-menu"
|
||||||
|
role="button"
|
||||||
|
aria-expanded="false"
|
||||||
|
tabindex="0"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-controls="navbar-dropdown"
|
||||||
|
>
|
||||||
|
{% include 'snippets/avatar.html' with user=request.user %}
|
||||||
|
<span class="ml-2">{{ request.user.display_name }}</span>
|
||||||
|
</a>
|
||||||
|
<ul class="navbar-dropdown" id="navbar-dropdown">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'direct-messages' %}" class="navbar-item">
|
||||||
|
{% trans "Direct Messages" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'directory' %}" class="navbar-item">
|
||||||
|
{% trans 'Directory' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/import" class="navbar-item">
|
||||||
|
{% trans 'Import Books' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/preferences/profile" class="navbar-item">
|
||||||
|
{% trans 'Settings' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if perms.bookwyrm.create_invites or perms.moderate_users %}
|
||||||
|
<li class="navbar-divider" role="presentation"></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.bookwyrm.create_invites %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings-invite-requests' %}" class="navbar-item">
|
||||||
|
{% trans 'Invites' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.bookwyrm.moderate_users %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings-users' %}" class="navbar-item">
|
||||||
|
{% trans 'Admin' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li class="navbar-divider" role="presentation"></li>
|
||||||
|
<li>
|
||||||
|
<a href="/logout" class="navbar-item">
|
||||||
|
{% trans 'Log out' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item">
|
||||||
|
<a href="/notifications" class="tags has-addons">
|
||||||
|
<span class="tag is-medium">
|
||||||
|
<span class="icon icon-bell" title="{% trans 'Notifications' %}">
|
||||||
|
<span class="is-sr-only">{% trans "Notifications" %}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="{% if not request.user|notification_count %}is-hidden {% endif %}tag is-danger is-medium transition-x" data-poll-wrapper>
|
||||||
|
<span data-poll="notifications">{{ request.user | notification_count }}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="navbar-item">
|
||||||
|
{% if request.path != '/login' and request.path != '/login/' %}
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<form name="login" method="post" action="/login">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="columns is-variable is-1">
|
||||||
|
<div class="column">
|
||||||
|
<label class="is-sr-only" for="id_localname">{% trans "Username:" %}</label>
|
||||||
|
<input type="text" name="localname" maxlength="150" class="input" required="" id="id_localname" placeholder="{% trans 'username' %}">
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<label class="is-sr-only" for="id_password">{% trans "Username:" %}</label>
|
||||||
|
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password" placeholder="{% trans 'password' %}">
|
||||||
|
<p class="help"><a href="/password-reset">{% trans "Forgot your password?" %}</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<button class="button is-primary" type="submit">{% trans "Log in" %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% if site.allow_registration and request.path != '' and request.path != '/' %}
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<a href="/" class="button is-link">
|
||||||
|
{% trans "Join" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<div class="section container">
|
<div class="section is-flex-grow-1">
|
||||||
{% block content %}
|
<div class="container">
|
||||||
{% endblock %}
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
|
|
|
@ -55,7 +55,7 @@ class List(TestCase):
|
||||||
|
|
||||||
self.assertTrue(item.approved)
|
self.assertTrue(item.approved)
|
||||||
self.assertEqual(item.privacy, "unlisted")
|
self.assertEqual(item.privacy, "unlisted")
|
||||||
self.assertEqual(item.recipients, [self.local_user])
|
self.assertEqual(item.recipients, [])
|
||||||
|
|
||||||
def test_list_item_pending(self, _):
|
def test_list_item_pending(self, _):
|
||||||
""" a list entry """
|
""" a list entry """
|
||||||
|
@ -71,4 +71,4 @@ class List(TestCase):
|
||||||
self.assertFalse(item.approved)
|
self.assertFalse(item.approved)
|
||||||
self.assertEqual(item.book_list.privacy, "public")
|
self.assertEqual(item.book_list.privacy, "public")
|
||||||
self.assertEqual(item.privacy, "direct")
|
self.assertEqual(item.privacy, "direct")
|
||||||
self.assertEqual(item.recipients, [self.local_user])
|
self.assertEqual(item.recipients, [])
|
||||||
|
|
|
@ -53,7 +53,7 @@ services:
|
||||||
- 8000:8000
|
- 8000:8000
|
||||||
redis_activity:
|
redis_activity:
|
||||||
image: redis
|
image: redis
|
||||||
command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD}
|
command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes
|
||||||
volumes:
|
volumes:
|
||||||
- ./redis.conf:/etc/redis/redis.conf
|
- ./redis.conf:/etc/redis/redis.conf
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
@ -62,9 +62,11 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- main
|
- main
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
volumes:
|
||||||
|
- redis_activity_data:/data
|
||||||
redis_broker:
|
redis_broker:
|
||||||
image: redis
|
image: redis
|
||||||
command: redis-server --requirepass ${REDIS_BROKER_PASSWORD}
|
command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes
|
||||||
volumes:
|
volumes:
|
||||||
- ./redis.conf:/etc/redis/redis.conf
|
- ./redis.conf:/etc/redis/redis.conf
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
@ -73,6 +75,8 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- main
|
- main
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
volumes:
|
||||||
|
- redis_broker_data:/data
|
||||||
celery_worker:
|
celery_worker:
|
||||||
env_file: .env
|
env_file: .env
|
||||||
build: .
|
build: .
|
||||||
|
@ -100,11 +104,13 @@ services:
|
||||||
- redis_broker
|
- redis_broker
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
ports:
|
ports:
|
||||||
- 8888:8888
|
- 8888:8888
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
backups:
|
backups:
|
||||||
static_volume:
|
static_volume:
|
||||||
media_volume:
|
media_volume:
|
||||||
|
redis_broker_data:
|
||||||
|
redis_activity_data:
|
||||||
networks:
|
networks:
|
||||||
main:
|
main:
|
||||||
|
|
Loading…
Reference in a new issue