[feature] Use `local_only` field, deprecate `federated` field (#3222)
* [feature] Use `local_only` field, deprecate `federated` field * use `deprecated` comment for form.Federated * nolint
This commit is contained in:
parent
ffcf6e73f7
commit
53fccb8af8
|
@ -2635,6 +2635,10 @@ definitions:
|
||||||
example: en
|
example: en
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Language
|
x-go-name: Language
|
||||||
|
local_only:
|
||||||
|
description: Set to "true" if status is not federated, ie., a "local only" status; omitted from response otherwise.
|
||||||
|
type: boolean
|
||||||
|
x-go-name: LocalOnly
|
||||||
media_attachments:
|
media_attachments:
|
||||||
description: Media that is attached to this status.
|
description: Media that is attached to this status.
|
||||||
items:
|
items:
|
||||||
|
@ -2828,6 +2832,10 @@ definitions:
|
||||||
example: en
|
example: en
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Language
|
x-go-name: Language
|
||||||
|
local_only:
|
||||||
|
description: Set to "true" if status is not federated, ie., a "local only" status; omitted from response otherwise.
|
||||||
|
type: boolean
|
||||||
|
x-go-name: LocalOnly
|
||||||
media_attachments:
|
media_attachments:
|
||||||
description: Media that is attached to this status.
|
description: Media that is attached to this status.
|
||||||
items:
|
items:
|
||||||
|
@ -8654,6 +8662,17 @@ paths:
|
||||||
name: visibility
|
name: visibility
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Visibility
|
x-go-name: Visibility
|
||||||
|
- default: false
|
||||||
|
description: If set to true, this status will be "local only" and will NOT be federated beyond the local timeline(s). If set to false (default), this status will be federated to your followers beyond the local timeline(s).
|
||||||
|
in: formData
|
||||||
|
name: local_only
|
||||||
|
type: boolean
|
||||||
|
x-go-name: LocalOnly
|
||||||
|
- description: '***DEPRECATED***. Included for back compat only. Only used if set and local_only is not yet. If set to true, this status will be federated beyond the local timeline(s). If set to false, this status will NOT be federated beyond the local timeline(s).'
|
||||||
|
in: formData
|
||||||
|
name: federated
|
||||||
|
type: boolean
|
||||||
|
x-go-name: Federated
|
||||||
- description: |-
|
- description: |-
|
||||||
ISO 8601 Datetime at which to schedule a status.
|
ISO 8601 Datetime at which to schedule a status.
|
||||||
Providing this parameter will cause ScheduledStatus to be returned instead of Status.
|
Providing this parameter will cause ScheduledStatus to be returned instead of Status.
|
||||||
|
@ -8677,11 +8696,6 @@ paths:
|
||||||
name: content_type
|
name: content_type
|
||||||
type: string
|
type: string
|
||||||
x-go-name: ContentType
|
x-go-name: ContentType
|
||||||
- description: This status will be federated beyond the local timeline(s).
|
|
||||||
in: formData
|
|
||||||
name: federated
|
|
||||||
type: boolean
|
|
||||||
x-go-name: Federated
|
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -137,6 +138,24 @@ import (
|
||||||
// - direct
|
// - direct
|
||||||
// in: formData
|
// in: formData
|
||||||
// -
|
// -
|
||||||
|
// name: local_only
|
||||||
|
// x-go-name: LocalOnly
|
||||||
|
// description: >-
|
||||||
|
// If set to true, this status will be "local only" and will NOT be federated beyond the local timeline(s).
|
||||||
|
// If set to false (default), this status will be federated to your followers beyond the local timeline(s).
|
||||||
|
// type: boolean
|
||||||
|
// in: formData
|
||||||
|
// default: false
|
||||||
|
// -
|
||||||
|
// name: federated
|
||||||
|
// x-go-name: Federated
|
||||||
|
// description: >-
|
||||||
|
// ***DEPRECATED***. Included for back compat only. Only used if set and local_only is not yet.
|
||||||
|
// If set to true, this status will be federated beyond the local timeline(s).
|
||||||
|
// If set to false, this status will NOT be federated beyond the local timeline(s).
|
||||||
|
// in: formData
|
||||||
|
// type: boolean
|
||||||
|
// -
|
||||||
// name: scheduled_at
|
// name: scheduled_at
|
||||||
// x-go-name: ScheduledAt
|
// x-go-name: ScheduledAt
|
||||||
// description: |-
|
// description: |-
|
||||||
|
@ -162,12 +181,6 @@ import (
|
||||||
// - text/plain
|
// - text/plain
|
||||||
// - text/markdown
|
// - text/markdown
|
||||||
// in: formData
|
// in: formData
|
||||||
// -
|
|
||||||
// name: federated
|
|
||||||
// x-go-name: Federated
|
|
||||||
// description: This status will be federated beyond the local timeline(s).
|
|
||||||
// in: formData
|
|
||||||
// type: boolean
|
|
||||||
//
|
//
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
|
@ -210,7 +223,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &apimodel.AdvancedStatusCreateForm{}
|
form := &apimodel.StatusCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
if err := c.ShouldBind(form); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
@ -249,7 +262,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||||
// overlength inputs.
|
// overlength inputs.
|
||||||
//
|
//
|
||||||
// Side effect: normalizes the post's language tag.
|
// Side effect: normalizes the post's language tag.
|
||||||
func validateNormalizeCreateStatus(form *apimodel.AdvancedStatusCreateForm) error {
|
func validateNormalizeCreateStatus(form *apimodel.StatusCreateRequest) error {
|
||||||
hasStatus := form.Status != ""
|
hasStatus := form.Status != ""
|
||||||
hasMedia := len(form.MediaIDs) != 0
|
hasMedia := len(form.MediaIDs) != 0
|
||||||
hasPoll := form.Poll != nil
|
hasPoll := form.Poll != nil
|
||||||
|
@ -286,10 +299,16 @@ func validateNormalizeCreateStatus(form *apimodel.AdvancedStatusCreateForm) erro
|
||||||
form.Language = language
|
form.Language = language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the deprecated "federated" field was
|
||||||
|
// set in lieu of "local_only", and use it if so.
|
||||||
|
if form.LocalOnly == nil && form.Federated != nil { // nolint:staticcheck
|
||||||
|
form.LocalOnly = util.Ptr(!*form.Federated) // nolint:staticcheck
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateNormalizeCreatePoll(form *apimodel.AdvancedStatusCreateForm) error {
|
func validateNormalizeCreatePoll(form *apimodel.StatusCreateRequest) error {
|
||||||
maxPollOptions := config.GetStatusesPollMaxOptions()
|
maxPollOptions := config.GetStatusesPollMaxOptions()
|
||||||
maxPollChars := config.GetStatusesPollOptionMaxChars()
|
maxPollChars := config.GetStatusesPollOptionMaxChars()
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ type Status struct {
|
||||||
// Visibility of this status.
|
// Visibility of this status.
|
||||||
// example: unlisted
|
// example: unlisted
|
||||||
Visibility Visibility `json:"visibility"`
|
Visibility Visibility `json:"visibility"`
|
||||||
|
// Set to "true" if status is not federated, ie., a "local only" status; omitted from response otherwise.
|
||||||
|
LocalOnly bool `json:"local_only,omitempty"`
|
||||||
// Primary language of this status (ISO 639 Part 1 two-letter language code).
|
// Primary language of this status (ISO 639 Part 1 two-letter language code).
|
||||||
// Will be null if language is not known.
|
// Will be null if language is not known.
|
||||||
// example: en
|
// example: en
|
||||||
|
@ -209,6 +211,10 @@ type StatusCreateRequest struct {
|
||||||
SpoilerText string `form:"spoiler_text" json:"spoiler_text" xml:"spoiler_text"`
|
SpoilerText string `form:"spoiler_text" json:"spoiler_text" xml:"spoiler_text"`
|
||||||
// Visibility of the posted status.
|
// Visibility of the posted status.
|
||||||
Visibility Visibility `form:"visibility" json:"visibility" xml:"visibility"`
|
Visibility Visibility `form:"visibility" json:"visibility" xml:"visibility"`
|
||||||
|
// Set to "true" if this status should not be federated, ie. it should be a "local only" status.
|
||||||
|
LocalOnly *bool `form:"local_only"`
|
||||||
|
// Deprecated: Only used if LocalOnly is not set.
|
||||||
|
Federated *bool `form:"federated"`
|
||||||
// ISO 8601 Datetime at which to schedule a status.
|
// ISO 8601 Datetime at which to schedule a status.
|
||||||
// Providing this parameter will cause ScheduledStatus to be returned instead of Status.
|
// Providing this parameter will cause ScheduledStatus to be returned instead of Status.
|
||||||
// Must be at least 5 minutes in the future.
|
// Must be at least 5 minutes in the future.
|
||||||
|
@ -238,24 +244,6 @@ const (
|
||||||
VisibilityDirect Visibility = "direct"
|
VisibilityDirect Visibility = "direct"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdvancedStatusCreateForm wraps the mastodon-compatible status create form along with the GTS advanced
|
|
||||||
// visibility settings.
|
|
||||||
//
|
|
||||||
// swagger:ignore
|
|
||||||
type AdvancedStatusCreateForm struct {
|
|
||||||
StatusCreateRequest
|
|
||||||
AdvancedVisibilityFlagsForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdvancedVisibilityFlagsForm allows a few more advanced flags to be set on new statuses, in addition
|
|
||||||
// to the standard mastodon-compatible ones.
|
|
||||||
//
|
|
||||||
// swagger:ignore
|
|
||||||
type AdvancedVisibilityFlagsForm struct {
|
|
||||||
// This status will be federated beyond the local timeline(s).
|
|
||||||
Federated *bool `form:"federated" json:"federated" xml:"federated"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatusContentType is the content type with which to parse the submitted status.
|
// StatusContentType is the content type with which to parse the submitted status.
|
||||||
// Can be either text/plain or text/markdown. Empty will default to text/plain.
|
// Can be either text/plain or text/markdown. Empty will default to text/plain.
|
||||||
//
|
//
|
||||||
|
|
|
@ -86,7 +86,7 @@ func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogs()
|
||||||
func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogsPublicOnly() {
|
func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogsPublicOnly() {
|
||||||
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, true)
|
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, true)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Len(statuses, 2)
|
suite.Len(statuses, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// populateTestStatus adds mandatory fields to a partially populated status.
|
// populateTestStatus adds mandatory fields to a partially populated status.
|
||||||
|
|
|
@ -97,10 +97,10 @@ func (f *Filter) isStatusVisible(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether status accounts are visible to the requester.
|
// Check whether status accounts are visible to the requester.
|
||||||
visible, err := f.areStatusAccountsVisible(ctx, requester, status)
|
acctsVisible, err := f.areStatusAccountsVisible(ctx, requester, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, gtserror.Newf("error checking status %s account visibility: %w", status.ID, err)
|
return false, gtserror.Newf("error checking status %s account visibility: %w", status.ID, err)
|
||||||
} else if !visible {
|
} else if !acctsVisible {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,22 +112,34 @@ func (f *Filter) isStatusVisible(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Visibility == gtsmodel.VisibilityPublic {
|
if requester == nil {
|
||||||
// This status will be visible to all.
|
// The request is unauthed. Only federated, Public statuses are visible without auth.
|
||||||
return true, nil
|
visibleUnauthed := !status.IsLocalOnly() && status.Visibility == gtsmodel.VisibilityPublic
|
||||||
|
return visibleUnauthed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if requester == nil {
|
/*
|
||||||
// This request is WITHOUT auth, and status is NOT public.
|
From this point down we know the request is authed.
|
||||||
log.Trace(ctx, "unauthorized request to non-public status")
|
*/
|
||||||
|
|
||||||
|
if requester.IsRemote() && status.IsLocalOnly() {
|
||||||
|
// Remote accounts can't see local-only
|
||||||
|
// posts regardless of their visibility.
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Visibility == gtsmodel.VisibilityUnlocked {
|
if status.Visibility == gtsmodel.VisibilityPublic ||
|
||||||
// This status is visible to all auth'd accounts.
|
status.Visibility == gtsmodel.VisibilityUnlocked {
|
||||||
|
// This status is visible to all auth'd accounts
|
||||||
|
// (pending blocks, which we already checked above).
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
From this point down we know the request
|
||||||
|
is of visibility followers-only or below.
|
||||||
|
*/
|
||||||
|
|
||||||
if requester.ID == status.AccountID {
|
if requester.ID == status.AccountID {
|
||||||
// Author can always see their own status.
|
// Author can always see their own status.
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
@ -200,6 +200,43 @@ func (suite *StatusVisibleTestSuite) TestVisiblePending() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *StatusVisibleTestSuite) TestVisibleLocalOnly() {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Local-only, Public status.
|
||||||
|
testStatus := suite.testStatuses["local_account_2_status_4"]
|
||||||
|
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
acct *gtsmodel.Account
|
||||||
|
visible bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
acct: suite.testAccounts["local_account_2"],
|
||||||
|
visible: true, // Own status, always visible.
|
||||||
|
},
|
||||||
|
{
|
||||||
|
acct: nil,
|
||||||
|
visible: false, // No auth, should not be visible..
|
||||||
|
},
|
||||||
|
{
|
||||||
|
acct: suite.testAccounts["local_account_1"],
|
||||||
|
visible: true, // Local account, should be visible.
|
||||||
|
},
|
||||||
|
{
|
||||||
|
acct: suite.testAccounts["remote_account_1"],
|
||||||
|
visible: false, // Blocked account, should not be visible.
|
||||||
|
},
|
||||||
|
{
|
||||||
|
acct: suite.testAccounts["remote_account_2"],
|
||||||
|
visible: false, // Remote account, should not be visible.
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
visible, err := suite.filter.StatusVisible(ctx, testCase.acct, testStatus)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(testCase.visible, visible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusVisibleTestSuite(t *testing.T) {
|
func TestStatusVisibleTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(StatusVisibleTestSuite))
|
suite.Run(t, new(StatusVisibleTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,6 +212,12 @@ func (s *Status) IsLocal() bool {
|
||||||
return s.Local != nil && *s.Local
|
return s.Local != nil && *s.Local
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLocalOnly returns true if this status
|
||||||
|
// is "local-only" ie., unfederated.
|
||||||
|
func (s *Status) IsLocalOnly() bool {
|
||||||
|
return s.Federated == nil || !*s.Federated
|
||||||
|
}
|
||||||
|
|
||||||
// StatusToTag is an intermediate struct to facilitate the many2many relationship between a status and one or more tags.
|
// StatusToTag is an intermediate struct to facilitate the many2many relationship between a status and one or more tags.
|
||||||
type StatusToTag struct {
|
type StatusToTag struct {
|
||||||
StatusID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
|
StatusID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
|
||||||
|
|
|
@ -129,6 +129,18 @@ func (p *Processor) OutboxGet(
|
||||||
hi = statuses[0].ID
|
hi = statuses[0].ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reslice statuses dropping all those invisible to requester
|
||||||
|
// (eg., local-only statuses, if the requester is remote).
|
||||||
|
statuses, err = p.visFilter.StatusesVisible(
|
||||||
|
ctx,
|
||||||
|
auth.requestingAcct,
|
||||||
|
statuses,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
err := gtserror.Newf("error filtering statuses: %w", err)
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start building AS collection page params.
|
// Start building AS collection page params.
|
||||||
params.Total = util.Ptr(*receivingAcct.Stats.StatusesCount)
|
params.Total = util.Ptr(*receivingAcct.Stats.StatusesCount)
|
||||||
var pageParams ap.CollectionPageParams
|
var pageParams ap.CollectionPageParams
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (p *Processor) Create(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
requester *gtsmodel.Account,
|
requester *gtsmodel.Account,
|
||||||
application *gtsmodel.Application,
|
application *gtsmodel.Application,
|
||||||
form *apimodel.AdvancedStatusCreateForm,
|
form *apimodel.StatusCreateRequest,
|
||||||
) (
|
) (
|
||||||
*apimodel.Status,
|
*apimodel.Status,
|
||||||
gtserror.WithCode,
|
gtserror.WithCode,
|
||||||
|
@ -290,7 +290,7 @@ func (p *Processor) processThreadID(ctx context.Context, status *gtsmodel.Status
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) processMediaIDs(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
|
func (p *Processor) processMediaIDs(ctx context.Context, form *apimodel.StatusCreateRequest, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
|
||||||
if form.MediaIDs == nil {
|
if form.MediaIDs == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ func (p *Processor) processMediaIDs(ctx context.Context, form *apimodel.Advanced
|
||||||
}
|
}
|
||||||
|
|
||||||
func processVisibility(
|
func processVisibility(
|
||||||
form *apimodel.AdvancedStatusCreateForm,
|
form *apimodel.StatusCreateRequest,
|
||||||
accountDefaultVis gtsmodel.Visibility,
|
accountDefaultVis gtsmodel.Visibility,
|
||||||
status *gtsmodel.Status,
|
status *gtsmodel.Status,
|
||||||
) error {
|
) error {
|
||||||
|
@ -356,16 +356,16 @@ func processVisibility(
|
||||||
status.Visibility = gtsmodel.VisibilityDefault
|
status.Visibility = gtsmodel.VisibilityDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set federated flag to form value
|
// Set federated according to "local_only" field,
|
||||||
// if provided, or default to true.
|
// assuming federated (ie., not local-only) by default.
|
||||||
federated := util.PtrOrValue(form.Federated, true)
|
localOnly := util.PtrOrValue(form.LocalOnly, false)
|
||||||
status.Federated = &federated
|
status.Federated = util.Ptr(!localOnly)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processInteractionPolicy(
|
func processInteractionPolicy(
|
||||||
_ *apimodel.AdvancedStatusCreateForm,
|
_ *apimodel.StatusCreateRequest,
|
||||||
settings *gtsmodel.AccountSettings,
|
settings *gtsmodel.AccountSettings,
|
||||||
status *gtsmodel.Status,
|
status *gtsmodel.Status,
|
||||||
) error {
|
) error {
|
||||||
|
@ -413,7 +413,7 @@ func processInteractionPolicy(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processLanguage(form *apimodel.AdvancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error {
|
func processLanguage(form *apimodel.StatusCreateRequest, accountDefaultLanguage string, status *gtsmodel.Status) error {
|
||||||
if form.Language != "" {
|
if form.Language != "" {
|
||||||
status.Language = form.Language
|
status.Language = form.Language
|
||||||
} else {
|
} else {
|
||||||
|
@ -425,7 +425,7 @@ func processLanguage(form *apimodel.AdvancedStatusCreateForm, accountDefaultLang
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) processContent(ctx context.Context, parseMention gtsmodel.ParseMentionFunc, form *apimodel.AdvancedStatusCreateForm, status *gtsmodel.Status) error {
|
func (p *Processor) processContent(ctx context.Context, parseMention gtsmodel.ParseMentionFunc, form *apimodel.StatusCreateRequest, status *gtsmodel.Status) error {
|
||||||
if form.ContentType == "" {
|
if form.ContentType == "" {
|
||||||
// If content type wasn't specified, use the author's preferred content-type.
|
// If content type wasn't specified, use the author's preferred content-type.
|
||||||
contentType := apimodel.StatusContentType(status.Account.Settings.StatusContentType)
|
contentType := apimodel.StatusContentType(status.Account.Settings.StatusContentType)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StatusCreateTestSuite struct {
|
type StatusCreateTestSuite struct {
|
||||||
|
@ -38,8 +39,7 @@ func (suite *StatusCreateTestSuite) TestProcessContentWarningWithQuotationMarks(
|
||||||
creatingAccount := suite.testAccounts["local_account_1"]
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
creatingApplication := suite.testApplications["application_1"]
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "poopoo peepee",
|
Status: "poopoo peepee",
|
||||||
MediaIDs: []string{},
|
MediaIDs: []string{},
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
|
@ -47,13 +47,10 @@ func (suite *StatusCreateTestSuite) TestProcessContentWarningWithQuotationMarks(
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
SpoilerText: "\"test\"", // these should not be html-escaped when the final text is rendered
|
SpoilerText: "\"test\"", // these should not be html-escaped when the final text is rendered
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "en",
|
Language: "en",
|
||||||
ContentType: apimodel.StatusContentTypePlain,
|
ContentType: apimodel.StatusContentTypePlain,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
@ -69,8 +66,7 @@ func (suite *StatusCreateTestSuite) TestProcessContentWarningWithHTMLEscapedQuot
|
||||||
creatingAccount := suite.testAccounts["local_account_1"]
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
creatingApplication := suite.testApplications["application_1"]
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "poopoo peepee",
|
Status: "poopoo peepee",
|
||||||
MediaIDs: []string{},
|
MediaIDs: []string{},
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
|
@ -78,13 +74,10 @@ func (suite *StatusCreateTestSuite) TestProcessContentWarningWithHTMLEscapedQuot
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
SpoilerText: ""test"", // the html-escaped quotation marks should appear as normal quotation marks in the finished text
|
SpoilerText: ""test"", // the html-escaped quotation marks should appear as normal quotation marks in the finished text
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "en",
|
Language: "en",
|
||||||
ContentType: apimodel.StatusContentTypePlain,
|
ContentType: apimodel.StatusContentTypePlain,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
@ -105,21 +98,17 @@ func (suite *StatusCreateTestSuite) TestProcessStatusMarkdownWithUnderscoreEmoji
|
||||||
creatingAccount := suite.testAccounts["local_account_1"]
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
creatingApplication := suite.testApplications["application_1"]
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "poopoo peepee :_rainbow_:",
|
Status: "poopoo peepee :_rainbow_:",
|
||||||
MediaIDs: []string{},
|
MediaIDs: []string{},
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "en",
|
Language: "en",
|
||||||
ContentType: apimodel.StatusContentTypeMarkdown,
|
ContentType: apimodel.StatusContentTypeMarkdown,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
@ -135,8 +124,7 @@ func (suite *StatusCreateTestSuite) TestProcessStatusMarkdownWithSpoilerTextEmoj
|
||||||
creatingAccount := suite.testAccounts["local_account_1"]
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
creatingApplication := suite.testApplications["application_1"]
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "poopoo peepee",
|
Status: "poopoo peepee",
|
||||||
SpoilerText: "testing something :rainbow:",
|
SpoilerText: "testing something :rainbow:",
|
||||||
MediaIDs: []string{},
|
MediaIDs: []string{},
|
||||||
|
@ -144,13 +132,10 @@ func (suite *StatusCreateTestSuite) TestProcessStatusMarkdownWithSpoilerTextEmoj
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "en",
|
Language: "en",
|
||||||
ContentType: apimodel.StatusContentTypeMarkdown,
|
ContentType: apimodel.StatusContentTypeMarkdown,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
@ -170,8 +155,7 @@ func (suite *StatusCreateTestSuite) TestProcessMediaDescriptionTooShort() {
|
||||||
creatingAccount := suite.testAccounts["local_account_1"]
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
creatingApplication := suite.testApplications["application_1"]
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "poopoo peepee",
|
Status: "poopoo peepee",
|
||||||
MediaIDs: []string{suite.testAttachments["local_account_1_unattached_1"].ID},
|
MediaIDs: []string{suite.testAttachments["local_account_1_unattached_1"].ID},
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
|
@ -179,13 +163,10 @@ func (suite *StatusCreateTestSuite) TestProcessMediaDescriptionTooShort() {
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
SpoilerText: "",
|
SpoilerText: "",
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "en",
|
Language: "en",
|
||||||
ContentType: apimodel.StatusContentTypePlain,
|
ContentType: apimodel.StatusContentTypePlain,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
@ -199,8 +180,7 @@ func (suite *StatusCreateTestSuite) TestProcessLanguageWithScriptPart() {
|
||||||
creatingAccount := suite.testAccounts["local_account_1"]
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
creatingApplication := suite.testApplications["application_1"]
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "你好世界", // hello world
|
Status: "你好世界", // hello world
|
||||||
MediaIDs: []string{},
|
MediaIDs: []string{},
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
|
@ -208,13 +188,10 @@ func (suite *StatusCreateTestSuite) TestProcessLanguageWithScriptPart() {
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
SpoilerText: "",
|
SpoilerText: "",
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "zh-Hans",
|
Language: "zh-Hans",
|
||||||
ContentType: apimodel.StatusContentTypePlain,
|
ContentType: apimodel.StatusContentTypePlain,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
@ -233,8 +210,7 @@ func (suite *StatusCreateTestSuite) TestProcessReplyToUnthreadedRemoteStatus() {
|
||||||
|
|
||||||
// Reply to a remote status that
|
// Reply to a remote status that
|
||||||
// doesn't have a threadID set on it.
|
// doesn't have a threadID set on it.
|
||||||
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
statusCreateForm := &apimodel.StatusCreateRequest{
|
||||||
StatusCreateRequest: apimodel.StatusCreateRequest{
|
|
||||||
Status: "boobies",
|
Status: "boobies",
|
||||||
MediaIDs: []string{},
|
MediaIDs: []string{},
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
|
@ -242,13 +218,10 @@ func (suite *StatusCreateTestSuite) TestProcessReplyToUnthreadedRemoteStatus() {
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
SpoilerText: "this is a reply",
|
SpoilerText: "this is a reply",
|
||||||
Visibility: apimodel.VisibilityPublic,
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
LocalOnly: util.Ptr(false),
|
||||||
ScheduledAt: "",
|
ScheduledAt: "",
|
||||||
Language: "en",
|
Language: "en",
|
||||||
ContentType: apimodel.StatusContentTypePlain,
|
ContentType: apimodel.StatusContentTypePlain,
|
||||||
},
|
|
||||||
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
|
||||||
Federated: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
|
|
@ -146,7 +146,7 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
|
||||||
func (f *federate) CreateStatus(ctx context.Context, status *gtsmodel.Status) error {
|
func (f *federate) CreateStatus(ctx context.Context, status *gtsmodel.Status) error {
|
||||||
// Do nothing if the status
|
// Do nothing if the status
|
||||||
// shouldn't be federated.
|
// shouldn't be federated.
|
||||||
if !*status.Federated {
|
if status.IsLocalOnly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ func (f *federate) CreatePollVote(ctx context.Context, poll *gtsmodel.Poll, vote
|
||||||
|
|
||||||
// Do nothing if the status
|
// Do nothing if the status
|
||||||
// shouldn't be federated.
|
// shouldn't be federated.
|
||||||
if !*status.Federated {
|
if status.IsLocalOnly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ func (f *federate) CreatePollVote(ctx context.Context, poll *gtsmodel.Poll, vote
|
||||||
func (f *federate) DeleteStatus(ctx context.Context, status *gtsmodel.Status) error {
|
func (f *federate) DeleteStatus(ctx context.Context, status *gtsmodel.Status) error {
|
||||||
// Do nothing if the status
|
// Do nothing if the status
|
||||||
// shouldn't be federated.
|
// shouldn't be federated.
|
||||||
if !*status.Federated {
|
if status.IsLocalOnly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ func (f *federate) DeleteStatus(ctx context.Context, status *gtsmodel.Status) er
|
||||||
func (f *federate) UpdateStatus(ctx context.Context, status *gtsmodel.Status) error {
|
func (f *federate) UpdateStatus(ctx context.Context, status *gtsmodel.Status) error {
|
||||||
// Do nothing if the status
|
// Do nothing if the status
|
||||||
// shouldn't be federated.
|
// shouldn't be federated.
|
||||||
if !*status.Federated {
|
if status.IsLocalOnly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1333,6 +1333,7 @@ func (c *Converter) baseStatusToFrontend(
|
||||||
Sensitive: *s.Sensitive,
|
Sensitive: *s.Sensitive,
|
||||||
SpoilerText: s.ContentWarning,
|
SpoilerText: s.ContentWarning,
|
||||||
Visibility: c.VisToAPIVis(ctx, s.Visibility),
|
Visibility: c.VisToAPIVis(ctx, s.Visibility),
|
||||||
|
LocalOnly: s.IsLocalOnly(),
|
||||||
Language: nil, // Set below.
|
Language: nil, // Set below.
|
||||||
URI: s.URI,
|
URI: s.URI,
|
||||||
URL: s.URL,
|
URL: s.URL,
|
||||||
|
|
|
@ -1536,8 +1536,8 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
ID: "01F8MHAYFKS4KMXF8K5Y1C0KRN",
|
ID: "01F8MHAYFKS4KMXF8K5Y1C0KRN",
|
||||||
URI: "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAYFKS4KMXF8K5Y1C0KRN",
|
URI: "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAYFKS4KMXF8K5Y1C0KRN",
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MHAYFKS4KMXF8K5Y1C0KRN",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MHAYFKS4KMXF8K5Y1C0KRN",
|
||||||
Content: "this is an unlocked local-only post that shouldn't federate, but it's still boostable, replyable, and likeable",
|
Content: "this is a Public local-only post that shouldn't federate, but it's still boostable, replyable, and likeable",
|
||||||
Text: "this is an unlocked local-only post that shouldn't federate, but it's still boostable, replyable, and likeable",
|
Text: "this is a Public local-only post that shouldn't federate, but it's still boostable, replyable, and likeable",
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
UpdatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
UpdatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1547,7 +1547,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ThreadID: "01HCWDVTW3HQWSX66VJQ91Z1RH",
|
ThreadID: "01HCWDVTW3HQWSX66VJQ91Z1RH",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityUnlocked,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "01F8MGY43H3N2C8EWPR2FPYEXG",
|
CreatedWithApplicationID: "01F8MGY43H3N2C8EWPR2FPYEXG",
|
||||||
|
|
Loading…
Reference in New Issue