Skip to content

Commit

Permalink
improve the wopi app driver
Browse files Browse the repository at this point in the history
  • Loading branch information
micbar committed Oct 4, 2022
1 parent f298394 commit 5d9efbb
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 32 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/wopi-driver-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: The wopi app driver supports more options

We now generate a folderurl that is used in the wopi protocol. It provides an endpoint to go back from the app to the containing folder in the file list. In addition to that, we now include the UI_LLCC parameter in the app-open URL.

https://github.com/cs3org/reva/pull/3290
https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/discovery#ui_llcc
40 changes: 26 additions & 14 deletions pkg/app/provider/wopi/wopi.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/cs3org/reva/v2/pkg/mime"
"github.com/cs3org/reva/v2/pkg/rhttp"
"github.com/cs3org/reva/v2/pkg/sharedconf"
"github.com/cs3org/reva/v2/pkg/storage/utils/templates"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/golang-jwt/jwt"
"github.com/mitchellh/mapstructure"
Expand All @@ -57,16 +58,18 @@ func init() {
}

type config struct {
IOPSecret string `mapstructure:"iop_secret" docs:";The IOP secret used to connect to the wopiserver."`
WopiURL string `mapstructure:"wopi_url" docs:";The wopiserver's URL."`
AppName string `mapstructure:"app_name" docs:";The App user-friendly name."`
AppIconURI string `mapstructure:"app_icon_uri" docs:";A URI to a static asset which represents the app icon."`
AppURL string `mapstructure:"app_url" docs:";The App URL."`
AppIntURL string `mapstructure:"app_int_url" docs:";The internal app URL in case of dockerized deployments. Defaults to AppURL"`
AppAPIKey string `mapstructure:"app_api_key" docs:";The API key used by the app, if applicable."`
JWTSecret string `mapstructure:"jwt_secret" docs:";The JWT secret to be used to retrieve the token TTL."`
AppDesktopOnly bool `mapstructure:"app_desktop_only" docs:"false;Specifies if the app can be opened only on desktop."`
InsecureConnections bool `mapstructure:"insecure_connections"`
IOPSecret string `mapstructure:"iop_secret" docs:";The IOP secret used to connect to the wopiserver."`
WopiURL string `mapstructure:"wopi_url" docs:";The wopiserver's URL."`
WopiFolderURLBaseURL string `mapstructure:"wopi_folder_url_base_url" docs:";The base URL to generate links to navigate back to the containing folder."`
WopiFolderURLPathTemplate string `mapstructure:"wopi_folder_url_path_template" docs:";The template to generate the folderurl path segments."`
AppName string `mapstructure:"app_name" docs:";The App user-friendly name."`
AppIconURI string `mapstructure:"app_icon_uri" docs:";A URI to a static asset which represents the app icon."`
AppURL string `mapstructure:"app_url" docs:";The App URL."`
AppIntURL string `mapstructure:"app_int_url" docs:";The internal app URL in case of dockerized deployments. Defaults to AppURL"`
AppAPIKey string `mapstructure:"app_api_key" docs:";The API key used by the app, if applicable."`
JWTSecret string `mapstructure:"jwt_secret" docs:";The JWT secret to be used to retrieve the token TTL."`
AppDesktopOnly bool `mapstructure:"app_desktop_only" docs:"false;Specifies if the app can be opened only on desktop."`
InsecureConnections bool `mapstructure:"insecure_connections"`
}

