From d73a1b4ec1606bb86af5b50b844cdf9afd146cab Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 31 Jan 2021 08:41:11 -0800 Subject: [PATCH] handles list privacy in display --- ...131_0500.py => 0041_auto_20210131_1614.py} | 4 +- bookwyrm/models/list.py | 2 +- bookwyrm/templates/lists/list-item.html | 2 + bookwyrm/templates/lists/lists.html | 16 ++-- bookwyrm/templates/snippets/trimmed_text.html | 12 +-- bookwyrm/views/helpers.py | 88 +++++++++++-------- bookwyrm/views/list.py | 13 ++- 7 files changed, 79 insertions(+), 58 deletions(-) rename bookwyrm/migrations/{0041_auto_20210131_0500.py => 0041_auto_20210131_1614.py} (95%) create mode 100644 bookwyrm/templates/lists/list-item.html diff --git a/bookwyrm/migrations/0041_auto_20210131_0500.py b/bookwyrm/migrations/0041_auto_20210131_1614.py similarity index 95% rename from bookwyrm/migrations/0041_auto_20210131_0500.py rename to bookwyrm/migrations/0041_auto_20210131_1614.py index eb00e3f37..8deb69a86 100644 --- a/bookwyrm/migrations/0041_auto_20210131_0500.py +++ b/bookwyrm/migrations/0041_auto_20210131_1614.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.7 on 2021-01-31 05:00 +# Generated by Django 3.0.7 on 2021-01-31 16:14 import bookwyrm.models.base_model import bookwyrm.models.fields @@ -24,7 +24,7 @@ class Migration(migrations.Migration): ('name', bookwyrm.models.fields.CharField(max_length=100)), ('description', bookwyrm.models.fields.TextField(blank=True, null=True)), ('privacy', bookwyrm.models.fields.CharField(choices=[('public', 'Public'), ('unlisted', 'Unlisted'), ('followers', 'Followers'), ('direct', 'Direct')], default='public', max_length=255)), - ('curation', bookwyrm.models.fields.CharField(choices=[('closed', 'Closed'), ('open', 'Open'), ('moderated', 'Moderated')], default='closed', max_length=255)), + ('curation', bookwyrm.models.fields.CharField(choices=[('closed', 'Closed'), ('open', 'Open'), ('curated', 'Curated')], default='closed', max_length=255)), ], options={ 'abstract': False, diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py index dd50f209c..28ee7a2a4 100644 --- a/bookwyrm/models/list.py +++ b/bookwyrm/models/list.py @@ -11,7 +11,7 @@ from . import fields CurationType = models.TextChoices('Curation', [ 'closed', 'open', - 'moderated', + 'curated', ]) class List(OrderedCollectionMixin, BookWyrmModel): diff --git a/bookwyrm/templates/lists/list-item.html b/bookwyrm/templates/lists/list-item.html new file mode 100644 index 000000000..0f0cb8a64 --- /dev/null +++ b/bookwyrm/templates/lists/list-item.html @@ -0,0 +1,2 @@ +

{{ list.name }} {% include 'snippets/privacy-icons.html' with item=list %}

+{% include 'snippets/trimmed_text.html' with full=list.description %} diff --git a/bookwyrm/templates/lists/lists.html b/bookwyrm/templates/lists/lists.html index 31221aaa9..73f98e77a 100644 --- a/bookwyrm/templates/lists/lists.html +++ b/bookwyrm/templates/lists/lists.html @@ -6,18 +6,18 @@ {% if request.user.is_authenticated %} -
+
-

Your lists

+

Your lists

{% include 'snippets/toggle/open_button.html' with controls_text="create-list" icon="plus" text="Create new list" %}
-
-

Create list

+ +

Create list

{% csrf_token %} @@ -68,7 +68,7 @@
    {% for list in request.user.list_set.all %}
  • - {{ list.name }} + {% include 'lists/list-item.html' with list=list %}
  • {% endfor %}
@@ -77,12 +77,12 @@ {% endif %} {% if lists.exists %} -
-

Recent Lists

+
+

Recent Lists

    {% for list in lists %}
  • - {{ list.name }} + {% include 'lists/list-item.html' with list=list %}
  • {% endfor %}
