mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-28 20:02:24 -05:00
[feature] Use hidesToPublicFromUnauthedWeb and hidesCcPublicFromUnauthedWeb properties for web visibility of statuses (#4315)
This pull request implements two new properties on ActivityPub actors: `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb`. As documented, these properties allow actors to signal their preference for whether or not their posts should be hidden from unauthenticated web views (ie., web pages like the GtS frontend, web apps like the Mastodon frontend, web APIs like the Mastodon public timeline API, etc). This allows remote accounts to *opt in* to having their unlisted visibility posts shown in (for example) the replies section of the web view of a GtS thread. In future, we can also use these properties to determine whether we should show boosts of a remote actor's post on a GtS profile, and that sort of thing. In keeping with our stance around privacy by default, GtS assumes `true` for `hidesCcPublicFromUnauthedWeb` if the property is not set on a remote actor, ie., hide unlisted/unlocked posts by default. `hidesToPublicFromUnauthedWeb` is assumed to be `false` if the property is not set on a remote actor, ie., show public posts by default. ~~WIP as I still want to work on the documentation for this a bit.~~ New props are already in the namespace document: https://gotosocial.org/ns Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4315 Reviewed-by: kim <gruf@noreply.codeberg.org> Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
This commit is contained in:
parent
c8a4ce9a88
commit
dcfc9b7885
159 changed files with 10900 additions and 2918 deletions
|
|
@ -227,6 +227,8 @@ type Accountable interface {
|
|||
WithMovedTo
|
||||
WithAlsoKnownAs
|
||||
WithManuallyApprovesFollowers
|
||||
WithHidesToPublicFromUnauthedWeb
|
||||
WithHidesCcPublicFromUnauthedWeb
|
||||
WithEndpoints
|
||||
WithTag
|
||||
WithPublished
|
||||
|
|
@ -711,6 +713,18 @@ type WithManuallyApprovesFollowers interface {
|
|||
SetActivityStreamsManuallyApprovesFollowers(vocab.ActivityStreamsManuallyApprovesFollowersProperty)
|
||||
}
|
||||
|
||||
// WithHidesToPublicFromUnauthedWeb represents a Person or profile with the hidesToPublicFromUnauthedWeb property.
|
||||
type WithHidesToPublicFromUnauthedWeb interface {
|
||||
GetGoToSocialHidesToPublicFromUnauthedWeb() vocab.GoToSocialHidesToPublicFromUnauthedWebProperty
|
||||
SetGoToSocialHidesToPublicFromUnauthedWeb(vocab.GoToSocialHidesToPublicFromUnauthedWebProperty)
|
||||
}
|
||||
|
||||
// WithHidesCcPublicFromUnauthedWeb represents a Person or profile with the hidesCcPublicFromUnauthedWeb property.
|
||||
type WithHidesCcPublicFromUnauthedWeb interface {
|
||||
GetGoToSocialHidesCcPublicFromUnauthedWeb() vocab.GoToSocialHidesCcPublicFromUnauthedWebProperty
|
||||
SetGoToSocialHidesCcPublicFromUnauthedWeb(vocab.GoToSocialHidesCcPublicFromUnauthedWebProperty)
|
||||
}
|
||||
|
||||
// WithEndpoints represents a Person or profile with the endpoints property
|
||||
type WithEndpoints interface {
|
||||
GetActivityStreamsEndpoints() vocab.ActivityStreamsEndpointsProperty
|
||||
|
|
|
|||
|
|
@ -562,6 +562,48 @@ func SetManuallyApprovesFollowers(with WithManuallyApprovesFollowers, manuallyAp
|
|||
mafProp.Set(manuallyApprovesFollowers)
|
||||
}
|
||||
|
||||
// GetHidesToPublicFromUnauthedWeb returns the boolean contained in the hidesToPublicFromUnauthedWeb property of 'with'.
|
||||
//
|
||||
// Returns default 'false' if property unusable or not set.
|
||||
func GetHidesToPublicFromUnauthedWeb(with WithHidesToPublicFromUnauthedWeb) bool {
|
||||
hidesProp := with.GetGoToSocialHidesToPublicFromUnauthedWeb()
|
||||
if hidesProp == nil || !hidesProp.IsXMLSchemaBoolean() {
|
||||
return false
|
||||
}
|
||||
return hidesProp.Get()
|
||||
}
|
||||
|
||||
// SetHidesToPublicFromUnauthedWeb sets the given boolean on the hidesToPublicFromUnauthedWeb property of 'with'.
|
||||
func SetHidesToPublicFromUnauthedWeb(with WithHidesToPublicFromUnauthedWeb, hidesToPublicFromUnauthedWeb bool) {
|
||||
hidesProp := with.GetGoToSocialHidesToPublicFromUnauthedWeb()
|
||||
if hidesProp == nil {
|
||||
hidesProp = streams.NewGoToSocialHidesToPublicFromUnauthedWebProperty()
|
||||
with.SetGoToSocialHidesToPublicFromUnauthedWeb(hidesProp)
|
||||
}
|
||||
hidesProp.Set(hidesToPublicFromUnauthedWeb)
|
||||
}
|
||||
|
||||
// GetHidesCcPublicFromUnauthedWeb returns the boolean contained in the hidesCcPublicFromUnauthedWeb property of 'with'.
|
||||
//
|
||||
// Returns default 'true' if property unusable or not set.
|
||||
func GetHidesCcPublicFromUnauthedWeb(with WithHidesCcPublicFromUnauthedWeb) bool {
|
||||
hidesProp := with.GetGoToSocialHidesCcPublicFromUnauthedWeb()
|
||||
if hidesProp == nil || !hidesProp.IsXMLSchemaBoolean() {
|
||||
return true
|
||||
}
|
||||
return hidesProp.Get()
|
||||
}
|
||||
|
||||
// SetHidesCcPublicFromUnauthedWeb sets the given boolean on the hidesCcPublicFromUnauthedWeb property of 'with'.
|
||||
func SetHidesCcPublicFromUnauthedWeb(with WithHidesCcPublicFromUnauthedWeb, hidesCcPublicFromUnauthedWeb bool) {
|
||||
hidesProp := with.GetGoToSocialHidesCcPublicFromUnauthedWeb()
|
||||
if hidesProp == nil {
|
||||
hidesProp = streams.NewGoToSocialHidesCcPublicFromUnauthedWebProperty()
|
||||
with.SetGoToSocialHidesCcPublicFromUnauthedWeb(hidesProp)
|
||||
}
|
||||
hidesProp.Set(hidesCcPublicFromUnauthedWeb)
|
||||
}
|
||||
|
||||
// GetApprovedBy returns the URL contained in
|
||||
// the ApprovedBy property of 'with', if set.
|
||||
func GetApprovedBy(with WithApprovedBy) *url.URL {
|
||||
|
|
|
|||
|
|
@ -1054,10 +1054,21 @@ func (a *accountDB) GetAccountWebStatuses(
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// Check for an easy case: account exposes no statuses via the web.
|
||||
webVisibility := account.Settings.WebVisibility
|
||||
if webVisibility == gtsmodel.VisibilityNone {
|
||||
return nil, db.ErrNoEntries
|
||||
// Derive visibility of statuses on the web.
|
||||
//
|
||||
// We don't account for situations where someone
|
||||
// hides public statuses but shows unlocked/unlisted,
|
||||
// since that's only an option for remote accts.
|
||||
var (
|
||||
hideAll = *account.HidesToPublicFromUnauthedWeb
|
||||
publicOnly = *account.HidesCcPublicFromUnauthedWeb
|
||||
)
|
||||
|
||||
if hideAll {
|
||||
// Account hides all
|
||||
// statuses from web,
|
||||
// nothing to do.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Ensure reasonable
|
||||
|
|
@ -1075,27 +1086,18 @@ func (a *accountDB) GetAccountWebStatuses(
|
|||
Column("status.id").
|
||||
Where("? = ?", bun.Ident("status.account_id"), account.ID)
|
||||
|
||||
// Select statuses for this account according
|
||||
// to their web visibility preference.
|
||||
switch webVisibility {
|
||||
|
||||
case gtsmodel.VisibilityPublic:
|
||||
// Select statuses according to
|
||||
// account's web visibility prefs.
|
||||
if publicOnly {
|
||||
// Only Public statuses.
|
||||
q = q.Where("? = ?", bun.Ident("status.visibility"), gtsmodel.VisibilityPublic)
|
||||
|
||||
case gtsmodel.VisibilityUnlocked:
|
||||
} else {
|
||||
// Public or Unlocked.
|
||||
visis := []gtsmodel.Visibility{
|
||||
gtsmodel.VisibilityPublic,
|
||||
gtsmodel.VisibilityUnlocked,
|
||||
}
|
||||
q = q.Where("? IN (?)", bun.Ident("status.visibility"), bun.In(visis))
|
||||
|
||||
default:
|
||||
return nil, gtserror.Newf(
|
||||
"unrecognized web visibility for account %s: %s",
|
||||
account.ID, webVisibility,
|
||||
)
|
||||
}
|
||||
|
||||
// Don't show replies, boosts, or
|
||||
|
|
|
|||
|
|
@ -120,20 +120,21 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
|
|||
}
|
||||
|
||||
account = >smodel.Account{
|
||||
ID: accountID,
|
||||
Username: newSignup.Username,
|
||||
DisplayName: newSignup.Username,
|
||||
URI: uris.UserURI,
|
||||
URL: uris.UserURL,
|
||||
InboxURI: uris.InboxURI,
|
||||
OutboxURI: uris.OutboxURI,
|
||||
FollowingURI: uris.FollowingURI,
|
||||
FollowersURI: uris.FollowersURI,
|
||||
FeaturedCollectionURI: uris.FeaturedCollectionURI,
|
||||
ActorType: gtsmodel.AccountActorTypePerson,
|
||||
PrivateKey: privKey,
|
||||
PublicKey: &privKey.PublicKey,
|
||||
PublicKeyURI: uris.PublicKeyURI,
|
||||
ID: accountID,
|
||||
Username: newSignup.Username,
|
||||
DisplayName: newSignup.Username,
|
||||
URI: uris.UserURI,
|
||||
URL: uris.UserURL,
|
||||
InboxURI: uris.InboxURI,
|
||||
OutboxURI: uris.OutboxURI,
|
||||
FollowingURI: uris.FollowingURI,
|
||||
FollowersURI: uris.FollowersURI,
|
||||
FeaturedCollectionURI: uris.FeaturedCollectionURI,
|
||||
ActorType: gtsmodel.AccountActorTypePerson,
|
||||
PrivateKey: privKey,
|
||||
PublicKey: &privKey.PublicKey,
|
||||
PublicKeyURI: uris.PublicKeyURI,
|
||||
HidesCcPublicFromUnauthedWeb: util.Ptr(true), // GtS default to hide unlisted.
|
||||
}
|
||||
|
||||
// Insert the new account!
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20250708074906_unauthed_web_updates/common"
|
||||
newmodel "code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20250708074906_unauthed_web_updates/new"
|
||||
oldmodel "code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20250708074906_unauthed_web_updates/old"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func init() {
|
||||
up := func(ctx context.Context, db *bun.DB) error {
|
||||
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
|
||||
var account *newmodel.Account
|
||||
accountType := reflect.TypeOf(account)
|
||||
|
||||
// Add new columns to accounts
|
||||
// table if they don't exist already.
|
||||
for _, new := range []struct {
|
||||
dbCol string
|
||||
fieldName string
|
||||
}{
|
||||
{
|
||||
dbCol: "hides_to_public_from_unauthed_web",
|
||||
fieldName: "HidesToPublicFromUnauthedWeb",
|
||||
},
|
||||
{
|
||||
dbCol: "hides_cc_public_from_unauthed_web",
|
||||
fieldName: "HidesCcPublicFromUnauthedWeb",
|
||||
},
|
||||
} {
|
||||
exists, err := doesColumnExist(
|
||||
ctx,
|
||||
tx,
|
||||
"accounts",
|
||||
new.dbCol,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
// Column already exists.
|
||||
continue
|
||||
}
|
||||
|
||||
// Column doesn't exist yet, add it.
|
||||
colDef, err := getBunColumnDef(tx, accountType, new.fieldName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making column def: %w", err)
|
||||
}
|
||||
|
||||
log.Infof(ctx, "adding accounts.%s column...", new.dbCol)
|
||||
if _, err := tx.
|
||||
NewAddColumn().
|
||||
Model(account).
|
||||
ColumnExpr(colDef).
|
||||
Exec(ctx); err != nil {
|
||||
return fmt.Errorf("error adding column: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// For each account settings we have
|
||||
// stored on this instance, set the
|
||||
// new account columns to values
|
||||
// corresponding to the setting.
|
||||
allSettings := []*oldmodel.AccountSettings{}
|
||||
if err := tx.
|
||||
NewSelect().
|
||||
Model(&allSettings).
|
||||
Column("account_id", "web_visibility").
|
||||
Scan(ctx); err != nil {
|
||||
return fmt.Errorf("error selecting settings: %w", err)
|
||||
}
|
||||
|
||||
for _, settings := range allSettings {
|
||||
|
||||
// Derive web visibility.
|
||||
var (
|
||||
hidesToPublicFromUnauthedWeb bool
|
||||
hidesCcPublicFromUnauthedWeb bool
|
||||
)
|
||||
|
||||
switch settings.WebVisibility {
|
||||
|
||||
// Show nothing.
|
||||
case common.VisibilityNone:
|
||||
hidesToPublicFromUnauthedWeb = true
|
||||
hidesCcPublicFromUnauthedWeb = true
|
||||
|
||||
// Show public only (GtS default).
|
||||
case common.VisibilityPublic:
|
||||
hidesToPublicFromUnauthedWeb = false
|
||||
hidesCcPublicFromUnauthedWeb = true
|
||||
|
||||
// Show public + unlisted (Masto default).
|
||||
case common.VisibilityUnlocked:
|
||||
hidesToPublicFromUnauthedWeb = false
|
||||
hidesCcPublicFromUnauthedWeb = false
|
||||
|
||||
default:
|
||||
log.Warnf(ctx,
|
||||
"local account %s had unrecognized settings.WebVisibility %d, skipping...",
|
||||
settings.AccountID, settings.WebVisibility,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// Update account.
|
||||
if _, err := tx.
|
||||
NewUpdate().
|
||||
Table("accounts").
|
||||
Set("? = ?", bun.Ident("hides_to_public_from_unauthed_web"), hidesToPublicFromUnauthedWeb).
|
||||
Set("? = ?", bun.Ident("hides_cc_public_from_unauthed_web"), hidesCcPublicFromUnauthedWeb).
|
||||
Where("? = ?", bun.Ident("id"), settings.AccountID).Exec(ctx); err != nil {
|
||||
return fmt.Errorf("error updating local account: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the old web_visibility column.
|
||||
if _, err := tx.
|
||||
NewDropColumn().
|
||||
Model((*oldmodel.AccountSettings)(nil)).
|
||||
Column("web_visibility").
|
||||
Exec(ctx); err != nil {
|
||||
return fmt.Errorf("error dropping old web_visibility column: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
down := func(ctx context.Context, db *bun.DB) error {
|
||||
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := Migrations.Register(up, down); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
// Visibility represents the
|
||||
// visibility granularity of a status.
|
||||
type Visibility int16
|
||||
|
||||
const (
|
||||
// VisibilityNone means nobody can see this.
|
||||
// It's only used for web status visibility.
|
||||
VisibilityNone Visibility = 1
|
||||
|
||||
// VisibilityPublic means this status will
|
||||
// be visible to everyone on all timelines.
|
||||
VisibilityPublic Visibility = 2
|
||||
|
||||
// VisibilityUnlocked means this status will be visible to everyone,
|
||||
// but will only show on home timeline to followers, and in lists.
|
||||
VisibilityUnlocked Visibility = 3
|
||||
|
||||
// VisibilityFollowersOnly means this status is viewable to followers only.
|
||||
VisibilityFollowersOnly Visibility = 4
|
||||
|
||||
// VisibilityMutualsOnly means this status
|
||||
// is visible to mutual followers only.
|
||||
VisibilityMutualsOnly Visibility = 5
|
||||
|
||||
// VisibilityDirect means this status is
|
||||
// visible only to mentioned recipients.
|
||||
VisibilityDirect Visibility = 6
|
||||
|
||||
// VisibilityDefault is used when no other setting can be found.
|
||||
VisibilityDefault Visibility = VisibilityUnlocked
|
||||
)
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package gtsmodel
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20250708074906_unauthed_web_updates/common"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"`
|
||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
FetchedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
Username string `bun:",nullzero,notnull,unique:accounts_username_domain_uniq"`
|
||||
Domain string `bun:",nullzero,unique:accounts_username_domain_uniq"`
|
||||
AvatarMediaAttachmentID string `bun:"type:CHAR(26),nullzero"`
|
||||
AvatarRemoteURL string `bun:",nullzero"`
|
||||
HeaderMediaAttachmentID string `bun:"type:CHAR(26),nullzero"`
|
||||
HeaderRemoteURL string `bun:",nullzero"`
|
||||
DisplayName string `bun:",nullzero"`
|
||||
EmojiIDs []string `bun:"emojis,array"`
|
||||
Fields []*Field `bun:",nullzero"`
|
||||
FieldsRaw []*Field `bun:",nullzero"`
|
||||
Note string `bun:",nullzero"`
|
||||
NoteRaw string `bun:",nullzero"`
|
||||
AlsoKnownAsURIs []string `bun:"also_known_as_uris,array"`
|
||||
AlsoKnownAs []*Account `bun:"-"`
|
||||
MovedToURI string `bun:",nullzero"`
|
||||
MovedTo *Account `bun:"-"`
|
||||
MoveID string `bun:"type:CHAR(26),nullzero"`
|
||||
Locked *bool `bun:",nullzero,notnull,default:true"`
|
||||
Discoverable *bool `bun:",nullzero,notnull,default:false"`
|
||||
URI string `bun:",nullzero,notnull,unique"`
|
||||
URL string `bun:",nullzero"`
|
||||
InboxURI string `bun:",nullzero"`
|
||||
SharedInboxURI *string `bun:""`
|
||||
OutboxURI string `bun:",nullzero"`
|
||||
FollowingURI string `bun:",nullzero"`
|
||||
FollowersURI string `bun:",nullzero"`
|
||||
FeaturedCollectionURI string `bun:",nullzero"`
|
||||
ActorType int16 `bun:",nullzero,notnull"`
|
||||
PrivateKey *rsa.PrivateKey `bun:""`
|
||||
PublicKey *rsa.PublicKey `bun:",notnull"`
|
||||
PublicKeyURI string `bun:",nullzero,notnull,unique"`
|
||||
PublicKeyExpiresAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
MemorializedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SensitizedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SilencedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SuspendedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SuspensionOrigin string `bun:"type:CHAR(26),nullzero"`
|
||||
|
||||
// Added in this migration:
|
||||
HidesToPublicFromUnauthedWeb *bool `bun:",nullzero,notnull,default:false"`
|
||||
HidesCcPublicFromUnauthedWeb *bool `bun:",nullzero,notnull,default:false"`
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Value string
|
||||
VerifiedAt time.Time `bun:",nullzero"`
|
||||
}
|
||||
|
||||
type AccountSettings struct {
|
||||
AccountID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"`
|
||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
Privacy common.Visibility `bun:",nullzero,default:3"`
|
||||
Sensitive *bool `bun:",nullzero,notnull,default:false"`
|
||||
Language string `bun:",nullzero,notnull,default:'en'"`
|
||||
StatusContentType string `bun:",nullzero"`
|
||||
Theme string `bun:",nullzero"`
|
||||
CustomCSS string `bun:",nullzero"`
|
||||
EnableRSS *bool `bun:",nullzero,notnull,default:false"`
|
||||
HideCollections *bool `bun:",nullzero,notnull,default:false"`
|
||||
WebLayout int16 `bun:",nullzero,notnull,default:1"`
|
||||
InteractionPolicyDirect *struct{} `bun:""`
|
||||
InteractionPolicyMutualsOnly *struct{} `bun:""`
|
||||
InteractionPolicyFollowersOnly *struct{} `bun:""`
|
||||
InteractionPolicyUnlocked *struct{} `bun:""`
|
||||
InteractionPolicyPublic *struct{} `bun:""`
|
||||
|
||||
// Removed in this migration:
|
||||
// WebVisibility common.Visibility `bun:",nullzero,notnull,default:3"`
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package gtsmodel
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20250708074906_unauthed_web_updates/common"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"`
|
||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
FetchedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
Username string `bun:",nullzero,notnull,unique:accounts_username_domain_uniq"`
|
||||
Domain string `bun:",nullzero,unique:accounts_username_domain_uniq"`
|
||||
AvatarMediaAttachmentID string `bun:"type:CHAR(26),nullzero"`
|
||||
AvatarRemoteURL string `bun:",nullzero"`
|
||||
HeaderMediaAttachmentID string `bun:"type:CHAR(26),nullzero"`
|
||||
HeaderRemoteURL string `bun:",nullzero"`
|
||||
DisplayName string `bun:",nullzero"`
|
||||
EmojiIDs []string `bun:"emojis,array"`
|
||||
Fields []*Field `bun:",nullzero"`
|
||||
FieldsRaw []*Field `bun:",nullzero"`
|
||||
Note string `bun:",nullzero"`
|
||||
NoteRaw string `bun:",nullzero"`
|
||||
AlsoKnownAsURIs []string `bun:"also_known_as_uris,array"`
|
||||
AlsoKnownAs []*Account `bun:"-"`
|
||||
MovedToURI string `bun:",nullzero"`
|
||||
MovedTo *Account `bun:"-"`
|
||||
MoveID string `bun:"type:CHAR(26),nullzero"`
|
||||
Locked *bool `bun:",nullzero,notnull,default:true"`
|
||||
Discoverable *bool `bun:",nullzero,notnull,default:false"`
|
||||
URI string `bun:",nullzero,notnull,unique"`
|
||||
URL string `bun:",nullzero"`
|
||||
InboxURI string `bun:",nullzero"`
|
||||
SharedInboxURI *string `bun:""`
|
||||
OutboxURI string `bun:",nullzero"`
|
||||
FollowingURI string `bun:",nullzero"`
|
||||
FollowersURI string `bun:",nullzero"`
|
||||
FeaturedCollectionURI string `bun:",nullzero"`
|
||||
ActorType int16 `bun:",nullzero,notnull"`
|
||||
PrivateKey *rsa.PrivateKey `bun:""`
|
||||
PublicKey *rsa.PublicKey `bun:",notnull"`
|
||||
PublicKeyURI string `bun:",nullzero,notnull,unique"`
|
||||
PublicKeyExpiresAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
MemorializedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SensitizedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SilencedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SuspendedAt time.Time `bun:"type:timestamptz,nullzero"`
|
||||
SuspensionOrigin string `bun:"type:CHAR(26),nullzero"`
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Value string
|
||||
VerifiedAt time.Time `bun:",nullzero"`
|
||||
}
|
||||
|
||||
type AccountSettings struct {
|
||||
AccountID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"`
|
||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"`
|
||||
Privacy common.Visibility `bun:",nullzero,default:3"`
|
||||
Sensitive *bool `bun:",nullzero,notnull,default:false"`
|
||||
Language string `bun:",nullzero,notnull,default:'en'"`
|
||||
StatusContentType string `bun:",nullzero"`
|
||||
Theme string `bun:",nullzero"`
|
||||
CustomCSS string `bun:",nullzero"`
|
||||
EnableRSS *bool `bun:",nullzero,notnull,default:false"`
|
||||
HideCollections *bool `bun:",nullzero,notnull,default:false"`
|
||||
WebVisibility common.Visibility `bun:",nullzero,notnull,default:3"`
|
||||
WebLayout int16 `bun:",nullzero,notnull,default:1"`
|
||||
InteractionPolicyDirect *struct{} `bun:""`
|
||||
InteractionPolicyMutualsOnly *struct{} `bun:""`
|
||||
InteractionPolicyFollowersOnly *struct{} `bun:""`
|
||||
InteractionPolicyUnlocked *struct{} `bun:""`
|
||||
InteractionPolicyPublic *struct{} `bun:""`
|
||||
}
|
||||
|
|
@ -115,9 +115,7 @@ func (f *Filter) isStatusVisible(
|
|||
if requester == nil {
|
||||
// Use a different visibility
|
||||
// heuristic for unauthed requests.
|
||||
return f.isStatusVisibleUnauthed(
|
||||
ctx, status,
|
||||
)
|
||||
return f.isStatusVisibleUnauthed(status), nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -245,57 +243,29 @@ func isPendingStatusVisible(requester *gtsmodel.Account, status *gtsmodel.Status
|
|||
return false
|
||||
}
|
||||
|
||||
// isStatusVisibleUnauthed returns whether status is visible without any unauthenticated account.
|
||||
func (f *Filter) isStatusVisibleUnauthed(ctx context.Context, status *gtsmodel.Status) (bool, error) {
|
||||
|
||||
// For remote accounts, only show
|
||||
// Public statuses via the web.
|
||||
if status.Account.IsRemote() {
|
||||
return status.Visibility == gtsmodel.VisibilityPublic, nil
|
||||
}
|
||||
// isStatusVisibleUnauthed returns whether status is visible without authentication.
|
||||
func (f *Filter) isStatusVisibleUnauthed(status *gtsmodel.Status) bool {
|
||||
|
||||
// If status is local only,
|
||||
// never show via the web.
|
||||
// never show without auth.
|
||||
if status.IsLocalOnly() {
|
||||
return false, nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Check account's settings to see
|
||||
// what they expose. Populate these
|
||||
// from the DB if necessary.
|
||||
if status.Account.Settings == nil {
|
||||
var err error
|
||||
status.Account.Settings, err = f.state.DB.GetAccountSettings(ctx, status.Account.ID)
|
||||
if err != nil {
|
||||
return false, gtserror.Newf(
|
||||
"error getting settings for account %s: %w",
|
||||
status.Account.ID, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
switch status.Visibility {
|
||||
|
||||
switch webvis := status.Account.Settings.WebVisibility; webvis {
|
||||
|
||||
// public_only: status must be Public.
|
||||
case gtsmodel.VisibilityPublic:
|
||||
return status.Visibility == gtsmodel.VisibilityPublic, nil
|
||||
// Visible if account doesn't hide Public statuses.
|
||||
return !*status.Account.HidesToPublicFromUnauthedWeb
|
||||
|
||||
// unlisted: status must be Public or Unlocked.
|
||||
case gtsmodel.VisibilityUnlocked:
|
||||
visible := status.Visibility == gtsmodel.VisibilityPublic ||
|
||||
status.Visibility == gtsmodel.VisibilityUnlocked
|
||||
return visible, nil
|
||||
// Visible if account doesn't hide Unlocked statuses.
|
||||
return !*status.Account.HidesCcPublicFromUnauthedWeb
|
||||
|
||||
// none: never show via the web.
|
||||
case gtsmodel.VisibilityNone:
|
||||
return false, nil
|
||||
|
||||
// Huh?
|
||||
default:
|
||||
return false, gtserror.Newf(
|
||||
"unrecognized web visibility for account %s: %s",
|
||||
status.Account.ID, webvis,
|
||||
)
|
||||
// For all other visibilities,
|
||||
// never show without auth.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,16 @@ type Account struct {
|
|||
//
|
||||
// Local accounts only.
|
||||
Stats *AccountStats `bun:"-"`
|
||||
|
||||
// True if the actor hides to-public statusables
|
||||
// from unauthenticated public access via the web.
|
||||
// Default "false" if not set on the actor model.
|
||||
HidesToPublicFromUnauthedWeb *bool `bun:",nullzero,notnull,default:false"`
|
||||
|
||||
// True if the actor hides cc-public statusables
|
||||
// from unauthenticated public access via the web.
|
||||
// Default "true" if not set on the actor model.
|
||||
HidesCcPublicFromUnauthedWeb *bool `bun:",nullzero,notnull,default:true"`
|
||||
}
|
||||
|
||||
// UsernameDomain returns account @username@domain (missing domain if local).
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ type AccountSettings struct {
|
|||
CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
|
||||
EnableRSS *bool `bun:",nullzero,notnull,default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
|
||||
HideCollections *bool `bun:",nullzero,notnull,default:false"` // Hide this account's followers/following collections.
|
||||
WebVisibility Visibility `bun:",nullzero,notnull,default:3"` // Visibility level of statuses that visitors can view via the web profile.
|
||||
WebLayout WebLayout `bun:",nullzero,notnull,default:1"` // Layout to use when showing this profile via the web.
|
||||
InteractionPolicyDirect *InteractionPolicy `bun:""` // Interaction policy to use for new direct visibility statuses by this account. If null, assume default policy.
|
||||
InteractionPolicyMutualsOnly *InteractionPolicy `bun:""` // Interaction policy to use for new mutuals only visibility statuses. If null, assume default policy.
|
||||
|
|
|
|||
|
|
@ -212,6 +212,37 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
|||
}
|
||||
}
|
||||
|
||||
if form.WebVisibility != nil {
|
||||
switch apimodel.Visibility(*form.WebVisibility) {
|
||||
|
||||
// Show none.
|
||||
case apimodel.VisibilityNone:
|
||||
account.HidesToPublicFromUnauthedWeb = util.Ptr(true)
|
||||
account.HidesCcPublicFromUnauthedWeb = util.Ptr(true)
|
||||
|
||||
// Show public only (GtS default).
|
||||
case apimodel.VisibilityPublic:
|
||||
account.HidesToPublicFromUnauthedWeb = util.Ptr(false)
|
||||
account.HidesCcPublicFromUnauthedWeb = util.Ptr(true)
|
||||
|
||||
// Show public and unlisted (Masto default).
|
||||
case apimodel.VisibilityUnlisted:
|
||||
account.HidesToPublicFromUnauthedWeb = util.Ptr(false)
|
||||
account.HidesCcPublicFromUnauthedWeb = util.Ptr(false)
|
||||
|
||||
default:
|
||||
const text = "web_visibility must be one of public, unlisted, or none"
|
||||
err := errors.New(text)
|
||||
return nil, gtserror.NewErrorBadRequest(err, text)
|
||||
}
|
||||
|
||||
acctColumns = append(
|
||||
acctColumns,
|
||||
"hides_to_public_from_unauthed_web",
|
||||
"hides_cc_public_from_unauthed_web",
|
||||
)
|
||||
}
|
||||
|
||||
// Account settings flags.
|
||||
|
||||
if form.Source != nil {
|
||||
|
|
@ -287,21 +318,6 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
|||
settingsColumns = append(settingsColumns, "hide_collections")
|
||||
}
|
||||
|
||||
if form.WebVisibility != nil {
|
||||
apiVis := apimodel.Visibility(*form.WebVisibility)
|
||||
webVisibility := typeutils.APIVisToVis(apiVis)
|
||||
if webVisibility != gtsmodel.VisibilityPublic &&
|
||||
webVisibility != gtsmodel.VisibilityUnlocked &&
|
||||
webVisibility != gtsmodel.VisibilityNone {
|
||||
const text = "web_visibility must be one of public, unlocked, or none"
|
||||
err := errors.New(text)
|
||||
return nil, gtserror.NewErrorBadRequest(err, text)
|
||||
}
|
||||
|
||||
account.Settings.WebVisibility = webVisibility
|
||||
settingsColumns = append(settingsColumns, "web_visibility")
|
||||
}
|
||||
|
||||
if form.WebLayout != nil {
|
||||
webLayout := gtsmodel.ParseWebLayout(*form.WebLayout)
|
||||
if webLayout == gtsmodel.WebLayoutUnknown {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ func (suite *PublicTestSuite) TestPublicTimelineGetNotEmpty() {
|
|||
ctx = suite.T().Context()
|
||||
requester = suite.testAccounts["local_account_1"]
|
||||
// Select 1 *just above* a status we know should
|
||||
// not be in the public timeline -- a public
|
||||
// not be in the public timeline -- an unlisted
|
||||
// reply to one of admin's statuses.
|
||||
maxID = "01HE7XJ1CG84TBKH5V9XKBVGF6"
|
||||
sinceID = ""
|
||||
|
|
@ -91,9 +91,9 @@ func (suite *PublicTestSuite) TestPublicTimelineGetNotEmpty() {
|
|||
// some other statuses were filtered out.
|
||||
suite.NoError(errWithCode)
|
||||
suite.Len(resp.Items, 1)
|
||||
suite.Equal(`<http://localhost:8080/api/v1/timelines/public?limit=1&local=false&max_id=01F8MHCP5P2NWYQ416SBA0XSEV>; rel="next", <http://localhost:8080/api/v1/timelines/public?limit=1&local=false&min_id=01HE7XJ1CG84TBKH5V9XKBVGF5>; rel="prev"`, resp.LinkHeader)
|
||||
suite.Equal(`<http://localhost:8080/api/v1/timelines/public?limit=1&local=false&max_id=01F8MHCP5P2NWYQ416SBA0XSEV>; rel="next", <http://localhost:8080/api/v1/timelines/public?limit=1&local=false&min_id=01FF25D5Q0DH7CHD57CTRS6WK0>; rel="prev"`, resp.LinkHeader)
|
||||
suite.Equal(`http://localhost:8080/api/v1/timelines/public?limit=1&local=false&max_id=01F8MHCP5P2NWYQ416SBA0XSEV`, resp.NextLink)
|
||||
suite.Equal(`http://localhost:8080/api/v1/timelines/public?limit=1&local=false&min_id=01HE7XJ1CG84TBKH5V9XKBVGF5`, resp.PrevLink)
|
||||
suite.Equal(`http://localhost:8080/api/v1/timelines/public?limit=1&local=false&min_id=01FF25D5Q0DH7CHD57CTRS6WK0`, resp.PrevLink)
|
||||
}
|
||||
|
||||
// A timeline containing a status hidden due to filtering should return other statuses with no error.
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ func (suite *DereferenceTestSuite) TestDerefLocalUser() {
|
|||
defer resp.Body.Close()
|
||||
|
||||
suite.Equal(http.StatusOK, resp.StatusCode)
|
||||
suite.EqualValues(2007, resp.ContentLength)
|
||||
suite.Equal("2007", resp.Header.Get("Content-Length"))
|
||||
suite.EqualValues(2109, resp.ContentLength)
|
||||
suite.Equal("2109", resp.Header.Get("Content-Length"))
|
||||
suite.Equal(apiutil.AppActivityLDJSON, resp.Header.Get("Content-Type"))
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
|
|
@ -59,6 +59,7 @@ func (suite *DereferenceTestSuite) TestDerefLocalUser() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -75,6 +76,8 @@ func (suite *DereferenceTestSuite) TestDerefLocalUser() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
|
|||
|
|
@ -244,6 +244,10 @@ func (c *Converter) ASRepresentationToAccount(
|
|||
acct.PublicKey = pkey
|
||||
acct.PublicKeyURI = pkeyURL.String()
|
||||
|
||||
// Web visibility for statuses.
|
||||
acct.HidesToPublicFromUnauthedWeb = util.Ptr(ap.GetHidesToPublicFromUnauthedWeb(accountable))
|
||||
acct.HidesCcPublicFromUnauthedWeb = util.Ptr(ap.GetHidesCcPublicFromUnauthedWeb(accountable))
|
||||
|
||||
return &acct, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -399,6 +399,10 @@ func (c *Converter) AccountToAS(
|
|||
}
|
||||
}
|
||||
|
||||
// Web visibility for statuses.
|
||||
ap.SetHidesToPublicFromUnauthedWeb(accountable, *a.HidesToPublicFromUnauthedWeb)
|
||||
ap.SetHidesCcPublicFromUnauthedWeb(accountable, *a.HidesCcPublicFromUnauthedWeb)
|
||||
|
||||
return accountable, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -64,6 +65,8 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
@ -116,6 +119,7 @@ func (suite *InternalToASTestSuite) TestAccountToASBot() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -132,6 +136,8 @@ func (suite *InternalToASTestSuite) TestAccountToASBot() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
@ -178,6 +184,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -209,6 +216,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
|||
"featured": "http://localhost:8080/users/1happyturtle/collections/featured",
|
||||
"followers": "http://localhost:8080/users/1happyturtle/followers",
|
||||
"following": "http://localhost:8080/users/1happyturtle/following",
|
||||
"hidesCcPublicFromUnauthedWeb": true,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"id": "http://localhost:8080/users/1happyturtle",
|
||||
"inbox": "http://localhost:8080/users/1happyturtle/inbox",
|
||||
"manuallyApprovesFollowers": true,
|
||||
|
|
@ -256,6 +265,7 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -279,6 +289,8 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
@ -328,6 +340,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
|||
// Despite only one field being set, attachments should still be a slice/array.
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -354,6 +367,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
|||
"featured": "http://localhost:8080/users/1happyturtle/collections/featured",
|
||||
"followers": "http://localhost:8080/users/1happyturtle/followers",
|
||||
"following": "http://localhost:8080/users/1happyturtle/following",
|
||||
"hidesCcPublicFromUnauthedWeb": true,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"id": "http://localhost:8080/users/1happyturtle",
|
||||
"inbox": "http://localhost:8080/users/1happyturtle/inbox",
|
||||
"manuallyApprovesFollowers": true,
|
||||
|
|
@ -389,6 +404,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -406,6 +422,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
@ -464,6 +482,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -483,6 +502,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
|
|||
|
|
@ -134,9 +134,26 @@ func (c *Converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmode
|
|||
statusContentType = a.Settings.StatusContentType
|
||||
}
|
||||
|
||||
// Derive web visibility for
|
||||
// this local account's statuses.
|
||||
var webVisibility apimodel.Visibility
|
||||
switch {
|
||||
case *a.HidesToPublicFromUnauthedWeb:
|
||||
// Hides all.
|
||||
webVisibility = apimodel.VisibilityNone
|
||||
|
||||
case !*a.HidesCcPublicFromUnauthedWeb:
|
||||
// Shows unlisted + public (Masto default).
|
||||
webVisibility = apimodel.VisibilityUnlisted
|
||||
|
||||
default:
|
||||
// Shows public only (GtS default).
|
||||
webVisibility = apimodel.VisibilityPublic
|
||||
}
|
||||
|
||||
apiAccount.Source = &apimodel.Source{
|
||||
Privacy: VisToAPIVis(a.Settings.Privacy),
|
||||
WebVisibility: VisToAPIVis(a.Settings.WebVisibility),
|
||||
WebVisibility: webVisibility,
|
||||
WebLayout: a.Settings.WebLayout.String(),
|
||||
Sensitive: *a.Settings.Sensitive,
|
||||
Language: a.Settings.Language,
|
||||
|
|
|
|||
|
|
@ -965,7 +965,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendUnknownAttachments
|
|||
"in_reply_to_account_id": "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||
"sensitive": true,
|
||||
"spoiler_text": "some unknown media included",
|
||||
"visibility": "public",
|
||||
"visibility": "unlisted",
|
||||
"language": "en",
|
||||
"uri": "http://example.org/users/Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
|
||||
"url": "http://example.org/@Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
|
||||
|
|
@ -1114,7 +1114,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
|
|||
"in_reply_to_account_id": "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||
"sensitive": true,
|
||||
"spoiler_text": "some unknown media included",
|
||||
"visibility": "public",
|
||||
"visibility": "unlisted",
|
||||
"language": "en",
|
||||
"uri": "http://example.org/users/Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
|
||||
"url": "http://example.org/@Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ func (suite *WrapTestSuite) TestWrapAccountableInUpdate() {
|
|||
|
||||
suite.Equal(`{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://w3id.org/security/v1",
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
|
|
@ -198,6 +199,8 @@ func (suite *WrapTestSuite) TestWrapAccountableInUpdate() {
|
|||
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"icon": {
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "a green goblin looking nasty",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue