Merge pull request #9722 from 2403905/issue-9700

Added a generic way to translate the necessary fields in composite entities.
This commit is contained in:
Jörn Friedrich Dreyer
2024-08-09 13:54:02 +02:00
committed by GitHub
26 changed files with 804 additions and 37 deletions

View File

@@ -0,0 +1,8 @@
Enhancement: Added generic way to translate composite entities
Added a generic way to translate the necessary fields in composite entities.
The function takes the entity, translation function and fields to translate that are described by the TranslateField function.
The function supports nested structs and slices of structs.
https://github.com/owncloud/ocis/pull/9722
https://github.com/owncloud/ocis/issues/9700

View File

@@ -6,6 +6,7 @@ import (
"errors"
"io/fs"
"os"
"reflect"
"github.com/leonelquinteros/gotext"
"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
@@ -14,8 +15,13 @@ import (
micrometadata "go-micro.dev/v4/metadata"
)
// HeaderAcceptLanguage is the header key for the accept-language header
var HeaderAcceptLanguage = "Accept-Language"
var (
// HeaderAcceptLanguage is the header key for the accept-language header
HeaderAcceptLanguage = "Accept-Language"
// ErrUnsupportedType is returned when the type is not supported
ErrUnsupportedType = errors.New("unsupported type")
)
// Template marks a string as translatable
func Template(s string) string { return s }
@@ -63,6 +69,61 @@ func (t Translator) Locale(locale string) *gotext.Locale {
return l
}
// TranslateEntity function provides the generic way to translate a struct, array or slice.
// Support for maps is also provided, but non-pointer values will not work.
// The function also takes the entity with fields to translate.
// The function supports nested structs and slices of structs.
/*
tr := NewTranslator("en", _domain, _fsys)
// a slice of translatables can be passed directly
val := []string{"description", "display name"}
err := tr.TranslateEntity(tr, s, val)
// string maps work the same way
val := map[string]string{
"entryOne": "description",
"entryTwo": "display name",
}
err := TranslateEntity(tr, val)
// struct fields need to be specified
type Struct struct {
Description string
DisplayName string
MetaInformation string
}
val := Struct{}
err := TranslateEntity(tr, val,
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName"),
)
// nested structures are supported
type InnerStruct struct {
Description string
Roles []string
}
type OuterStruct struct {
DisplayName string
First InnerStruct
Others map[string]InnerStruct
}
val := OuterStruct{}
err := TranslateEntity(tr, val,
l10n.TranslateField("DisplayName"),
l10n.TranslateStruct("First",
l10n.TranslateField("Description"),
l10n.TranslateEach("Roles"),
),
l10n.TranslateMap("Others",
l10n.TranslateField("Description"),
},
*/
func (t Translator) TranslateEntity(locale string, entity any, opts ...TranslateOption) error {
return TranslateEntity(t.Locale(locale).Get, entity, opts...)
}
// MustGetUserLocale returns the locale the user wants to use, omitting errors
func MustGetUserLocale(ctx context.Context, userID string, preferedLang string, vc settingssvc.ValueService) string {
if preferedLang != "" {
@@ -91,3 +152,194 @@ func GetUserLocale(ctx context.Context, userID string, vc settingssvc.ValueServi
}
return val[0].GetStringValue(), nil
}
// TranslateOption is used to specify fields in structs to translate
type TranslateOption func() (string, FieldType, []TranslateOption)
// FieldType is used to specify the type of field to translate
type FieldType int
const (
// FieldTypeString is a string field
FieldTypeString FieldType = iota
// FieldTypeStruct is a struct field
FieldTypeStruct
// FieldTypeIterable is a slice or array field
FieldTypeIterable
// FieldTypeMap is a map field
FieldTypeMap
)
// TranslateField function provides the generic way to translate the necessary field in composite entities.
func TranslateField(fieldName string) TranslateOption {
return func() (string, FieldType, []TranslateOption) {
return fieldName, FieldTypeString, nil
}
}
// TranslateStruct function provides the generic way to translate the nested fields in composite entities.
func TranslateStruct(fieldName string, args ...TranslateOption) TranslateOption {
return func() (string, FieldType, []TranslateOption) {
return fieldName, FieldTypeStruct, args
}
}
// TranslateEach function provides the generic way to translate the necessary fields in slices or nested entities.
func TranslateEach(fieldName string, args ...TranslateOption) TranslateOption {
return func() (string, FieldType, []TranslateOption) {
return fieldName, FieldTypeIterable, args
}
}
// TranslateMap function provides the generic way to translate the necessary fields in maps.
func TranslateMap(fieldName string, args ...TranslateOption) TranslateOption {
return func() (string, FieldType, []TranslateOption) {
return fieldName, FieldTypeMap, args
}
}
// TranslateEntity translates a slice, array or struct
// See Translator.TranslateEntity for more information
func TranslateEntity(tr func(string, ...any) string, entity any, opts ...TranslateOption) error {
value := reflect.ValueOf(entity)
value, ok := cleanValue(value)
if !ok {
return errors.New("entity is not valid")
}
switch value.Kind() {
case reflect.Struct:
rangeOverArgs(tr, value, opts...)
case reflect.Slice, reflect.Array, reflect.Map:
translateEach(tr, value, opts...)
case reflect.String:
translateField(tr, value)
default:
return ErrUnsupportedType
}
return nil
}
func translateEach(tr func(string, ...any) string, value reflect.Value, args ...TranslateOption) {
value, ok := cleanValue(value)
if !ok {
return
}
switch value.Kind() {
case reflect.Array, reflect.Slice:
for i := 0; i < value.Len(); i++ {
v := value.Index(i)
switch v.Kind() {
case reflect.Struct, reflect.Ptr:
rangeOverArgs(tr, v, args...)
case reflect.String:
translateField(tr, v)
case reflect.Slice, reflect.Array, reflect.Map:
translateEach(tr, v, args...)
}
}
case reflect.Map:
for _, k := range value.MapKeys() {
v := value.MapIndex(k)
switch v.Kind() {
case reflect.Struct:
// FIXME: add support for non-pointer values
case reflect.Pointer:
rangeOverArgs(tr, v, args...)
case reflect.String:
if nv := tr(v.String()); nv != "" {
value.SetMapIndex(k, reflect.ValueOf(nv))
}
case reflect.Slice, reflect.Array, reflect.Map:
translateEach(tr, v, args...)
}
}
}
}
func rangeOverArgs(tr func(string, ...any) string, value reflect.Value, args ...TranslateOption) {
value, ok := cleanValue(value)
if !ok {
return
}
for _, arg := range args {
fieldName, fieldType, opts := arg()
switch fieldType {
case FieldTypeString:
f := value.FieldByName(fieldName)
translateField(tr, f)
case FieldTypeStruct:
innerValue := value.FieldByName(fieldName)
if !innerValue.IsValid() || !isStruct(innerValue) {
return
}
rangeOverArgs(tr, innerValue, opts...)
case FieldTypeIterable:
innerValue := value.FieldByName(fieldName)
if !innerValue.IsValid() {
return
}
if kind := innerValue.Kind(); kind != reflect.Array && kind != reflect.Slice {
return
}
translateEach(tr, innerValue, opts...)
case FieldTypeMap:
innerValue := value.FieldByName(fieldName)
if !innerValue.IsValid() {
return
}
if kind := innerValue.Kind(); kind != reflect.Map {
return
}
translateEach(tr, innerValue, opts...)
}
}
}
func translateField(tr func(string, ...any) string, f reflect.Value) {
if f.IsValid() {
if f.Kind() == reflect.Ptr {
if f.IsNil() {
return
}
f = f.Elem()
}
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if f.CanSet() {
// change value
if f.Kind() == reflect.String {
val := tr(f.String())
if val == "" {
return
}
f.SetString(val)
}
}
}
}
func isStruct(r reflect.Value) bool {
if r.Kind() == reflect.Ptr {
r = r.Elem()
}
return r.Kind() == reflect.Struct
}
func cleanValue(v reflect.Value) (reflect.Value, bool) {
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
if v.IsNil() {
return v, false
}
v = v.Elem()
}
if !v.IsValid() {
return v, false
}
return v, true
}

414
ocis-pkg/l10n/l10n_test.go Normal file
View File

@@ -0,0 +1,414 @@
package l10n
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestTranslateStruct(t *testing.T) {
type InnerStruct struct {
Description string
DisplayName *string
}
type TopLevelStruct struct {
Description string
DisplayName *string
SubStruct *InnerStruct
}
type WrapperStruct struct {
Description string
StructList []*InnerStruct
}
toStrPointer := func(str string) *string {
return &str
}
tests := []struct {
name string
entity any
args []TranslateOption
expected any
wantErr bool
}{
{
name: "top level slice of struct",
entity: []*InnerStruct{
{
Description: "inner 1",
DisplayName: toStrPointer("innerDisplayName 1"),
},
{
Description: "inner 2",
DisplayName: toStrPointer("innerDisplayName 2"),
},
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
},
expected: []*InnerStruct{
{
Description: "new Inner 1",
DisplayName: toStrPointer("new InnerDisplayName 1"),
},
{
Description: "new Inner 2",
DisplayName: toStrPointer("new InnerDisplayName 2"),
},
},
},
{
name: "top level slice of string",
entity: []string{
"inner 1",
"inner 2",
},
expected: []string{
"new Inner 1",
"new Inner 2",
},
},
{
name: "top level slice of struct",
entity: []*TopLevelStruct{
{
Description: "inner 1",
DisplayName: toStrPointer("innerDisplayName 1"),
SubStruct: &InnerStruct{
Description: "inner",
DisplayName: toStrPointer("innerDisplayName"),
},
},
{
Description: "inner 2",
DisplayName: toStrPointer("innerDisplayName 2"),
},
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: []*TopLevelStruct{
{
Description: "new Inner 1",
DisplayName: toStrPointer("new InnerDisplayName 1"),
SubStruct: &InnerStruct{
Description: "new Inner",
DisplayName: toStrPointer("new InnerDisplayName"),
},
},
{
Description: "new Inner 2",
DisplayName: toStrPointer("new InnerDisplayName 2"),
},
},
},
{
name: "wrapped struct full",
entity: &WrapperStruct{
StructList: []*InnerStruct{
{
Description: "inner 1",
DisplayName: toStrPointer("innerDisplayName 1"),
},
{
Description: "inner 2",
DisplayName: toStrPointer("innerDisplayName 2"),
},
},
},
args: []TranslateOption{
TranslateEach("StructList",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: &WrapperStruct{
StructList: []*InnerStruct{
{
Description: "new Inner 1",
DisplayName: toStrPointer("new InnerDisplayName 1"),
},
{
Description: "new Inner 2",
DisplayName: toStrPointer("new InnerDisplayName 2"),
},
},
},
},
{
name: "empty struct, NotExistingSubStructName",
entity: &TopLevelStruct{},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("NotExistingSubStructName",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: &TopLevelStruct{},
},
{
name: "empty struct",
entity: &TopLevelStruct{},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: &TopLevelStruct{},
},
{
name: "empty struct, not existing field",
entity: &TopLevelStruct{
Description: "description",
DisplayName: toStrPointer("displayName"),
},
args: []TranslateOption{
TranslateField("NotExistingFieldName"),
TranslateStruct("SubStruct",
TranslateField("NotExistingFieldName"),
),
},
expected: &TopLevelStruct{
Description: "description",
DisplayName: toStrPointer("displayName"),
},
},
{
name: "inner struct DisplayName empy",
entity: &TopLevelStruct{
Description: "description",
DisplayName: toStrPointer("displayName"),
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: &TopLevelStruct{
Description: "new Description",
DisplayName: toStrPointer("new DisplayName"),
},
},
{
name: "inner struct full",
entity: &TopLevelStruct{
Description: "description",
DisplayName: toStrPointer("displayName"),
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: &TopLevelStruct{
Description: "new Description",
DisplayName: toStrPointer("new DisplayName"),
},
},
{
name: "full struct",
entity: &TopLevelStruct{
Description: "description",
DisplayName: toStrPointer("displayName"),
SubStruct: &InnerStruct{
Description: "inner",
DisplayName: toStrPointer("innerDisplayName"),
},
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
TranslateStruct("SubStruct",
TranslateField("Description"),
TranslateField("DisplayName"),
),
},
expected: &TopLevelStruct{
Description: "new Description",
DisplayName: toStrPointer("new DisplayName"),
SubStruct: &InnerStruct{
Description: "new Inner",
DisplayName: toStrPointer("new InnerDisplayName"),
},
},
},
{
name: "nil",
wantErr: true,
},
{
name: "empty slice",
wantErr: true,
},
{
name: "string slice",
entity: []string{"description", "inner"},
expected: []string{"new Description", "new Inner"},
},
{
name: "string map",
entity: map[string]string{
"entryOne": "description",
"entryTwo": "inner",
},
expected: map[string]string{
"entryOne": "new Description",
"entryTwo": "new Inner",
},
},
{
name: "pointer struct map",
entity: map[string]*InnerStruct{
"entryOne": {Description: "description", DisplayName: toStrPointer("displayName")},
"entryTwo": {Description: "inner", DisplayName: toStrPointer("innerDisplayName")},
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
},
expected: map[string]*InnerStruct{
"entryOne": {Description: "new Description", DisplayName: toStrPointer("new DisplayName")},
"entryTwo": {Description: "new Inner", DisplayName: toStrPointer("new InnerDisplayName")},
},
},
/* FIXME: non pointer maps are currently not working
{
name: "struct map",
entity: map[string]InnerStruct{
"entryOne": {Description: "description", DisplayName: toStrPointer("displayName")},
"entryTwo": {Description: "inner", DisplayName: toStrPointer("innerDisplayName")},
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
},
expected: map[string]InnerStruct{
"entryOne": {Description: "new Description", DisplayName: toStrPointer("new DisplayName")},
"entryTwo": {Description: "new Inner", DisplayName: toStrPointer("new InnerDisplayName")},
},
},
*/
{
name: "slice map",
entity: map[string][]string{
"entryOne": {"description", "inner"},
"entryTwo": {"inner 2", "innerDisplayName 2"},
},
expected: map[string][]string{
"entryOne": {"new Description", "new Inner"},
"entryTwo": {"new Inner 2", "new InnerDisplayName 2"},
},
},
{
name: "double slice",
entity: [][]string{
{"description", "inner"},
{"inner 2", "innerDisplayName 2"},
},
expected: [][]string{
{"new Description", "new Inner"},
{"new Inner 2", "new InnerDisplayName 2"},
},
},
{
name: "nested structs",
entity: [][]*InnerStruct{
{
&InnerStruct{Description: "description", DisplayName: toStrPointer("displayName")},
&InnerStruct{Description: "inner", DisplayName: toStrPointer("innerDisplayName")},
},
{
&InnerStruct{Description: "inner 2", DisplayName: toStrPointer("innerDisplayName 2")},
},
},
args: []TranslateOption{
TranslateField("Description"),
TranslateField("DisplayName"),
},
expected: [][]*InnerStruct{
{
&InnerStruct{Description: "new Description", DisplayName: toStrPointer("new DisplayName")},
&InnerStruct{Description: "new Inner", DisplayName: toStrPointer("new InnerDisplayName")},
},
{
&InnerStruct{Description: "new Inner 2", DisplayName: toStrPointer("new InnerDisplayName 2")},
},
},
},
{
name: "double mapslices",
entity: []map[string][]string{
{
"entryOne": {"inner 1", "innerDisplayName 1"},
"entryTwo": {"inner 2", "innerDisplayName 2"},
},
{
"entryOne": {"description", "displayName"},
},
},
expected: []map[string][]string{
{
"entryOne": {"new Inner 1", "new InnerDisplayName 1"},
"entryTwo": {"new Inner 2", "new InnerDisplayName 2"},
},
{
"entryOne": {"new Description", "new DisplayName"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := TranslateEntity(mock(), tt.entity, tt.args...)
if (err != nil) != tt.wantErr {
t.Errorf("TranslateEntity() error = %v, wantErr %v", err, tt.wantErr)
}
assert.Equal(t, tt.expected, tt.entity)
})
}
}
func mock() func(string, ...interface{}) string {
return func(s string, i ...interface{}) string {
switch s {
case "description":
return "new Description"
case "displayName":
return "new DisplayName"
case "inner":
return "new Inner"
case "innerDisplayName":
return "new InnerDisplayName"
case "inner 1":
return "new Inner 1"
case "innerDisplayName 1":
return "new InnerDisplayName 1"
case "inner 2":
return "new Inner 2"
case "innerDisplayName 2":
return "new InnerDisplayName 2"
}
return s
}
}

View File

@@ -2,8 +2,8 @@ SHELL := bash
NAME := graph
# Where to write the files generated by this makefile.
OUTPUT_DIR = ./pkg/service/v0/l10n
TEMPLATE_FILE = ./pkg/service/v0/l10n/graph.pot
OUTPUT_DIR = ./pkg/l10n
TEMPLATE_FILE = ./pkg/l10n/graph.pot
include ../../.make/recursion.mk
@@ -45,7 +45,7 @@ l10n-push:
.PHONY: l10n-read
l10n-read: $(GO_XGETTEXT)
go-xgettext -o $(OUTPUT_DIR)/graph.pot --keyword=l10n.Template --add-comments -s pkg/service/v0/spacetemplates.go
go-xgettext -o $(OUTPUT_DIR)/graph.pot --keyword=l10n.Template --add-comments -s pkg/service/v0/spacetemplates.go -s pkg/unifiedrole/unifiedrole.go
.PHONY: l10n-write
l10n-write:

View File

@@ -2,10 +2,10 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
# Translators:
# Alex <hostspepc@gmail.com>, 2024
#
#
#, fuzzy
msgid ""
msgstr ""

View File

@@ -0,0 +1,32 @@
package l10n
import (
"embed"
"github.com/owncloud/ocis/v2/ocis-pkg/l10n"
)
var (
//go:embed locale
_localeFS embed.FS
)
const (
// subfolder where the translation files are stored
_localeSubPath = "locale"
// domain of the graph service (transifex)
_domain = "graph"
)
// Translate translates a string based on the locale and default locale
func Translate(content, locale, defaultLocale string) string {
t := l10n.NewTranslatorFromCommonConfig(defaultLocale, _domain, "", _localeFS, _localeSubPath)
return t.Translate(content, locale)
}
// TranslateEntity returns a function that translates a struct or slice based on the locale
func TranslateEntity(locale, defaultLocale string, entity any, opts ...l10n.TranslateOption) error {
t := l10n.NewTranslatorFromCommonConfig(defaultLocale, _domain, "", _localeFS, _localeSubPath)
return t.TranslateEntity(locale, entity, opts...)
}

View File

@@ -25,6 +25,8 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/l10n"
l10n_pkg "github.com/owncloud/ocis/v2/services/graph/pkg/l10n"
"github.com/owncloud/ocis/v2/ocis-pkg/conversions"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -632,6 +634,20 @@ func (api DriveItemPermissionsApi) ListPermissions(w http.ResponseWriter, r *htt
return
}
loc := r.Header.Get(l10n.HeaderAcceptLanguage)
w.Header().Add("Content-Language", loc)
if loc != "" && loc != "en" {
err := l10n_pkg.TranslateEntity(loc, "en", permissions,
l10n.TranslateEach("LibreGraphPermissionsRolesAllowedValues",
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName"),
),
)
if err != nil {
api.logger.Error().Err(err).Msg("tranlation error")
}
}
render.Status(r, http.StatusOK)
render.JSON(w, r, permissions)
}
@@ -653,6 +669,20 @@ func (api DriveItemPermissionsApi) ListSpaceRootPermissions(w http.ResponseWrite
return
}
loc := r.Header.Get(l10n.HeaderAcceptLanguage)
w.Header().Add("Content-Language", loc)
if loc != "" && loc != "en" {
err := l10n_pkg.TranslateEntity(loc, "en", permissions,
l10n.TranslateEach("LibreGraphPermissionsRolesAllowedValues",
l10n.TranslateField("Description"),
l10n.TranslateField("DisplayName"),
),
)
if err != nil {
api.logger.Error().Err(err).Msg("tranlation error")
}
}
render.Status(r, http.StatusOK)
render.JSON(w, r, permissions)
}

View File

@@ -15,18 +15,13 @@ import (
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/owncloud/ocis/v2/ocis-pkg/l10n"
l10n_pkg "github.com/owncloud/ocis/v2/services/graph/pkg/l10n"
)
var (
//go:embed spacetemplate/*
_spaceTemplateFS embed.FS
//go:embed l10n/locale
_localeFS embed.FS
// subfolder where the translation files are stored
_localeSubPath = "l10n/locale"
// name of the secret space folder
_spaceFolderName = ".space"
@@ -39,9 +34,6 @@ var (
// name of the readme.md file
_readmeName = "readme.md"
// domain of the graph service (transifex)
_domain = "graph"
// HeaderAcceptLanguage is the header key for the accept-language header
HeaderAcceptLanguage = "Accept-Language"
@@ -121,10 +113,9 @@ func imageUpload(ctx context.Context, mdc *metadata.CS3) (string, error) {
}
func readmeUpload(ctx context.Context, mdc *metadata.CS3, locale string, defaultLocale string) (string, error) {
t := l10n.NewTranslatorFromCommonConfig(defaultLocale, _domain, "", _localeFS, _localeSubPath)
res, err := mdc.Upload(ctx, metadata.UploadRequest{
Path: filepath.Join(_spaceFolderName, _readmeName),
Content: []byte(t.Translate(_readmeText, locale)),
Content: []byte(l10n_pkg.Translate(_readmeText, locale, defaultLocale)),
})
if err != nil {
return "", err

View File

@@ -7,6 +7,7 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/l10n"
"google.golang.org/protobuf/proto"
"github.com/cs3org/reva/v2/pkg/conversions"
@@ -71,12 +72,54 @@ var legacyNames map[string]string = map[string]string{
UnifiedRoleSecureViewerID: conversions.RoleSecureViewer,
}
var (
// UnifiedRole Viewer, Role Description (resolves directly)
_viewerUnifiedRoleDescription = l10n.Template("View and download.")
// UnifiedRole Viewer, Role DisplayName (resolves directly)
_viewerUnifiedRoleDisplayName = l10n.Template("Can view")
// UnifiedRole SpaceViewer, Role Description (resolves directly)
_spaceViewerUnifiedRoleDescription = l10n.Template("View and download.")
// UnifiedRole SpaseViewer, Role DisplayName (resolves directly)
_spaceViewerUnifiedRoleDisplayName = l10n.Template("Can view")
// UnifiedRole Editor, Role Description (resolves directly)
_editorUnifiedRoleDescription = l10n.Template("View, download, upload, edit, add and delete.")
// UnifiedRole Editor, Role DisplayName (resolves directly)
_editorUnifiedRoleDisplayName = l10n.Template("Can edit")
// UnifiedRole SpaseEditor, Role Description (resolves directly)
_spaceEditorUnifiedRoleDescription = l10n.Template("View, download, upload, edit, add and delete.")
// UnifiedRole SpaseEditor, Role DisplayName (resolves directly)
_spaceEditorUnifiedRoleDisplayName = l10n.Template("Can edit")
// UnifiedRole FileEditor, Role Description (resolves directly)
_fileEditorUnifiedRoleDescription = l10n.Template("View, download and edit.")
// UnifiedRole FileEditor, Role DisplayName (resolves directly)
_fileEditorUnifiedRoleDisplayName = l10n.Template("Can edit")
// UnifiedRole EditorLite, Role Description (resolves directly)
_editorLiteUnifiedRoleDescription = l10n.Template("View, download and upload.")
// UnifiedRole EditorLite, Role DisplayName (resolves directly)
_editorLiteUnifiedRoleDisplayName = l10n.Template("Can upload")
// UnifiedRole Manager, Role Description (resolves directly)
_managerUnifiedRoleDescription = l10n.Template("View, download, upload, edit, add, delete and manage members.")
// UnifiedRole Manager, Role DisplayName (resolves directly)
_managerUnifiedRoleDisplayName = l10n.Template("Can manage")
// UnifiedRole SecureViewer, Role Description (resolves directly)
_secureViewerUnifiedRoleDescription = l10n.Template("View only documents, images and PDFs. Watermarks will be applied.")
// UnifiedRole SecureViewer, Role DisplayName (resolves directly)
_secureViewerUnifiedRoleDisplayName = l10n.Template("Can view (secure)")
)
// NewViewerUnifiedRole creates a viewer role.
func NewViewerUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewViewerRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleViewerID),
Description: proto.String("View and download."),
Description: proto.String(_viewerUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -97,7 +140,7 @@ func NewSpaceViewerUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSpaceViewerRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSpaceViewerID),
Description: proto.String("View and download."),
Description: proto.String(_spaceViewerUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -114,7 +157,7 @@ func NewEditorUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewEditorRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleEditorID),
Description: proto.String("View, download, upload, edit, add and delete."),
Description: proto.String(_editorUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -131,7 +174,7 @@ func NewSpaceEditorUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSpaceEditorRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSpaceEditorID),
Description: proto.String("View, download, upload, edit, add and delete."),
Description: proto.String(_spaceEditorUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -148,7 +191,7 @@ func NewFileEditorUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewFileEditorRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleFileEditorID),
Description: proto.String("View, download and edit."),
Description: proto.String(_fileEditorUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -165,7 +208,7 @@ func NewEditorLiteUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewEditorLiteRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleEditorLiteID),
Description: proto.String("View, download and upload."),
Description: proto.String(_editorLiteUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -182,7 +225,7 @@ func NewManagerUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewManagerRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleManagerID),
Description: proto.String("View, download, upload, edit, add, delete and manage members."),
Description: proto.String(_managerUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -199,7 +242,7 @@ func NewSecureViewerUnifiedRole() *libregraph.UnifiedRoleDefinition {
r := conversions.NewSecureViewerRole()
return &libregraph.UnifiedRoleDefinition{
Id: proto.String(UnifiedRoleSecureViewerID),
Description: proto.String("View only documents, images and PDFs. Watermarks will be applied."),
Description: proto.String(_secureViewerUnifiedRoleDescription),
DisplayName: displayName(r),
RolePermissions: []libregraph.UnifiedRolePermission{
{
@@ -482,27 +525,24 @@ func displayName(role *conversions.Role) *string {
return nil
}
// linter wants this to be a var
canEdit := "Can edit"
var displayName string
switch role.Name {
case conversions.RoleViewer:
displayName = "Can view"
displayName = _viewerUnifiedRoleDisplayName
case conversions.RoleSpaceViewer:
displayName = "Can view"
displayName = _spaceViewerUnifiedRoleDisplayName
case conversions.RoleEditor:
displayName = canEdit
displayName = _editorUnifiedRoleDisplayName
case conversions.RoleSpaceEditor:
displayName = canEdit
displayName = _spaceEditorUnifiedRoleDisplayName
case conversions.RoleFileEditor:
displayName = canEdit
displayName = _fileEditorUnifiedRoleDisplayName
case conversions.RoleEditorLite:
displayName = "Can upload"
displayName = _editorLiteUnifiedRoleDisplayName
case conversions.RoleManager:
displayName = "Can manage"
displayName = _managerUnifiedRoleDisplayName
case conversions.RoleSecureViewer:
displayName = "Can view (secure)"
displayName = _secureViewerUnifiedRoleDisplayName
default:
return nil
}