From 1cf1f2e5430e1946b6f7520e4d339de82a392c21 Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Fri, 25 Nov 2022 18:32:45 -0700 Subject: [PATCH] Implement user discoverability --- activities/migrations/0001_initial.py | 4 ++- core/views.py | 5 ++- templates/identity/create.html | 1 + templates/settings/profile.html | 1 + .../migrations/0002_identity_discoverable.py | 18 +++++++++++ users/models/identity.py | 6 +++- users/views/identity.py | 9 ++++++ users/views/settings/profile.py | 31 +++++++++++++------ 8 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 users/migrations/0002_identity_discoverable.py diff --git a/activities/migrations/0001_initial.py b/activities/migrations/0001_initial.py index 19f3026c..82a90859 100644 --- a/activities/migrations/0001_initial.py +++ b/activities/migrations/0001_initial.py @@ -83,7 +83,7 @@ class Migration(migrations.Migration): ( "author", models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, + on_delete=django.db.models.deletion.CASCADE, related_name="posts", to="users.identity", ), @@ -257,6 +257,8 @@ class Migration(migrations.Migration): models.CharField( choices=[ ("post", "Post"), + ("post_edited", "Post Edited"), + ("post_deleted", "Post Deleted"), ("interaction", "Interaction"), ("undo_interaction", "Undo Interaction"), ], diff --git a/core/views.py b/core/views.py index fdc66426..889fa27f 100644 --- a/core/views.py +++ b/core/views.py @@ -19,7 +19,10 @@ class LoggedOutHomepage(TemplateView): def get_context_data(self): return { - "identities": Identity.objects.filter(local=True).order_by("-created")[:20], + "identities": Identity.objects.filter( + local=True, + discoverable=True, + ).order_by("-created")[:20], } diff --git a/templates/identity/create.html b/templates/identity/create.html index fbdd66c1..8afd1d9d 100644 --- a/templates/identity/create.html +++ b/templates/identity/create.html @@ -13,6 +13,7 @@

Create New Identity

{% include "forms/_field.html" with field=form.username %} {% include "forms/_field.html" with field=form.domain %} {% include "forms/_field.html" with field=form.name %} + {% include "forms/_field.html" with field=form.discoverable %}
diff --git a/templates/settings/profile.html b/templates/settings/profile.html index 12ea2063..806a9308 100644 --- a/templates/settings/profile.html +++ b/templates/settings/profile.html @@ -9,6 +9,7 @@ Details {% include "forms/_field.html" with field=form.name %} {% include "forms/_field.html" with field=form.summary %} + {% include "forms/_field.html" with field=form.discoverable %}
Images diff --git a/users/migrations/0002_identity_discoverable.py b/users/migrations/0002_identity_discoverable.py new file mode 100644 index 00000000..cbee6284 --- /dev/null +++ b/users/migrations/0002_identity_discoverable.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.3 on 2022-11-26 01:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("users", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="identity", + name="discoverable", + field=models.BooleanField(default=True), + ), + ] diff --git a/users/models/identity.py b/users/models/identity.py index 7730c8fa..5d7fd0be 100644 --- a/users/models/identity.py +++ b/users/models/identity.py @@ -67,6 +67,7 @@ class Identity(StatorModel): name = models.CharField(max_length=500, blank=True, null=True) summary = models.TextField(blank=True, null=True) manually_approves_followers = models.BooleanField(blank=True, null=True) + discoverable = models.BooleanField(default=True) profile_uri = models.CharField(max_length=500, blank=True, null=True) inbox_uri = models.CharField(max_length=500, blank=True, null=True) @@ -240,7 +241,7 @@ def to_ap(self): }, "published": self.created.strftime("%Y-%m-%dT%H:%M:%SZ"), "url": self.absolute_profile_uri(), - "discoverable": True, + "http://joinmastodon.org/ns#discoverable": self.discoverable, } if self.name: response["name"] = self.name @@ -373,6 +374,9 @@ async def fetch_actor(self) -> bool: self.public_key_id = document.get("publicKey", {}).get("id") self.icon_uri = document.get("icon", {}).get("url") self.image_uri = document.get("image", {}).get("url") + self.discoverable = document.get( + "http://joinmastodon.org/ns#discoverable", True + ) # Now go do webfinger with that info to see if we can get a canonical domain actor_url_parts = urlparse(self.actor_uri) get_domain = sync_to_async(Domain.get_remote_domain) diff --git a/users/views/identity.py b/users/views/identity.py index 7e50a0d9..22b42f47 100644 --- a/users/views/identity.py +++ b/users/views/identity.py @@ -150,6 +150,14 @@ class form_class(forms.Form): name = forms.CharField( help_text="The display name other users see. You can change this easily." ) + discoverable = forms.BooleanField( + help_text="If this user is visible on the frontpage and in user directories.", + initial=True, + widget=forms.Select( + choices=[(True, "Discoverable"), (False, "Not Discoverable")] + ), + required=False, + ) def __init__(self, user, *args, **kwargs): super().__init__(*args, **kwargs) @@ -219,6 +227,7 @@ def form_valid(self, form): domain_id=domain, name=form.cleaned_data["name"], local=True, + discoverable=form.cleaned_data["discoverable"], ) new_identity.users.add(self.request.user) new_identity.generate_keypair() diff --git a/users/views/settings/profile.py b/users/views/settings/profile.py index 98a18e09..288badde 100644 --- a/users/views/settings/profile.py +++ b/users/views/settings/profile.py @@ -33,13 +33,22 @@ class form_class(forms.Form): image = forms.ImageField( required=False, help_text="Shown at the top of your profile" ) + discoverable = forms.BooleanField( + help_text="If this user is visible on the frontpage and in user directories.", + widget=forms.Select( + choices=[(True, "Discoverable"), (False, "Not Discoverable")] + ), + required=False, + ) def get_initial(self): + identity = self.request.identity return { - "name": self.request.identity.name, - "summary": self.request.identity.summary, - "icon": self.request.identity.icon and self.request.identity.icon.url, - "image": self.request.identity.image and self.request.identity.image.url, + "name": identity.name, + "summary": identity.summary, + "icon": identity.icon and identity.icon.url, + "image": identity.image and identity.image.url, + "discoverable": identity.discoverable, } def resize_image(self, image: File, *, size: tuple[int, int]) -> File: @@ -50,21 +59,23 @@ def resize_image(self, image: File, *, size: tuple[int, int]) -> File: return File(new_image_bytes) def form_valid(self, form): - # Update identity name and summary - self.request.identity.name = form.cleaned_data["name"] - self.request.identity.summary = form.cleaned_data["summary"] + # Update basic info + identity = self.request.identity + identity.name = form.cleaned_data["name"] + identity.summary = form.cleaned_data["summary"] + identity.discoverable = form.cleaned_data["discoverable"] # Resize images icon = form.cleaned_data.get("icon") image = form.cleaned_data.get("image") if isinstance(icon, File): - self.request.identity.icon.save( + identity.icon.save( icon.name, self.resize_image(icon, size=(400, 400)), ) if isinstance(image, File): - self.request.identity.image.save( + identity.image.save( image.name, self.resize_image(image, size=(1500, 500)), ) - self.request.identity.save() + identity.save() return redirect(".")