Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for enabling Mastodon 4.2 search indexing #656

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Account(Schema):
bot: bool
group: bool
discoverable: bool
indexable: bool
moved: Union[None, bool, "Account"]
suspended: bool = False
limited: bool = False
Expand Down
1 change: 1 addition & 0 deletions core/ld.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@
"@context": {
"toot": "http://joinmastodon.org/ns#",
"discoverable": "toot:discoverable",
"indexable": "toot:indexable",
"devices": "toot:devices",
"featured": "toot:featured",
"featuredTags": "toot:featuredTags",
Expand Down
1 change: 1 addition & 0 deletions templates/identity/create.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ <h1>Create New Identity</h1>
{% include "forms/_field.html" with field=form.domain %}
{% include "forms/_field.html" with field=form.name %}
{% include "forms/_field.html" with field=form.discoverable %}
{% include "forms/_field.html" with field=form.indexable %}
</fieldset>
<div class="buttons">
<button>Create</button>
Expand Down
1 change: 1 addition & 0 deletions templates/settings/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<fieldset>
<legend>Privacy</legend>
{% include "forms/_field.html" with field=form.discoverable %}
{% include "forms/_field.html" with field=form.indexable %}
{% include "forms/_field.html" with field=form.visible_follows %}
{% include "forms/_field.html" with field=form.search_enabled %}
</fieldset>
Expand Down
2 changes: 2 additions & 0 deletions tests/users/models/test_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ def test_fetch_actor(httpx_mock, config_system):
assert identity.image_uri == "https://example.com/image.jpg"
assert identity.summary == "<p>A test user</p>"
assert "ts-a-faaaake" in identity.public_key
# convention is that indexability should be opt-in
assert not identity.indexable


@pytest.mark.django_db
Expand Down
17 changes: 17 additions & 0 deletions users/migrations/0023_identity_indexable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.3 on 2023-11-16 13:03

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("users", "0022_follow_request"),
]

operations = [
migrations.AddField(
model_name="identity",
name="indexable",
field=models.BooleanField(default=False),
),
]
4 changes: 4 additions & 0 deletions users/models/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class Restriction(models.IntegerChoices):
summary = models.TextField(blank=True, null=True)
manually_approves_followers = models.BooleanField(blank=True, null=True)
discoverable = models.BooleanField(default=True)
indexable = models.BooleanField(default=False)

profile_uri = models.CharField(max_length=500, blank=True, null=True)
inbox_uri = models.CharField(max_length=500, blank=True, null=True)
Expand Down Expand Up @@ -557,6 +558,7 @@ def to_ap(self):
"published": self.created.strftime("%Y-%m-%dT%H:%M:%SZ"),
"url": self.absolute_profile_uri(),
"toot:discoverable": self.discoverable,
"toot:indexable": self.indexable,
}
if self.name:
response["name"] = self.name
Expand Down Expand Up @@ -914,6 +916,7 @@ def fetch_actor(self) -> bool:
self.icon_uri = get_first_image_url(document.get("icon", None))
self.image_uri = get_first_image_url(document.get("image", None))
self.discoverable = document.get("toot:discoverable", True)
self.indexable = document.get("toot:indexable", False)
# Profile links/metadata
self.metadata = []
for attachment in get_list(document, "attachment"):
Expand Down Expand Up @@ -1051,6 +1054,7 @@ def to_mastodon_json(self, source=False, include_counts=True):
"bot": self.actor_type.lower() in ["service", "application"],
"group": self.actor_type.lower() == "group",
"discoverable": self.discoverable,
"indexable": self.indexable,
"suspended": False,
"limited": False,
"created_at": format_ld_date(
Expand Down
2 changes: 2 additions & 0 deletions users/services/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def create(
domain: Domain,
name: str,
discoverable: bool = True,
indexable: bool = False,
) -> Identity:
identity = Identity.objects.create(
actor_uri=f"https://{domain.uri_domain}/@{username}@{domain.domain}/",
Expand All @@ -44,6 +45,7 @@ def create(
name=name,
local=True,
discoverable=discoverable,
indexable=indexable,
)
identity.users.add(user)
identity.generate_keypair()
Expand Down
9 changes: 9 additions & 0 deletions users/views/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@ class form_class(forms.Form):
),
required=False,
)
indexable = forms.BooleanField(
help_text="Should this user's activities be indexable on other servers.",
initial=False,
widget=forms.Select(
choices=[(True, "Indexable"), (False, "Not Indexable")]
),
required=False,
)

def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -385,6 +393,7 @@ def form_valid(self, form):
domain=domain_instance,
name=form.cleaned_data["name"],
discoverable=form.cleaned_data["discoverable"],
indexable=form.cleaned_data["indexable"],
)
self.request.session["identity_id"] = identity.id
return redirect(identity.urls.view)
Expand Down
9 changes: 9 additions & 0 deletions users/views/settings/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class form_class(forms.Form):
),
required=False,
)
indexable = forms.BooleanField(
help_text="Opt-in to be indexed for search on other servers.\n(Disabling this does not guarantee third-party servers won't index your posts without permission)",
widget=forms.Select(
choices=[(True, "Indexable"), (False, "Not Indexable")]
),
required=False,
)
visible_follows = forms.BooleanField(
help_text="Whether or not to show your following and follower counts in your profile",
widget=forms.Select(choices=[(True, "Visible"), (False, "Hidden")]),
Expand Down Expand Up @@ -93,6 +100,7 @@ def get_initial(self):
"icon": self.identity.icon and self.identity.icon.url,
"image": self.identity.image and self.identity.image.url,
"discoverable": self.identity.discoverable,
"indexable": self.identity.indexable,
"visible_follows": self.identity.config_identity.visible_follows,
"metadata": self.identity.metadata or [],
"search_enabled": self.identity.config_identity.search_enabled,
Expand All @@ -104,6 +112,7 @@ def form_valid(self, form):
service = IdentityService(self.identity)
self.identity.name = form.cleaned_data["name"]
self.identity.discoverable = form.cleaned_data["discoverable"]
self.identity.indexable = form.cleaned_data["indexable"]
service.set_summary(form.cleaned_data["summary"])
# Resize images
icon = form.cleaned_data.get("icon")
Expand Down