diff --git a/bookwyrm/templates/snippets/trimmed_text.html b/bookwyrm/templates/snippets/trimmed_text.html index 5d287ab33..b349a4fc0 100644 --- a/bookwyrm/templates/snippets/trimmed_text.html +++ b/bookwyrm/templates/snippets/trimmed_text.html @@ -1,24 +1,26 @@ {% load bookwyrm_tags %} {% with 0|uuid as uuid %} {% if full %} +{% with full|to_markdown|safe as full %} {% with full|to_markdown|safe|truncatewords_html:60 as trimmed %} {% if trimmed != full %}
-
{{ trimmed }} +
{{ trimmed }} {% include 'snippets/toggle/open_button.html' with text="show more" controls_text="full" controls_uid=uuid class="is-small" %} -
+
{% else %} -
{{ full | to_markdown | safe }}
+
{{ full }}
{% endif %} {% endwith %} +{% endwith %} {% endif %} {% endwith %} diff --git a/bookwyrm/views/helpers.py b/bookwyrm/views/helpers.py index 6bda81c8b..8742884f1 100644 --- a/bookwyrm/views/helpers.py +++ b/bookwyrm/views/helpers.py @@ -59,11 +59,55 @@ def object_visible_to_user(viewer, obj): return True return False + +def privacy_filter(viewer, queryset, privacy_levels, following_only=False): + ''' filter objects that have "user" and "privacy" fields ''' + # exclude blocks from both directions + if not viewer.is_anonymous: + blocked = models.User.objects.filter(id__in=viewer.blocks.all()).all() + queryset = queryset.exclude( + Q(user__in=blocked) | Q(user__blocks=viewer)) + + # you can't see followers only or direct messages if you're not logged in + if viewer.is_anonymous: + privacy_levels = [p for p in privacy_levels if \ + not p in ['followers', 'direct']] + + # filter to only privided privacy levels + queryset = queryset.filter(privacy__in=privacy_levels) + + # only include statuses the user follows + if following_only: + queryset = queryset.exclude( + ~Q(# remove everythign except + Q(user__in=viewer.following.all()) | # user following + Q(user=viewer) |# is self + Q(mention_users=viewer)# mentions user + ), + ) + # exclude followers-only statuses the user doesn't follow + elif 'followers' in privacy_levels: + queryset = queryset.exclude( + ~Q(# user isn't following and it isn't their own status + Q(user__in=viewer.following.all()) | Q(user=viewer) + ), + privacy='followers' # and the status is followers only + ) + + # exclude direct messages not intended for the user + if 'direct' in privacy_levels: + queryset = queryset.exclude( + ~Q( + Q(user=viewer) | Q(mention_users=viewer) + ), privacy='direct' + ) + return queryset + + def get_activity_feed( user, privacy, local_only=False, following_only=False, queryset=models.Status.objects): ''' get a filtered queryset of statuses ''' - privacy = privacy if isinstance(privacy, list) else [privacy] # if we're looking at Status, we need this. We don't if it's Comment if hasattr(queryset, 'select_subclasses'): queryset = queryset.select_subclasses() @@ -71,44 +115,10 @@ def get_activity_feed( # exclude deleted queryset = queryset.exclude(deleted=True).order_by('-published_date') - # exclude blocks from both directions - if not user.is_anonymous: - blocked = models.User.objects.filter(id__in=user.blocks.all()).all() - queryset = queryset.exclude( - Q(user__in=blocked) | Q(user__blocks=user)) - - # you can't see followers only or direct messages if you're not logged in - if user.is_anonymous: - privacy = [p for p in privacy if not p in ['followers', 'direct']] - - # filter to only privided privacy levels - queryset = queryset.filter(privacy__in=privacy) - - # only include statuses the user follows - if following_only: - queryset = queryset.exclude( - ~Q(# remove everythign except - Q(user__in=user.following.all()) | # user follwoing - Q(user=user) |# is self - Q(mention_users=user)# mentions user - ), - ) - # exclude followers-only statuses the user doesn't follow - elif 'followers' in privacy: - queryset = queryset.exclude( - ~Q(# user isn't following and it isn't their own status - Q(user__in=user.following.all()) | Q(user=user) - ), - privacy='followers' # and the status is followers only - ) - - # exclude direct messages not intended for the user - if 'direct' in privacy: - queryset = queryset.exclude( - ~Q( - Q(user=user) | Q(mention_users=user) - ), privacy='direct' - ) + # apply privacy filters + privacy = privacy if isinstance(privacy, list) else [privacy] + queryset = privacy_filter( + user, queryset, privacy, following_only=following_only) # filter for only local status if local_only: diff --git a/bookwyrm/views/list.py b/bookwyrm/views/list.py index bc022c2e6..ccc6c8c83 100644 --- a/bookwyrm/views/list.py +++ b/bookwyrm/views/list.py @@ -9,7 +9,7 @@ from django.views import View from bookwyrm import forms, models from bookwyrm.activitypub import ActivitypubResponse -from .helpers import is_api_request, object_visible_to_user +from .helpers import is_api_request, object_visible_to_user, privacy_filter # pylint: disable=no-self-use @@ -18,7 +18,10 @@ class Lists(View): def get(self, request): ''' display a book list ''' user = request.user if request.user.is_authenticated else None - lists = models.List.objects.filter(~Q(user=user)).all() + lists = models.List.objects.filter( + ~Q(user=user), + ).all() + lists = privacy_filter(request.user, lists, ['public', 'followers']) data = { 'title': 'Lists', 'lists': lists, @@ -30,7 +33,11 @@ class Lists(View): # pylint: disable=unused-argument def post(self, request): ''' create a book_list ''' - book_list = None + form = forms.ListForm(request.POST) + if not form.is_valid(): + print(form.errors) + return redirect('lists') + book_list = form.save() return redirect(book_list.local_path)