mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-21 19:26:40 +00:00
Compare commits
5 commits
5f2155e2e6
...
c7d7f29417
Author | SHA1 | Date | |
---|---|---|---|
c7d7f29417 | |||
49eb8f602e | |||
cad76b8a25 | |||
3b037d165c | |||
c8fb4c17f1 |
|
@ -167,3 +167,11 @@ Links to the set contact account and/or email address will appear on the footer
|
|||
The selected **contact user** must be an active (not suspended) admin and/or moderator on the instance.
|
||||
|
||||
If you're on a single-user instance and you give admin privileges to your main account, you can just fill in your own username here; you don't need to make a separate admin account just for this.
|
||||
|
||||
### Instance Custom CSS
|
||||
|
||||
custom CSS allows you to further customize the way your instance looks when visited through a browser.
|
||||
|
||||
This custom CSS will be applied to all pages of your instance. Users themes and CSS still take precedence over this customization.
|
||||
|
||||
See the [Custom CSS](./custom_css.md) page for some tips on writing custom CSS for your instance.
|
||||
|
|
|
@ -1564,6 +1564,10 @@ definitions:
|
|||
$ref: '#/definitions/instanceV1Configuration'
|
||||
contact_account:
|
||||
$ref: '#/definitions/account'
|
||||
custom_css:
|
||||
description: Custom CSS for the instance.
|
||||
type: string
|
||||
x-go-name: CustomCSS
|
||||
debug:
|
||||
description: Whether or not instance is running in DEBUG mode. Omitted if false.
|
||||
type: boolean
|
||||
|
@ -1744,6 +1748,10 @@ definitions:
|
|||
$ref: '#/definitions/instanceV2Configuration'
|
||||
contact:
|
||||
$ref: '#/definitions/instanceV2Contact'
|
||||
custom_css:
|
||||
description: Instance Custom Css
|
||||
type: string
|
||||
x-go-name: CustomCSS
|
||||
debug:
|
||||
description: Whether or not instance is running in DEBUG mode. Omitted if false.
|
||||
type: boolean
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 229 KiB |
|
@ -78,11 +78,13 @@ Some examples:
|
|||
|
||||
#### Visibility Level of Posts to Show on Your Profile
|
||||
|
||||
Using this dropdown, you can choose what visibility level(s) of posts should be shown on the public web view of your profile, and served in your RSS feed (if you have enabled RSS).
|
||||
Using this dropdown, you can choose what visibility level(s) of posts should be shown on the public web views of your profile, of your statuses, and in your RSS feed (if you have enabled RSS).
|
||||
|
||||
**By default, GoToSocial shows only Public visibility posts on the web view of your profile, not Unlisted.** You can adjust this setting to also show Unlisted visibility posts on your profile, which is similar to the default for other ActivityPub softwares like Mastodon etc.
|
||||
**By default, GoToSocial shows only Public visibility posts on its web views, not Unlisted.** You can adjust this setting to also show Unlisted visibility posts, which is similar to the default for other ActivityPub softwares like Mastodon etc.
|
||||
|
||||
You can also choose to show no posts at all on the web view of your profile. This allows you to write posts without having to worry about scrapers, rubberneckers, and other nosy parkers visiting your web profile and looking at your posts.
|
||||
You can also choose to show no posts at all on GoToSocial's web views. This allows you to write posts without having to worry about scrapers, rubberneckers, and other nosy parkers visiting your web profile and looking at your posts.
|
||||
|
||||
This setting only applies to the visibility of your own posts. Other user's Unlisted posts are never shown.
|
||||
|
||||
This setting does not affect visibility of your posts over the ActivityPub protocol, so even if you choose to show no posts on your public web profile, others will be able to see your posts in their client if they follow you, and/or have your posts boosted onto their timeline, use a link to search a post of yours, etc.
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@ func validateInstanceUpdate(form *apimodel.InstanceSettingsUpdateRequest) error
|
|||
form.ContactEmail == nil &&
|
||||
form.ShortDescription == nil &&
|
||||
form.Description == nil &&
|
||||
form.CustomCSS == nil &&
|
||||
form.Terms == nil &&
|
||||
form.Avatar == nil &&
|
||||
form.AvatarDescription == nil &&
|
||||
|
|
|
@ -33,6 +33,8 @@ type InstanceSettingsUpdateRequest struct {
|
|||
ShortDescription *string `form:"short_description" json:"short_description" xml:"short_description"`
|
||||
// Longer description of the instance, max 5,000 chars. HTML formatting accepted.
|
||||
Description *string `form:"description" json:"description" xml:"description"`
|
||||
// Custom CSS for the instance.
|
||||
CustomCSS *string `form:"custom_css" json:"custom_css,omitempty" xml:"custom_css"`
|
||||
// Terms and conditions of the instance, max 5,000 chars. HTML formatting accepted.
|
||||
Terms *string `form:"terms" json:"terms" xml:"terms"`
|
||||
// Image to use as the instance thumbnail.
|
||||
|
|
|
@ -38,6 +38,8 @@ type InstanceV1 struct {
|
|||
//
|
||||
// This should be displayed on the 'about' page for an instance.
|
||||
Description string `json:"description"`
|
||||
// Custom CSS for the instance.
|
||||
CustomCSS string `json:"custom_css,omitempty"`
|
||||
// Raw (unparsed) version of description.
|
||||
DescriptionText string `json:"description_text,omitempty"`
|
||||
// A shorter description of the instance.
|
||||
|
|
|
@ -53,6 +53,8 @@ type InstanceV2 struct {
|
|||
Description string `json:"description"`
|
||||
// Raw (unparsed) version of description.
|
||||
DescriptionText string `json:"description_text,omitempty"`
|
||||
// Instance Custom Css
|
||||
CustomCSS string `json:"custom_css,omitempty"`
|
||||
// Basic anonymous usage data for this instance.
|
||||
Usage InstanceV2Usage `json:"usage"`
|
||||
// An image used to represent this instance.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// 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 {
|
||||
_, err := db.ExecContext(ctx, "ALTER TABLE ? ADD COLUMN ? TEXT", bun.Ident("instances"), bun.Ident("custom_css"))
|
||||
if err != nil && !(strings.Contains(err.Error(), "already exists") || strings.Contains(err.Error(), "duplicate column name") || strings.Contains(err.Error(), "SQLSTATE 42701")) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
down := func(ctx context.Context, db *bun.DB) error {
|
||||
_, err := db.ExecContext(ctx, "ALTER TABLE ? DROP COLUMN ?", bun.Ident("instances"), bun.Ident("custom_css"))
|
||||
return err
|
||||
}
|
||||
|
||||
if err := Migrations.Register(up, down); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ type Instance struct {
|
|||
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).
|
||||
CustomCSS string `bun:",nullzero"` // Custom CSS for the instance.
|
||||
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
|
||||
|
|
|
@ -227,6 +227,17 @@ func (p *Processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe
|
|||
columns = append(columns, []string{"description", "description_text"}...)
|
||||
}
|
||||
|
||||
// validate & update site custom css if it's set on the form
|
||||
if form.CustomCSS != nil {
|
||||
customCSS := *form.CustomCSS
|
||||
if err := validate.InstanceCustomCSS(customCSS); err != nil {
|
||||
return nil, gtserror.NewErrorBadRequest(err, err.Error())
|
||||
}
|
||||
|
||||
instance.CustomCSS = text.SanitizeToPlaintext(customCSS)
|
||||
columns = append(columns, []string{"custom_css"}...)
|
||||
}
|
||||
|
||||
// Validate & update site
|
||||
// terms if set on the form.
|
||||
if form.Terms != nil {
|
||||
|
|
|
@ -1535,6 +1535,7 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
|
|||
Title: i.Title,
|
||||
Description: i.Description,
|
||||
DescriptionText: i.DescriptionText,
|
||||
CustomCSS: i.CustomCSS,
|
||||
ShortDescription: i.ShortDescription,
|
||||
ShortDescriptionText: i.ShortDescriptionText,
|
||||
Email: i.ContactEmail,
|
||||
|
@ -1662,6 +1663,7 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins
|
|||
SourceURL: instanceSourceURL,
|
||||
Description: i.Description,
|
||||
DescriptionText: i.DescriptionText,
|
||||
CustomCSS: i.CustomCSS,
|
||||
Usage: apimodel.InstanceV2Usage{}, // todo: not implemented
|
||||
Languages: config.GetInstanceLanguages().TagStrs(),
|
||||
Rules: c.InstanceRulesToAPIRules(i.Rules),
|
||||
|
|
|
@ -189,6 +189,16 @@ func CustomCSS(customCSS string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func InstanceCustomCSS(customCSS string) error {
|
||||
|
||||
maximumCustomCSSLength := config.GetAccountsCustomCSSLength()
|
||||
if length := len([]rune(customCSS)); length > maximumCustomCSSLength {
|
||||
return fmt.Errorf("custom_css must be less than %d characters, but submitted custom_css was %d characters", maximumCustomCSSLength, length)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EmojiShortcode just runs the given shortcode through the regular expression
|
||||
// for emoji shortcodes, to figure out whether it's a valid shortcode, ie., 2-30 characters,
|
||||
// a-zA-Z, numbers, and underscores.
|
||||
|
|
|
@ -54,7 +54,7 @@ func (m *Module) aboutGETHandler(c *gin.Context) {
|
|||
Template: "about.tmpl",
|
||||
Instance: instance,
|
||||
OGMeta: apiutil.OGBase(instance),
|
||||
Stylesheets: []string{cssAbout},
|
||||
Stylesheets: []string{cssAbout, instanceCustomCSSPath},
|
||||
Extra: map[string]any{
|
||||
"showStrap": true,
|
||||
"blocklistExposed": config.GetInstanceExposeSuspendedWeb(),
|
||||
|
|
|
@ -127,8 +127,9 @@ func (m *Module) confirmEmailPOSTHandler(c *gin.Context) {
|
|||
// Serve page informing user that their
|
||||
// email address is now confirmed.
|
||||
page := apiutil.WebPage{
|
||||
Template: "confirmed_email.tmpl",
|
||||
Instance: instance,
|
||||
Template: "confirmed_email.tmpl",
|
||||
Instance: instance,
|
||||
Stylesheets: []string{instanceCustomCSSPath},
|
||||
Extra: map[string]any{
|
||||
"email": user.Email,
|
||||
"username": user.Account.Username,
|
||||
|
|
|
@ -55,3 +55,22 @@ func (m *Module) customCSSGETHandler(c *gin.Context) {
|
|||
c.Header(cacheControlHeader, cacheControlNoCache)
|
||||
c.Data(http.StatusOK, textCSSUTF8, []byte(customCSS))
|
||||
}
|
||||
|
||||
func (m *Module) instanceCustomCSSGETHandler(c *gin.Context) {
|
||||
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.TextCSS); err != nil {
|
||||
apiutil.WebErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
instanceV1, errWithCode := m.processor.InstanceGetV1(c.Request.Context())
|
||||
if errWithCode != nil {
|
||||
apiutil.WebErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
instanceCustomCSS := instanceV1.CustomCSS
|
||||
|
||||
c.Header(cacheControlHeader, cacheControlNoCache)
|
||||
c.Data(http.StatusOK, textCSSUTF8, []byte(instanceCustomCSS))
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ func (m *Module) domainBlockListGETHandler(c *gin.Context) {
|
|||
Template: "domain-blocklist.tmpl",
|
||||
Instance: instance,
|
||||
OGMeta: apiutil.OGBase(instance),
|
||||
Stylesheets: []string{cssFA},
|
||||
Stylesheets: []string{cssFA, instanceCustomCSSPath},
|
||||
Javascript: []string{jsFrontend},
|
||||
Extra: map[string]any{"blocklist": domainBlocks},
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func (m *Module) indexHandler(c *gin.Context) {
|
|||
Template: "index.tmpl",
|
||||
Instance: instance,
|
||||
OGMeta: apiutil.OGBase(instance),
|
||||
Stylesheets: []string{cssAbout, cssIndex},
|
||||
Stylesheets: []string{cssAbout, cssIndex, instanceCustomCSSPath},
|
||||
Extra: map[string]any{"showStrap": true},
|
||||
}
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ func (m *Module) profileGETHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
// Prepare stylesheets for profile.
|
||||
stylesheets := make([]string, 0, 6)
|
||||
stylesheets := make([]string, 0, 7)
|
||||
|
||||
// Basic profile stylesheets.
|
||||
stylesheets = append(
|
||||
|
@ -142,6 +142,7 @@ func (m *Module) profileGETHandler(c *gin.Context) {
|
|||
cssStatus,
|
||||
cssThread,
|
||||
cssProfile,
|
||||
instanceCustomCSSPath,
|
||||
}...,
|
||||
)
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ func (m *Module) SettingsPanelHandler(c *gin.Context) {
|
|||
cssProfile, // Used for rendering stub/fake profiles.
|
||||
cssStatus, // Used for rendering stub/fake statuses.
|
||||
cssSettings,
|
||||
instanceCustomCSSPath,
|
||||
},
|
||||
Javascript: []string{jsSettings},
|
||||
}
|
||||
|
|
|
@ -126,9 +126,10 @@ func (m *Module) signupPOSTHandler(c *gin.Context) {
|
|||
// Serve a page informing the
|
||||
// user that they've signed up.
|
||||
page := apiutil.WebPage{
|
||||
Template: "signed-up.tmpl",
|
||||
Instance: instance,
|
||||
OGMeta: apiutil.OGBase(instance),
|
||||
Template: "signed-up.tmpl",
|
||||
Instance: instance,
|
||||
Stylesheets: []string{instanceCustomCSSPath},
|
||||
OGMeta: apiutil.OGBase(instance),
|
||||
Extra: map[string]any{
|
||||
"email": user.UnconfirmedEmail,
|
||||
"username": user.Account.Username,
|
||||
|
|
|
@ -59,7 +59,7 @@ func (m *Module) tagGETHandler(c *gin.Context) {
|
|||
Template: "tag.tmpl",
|
||||
Instance: instance,
|
||||
OGMeta: apiutil.OGBase(instance),
|
||||
Stylesheets: []string{cssFA, cssThread, cssTag},
|
||||
Stylesheets: []string{cssFA, cssThread, cssTag, instanceCustomCSSPath},
|
||||
Extra: map[string]any{"tagName": tagName},
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ func (m *Module) threadGETHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
// Prepare stylesheets for thread.
|
||||
stylesheets := make([]string, 0, 5)
|
||||
stylesheets := make([]string, 0, 6)
|
||||
|
||||
// Basic thread stylesheets.
|
||||
stylesheets = append(
|
||||
|
@ -131,6 +131,7 @@ func (m *Module) threadGETHandler(c *gin.Context) {
|
|||
if theme := targetAccount.Theme; theme != "" {
|
||||
stylesheets = append(
|
||||
stylesheets,
|
||||
instanceCustomCSSPath,
|
||||
themesPathPrefix+"/"+theme,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -36,20 +36,21 @@
|
|||
)
|
||||
|
||||
const (
|
||||
confirmEmailPath = "/" + uris.ConfirmEmailPath
|
||||
profileGroupPath = "/@:username"
|
||||
statusPath = "/statuses/:" + apiutil.WebStatusIDKey // leave out the '/@:username' prefix as this will be served within the profile group
|
||||
tagsPath = "/tags/:" + apiutil.TagNameKey
|
||||
customCSSPath = profileGroupPath + "/custom.css"
|
||||
rssFeedPath = profileGroupPath + "/feed.rss"
|
||||
assetsPathPrefix = "/assets"
|
||||
distPathPrefix = assetsPathPrefix + "/dist"
|
||||
themesPathPrefix = assetsPathPrefix + "/themes"
|
||||
settingsPathPrefix = "/settings"
|
||||
settingsPanelGlob = settingsPathPrefix + "/*panel"
|
||||
userPanelPath = settingsPathPrefix + "/user"
|
||||
adminPanelPath = settingsPathPrefix + "/admin"
|
||||
signupPath = "/signup"
|
||||
confirmEmailPath = "/" + uris.ConfirmEmailPath
|
||||
profileGroupPath = "/@:username"
|
||||
statusPath = "/statuses/:" + apiutil.WebStatusIDKey // leave out the '/@:username' prefix as this will be served within the profile group
|
||||
tagsPath = "/tags/:" + apiutil.TagNameKey
|
||||
customCSSPath = profileGroupPath + "/custom.css"
|
||||
instanceCustomCSSPath = "/custom.css"
|
||||
rssFeedPath = profileGroupPath + "/feed.rss"
|
||||
assetsPathPrefix = "/assets"
|
||||
distPathPrefix = assetsPathPrefix + "/dist"
|
||||
themesPathPrefix = assetsPathPrefix + "/themes"
|
||||
settingsPathPrefix = "/settings"
|
||||
settingsPanelGlob = settingsPathPrefix + "/*panel"
|
||||
userPanelPath = settingsPathPrefix + "/user"
|
||||
adminPanelPath = settingsPathPrefix + "/admin"
|
||||
signupPath = "/signup"
|
||||
|
||||
cacheControlHeader = "Cache-Control" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
||||
cacheControlNoCache = "no-cache" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives
|
||||
|
@ -114,6 +115,7 @@ func (m *Module) Route(r *router.Router, mi ...gin.HandlerFunc) {
|
|||
r.AttachHandler(http.MethodGet, settingsPathPrefix, m.SettingsPanelHandler)
|
||||
r.AttachHandler(http.MethodGet, settingsPanelGlob, m.SettingsPanelHandler)
|
||||
r.AttachHandler(http.MethodGet, customCSSPath, m.customCSSGETHandler)
|
||||
r.AttachHandler(http.MethodGet, instanceCustomCSSPath, m.instanceCustomCSSGETHandler)
|
||||
r.AttachHandler(http.MethodGet, rssFeedPath, m.rssFeedGETHandler)
|
||||
r.AttachHandler(http.MethodGet, confirmEmailPath, m.confirmEmailGETHandler)
|
||||
r.AttachHandler(http.MethodPost, confirmEmailPath, m.confirmEmailPOSTHandler)
|
||||
|
|
|
@ -25,6 +25,7 @@ export interface InstanceV1 {
|
|||
description_text?: string;
|
||||
short_description: string;
|
||||
short_description_text?: string;
|
||||
custom_css: string;
|
||||
email: string;
|
||||
version: string;
|
||||
debug?: boolean;
|
||||
|
|
|
@ -66,6 +66,10 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
|
|||
valueSelector: (s: InstanceV1) => s.description_text,
|
||||
validator: (val: string) => val.length <= descLimit ? "" : `Instance description is ${val.length} characters; must be ${descLimit} characters or less`
|
||||
}),
|
||||
customCSS: useTextInput("custom_css", {
|
||||
source: instance,
|
||||
valueSelector: (s: InstanceV1) => s.custom_css
|
||||
}),
|
||||
terms: useTextInput("terms", {
|
||||
source: instance,
|
||||
// Select "raw" text version of parsed field for editing.
|
||||
|
@ -191,6 +195,15 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
|
|||
type="email"
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
field={form.customCSS}
|
||||
label={"Custom CSS"}
|
||||
className="monospace"
|
||||
rows={8}
|
||||
autoCapitalize="none"
|
||||
spellCheck="false"
|
||||
/>
|
||||
|
||||
<MutationButton label="Save" result={result} disabled={false} />
|
||||
</form>
|
||||
);
|
||||
|
|
|
@ -3213,9 +3213,9 @@ electron-to-chromium@^1.4.668:
|
|||
integrity sha512-jeWaIta2rIG2FzHaYIhSuVWqC6KJYo7oSBX4Jv7g+aVujKztfvdpf+n6MGwZdC5hQXbax4nntykLH2juIQrfPg==
|
||||
|
||||
elliptic@^6.5.3, elliptic@^6.5.4:
|
||||
version "6.5.7"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
|
||||
integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210"
|
||||
integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==
|
||||
dependencies:
|
||||
bn.js "^4.11.9"
|
||||
brorand "^1.1.0"
|
||||
|
|
|
@ -172,7 +172,7 @@ Polls can have up to
|
|||
<p>
|
||||
ActivityPub instances federate with other instances by exchanging data with them over the network.
|
||||
Exchanged data includes things like accounts, statuses, likes, boosts, and media attachments.
|
||||
This exchange of data can prevented for instances on specific domains via a domain block created
|
||||
This exchange of data can be prevented for instances on specific domains via a domain block created
|
||||
by an instance admin. When an instance is domain blocked by another instance:
|
||||
</p>
|
||||
<ul>
|
||||
|
|
Loading…
Reference in a new issue