diff --git a/automation/service/locale.gen.go b/automation/service/locale.gen.go
deleted file mode 100644
index 26c97ce89..000000000
--- a/automation/service/locale.gen.go
+++ /dev/null
@@ -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
-}
diff --git a/automation/service/locale.go b/automation/service/locale.go
deleted file mode 100644
index de16afb47..000000000
--- a/automation/service/locale.go
+++ /dev/null
@@ -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)
-}
diff --git a/compose/service/locale.gen.go b/compose/service/locale.gen.go
index 7e70de770..d2428001e 100644
--- a/compose/service/locale.gen.go
+++ b/compose/service/locale.gen.go
@@ -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)
diff --git a/compose/types/locale.gen.go b/compose/types/locale.gen.go
index a061d52ff..aaa6e824e 100644
--- a/compose/types/locale.gen.go
+++ b/compose/types/locale.gen.go
@@ -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),
})
}
diff --git a/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl b/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl
index 03d892764..20366955b 100644
--- a/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl
+++ b/pkg/codegen-v3/assets/templates/gocode/locale/service.go.tpl
@@ -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
}
+
diff --git a/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl b/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl
index 2612b74e4..05be93e59 100644
--- a/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl
+++ b/pkg/codegen-v3/assets/templates/gocode/locale/types.go.tpl
@@ -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 }}),
})
}
diff --git a/pkg/locale/input.go b/pkg/locale/input.go
new file mode 100644
index 000000000..118eb8242
--- /dev/null
+++ b/pkg/locale/input.go
@@ -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))
+}
diff --git a/pkg/locale/input_test.go b/pkg/locale/input_test.go
new file mode 100644
index 000000000..47238ad5d
--- /dev/null
+++ b/pkg/locale/input_test.go
@@ -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", "čšž", "čšž"},
+ {"broken html 1", "čšžčšžčšž"},
+ {"broken html 3", "` 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 `<
` not just `
`
- 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
-}