mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-15 04:36:34 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
d72dc842bd
20 changed files with 201 additions and 15 deletions
|
@ -30,5 +30,5 @@ class Person(ActivityObject):
|
||||||
icon: Image = field(default_factory=lambda: {})
|
icon: Image = field(default_factory=lambda: {})
|
||||||
bookwyrmUser: bool = False
|
bookwyrmUser: bool = False
|
||||||
manuallyApprovesFollowers: str = False
|
manuallyApprovesFollowers: str = False
|
||||||
discoverable: str = True
|
discoverable: str = False
|
||||||
type: str = "Person"
|
type: str = "Person"
|
||||||
|
|
|
@ -3,7 +3,6 @@ import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.forms import ModelForm, PasswordInput, widgets
|
from django.forms import ModelForm, PasswordInput, widgets
|
||||||
from django.forms.widgets import Textarea
|
from django.forms.widgets import Textarea
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -130,8 +129,9 @@ class EditUserForm(CustomForm):
|
||||||
"name",
|
"name",
|
||||||
"email",
|
"email",
|
||||||
"summary",
|
"summary",
|
||||||
"manually_approves_followers",
|
|
||||||
"show_goal",
|
"show_goal",
|
||||||
|
"manually_approves_followers",
|
||||||
|
"discoverable",
|
||||||
]
|
]
|
||||||
help_texts = {f: None for f in fields}
|
help_texts = {f: None for f in fields}
|
||||||
|
|
||||||
|
|
19
bookwyrm/migrations/0057_user_discoverable.py
Normal file
19
bookwyrm/migrations/0057_user_discoverable.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.1.6 on 2021-03-21 21:44
|
||||||
|
|
||||||
|
import bookwyrm.models.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0056_auto_20210321_0303"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="discoverable",
|
||||||
|
field=bookwyrm.models.fields.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
|
@ -103,6 +103,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
last_active_date = models.DateTimeField(auto_now=True)
|
last_active_date = models.DateTimeField(auto_now=True)
|
||||||
manually_approves_followers = fields.BooleanField(default=False)
|
manually_approves_followers = fields.BooleanField(default=False)
|
||||||
show_goal = models.BooleanField(default=True)
|
show_goal = models.BooleanField(default=True)
|
||||||
|
discoverable = fields.BooleanField(default=False)
|
||||||
|
|
||||||
name_field = "username"
|
name_field = "username"
|
||||||
|
|
||||||
|
|
|
@ -296,3 +296,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script src="/static/js/tabs.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
67
bookwyrm/templates/directory.html
Normal file
67
bookwyrm/templates/directory.html
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load bookwyrm_tags %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Directory" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<header class="block">
|
||||||
|
<h1 class="title">
|
||||||
|
{% trans "Directory" %}
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
{% for user in users %}
|
||||||
|
<div class="column is-one-third">
|
||||||
|
<div class="card block">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="media">
|
||||||
|
<span class="media-left">
|
||||||
|
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||||
|
</span>
|
||||||
|
<div class="media-content">
|
||||||
|
<a href="{{ user.local_path }}" class="is-block mb-2">
|
||||||
|
<span class="title is-4 is-block">{{ user.display_name }}</span>
|
||||||
|
<span class="subtitle is-6 is-block">@{{ user|username }}</span>
|
||||||
|
</a>
|
||||||
|
{% include 'snippets/follow_button.html' with user=user %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if user.summary %}
|
||||||
|
<div class="content">
|
||||||
|
{{ user.summary | to_markdown | safe | truncatechars_html:40 }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<footer class="card-footer content">
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<p class="title is-6 mb-0">{{ user.shelfbook_set.count }}</p>
|
||||||
|
<p class="help">{% trans "books shelved" %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<p class="title is-6 mb-0">{{ user.status_set.count|intword }}</p>
|
||||||
|
<p class="help">{% trans "posts" %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<p class="title is-6 mb-0">{{ user.last_active_date|naturalday }}</p>
|
||||||
|
<p class="help">{% trans "last active" %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% include 'snippets/pagination.html' with page=users path="/directory" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -2,11 +2,11 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
|
||||||
{% block title %}{% trans "Edit Author" %}: {{ author.name }}{% endblock %}
|
{% block title %}{% trans "Edit Author:" %} {{ author.name }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<header class="block">
|
<header class="block">
|
||||||
<h1 class="title level-left">
|
<h1 class="title">
|
||||||
Edit "{{ author.name }}"
|
Edit "{{ author.name }}"
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# announcements and system messages #}
|
{# announcements and system messages #}
|
||||||
{% if request.user.show_goal and not goal and tab == 'home' %}
|
{% if request.user.show_goal and not goal and tab == 'home' and not activities.number > 1 %}
|
||||||
{% now 'Y' as year %}
|
{% now 'Y' as year %}
|
||||||
<section class="block">
|
<section class="block">
|
||||||
{% include 'snippets/goal_card.html' with year=year %}
|
{% include 'snippets/goal_card.html' with year=year %}
|
||||||
|
@ -31,9 +31,10 @@
|
||||||
{% if not activities %}
|
{% if not activities %}
|
||||||
<p>{% trans "There aren't any activities right now! Try following a user to get started" %}</p>
|
<p>{% trans "There aren't any activities right now! Try following a user to get started" %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for activity in activities %}
|
{% for activity in activities %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
{% include 'snippets/status/status.html' with status=activity %}
|
{% include 'snippets/status/status.html' with status=activity %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -83,18 +83,18 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="navbar-dropdown" id="navbar-dropdown">
|
<ul class="navbar-dropdown" id="navbar-dropdown">
|
||||||
<li>
|
<li>
|
||||||
<a href="/direct-messages" class="navbar-item">
|
<a href="{% url 'direct-messages' %}" class="navbar-item">
|
||||||
{% trans "Direct Messages" %}
|
{% trans "Direct Messages" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/user/{{request.user.localname}}" class="navbar-item">
|
<a href="{{ request.user.local_path }}" class="navbar-item">
|
||||||
{% trans 'Profile' %}
|
{% trans 'Profile' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/preferences/profile" class="navbar-item">
|
<a href="{% url 'directory' %}" class="navbar-item">
|
||||||
{% trans 'Settings' %}
|
{% trans 'Directory' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -102,12 +102,17 @@
|
||||||
{% trans 'Import Books' %}
|
{% trans 'Import Books' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/preferences/profile" class="navbar-item">
|
||||||
|
{% trans 'Settings' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
{% if perms.bookwyrm.create_invites or perms.bookwyrm.edit_instance_settings%}
|
{% if perms.bookwyrm.create_invites or perms.bookwyrm.edit_instance_settings%}
|
||||||
<li class="navbar-divider" role="presentation"></li>
|
<li class="navbar-divider" role="presentation"></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.bookwyrm.create_invites %}
|
{% if perms.bookwyrm.create_invites %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'settings-invites' %}" class="navbar-item">
|
<a href="{% url 'settings-invite-requests' %}" class="navbar-item">
|
||||||
{% trans 'Invites' %}
|
{% trans 'Invites' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -53,6 +53,14 @@
|
||||||
{{ form.manually_approves_followers }}
|
{{ form.manually_approves_followers }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<label class="checkbox label" for="id_discoverable">
|
||||||
|
{% trans "Show this account in suggested users:" %}
|
||||||
|
{{ form.discoverable }}
|
||||||
|
</label>
|
||||||
|
{% url 'directory' as path %}
|
||||||
|
<p class="help">{% blocktrans %}Your account will show up in the <a href="{{ path }}">directory</a>, and may be recommended to other BookWyrm users.{% endblocktrans %}</p>
|
||||||
|
</div>
|
||||||
<button class="button is-primary" type="submit">{% trans "Save" %}</button>
|
<button class="button is-primary" type="submit">{% trans "Save" %}</button>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{% load bookwyrm_tags %}
|
{% load bookwyrm_tags %}
|
||||||
<img class="avatar image {% if large %}is-96x96{% else %}is-32x32{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}" {% if ariaHide %}aria-hidden="true"{% endif %} alt="{{ user.alt_text }}">
|
<img class="avatar image {% if large %}is-96x96{% elif medium %}is-48x48{% else %}is-32x32{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}" {% if ariaHide %}aria-hidden="true"{% endif %} alt="{{ user.alt_text }}">
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
},
|
},
|
||||||
"bookwyrmUser": true,
|
"bookwyrmUser": true,
|
||||||
"manuallyApprovesFollowers": false,
|
"manuallyApprovesFollowers": false,
|
||||||
|
"discoverable": true,
|
||||||
|
"devices": "https://friend.camp/users/tripofmice/collections/devices",
|
||||||
|
"tag": [],
|
||||||
"icon": {
|
"icon": {
|
||||||
"type": "Image",
|
"type": "Image",
|
||||||
"mediaType": "image/png",
|
"mediaType": "image/png",
|
||||||
|
|
|
@ -77,7 +77,7 @@ class User(TestCase):
|
||||||
self.assertEqual(activity["inbox"], self.user.inbox)
|
self.assertEqual(activity["inbox"], self.user.inbox)
|
||||||
self.assertEqual(activity["outbox"], self.user.outbox)
|
self.assertEqual(activity["outbox"], self.user.outbox)
|
||||||
self.assertEqual(activity["bookwyrmUser"], False)
|
self.assertEqual(activity["bookwyrmUser"], False)
|
||||||
self.assertEqual(activity["discoverable"], True)
|
self.assertEqual(activity["discoverable"], False)
|
||||||
self.assertEqual(activity["type"], "Person")
|
self.assertEqual(activity["type"], "Person")
|
||||||
|
|
||||||
def test_activitypub_outbox(self):
|
def test_activitypub_outbox(self):
|
||||||
|
|
43
bookwyrm/tests/views/test_directory.py
Normal file
43
bookwyrm/tests/views/test_directory.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
""" test for app action functionality """
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.client import RequestFactory
|
||||||
|
|
||||||
|
from bookwyrm import models, views
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
class DirectoryViews(TestCase):
|
||||||
|
""" tag views"""
|
||||||
|
|
||||||
|
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.com",
|
||||||
|
"mouseword",
|
||||||
|
local=True,
|
||||||
|
localname="mouse",
|
||||||
|
remote_id="https://example.com/users/mouse",
|
||||||
|
)
|
||||||
|
self.rat = models.User.objects.create_user(
|
||||||
|
"rat@local.com",
|
||||||
|
"rat@rat.com",
|
||||||
|
"ratword",
|
||||||
|
local=True,
|
||||||
|
localname="rat",
|
||||||
|
remote_id="https://example.com/users/rat",
|
||||||
|
discoverable=True,
|
||||||
|
)
|
||||||
|
models.SiteSettings.objects.create()
|
||||||
|
|
||||||
|
def test_directory_page(self):
|
||||||
|
""" there are so many views, this just makes sure it LOADS """
|
||||||
|
view = views.Directory.as_view()
|
||||||
|
request = self.factory.get("")
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
result = view(request)
|
||||||
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
|
result.render()
|
||||||
|
self.assertEqual(result.status_code, 200)
|
|
@ -789,6 +789,7 @@ class Inbox(TestCase):
|
||||||
self.assertEqual(user.name, "MOUSE?? MOUSE!!")
|
self.assertEqual(user.name, "MOUSE?? MOUSE!!")
|
||||||
self.assertEqual(user.username, "mouse@example.com")
|
self.assertEqual(user.username, "mouse@example.com")
|
||||||
self.assertEqual(user.localname, "mouse")
|
self.assertEqual(user.localname, "mouse")
|
||||||
|
self.assertTrue(user.discoverable)
|
||||||
|
|
||||||
def test_handle_update_edition(self):
|
def test_handle_update_edition(self):
|
||||||
""" update an existing edition """
|
""" update an existing edition """
|
||||||
|
|
|
@ -96,6 +96,7 @@ urlpatterns = [
|
||||||
path("", views.Home.as_view(), name="landing"),
|
path("", views.Home.as_view(), name="landing"),
|
||||||
re_path(r"^discover/?$", views.Discover.as_view()),
|
re_path(r"^discover/?$", views.Discover.as_view()),
|
||||||
re_path(r"^notifications/?$", views.Notifications.as_view()),
|
re_path(r"^notifications/?$", views.Notifications.as_view()),
|
||||||
|
re_path(r"^directory/?", views.Directory.as_view(), name="directory"),
|
||||||
# feeds
|
# feeds
|
||||||
re_path(r"^(?P<tab>home|local|federated)/?$", views.Feed.as_view()),
|
re_path(r"^(?P<tab>home|local|federated)/?$", views.Feed.as_view()),
|
||||||
re_path(
|
re_path(
|
||||||
|
|
|
@ -4,6 +4,7 @@ from .author import Author, EditAuthor
|
||||||
from .block import Block, unblock
|
from .block import Block, unblock
|
||||||
from .books import Book, EditBook, ConfirmEditBook, Editions
|
from .books import Book, EditBook, ConfirmEditBook, Editions
|
||||||
from .books import upload_cover, add_description, switch_edition, resolve_book
|
from .books import upload_cover, add_description, switch_edition, resolve_book
|
||||||
|
from .directory import Directory
|
||||||
from .error import not_found_page, server_error_page
|
from .error import not_found_page, server_error_page
|
||||||
from .federation import Federation
|
from .federation import Federation
|
||||||
from .feed import DirectMessage, Feed, Replies, Status
|
from .feed import DirectMessage, Feed, Replies, Status
|
||||||
|
|
32
bookwyrm/views/directory.py
Normal file
32
bookwyrm/views/directory.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
""" who all's here? """
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.views import View
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from bookwyrm import models
|
||||||
|
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
class Directory(View):
|
||||||
|
""" display of known bookwyrm users """
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
""" lets see your cute faces """
|
||||||
|
try:
|
||||||
|
page = int(request.GET.get("page", 1))
|
||||||
|
except ValueError:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
users = models.User.objects.filter(
|
||||||
|
discoverable=True,
|
||||||
|
bookwyrm_user=True,
|
||||||
|
is_active=True,
|
||||||
|
).order_by("-last_active_date")
|
||||||
|
paginated = Paginator(users, 12)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"users": paginated.page(page),
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, "directory.html", data)
|
|
@ -2,7 +2,7 @@
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
||||||
from bookwyrm import forms, models
|
from bookwyrm import forms
|
||||||
from .feed import Feed
|
from .feed import Feed
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
|
|
Binary file not shown.
Loading…
Reference in a new issue