func parseConfig(m map[string]interface{}) (*config, error) {
Expand Down Expand Up @@ -140,6 +143,16 @@ func (p *wopiProvider) GetAppURL(ctx context.Context, resource *provider.Resourc
q.Add("fileid", resource.GetId().OpaqueId)
q.Add("viewmode", viewMode.String())

folderURLPath := templates.WithResourceInfo(resource, p.conf.WopiFolderURLPathTemplate)
folderURLBaseURL, err := url.Parse(p.conf.WopiFolderURLBaseURL)
if err != nil {
return nil, err
}
if folderURLPath != "" {
folderURLBaseURL.Path = path.Join(folderURLBaseURL.Path, folderURLPath)
q.Add("folderurl", folderURLBaseURL.String())
}

u, ok := ctxpkg.ContextGetUser(ctx)
if ok { // else defaults to "Guest xyz"
if u.Id.Type == userpb.UserType_USER_TYPE_LIGHTWEIGHT || u.Id.Type == userpb.UserType_USER_TYPE_FEDERATED {
Expand Down Expand Up @@ -242,10 +255,9 @@ func (p *wopiProvider) GetAppURL(ctx context.Context, resource *provider.Resourc
return nil, err
}
urlQuery := url.Query()
// we could improve this by using the UI_LLCC value from the wopi discovery url
// https://docs.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/discovery#ui_llcc
urlQuery.Set("ui", language) // OnlyOffice
urlQuery.Set("lang", language) // Collabora
urlQuery.Set("ui", language) // OnlyOffice
urlQuery.Set("lang", language) // Collabora
urlQuery.Set("UI_LLCC", language) // Office365
url.RawQuery = urlQuery.Encode()
appFullURL = url.String()
}
Expand Down
73 changes: 55 additions & 18 deletions pkg/storage/utils/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,42 @@ import (

"github.com/Masterminds/sprig"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/pkg/errors"
)

// UserData contains the template placeholders for a user.
// For example {{.Username}} or {{.Id.Idp}}
type UserData struct {
*userpb.User
Email EmailData
}
type (
UserData struct {
*userpb.User
Email EmailData
}

// SpaceData contains the templace placeholders for a space.
// For example {{.SpaceName}} {{.SpaceType}} or {{.User.Id.OpaqueId}}
type SpaceData struct {
*UserData
SpaceType string
SpaceName string
}
// SpaceData contains the templace placeholders for a space.
// For example {{.SpaceName}} {{.SpaceType}} or {{.User.Id.OpaqueId}}
SpaceData struct {
*UserData
SpaceType string
SpaceName string
}

// EmailData contains mail data
// split into local and domain part.
// It is extracted from splitting the username by @.
type EmailData struct {
Local string
Domain string
}
// EmailData contains mail data
// split into local and domain part.
// It is extracted from splitting the username by @.
EmailData struct {
Local string
Domain string
}

// ResourceData contains the ResourceInfo
// ResourceData.ResourceID is a stringified form of ResourceInfo.Id
ResourceData struct {
ResourceInfo *providerv1beta1.ResourceInfo
ResourceID string
}
)

// WithUser generates a layout based on user data.
func WithUser(u *userpb.User, tpl string) string {
Expand Down Expand Up @@ -95,6 +106,24 @@ func WithSpacePropertiesAndUser(u *userpb.User, spaceType string, spaceName stri
return b.String()
}

// WithResourceInfo renders template stings with ResourceInfo variables
func WithResourceInfo(i *providerv1beta1.ResourceInfo, tpl string) string {
tpl = clean(tpl)
data := newResourceData(i)
// compile given template tpl
t, err := template.New("tpl").Funcs(sprig.TxtFuncMap()).Parse(tpl)
if err != nil {
err := errors.Wrap(err, fmt.Sprintf("error parsing template: fileinfoandresourceid_template:%+v tpl:%s", data, tpl))
panic(err)
}
b := bytes.Buffer{}
if err := t.Execute(&b, data); err != nil {
err := errors.Wrap(err, fmt.Sprintf("error executing template: fileinfoandresourceid_template:%+v tpl:%s", data, tpl))
panic(err)
}
return b.String()
}

func newUserData(u *userpb.User) *UserData {
usernameSplit := strings.Split(u.Username, "@")
if u.Mail != "" {
Expand Down Expand Up @@ -128,6 +157,14 @@ func newSpaceData(u *userpb.User, st string, n string) *SpaceData {
return sd
}

func newResourceData(i *providerv1beta1.ResourceInfo) *ResourceData {
rd := &ResourceData{
ResourceInfo: i,
ResourceID: storagespace.FormatResourceID(*i.Id),
}
return rd
}

func clean(a string) string {
return path.Clean(a)
}

0 comments on commit 5d9efbb

Please sign in to comment.