filter hasPart recent posts on hide labels
Mirrors the gating applied to comment[]: recent posts whose own labels match hideEmbedLabels or hideReplyLabels are dropped from hasPart entirely so flagged content isn't surfaced into the profile's structured data. Negated post-view labels are honored.
This commit is contained in:
@@ -411,7 +411,9 @@ func buildPostJSONLD(pv *appbsky.FeedDefs_PostView, replies []*appbsky.FeedDefs_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildProfileJSONLD marshals a ProfilePage (with hasPart recent posts).
|
// buildProfileJSONLD marshals a ProfilePage (with hasPart recent posts).
|
||||||
func buildProfileJSONLD(pv *appbsky.ActorDefs_ProfileViewDetailed, recentPosts []*appbsky.FeedDefs_PostView, hideLabels map[string]bool) (string, error) {
|
// Recent posts whose own labels match hideLabels or hideReplyLabels are
|
||||||
|
// dropped from hasPart, mirroring the gating applied to comment[].
|
||||||
|
func buildProfileJSONLD(pv *appbsky.ActorDefs_ProfileViewDetailed, recentPosts []*appbsky.FeedDefs_PostView, hideLabels, hideReplyLabels map[string]bool) (string, error) {
|
||||||
if pv == nil {
|
if pv == nil {
|
||||||
return "", fmt.Errorf("nil profile view")
|
return "", fmt.Errorf("nil profile view")
|
||||||
}
|
}
|
||||||
@@ -463,6 +465,11 @@ func buildProfileJSONLD(pv *appbsky.ActorDefs_ProfileViewDetailed, recentPosts [
|
|||||||
if len(page.HasPart) >= maxRecentPosts {
|
if len(page.HasPart) >= maxRecentPosts {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// Drop labeled posts entirely so flagged content isn't surfaced
|
||||||
|
// into the profile's structured data.
|
||||||
|
if postHasHideLabel(rp, hideReplyLabels) || postHasHideLabel(rp, hideLabels) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Recent posts go in nested form. No replies are passed, so the
|
// Recent posts go in nested form. No replies are passed, so the
|
||||||
// reply-label set is irrelevant; pass nil.
|
// reply-label set is irrelevant; pass nil.
|
||||||
node := buildPostNode(rp, nil, hideLabels, nil)
|
node := buildPostNode(rp, nil, hideLabels, nil)
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ func TestBuildProfileJSONLD_Basic(t *testing.T) {
|
|||||||
PostsCount: intPtr(200),
|
PostsCount: intPtr(200),
|
||||||
CreatedAt: strPtr("2023-01-01T00:00:00Z"),
|
CreatedAt: strPtr("2023-01-01T00:00:00Z"),
|
||||||
}
|
}
|
||||||
out, err := buildProfileJSONLD(pv, nil, hideEmbedLabels)
|
out, err := buildProfileJSONLD(pv, nil, hideEmbedLabels, hideReplyLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -570,7 +570,7 @@ func TestBuildProfileJSONLD_HasPart(t *testing.T) {
|
|||||||
posts = append(posts, makePostView("alice.bsky.social", "did:plc:alice",
|
posts = append(posts, makePostView("alice.bsky.social", "did:plc:alice",
|
||||||
"r"+string(rune('0'+i)), "post"))
|
"r"+string(rune('0'+i)), "post"))
|
||||||
}
|
}
|
||||||
out, _ := buildProfileJSONLD(pv, posts, hideEmbedLabels)
|
out, _ := buildProfileJSONLD(pv, posts, hideEmbedLabels, hideReplyLabels)
|
||||||
page := unmarshalLD(t, out)
|
page := unmarshalLD(t, out)
|
||||||
hp, ok := page["hasPart"].([]any)
|
hp, ok := page["hasPart"].([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -588,6 +588,51 @@ func TestBuildProfileJSONLD_HasPart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildProfileJSONLD_HasPartLabelFiltered(t *testing.T) {
|
||||||
|
// Recent posts carrying hideReplyLabels or hideEmbedLabels are dropped
|
||||||
|
// from hasPart entirely. Negation is honored on post-view labels.
|
||||||
|
pv := &appbsky.ActorDefs_ProfileViewDetailed{
|
||||||
|
Did: "did:plc:alice", Handle: "alice.bsky.social",
|
||||||
|
DisplayName: strPtr("Alice"),
|
||||||
|
CreatedAt: strPtr("2023-01-01T00:00:00Z"),
|
||||||
|
}
|
||||||
|
good := makePostView("alice.bsky.social", "did:plc:alice", "good", "ok")
|
||||||
|
hidden := makePostView("alice.bsky.social", "did:plc:alice", "hide", "hidden",
|
||||||
|
withPostLabel("!hide", false))
|
||||||
|
spam := makePostView("alice.bsky.social", "did:plc:alice", "spam", "spam",
|
||||||
|
withSelfLabel("spam"))
|
||||||
|
embedHide := makePostView("alice.bsky.social", "did:plc:alice", "harm", "embed-only",
|
||||||
|
withPostLabel("self-harm", false))
|
||||||
|
negated := makePostView("alice.bsky.social", "did:plc:alice", "neg", "negated",
|
||||||
|
withPostLabel("!hide", true))
|
||||||
|
|
||||||
|
out, _ := buildProfileJSONLD(pv, []*appbsky.FeedDefs_PostView{
|
||||||
|
good, hidden, spam, embedHide, negated,
|
||||||
|
}, hideEmbedLabels, hideReplyLabels)
|
||||||
|
page := unmarshalLD(t, out)
|
||||||
|
hp, _ := page["hasPart"].([]any)
|
||||||
|
|
||||||
|
got := make(map[string]bool, len(hp))
|
||||||
|
for _, e := range hp {
|
||||||
|
got[e.(map[string]any)["identifier"].(string)] = true
|
||||||
|
}
|
||||||
|
if !got[good.Uri] {
|
||||||
|
t.Errorf("expected unlabeled post in hasPart")
|
||||||
|
}
|
||||||
|
if !got[negated.Uri] {
|
||||||
|
t.Errorf("negated hide label should not gate; expected post in hasPart")
|
||||||
|
}
|
||||||
|
if got[hidden.Uri] {
|
||||||
|
t.Errorf("post with !hide label should be dropped from hasPart")
|
||||||
|
}
|
||||||
|
if got[spam.Uri] {
|
||||||
|
t.Errorf("self-labeled spam post should be dropped from hasPart")
|
||||||
|
}
|
||||||
|
if got[embedHide.Uri] {
|
||||||
|
t.Errorf("post with hideEmbedLabels label should be dropped from hasPart")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBskyPostURL(t *testing.T) {
|
func TestBskyPostURL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name, handle, rkey, want string
|
name, handle, rkey, want string
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func TestRenderPost_FallsBackToCanonicalizeFilter(t *testing.T) {
|
|||||||
|
|
||||||
func TestRenderProfile_EmitsJSONLD(t *testing.T) {
|
func TestRenderProfile_EmitsJSONLD(t *testing.T) {
|
||||||
pv := newProfileViewDetailed()
|
pv := newProfileViewDetailed()
|
||||||
ld, err := buildProfileJSONLD(pv, nil, hideEmbedLabels)
|
ld, err := buildProfileJSONLD(pv, nil, hideEmbedLabels, hideReplyLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ func TestRenderProfile_EmitsJSONLD(t *testing.T) {
|
|||||||
// before buildProfileJSONLD.
|
// before buildProfileJSONLD.
|
||||||
func TestRenderProfile_AuthRequiredEmitsJSONLD(t *testing.T) {
|
func TestRenderProfile_AuthRequiredEmitsJSONLD(t *testing.T) {
|
||||||
pv := newProfileViewDetailed()
|
pv := newProfileViewDetailed()
|
||||||
ld, err := buildProfileJSONLD(pv, nil, hideEmbedLabels)
|
ld, err := buildProfileJSONLD(pv, nil, hideEmbedLabels, hideReplyLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -713,7 +713,7 @@ func (srv *Server) WebProfile(c echo.Context) error {
|
|||||||
data["requiresAuth"] = true
|
data["requiresAuth"] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if jsonld, err := buildProfileJSONLD(pv, recentPosts, hideEmbedLabels); err == nil {
|
if jsonld, err := buildProfileJSONLD(pv, recentPosts, hideEmbedLabels, hideReplyLabels); err == nil {
|
||||||
data["profileJSONLD"] = jsonld
|
data["profileJSONLD"] = jsonld
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("failed to build profile JSON-LD for %s: %v", pv.Did, err)
|
log.Warnf("failed to build profile JSON-LD for %s: %v", pv.Did, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user