From 9785c6ba141604a7dcad41056ba6b88bbb10b924 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 25 Mar 2024 12:26:35 +0100 Subject: [PATCH 1/9] fix quota response on createdrive Signed-off-by: jkoberg --- changelog/unreleased/space-templates.md | 5 +++++ services/graph/pkg/service/v0/drives.go | 24 +++++++++++++++++------- services/graph/pkg/service/v0/utils.go | 16 ++++++++-------- 3 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 changelog/unreleased/space-templates.md diff --git a/changelog/unreleased/space-templates.md b/changelog/unreleased/space-templates.md new file mode 100644 index 0000000000..a2076dff68 --- /dev/null +++ b/changelog/unreleased/space-templates.md @@ -0,0 +1,5 @@ +Enhancement: Make server side space templates production ready + +Fixes several smaller bugs and adds some improvements to space templates, introduced with https://github.com/owncloud/ocis/pull/8558 + +https://github.com/owncloud/ocis/pull/8723 diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index e4f0df8398..c7e9f26dd8 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -423,27 +423,37 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { return } + space := resp.GetStorageSpace() if driveType == _spaceTypeProject { - opaque, err := g.applySpaceTemplate(gatewayClient, resp.GetStorageSpace().GetRoot(), r.URL.Query().Get("template")) - if err != nil { + if err := g.applySpaceTemplate(gatewayClient, resp.GetStorageSpace().GetRoot(), r.URL.Query().Get("template")); err != nil { logger.Error().Err(err).Msg("could not apply template to space") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) return } - resp.StorageSpace.Opaque = utils.MergeOpaques(resp.GetStorageSpace().GetOpaque(), opaque) + // refetch the drive to get quota information - should we calculate this ourselves to avoid the extra call? + space, err = utils.GetSpace(r.Context(), resp.GetStorageSpace().GetId().GetOpaqueId(), gatewayClient) + if err != nil { + logger.Error().Err(err).Msg("could not refetch space") + errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) + return + } } - newDrive, err := g.cs3StorageSpaceToDrive(r.Context(), webDavBaseURL, resp.GetStorageSpace(), APIVersion_1) + spaces, err := g.formatDrives(r.Context(), webDavBaseURL, []*storageprovider.StorageSpace{space}, APIVersion_1) if err != nil { - logger.Debug().Err(err).Msg("could not create drive: error parsing drive") + logger.Debug().Err(err).Msg("could not get drive: error parsing grpc response") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) return } - newDrive.Special = g.getSpecialDriveItems(r.Context(), webDavBaseURL, resp.GetStorageSpace()) + if len(spaces) == 0 { + logger.Error().Msg("could not convert space") + errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "could not convert space") + return + } render.Status(r, http.StatusCreated) - render.JSON(w, r, newDrive) + render.JSON(w, r, spaces[0]) } // UpdateDrive updates the properties of a storage drive (space). diff --git a/services/graph/pkg/service/v0/utils.go b/services/graph/pkg/service/v0/utils.go index ee5ccd03e9..040b1cb288 100644 --- a/services/graph/pkg/service/v0/utils.go +++ b/services/graph/pkg/service/v0/utils.go @@ -446,18 +446,18 @@ func cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, logger *log.Lo return permission, nil } -func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) (*v1beta1.Opaque, error) { +func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) error { var fsys fs.ReadDirFS switch template { default: fallthrough case "none": - return &v1beta1.Opaque{}, nil + return nil case "default": f, err := fs.Sub(_spaceTemplateFS, "spacetemplate") if err != nil { - return nil, err + return err } fsys = f.(fs.ReadDirFS) } @@ -467,12 +467,12 @@ func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storagepro ctx, err := utils.GetServiceUserContext(g.config.ServiceAccount.ServiceAccountID, gwc, g.config.ServiceAccount.ServiceAccountSecret) if err != nil { - return nil, err + return err } opaque, err := uploadFolder(ctx, mdc, ".", "", nil, fsys) if err != nil { - return nil, err + return err } resp, err := gwc.UpdateStorageSpace(ctx, &storageprovider.UpdateStorageSpaceRequest{ @@ -486,11 +486,11 @@ func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storagepro }) switch { case err != nil: - return nil, err + return err case resp.GetStatus().GetCode() != rpc.Code_CODE_OK: - return nil, fmt.Errorf("could not update storage space: %s", resp.GetStatus().GetMessage()) + return fmt.Errorf("could not update storage space: %s", resp.GetStatus().GetMessage()) default: - return opaque, nil + return nil } } From 0567dc4f70a8b74d5804bb2dbbe9341be20c4181 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 25 Mar 2024 12:27:14 +0100 Subject: [PATCH 2/9] use correct spaceimage Signed-off-by: jkoberg --- .../service/v0/spacetemplate/.space/image.png | Bin 3856 -> 47233 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/services/graph/pkg/service/v0/spacetemplate/.space/image.png b/services/graph/pkg/service/v0/spacetemplate/.space/image.png index 3b03d6554dd3b398deecc3a808e792a002838295..0a9dc3f761db4d1cf6675cec0e2447389f9bc0c6 100644 GIT binary patch literal 47233 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 literal 3856 zcmeH~>01+77RHNInh*pc5J4pvX_9~-Esr!xWJ#kU1e>M9VjK_x5pY2^QIRDiD4VTB z5ey0e@xcX+EKN6y2u8MugQ6fY3eo`-aKaK5On?NYKzqh{`VW|Adi+pTAMU;9+;i*H z?|m!N)7@E9eYH9O0Gec%?G)%KUA)v(p}T|6+iU>PdQRT%=p7d@&Lw38IH6mg??9)} zOit|!G1^z5HwJezJA`&L*FLm;IZvU_vq)K+!PUD$rJAPDj8bw_x$qMrkpVA7KH}ig zvgeqra5Dcxe^TVI{>mSPbB_vUxCtY9J{_6}1rva8+9^(ZWaexGUmL{-y5Y-3mMW@o z&BI29Fbzw4fTgKRV{Q&#(`5t=g8w^%6>TaEv+(0M;S1_9!JQDx{IZs$6@GPn9 z!(#u?O?wazx5{)Y4s+`_?TUwWv* zFhaZk{_CJ%0>0*Fc4GCMz+DMvjGzI<42R9cD=-zt{wAmg2!kqW)Q+MdO1h%M<$+2;Ky zE>FquzWxDczw$ce*=ISJc(+xW#=B#K&-L0a9#i(}W4bQs8~OFLV5Y^d!@rm8z2K@9 z!nAnx`ePP6qN;T~z2w?!PgUxBf_Y4W00?XGSeT1|_KJtrWN+RLMNzdeehgs0{h^t6 zD@aP;!*Ds!S6&-yioCmw(3lxgY`s9hS9!;ng5hroHOFvK`4)d(KFOM~kMd(p&658z7q(au3a&v$o0q1uc&J zY8@M6ci4IUA^WW`gPFRT7}AaG#_VlmT6;e4!MGu^K-+{x7FVw^Vx3X+`PE^)$${;~ zxQf?qzKiGjIO9AY2Nislzk@MxyAO4$bRxo8?Vb zq3h+9q$fnh*=OEb((Ym@xP$>a?Lc|Uml$XyqTLrnN3WOUT#>*Dp<(nr*2eeQO6KD? zOEFDYD8Z~N#r5eC7^>Byk&8qy#jFjb5bxoxZ-j9~NqmBf>!b-}h6AQGU0itN8*8E*&5j_ceaCV-5-F8wu z2QV-gUORh%SYNI3@wYX$jx+$KAq5DBTNy}8zB2yrk5t$40TgZHt}zY(_LbN4h5uJ4 z@K+<`rF^9e@2v;6)>fFY;8h;10R}Gz3-|QA z>t1M^nxO`6;Hi_c5CLxll?Q|y`H^XNHtR(^u`rbF%1FWsdzWRlvAZ2Nj0-Xk2rc>D zMGvI2ThG)@(*sT|>rh^|5~ug$cgmx0Uiybh;r!%6k>u_T4H&9zHiDodue22rl`cI{ zqg?cqdGUHFxo8E4KPA}RuP;Ga=_y30QGW>DW(awzr%h7#DQvxN+YyY(T9%5Jq5kF?MTcbkz zT97%EwB#%Eze?O+Rnbw}a(l*+YiC|m6cvhI1vlFNVN@q}`EQx}+taIZiG6({^JOwp zv658(yaW8gc$%Ue)-4%X6l7%czBIBP=}joyqLGzuPaM$w4-5=@+9>J0+-%CJGTpp9 z?wKP!r{Vv$&V_IXjKrM0&gnVQ4o#XlO2GV!X(%a9+Cz=J>bfpZ?e0nm_u^0Dbgeif z$=#ARw)#nhkNIH*q&P87-u-5FD@tg_Z+3FqWx{WzZ zJn3K{$xr=eNbrt-OJU>t=#pxjPMjA{K% zize=#m;~j~Q56Kz{K(i#RnhbKF*r*_&5{r1b*z}f^8uN}VzU7g0PUFV{*{3+ji`J5 fF;#!H2~yMtwflf>; Date: Mon, 25 Mar 2024 13:14:01 +0100 Subject: [PATCH 3/9] move translation logic from notifications to own package Signed-off-by: jkoberg --- ocis-pkg/l10n/l10n.go | 51 +++++++++++++++++++ services/notifications/pkg/email/composer.go | 29 +++++++---- .../notifications/pkg/email/l10n/locate.go | 51 ------------------- 3 files changed, 69 insertions(+), 62 deletions(-) create mode 100644 ocis-pkg/l10n/l10n.go delete mode 100644 services/notifications/pkg/email/l10n/locate.go diff --git a/ocis-pkg/l10n/l10n.go b/ocis-pkg/l10n/l10n.go new file mode 100644 index 0000000000..658f60fd09 --- /dev/null +++ b/ocis-pkg/l10n/l10n.go @@ -0,0 +1,51 @@ +// package l10n holds translation mechanics that are used by user facing services (notifications, userlog, graph) +package l10n + +import ( + "io/fs" + "os" + + "github.com/leonelquinteros/gotext" +) + +// Translator is able to translate strings +type Translator struct { + fs fs.FS + defaultLocale string + domain string +} + +// NewTranslator creates a Translator with library path and language code and load default domain +func NewTranslator(defaultLocale string, domain string, fsys fs.FS) Translator { + return Translator{ + fs: fsys, + defaultLocale: defaultLocale, + domain: domain, + } +} + +// NewTranslatorFromCommonConfig creates a new Translator from legacy config +func NewTranslatorFromCommonConfig(defaultLocale string, domain string, path string, fsys fs.FS, fsSubPath string) Translator { + var filesystem fs.FS + if path == "" { + filesystem, _ = fs.Sub(fsys, fsSubPath) + } else { // use custom path instead + filesystem = os.DirFS(path) + } + return NewTranslator(defaultLocale, domain, filesystem) +} + +// Translate translates a string to the locale +func (t Translator) Translate(str, locale string) string { + return t.Locale(locale).Get(str) +} + +// Locale returns the gotext.Locale, use `.Get` method to translate strings +func (t Translator) Locale(locale string) *gotext.Locale { + l := gotext.NewLocaleFS(locale, t.fs) + if locale != "en" && len(l.GetTranslations()) == 0 { + l = gotext.NewLocaleFS(t.defaultLocale, t.fs) + } + l.AddDomain(t.domain) // make domain configurable only if needed + return l +} diff --git a/services/notifications/pkg/email/composer.go b/services/notifications/pkg/email/composer.go index 81f4cab1ac..b3f33f0bbb 100644 --- a/services/notifications/pkg/email/composer.go +++ b/services/notifications/pkg/email/composer.go @@ -2,29 +2,36 @@ package email import ( "bytes" + "embed" "strings" "text/template" - "github.com/owncloud/ocis/v2/services/notifications/pkg/email/l10n" + "github.com/owncloud/ocis/v2/ocis-pkg/l10n" +) + +var ( + //go:embed l10n/locale + _translationFS embed.FS + _domain = "notifications" ) // NewTextTemplate replace the body message template placeholders with the translated template func NewTextTemplate(mt MessageTemplate, locale, defaultLocale string, translationPath string, vars map[string]string) (MessageTemplate, error) { var err error - t := l10n.NewTranslator(locale, defaultLocale, translationPath) - mt.Subject, err = composeMessage(t.Translate(mt.Subject), vars) + t := l10n.NewTranslatorFromCommonConfig(defaultLocale, _domain, translationPath, _translationFS, "l10n/locale").Locale(locale) + mt.Subject, err = composeMessage(t.Get(mt.Subject), vars) if err != nil { return mt, err } - mt.Greeting, err = composeMessage(t.Translate(mt.Greeting), vars) + mt.Greeting, err = composeMessage(t.Get(mt.Greeting), vars) if err != nil { return mt, err } - mt.MessageBody, err = composeMessage(t.Translate(mt.MessageBody), vars) + mt.MessageBody, err = composeMessage(t.Get(mt.MessageBody), vars) if err != nil { return mt, err } - mt.CallToAction, err = composeMessage(t.Translate(mt.CallToAction), vars) + mt.CallToAction, err = composeMessage(t.Get(mt.CallToAction), vars) if err != nil { return mt, err } @@ -34,20 +41,20 @@ func NewTextTemplate(mt MessageTemplate, locale, defaultLocale string, translati // NewHTMLTemplate replace the body message template placeholders with the translated template func NewHTMLTemplate(mt MessageTemplate, locale, defaultLocale string, translationPath string, vars map[string]string) (MessageTemplate, error) { var err error - t := l10n.NewTranslator(locale, defaultLocale, translationPath) - mt.Subject, err = composeMessage(t.Translate(mt.Subject), vars) + t := l10n.NewTranslatorFromCommonConfig(defaultLocale, _domain, translationPath, _translationFS, "l10n/locale").Locale(locale) + mt.Subject, err = composeMessage(t.Get(mt.Subject), vars) if err != nil { return mt, err } - mt.Greeting, err = composeMessage(newlineToBr(t.Translate(mt.Greeting)), vars) + mt.Greeting, err = composeMessage(newlineToBr(t.Get(mt.Greeting)), vars) if err != nil { return mt, err } - mt.MessageBody, err = composeMessage(newlineToBr(t.Translate(mt.MessageBody)), vars) + mt.MessageBody, err = composeMessage(newlineToBr(t.Get(mt.MessageBody)), vars) if err != nil { return mt, err } - mt.CallToAction, err = composeMessage(callToActionToHTML(t.Translate(mt.CallToAction)), vars) + mt.CallToAction, err = composeMessage(callToActionToHTML(t.Get(mt.CallToAction)), vars) if err != nil { return mt, err } diff --git a/services/notifications/pkg/email/l10n/locate.go b/services/notifications/pkg/email/l10n/locate.go deleted file mode 100644 index 65ced72103..0000000000 --- a/services/notifications/pkg/email/l10n/locate.go +++ /dev/null @@ -1,51 +0,0 @@ -// Package l10n implements utility for translation the text templates. -// -// The l10n package use transifex translation for text templates. -package l10n - -import ( - "embed" - "io/fs" - - "github.com/leonelquinteros/gotext" -) - -var ( - //go:embed locale - _translationFS embed.FS - _domain = "notifications" -) - -// Translator is the interface to the translation -type Translator interface { - Translate(str string) string -} - -type translator struct { - locale *gotext.Locale -} - -// NewTranslator Create Translator with library path and language code and load default domain -func NewTranslator(locale, defaultLocale string, path string) Translator { - l := newLocate(locale, path) - if locale != "en" && len(l.GetTranslations()) == 0 { - l = newLocate(defaultLocale, path) - } - return &translator{locale: l} -} - -func newLocate(local string, path string) *gotext.Locale { - var l *gotext.Locale - if path == "" { - filesystem, _ := fs.Sub(_translationFS, "locale") - l = gotext.NewLocaleFS(local, filesystem) - } else { // use custom path instead - l = gotext.NewLocale(path, local) - } - l.AddDomain(_domain) // make domain configurable only if needed - return l -} - -func (t *translator) Translate(str string) string { - return t.locale.Get(str) -} From 979bcd7106f89c112986bf5e604d5e2c7d3d9c16 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 25 Mar 2024 13:17:49 +0100 Subject: [PATCH 4/9] use l10n package in userlog also Signed-off-by: jkoberg --- services/userlog/pkg/service/conversion.go | 25 ++++------------------ 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index d84dd7bca5..967e75cdcf 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -6,7 +6,6 @@ import ( "embed" "encoding/json" "fmt" - "io/fs" "strings" "text/template" "time" @@ -18,7 +17,7 @@ import ( "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/leonelquinteros/gotext" + "github.com/owncloud/ocis/v2/ocis-pkg/l10n" ) //go:embed l10n/locale @@ -338,7 +337,7 @@ func (c *Converter) getResource(ctx context.Context, resourceID *storageprovider return resource, err } -func (c *Converter) getUser(ctx context.Context, userID *user.UserId) (*user.User, error) { +func (c *Converter) getUser(_ context.Context, userID *user.UserId) (*user.User, error) { if u, ok := c.users[userID.GetOpaqueId()]; ok { return u, nil } @@ -361,25 +360,9 @@ func composeMessage(nt NotificationTemplate, locale, defaultLocale, path string, return subject, subjectraw, message, messageraw, err } -func newLocate(locale string, path string) *gotext.Locale { - // Create Locale with library path and language code and load default domain - var l *gotext.Locale - if path == "" { - filesystem, _ := fs.Sub(_translationFS, "l10n/locale") - l = gotext.NewLocaleFS(locale, filesystem) - } else { // use custom path instead - l = gotext.NewLocale(path, locale) - } - l.AddDomain(_domain) // make domain configurable only if needed - return l -} - func loadTemplates(nt NotificationTemplate, locale, defaultLocale, path string) (string, string) { - l := newLocate(locale, path) - if locale != "en" && len(l.GetTranslations()) == 0 { - l = newLocate(defaultLocale, path) - } - return l.Get(nt.Subject), l.Get(nt.Message) + t := l10n.NewTranslatorFromCommonConfig(defaultLocale, _domain, path, _translationFS, "l10n/locale").Locale(locale) + return t.Get(nt.Subject), t.Get(nt.Message) } func executeTemplate(raw string, vars map[string]interface{}) (string, error) { From 1bc59de021fc63c29088ead7e141507e4e3d1eb9 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Mon, 25 Mar 2024 13:37:29 +0100 Subject: [PATCH 5/9] fix graph tests Signed-off-by: jkoberg --- ocis-pkg/l10n/l10n.go | 3 ++- services/graph/pkg/service/v0/drives.go | 4 ++-- services/graph/pkg/service/v0/graph_test.go | 6 ++++++ services/graph/pkg/service/v0/utils.go | 7 +------ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ocis-pkg/l10n/l10n.go b/ocis-pkg/l10n/l10n.go index 658f60fd09..89a91adf69 100644 --- a/ocis-pkg/l10n/l10n.go +++ b/ocis-pkg/l10n/l10n.go @@ -43,9 +43,10 @@ func (t Translator) Translate(str, locale string) string { // Locale returns the gotext.Locale, use `.Get` method to translate strings func (t Translator) Locale(locale string) *gotext.Locale { l := gotext.NewLocaleFS(locale, t.fs) + l.AddDomain(t.domain) // make domain configurable only if needed if locale != "en" && len(l.GetTranslations()) == 0 { l = gotext.NewLocaleFS(t.defaultLocale, t.fs) + l.AddDomain(t.domain) // make domain configurable only if needed } - l.AddDomain(t.domain) // make domain configurable only if needed return l } diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index c7e9f26dd8..6dbf31864b 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -424,8 +424,8 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { } space := resp.GetStorageSpace() - if driveType == _spaceTypeProject { - if err := g.applySpaceTemplate(gatewayClient, resp.GetStorageSpace().GetRoot(), r.URL.Query().Get("template")); err != nil { + if t := r.URL.Query().Get("template"); t != "" && driveType == _spaceTypeProject { + if err := g.applySpaceTemplate(r.Context(), gatewayClient, resp.GetStorageSpace().GetRoot(), t); err != nil { logger.Error().Err(err).Msg("could not apply template to space") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) return diff --git a/services/graph/pkg/service/v0/graph_test.go b/services/graph/pkg/service/v0/graph_test.go index cb56ee268d..7468db8497 100644 --- a/services/graph/pkg/service/v0/graph_test.go +++ b/services/graph/pkg/service/v0/graph_test.go @@ -764,6 +764,12 @@ var _ = Describe("Graph", func() { Constraint: v0.Permission_CONSTRAINT_ALL, }, }, nil) + + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewOK(ctx), + TotalBytes: 500, + }, nil) + jsonBody := []byte(`{"Name": "Test Space", "DriveType": "project", "Description": "This space is for testing", "DriveAlias": "project/testspace"}`) r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) rr := httptest.NewRecorder() diff --git a/services/graph/pkg/service/v0/utils.go b/services/graph/pkg/service/v0/utils.go index 040b1cb288..ea4a64c942 100644 --- a/services/graph/pkg/service/v0/utils.go +++ b/services/graph/pkg/service/v0/utils.go @@ -446,7 +446,7 @@ func cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, logger *log.Lo return permission, nil } -func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) error { +func (g Graph) applySpaceTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) error { var fsys fs.ReadDirFS switch template { @@ -465,11 +465,6 @@ func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storagepro mdc := metadata.NewCS3(g.config.Reva.Address, g.config.Spaces.StorageUsersAddress) mdc.SpaceRoot = root - ctx, err := utils.GetServiceUserContext(g.config.ServiceAccount.ServiceAccountID, gwc, g.config.ServiceAccount.ServiceAccountSecret) - if err != nil { - return err - } - opaque, err := uploadFolder(ctx, mdc, ".", "", nil, fsys) if err != nil { return err From db346dff10af27aee6d0d90aaa86c8e5a172d1ac Mon Sep 17 00:00:00 2001 From: jkoberg Date: Tue, 26 Mar 2024 12:58:41 +0100 Subject: [PATCH 6/9] make readmes translateable Signed-off-by: jkoberg --- ocis-pkg/l10n/l10n.go | 3 + services/graph/Makefile | 25 ++++ services/graph/pkg/service/v0/service.go | 6 - .../service/v0/spacetemplate/.space/readme.md | 1 - .../v0/spacetemplate/{.space => }/image.png | Bin .../graph/pkg/service/v0/spacetemplates.go | 113 ++++++++++++++++++ services/graph/pkg/service/v0/utils.go | 104 ---------------- 7 files changed, 141 insertions(+), 111 deletions(-) delete mode 100644 services/graph/pkg/service/v0/spacetemplate/.space/readme.md rename services/graph/pkg/service/v0/spacetemplate/{.space => }/image.png (100%) create mode 100644 services/graph/pkg/service/v0/spacetemplates.go diff --git a/ocis-pkg/l10n/l10n.go b/ocis-pkg/l10n/l10n.go index 89a91adf69..742c86bf29 100644 --- a/ocis-pkg/l10n/l10n.go +++ b/ocis-pkg/l10n/l10n.go @@ -8,6 +8,9 @@ import ( "github.com/leonelquinteros/gotext" ) +// Template marks a string as translatable +func Template(s string) string { return s } + // Translator is able to translate strings type Translator struct { fs fs.FS diff --git a/services/graph/Makefile b/services/graph/Makefile index fe8a84f023..60a640b069 100644 --- a/services/graph/Makefile +++ b/services/graph/Makefile @@ -1,6 +1,10 @@ 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 + include ../../.make/recursion.mk ############ tooling ############ @@ -30,6 +34,27 @@ ci-go-generate: $(MOCKERY) # CI runs ci-node-generate automatically before this .PHONY: ci-node-generate ci-node-generate: +############ translations ######## +.PHONY: l10n-pull +l10n-pull: + cd $(OUTPUT_DIR) && tx pull --all --force --skip --minimum-perc=75 + +.PHONY: l10n-push +l10n-push: + cd $(OUTPUT_DIR) && tx push -s --skip + +.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 + +.PHONY: l10n-write +l10n-write: + +.PHONY: l10n-clean +l10n-clean: + rm -f $(TEMPLATE_FILE); + + ############ licenses ############ .PHONY: ci-node-check-licenses ci-node-check-licenses: diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index ff1a28ea37..25c19b2dce 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -3,7 +3,6 @@ package svc import ( "crypto/tls" "crypto/x509" - "embed" "errors" "fmt" "net/http" @@ -36,11 +35,6 @@ const ( displayNameAttr = "displayName" ) -var ( - //go:embed spacetemplate/* - _spaceTemplateFS embed.FS -) - // Service defines the service handlers. type Service interface { ServeHTTP(http.ResponseWriter, *http.Request) diff --git a/services/graph/pkg/service/v0/spacetemplate/.space/readme.md b/services/graph/pkg/service/v0/spacetemplate/.space/readme.md deleted file mode 100644 index b96145d09b..0000000000 --- a/services/graph/pkg/service/v0/spacetemplate/.space/readme.md +++ /dev/null @@ -1 +0,0 @@ -Here you can add a description for this Space. diff --git a/services/graph/pkg/service/v0/spacetemplate/.space/image.png b/services/graph/pkg/service/v0/spacetemplate/image.png similarity index 100% rename from services/graph/pkg/service/v0/spacetemplate/.space/image.png rename to services/graph/pkg/service/v0/spacetemplate/image.png diff --git a/services/graph/pkg/service/v0/spacetemplates.go b/services/graph/pkg/service/v0/spacetemplates.go new file mode 100644 index 0000000000..c664532958 --- /dev/null +++ b/services/graph/pkg/service/v0/spacetemplates.go @@ -0,0 +1,113 @@ +package svc + +import ( + "context" + "embed" + "fmt" + "io/fs" + "path/filepath" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + v1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" + "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/l10n" +) + +var ( + //go:embed spacetemplate/* + _spaceTemplateFS embed.FS + + //go:embed l10n/locale + _localeFS embed.FS + + _imagepath = "spacetemplate/image.png" + + _readmeText = l10n.Template("Here you can add a description for this Space.") +) + +func (g Graph) applySpaceTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) error { + switch template { + default: + fallthrough + case "none": + return nil + case "default": + return g.applyDefaultTemplate(ctx, gwc, root) + } +} + +func (g Graph) applyDefaultTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId) error { + mdc := metadata.NewCS3(g.config.Reva.Address, g.config.Spaces.StorageUsersAddress) + mdc.SpaceRoot = root + + var opaque *v1beta1.Opaque + + // create .space folder + if err := mdc.MakeDirIfNotExist(ctx, ".space"); err != nil { + return err + } + + // upload space image + iid, err := imageUpload(ctx, mdc) + if err != nil { + return err + } + opaque = utils.AppendPlainToOpaque(opaque, SpaceImageSpecialFolderName, iid) + + // upload readme.md + rid, err := readmeUpload(ctx, mdc) + if err != nil { + return err + } + opaque = utils.AppendPlainToOpaque(opaque, ReadmeSpecialFolderName, rid) + + // update space + resp, err := gwc.UpdateStorageSpace(ctx, &storageprovider.UpdateStorageSpaceRequest{ + StorageSpace: &storageprovider.StorageSpace{ + Id: &storageprovider.StorageSpaceId{ + OpaqueId: storagespace.FormatResourceID(*root), + }, + Root: root, + Opaque: opaque, + }, + }) + switch { + case err != nil: + return err + case resp.GetStatus().GetCode() != rpc.Code_CODE_OK: + return fmt.Errorf("could not update storage space: %s", resp.GetStatus().GetMessage()) + default: + return nil + } +} + +func imageUpload(ctx context.Context, mdc *metadata.CS3) (string, error) { + b, err := fs.ReadFile(_spaceTemplateFS, _imagepath) + if err != nil { + return "", err + } + res, err := mdc.Upload(ctx, metadata.UploadRequest{ + Path: filepath.Join(".space", filepath.Base(_imagepath)), + Content: b, + }) + if err != nil { + return "", err + } + return res.FileID, nil +} + +func readmeUpload(ctx context.Context, mdc *metadata.CS3) (string, error) { + t := l10n.NewTranslatorFromCommonConfig("en", "graph", "", _localeFS, "l10n/locale") + res, err := mdc.Upload(ctx, metadata.UploadRequest{ + Path: filepath.Join(".space", "readme.md"), + Content: []byte(t.Translate(_readmeText, "en_GB")), + }) + if err != nil { + return "", err + } + return res.FileID, nil +} diff --git a/services/graph/pkg/service/v0/utils.go b/services/graph/pkg/service/v0/utils.go index ea4a64c942..ec8e6ce6a8 100644 --- a/services/graph/pkg/service/v0/utils.go +++ b/services/graph/pkg/service/v0/utils.go @@ -3,22 +3,14 @@ package svc import ( "context" "encoding/json" - "fmt" "io" - "io/fs" "net/http" - "os" - "path/filepath" "reflect" - "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" cs3User "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - v1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "golang.org/x/sync/errgroup" @@ -445,99 +437,3 @@ func cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, logger *log.Lo return permission, nil } - -func (g Graph) applySpaceTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) error { - var fsys fs.ReadDirFS - - switch template { - default: - fallthrough - case "none": - return nil - case "default": - f, err := fs.Sub(_spaceTemplateFS, "spacetemplate") - if err != nil { - return err - } - fsys = f.(fs.ReadDirFS) - } - - mdc := metadata.NewCS3(g.config.Reva.Address, g.config.Spaces.StorageUsersAddress) - mdc.SpaceRoot = root - - opaque, err := uploadFolder(ctx, mdc, ".", "", nil, fsys) - if err != nil { - return err - } - - resp, err := gwc.UpdateStorageSpace(ctx, &storageprovider.UpdateStorageSpaceRequest{ - StorageSpace: &storageprovider.StorageSpace{ - Id: &storageprovider.StorageSpaceId{ - OpaqueId: storagespace.FormatResourceID(*root), - }, - Root: root, - Opaque: opaque, - }, - }) - switch { - case err != nil: - return err - case resp.GetStatus().GetCode() != rpc.Code_CODE_OK: - return fmt.Errorf("could not update storage space: %s", resp.GetStatus().GetMessage()) - default: - return nil - } -} - -func uploadFolder(ctx context.Context, mdc *metadata.CS3, pathOnDisc, pathOnSpace string, opaque *v1beta1.Opaque, fsys fs.ReadDirFS) (*v1beta1.Opaque, error) { - entries, err := fsys.ReadDir(pathOnDisc) - if err != nil { - return nil, err - } - - for _, entry := range entries { - opaque, err = uploadEntry(ctx, mdc, pathOnDisc, pathOnSpace, opaque, fsys, entry) - if err != nil { - return nil, err - } - } - - return opaque, nil -} - -func uploadEntry(ctx context.Context, mdc *metadata.CS3, pathOnDisc, pathOnSpace string, opaque *v1beta1.Opaque, fsys fs.ReadDirFS, entry os.DirEntry) (*v1beta1.Opaque, error) { - spacePath := filepath.Join(pathOnSpace, entry.Name()) - discPath := filepath.Join(pathOnDisc, entry.Name()) - - switch { - case entry.IsDir(): - err := mdc.MakeDirIfNotExist(ctx, spacePath) - if err != nil { - return nil, err - } - - return uploadFolder(ctx, mdc, discPath, spacePath, opaque, fsys) - default: - b, err := fs.ReadFile(fsys, discPath) - if err != nil { - return nil, err - } - - res, err := mdc.Upload(ctx, metadata.UploadRequest{ - Path: spacePath, - Content: b, - }) - if err != nil { - return nil, err - } - - identifier := strings.TrimSuffix(entry.Name(), filepath.Ext(entry.Name())) - for _, special := range []string{ReadmeSpecialFolderName, SpaceImageSpecialFolderName} { - if special == identifier { - opaque = utils.AppendPlainToOpaque(opaque, identifier, res.FileID) - break - } - } - return opaque, nil - } -} From e0a27713cb3f1c0648854338dc9ca28c0b714ca8 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Tue, 26 Mar 2024 14:17:37 +0100 Subject: [PATCH 7/9] add random file to keep locale folder Signed-off-by: jkoberg --- services/graph/pkg/service/v0/l10n/.tx/config | 9 ++++++++ .../v0/l10n/locale/en_GB/LC_MESSAGES/graph.po | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 services/graph/pkg/service/v0/l10n/.tx/config create mode 100644 services/graph/pkg/service/v0/l10n/locale/en_GB/LC_MESSAGES/graph.po diff --git a/services/graph/pkg/service/v0/l10n/.tx/config b/services/graph/pkg/service/v0/l10n/.tx/config new file mode 100644 index 0000000000..c25695aa8a --- /dev/null +++ b/services/graph/pkg/service/v0/l10n/.tx/config @@ -0,0 +1,9 @@ +[main] +host = https://www.transifex.com + +[o:owncloud-org:p:owncloud:r:ocis-graph] +file_filter = locale//LC_MESSAGES/graph.po +minimum_perc = 75 +source_file = graph.pot +source_lang = en +type = PO diff --git a/services/graph/pkg/service/v0/l10n/locale/en_GB/LC_MESSAGES/graph.po b/services/graph/pkg/service/v0/l10n/locale/en_GB/LC_MESSAGES/graph.po new file mode 100644 index 0000000000..fbf642e9c4 --- /dev/null +++ b/services/graph/pkg/service/v0/l10n/locale/en_GB/LC_MESSAGES/graph.po @@ -0,0 +1,22 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: \n" + "Report-Msgid-Bugs-To: EMAIL\n" + "POT-Creation-Date: 2024-03-26 15:11+0100\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: \n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=CHARSET\n" + "Content-Transfer-Encoding: 8bit\n" + +#: pkg/service/v0/spacetemplates.go:29 +msgid "Here you can add a description for this Space." +msgstr "" + From b54344196ed45b00d2abfbf0f8601076c754fc7f Mon Sep 17 00:00:00 2001 From: jkoberg Date: Wed, 27 Mar 2024 10:15:13 +0100 Subject: [PATCH 8/9] move more functionality to l10n pkg Signed-off-by: jkoberg --- ocis-pkg/l10n/l10n.go | 35 ++++++++++++++ services/notifications/Makefile | 2 +- services/notifications/pkg/email/templates.go | 39 ++++++++------- services/notifications/pkg/service/service.go | 19 +------- services/userlog/Makefile | 2 +- services/userlog/pkg/service/service.go | 25 +--------- services/userlog/pkg/service/templates.go | 47 +++++++++---------- 7 files changed, 83 insertions(+), 86 deletions(-) diff --git a/ocis-pkg/l10n/l10n.go b/ocis-pkg/l10n/l10n.go index 742c86bf29..f9b89528f1 100644 --- a/ocis-pkg/l10n/l10n.go +++ b/ocis-pkg/l10n/l10n.go @@ -2,10 +2,16 @@ package l10n import ( + "context" + "errors" "io/fs" "os" "github.com/leonelquinteros/gotext" + "github.com/owncloud/ocis/v2/ocis-pkg/middleware" + settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" + "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults" + micrometadata "go-micro.dev/v4/metadata" ) // Template marks a string as translatable @@ -53,3 +59,32 @@ func (t Translator) Locale(locale string) *gotext.Locale { } return l } + +// 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 != "" { + return preferedLang + } + + locale, _ := GetUserLocale(ctx, userID, vc) + return locale +} + +// GetUserLocale returns the locale of the user +func GetUserLocale(ctx context.Context, userID string, vc settingssvc.ValueService) (string, error) { + resp, err := vc.GetValueByUniqueIdentifiers( + micrometadata.Set(ctx, middleware.AccountID, userID), + &settingssvc.GetValueByUniqueIdentifiersRequest{ + AccountUuid: userID, + SettingId: defaults.SettingUUIDProfileLanguage, + }, + ) + if err != nil { + return "", err + } + val := resp.GetValue().GetValue().GetListValue().GetValues() + if len(val) == 0 { + return "", errors.New("no language setting found") + } + return val[0].GetStringValue(), nil +} diff --git a/services/notifications/Makefile b/services/notifications/Makefile index dff7c0f58e..08f9b2820a 100644 --- a/services/notifications/Makefile +++ b/services/notifications/Makefile @@ -44,7 +44,7 @@ l10n-push: .PHONY: l10n-read l10n-read: $(GO_XGETTEXT) - go-xgettext -o $(OUTPUT_DIR)/notifications.pot --keyword=Template --add-comments -s pkg/email/templates.go + go-xgettext -o $(OUTPUT_DIR)/notifications.pot --keyword=l10n.Template --add-comments -s pkg/email/templates.go .PHONY: l10n-write l10n-write: diff --git a/services/notifications/pkg/email/templates.go b/services/notifications/pkg/email/templates.go index c90eee4444..615e8409b6 100644 --- a/services/notifications/pkg/email/templates.go +++ b/services/notifications/pkg/email/templates.go @@ -1,7 +1,6 @@ package email -// Template marks the string as a translatable template -func Template(s string) string { return s } +import "github.com/owncloud/ocis/v2/ocis-pkg/l10n" // the available templates var ( @@ -10,24 +9,24 @@ var ( textTemplate: "templates/text/email.text.tmpl", htmlTemplate: "templates/html/email.html.tmpl", // ShareCreated email template, Subject field (resolves directly) - Subject: Template(`{ShareSharer} shared '{ShareFolder}' with you`), + Subject: l10n.Template(`{ShareSharer} shared '{ShareFolder}' with you`), // ShareCreated email template, resolves via {{ .Greeting }} - Greeting: Template(`Hello {ShareGrantee}`), + Greeting: l10n.Template(`Hello {ShareGrantee}`), // ShareCreated email template, resolves via {{ .MessageBody }} - MessageBody: Template(`{ShareSharer} has shared "{ShareFolder}" with you.`), + MessageBody: l10n.Template(`{ShareSharer} has shared "{ShareFolder}" with you.`), // ShareCreated email template, resolves via {{ .CallToAction }} - CallToAction: Template(`Click here to view it: {ShareLink}`), + CallToAction: l10n.Template(`Click here to view it: {ShareLink}`), } ShareExpired = MessageTemplate{ textTemplate: "templates/text/email.text.tmpl", htmlTemplate: "templates/html/email.html.tmpl", // ShareExpired email template, Subject field (resolves directly) - Subject: Template(`Share to '{ShareFolder}' expired at {ExpiredAt}`), + Subject: l10n.Template(`Share to '{ShareFolder}' expired at {ExpiredAt}`), // ShareExpired email template, resolves via {{ .Greeting }} - Greeting: Template(`Hello {ShareGrantee},`), + Greeting: l10n.Template(`Hello {ShareGrantee},`), // ShareExpired email template, resolves via {{ .MessageBody }} - MessageBody: Template(`Your share to {ShareFolder} has expired at {ExpiredAt} + MessageBody: l10n.Template(`Your share to {ShareFolder} has expired at {ExpiredAt} Even though this share has been revoked you still might have access through other shares and/or space memberships.`), } @@ -37,39 +36,39 @@ Even though this share has been revoked you still might have access through othe textTemplate: "templates/text/email.text.tmpl", htmlTemplate: "templates/html/email.html.tmpl", // SharedSpace email template, Subject field (resolves directly) - Subject: Template("{SpaceSharer} invited you to join {SpaceName}"), + Subject: l10n.Template("{SpaceSharer} invited you to join {SpaceName}"), // SharedSpace email template, resolves via {{ .Greeting }} - Greeting: Template(`Hello {SpaceGrantee},`), + Greeting: l10n.Template(`Hello {SpaceGrantee},`), // SharedSpace email template, resolves via {{ .MessageBody }} - MessageBody: Template(`{SpaceSharer} has invited you to join "{SpaceName}".`), + MessageBody: l10n.Template(`{SpaceSharer} has invited you to join "{SpaceName}".`), // SharedSpace email template, resolves via {{ .CallToAction }} - CallToAction: Template(`Click here to view it: {ShareLink}`), + CallToAction: l10n.Template(`Click here to view it: {ShareLink}`), } UnsharedSpace = MessageTemplate{ textTemplate: "templates/text/email.text.tmpl", htmlTemplate: "templates/html/email.html.tmpl", // UnsharedSpace email template, Subject field (resolves directly) - Subject: Template(`{SpaceSharer} removed you from {SpaceName}`), + Subject: l10n.Template(`{SpaceSharer} removed you from {SpaceName}`), // UnsharedSpace email template, resolves via {{ .Greeting }} - Greeting: Template(`Hello {SpaceGrantee},`), + Greeting: l10n.Template(`Hello {SpaceGrantee},`), // UnsharedSpace email template, resolves via {{ .MessageBody }} - MessageBody: Template(`{SpaceSharer} has removed you from "{SpaceName}". + MessageBody: l10n.Template(`{SpaceSharer} has removed you from "{SpaceName}". You might still have access through your other groups or direct membership.`), // UnsharedSpace email template, resolves via {{ .CallToAction }} - CallToAction: Template(`Click here to check it: {ShareLink}`), + CallToAction: l10n.Template(`Click here to check it: {ShareLink}`), } MembershipExpired = MessageTemplate{ textTemplate: "templates/text/email.text.tmpl", htmlTemplate: "templates/html/email.html.tmpl", // MembershipExpired email template, Subject field (resolves directly) - Subject: Template(`Membership of '{SpaceName}' expired at {ExpiredAt}`), + Subject: l10n.Template(`Membership of '{SpaceName}' expired at {ExpiredAt}`), // MembershipExpired email template, resolves via {{ .Greeting }} - Greeting: Template(`Hello {SpaceGrantee},`), + Greeting: l10n.Template(`Hello {SpaceGrantee},`), // MembershipExpired email template, resolves via {{ .MessageBody }} - MessageBody: Template(`Your membership of space {SpaceName} has expired at {ExpiredAt} + MessageBody: l10n.Template(`Your membership of space {SpaceName} has expired at {ExpiredAt} Even though this membership has expired you still might have access through other shares and/or space memberships`), } diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index 8b4de702fb..d24a133563 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -18,6 +18,7 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/owncloud/ocis/v2/ocis-pkg/l10n" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -108,7 +109,7 @@ func (s eventsNotifier) render(ctx context.Context, template email.MessageTempla // Render the Email Template for each user messageList := make([]*channels.Message, len(granteeList)) for i, usr := range granteeList { - locale := s.getUserLang(ctx, usr.GetId()) + locale := l10n.MustGetUserLocale(ctx, usr.GetId().GetOpaqueId(), "", s.valueService) fields[granteeFieldName] = usr.GetDisplayName() rendered, err := email.RenderEmailTemplate(template, locale, s.defaultLanguage, s.emailTemplatePath, s.translationPath, fields) @@ -215,22 +216,6 @@ func (s eventsNotifier) getUser(ctx context.Context, u *user.UserId) (*user.User return r.GetUser(), nil } -func (s eventsNotifier) getUserLang(ctx context.Context, u *user.UserId) string { - granteeCtx := metadata.Set(ctx, middleware.AccountID, u.GetOpaqueId()) - if resp, err := s.valueService.GetValueByUniqueIdentifiers(granteeCtx, - &settingssvc.GetValueByUniqueIdentifiersRequest{ - AccountUuid: u.GetOpaqueId(), - SettingId: defaults.SettingUUIDProfileLanguage, - }, - ); err == nil { - val := resp.GetValue().GetValue().GetListValue().GetValues() - if len(val) > 0 && val[0] != nil { - return val[0].GetStringValue() - } - } - return _defaultLocale -} - func (s eventsNotifier) disableEmails(ctx context.Context, u *user.UserId) bool { granteeCtx := metadata.Set(ctx, middleware.AccountID, u.OpaqueId) if resp, err := s.valueService.GetValueByUniqueIdentifiers(granteeCtx, diff --git a/services/userlog/Makefile b/services/userlog/Makefile index eb2a175efa..4be42ab76b 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -44,7 +44,7 @@ l10n-push: .PHONY: l10n-read l10n-read: $(GO_XGETTEXT) - go-xgettext -o $(OUTPUT_DIR)/userlog.pot --keyword=Template -s pkg/service/templates.go + go-xgettext -o $(OUTPUT_DIR)/userlog.pot --keyword=l10n.Template -s pkg/service/templates.go .PHONY: l10n-write l10n-write: diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index 696055de2d..f34e3ca157 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -11,20 +11,18 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/go-chi/chi/v5" - micrometadata "go-micro.dev/v4/metadata" "go-micro.dev/v4/store" "go.opentelemetry.io/otel/trace" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/l10n" "github.com/owncloud/ocis/v2/ocis-pkg/log" - "github.com/owncloud/ocis/v2/ocis-pkg/middleware" "github.com/owncloud/ocis/v2/ocis-pkg/roles" ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0" ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" - "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults" "github.com/owncloud/ocis/v2/services/userlog/pkg/config" ) @@ -343,7 +341,7 @@ func (ul *UserlogService) sendSSE(ctx context.Context, userIDs []string, event e m := make(map[string]events.SendSSE) for _, userid := range userIDs { - loc := ul.getUserLocale(userid) + loc := l10n.MustGetUserLocale(ctx, userid, "", ul.valueClient) if ev, ok := m[loc]; ok { ev.UserIDs = append(m[loc].UserIDs, userid) m[loc] = ev @@ -455,25 +453,6 @@ func (ul *UserlogService) alterGlobalEvents(ctx context.Context, alter func(map[ }) } -func (ul *UserlogService) getUserLocale(userid string) string { - resp, err := ul.valueClient.GetValueByUniqueIdentifiers( - micrometadata.Set(context.Background(), middleware.AccountID, userid), - &settingssvc.GetValueByUniqueIdentifiersRequest{ - AccountUuid: userid, - SettingId: defaults.SettingUUIDProfileLanguage, - }, - ) - if err != nil { - ul.log.Error().Err(err).Str("userid", userid).Msg("cannot get users locale") - return "" - } - val := resp.GetValue().GetValue().GetListValue().GetValues() - if len(val) == 0 { - return "" - } - return val[0].GetStringValue() -} - func removeExecutant(users []string, executant *user.UserId) []string { var usrs []string for _, u := range users { diff --git a/services/userlog/pkg/service/templates.go b/services/userlog/pkg/service/templates.go index 6acf6fb8ca..087f39d9f3 100644 --- a/services/userlog/pkg/service/templates.go +++ b/services/userlog/pkg/service/templates.go @@ -1,63 +1,62 @@ package service -// Template marks the string as a translatable template -func Template(s string) string { return s } +import "github.com/owncloud/ocis/v2/ocis-pkg/l10n" // the available templates var ( VirusFound = NotificationTemplate{ - Subject: Template("Virus found"), - Message: Template("Virus found in {resource}. Upload not possible. Virus: {virus}"), + Subject: l10n.Template("Virus found"), + Message: l10n.Template("Virus found in {resource}. Upload not possible. Virus: {virus}"), } PoliciesEnforced = NotificationTemplate{ - Subject: Template("Policies enforced"), - Message: Template("File {resource} was deleted because it violates the policies"), + Subject: l10n.Template("Policies enforced"), + Message: l10n.Template("File {resource} was deleted because it violates the policies"), } SpaceShared = NotificationTemplate{ - Subject: Template("Space shared"), - Message: Template("{user} added you to Space {space}"), + Subject: l10n.Template("Space shared"), + Message: l10n.Template("{user} added you to Space {space}"), } SpaceUnshared = NotificationTemplate{ - Subject: Template("Removed from Space"), - Message: Template("{user} removed you from Space {space}"), + Subject: l10n.Template("Removed from Space"), + Message: l10n.Template("{user} removed you from Space {space}"), } SpaceDisabled = NotificationTemplate{ - Subject: Template("Space disabled"), - Message: Template("{user} disabled Space {space}"), + Subject: l10n.Template("Space disabled"), + Message: l10n.Template("{user} disabled Space {space}"), } SpaceDeleted = NotificationTemplate{ - Subject: Template("Space deleted"), - Message: Template("{user} deleted Space {space}"), + Subject: l10n.Template("Space deleted"), + Message: l10n.Template("{user} deleted Space {space}"), } SpaceMembershipExpired = NotificationTemplate{ - Subject: Template("Membership expired"), - Message: Template("Access to Space {space} lost"), + Subject: l10n.Template("Membership expired"), + Message: l10n.Template("Access to Space {space} lost"), } ShareCreated = NotificationTemplate{ - Subject: Template("Resource shared"), - Message: Template("{user} shared {resource} with you"), + Subject: l10n.Template("Resource shared"), + Message: l10n.Template("{user} shared {resource} with you"), } ShareRemoved = NotificationTemplate{ - Subject: Template("Resource unshared"), - Message: Template("{user} unshared {resource} with you"), + Subject: l10n.Template("Resource unshared"), + Message: l10n.Template("{user} unshared {resource} with you"), } ShareExpired = NotificationTemplate{ - Subject: Template("Share expired"), - Message: Template("Access to {resource} expired"), + Subject: l10n.Template("Share expired"), + Message: l10n.Template("Access to {resource} expired"), } PlatformDeprovision = NotificationTemplate{ - Subject: Template("Instance will be shut down and deprovisioned"), - Message: Template("Attention! The instance will be shut down and deprovisioned on {date}. Download all your data before that date as no access past that date is possible."), + Subject: l10n.Template("Instance will be shut down and deprovisioned"), + Message: l10n.Template("Attention! The instance will be shut down and deprovisioned on {date}. Download all your data before that date as no access past that date is possible."), } ) From 24b5f8547a125b9c583295b420b777c6992f63ca Mon Sep 17 00:00:00 2001 From: jkoberg Date: Wed, 27 Mar 2024 10:55:14 +0100 Subject: [PATCH 9/9] allow using custom lang for space templates Signed-off-by: jkoberg --- services/graph/pkg/config/config.go | 1 + services/graph/pkg/service/v0/drives.go | 19 +++++---- .../graph/pkg/service/v0/spacetemplates.go | 40 ++++++++++++++----- services/notifications/pkg/service/service.go | 2 - 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go index 950760825e..2198ddcacd 100644 --- a/services/graph/pkg/config/config.go +++ b/services/graph/pkg/config/config.go @@ -46,6 +46,7 @@ type Spaces struct { UsersCacheTTL int `yaml:"users_cache_ttl" env:"GRAPH_SPACES_USERS_CACHE_TTL" desc:"Max TTL in seconds for the spaces users cache." introductionVersion:"pre5.0"` GroupsCacheTTL int `yaml:"groups_cache_ttl" env:"GRAPH_SPACES_GROUPS_CACHE_TTL" desc:"Max TTL in seconds for the spaces groups cache." introductionVersion:"pre5.0"` StorageUsersAddress string `yaml:"storage_users_address" env:"GRAPH_SPACES_STORAGE_USERS_ADDRESS" desc:"The address of the storage-users service." introductionVersion:"5.0"` + DefaultLanguage string `yaml:"default_language" env:"OCIS_DEFAULT_LANGUAGE" desc:"The default language used by services and the WebUI. If not defined, English will be used as default. See the documentation for more details." introductionVersion:"5.0"` } type LDAP struct { diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index 6dbf31864b..5719e0844e 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -29,6 +29,7 @@ import ( "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/owncloud/ocis/v2/ocis-pkg/conversions" + "github.com/owncloud/ocis/v2/ocis-pkg/l10n" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -326,7 +327,10 @@ func (g Graph) canCreateSpace(ctx context.Context, ownPersonalHome bool) bool { func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { logger := g.logger.SubloggerWithRequestID(r.Context()) logger.Info().Msg("calling create drive") - us, ok := revactx.ContextGetUser(r.Context()) + + ctx := r.Context() + + us, ok := revactx.ContextGetUser(ctx) if !ok { logger.Debug().Msg("could not create drive: invalid user") errorcode.NotAllowed.Render(w, r, http.StatusUnauthorized, "invalid user") @@ -334,7 +338,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { } // TODO determine if the user tries to create his own personal space and pass that as a boolean - canCreateSpace := g.canCreateSpace(r.Context(), false) + canCreateSpace := g.canCreateSpace(ctx, false) if !canCreateSpace { logger.Debug().Bool("cancreatespace", canCreateSpace).Msg("could not create drive: insufficient permissions") // if the permission is not existing for the user in context we can assume we don't have it. Return 401. @@ -393,7 +397,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { csr.Owner = us } - resp, err := gatewayClient.CreateStorageSpace(r.Context(), &csr) + resp, err := gatewayClient.CreateStorageSpace(ctx, &csr) if err != nil { logger.Error().Err(err).Msg("could not create drive: transport error") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) @@ -424,15 +428,16 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { } space := resp.GetStorageSpace() - if t := r.URL.Query().Get("template"); t != "" && driveType == _spaceTypeProject { - if err := g.applySpaceTemplate(r.Context(), gatewayClient, resp.GetStorageSpace().GetRoot(), t); err != nil { + if t := r.URL.Query().Get(TemplateParameter); t != "" && driveType == _spaceTypeProject { + loc := l10n.MustGetUserLocale(ctx, us.GetId().GetOpaqueId(), r.Header.Get(HeaderAcceptLanguage), g.valueService) + if err := g.applySpaceTemplate(ctx, gatewayClient, space.GetRoot(), t, loc); err != nil { logger.Error().Err(err).Msg("could not apply template to space") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) return } // refetch the drive to get quota information - should we calculate this ourselves to avoid the extra call? - space, err = utils.GetSpace(r.Context(), resp.GetStorageSpace().GetId().GetOpaqueId(), gatewayClient) + space, err = utils.GetSpace(ctx, space.GetId().GetOpaqueId(), gatewayClient) if err != nil { logger.Error().Err(err).Msg("could not refetch space") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) @@ -440,7 +445,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { } } - spaces, err := g.formatDrives(r.Context(), webDavBaseURL, []*storageprovider.StorageSpace{space}, APIVersion_1) + spaces, err := g.formatDrives(ctx, webDavBaseURL, []*storageprovider.StorageSpace{space}, APIVersion_1) if err != nil { logger.Debug().Err(err).Msg("could not get drive: error parsing grpc response") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) diff --git a/services/graph/pkg/service/v0/spacetemplates.go b/services/graph/pkg/service/v0/spacetemplates.go index c664532958..b9d41438aa 100644 --- a/services/graph/pkg/service/v0/spacetemplates.go +++ b/services/graph/pkg/service/v0/spacetemplates.go @@ -24,30 +24,50 @@ var ( //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" + + // path to the image file _imagepath = "spacetemplate/image.png" + // text for the readme.md file _readmeText = l10n.Template("Here you can add a description for this Space.") + + // 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" + + // TemplateParameter is the key for the template parameter in the request + TemplateParameter = "template" ) -func (g Graph) applySpaceTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) error { +func (g Graph) applySpaceTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string, locale string) error { switch template { default: fallthrough case "none": return nil case "default": - return g.applyDefaultTemplate(ctx, gwc, root) + return g.applyDefaultTemplate(ctx, gwc, root, locale) } } -func (g Graph) applyDefaultTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId) error { +func (g Graph) applyDefaultTemplate(ctx context.Context, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, locale string) error { mdc := metadata.NewCS3(g.config.Reva.Address, g.config.Spaces.StorageUsersAddress) mdc.SpaceRoot = root var opaque *v1beta1.Opaque // create .space folder - if err := mdc.MakeDirIfNotExist(ctx, ".space"); err != nil { + if err := mdc.MakeDirIfNotExist(ctx, _spaceFolderName); err != nil { return err } @@ -59,7 +79,7 @@ func (g Graph) applyDefaultTemplate(ctx context.Context, gwc gateway.GatewayAPIC opaque = utils.AppendPlainToOpaque(opaque, SpaceImageSpecialFolderName, iid) // upload readme.md - rid, err := readmeUpload(ctx, mdc) + rid, err := readmeUpload(ctx, mdc, locale, g.config.Spaces.DefaultLanguage) if err != nil { return err } @@ -91,7 +111,7 @@ func imageUpload(ctx context.Context, mdc *metadata.CS3) (string, error) { return "", err } res, err := mdc.Upload(ctx, metadata.UploadRequest{ - Path: filepath.Join(".space", filepath.Base(_imagepath)), + Path: filepath.Join(_spaceFolderName, filepath.Base(_imagepath)), Content: b, }) if err != nil { @@ -100,11 +120,11 @@ func imageUpload(ctx context.Context, mdc *metadata.CS3) (string, error) { return res.FileID, nil } -func readmeUpload(ctx context.Context, mdc *metadata.CS3) (string, error) { - t := l10n.NewTranslatorFromCommonConfig("en", "graph", "", _localeFS, "l10n/locale") +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(".space", "readme.md"), - Content: []byte(t.Translate(_readmeText, "en_GB")), + Path: filepath.Join(_spaceFolderName, _readmeName), + Content: []byte(t.Translate(_readmeText, locale)), }) if err != nil { return "", err diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index d24a133563..3a48f788cd 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -29,8 +29,6 @@ import ( "google.golang.org/protobuf/types/known/fieldmaskpb" ) -var _defaultLocale = "en" - // Service should be named `Runner` type Service interface { Run() error