diff options
author | 2024-01-05 13:39:31 +0100 | |
---|---|---|
committer | 2024-01-05 13:39:31 +0100 | |
commit | d5e3996a18ee37fc4bdf5718632d3d19ac7a8c1b (patch) | |
tree | 7b8df224893611c77cadc847c2fd8d7ec239b636 /internal | |
parent | [bugfix] fix check for closed poll to account for non-zero closed time but in... (diff) | |
download | gotosocial-d5e3996a18ee37fc4bdf5718632d3d19ac7a8c1b.tar.xz |
[feature] Parse instance descriptors as markdown, show T&C on /about (#2481)
* [feature] Parse instance descriptors as markdown, show T&C on /about
* lint
* remove unnecessary nullzero tags
Diffstat (limited to 'internal')
-rw-r--r-- | internal/api/client/instance/instancepatch_test.go | 50 | ||||
-rw-r--r-- | internal/api/model/instancev1.go | 6 | ||||
-rw-r--r-- | internal/api/model/instancev2.go | 4 | ||||
-rw-r--r-- | internal/db/bundb/migrations/20231227110845_instance_description_updates.go | 64 | ||||
-rw-r--r-- | internal/gtsmodel/instance.go | 7 | ||||
-rw-r--r-- | internal/processing/instance.go | 228 | ||||
-rw-r--r-- | internal/processing/processor.go | 17 | ||||
-rw-r--r-- | internal/typeutils/internaltofrontend.go | 53 | ||||
-rw-r--r-- | internal/typeutils/internaltofrontend_test.go | 15 |
9 files changed, 311 insertions, 133 deletions
diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index 420bcd79e..936d6efd9 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -78,8 +78,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "Example Instance", - "description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "description": "<p>Here's a fuller description of the GoToSocial testrig instance.</p><p>This instance is for testing purposes only. It doesn't federate at all. Go check out <a href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/tree/main/testrig</a> and <a href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing</a></p><p>Users on this instance:</p><ul><li><span class=\"h-card\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>admin</span></a></span> (admin!).</li><li><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> (posts about turtles, we don't know why).</li><li><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span> (who knows).</li></ul><p>If you need to edit the models for the testrig, you can do so at <code>internal/testmodels.go</code>.</p>", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "short_description_text": "This is the GoToSocial testrig. It doesn't federate or anything.\n\nWhen the testrig is shut down, all data on it will be deleted.\n\nDon't use this in production!", "email": "someone@example.org", "version": "0.0.0-testrig", "languages": [ @@ -173,7 +175,9 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { "id": "01GP3DFY9XQ1TJMZT5BGAZPXX3", "text": "Do crime" } - ] + ], + "terms": "<p>This is where a list of terms and conditions might go.</p><p>For example:</p><p>If you want to sign up on this instance, you oughta know that we:</p><ol><li>Will sell your data to whoever offers.</li><li>Secure the server with password <code>password</code> wherever possible.</li></ol>", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, dst.String()) } @@ -195,8 +199,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "Geoff's Instance", - "description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "description": "<p>Here's a fuller description of the GoToSocial testrig instance.</p><p>This instance is for testing purposes only. It doesn't federate at all. Go check out <a href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/tree/main/testrig</a> and <a href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing</a></p><p>Users on this instance:</p><ul><li><span class=\"h-card\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>admin</span></a></span> (admin!).</li><li><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> (posts about turtles, we don't know why).</li><li><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span> (who knows).</li></ul><p>If you need to edit the models for the testrig, you can do so at <code>internal/testmodels.go</code>.</p>", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "short_description_text": "This is the GoToSocial testrig. It doesn't federate or anything.\n\nWhen the testrig is shut down, all data on it will be deleted.\n\nDon't use this in production!", "email": "admin@example.org", "version": "0.0.0-testrig", "languages": [ @@ -290,13 +296,15 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { "id": "01GP3DFY9XQ1TJMZT5BGAZPXX3", "text": "Do crime" } - ] + ], + "terms": "<p>This is where a list of terms and conditions might go.</p><p>For example:</p><p>If you want to sign up on this instance, you oughta know that we:</p><ol><li>Will sell your data to whoever offers.</li><li>Secure the server with password <code>password</code> wherever possible.</li></ol>", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, dst.String()) } func (suite *InstancePatchTestSuite) TestInstancePatch3() { code, b := suite.instancePatch("", "", map[string][]string{ - "short_description": {"<p>This is some html, which is <em>allowed</em> in short descriptions.</p>"}, + "short_description": {"This is some html, which is <em>allowed</em> in short descriptions."}, }) if expectedCode := http.StatusOK; code != expectedCode { @@ -312,8 +320,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "GoToSocial Testrig Instance", - "description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "description": "<p>Here's a fuller description of the GoToSocial testrig instance.</p><p>This instance is for testing purposes only. It doesn't federate at all. Go check out <a href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/tree/main/testrig</a> and <a href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing</a></p><p>Users on this instance:</p><ul><li><span class=\"h-card\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>admin</span></a></span> (admin!).</li><li><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> (posts about turtles, we don't know why).</li><li><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span> (who knows).</li></ul><p>If you need to edit the models for the testrig, you can do so at <code>internal/testmodels.go</code>.</p>", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "<p>This is some html, which is <em>allowed</em> in short descriptions.</p>", + "short_description_text": "This is some html, which is <em>allowed</em> in short descriptions.", "email": "admin@example.org", "version": "0.0.0-testrig", "languages": [ @@ -407,7 +417,9 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { "id": "01GP3DFY9XQ1TJMZT5BGAZPXX3", "text": "Do crime" } - ] + ], + "terms": "<p>This is where a list of terms and conditions might go.</p><p>For example:</p><p>If you want to sign up on this instance, you oughta know that we:</p><ol><li>Will sell your data to whoever offers.</li><li>Secure the server with password <code>password</code> wherever possible.</li></ol>", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, dst.String()) } @@ -480,8 +492,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "GoToSocial Testrig Instance", - "description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "description": "<p>Here's a fuller description of the GoToSocial testrig instance.</p><p>This instance is for testing purposes only. It doesn't federate at all. Go check out <a href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/tree/main/testrig</a> and <a href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing</a></p><p>Users on this instance:</p><ul><li><span class=\"h-card\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>admin</span></a></span> (admin!).</li><li><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> (posts about turtles, we don't know why).</li><li><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span> (who knows).</li></ul><p>If you need to edit the models for the testrig, you can do so at <code>internal/testmodels.go</code>.</p>", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "short_description_text": "This is the GoToSocial testrig. It doesn't federate or anything.\n\nWhen the testrig is shut down, all data on it will be deleted.\n\nDon't use this in production!", "email": "", "version": "0.0.0-testrig", "languages": [ @@ -575,7 +589,9 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { "id": "01GP3DFY9XQ1TJMZT5BGAZPXX3", "text": "Do crime" } - ] + ], + "terms": "<p>This is where a list of terms and conditions might go.</p><p>For example:</p><p>If you want to sign up on this instance, you oughta know that we:</p><ol><li>Will sell your data to whoever offers.</li><li>Secure the server with password <code>password</code> wherever possible.</li></ol>", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, dst.String()) } @@ -619,8 +635,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "GoToSocial Testrig Instance", - "description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "description": "<p>Here's a fuller description of the GoToSocial testrig instance.</p><p>This instance is for testing purposes only. It doesn't federate at all. Go check out <a href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/tree/main/testrig</a> and <a href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing</a></p><p>Users on this instance:</p><ul><li><span class=\"h-card\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>admin</span></a></span> (admin!).</li><li><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> (posts about turtles, we don't know why).</li><li><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span> (who knows).</li></ul><p>If you need to edit the models for the testrig, you can do so at <code>internal/testmodels.go</code>.</p>", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "short_description_text": "This is the GoToSocial testrig. It doesn't federate or anything.\n\nWhen the testrig is shut down, all data on it will be deleted.\n\nDon't use this in production!", "email": "admin@example.org", "version": "0.0.0-testrig", "languages": [ @@ -716,7 +734,9 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { "id": "01GP3DFY9XQ1TJMZT5BGAZPXX3", "text": "Do crime" } - ] + ], + "terms": "<p>This is where a list of terms and conditions might go.</p><p>For example:</p><p>If you want to sign up on this instance, you oughta know that we:</p><ol><li>Will sell your data to whoever offers.</li><li>Secure the server with password <code>password</code> wherever possible.</li></ol>", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, dst.String()) // extra bonus: check the v2 model thumbnail after the patch @@ -773,8 +793,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "GoToSocial Testrig Instance", - "description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "description": "<p>Here's a fuller description of the GoToSocial testrig instance.</p><p>This instance is for testing purposes only. It doesn't federate at all. Go check out <a href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/tree/main/testrig</a> and <a href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing</a></p><p>Users on this instance:</p><ul><li><span class=\"h-card\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>admin</span></a></span> (admin!).</li><li><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> (posts about turtles, we don't know why).</li><li><span class=\"h-card\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>the_mighty_zork</span></a></span> (who knows).</li></ul><p>If you need to edit the models for the testrig, you can do so at <code>internal/testmodels.go</code>.</p>", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "<p>This is the GoToSocial testrig. It doesn't federate or anything.</p><p>When the testrig is shut down, all data on it will be deleted.</p><p>Don't use this in production!</p>", + "short_description_text": "This is the GoToSocial testrig. It doesn't federate or anything.\n\nWhen the testrig is shut down, all data on it will be deleted.\n\nDon't use this in production!", "email": "admin@example.org", "version": "0.0.0-testrig", "languages": [ @@ -868,7 +890,9 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() { "id": "01GP3DFY9XQ1TJMZT5BGAZPXX3", "text": "Do crime" } - ] + ], + "terms": "<p>This is where a list of terms and conditions might go.</p><p>For example:</p><p>If you want to sign up on this instance, you oughta know that we:</p><ol><li>Will sell your data to whoever offers.</li><li>Secure the server with password <code>password</code> wherever possible.</li></ol>", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, dst.String()) } diff --git a/internal/api/model/instancev1.go b/internal/api/model/instancev1.go index 2e5fef123..bec719941 100644 --- a/internal/api/model/instancev1.go +++ b/internal/api/model/instancev1.go @@ -38,12 +38,16 @@ type InstanceV1 struct { // // This should be displayed on the 'about' page for an instance. Description string `json:"description"` + // Raw (unparsed) version of description. + DescriptionText string `json:"description_text,omitempty"` // A shorter description of the instance. // // Should be HTML formatted, but might be plaintext. // // This should be displayed on the instance splash/landing page. ShortDescription string `json:"short_description"` + // Raw (unparsed) version of short description. + ShortDescriptionText string `json:"short_description_text,omitempty"` // An email address that may be used for inquiries. // example: admin@example.org Email string `json:"email"` @@ -92,6 +96,8 @@ type InstanceV1 struct { Rules []InstanceRule `json:"rules"` // Terms and conditions for accounts on this instance. Terms string `json:"terms,omitempty"` + // Raw (unparsed) version of terms. + TermsRaw string `json:"terms_text,omitempty"` } // InstanceV1URLs models instance-relevant URLs for client application consumption. diff --git a/internal/api/model/instancev2.go b/internal/api/model/instancev2.go index 6af657813..dda9033b4 100644 --- a/internal/api/model/instancev2.go +++ b/internal/api/model/instancev2.go @@ -49,6 +49,8 @@ type InstanceV2 struct { // // This should be displayed on the 'about' page for an instance. Description string `json:"description"` + // Raw (unparsed) version of description. + DescriptionText string `json:"description_text,omitempty"` // Basic anonymous usage data for this instance. Usage InstanceV2Usage `json:"usage"` // An image used to represent this instance. @@ -66,6 +68,8 @@ type InstanceV2 struct { Rules []InstanceRule `json:"rules"` // Terms and conditions for accounts on this instance. Terms string `json:"terms,omitempty"` + // Raw (unparsed) version of terms. + TermsText string `json:"terms_text,omitempty"` } // Usage data for this instance. diff --git a/internal/db/bundb/migrations/20231227110845_instance_description_updates.go b/internal/db/bundb/migrations/20231227110845_instance_description_updates.go new file mode 100644 index 000000000..94914ff55 --- /dev/null +++ b/internal/db/bundb/migrations/20231227110845_instance_description_updates.go @@ -0,0 +1,64 @@ +// 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" + "strings" + + "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 { + columns := []string{ + "short_description_text", + "description_text", + "terms_text", + } + + for _, column := range columns { + _, err := tx.ExecContext(ctx, + "ALTER TABLE ? ADD COLUMN ? TEXT", + bun.Ident("instances"), bun.Ident(column), + ) + if err != nil { + e := err.Error() + if !(strings.Contains(e, "already exists") || + strings.Contains(e, "duplicate column name") || + strings.Contains(e, "SQLSTATE 42701")) { + 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) + } +} diff --git a/internal/gtsmodel/instance.go b/internal/gtsmodel/instance.go index 6d572f519..027d8fba4 100644 --- a/internal/gtsmodel/instance.go +++ b/internal/gtsmodel/instance.go @@ -31,8 +31,11 @@ type Instance struct { DomainBlockID string `bun:"type:CHAR(26),nullzero"` // ID of any existing domain block for this instance in the database DomainBlock *DomainBlock `bun:"rel:belongs-to"` // Domain block corresponding to domainBlockID ShortDescription string `bun:""` // Short description of this instance - Description string `bun:""` // Longer description of this instance - Terms string `bun:""` // Terms and conditions of this instance + ShortDescriptionText string `bun:""` // Raw text version of short description (before parsing). + Description string `bun:""` // Longer description of this instance. + DescriptionText string `bun:""` // Raw text version of long description (before parsing). + Terms string `bun:""` // Terms and conditions of this instance. + TermsText string `bun:""` // Raw text version of terms (before parsing). ContactEmail string `bun:""` // Contact email address for this instance ContactAccountUsername string `bun:",nullzero"` // Username of the contact account for this instance ContactAccountID string `bun:"type:CHAR(26),nullzero"` // Contact account ID in the database for this instance diff --git a/internal/processing/instance.go b/internal/processing/instance.go index caf2b9fc1..a93936425 100644 --- a/internal/processing/instance.go +++ b/internal/processing/instance.go @@ -33,15 +33,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/validate" ) -func (p *Processor) getThisInstance(ctx context.Context) (*gtsmodel.Instance, error) { - instance, err := p.state.DB.GetInstance(ctx, config.GetHost()) - if err != nil { - return nil, err - } - - return instance, nil -} - func (p *Processor) InstanceGetV1(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) { i, err := p.getThisInstance(ctx) if err != nil { @@ -146,67 +137,55 @@ func (p *Processor) InstanceGetRules(ctx context.Context) ([]apimodel.InstanceRu } func (p *Processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.InstanceV1, gtserror.WithCode) { - // fetch the instance entry from the db for processing - host := config.GetHost() - - instance, err := p.state.DB.GetInstance(ctx, host) + // Fetch this instance from the db for processing. + instance, err := p.getThisInstance(ctx) if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", host, err)) + err = fmt.Errorf("db error fetching instance: %w", err) + return nil, gtserror.NewErrorInternalError(err) } - // fetch the instance account from the db for processing - ia, err := p.state.DB.GetInstanceAccount(ctx, "") + // Fetch this instance account from the db for processing. + instanceAcc, err := p.state.DB.GetInstanceAccount(ctx, "") if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance account %s: %s", host, err)) + err = fmt.Errorf("db error fetching instance account: %w", err) + return nil, gtserror.NewErrorInternalError(err) } - updatingColumns := []string{} + // Columns to update + // in the database. + var columns []string - // validate & update site title if it's set on the form + // Validate & update site + // title if set on the form. if form.Title != nil { - if err := validate.SiteTitle(*form.Title); err != nil { - return nil, gtserror.NewErrorBadRequest(err, fmt.Sprintf("site title invalid: %s", err)) + title := *form.Title + if err := validate.SiteTitle(title); err != nil { + return nil, gtserror.NewErrorBadRequest(err, err.Error()) } - updatingColumns = append(updatingColumns, "title") - instance.Title = text.SanitizeToPlaintext(*form.Title) // don't allow html in site title + + // Don't allow html in site title. + instance.Title = text.SanitizeToPlaintext(title) + columns = append(columns, "title") } - // validate & update site contact account if it's set on the form + // Validate & update site contact + // account if set on the form. + // + // Empty username unsets contact. if form.ContactUsername != nil { - // make sure the account with the given username exists in the db - contactAccount, err := p.state.DB.GetAccountByUsernameDomain(ctx, *form.ContactUsername, "") - if err != nil { - return nil, gtserror.NewErrorBadRequest(err, fmt.Sprintf("account with username %s not retrievable", *form.ContactUsername)) - } - // make sure it has a user associated with it - contactUser, err := p.state.DB.GetUserByAccountID(ctx, contactAccount.ID) + contactAccountID, err := p.contactAccountIDForUsername(ctx, *form.ContactUsername) if err != nil { - return nil, gtserror.NewErrorBadRequest(err, fmt.Sprintf("user for account with username %s not retrievable", *form.ContactUsername)) - } - // suspended accounts cannot be contact accounts - if !contactAccount.SuspendedAt.IsZero() { - err := fmt.Errorf("selected contact account %s is suspended", contactAccount.Username) - return nil, gtserror.NewErrorBadRequest(err, err.Error()) - } - // unconfirmed or unapproved users cannot be contacts - if contactUser.ConfirmedAt.IsZero() { - err := fmt.Errorf("user of selected contact account %s is not confirmed", contactAccount.Username) - return nil, gtserror.NewErrorBadRequest(err, err.Error()) - } - if !*contactUser.Approved { - err := fmt.Errorf("user of selected contact account %s is not approved", contactAccount.Username) - return nil, gtserror.NewErrorBadRequest(err, err.Error()) - } - // contact account user must be admin or moderator otherwise what's the point of contacting them - if !*contactUser.Admin && !*contactUser.Moderator { - err := fmt.Errorf("user of selected contact account %s is neither admin nor moderator", contactAccount.Username) return nil, gtserror.NewErrorBadRequest(err, err.Error()) } - updatingColumns = append(updatingColumns, "contact_account_id") - instance.ContactAccountID = contactAccount.ID + + columns = append(columns, "contact_account_id") + instance.ContactAccountID = contactAccountID } - // validate & update site contact email if it's set on the form + // Validate & update contact + // email if set on the form. + // + // Empty email unsets contact. if form.ContactEmail != nil { contactEmail := *form.ContactEmail if contactEmail != "" { @@ -214,87 +193,162 @@ func (p *Processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe return nil, gtserror.NewErrorBadRequest(err, err.Error()) } } - updatingColumns = append(updatingColumns, "contact_email") + + columns = append(columns, "contact_email") instance.ContactEmail = contactEmail } - // validate & update site short description if it's set on the form + // Validate & update site short + // description if set on the form. if form.ShortDescription != nil { - if err := validate.SiteShortDescription(*form.ShortDescription); err != nil { + shortDescription := *form.ShortDescription + if err := validate.SiteShortDescription(shortDescription); err != nil { return nil, gtserror.NewErrorBadRequest(err, err.Error()) } - updatingColumns = append(updatingColumns, "short_description") - instance.ShortDescription = text.SanitizeToHTML(*form.ShortDescription) // html is OK in site description, but we should sanitize it + + // Parse description as Markdown, keep + // the raw version for later editing. + instance.ShortDescriptionText = shortDescription + instance.ShortDescription = p.formatter.FromMarkdown(ctx, p.parseMentionFunc, instanceAcc.ID, "", shortDescription).HTML + columns = append(columns, []string{"short_description", "short_description_text"}...) } // validate & update site description if it's set on the form if form.Description != nil { - if err := validate.SiteDescription(*form.Description); err != nil { + description := *form.Description + if err := validate.SiteDescription(description); err != nil { return nil, gtserror.NewErrorBadRequest(err, err.Error()) } - updatingColumns = append(updatingColumns, "description") - instance.Description = text.SanitizeToHTML(*form.Description) // html is OK in site description, but we should sanitize it + + // Parse description as Markdown, keep + // the raw version for later editing. + instance.DescriptionText = description + instance.Description = p.formatter.FromMarkdown(ctx, p.parseMentionFunc, instanceAcc.ID, "", description).HTML + columns = append(columns, []string{"description", "description_text"}...) } - // validate & update site terms if it's set on the form + // Validate & update site + // terms if set on the form. if form.Terms != nil { - if err := validate.SiteTerms(*form.Terms); err != nil { + terms := *form.Terms + if err := validate.SiteTerms(terms); err != nil { return nil, gtserror.NewErrorBadRequest(err, err.Error()) } - updatingColumns = append(updatingColumns, "terms") - instance.Terms = text.SanitizeToHTML(*form.Terms) // html is OK in site terms, but we should sanitize it + + // Parse terms as Markdown, keep + // the raw version for later editing. + instance.TermsText = terms + instance.Terms = p.formatter.FromMarkdown(ctx, p.parseMentionFunc, "", "", terms).HTML + columns = append(columns, []string{"terms", "terms_text"}...) } var updateInstanceAccount bool if form.Avatar != nil && form.Avatar.Size != 0 { - // process instance avatar image + description - avatarInfo, err := p.account.UpdateAvatar(ctx, form.Avatar, form.AvatarDescription, ia.ID) + // Process instance avatar image + description. + avatarInfo, err := p.account.UpdateAvatar(ctx, form.Avatar, form.AvatarDescription, instanceAcc.ID) if err != nil { return nil, gtserror.NewErrorBadRequest(err, "error processing avatar") } - ia.AvatarMediaAttachmentID = avatarInfo.ID - ia.AvatarMediaAttachment = avatarInfo + instanceAcc.AvatarMediaAttachmentID = avatarInfo.ID + instanceAcc.AvatarMediaAttachment = avatarInfo updateInstanceAccount = true - } else if form.AvatarDescription != nil && ia.AvatarMediaAttachment != nil { - // process just the description for the existing avatar - ia.AvatarMediaAttachment.Description = *form.AvatarDescription - if err := p.state.DB.UpdateAttachment(ctx, ia.AvatarMediaAttachment, "description"); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance avatar description: %s", err)) + } else if form.AvatarDescription != nil && instanceAcc.AvatarMediaAttachment != nil { + // Process just the description for the existing avatar. + instanceAcc.AvatarMediaAttachment.Description = *form.AvatarDescription + if err := p.state.DB.UpdateAttachment(ctx, instanceAcc.AvatarMediaAttachment, "description"); err != nil { + err = fmt.Errorf("db error updating instance avatar description: %w", err) + return nil, gtserror.NewErrorInternalError(err) } } if form.Header != nil && form.Header.Size != 0 { // process instance header image - headerInfo, err := p.account.UpdateHeader(ctx, form.Header, nil, ia.ID) + headerInfo, err := p.account.UpdateHeader(ctx, form.Header, nil, instanceAcc.ID) if err != nil { return nil, gtserror.NewErrorBadRequest(err, "error processing header") } - ia.HeaderMediaAttachmentID = headerInfo.ID - ia.HeaderMediaAttachment = headerInfo + instanceAcc.HeaderMediaAttachmentID = headerInfo.ID + instanceAcc.HeaderMediaAttachment = headerInfo updateInstanceAccount = true } if updateInstanceAccount { - // if either avatar or header is updated, we need - // to update the instance account that stores them - if err := p.state.DB.UpdateAccount(ctx, ia); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance account: %s", err)) + // If either avatar or header is updated, we need + // to update the instance account that stores them. + if err := p.state.DB.UpdateAccount(ctx, instanceAcc); err != nil { + err = fmt.Errorf("db error updating instance account: %w", err) + return nil, gtserror.NewErrorInternalError(err, err.Error()) } } - if len(updatingColumns) != 0 { - if err := p.state.DB.UpdateInstance(ctx, instance, updatingColumns...); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance %s: %s", host, err)) + if len(columns) != 0 { + if err := p.state.DB.UpdateInstance(ctx, instance, columns...); err != nil { + err = fmt.Errorf("db error updating instance: %w", err) + return nil, gtserror.NewErrorInternalError(err, err.Error()) } } - ai, err := p.converter.InstanceToAPIV1Instance(ctx, instance) + return p.InstanceGetV1(ctx) +} + +func (p *Processor) getThisInstance(ctx context.Context) (*gtsmodel.Instance, error) { + instance, err := p.state.DB.GetInstance(ctx, config.GetHost()) if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err)) + return nil, err } - return ai, nil + return instance, nil +} + +func (p *Processor) contactAccountIDForUsername(ctx context.Context, username string) (string, error) { + if username == "" { + // Easy: unset + // contact account. + return "", nil + } + + // Make sure local account with the given username exists in the db. + contactAccount, err := p.state.DB.GetAccountByUsernameDomain(ctx, username, "") + if err != nil { + err = fmt.Errorf("db error getting selected contact account with username %s: %w", username, err) + return "", err + } + + // Make sure account corresponds to a user. + contactUser, err := p.state.DB.GetUserByAccountID(ctx, contactAccount.ID) + if err != nil { + err = fmt.Errorf("db error getting user for selected contact account %s: %w", username, err) + return "", err + } + + // Ensure account/user is: + // + // - confirmed and approved + // - not suspended + // - an admin or a moderator + if contactUser.ConfirmedAt.IsZero() { + err := fmt.Errorf("user of selected contact account %s is not confirmed", contactAccount.Username) + return "", err + } + + if !*contactUser.Approved { + err := fmt.Errorf("user of selected contact account %s is not approved", contactAccount.Username) + return "", err + } + + if !contactAccount.SuspendedAt.IsZero() { + err := fmt.Errorf("selected contact account %s is suspended", contactAccount.Username) + return "", err + } + + if !*contactUser.Admin && !*contactUser.Moderator { + err := fmt.Errorf("user of selected contact account %s is neither admin nor moderator", contactAccount.Username) + return "", err + } + + // All good! + return contactAccount.ID, nil } func obfuscate(domain string) string { diff --git a/internal/processing/processor.go b/internal/processing/processor.go index ac930aeb2..6ac7a6bf0 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -21,6 +21,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/cleaner" "github.com/superseriousbusiness/gotosocial/internal/email" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" mm "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/processing/account" @@ -39,6 +40,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/processing/user" "github.com/superseriousbusiness/gotosocial/internal/processing/workers" "github.com/superseriousbusiness/gotosocial/internal/state" + "github.com/superseriousbusiness/gotosocial/internal/text" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/visibility" ) @@ -56,6 +58,13 @@ type Processor struct { state *state.State /* + Required for instance description / terms updating. + */ + + formatter *text.Formatter + parseMentionFunc gtsmodel.ParseMentionFunc + + /* SUB-PROCESSORS */ @@ -147,9 +156,11 @@ func NewProcessor( ) processor := &Processor{ - converter: converter, - oauthServer: oauthServer, - state: state, + converter: converter, + oauthServer: oauthServer, + state: state, + formatter: text.NewFormatter(state.DB), + parseMentionFunc: parseMentionFunc, } // Instantiate sub processors. diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 9d1f7edeb..abae81d04 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -941,20 +941,23 @@ func (c *Converter) InstanceRuleToAdminAPIRule(r *gtsmodel.Rule) *apimodel.Admin // InstanceToAPIV1Instance converts a gts instance into its api equivalent for serving at /api/v1/instance func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.InstanceV1, error) { instance := &apimodel.InstanceV1{ - URI: i.URI, - AccountDomain: config.GetAccountDomain(), - Title: i.Title, - Description: i.Description, - ShortDescription: i.ShortDescription, - Email: i.ContactEmail, - Version: config.GetSoftwareVersion(), - Languages: config.GetInstanceLanguages().TagStrs(), - Registrations: config.GetAccountsRegistrationOpen(), - ApprovalRequired: config.GetAccountsApprovalRequired(), - InvitesEnabled: false, // todo: not supported yet - MaxTootChars: uint(config.GetStatusesMaxChars()), - Rules: c.InstanceRulesToAPIRules(i.Rules), - Terms: i.Terms, + URI: i.URI, + AccountDomain: config.GetAccountDomain(), + Title: i.Title, + Description: i.Description, + DescriptionText: i.DescriptionText, + ShortDescription: i.ShortDescription, + ShortDescriptionText: i.ShortDescriptionText, + Email: i.ContactEmail, + Version: config.GetSoftwareVersion(), + Languages: config.GetInstanceLanguages().TagStrs(), + Registrations: config.GetAccountsRegistrationOpen(), + ApprovalRequired: config.GetAccountsApprovalRequired(), + InvitesEnabled: false, // todo: not supported yet + MaxTootChars: uint(config.GetStatusesMaxChars()), + Rules: c.InstanceRulesToAPIRules(i.Rules), + Terms: i.Terms, + TermsRaw: i.TermsText, } if config.GetInstanceInjectMastodonVersion() { @@ -1050,16 +1053,18 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins // InstanceToAPIV2Instance converts a gts instance into its api equivalent for serving at /api/v2/instance func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.InstanceV2, error) { instance := &apimodel.InstanceV2{ - Domain: i.Domain, - AccountDomain: config.GetAccountDomain(), - Title: i.Title, - Version: config.GetSoftwareVersion(), - SourceURL: instanceSourceURL, - Description: i.Description, - Usage: apimodel.InstanceV2Usage{}, // todo: not implemented - Languages: config.GetInstanceLanguages().TagStrs(), - Rules: c.InstanceRulesToAPIRules(i.Rules), - Terms: i.Terms, + Domain: i.Domain, + AccountDomain: config.GetAccountDomain(), + Title: i.Title, + Version: config.GetSoftwareVersion(), + SourceURL: instanceSourceURL, + Description: i.Description, + DescriptionText: i.DescriptionText, + Usage: apimodel.InstanceV2Usage{}, // todo: not implemented + Languages: config.GetInstanceLanguages().TagStrs(), + Rules: c.InstanceRulesToAPIRules(i.Rules), + Terms: i.Terms, + TermsText: i.TermsText, } if config.GetInstanceInjectMastodonVersion() { diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index d65d47bf1..68b075310 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -841,8 +841,10 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() { "uri": "http://localhost:8080", "account_domain": "localhost:8080", "title": "GoToSocial Testrig Instance", - "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e", + "description": "\u003cp\u003eHere's a fuller description of the GoToSocial testrig instance.\u003c/p\u003e\u003cp\u003eThis instance is for testing purposes only. It doesn't federate at all. Go check out \u003ca href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://github.com/superseriousbusiness/gotosocial/tree/main/testrig\u003c/a\u003e and \u003ca href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\u003c/a\u003e\u003c/p\u003e\u003cp\u003eUsers on this instance:\u003c/p\u003e\u003cul\u003e\u003cli\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003e@\u003cspan\u003eadmin\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e (admin!).\u003c/li\u003e\u003cli\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003e@\u003cspan\u003e1happyturtle\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e (posts about turtles, we don't know why).\u003c/li\u003e\u003cli\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003e@\u003cspan\u003ethe_mighty_zork\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e (who knows).\u003c/li\u003e\u003c/ul\u003e\u003cp\u003eIf you need to edit the models for the testrig, you can do so at \u003ccode\u003einternal/testmodels.go\u003c/code\u003e.\u003c/p\u003e", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e", + "short_description_text": "This is the GoToSocial testrig. It doesn't federate or anything.\n\nWhen the testrig is shut down, all data on it will be deleted.\n\nDon't use this in production!", "email": "admin@example.org", "version": "0.0.0-testrig", "languages": [ @@ -927,7 +929,9 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() { } }, "max_toot_chars": 5000, - "rules": [] + "rules": [], + "terms": "\u003cp\u003eThis is where a list of terms and conditions might go.\u003c/p\u003e\u003cp\u003eFor example:\u003c/p\u003e\u003cp\u003eIf you want to sign up on this instance, you oughta know that we:\u003c/p\u003e\u003col\u003e\u003cli\u003eWill sell your data to whoever offers.\u003c/li\u003e\u003cli\u003eSecure the server with password \u003ccode\u003epassword\u003c/code\u003e wherever possible.\u003c/li\u003e\u003c/ol\u003e", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, string(b)) } @@ -953,7 +957,8 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() { "title": "GoToSocial Testrig Instance", "version": "0.0.0-testrig", "source_url": "https://github.com/superseriousbusiness/gotosocial", - "description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e", + "description": "\u003cp\u003eHere's a fuller description of the GoToSocial testrig instance.\u003c/p\u003e\u003cp\u003eThis instance is for testing purposes only. It doesn't federate at all. Go check out \u003ca href=\"https://github.com/superseriousbusiness/gotosocial/tree/main/testrig\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://github.com/superseriousbusiness/gotosocial/tree/main/testrig\u003c/a\u003e and \u003ca href=\"https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\u003c/a\u003e\u003c/p\u003e\u003cp\u003eUsers on this instance:\u003c/p\u003e\u003cul\u003e\u003cli\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"http://localhost:8080/@admin\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003e@\u003cspan\u003eadmin\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e (admin!).\u003c/li\u003e\u003cli\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"http://localhost:8080/@1happyturtle\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003e@\u003cspan\u003e1happyturtle\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e (posts about turtles, we don't know why).\u003c/li\u003e\u003cli\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003e@\u003cspan\u003ethe_mighty_zork\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e (who knows).\u003c/li\u003e\u003c/ul\u003e\u003cp\u003eIf you need to edit the models for the testrig, you can do so at \u003ccode\u003einternal/testmodels.go\u003c/code\u003e.\u003c/p\u003e", + "description_text": "Here's a fuller description of the GoToSocial testrig instance.\n\nThis instance is for testing purposes only. It doesn't federate at all. Go check out https://github.com/superseriousbusiness/gotosocial/tree/main/testrig and https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#testing\n\nUsers on this instance:\n\n- @admin (admin!).\n- @1happyturtle (posts about turtles, we don't know why).\n- @the_mighty_zork (who knows).\n\nIf you need to edit the models for the testrig, you can do so at `+"`"+`internal/testmodels.go`+"`"+`.", "usage": { "users": { "active_month": 0 @@ -1045,7 +1050,9 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() { } } }, - "rules": [] + "rules": [], + "terms": "\u003cp\u003eThis is where a list of terms and conditions might go.\u003c/p\u003e\u003cp\u003eFor example:\u003c/p\u003e\u003cp\u003eIf you want to sign up on this instance, you oughta know that we:\u003c/p\u003e\u003col\u003e\u003cli\u003eWill sell your data to whoever offers.\u003c/li\u003e\u003cli\u003eSecure the server with password \u003ccode\u003epassword\u003c/code\u003e wherever possible.\u003c/li\u003e\u003c/ol\u003e", + "terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible." }`, string(b)) } |