feat(office): add display name support for public share sessions
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run

- Updated publicOfficeSessionRequest to include a new DisplayName field.
- Modified PublicEditorConfig to accept and utilize the display name for editor configuration.
- Implemented editorLabelPath function to determine the correct file name for single-file public shares.
- Added unit tests for editor label path and build editor config functionalities.
This commit is contained in:
R3D347HR4Y 2026-06-15 11:10:14 +02:00
parent 621b0099d6
commit 71b716edba
4 changed files with 77 additions and 10 deletions

View File

@ -26,11 +26,12 @@ func (h *Handler) RegisterPublicShareRoutes(r chi.Router) {
} }
type publicOfficeSessionRequest struct { type publicOfficeSessionRequest struct {
Path string `json:"path"` Path string `json:"path"`
Mode string `json:"mode"` Mode string `json:"mode"`
Password string `json:"password"` Password string `json:"password"`
GuestID string `json:"guest_id"` GuestID string `json:"guest_id"`
GuestName string `json:"guest_name"` GuestName string `json:"guest_name"`
DisplayName string `json:"display_name"`
} }
func publicSharePassword(r *http.Request) string { func publicSharePassword(r *http.Request) string {
@ -73,7 +74,7 @@ func (h *Handler) PublicShareSession(w http.ResponseWriter, r *http.Request) {
if mode == "edit" && !nextcloud.PublicShareCanUpdate(perms) { if mode == "edit" && !nextcloud.PublicShareCanUpdate(perms) {
mode = "view" mode = "view"
} }
cfg, err := h.svc.PublicEditorConfig(r.Context(), token, req.Path, mode, password, req.GuestID, req.GuestName) cfg, err := h.svc.PublicEditorConfig(r.Context(), token, req.Path, mode, password, req.GuestID, req.GuestName, strings.TrimSpace(req.DisplayName))
if err != nil { if err != nil {
h.logger.Error("public editor config", "error", err) h.logger.Error("public editor config", "error", err)
apivalidate.WriteInternal(w, r) apivalidate.WriteInternal(w, r)

View File

@ -15,7 +15,7 @@ type PublicShareAccess struct {
Password string Password string
} }
func (s *Service) PublicEditorConfig(ctx context.Context, token, filePath, mode, password, guestID, guestName string) (map[string]any, error) { func (s *Service) PublicEditorConfig(ctx context.Context, token, filePath, mode, password, guestID, guestName, displayName string) (map[string]any, error) {
token = strings.TrimSpace(token) token = strings.TrimSpace(token)
filePath = normalizePath(filePath) filePath = normalizePath(filePath)
if token == "" || filePath == "" { if token == "" || filePath == "" {
@ -51,6 +51,7 @@ func (s *Service) PublicEditorConfig(ctx context.Context, token, filePath, mode,
config, err := buildEditorConfig(buildEditorConfigInput{ config, err := buildEditorConfig(buildEditorConfigInput{
filePath: filePath, filePath: filePath,
displayName: displayName,
mode: mode, mode: mode,
editorUserID: editorUserID, editorUserID: editorUserID,
userName: guestName, userName: guestName,

View File

@ -102,6 +102,7 @@ func (s *Service) SaveDocument(ctx context.Context, ncUser, filePath string, bod
type buildEditorConfigInput struct { type buildEditorConfigInput struct {
filePath string filePath string
displayName string
mode string mode string
editorUserID string editorUserID string
userName string userName string
@ -110,13 +111,26 @@ type buildEditorConfigInput struct {
callbackURL string callbackURL string
} }
// editorLabelPath picks the name used for OnlyOffice fileType/documentType/title.
// Single-file public shares use WebDAV path "/" — displayName carries the real filename.
func editorLabelPath(filePath, displayName string) string {
if ext := path.Ext(filePath); ext != "" {
return filePath
}
if name := strings.TrimSpace(displayName); name != "" {
return name
}
return filePath
}
func buildEditorConfig(in buildEditorConfigInput) (map[string]any, error) { func buildEditorConfig(in buildEditorConfigInput) (map[string]any, error) {
docType := documentType(in.filePath) labelPath := editorLabelPath(in.filePath, in.displayName)
docType := documentType(labelPath)
edit := in.mode == "edit" edit := in.mode == "edit"
document := map[string]any{ document := map[string]any{
"fileType": fileExt(in.filePath), "fileType": fileExt(labelPath),
"key": in.documentKey, "key": in.documentKey,
"title": path.Base(in.filePath), "title": path.Base(labelPath),
"url": in.downloadURL, "url": in.downloadURL,
"permissions": map[string]any{ "permissions": map[string]any{
"comment": true, "comment": true,

View File

@ -0,0 +1,51 @@
package office
import "testing"
func TestEditorLabelPath(t *testing.T) {
t.Parallel()
tests := []struct {
filePath string
displayName string
want string
}{
{filePath: "/docs/report.xlsx", displayName: "", want: "/docs/report.xlsx"},
{filePath: "/docs/report.xlsx", displayName: "other.docx", want: "/docs/report.xlsx"},
{filePath: "/", displayName: "testtable.xlsx", want: "testtable.xlsx"},
{filePath: "/", displayName: "", want: "/"},
}
for _, tc := range tests {
got := editorLabelPath(tc.filePath, tc.displayName)
if got != tc.want {
t.Errorf("editorLabelPath(%q, %q) = %q, want %q", tc.filePath, tc.displayName, got, tc.want)
}
}
}
func TestBuildEditorConfigSingleFileShareSpreadsheet(t *testing.T) {
t.Parallel()
cfg, err := buildEditorConfig(buildEditorConfigInput{
filePath: "/",
displayName: "testtable.xlsx",
mode: "edit",
documentKey: "key",
downloadURL: "http://example/doc",
callbackURL: "http://example/cb",
})
if err != nil {
t.Fatalf("buildEditorConfig: %v", err)
}
if cfg["documentType"] != "cell" {
t.Fatalf("documentType = %v, want cell", cfg["documentType"])
}
doc, ok := cfg["document"].(map[string]any)
if !ok {
t.Fatalf("document missing")
}
if doc["fileType"] != "xlsx" {
t.Fatalf("fileType = %v, want xlsx", doc["fileType"])
}
if doc["title"] != "testtable.xlsx" {
t.Fatalf("title = %v, want testtable.xlsx", doc["title"])
}
}