mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-03-10 15:48:52 +01:00
[bugfix] Store and expose status content type (#3870)
* Add ContentType to internal models * Add ContentType to API models StatusSource and StatusEdit * Add helpers to convert between API/internal StatusContentType * Write status content type on create/edit * Add migration * Update API docs go run github.com/go-swagger/go-swagger/cmd/swagger generate spec --scan-models --exclude-deps --output docs/api/swagger.yaml * ensure ContentType is updated anywhere Text is * Update docs, take care of TODOs * Set ContentType in more places where Text is set * We don't actually use ContentType on the API status model * Update StatusSource test * Remove unused helper function I copied * Revert change to StatusContentType swagger annotation I'm going to include this in a follow-on PR instead. * Add test for updating content type in edits * Return a value from processContentType instead of modifying the existing status Fixes an issue that was caught by the test I just added - the recorded edit would be marked with the *new* content type instead of the old one, which is obviously bad * Add test for handling of statuses with no stored content type * repurpose an existing test status instead of adding a new one to avoid breaking other tests * Add test to ensure newly created statuses always have content type saved * Do include content type on status API model actually This is mostly important when deleting and redrafting. The comment on `apimodel.Status.Text` implies that it's not sent except in response to status deletion, but actually this doesn't seem to be the case; it also appears to be present in responses to creations and normal fetches and stuff. So I'm treating `ContentType` the same here. * Update new tests to check content type on API statuses * Check content type of API statuses in all tests where text is checked * update other api tests with status content type field * Add test ensuring text and content type are returned when deleting a status * Convert processContentType to free function and remove unused parameter * check for the correct value in the deletion test * Be explicit about this test status having an empty content type * Use omitempty consistently on API models * clean up the final diff a bit * one more swagger regen for the road * Handle nil statuses in processContentType * Don't pass processContentType the entire edit form, it doesn't need it * Move processContentType to common.go and use for creation as well * Remove unused parameters to ContentTypeToAPIContentType
This commit is contained in:
parent
69461c461b
commit
424f62dd70
26 changed files with 560 additions and 14 deletions
|
@ -2840,6 +2840,13 @@ definitions:
|
||||||
example: <p>Hey this is a status!</p>
|
example: <p>Hey this is a status!</p>
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Content
|
x-go-name: Content
|
||||||
|
content_type:
|
||||||
|
description: |-
|
||||||
|
Content type that was used to parse the status's text. Returned when
|
||||||
|
status is deleted, so if the user is redrafting the message the client
|
||||||
|
can default to the same content type.
|
||||||
|
type: string
|
||||||
|
x-go-name: ContentType
|
||||||
created_at:
|
created_at:
|
||||||
description: The date when this status was created (ISO 8601 Datetime).
|
description: The date when this status was created (ISO 8601 Datetime).
|
||||||
example: "2021-07-30T09:20:25+00:00"
|
example: "2021-07-30T09:20:25+00:00"
|
||||||
|
@ -3042,6 +3049,13 @@ definitions:
|
||||||
example: <p>Hey this is a status!</p>
|
example: <p>Hey this is a status!</p>
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Content
|
x-go-name: Content
|
||||||
|
content_type:
|
||||||
|
description: |-
|
||||||
|
Content type that was used to parse the status's text. Returned when
|
||||||
|
status is deleted, so if the user is redrafting the message the client
|
||||||
|
can default to the same content type.
|
||||||
|
type: string
|
||||||
|
x-go-name: ContentType
|
||||||
created_at:
|
created_at:
|
||||||
description: The date when this status was created (ISO 8601 Datetime).
|
description: The date when this status was created (ISO 8601 Datetime).
|
||||||
example: "2021-07-30T09:20:25+00:00"
|
example: "2021-07-30T09:20:25+00:00"
|
||||||
|
@ -3186,6 +3200,10 @@ definitions:
|
||||||
StatusSource represents the source text of a
|
StatusSource represents the source text of a
|
||||||
status as submitted to the API when it was created.
|
status as submitted to the API when it was created.
|
||||||
properties:
|
properties:
|
||||||
|
content_type:
|
||||||
|
description: Content type that was used to parse the text.
|
||||||
|
type: string
|
||||||
|
x-go-name: ContentType
|
||||||
id:
|
id:
|
||||||
description: ID of the status.
|
description: ID of the status.
|
||||||
example: 01FBVD42CQ3ZEEVMW180SBX03B
|
example: 01FBVD42CQ3ZEEVMW180SBX03B
|
||||||
|
|
|
@ -145,6 +145,7 @@ func (suite *StatusBoostTestSuite) TestPostBoost() {
|
||||||
"bookmarked": true,
|
"bookmarked": true,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "hello world! #welcome ! first post on the instance :rainbow: !",
|
"content": "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [
|
"emojis": [
|
||||||
|
@ -331,6 +332,7 @@ func (suite *StatusBoostTestSuite) TestPostBoostOwnFollowersOnly() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "hi!",
|
"content": "hi!",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -543,6 +545,7 @@ func (suite *StatusBoostTestSuite) TestPostBoostImplicitAccept() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>Hi <span class=\"h-card\"><a href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>1happyturtle</span></a></span>, can I reply?</p>",
|
"content": "<p>Hi <span class=\"h-card\"><a href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>1happyturtle</span></a></span>, can I reply?</p>",
|
||||||
|
"content_type": "text/markdown",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
|
|
@ -139,6 +139,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>",
|
"content": "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -225,6 +226,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusIntPolicy() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>",
|
"content": "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -321,6 +323,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusIntPolicyJSON() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>",
|
"content": "<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -535,6 +538,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusMarkdown() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<h1>Title</h1><h2>Smaller title</h2><p>This is a post written in <a href=\"https://www.markdownguide.org/\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">markdown</a></p>",
|
"content": "<h1>Title</h1><h2>Smaller title</h2><p>This is a post written in <a href=\"https://www.markdownguide.org/\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">markdown</a></p>",
|
||||||
|
"content_type": "text/markdown",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -619,6 +623,7 @@ func (suite *StatusCreateTestSuite) TestMentionUnknownAccount() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>hello <span class=\"h-card\"><a href=\"https://unknown-instance.com/@brand_new_person\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>brand_new_person</span></a></span></p>",
|
"content": "<p>hello <span class=\"h-card\"><a href=\"https://unknown-instance.com/@brand_new_person\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>brand_new_person</span></a></span></p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -697,6 +702,7 @@ func (suite *StatusCreateTestSuite) TestPostStatusWithLinksAndTags() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p><a href=\"http://localhost:8080/tags/test\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>test</span></a> alright, should be able to post <a href=\"http://localhost:8080/tags/links\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>links</span></a> with fragments in them now, let's see........<br><br><a href=\"https://docs.gotosocial.org/en/latest/user_guide/posts/#links\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://docs.gotosocial.org/en/latest/user_guide/posts/#links</a><br><br><a href=\"http://localhost:8080/tags/gotosocial\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>gotosocial</span></a><br><br>(tobi remember to pull the docker image challenge)</p>",
|
"content": "<p><a href=\"http://localhost:8080/tags/test\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>test</span></a> alright, should be able to post <a href=\"http://localhost:8080/tags/links\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>links</span></a> with fragments in them now, let's see........<br><br><a href=\"https://docs.gotosocial.org/en/latest/user_guide/posts/#links\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://docs.gotosocial.org/en/latest/user_guide/posts/#links</a><br><br><a href=\"http://localhost:8080/tags/gotosocial\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>gotosocial</span></a><br><br>(tobi remember to pull the docker image challenge)</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -781,6 +787,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow:<br>here's an emoji that isn't in the db: :test_emoji:</p>",
|
"content": "<p>here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow:<br>here's an emoji that isn't in the db: :test_emoji:</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [
|
"emojis": [
|
||||||
|
@ -879,6 +886,7 @@ func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>hello <span class=\"h-card\"><a href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>1happyturtle</span></a></span> this reply should work!</p>",
|
"content": "<p>hello <span class=\"h-card\"><a href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>1happyturtle</span></a></span> this reply should work!</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -962,6 +970,7 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>here's an image attachment</p>",
|
"content": "<p>here's an image attachment</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -1067,6 +1076,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithNoncanonicalLanguageTag
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>English? what's English? i speak American</p>",
|
"content": "<p>English? what's English? i speak American</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -1142,6 +1152,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithPollForm() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>this is a status with a poll!</p>",
|
"content": "<p>this is a status with a poll!</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -1239,6 +1250,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithPollJSON() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>this is a status with a poll!</p>",
|
"content": "<p>this is a status with a poll!</p>",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
|
|
@ -77,6 +77,10 @@ func (suite *StatusDeleteTestSuite) TestPostDelete() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.NotNil(statusReply)
|
suite.NotNil(statusReply)
|
||||||
|
|
||||||
|
// Check that text and content type are returned for delete and redraft
|
||||||
|
suite.Equal("hello everyone!", statusReply.Text)
|
||||||
|
suite.Equal(apimodel.StatusContentTypePlain, statusReply.ContentType)
|
||||||
|
|
||||||
if !testrig.WaitFor(func() bool {
|
if !testrig.WaitFor(func() bool {
|
||||||
_, err := suite.db.GetStatusByID(ctx, targetStatus.ID)
|
_, err := suite.db.GetStatusByID(ctx, targetStatus.ID)
|
||||||
return errors.Is(err, db.ErrNoEntries)
|
return errors.Is(err, db.ErrNoEntries)
|
||||||
|
|
|
@ -104,6 +104,7 @@ func (suite *StatusFaveTestSuite) TestPostFave() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "🐕🐕🐕🐕🐕",
|
"content": "🐕🐕🐕🐕🐕",
|
||||||
|
"content_type": "text/plain",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
@ -228,6 +229,7 @@ func (suite *StatusFaveTestSuite) TestPostFaveImplicitAccept() {
|
||||||
"bookmarked": false,
|
"bookmarked": false,
|
||||||
"card": null,
|
"card": null,
|
||||||
"content": "<p>Hi <span class=\"h-card\"><a href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>1happyturtle</span></a></span>, can I reply?</p>",
|
"content": "<p>Hi <span class=\"h-card\"><a href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>1happyturtle</span></a></span>, can I reply?</p>",
|
||||||
|
"content_type": "text/markdown",
|
||||||
"created_at": "right the hell just now babyee",
|
"created_at": "right the hell just now babyee",
|
||||||
"edited_at": null,
|
"edited_at": null,
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
|
|
|
@ -149,6 +149,7 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello everyone!",
|
"text": "hello everyone!",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -238,6 +239,7 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello everyone!",
|
"text": "hello everyone!",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
|
|
@ -92,7 +92,8 @@ func (suite *StatusSourceTestSuite) TestGetSource() {
|
||||||
suite.Equal(`{
|
suite.Equal(`{
|
||||||
"id": "01F8MHAMCHF6Y650WCRSCP4WMY",
|
"id": "01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||||
"text": "hello everyone!",
|
"text": "hello everyone!",
|
||||||
"spoiler_text": "introduction post"
|
"spoiler_text": "introduction post",
|
||||||
|
"content_type": "text/plain"
|
||||||
}`, dst.String())
|
}`, dst.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,10 @@ type Status struct {
|
||||||
// so the user may redraft from the source text without the client having to reverse-engineer
|
// so the user may redraft from the source text without the client having to reverse-engineer
|
||||||
// the original text from the HTML content.
|
// the original text from the HTML content.
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
|
// Content type that was used to parse the status's text. Returned when
|
||||||
|
// status is deleted, so if the user is redrafting the message the client
|
||||||
|
// can default to the same content type.
|
||||||
|
ContentType StatusContentType `json:"content_type,omitempty"`
|
||||||
// A list of filters that matched this status and why they matched, if there are any such filters.
|
// A list of filters that matched this status and why they matched, if there are any such filters.
|
||||||
Filtered []FilterResult `json:"filtered,omitempty"`
|
Filtered []FilterResult `json:"filtered,omitempty"`
|
||||||
// The interaction policy for this status, as set by the status author.
|
// The interaction policy for this status, as set by the status author.
|
||||||
|
@ -320,6 +324,9 @@ type StatusSource struct {
|
||||||
|
|
||||||
// Plain-text version of spoiler text.
|
// Plain-text version of spoiler text.
|
||||||
SpoilerText string `json:"spoiler_text"`
|
SpoilerText string `json:"spoiler_text"`
|
||||||
|
|
||||||
|
// Content type that was used to parse the text.
|
||||||
|
ContentType StatusContentType `json:"content_type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusEdit represents one historical revision of a status, containing
|
// StatusEdit represents one historical revision of a status, containing
|
||||||
|
|
2
internal/cache/size.go
vendored
2
internal/cache/size.go
vendored
|
@ -649,6 +649,7 @@ func sizeofStatus() uintptr {
|
||||||
URL: exampleURI,
|
URL: exampleURI,
|
||||||
Content: exampleText,
|
Content: exampleText,
|
||||||
Text: exampleText,
|
Text: exampleText,
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: []string{exampleID, exampleID, exampleID},
|
AttachmentIDs: []string{exampleID, exampleID, exampleID},
|
||||||
TagIDs: []string{exampleID, exampleID, exampleID},
|
TagIDs: []string{exampleID, exampleID, exampleID},
|
||||||
MentionIDs: []string{},
|
MentionIDs: []string{},
|
||||||
|
@ -694,6 +695,7 @@ func sizeofStatusEdit() uintptr {
|
||||||
Content: exampleText,
|
Content: exampleText,
|
||||||
ContentWarning: exampleUsername, // similar length
|
ContentWarning: exampleUsername, // similar length
|
||||||
Text: exampleText,
|
Text: exampleText,
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: func() *bool { ok := false; return &ok }(),
|
Sensitive: func() *bool { ok := false; return &ok }(),
|
||||||
AttachmentIDs: []string{exampleID, exampleID, exampleID},
|
AttachmentIDs: []string{exampleID, exampleID, exampleID},
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
// 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"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
gtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations/20250226013442_add_status_content_type"
|
||||||
|
|
||||||
|
"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 {
|
||||||
|
|
||||||
|
// Generate new Status.ContentType column definition from bun.
|
||||||
|
statusType := reflect.TypeOf((*gtsmodel.Status)(nil))
|
||||||
|
colDef, err := getBunColumnDef(tx, statusType, "ContentType")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add column to Status table.
|
||||||
|
_, err = tx.NewAddColumn().
|
||||||
|
Model((*gtsmodel.Status)(nil)).
|
||||||
|
ColumnExpr(colDef).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// same for StatusEdit
|
||||||
|
|
||||||
|
statusEditType := reflect.TypeOf((*gtsmodel.StatusEdit)(nil))
|
||||||
|
colDef, err = getBunColumnDef(tx, statusEditType, "ContentType")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.NewAddColumn().
|
||||||
|
Model((*gtsmodel.StatusEdit)(nil)).
|
||||||
|
ColumnExpr(colDef).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 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,89 @@
|
||||||
|
// 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 (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Status represents a user-created 'post' or 'status' in the database, either remote or local
|
||||||
|
type Status struct {
|
||||||
|
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
|
||||||
|
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
|
||||||
|
EditedAt time.Time `bun:"type:timestamptz,nullzero"` // when this status was last edited (if set)
|
||||||
|
FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched.
|
||||||
|
PinnedAt time.Time `bun:"type:timestamptz,nullzero"` // Status was pinned by owning account at this time.
|
||||||
|
URI string `bun:",unique,nullzero,notnull"` // activitypub URI of this status
|
||||||
|
URL string `bun:",nullzero"` // web url for viewing this status
|
||||||
|
Content string `bun:""` // content of this status; likely html-formatted but not guaranteed
|
||||||
|
AttachmentIDs []string `bun:"attachments,array"` // Database IDs of any media attachments associated with this status
|
||||||
|
Attachments []*gtsmodel.MediaAttachment `bun:"attached_media,rel:has-many"` // Attachments corresponding to attachmentIDs
|
||||||
|
TagIDs []string `bun:"tags,array"` // Database IDs of any tags used in this status
|
||||||
|
Tags []*gtsmodel.Tag `bun:"attached_tags,m2m:status_to_tags"` // Tags corresponding to tagIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
|
||||||
|
MentionIDs []string `bun:"mentions,array"` // Database IDs of any mentions in this status
|
||||||
|
Mentions []*gtsmodel.Mention `bun:"attached_mentions,rel:has-many"` // Mentions corresponding to mentionIDs
|
||||||
|
EmojiIDs []string `bun:"emojis,array"` // Database IDs of any emojis used in this status
|
||||||
|
Emojis []*gtsmodel.Emoji `bun:"attached_emojis,m2m:status_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
|
||||||
|
Local *bool `bun:",nullzero,notnull,default:false"` // is this status from a local account?
|
||||||
|
AccountID string `bun:"type:CHAR(26),nullzero,notnull"` // which account posted this status?
|
||||||
|
Account *gtsmodel.Account `bun:"rel:belongs-to"` // account corresponding to accountID
|
||||||
|
AccountURI string `bun:",nullzero,notnull"` // activitypub uri of the owner of this status
|
||||||
|
InReplyToID string `bun:"type:CHAR(26),nullzero"` // id of the status this status replies to
|
||||||
|
InReplyToURI string `bun:",nullzero"` // activitypub uri of the status this status is a reply to
|
||||||
|
InReplyToAccountID string `bun:"type:CHAR(26),nullzero"` // id of the account that this status replies to
|
||||||
|
InReplyTo *Status `bun:"-"` // status corresponding to inReplyToID
|
||||||
|
InReplyToAccount *gtsmodel.Account `bun:"rel:belongs-to"` // account corresponding to inReplyToAccountID
|
||||||
|
BoostOfID string `bun:"type:CHAR(26),nullzero"` // id of the status this status is a boost of
|
||||||
|
BoostOfURI string `bun:"-"` // URI of the status this status is a boost of; field not inserted in the db, just for dereferencing purposes.
|
||||||
|
BoostOfAccountID string `bun:"type:CHAR(26),nullzero"` // id of the account that owns the boosted status
|
||||||
|
BoostOf *Status `bun:"-"` // status that corresponds to boostOfID
|
||||||
|
BoostOfAccount *gtsmodel.Account `bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID
|
||||||
|
ThreadID string `bun:"type:CHAR(26),nullzero"` // id of the thread to which this status belongs; only set for remote statuses if a local account is involved at some point in the thread, otherwise null
|
||||||
|
EditIDs []string `bun:"edits,array"` //
|
||||||
|
Edits []*gtsmodel.StatusEdit `bun:"-"` //
|
||||||
|
PollID string `bun:"type:CHAR(26),nullzero"` //
|
||||||
|
Poll *gtsmodel.Poll `bun:"-"` //
|
||||||
|
ContentWarning string `bun:",nullzero"` // cw string for this status
|
||||||
|
Visibility gtsmodel.Visibility `bun:",nullzero,notnull"` // visibility entry for this status
|
||||||
|
Sensitive *bool `bun:",nullzero,notnull,default:false"` // mark the status as sensitive?
|
||||||
|
Language string `bun:",nullzero"` // what language is this status written in?
|
||||||
|
CreatedWithApplicationID string `bun:"type:CHAR(26),nullzero"` // Which application was used to create this status?
|
||||||
|
CreatedWithApplication *gtsmodel.Application `bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID
|
||||||
|
ActivityStreamsType string `bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!.
|
||||||
|
Text string `bun:""` // Original text of the status without formatting
|
||||||
|
ContentType StatusContentType `bun:",nullzero"` // Content type used to process the original text of the status
|
||||||
|
Federated *bool `bun:",notnull"` // This status will be federated beyond the local timeline(s)
|
||||||
|
InteractionPolicy *gtsmodel.InteractionPolicy `bun:""` // InteractionPolicy for this status. If null then the default InteractionPolicy should be assumed for this status's Visibility. Always null for boost wrappers.
|
||||||
|
PendingApproval *bool `bun:",nullzero,notnull,default:false"` // If true then status is a reply or boost wrapper that must be Approved by the reply-ee or boost-ee before being fully distributed.
|
||||||
|
PreApproved bool `bun:"-"` // If true, then status is a reply to or boost wrapper of a status on our instance, has permission to do the interaction, and an Accept should be sent out for it immediately. Field not stored in the DB.
|
||||||
|
ApprovedByURI string `bun:",nullzero"` // URI of an Accept Activity that approves the Announce or Create Activity that this status was/will be attached to.
|
||||||
|
}
|
||||||
|
|
||||||
|
type enumType int16
|
||||||
|
|
||||||
|
// StatusContentType is the content type with which a status's text is
|
||||||
|
// parsed. Can be either plain or markdown. Empty will default to plain.
|
||||||
|
type StatusContentType enumType
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusContentTypePlain StatusContentType = 1
|
||||||
|
StatusContentTypeMarkdown StatusContentType = 2
|
||||||
|
StatusContentTypeDefault = StatusContentTypePlain
|
||||||
|
)
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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 (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusEdit represents a **historical** view of a Status
|
||||||
|
// after a received edit. The Status itself will always
|
||||||
|
// contain the latest up-to-date information.
|
||||||
|
//
|
||||||
|
// Note that stored status edits may not exactly match that
|
||||||
|
// of the origin server, they are a best-effort by receiver
|
||||||
|
// to store version history. There is no AP history endpoint.
|
||||||
|
type StatusEdit struct {
|
||||||
|
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
|
||||||
|
Content string `bun:""` // Content of status at time of edit; likely html-formatted but not guaranteed.
|
||||||
|
ContentWarning string `bun:",nullzero"` // Content warning of status at time of edit.
|
||||||
|
Text string `bun:""` // Original status text, without formatting, at time of edit.
|
||||||
|
ContentType StatusContentType `bun:",nullzero"` // Content type used to process the original text of the status.
|
||||||
|
Language string `bun:",nullzero"` // Status language at time of edit.
|
||||||
|
Sensitive *bool `bun:",nullzero,notnull,default:false"` // Status sensitive flag at time of edit.
|
||||||
|
AttachmentIDs []string `bun:"attachments,array"` // Database IDs of media attachments associated with status at time of edit.
|
||||||
|
AttachmentDescriptions []string `bun:",array"` // Previous media descriptions of media attachments associated with status at time of edit.
|
||||||
|
Attachments []*gtsmodel.MediaAttachment `bun:"-"` // Media attachments relating to .AttachmentIDs field (not always populated).
|
||||||
|
PollOptions []string `bun:",array"` // Poll options of status at time of edit, only set if status contains a poll.
|
||||||
|
PollVotes []int `bun:",array"` // Poll vote count at time of status edit, only set if poll votes were reset.
|
||||||
|
StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // The originating status ID this is a historical edit of.
|
||||||
|
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // The creation time of this version of the status content (according to receiving server).
|
||||||
|
// We don't bother having a *gtsmodel.Status model here
|
||||||
|
// as the StatusEdit is always just attached to a Status,
|
||||||
|
// so it doesn't need a self-reference back to it.
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ func getFutureStatus() *gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@admin/statuses/" + id,
|
URL: "http://localhost:8080/@admin/statuses/" + id,
|
||||||
Content: "it's the future, wooooooooooooooooooooooooooooooooo",
|
Content: "it's the future, wooooooooooooooooooooooooooooooooo",
|
||||||
Text: "it's the future, wooooooooooooooooooooooooooooooooo",
|
Text: "it's the future, wooooooooooooooooooooooooooooooooo",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: []string{},
|
AttachmentIDs: []string{},
|
||||||
TagIDs: []string{},
|
TagIDs: []string{},
|
||||||
MentionIDs: []string{},
|
MentionIDs: []string{},
|
||||||
|
|
|
@ -1239,6 +1239,7 @@ func (d *Dereferencer) handleStatusEdit(
|
||||||
edit.Content = existing.Content
|
edit.Content = existing.Content
|
||||||
edit.ContentWarning = existing.ContentWarning
|
edit.ContentWarning = existing.ContentWarning
|
||||||
edit.Text = existing.Text
|
edit.Text = existing.Text
|
||||||
|
edit.ContentType = existing.ContentType
|
||||||
edit.Language = existing.Language
|
edit.Language = existing.Language
|
||||||
edit.Sensitive = existing.Sensitive
|
edit.Sensitive = existing.Sensitive
|
||||||
edit.StatusID = status.ID
|
edit.StatusID = status.ID
|
||||||
|
|
|
@ -69,6 +69,7 @@ type Status struct {
|
||||||
CreatedWithApplication *Application `bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID
|
CreatedWithApplication *Application `bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID
|
||||||
ActivityStreamsType string `bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!.
|
ActivityStreamsType string `bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!.
|
||||||
Text string `bun:""` // Original text of the status without formatting
|
Text string `bun:""` // Original text of the status without formatting
|
||||||
|
ContentType StatusContentType `bun:",nullzero"` // Content type used to process the original text of the status
|
||||||
Federated *bool `bun:",notnull"` // This status will be federated beyond the local timeline(s)
|
Federated *bool `bun:",notnull"` // This status will be federated beyond the local timeline(s)
|
||||||
InteractionPolicy *InteractionPolicy `bun:""` // InteractionPolicy for this status. If null then the default InteractionPolicy should be assumed for this status's Visibility. Always null for boost wrappers.
|
InteractionPolicy *InteractionPolicy `bun:""` // InteractionPolicy for this status. If null then the default InteractionPolicy should be assumed for this status's Visibility. Always null for boost wrappers.
|
||||||
PendingApproval *bool `bun:",nullzero,notnull,default:false"` // If true then status is a reply or boost wrapper that must be Approved by the reply-ee or boost-ee before being fully distributed.
|
PendingApproval *bool `bun:",nullzero,notnull,default:false"` // If true then status is a reply or boost wrapper that must be Approved by the reply-ee or boost-ee before being fully distributed.
|
||||||
|
@ -376,6 +377,16 @@ func (v Visibility) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatusContentType is the content type with which a status's text is
|
||||||
|
// parsed. Can be either plain or markdown. Empty will default to plain.
|
||||||
|
type StatusContentType enumType
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusContentTypePlain StatusContentType = 1
|
||||||
|
StatusContentTypeMarkdown StatusContentType = 2
|
||||||
|
StatusContentTypeDefault = StatusContentTypePlain
|
||||||
|
)
|
||||||
|
|
||||||
// Content models the simple string content
|
// Content models the simple string content
|
||||||
// of a status along with its ContentMap,
|
// of a status along with its ContentMap,
|
||||||
// which contains content entries keyed by
|
// which contains content entries keyed by
|
||||||
|
|
|
@ -31,6 +31,7 @@ type StatusEdit struct {
|
||||||
Content string `bun:""` // Content of status at time of edit; likely html-formatted but not guaranteed.
|
Content string `bun:""` // Content of status at time of edit; likely html-formatted but not guaranteed.
|
||||||
ContentWarning string `bun:",nullzero"` // Content warning of status at time of edit.
|
ContentWarning string `bun:",nullzero"` // Content warning of status at time of edit.
|
||||||
Text string `bun:""` // Original status text, without formatting, at time of edit.
|
Text string `bun:""` // Original status text, without formatting, at time of edit.
|
||||||
|
ContentType StatusContentType `bun:",nullzero"` // Content type used to process the original text of the status.
|
||||||
Language string `bun:",nullzero"` // Status language at time of edit.
|
Language string `bun:",nullzero"` // Status language at time of edit.
|
||||||
Sensitive *bool `bun:",nullzero,notnull,default:false"` // Status sensitive flag at time of edit.
|
Sensitive *bool `bun:",nullzero,notnull,default:false"` // Status sensitive flag at time of edit.
|
||||||
AttachmentIDs []string `bun:"attachments,array"` // Database IDs of media attachments associated with status at time of edit.
|
AttachmentIDs []string `bun:"attachments,array"` // Database IDs of media attachments associated with status at time of edit.
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||||
)
|
)
|
||||||
|
@ -106,11 +107,39 @@ type statusContent struct {
|
||||||
Tags []*gtsmodel.Tag
|
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(
|
func (p *Processor) processContent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
author *gtsmodel.Account,
|
author *gtsmodel.Account,
|
||||||
statusID string,
|
statusID string,
|
||||||
contentType string,
|
contentType gtsmodel.StatusContentType,
|
||||||
content string,
|
content string,
|
||||||
contentWarning string,
|
contentWarning string,
|
||||||
language string,
|
language string,
|
||||||
|
@ -146,20 +175,14 @@ func (p *Processor) processContent(
|
||||||
// function, according to the provided content-type.
|
// function, according to the provided content-type.
|
||||||
var format text.FormatFunc
|
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 {
|
switch contentType {
|
||||||
|
|
||||||
// Format status according to text/plain.
|
// Format status according to text/plain.
|
||||||
case "", string(apimodel.StatusContentTypePlain):
|
case gtsmodel.StatusContentTypePlain:
|
||||||
format = p.formatter.FromPlain
|
format = p.formatter.FromPlain
|
||||||
|
|
||||||
// Format status according to text/markdown.
|
// Format status according to text/markdown.
|
||||||
case string(apimodel.StatusContentTypeMarkdown):
|
case gtsmodel.StatusContentTypeMarkdown:
|
||||||
format = p.formatter.FromMarkdown
|
format = p.formatter.FromMarkdown
|
||||||
|
|
||||||
// Unknown.
|
// Unknown.
|
||||||
|
|
|
@ -66,11 +66,14 @@ func (p *Processor) Create(
|
||||||
// Generate new ID for status.
|
// Generate new ID for status.
|
||||||
statusID := id.NewULID()
|
statusID := id.NewULID()
|
||||||
|
|
||||||
|
// Process incoming content type.
|
||||||
|
contentType := processContentType(form.ContentType, nil, requester.Settings.StatusContentType)
|
||||||
|
|
||||||
// Process incoming status content fields.
|
// Process incoming status content fields.
|
||||||
content, errWithCode := p.processContent(ctx,
|
content, errWithCode := p.processContent(ctx,
|
||||||
requester,
|
requester,
|
||||||
statusID,
|
statusID,
|
||||||
string(form.ContentType),
|
contentType,
|
||||||
form.Status,
|
form.Status,
|
||||||
form.SpoilerText,
|
form.SpoilerText,
|
||||||
form.Language,
|
form.Language,
|
||||||
|
@ -163,6 +166,7 @@ func (p *Processor) Create(
|
||||||
Content: content.Content,
|
Content: content.Content,
|
||||||
ContentWarning: content.ContentWarning,
|
ContentWarning: content.ContentWarning,
|
||||||
Text: form.Status, // raw
|
Text: form.Status, // raw
|
||||||
|
ContentType: contentType,
|
||||||
|
|
||||||
// Set gathered mentions.
|
// Set gathered mentions.
|
||||||
MentionIDs: content.MentionIDs,
|
MentionIDs: content.MentionIDs,
|
||||||
|
|
|
@ -238,6 +238,36 @@ func (suite *StatusCreateTestSuite) TestProcessReplyToUnthreadedRemoteStatus() {
|
||||||
suite.NotEmpty(dbStatus.ThreadID)
|
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) {
|
func TestStatusCreateTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(StatusCreateTestSuite))
|
suite.Run(t, new(StatusCreateTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,11 +84,14 @@ func (p *Processor) Edit(
|
||||||
return nil, errWithCode
|
return nil, errWithCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process incoming content type.
|
||||||
|
contentType := processContentType(form.ContentType, status, requester.Settings.StatusContentType)
|
||||||
|
|
||||||
// Process incoming status edit content fields.
|
// Process incoming status edit content fields.
|
||||||
content, errWithCode := p.processContent(ctx,
|
content, errWithCode := p.processContent(ctx,
|
||||||
requester,
|
requester,
|
||||||
statusID,
|
statusID,
|
||||||
string(form.ContentType),
|
contentType,
|
||||||
form.Status,
|
form.Status,
|
||||||
form.SpoilerText,
|
form.SpoilerText,
|
||||||
form.Language,
|
form.Language,
|
||||||
|
@ -256,6 +259,7 @@ func (p *Processor) Edit(
|
||||||
edit.Content = status.Content
|
edit.Content = status.Content
|
||||||
edit.ContentWarning = status.ContentWarning
|
edit.ContentWarning = status.ContentWarning
|
||||||
edit.Text = status.Text
|
edit.Text = status.Text
|
||||||
|
edit.ContentType = status.ContentType
|
||||||
edit.Language = status.Language
|
edit.Language = status.Language
|
||||||
edit.Sensitive = status.Sensitive
|
edit.Sensitive = status.Sensitive
|
||||||
edit.StatusID = status.ID
|
edit.StatusID = status.ID
|
||||||
|
@ -298,6 +302,7 @@ func (p *Processor) Edit(
|
||||||
status.Content = content.Content
|
status.Content = content.Content
|
||||||
status.ContentWarning = content.ContentWarning
|
status.ContentWarning = content.ContentWarning
|
||||||
status.Text = form.Status
|
status.Text = form.Status
|
||||||
|
status.ContentType = contentType
|
||||||
status.Language = content.Language
|
status.Language = content.Language
|
||||||
status.Sensitive = &form.Sensitive
|
status.Sensitive = &form.Sensitive
|
||||||
status.AttachmentIDs = form.MediaIDs
|
status.AttachmentIDs = form.MediaIDs
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
||||||
)
|
)
|
||||||
|
@ -90,6 +91,142 @@ func (suite *StatusEditTestSuite) TestSimpleEdit() {
|
||||||
previousEdit := latestStatus.Edits[len(latestStatus.Edits)-1]
|
previousEdit := latestStatus.Edits[len(latestStatus.Edits)-1]
|
||||||
suite.Equal(status.Content, previousEdit.Content)
|
suite.Equal(status.Content, previousEdit.Content)
|
||||||
suite.Equal(status.Text, previousEdit.Text)
|
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.ContentWarning, previousEdit.ContentWarning)
|
||||||
suite.Equal(*status.Sensitive, *previousEdit.Sensitive)
|
suite.Equal(*status.Sensitive, *previousEdit.Sensitive)
|
||||||
suite.Equal(status.Language, previousEdit.Language)
|
suite.Equal(status.Language, previousEdit.Language)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"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.
|
// 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,
|
ID: status.ID,
|
||||||
Text: status.Text,
|
Text: status.Text,
|
||||||
SpoilerText: status.ContentWarning,
|
SpoilerText: status.ContentWarning,
|
||||||
|
ContentType: typeutils.ContentTypeToAPIContentType(status.ContentType),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,16 @@ func APIVisToVis(m apimodel.Visibility) gtsmodel.Visibility {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func APIContentTypeToContentType(m apimodel.StatusContentType) gtsmodel.StatusContentType {
|
||||||
|
switch m {
|
||||||
|
case apimodel.StatusContentTypePlain:
|
||||||
|
return gtsmodel.StatusContentTypePlain
|
||||||
|
case apimodel.StatusContentTypeMarkdown:
|
||||||
|
return gtsmodel.StatusContentTypeMarkdown
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func APIMarkerNameToMarkerName(m apimodel.MarkerName) gtsmodel.MarkerName {
|
func APIMarkerNameToMarkerName(m apimodel.MarkerName) gtsmodel.MarkerName {
|
||||||
switch m {
|
switch m {
|
||||||
case apimodel.MarkerNameHome:
|
case apimodel.MarkerNameHome:
|
||||||
|
|
|
@ -1391,6 +1391,7 @@ func (c *Converter) baseStatusToFrontend(
|
||||||
Emojis: apiEmojis,
|
Emojis: apiEmojis,
|
||||||
Card: nil, // TODO: implement cards
|
Card: nil, // TODO: implement cards
|
||||||
Text: s.Text,
|
Text: s.Text,
|
||||||
|
ContentType: ContentTypeToAPIContentType(s.ContentType),
|
||||||
InteractionPolicy: *apiInteractionPolicy,
|
InteractionPolicy: *apiInteractionPolicy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1626,6 +1627,17 @@ func (c *Converter) VisToAPIVis(ctx context.Context, m gtsmodel.Visibility) apim
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts a gts status content type into its api equivalent
|
||||||
|
func ContentTypeToAPIContentType(m gtsmodel.StatusContentType) apimodel.StatusContentType {
|
||||||
|
switch m {
|
||||||
|
case gtsmodel.StatusContentTypePlain:
|
||||||
|
return apimodel.StatusContentTypePlain
|
||||||
|
case gtsmodel.StatusContentTypeMarkdown:
|
||||||
|
return apimodel.StatusContentTypeMarkdown
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// InstanceRuleToAdminAPIRule converts a local instance rule into its api equivalent for serving at /api/v1/admin/instance/rules/:id
|
// InstanceRuleToAdminAPIRule converts a local instance rule into its api equivalent for serving at /api/v1/admin/instance/rules/:id
|
||||||
func InstanceRuleToAPIRule(r gtsmodel.Rule) apimodel.InstanceRule {
|
func InstanceRuleToAPIRule(r gtsmodel.Rule) apimodel.InstanceRule {
|
||||||
return apimodel.InstanceRule{
|
return apimodel.InstanceRule{
|
||||||
|
|
|
@ -578,6 +578,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontend() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello world! #welcome ! first post on the instance :rainbow: !",
|
"text": "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -758,6 +759,7 @@ func (suite *InternalToFrontendTestSuite) TestWarnFilteredStatusToFrontend() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello world! #welcome ! first post on the instance :rainbow: ! fnord",
|
"text": "hello world! #welcome ! first post on the instance :rainbow: ! fnord",
|
||||||
|
"content_type": "text/plain",
|
||||||
"filtered": [
|
"filtered": [
|
||||||
{
|
{
|
||||||
"filter": {
|
"filter": {
|
||||||
|
@ -943,6 +945,7 @@ func (suite *InternalToFrontendTestSuite) TestWarnFilteredBoostToFrontend() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello world! #welcome ! first post on the instance :rainbow: ! fnord",
|
"text": "hello world! #welcome ! first post on the instance :rainbow: ! fnord",
|
||||||
|
"content_type": "text/plain",
|
||||||
"filtered": [
|
"filtered": [
|
||||||
{
|
{
|
||||||
"filter": {
|
"filter": {
|
||||||
|
@ -1676,6 +1679,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendUnknownLanguage()
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello world! #welcome ! first post on the instance :rainbow: !",
|
"text": "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -1774,6 +1778,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendPartialInteraction
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
"text": "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -1897,6 +1902,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToAPIStatusPendingApproval()
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "Hi @1happyturtle, can I reply?",
|
"text": "Hi @1happyturtle, can I reply?",
|
||||||
|
"content_type": "text/markdown",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -3375,6 +3381,7 @@ func (suite *InternalToFrontendTestSuite) TestIntReqToAPI() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "🐢 i don't mind people sharing and liking this one but I want to moderate replies to it 🐢",
|
"text": "🐢 i don't mind people sharing and liking this one but I want to moderate replies to it 🐢",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -3473,6 +3480,7 @@ func (suite *InternalToFrontendTestSuite) TestIntReqToAPI() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "Hi @1happyturtle, can I reply?",
|
"text": "Hi @1happyturtle, can I reply?",
|
||||||
|
"content_type": "text/markdown",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -3632,6 +3640,7 @@ func (suite *InternalToFrontendTestSuite) TestConversationToAPISelfConvo() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello everyone!",
|
"text": "hello everyone!",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
@ -3801,6 +3810,7 @@ func (suite *InternalToFrontendTestSuite) TestConversationToAPI() {
|
||||||
"card": null,
|
"card": null,
|
||||||
"poll": null,
|
"poll": null,
|
||||||
"text": "hello everyone!",
|
"text": "hello everyone!",
|
||||||
|
"content_type": "text/plain",
|
||||||
"interaction_policy": {
|
"interaction_policy": {
|
||||||
"can_favourite": {
|
"can_favourite": {
|
||||||
"always": [
|
"always": [
|
||||||
|
|
|
@ -1408,6 +1408,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
URL: "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
|
||||||
Content: "hello world! #welcome ! first post on the instance :rainbow: !",
|
Content: "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||||
Text: "hello world! #welcome ! first post on the instance :rainbow: !",
|
Text: "hello world! #welcome ! first post on the instance :rainbow: !",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: []string{"01F8MH6NEM8D7527KZAECTCR76"},
|
AttachmentIDs: []string{"01F8MH6NEM8D7527KZAECTCR76"},
|
||||||
TagIDs: []string{"01F8MHA1A2NF9MJ3WCCQ3K8BSZ"},
|
TagIDs: []string{"01F8MHA1A2NF9MJ3WCCQ3K8BSZ"},
|
||||||
MentionIDs: []string{},
|
MentionIDs: []string{},
|
||||||
|
@ -1436,6 +1437,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37",
|
URL: "http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37",
|
||||||
Content: "🐕🐕🐕🐕🐕",
|
Content: "🐕🐕🐕🐕🐕",
|
||||||
Text: "🐕🐕🐕🐕🐕",
|
Text: "🐕🐕🐕🐕🐕",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:36:45Z"),
|
CreatedAt: TimeMustParse("2021-10-20T12:36:45Z"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1459,6 +1461,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0",
|
URL: "http://localhost:8080/@admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0",
|
||||||
Content: "hi @the_mighty_zork welcome to the instance!",
|
Content: "hi @the_mighty_zork welcome to the instance!",
|
||||||
Text: "hi @the_mighty_zork welcome to the instance!",
|
Text: "hi @the_mighty_zork welcome to the instance!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-11-20T13:32:16Z"),
|
CreatedAt: TimeMustParse("2021-11-20T13:32:16Z"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1506,6 +1509,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@admin/statuses/01J5QVB9VC76NPPRQ207GG4DRZ",
|
URL: "http://localhost:8080/@admin/statuses/01J5QVB9VC76NPPRQ207GG4DRZ",
|
||||||
Content: `<p>Hi <span class="h-card"><a href="http://localhost:8080/@1happyturtle" class="u-url mention" rel="nofollow noreferrer noopener" target="_blank">@<span>1happyturtle</span></a></span>, can I reply?</p>`,
|
Content: `<p>Hi <span class="h-card"><a href="http://localhost:8080/@1happyturtle" class="u-url mention" rel="nofollow noreferrer noopener" target="_blank">@<span>1happyturtle</span></a></span>, can I reply?</p>`,
|
||||||
Text: "Hi @1happyturtle, can I reply?",
|
Text: "Hi @1happyturtle, can I reply?",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
CreatedAt: TimeMustParse("2024-02-20T12:41:37+02:00"),
|
CreatedAt: TimeMustParse("2024-02-20T12:41:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1531,6 +1535,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||||
Content: "hello everyone!",
|
Content: "hello everyone!",
|
||||||
Text: "hello everyone!",
|
Text: "hello everyone!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1552,8 +1557,9 @@ 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 a Public 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. also it has no stored content type",
|
||||||
Text: "this is a Public 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. also it has no stored content type",
|
||||||
|
ContentType: 0,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1577,6 +1583,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MHBBN8120SYH7D5S050MGK",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MHBBN8120SYH7D5S050MGK",
|
||||||
Content: "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
Content: "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
||||||
Text: "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
Text: "this is a very personal post that I don't want anyone to interact with at all, and i only want mutuals to see it",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1611,6 +1618,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MH82FYRXD2RC6108DAJ5HB",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MH82FYRXD2RC6108DAJ5HB",
|
||||||
Content: "here's a little gif of trent.... and also a cow",
|
Content: "here's a little gif of trent.... and also a cow",
|
||||||
Text: "here's a little gif of trent.... and also a cow",
|
Text: "here's a little gif of trent.... and also a cow",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: []string{"01F8MH7TDVANYKWVE8VVKFPJTJ", "01CDR64G398ADCHXK08WWTHEZ5"},
|
AttachmentIDs: []string{"01F8MH7TDVANYKWVE8VVKFPJTJ", "01CDR64G398ADCHXK08WWTHEZ5"},
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
|
@ -1635,6 +1643,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS",
|
||||||
Content: "hi!",
|
Content: "hi!",
|
||||||
Text: "hi!",
|
Text: "hi!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: []string{},
|
AttachmentIDs: []string{},
|
||||||
CreatedAt: TimeMustParse("2022-05-20T11:37:55Z"),
|
CreatedAt: TimeMustParse("2022-05-20T11:37:55Z"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
|
@ -1659,6 +1668,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/065TKBPE0H2AH8S5X8JCK4XC58",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/065TKBPE0H2AH8S5X8JCK4XC58",
|
||||||
Content: "what do you think of sloths?",
|
Content: "what do you think of sloths?",
|
||||||
Text: "what do you think of sloths?",
|
Text: "what do you think of sloths?",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
CreatedAt: TimeMustParse("2022-05-20T11:41:10Z"),
|
CreatedAt: TimeMustParse("2022-05-20T11:41:10Z"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
|
@ -1684,6 +1694,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40",
|
||||||
Content: "<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\"><section class="about-user">\n <div class="col-header">\n <h2>About</h2>\n </div> \n <div class="fields">\n <h3 class="sr-only">Fields</h3>\n <dl>\n <div class="field">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class="field">\n <dt>age</dt>\n <dd>120</dd>\n </div>\n </dl>\n </div>\n <div class="bio">\n <h3 class="sr-only">Bio</h3>\n <p>i post about things that concern me</p>\n </div>\n <div class="sr-only" role="group">\n <h3 class="sr-only">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class="accountstats" aria-hidden="true">\n <b>Joined</b><time datetime="2022-06-04T13:12:00.000Z">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\n</code></pre><p>There, hope you liked that!</p>",
|
Content: "<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\"><section class="about-user">\n <div class="col-header">\n <h2>About</h2>\n </div> \n <div class="fields">\n <h3 class="sr-only">Fields</h3>\n <dl>\n <div class="field">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class="field">\n <dt>age</dt>\n <dd>120</dd>\n </div>\n </dl>\n </div>\n <div class="bio">\n <h3 class="sr-only">Bio</h3>\n <p>i post about things that concern me</p>\n </div>\n <div class="sr-only" role="group">\n <h3 class="sr-only">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class="accountstats" aria-hidden="true">\n <b>Joined</b><time datetime="2022-06-04T13:12:00.000Z">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\n</code></pre><p>There, hope you liked that!</p>",
|
||||||
Text: "Here's a bunch of HTML, read it and weep, weep then!\n\n```html\n<section class=\"about-user\">\n <div class=\"col-header\">\n <h2>About</h2>\n </div> \n <div class=\"fields\">\n <h3 class=\"sr-only\">Fields</h3>\n <dl>\n <div class=\"field\">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class=\"field\">\n <dt>age</dt>\n <dd>120</dd>\n </div>… <h3 class=\"sr-only\">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class=\"accountstats\" aria-hidden=\"true\">\n <b>Joined</b><time datetime=\"2022-06-04T13:12:00.000Z\">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\n```\n\nThere, hope you liked that!",
|
Text: "Here's a bunch of HTML, read it and weep, weep then!\n\n```html\n<section class=\"about-user\">\n <div class=\"col-header\">\n <h2>About</h2>\n </div> \n <div class=\"fields\">\n <h3 class=\"sr-only\">Fields</h3>\n <dl>\n <div class=\"field\">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class=\"field\">\n <dt>age</dt>\n <dd>120</dd>\n </div>… <h3 class=\"sr-only\">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class=\"accountstats\" aria-hidden=\"true\">\n <b>Joined</b><time datetime=\"2022-06-04T13:12:00.000Z\">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\n```\n\nThere, hope you liked that!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
CreatedAt: TimeMustParse("2023-12-10T11:24:00+02:00"),
|
CreatedAt: TimeMustParse("2023-12-10T11:24:00+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1707,6 +1718,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01J2M1HPFSS54S60Y0KYV23KJE",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01J2M1HPFSS54S60Y0KYV23KJE",
|
||||||
Content: "<p>Thanks! Here's a NIN track</p>",
|
Content: "<p>Thanks! Here's a NIN track</p>",
|
||||||
Text: "Thanks! Here's a NIN track",
|
Text: "Thanks! Here's a NIN track",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
AttachmentIDs: []string{"01J2M20K6K9XQC4WSB961YJHV6"},
|
AttachmentIDs: []string{"01J2M20K6K9XQC4WSB961YJHV6"},
|
||||||
CreatedAt: TimeMustParse("2024-01-10T11:24:00+02:00"),
|
CreatedAt: TimeMustParse("2024-01-10T11:24:00+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
|
@ -1732,6 +1744,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@the_mighty_zork/statuses/01JDPZC707CKDN8N4QVWM4Z1NR",
|
URL: "http://localhost:8080/@the_mighty_zork/statuses/01JDPZC707CKDN8N4QVWM4Z1NR",
|
||||||
Content: "<p>this is the latest revision of the status, with a content-warning</p>",
|
Content: "<p>this is the latest revision of the status, with a content-warning</p>",
|
||||||
Text: "this is the latest revision of the status, with a content-warning",
|
Text: "this is the latest revision of the status, with a content-warning",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
ContentWarning: "edited status",
|
ContentWarning: "edited status",
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
CreatedAt: TimeMustParse("2024-11-01T11:00:00+02:00"),
|
CreatedAt: TimeMustParse("2024-11-01T11:00:00+02:00"),
|
||||||
|
@ -1758,6 +1771,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHBQCBTDKN6X5VHGMMN4MA",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHBQCBTDKN6X5VHGMMN4MA",
|
||||||
Content: "🐢 hi everyone i post about turtles 🐢",
|
Content: "🐢 hi everyone i post about turtles 🐢",
|
||||||
Text: "🐢 hi everyone i post about turtles 🐢",
|
Text: "🐢 hi everyone i post about turtles 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1781,6 +1795,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHC0H0A7XHTVH5F596ZKBM",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHC0H0A7XHTVH5F596ZKBM",
|
||||||
Content: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
Content: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
||||||
Text: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
Text: "🐢 this one is federated, likeable, and boostable but not replyable 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1815,6 +1830,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHC8VWDRBQR0N1BATDDEM5",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHC8VWDRBQR0N1BATDDEM5",
|
||||||
Content: "🐢 i don't mind people sharing and liking this one but I want to moderate replies to it 🐢",
|
Content: "🐢 i don't mind people sharing and liking this one but I want to moderate replies to it 🐢",
|
||||||
Text: "🐢 i don't mind people sharing and liking this one but I want to moderate replies to it 🐢",
|
Text: "🐢 i don't mind people sharing and liking this one but I want to moderate replies to it 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1850,6 +1866,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHCP5P2NWYQ416SBA0XSEV",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01F8MHCP5P2NWYQ416SBA0XSEV",
|
||||||
Content: "🐢 this is a public status but I want it local only and not boostable 🐢",
|
Content: "🐢 this is a public status but I want it local only and not boostable 🐢",
|
||||||
Text: "🐢 this is a public status but I want it local only and not boostable 🐢",
|
Text: "🐢 this is a public status but I want it local only and not boostable 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1884,6 +1901,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01FCQSQ667XHJ9AV9T27SJJSX5",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01FCQSQ667XHJ9AV9T27SJJSX5",
|
||||||
Content: "🐢 @the_mighty_zork hi zork! 🐢",
|
Content: "🐢 @the_mighty_zork hi zork! 🐢",
|
||||||
Text: "🐢 @the_mighty_zork hi zork! 🐢",
|
Text: "🐢 @the_mighty_zork hi zork! 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1910,6 +1928,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01FN3VJGFH10KR7S2PB0GFJZYG",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01FN3VJGFH10KR7S2PB0GFJZYG",
|
||||||
Content: "🐢 @the_mighty_zork hi zork, this is a direct message, shhhhhh! 🐢",
|
Content: "🐢 @the_mighty_zork hi zork, this is a direct message, shhhhhh! 🐢",
|
||||||
Text: "🐢 @the_mighty_zork hi zork, this is a direct message, shhhhhh! 🐢",
|
Text: "🐢 @the_mighty_zork hi zork, this is a direct message, shhhhhh! 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
|
@ -1937,6 +1956,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01G20ZM733MGN8J344T4ZDDFY1",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01G20ZM733MGN8J344T4ZDDFY1",
|
||||||
Content: "🐢 hi followers! did u know i'm a turtle? 🐢",
|
Content: "🐢 hi followers! did u know i'm a turtle? 🐢",
|
||||||
Text: "🐢 hi followers! did u know i'm a turtle? 🐢",
|
Text: "🐢 hi followers! did u know i'm a turtle? 🐢",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: []string{},
|
AttachmentIDs: []string{},
|
||||||
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-10-20T12:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
|
@ -1961,6 +1981,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01HEN2PRXT0TF4YDRA64FZZRN7",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01HEN2PRXT0TF4YDRA64FZZRN7",
|
||||||
Content: "hey everyone i got stuck in a shed. any ideas for how to get out?",
|
Content: "hey everyone i got stuck in a shed. any ideas for how to get out?",
|
||||||
Text: "hey everyone i got stuck in a shed. any ideas for how to get out?",
|
Text: "hey everyone i got stuck in a shed. any ideas for how to get out?",
|
||||||
|
ContentType: gtsmodel.StatusContentTypePlain,
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
CreatedAt: TimeMustParse("2021-07-28T10:40:37+02:00"),
|
CreatedAt: TimeMustParse("2021-07-28T10:40:37+02:00"),
|
||||||
EditedAt: time.Time{},
|
EditedAt: time.Time{},
|
||||||
|
@ -1986,6 +2007,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://localhost:8080/@1happyturtle/statuses/01JDPZEZ77X1NX0TY9M10BK1HM",
|
URL: "http://localhost:8080/@1happyturtle/statuses/01JDPZEZ77X1NX0TY9M10BK1HM",
|
||||||
Content: "<p>now edited to bring back the previous edit's media!</p>",
|
Content: "<p>now edited to bring back the previous edit's media!</p>",
|
||||||
Text: "now edited to bring back the previous edit's media!",
|
Text: "now edited to bring back the previous edit's media!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
ContentWarning: "edit with media attachments",
|
ContentWarning: "edit with media attachments",
|
||||||
AttachmentIDs: []string{"01JDQ164HM08SGJ7ZEK9003Z4B"},
|
AttachmentIDs: []string{"01JDQ164HM08SGJ7ZEK9003Z4B"},
|
||||||
CreatedAt: TimeMustParse("2024-11-01T10:00:00+02:00"),
|
CreatedAt: TimeMustParse("2024-11-01T10:00:00+02:00"),
|
||||||
|
@ -2087,6 +2109,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
|
||||||
URL: "http://fossbros-anonymous.io/@foss_satan/statuses/______",
|
URL: "http://fossbros-anonymous.io/@foss_satan/statuses/______",
|
||||||
Content: "<p>this is the latest status edit without poll change</p>",
|
Content: "<p>this is the latest status edit without poll change</p>",
|
||||||
Text: "this is the latest status edit without poll change",
|
Text: "this is the latest status edit without poll change",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
CreatedAt: TimeMustParse("2024-11-01T09:00:00+02:00"),
|
CreatedAt: TimeMustParse("2024-11-01T09:00:00+02:00"),
|
||||||
|
@ -3617,6 +3640,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>this is the original status</p>",
|
Content: "<p>this is the original status</p>",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Text: "this is the original status",
|
Text: "this is the original status",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
|
@ -3630,6 +3654,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>this is the first status edit! now with content-warning</p>",
|
Content: "<p>this is the first status edit! now with content-warning</p>",
|
||||||
ContentWarning: "edited status",
|
ContentWarning: "edited status",
|
||||||
Text: "this is the first status edit! now with content-warning",
|
Text: "this is the first status edit! now with content-warning",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
|
@ -3643,6 +3668,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>this is the original status</p>",
|
Content: "<p>this is the original status</p>",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Text: "this is the original status",
|
Text: "this is the original status",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
|
@ -3656,6 +3682,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>now edited to have some media!</p>",
|
Content: "<p>now edited to have some media!</p>",
|
||||||
ContentWarning: "edit with media attachments",
|
ContentWarning: "edit with media attachments",
|
||||||
Text: "now edited to have some media!",
|
Text: "now edited to have some media!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(true),
|
Sensitive: util.Ptr(true),
|
||||||
AttachmentIDs: []string{"01JDQ164HM08SGJ7ZEK9003Z4B"},
|
AttachmentIDs: []string{"01JDQ164HM08SGJ7ZEK9003Z4B"},
|
||||||
|
@ -3669,6 +3696,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>now edited to remove the media</p>",
|
Content: "<p>now edited to remove the media</p>",
|
||||||
ContentWarning: "edit missing previous media attachments",
|
ContentWarning: "edit missing previous media attachments",
|
||||||
Text: "now edited to remove the media",
|
Text: "now edited to remove the media",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
|
@ -3682,6 +3710,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>this is the original status, with a poll!</p>",
|
Content: "<p>this is the original status, with a poll!</p>",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Text: "this is the original status, with a poll!",
|
Text: "this is the original status, with a poll!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
|
@ -3695,6 +3724,7 @@ func NewTestStatusEdits() map[string]*gtsmodel.StatusEdit {
|
||||||
Content: "<p>this is the first status edit! now with a different poll!</p>",
|
Content: "<p>this is the first status edit! now with a different poll!</p>",
|
||||||
ContentWarning: "edited status",
|
ContentWarning: "edited status",
|
||||||
Text: "this is the first status edit! now with a different poll!",
|
Text: "this is the first status edit! now with a different poll!",
|
||||||
|
ContentType: gtsmodel.StatusContentTypeMarkdown,
|
||||||
Language: "en",
|
Language: "en",
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
AttachmentIDs: nil,
|
AttachmentIDs: nil,
|
||||||
|
|
Loading…
Reference in a new issue