Merge pull request #780 from mouse-reeve/directory

Adds opt-in directory of users
This commit is contained in:
Mouse Reeve 2021-03-21 17:38:45 -07:00 committed by GitHub
commit 1025a2e4da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 194 additions and 13 deletions

View file

@ -30,5 +30,5 @@ class Person(ActivityObject):
icon: Image = field(default_factory=lambda: {})
bookwyrmUser: bool = False
manuallyApprovesFollowers: str = False
discoverable: str = True
discoverable: str = False
type: str = "Person"

View file

@ -3,7 +3,6 @@ import datetime
from collections import defaultdict
from django import forms
from django.core.exceptions import ValidationError
from django.forms import ModelForm, PasswordInput, widgets
from django.forms.widgets import Textarea
from django.utils import timezone
@ -130,8 +129,9 @@ class EditUserForm(CustomForm):
"name",
"email",
"summary",
"manually_approves_followers",
"show_goal",
"manually_approves_followers",
"discoverable",
]
help_texts = {f: None for f in fields}

View 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),
),
]

View file

@ -103,6 +103,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
last_active_date = models.DateTimeField(auto_now=True)
manually_approves_followers = fields.BooleanField(default=False)
show_goal = models.BooleanField(default=True)
discoverable = fields.BooleanField(default=False)
name_field = "username"

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

View file

@ -2,11 +2,11 @@
{% load i18n %}
{% load humanize %}
{% block title %}{% trans "Edit Author" %}: {{ author.name }}{% endblock %}
{% block title %}{% trans "Edit Author:" %} {{ author.name }}{% endblock %}
{% block content %}
<header class="block">
<h1 class="title level-left">
<h1 class="title">
Edit "{{ author.name }}"
</h1>
<div>

View file

@ -83,18 +83,18 @@
</a>
<ul class="navbar-dropdown" id="navbar-dropdown">
<li>
<a href="/direct-messages" class="navbar-item">
<a href="{% url 'direct-messages' %}" class="navbar-item">
{% trans "Direct Messages" %}
</a>
</li>
<li>
<a href="/user/{{request.user.localname}}" class="navbar-item">
<a href="{{ request.user.local_path }}" class="navbar-item">
{% trans 'Profile' %}
</a>
</li>
<li>
<a href="/preferences/profile" class="navbar-item">
{% trans 'Settings' %}
<a href="{% url 'directory' %}" class="navbar-item">
{% trans 'Directory' %}
</a>
</li>
<li>
@ -102,12 +102,17 @@
{% trans 'Import Books' %}
</a>
</li>
<li>
<a href="/preferences/profile" class="navbar-item">
{% trans 'Settings' %}
</a>
</li>
{% if perms.bookwyrm.create_invites or perms.bookwyrm.edit_instance_settings%}
<li class="navbar-divider" role="presentation"></li>
{% endif %}
{% if perms.bookwyrm.create_invites %}
<li>
<a href="{% url 'settings-invites' %}" class="navbar-item">
<a href="{% url 'settings-invite-requests' %}" class="navbar-item">
{% trans 'Invites' %}
</a>
</li>

View file

@ -53,6 +53,14 @@
{{ form.manually_approves_followers }}
</label>
</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>
</form>
{% endblock %}

View file

@ -1,3 +1,3 @@
{% 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 }}">

View file

@ -28,6 +28,9 @@
},
"bookwyrmUser": true,
"manuallyApprovesFollowers": false,
"discoverable": true,
"devices": "https://friend.camp/users/tripofmice/collections/devices",
"tag": [],
"icon": {
"type": "Image",
"mediaType": "image/png",

View file

@ -77,7 +77,7 @@ class User(TestCase):
self.assertEqual(activity["inbox"], self.user.inbox)
self.assertEqual(activity["outbox"], self.user.outbox)
self.assertEqual(activity["bookwyrmUser"], False)
self.assertEqual(activity["discoverable"], True)
self.assertEqual(activity["discoverable"], False)
self.assertEqual(activity["type"], "Person")
def test_activitypub_outbox(self):

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

View file

@ -789,6 +789,7 @@ class Inbox(TestCase):
self.assertEqual(user.name, "MOUSE?? MOUSE!!")
self.assertEqual(user.username, "mouse@example.com")
self.assertEqual(user.localname, "mouse")
self.assertTrue(user.discoverable)
def test_handle_update_edition(self):
""" update an existing edition """

View file

@ -96,6 +96,7 @@ urlpatterns = [
path("", views.Home.as_view(), name="landing"),
re_path(r"^discover/?$", views.Discover.as_view()),
re_path(r"^notifications/?$", views.Notifications.as_view()),
re_path(r"^directory/?", views.Directory.as_view(), name="directory"),
# feeds
re_path(r"^(?P<tab>home|local|federated)/?$", views.Feed.as_view()),
re_path(

View file

@ -4,6 +4,7 @@ from .author import Author, EditAuthor
from .block import Block, unblock
from .books import Book, EditBook, ConfirmEditBook, Editions
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 .federation import Federation
from .feed import DirectMessage, Feed, Replies, Status

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

View file

@ -2,7 +2,7 @@
from django.template.response import TemplateResponse
from django.views import View
from bookwyrm import forms, models
from bookwyrm import forms
from .feed import Feed
from . import helpers