summaryrefslogtreecommitdiff
path: root/internal/processing
diff options
context:
space:
mode:
Diffstat (limited to 'internal/processing')
-rw-r--r--internal/processing/status/common.go41
-rw-r--r--internal/processing/status/create.go6
-rw-r--r--internal/processing/status/create_test.go30
-rw-r--r--internal/processing/status/edit.go7
-rw-r--r--internal/processing/status/edit_test.go137
-rw-r--r--internal/processing/status/get.go2
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
}