diff options
Diffstat (limited to 'internal/processing/status')
| -rw-r--r-- | internal/processing/status/common.go | 41 | ||||
| -rw-r--r-- | internal/processing/status/create.go | 6 | ||||
| -rw-r--r-- | internal/processing/status/create_test.go | 30 | ||||
| -rw-r--r-- | internal/processing/status/edit.go | 7 | ||||
| -rw-r--r-- | internal/processing/status/edit_test.go | 137 | ||||
| -rw-r--r-- | internal/processing/status/get.go | 2 |
6 files changed, 212 insertions, 11 deletions
diff --git a/internal/processing/status/common.go b/internal/processing/status/common.go index 3f2b7b6cb..6a4851f51 100644 --- a/internal/processing/status/common.go +++ b/internal/processing/status/common.go @@ -30,6 +30,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/text" + "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/util/xslices" "github.com/superseriousbusiness/gotosocial/internal/validate" ) @@ -106,11 +107,39 @@ type statusContent struct { Tags []*gtsmodel.Tag } +// Returns the final content type to use when creating or editing a status. +func processContentType( + requestContentType apimodel.StatusContentType, + existingStatus *gtsmodel.Status, + accountDefaultContentType string, +) gtsmodel.StatusContentType { + switch { + // Content type set in the request, return the new value. + case requestContentType != "": + return typeutils.APIContentTypeToContentType(requestContentType) + + // No content type in the request, return the existing + // status's current content type if we know of one. + case existingStatus != nil && existingStatus.ContentType != 0: + return existingStatus.ContentType + + // We aren't editing an existing status, or if we are + // it's an old one that doesn't have a saved content + // type. Use the user's default content type setting. + case accountDefaultContentType != "": + return typeutils.APIContentTypeToContentType(apimodel.StatusContentType(accountDefaultContentType)) + + // uhh.. Fall back to global default. + default: + return gtsmodel.StatusContentTypeDefault + } +} + func (p *Processor) processContent( ctx context.Context, author *gtsmodel.Account, statusID string, - contentType string, + contentType gtsmodel.StatusContentType, content string, contentWarning string, language string, @@ -146,20 +175,14 @@ func (p *Processor) processContent( // function, according to the provided content-type. var format text.FormatFunc - if contentType == "" { - // If content type wasn't specified, use - // the author's preferred content-type. - contentType = author.Settings.StatusContentType - } - switch contentType { // Format status according to text/plain. - case "", string(apimodel.StatusContentTypePlain): + case gtsmodel.StatusContentTypePlain: format = p.formatter.FromPlain // Format status according to text/markdown. - case string(apimodel.StatusContentTypeMarkdown): + case gtsmodel.StatusContentTypeMarkdown: format = p.formatter.FromMarkdown // Unknown. diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go index 727c12084..73ac8d677 100644 --- a/internal/processing/status/create.go +++ b/internal/processing/status/create.go @@ -66,11 +66,14 @@ func (p *Processor) Create( // Generate new ID for status. statusID := id.NewULID() + // Process incoming content type. + contentType := processContentType(form.ContentType, nil, requester.Settings.StatusContentType) + // Process incoming status content fields. content, errWithCode := p.processContent(ctx, requester, statusID, - string(form.ContentType), + contentType, form.Status, form.SpoilerText, form.Language, @@ -163,6 +166,7 @@ func (p *Processor) Create( Content: content.Content, ContentWarning: content.ContentWarning, Text: form.Status, // raw + ContentType: contentType, // Set gathered mentions. MentionIDs: content.MentionIDs, diff --git a/internal/processing/status/create_test.go b/internal/processing/status/create_test.go index 16cefcebf..ec1ded3b0 100644 --- a/internal/processing/status/create_test.go +++ b/internal/processing/status/create_test.go @@ -238,6 +238,36 @@ func (suite *StatusCreateTestSuite) TestProcessReplyToUnthreadedRemoteStatus() { suite.NotEmpty(dbStatus.ThreadID) } +func (suite *StatusCreateTestSuite) TestProcessNoContentTypeUsesDefault() { + ctx := context.Background() + creatingAccount := suite.testAccounts["local_account_1"] + creatingApplication := suite.testApplications["application_1"] + + statusCreateForm := &apimodel.StatusCreateRequest{ + Status: "poopoo peepee", + SpoilerText: "", + MediaIDs: []string{}, + Poll: nil, + InReplyToID: "", + Sensitive: false, + Visibility: apimodel.VisibilityPublic, + LocalOnly: util.Ptr(false), + ScheduledAt: nil, + Language: "en", + ContentType: "", + } + + apiStatus, errWithCode := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm) + suite.NoError(errWithCode) + suite.NotNil(apiStatus) + + suite.Equal("<p>poopoo peepee</p>", apiStatus.Content) + + // the test accounts don't have settings, so we're comparing to + // the global default value instead of the requester's default + suite.Equal(apimodel.StatusContentTypeDefault, apiStatus.ContentType) +} + func TestStatusCreateTestSuite(t *testing.T) { suite.Run(t, new(StatusCreateTestSuite)) } diff --git a/internal/processing/status/edit.go b/internal/processing/status/edit.go index 95665074e..590fe565a 100644 --- a/internal/processing/status/edit.go +++ b/internal/processing/status/edit.go @@ -84,11 +84,14 @@ func (p *Processor) Edit( return nil, errWithCode } + // Process incoming content type. + contentType := processContentType(form.ContentType, status, requester.Settings.StatusContentType) + // Process incoming status edit content fields. content, errWithCode := p.processContent(ctx, requester, statusID, - string(form.ContentType), + contentType, form.Status, form.SpoilerText, form.Language, @@ -256,6 +259,7 @@ func (p *Processor) Edit( edit.Content = status.Content edit.ContentWarning = status.ContentWarning edit.Text = status.Text + edit.ContentType = status.ContentType edit.Language = status.Language edit.Sensitive = status.Sensitive edit.StatusID = status.ID @@ -298,6 +302,7 @@ func (p *Processor) Edit( status.Content = content.Content status.ContentWarning = content.ContentWarning status.Text = form.Status + status.ContentType = contentType status.Language = content.Language status.Sensitive = &form.Sensitive status.AttachmentIDs = form.MediaIDs diff --git a/internal/processing/status/edit_test.go b/internal/processing/status/edit_test.go index 36ebf2765..e6673c3cc 100644 --- a/internal/processing/status/edit_test.go +++ b/internal/processing/status/edit_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/suite" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util/xslices" ) @@ -90,6 +91,142 @@ func (suite *StatusEditTestSuite) TestSimpleEdit() { previousEdit := latestStatus.Edits[len(latestStatus.Edits)-1] suite.Equal(status.Content, previousEdit.Content) suite.Equal(status.Text, previousEdit.Text) + suite.Equal(status.ContentType, previousEdit.ContentType) + suite.Equal(status.ContentWarning, previousEdit.ContentWarning) + suite.Equal(*status.Sensitive, *previousEdit.Sensitive) + suite.Equal(status.Language, previousEdit.Language) + suite.Equal(status.UpdatedAt(), previousEdit.CreatedAt) +} + +func (suite *StatusEditTestSuite) TestEditChangeContentType() { + // Create cancellable context to use for test. + ctx, cncl := context.WithCancel(context.Background()) + defer cncl() + + // Get a local account to use as test requester. + requester := suite.testAccounts["local_account_1"] + requester, _ = suite.state.DB.GetAccountByID(ctx, requester.ID) + + // Get requester's existing plain text status to perform an edit on. + status := suite.testStatuses["local_account_1_status_6"] + status, _ = suite.state.DB.GetStatusByID(ctx, status.ID) + + // Prepare edit with a Markdown body. + form := &apimodel.StatusEditRequest{ + Status: "ooh the status is *fancy* now!", + ContentType: apimodel.StatusContentTypeMarkdown, + SpoilerText: "shhhhh", + Sensitive: true, + Language: "fr", // hoh hoh hoh + MediaIDs: nil, + MediaAttributes: nil, + Poll: nil, + } + + // Pass the prepared form to the status processor to perform the edit. + apiStatus, errWithCode := suite.status.Edit(ctx, requester, status.ID, form) + suite.NotNil(apiStatus) + suite.NoError(errWithCode) + + // Check response against input form data. + suite.Equal(form.Status, apiStatus.Text) + suite.Equal(form.ContentType, apiStatus.ContentType) + suite.Equal(form.SpoilerText, apiStatus.SpoilerText) + suite.Equal(form.Sensitive, apiStatus.Sensitive) + suite.Equal(form.Language, *apiStatus.Language) + suite.NotEqual(util.FormatISO8601(status.EditedAt), *apiStatus.EditedAt) + + // Fetched the latest version of edited status from the database. + latestStatus, err := suite.state.DB.GetStatusByID(ctx, status.ID) + suite.NoError(err) + + // Check latest status against input form data. + suite.Equal(form.Status, latestStatus.Text) + suite.Equal(typeutils.APIContentTypeToContentType(form.ContentType), latestStatus.ContentType) + suite.Equal(form.SpoilerText, latestStatus.ContentWarning) + suite.Equal(form.Sensitive, *latestStatus.Sensitive) + suite.Equal(form.Language, latestStatus.Language) + suite.Equal(len(status.EditIDs)+1, len(latestStatus.EditIDs)) + suite.NotEqual(status.UpdatedAt(), latestStatus.UpdatedAt()) + + // Populate all historical edits for this status. + err = suite.state.DB.PopulateStatusEdits(ctx, latestStatus) + suite.NoError(err) + + // Check previous status edit matches original status content. + previousEdit := latestStatus.Edits[len(latestStatus.Edits)-1] + suite.Equal(status.Content, previousEdit.Content) + suite.Equal(status.Text, previousEdit.Text) + suite.Equal(status.ContentType, previousEdit.ContentType) + suite.Equal(status.ContentWarning, previousEdit.ContentWarning) + suite.Equal(*status.Sensitive, *previousEdit.Sensitive) + suite.Equal(status.Language, previousEdit.Language) + suite.Equal(status.UpdatedAt(), previousEdit.CreatedAt) +} + +func (suite *StatusEditTestSuite) TestEditOnStatusWithNoContentType() { + // Create cancellable context to use for test. + ctx, cncl := context.WithCancel(context.Background()) + defer cncl() + + // Get a local account to use as test requester. + requester := suite.testAccounts["local_account_1"] + requester, _ = suite.state.DB.GetAccountByID(ctx, requester.ID) + + // Get requester's existing status, which has no + // stored content type, to perform an edit on. + status := suite.testStatuses["local_account_1_status_2"] + status, _ = suite.state.DB.GetStatusByID(ctx, status.ID) + + // Prepare edit without setting a new content type. + form := &apimodel.StatusEditRequest{ + Status: "how will this text be parsed? it is a mystery", + SpoilerText: "shhhhh", + Sensitive: true, + Language: "fr", // hoh hoh hoh + MediaIDs: nil, + MediaAttributes: nil, + Poll: nil, + } + + // Pass the prepared form to the status processor to perform the edit. + apiStatus, errWithCode := suite.status.Edit(ctx, requester, status.ID, form) + suite.NotNil(apiStatus) + suite.NoError(errWithCode) + + // Check response against input form data. + suite.Equal(form.Status, apiStatus.Text) + suite.NotEqual(util.FormatISO8601(status.EditedAt), *apiStatus.EditedAt) + + // Check response against requester's default content type setting + // (the test accounts don't actually have settings on them, so + // instead we check that the global default content type is used) + suite.Equal(apimodel.StatusContentTypeDefault, apiStatus.ContentType) + + // Fetched the latest version of edited status from the database. + latestStatus, err := suite.state.DB.GetStatusByID(ctx, status.ID) + suite.NoError(err) + + // Check latest status against input form data + suite.Equal(form.Status, latestStatus.Text) + suite.Equal(form.Sensitive, *latestStatus.Sensitive) + suite.Equal(form.Language, latestStatus.Language) + suite.Equal(len(status.EditIDs)+1, len(latestStatus.EditIDs)) + suite.NotEqual(status.UpdatedAt(), latestStatus.UpdatedAt()) + + // Check latest status against requester's default content + // type (again, actually just checking for the global default) + suite.Equal(gtsmodel.StatusContentTypeDefault, latestStatus.ContentType) + + // Populate all historical edits for this status. + err = suite.state.DB.PopulateStatusEdits(ctx, latestStatus) + suite.NoError(err) + + // Check previous status edit matches original status content. + previousEdit := latestStatus.Edits[len(latestStatus.Edits)-1] + suite.Equal(status.Content, previousEdit.Content) + suite.Equal(status.Text, previousEdit.Text) + suite.Equal(status.ContentType, previousEdit.ContentType) suite.Equal(status.ContentWarning, previousEdit.ContentWarning) suite.Equal(*status.Sensitive, *previousEdit.Sensitive) suite.Equal(status.Language, previousEdit.Language) diff --git a/internal/processing/status/get.go b/internal/processing/status/get.go index 812f01683..6065c7cfe 100644 --- a/internal/processing/status/get.go +++ b/internal/processing/status/get.go @@ -24,6 +24,7 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/typeutils" ) // Get gets the given status, taking account of privacy settings and blocks etc. @@ -56,5 +57,6 @@ func (p *Processor) SourceGet(ctx context.Context, requester *gtsmodel.Account, ID: status.ID, Text: status.Text, SpoilerText: status.ContentWarning, + ContentType: typeutils.ContentTypeToAPIContentType(status.ContentType), }, nil } |
