From 41c42b968e7b956236ab4fdb1d2443d170c571cf Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 15:15:57 -0500 Subject: [PATCH] fix(wiki): preserve slashes in page titles for folder creation UserTitleToWebPath now splits on / and sanitizes each segment independently, preserving the directory structure. This allows creating pages like "features/Custom-Fields" as actual nested files. --- services/wiki/wiki_path.go | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/services/wiki/wiki_path.go b/services/wiki/wiki_path.go index 648816cbcd..e3dd162851 100644 --- a/services/wiki/wiki_path.go +++ b/services/wiki/wiki_path.go @@ -151,14 +151,16 @@ func WebPathFromRequest(s string) WebPath { var multiHyphenRe = regexp.MustCompile(`-{2,}`) var nonSlugRe = regexp.MustCompile(`[^a-zA-Z0-9+.\-]`) +var nonSlugReWithSlash = regexp.MustCompile(`[^a-zA-Z0-9+.\-/]`) // sanitizeWikiTitle converts a user-provided title into a clean, URL-friendly slug. // Spaces and special characters become hyphens, consecutive hyphens collapse to one. -// Preserves: letters, digits, hyphens, plus signs (+), and dots (.) +// Preserves: letters, digits, hyphens, plus signs (+), dots (.), and slashes (/). func sanitizeWikiTitle(title string) string { title = strings.TrimSpace(title) title = strings.ReplaceAll(title, " ", "-") - title = nonSlugRe.ReplaceAllString(title, "-") + // Preserve slashes as directory separators + title = nonSlugReWithSlash.ReplaceAllString(title, "-") title = multiHyphenRe.ReplaceAllString(title, "-") title = strings.NewReplacer("-+-", "-", "+-", "-", "-+", "-").Replace(title) // clean stray plus signs title = strings.Trim(title, "-+.") @@ -166,14 +168,28 @@ func sanitizeWikiTitle(title string) string { } func UserTitleToWebPath(base, title string) WebPath { - // TODO: no support for subdirectory, because the old wiki code's behavior is always using %2F, instead of subdirectory. - // So we do not add the support for writing slashes in title at the moment. - title = sanitizeWikiTitle(title) - title = util.PathJoinRelX(base, escapeSegToWeb(title, false)) - if title == "" || title == "." { - title = "unnamed" + // MokoGitea: support subdirectories - slashes in title create folder structure. + // Split on /, sanitize each segment, rejoin. + parts := strings.Split(title, "/") + sanitized := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + p = sanitizeWikiTitle(p) + if p != "" { + sanitized = append(sanitized, escapeSegToWeb(p, false)) + } } - return WebPath(title) + result := strings.Join(sanitized, "/") + if base != "" { + result = util.PathJoinRelX(base, result) + } + if result == "" || result == "." { + result = "unnamed" + } + return WebPath(result) } // ToWikiPageMetaData converts meta information to a WikiPageMetaData -- 2.52.0