mirror of
https://github.com/go-gitea/gitea.git
synced 2025-12-11 21:44:59 +01:00
Updated all calls of function to include the new signature The new signature calls for the repo setting for wiki formats
197 lines
6.0 KiB
Go
197 lines
6.0 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package wiki
|
|
|
|
import (
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/util"
|
|
"code.gitea.io/gitea/services/convert"
|
|
)
|
|
|
|
// To define the wiki related concepts:
|
|
// * Display Segment: the text what user see for a wiki page (aka, the title):
|
|
// - "Home Page"
|
|
// - "100% Free"
|
|
// - "2000-01-02 meeting"
|
|
// * Web Path:
|
|
// - "/wiki/Home-Page"
|
|
// - "/wiki/100%25+Free"
|
|
// - "/wiki/2000-01-02+meeting.-"
|
|
// - If a segment has a suffix "DashMarker(.-)", it means that there is no dash-space conversion for this segment.
|
|
// - If a WebPath is a "*.md" pattern, then use the unescaped value directly as GitPath, to make users can access the raw file.
|
|
// * Git Path (only space doesn't need to be escaped):
|
|
// - "/.wiki.git/Home-Page.md"
|
|
// - "/.wiki.git/100%25 Free.md"
|
|
// - "/.wiki.git/2000-01-02 meeting.-.md"
|
|
// TODO: support subdirectory in the future
|
|
//
|
|
// Although this package now has the ability to support subdirectory, but the route package doesn't:
|
|
// * Double-escaping problem: the URL "/wiki/abc%2Fdef" becomes "/wiki/abc/def" by ctx.PathParam, which is incorrect
|
|
// * This problem should have been 99% fixed, but it needs more tests.
|
|
// * The old wiki code's behavior is always using %2F, instead of subdirectory, so there are a lot of legacy "%2F" files in user wikis.
|
|
|
|
type WebPath string
|
|
|
|
var reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"}
|
|
|
|
func validateWebPath(name WebPath) error {
|
|
for _, s := range WebPathSegments(name) {
|
|
if util.SliceContainsString(reservedWikiNames, s) {
|
|
return repo_model.ErrWikiReservedName{Title: s}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func hasDashMarker(s string) bool {
|
|
return strings.HasSuffix(s, ".-")
|
|
}
|
|
|
|
func removeDashMarker(s string) string {
|
|
return strings.TrimSuffix(s, ".-")
|
|
}
|
|
|
|
func addDashMarker(s string) string {
|
|
return s + ".-"
|
|
}
|
|
|
|
func unescapeSegment(s string) (string, error) {
|
|
if hasDashMarker(s) {
|
|
s = removeDashMarker(s)
|
|
} else {
|
|
s = strings.ReplaceAll(s, "-", " ")
|
|
}
|
|
unescaped, err := url.QueryUnescape(s)
|
|
if err != nil {
|
|
return s, err // un-escaping failed, but it's still safe to return the original string, because it is only a title for end users
|
|
}
|
|
return unescaped, nil
|
|
}
|
|
|
|
func escapeSegToWeb(s string, hadDashMarker bool) string {
|
|
if hadDashMarker || strings.Contains(s, "-") || strings.HasSuffix(s, ".md") || strings.HasSuffix(s, ".org") {
|
|
s = addDashMarker(s)
|
|
} else {
|
|
s = strings.ReplaceAll(s, " ", "-")
|
|
}
|
|
s = url.QueryEscape(s)
|
|
return s
|
|
}
|
|
|
|
func WebPathSegments(s WebPath) []string {
|
|
a := strings.Split(string(s), "/")
|
|
for i := range a {
|
|
a[i], _ = unescapeSegment(a[i])
|
|
}
|
|
return a
|
|
}
|
|
|
|
func WebPathToGitPath(s WebPath, repoDefaultWikiFormat string) string {
|
|
str := string(s)
|
|
// Accept only .md or .org directly
|
|
if strings.HasSuffix(str, ".md") || strings.HasSuffix(str, ".org") {
|
|
ret, _ := url.PathUnescape(str)
|
|
return util.PathJoinRelX(ret)
|
|
}
|
|
|
|
// Prioritize repository's DefaultWikiFormat, fallback to global setting if empty
|
|
defaultWikiFormat := repoDefaultWikiFormat
|
|
if defaultWikiFormat == "" {
|
|
defaultWikiFormat = setting.Repository.DefaultWikiFormat
|
|
}
|
|
|
|
a := strings.Split(string(s), "/")
|
|
for i := range a {
|
|
shouldAddDashMarker := hasDashMarker(a[i])
|
|
a[i], _ = unescapeSegment(a[i])
|
|
a[i] = escapeSegToWeb(a[i], shouldAddDashMarker)
|
|
a[i] = strings.ReplaceAll(a[i], "%20", " ") // space is safe to be kept in git path
|
|
a[i] = strings.ReplaceAll(a[i], "+", " ")
|
|
}
|
|
basePath := strings.Join(a, "/")
|
|
|
|
// Determine extension based on format setting
|
|
if defaultWikiFormat == "org" {
|
|
return basePath + ".org"
|
|
}
|
|
// For "both" or "markdown", default to .md
|
|
return basePath + ".md"
|
|
}
|
|
|
|
func GitPathToWebPath(s string) (wp WebPath, err error) {
|
|
// Trim .md or .org suffix if present
|
|
if before, ok := strings.CutSuffix(s, ".md"); ok {
|
|
s = before
|
|
} else if before, ok := strings.CutSuffix(s, ".org"); ok {
|
|
s = before
|
|
} else {
|
|
// If it doesn't end with .md or .org, it's not a valid wiki file
|
|
return "", repo_model.ErrWikiInvalidFileName{FileName: s}
|
|
}
|
|
a := strings.Split(s, "/")
|
|
for i := range a {
|
|
shouldAddDashMarker := hasDashMarker(a[i])
|
|
if a[i], err = unescapeSegment(a[i]); err != nil {
|
|
return "", err
|
|
}
|
|
a[i] = escapeSegToWeb(a[i], shouldAddDashMarker)
|
|
}
|
|
return WebPath(strings.Join(a, "/")), nil
|
|
}
|
|
|
|
func WebPathToUserTitle(s WebPath) (dir, display string) {
|
|
dir = path.Dir(string(s))
|
|
display = path.Base(string(s))
|
|
if before, ok := strings.CutSuffix(display, ".md"); ok {
|
|
display = before
|
|
display, _ = url.PathUnescape(display)
|
|
} else if before, ok := strings.CutSuffix(display, ".org"); ok {
|
|
display = before
|
|
display, _ = url.PathUnescape(display)
|
|
}
|
|
display, _ = unescapeSegment(display)
|
|
return dir, display
|
|
}
|
|
|
|
func WebPathToURLPath(s WebPath) string {
|
|
return string(s)
|
|
}
|
|
|
|
func WebPathFromRequest(s string) WebPath {
|
|
s = util.PathJoinRelX(s)
|
|
// The old wiki code's behavior is always using %2F, instead of subdirectory.
|
|
s = strings.ReplaceAll(s, "/", "%2F")
|
|
return WebPath(s)
|
|
}
|
|
|
|
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 = strings.TrimSpace(title)
|
|
title = util.PathJoinRelX(base, escapeSegToWeb(title, false))
|
|
if title == "" || title == "." {
|
|
title = "unnamed"
|
|
}
|
|
return WebPath(title)
|
|
}
|
|
|
|
// ToWikiPageMetaData converts meta information to a WikiPageMetaData
|
|
func ToWikiPageMetaData(wikiName WebPath, lastCommit *git.Commit, repo *repo_model.Repository) *api.WikiPageMetaData {
|
|
subURL := string(wikiName)
|
|
_, title := WebPathToUserTitle(wikiName)
|
|
return &api.WikiPageMetaData{
|
|
Title: title,
|
|
HTMLURL: util.URLJoin(repo.HTMLURL(), "wiki", subURL),
|
|
SubURL: subURL,
|
|
LastCommit: convert.ToWikiCommit(lastCommit),
|
|
}
|
|
}
|