[feature] Allow users to set custom css for their profiles + threads (#808)

* add custom css account property + db func to fetch

* allow account to get/set custom css

* serve custom css for an account

* go fmt

* use monospace for customcss, add link

* add custom css to account cache

* fix broken field

* add custom css docs to user guide

* add `accounts-allow-custom-css` config flag

* add allow custom css to /api/v1/instance response

* only show/set custom css if allowed to do so

* only set/serve custom account css if enabled

* update swagger docs

* chain promise

* make bool a bit clearer

* use cache for GetAccountCustomCSSByUsername
This commit is contained in:
tobi 2022-09-12 13:14:29 +02:00 committed by GitHub
commit b42469e4e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 458 additions and 39 deletions

60
internal/web/customcss.go Normal file
View file

@ -0,0 +1,60 @@
/*
GoToSocial
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
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 web
import (
"errors"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
func (m *Module) customCSSGETHandler(c *gin.Context) {
if !config.GetAccountsAllowCustomCSS() {
err := errors.New("accounts-allow-custom-css is not enabled on this instance")
api.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
return
}
if _, err := api.NegotiateAccept(c, api.TextCSS); err != nil {
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
// usernames on our instance will always be lowercase
username := strings.ToLower(c.Param(usernameKey))
if username == "" {
err := errors.New("no account username specified")
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
customCSS, errWithCode := m.processor.AccountGetCustomCSSForUsername(c.Request.Context(), username)
if errWithCode != nil {
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.Header("Cache-Control", "no-cache")
c.Data(http.StatusOK, "text/css; charset=utf-8", []byte(customCSS))
}

View file

@ -99,6 +99,15 @@ func (m *Module) profileGETHandler(c *gin.Context) {
return
}
stylesheets := []string{
"/assets/Fork-Awesome/css/fork-awesome.min.css",
"/assets/dist/status.css",
"/assets/dist/profile.css",
}
if config.GetAccountsAllowCustomCSS() {
stylesheets = append(stylesheets, "/@"+account.Username+"/custom.css")
}
c.HTML(http.StatusOK, "profile.tmpl", gin.H{
"instance": instance,
"account": account,
@ -106,11 +115,7 @@ func (m *Module) profileGETHandler(c *gin.Context) {
"statuses": statusResp.Items,
"statuses_next": statusResp.NextLink,
"show_back_to_top": showBackToTop,
"stylesheets": []string{
"/assets/Fork-Awesome/css/fork-awesome.min.css",
"/assets/dist/status.css",
"/assets/dist/profile.css",
},
"stylesheets": stylesheets,
"javascript": []string{
"/assets/dist/frontend.js",
},

View file

@ -104,15 +104,20 @@ func (m *Module) threadGETHandler(c *gin.Context) {
return
}
stylesheets := []string{
"/assets/Fork-Awesome/css/fork-awesome.min.css",
"/assets/dist/status.css",
}
if config.GetAccountsAllowCustomCSS() {
stylesheets = append(stylesheets, "/@"+username+"/custom.css")
}
c.HTML(http.StatusOK, "thread.tmpl", gin.H{
"instance": instance,
"status": status,
"context": context,
"ogMeta": ogBase(instance).withStatus(status),
"stylesheets": []string{
"/assets/Fork-Awesome/css/fork-awesome.min.css",
"/assets/dist/status.css",
},
"instance": instance,
"status": status,
"context": context,
"ogMeta": ogBase(instance).withStatus(status),
"stylesheets": stylesheets,
"javascript": []string{
"/assets/dist/frontend.js",
},

View file

@ -35,6 +35,7 @@ import (
const (
confirmEmailPath = "/" + uris.ConfirmEmailPath
profilePath = "/@:" + usernameKey
customCSSPath = profilePath + "/custom.css"
statusPath = profilePath + "/statuses/:" + statusIDKey
adminPanelPath = "/admin"
userPanelpath = "/user"
@ -91,6 +92,9 @@ func (m *Module) Route(s router.Router) error {
// serve profile pages at /@username
s.AttachHandler(http.MethodGet, profilePath, m.profileGETHandler)
// serve custom css at /@username/custom.css
s.AttachHandler(http.MethodGet, customCSSPath, m.customCSSGETHandler)
// serve statuses
s.AttachHandler(http.MethodGet, statusPath, m.threadGETHandler)