Fixes res. translation enc/dec (accents, html)
This commit is contained in:
154
automation/service/locale.gen.go
generated
154
automation/service/locale.gen.go
generated
@@ -1,154 +0,0 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
// Definitions file that controls how this file is generated:
|
||||
// - automation.workflow.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
intAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
resourceTranslationsManager struct {
|
||||
actionlog actionlog.Recorder
|
||||
locale locale.Resource
|
||||
store store.Storer
|
||||
ac localeAccessController
|
||||
}
|
||||
|
||||
localeAccessController interface {
|
||||
CanManageResourceTranslations(context.Context) bool
|
||||
}
|
||||
|
||||
ResourceTranslationsManagerService interface {
|
||||
Workflow(ctx context.Context, ID uint64) (locale.ResourceTranslationSet, error)
|
||||
|
||||
Upsert(context.Context, locale.ResourceTranslationSet) error
|
||||
Locale() locale.Resource
|
||||
}
|
||||
)
|
||||
|
||||
var ErrNotAllowedToManageResourceTranslations = errors.Unauthorized("not allowed to manage resource translations")
|
||||
|
||||
func ResourceTranslationsManager(ls locale.Resource) *resourceTranslationsManager {
|
||||
return &resourceTranslationsManager{
|
||||
actionlog: DefaultActionlog,
|
||||
store: DefaultStore,
|
||||
ac: DefaultAccessControl,
|
||||
locale: ls,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.ResourceTranslationSet) (err error) {
|
||||
// User is allowed to manage resource translations when:
|
||||
// - managed resource translation strings are all for default language
|
||||
// or
|
||||
// - user is allowed to manage resource translations
|
||||
if rr.ContainsForeign(svc.Locale().Default().Tag) {
|
||||
if !svc.ac.CanManageResourceTranslations(ctx) {
|
||||
return ErrNotAllowedToManageResourceTranslations
|
||||
}
|
||||
}
|
||||
|
||||
// @todo validation
|
||||
|
||||
me := intAuth.GetIdentityFromContext(ctx)
|
||||
|
||||
// - group by resource
|
||||
localeByRes := make(map[string]locale.ResourceTranslationSet)
|
||||
for _, r := range rr {
|
||||
localeByRes[r.Resource] = append(localeByRes[r.Resource], r)
|
||||
}
|
||||
|
||||
// - for each resource, fetch the current state
|
||||
sysLocale := make(systemTypes.ResourceTranslationSet, 0, len(rr))
|
||||
for res, rr := range localeByRes {
|
||||
current, _, err := store.SearchResourceTranslations(ctx, svc.store, systemTypes.ResourceTranslationFilter{
|
||||
Resource: res,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get deltas and prepare upsert accordingly
|
||||
aux := current.New(rr)
|
||||
aux.Walk(func(cc *systemTypes.ResourceTranslation) error {
|
||||
cc.ID = nextID()
|
||||
cc.CreatedAt = *now()
|
||||
cc.CreatedBy = me.Identity()
|
||||
|
||||
return nil
|
||||
})
|
||||
sysLocale = append(sysLocale, aux...)
|
||||
|
||||
aux = current.Old(rr)
|
||||
aux.Walk(func(cc *systemTypes.ResourceTranslation) error {
|
||||
cc.UpdatedAt = now()
|
||||
cc.UpdatedBy = me.Identity()
|
||||
return nil
|
||||
})
|
||||
sysLocale = append(sysLocale, aux...)
|
||||
}
|
||||
|
||||
err = store.UpsertResourceTranslation(ctx, svc.store, sysLocale...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reload ALL resource translations
|
||||
// @todo we could probably do this more selectively and refresh only updated resources?
|
||||
_ = locale.Global().ReloadResourceTranslations(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc resourceTranslationsManager) Locale() locale.Resource {
|
||||
return svc.locale
|
||||
}
|
||||
|
||||
func (svc resourceTranslationsManager) Workflow(ctx context.Context, ID uint64) (locale.ResourceTranslationSet, error) {
|
||||
var (
|
||||
err error
|
||||
out locale.ResourceTranslationSet
|
||||
res *types.Workflow
|
||||
k types.LocaleKey
|
||||
)
|
||||
|
||||
res, err = svc.loadWorkflow(ctx, svc.store, ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, tag := range svc.locale.Tags() {
|
||||
k = types.LocaleKeyWorkflowName
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: res.ResourceTranslation(),
|
||||
Lang: tag.String(),
|
||||
Key: k.Path,
|
||||
Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path),
|
||||
})
|
||||
|
||||
k = types.LocaleKeyWorkflowDescription
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: res.ResourceTranslation(),
|
||||
Lang: tag.String(),
|
||||
Key: k.Path,
|
||||
Msg: svc.locale.TResourceFor(tag, res.ResourceTranslation(), k.Path),
|
||||
})
|
||||
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
)
|
||||
|
||||
func (svc resourceTranslationsManager) loadWorkflow(ctx context.Context, s store.Storer, ID uint64) (*types.Workflow, error) {
|
||||
return loadWorkflow(ctx, s, ID)
|
||||
}
|
||||
4
compose/service/locale.gen.go
generated
4
compose/service/locale.gen.go
generated
@@ -72,6 +72,10 @@ func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.Res
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range rr {
|
||||
r.Msg = locale.SanitizeMessage(r.Msg)
|
||||
}
|
||||
|
||||
// @todo validation
|
||||
|
||||
me := intAuth.GetIdentityFromContext(ctx)
|
||||
|
||||
22
compose/types/locale.gen.go
generated
22
compose/types/locale.gen.go
generated
@@ -120,7 +120,7 @@ var (
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for ModuleField by calling ModuleFieldResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in the compose:module-field/... format
|
||||
// Locale resource is in "compose:module-field/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r ModuleField) ResourceTranslation() string {
|
||||
@@ -162,7 +162,7 @@ func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleFieldLabel.Path,
|
||||
Msg: r.Label,
|
||||
Msg: locale.SanitizeMessage(r.Label),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ func (r *ModuleField) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Module by calling ModuleResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in the compose:module/... format
|
||||
// Locale resource is in "compose:module/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Module) ResourceTranslation() string {
|
||||
@@ -216,7 +216,7 @@ func (r *Module) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyModuleName.Path,
|
||||
Msg: r.Name,
|
||||
Msg: locale.SanitizeMessage(r.Name),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ func (r *Module) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Namespace by calling NamespaceResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in the compose:namespace/... format
|
||||
// Locale resource is in "compose:namespace/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Namespace) ResourceTranslation() string {
|
||||
@@ -270,21 +270,21 @@ func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyNamespaceName.Path,
|
||||
Msg: r.Name,
|
||||
Msg: locale.SanitizeMessage(r.Name),
|
||||
})
|
||||
}
|
||||
if r.Meta.Subtitle != "" {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyNamespaceSubtitle.Path,
|
||||
Msg: r.Meta.Subtitle,
|
||||
Msg: locale.SanitizeMessage(r.Meta.Subtitle),
|
||||
})
|
||||
}
|
||||
if r.Meta.Description != "" {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyNamespaceDescription.Path,
|
||||
Msg: r.Meta.Description,
|
||||
Msg: locale.SanitizeMessage(r.Meta.Description),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ func (r *Namespace) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for Page by calling PageResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in the compose:page/... format
|
||||
// Locale resource is in "compose:page/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Page) ResourceTranslation() string {
|
||||
@@ -335,14 +335,14 @@ func (r *Page) EncodeTranslations() (out locale.ResourceTranslationSet) {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyPageTitle.Path,
|
||||
Msg: r.Title,
|
||||
Msg: locale.SanitizeMessage(r.Title),
|
||||
})
|
||||
}
|
||||
if r.Description != "" {
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKeyPageDescription.Path,
|
||||
Msg: r.Description,
|
||||
Msg: locale.SanitizeMessage(r.Description),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ func (svc resourceTranslationsManager) Upsert(ctx context.Context, rr locale.Res
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range rr {
|
||||
r.Msg = locale.SanitizeMessage(r.Msg)
|
||||
}
|
||||
|
||||
// @todo validation
|
||||
|
||||
me := intAuth.GetIdentityFromContext(ctx)
|
||||
@@ -209,3 +213,4 @@ func updateTranslations(ctx context.Context, ac localeAccessControl, lsvc Resour
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ var (
|
||||
|
||||
// ResourceTranslation returns string representation of Locale resource for {{ .Resource }} by calling {{ .Resource }}ResourceTranslation fn
|
||||
//
|
||||
// Locale resource is in the {{ .Locale.ResourceType }}/... format
|
||||
// Locale resource is in "{{ .Locale.ResourceType }}/..." format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r {{ .Resource }}) ResourceTranslation() string {
|
||||
@@ -112,7 +112,7 @@ func (r *{{ .Resource }}) EncodeTranslations() (out locale.ResourceTranslationSe
|
||||
out = append(out, &locale.ResourceTranslation{
|
||||
Resource: r.ResourceTranslation(),
|
||||
Key: LocaleKey{{ $Resource }}{{coalesce (export .Name) (export .Path) }}.Path,
|
||||
Msg: r.{{ .Field }},
|
||||
Msg: locale.SanitizeMessage(r.{{ .Field }}),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
15
pkg/locale/input.go
Normal file
15
pkg/locale/input.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package locale
|
||||
|
||||
import (
|
||||
"html"
|
||||
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
)
|
||||
|
||||
var (
|
||||
stripHtml = bluemonday.StripTagsPolicy().Sanitize
|
||||
)
|
||||
|
||||
func SanitizeMessage(in string) string {
|
||||
return html.UnescapeString(stripHtml(in))
|
||||
}
|
||||
27
pkg/locale/input_test.go
Normal file
27
pkg/locale/input_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package locale
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_SanitizeMessage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"simple", "abc", "abc"},
|
||||
{"accents", "čšž", "čšž"},
|
||||
{"html", "<b>čšž</b>", "čšž"},
|
||||
{"broken html 1", "<b>čšž</b", "čšž"},
|
||||
{"broken html 2", "b>čšž</b", "b>čšž"},
|
||||
{"broken html 3", "<b fff=\"čšž</b", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.out, SanitizeMessage(tt.in))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,8 @@ package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"html"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
@@ -88,7 +86,7 @@ outer:
|
||||
Lang: Lang{Tag: language.Make(b.Lang)},
|
||||
Resource: b.Resource,
|
||||
K: b.Key,
|
||||
Message: sanitizeHtml(b.Msg),
|
||||
Message: b.Msg,
|
||||
})
|
||||
}
|
||||
return
|
||||
@@ -99,7 +97,7 @@ func (set ResourceTranslationSet) Old(bb locale.ResourceTranslationSet) (out Res
|
||||
for _, a := range set {
|
||||
// It's not new
|
||||
if a.Compare(b) {
|
||||
a.Message = sanitizeHtml(b.Msg)
|
||||
a.Message = b.Msg
|
||||
out = append(out, a)
|
||||
break
|
||||
}
|
||||
@@ -114,59 +112,9 @@ func FromLocale(ll locale.ResourceTranslationSet) (out ResourceTranslationSet) {
|
||||
Lang: Lang{Tag: language.Make(l.Lang)},
|
||||
Resource: l.Resource,
|
||||
K: l.Key,
|
||||
Message: sanitizeHtml(l.Msg),
|
||||
Message: l.Msg,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// sanitizeHtml Aggressively strips HTML tags from a string.
|
||||
// It will only keep anything between `>` and `<`.
|
||||
func sanitizeHtml(s string) string {
|
||||
const (
|
||||
htmlTagStart = 60 // Unicode `<`
|
||||
htmlTagEnd = 62 // Unicode `>`
|
||||
)
|
||||
|
||||
// Unescape string with any html tags
|
||||
s = html.UnescapeString(s)
|
||||
|
||||
// Set up a string builder and allocate enough memory for the new string.
|
||||
var builder strings.Builder
|
||||
builder.Grow(len(s) + utf8.UTFMax)
|
||||
|
||||
in := false // True if we are inside an HTML tag.
|
||||
start := 0 // The index of the previous start tag character `<`
|
||||
end := 0 // The index of the previous end tag character `>`
|
||||
|
||||
for i, c := range s {
|
||||
// If this is the last character, and we are not in an HTML tag, save it.
|
||||
if (i+1) == len(s) && end >= start {
|
||||
builder.WriteString(s[end:])
|
||||
}
|
||||
|
||||
// Keep going if the character is not `<` or `>`
|
||||
if c != htmlTagStart && c != htmlTagEnd {
|
||||
continue
|
||||
}
|
||||
|
||||
if c == htmlTagStart {
|
||||
// Only update the start if we are not in a tag.
|
||||
// This make sure we strip `<<br>` not just `<br>`
|
||||
if !in {
|
||||
start = i
|
||||
}
|
||||
in = true
|
||||
|
||||
// Write the valid string between the close and start of the two tags.
|
||||
builder.WriteString(s[end:start])
|
||||
continue
|
||||
}
|
||||
// else c == htmlTagEnd
|
||||
in = false
|
||||
end = i + 1
|
||||
}
|
||||
s = builder.String()
|
||||
return s
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user