mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-22 23:33:15 -05:00
Improve Wiki TOC (#24137)
The old code has a lot of technical debts, eg: `repo/wiki/view.tmpl` / `Iterate` This PR improves the Wiki TOC display and improves the code. --------- Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
parent
f045e58cc7
commit
1ab16e48cc
12 changed files with 129 additions and 118 deletions
|
@ -34,7 +34,7 @@ func nodeToTable(meta *yaml.Node) ast.Node {
|
||||||
|
|
||||||
func mappingNodeToTable(meta *yaml.Node) ast.Node {
|
func mappingNodeToTable(meta *yaml.Node) ast.Node {
|
||||||
table := east.NewTable()
|
table := east.NewTable()
|
||||||
alignments := []east.Alignment{}
|
alignments := make([]east.Alignment, 0, len(meta.Content)/2)
|
||||||
for i := 0; i < len(meta.Content); i += 2 {
|
for i := 0; i < len(meta.Content); i += 2 {
|
||||||
alignments = append(alignments, east.AlignNone)
|
alignments = append(alignments, east.AlignNone)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,16 +34,17 @@ type ASTTransformer struct{}
|
||||||
// Transform transforms the given AST tree.
|
// Transform transforms the given AST tree.
|
||||||
func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
|
func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
|
||||||
firstChild := node.FirstChild()
|
firstChild := node.FirstChild()
|
||||||
createTOC := false
|
tocMode := ""
|
||||||
ctx := pc.Get(renderContextKey).(*markup.RenderContext)
|
ctx := pc.Get(renderContextKey).(*markup.RenderContext)
|
||||||
rc := pc.Get(renderConfigKey).(*RenderConfig)
|
rc := pc.Get(renderConfigKey).(*RenderConfig)
|
||||||
|
|
||||||
|
tocList := make([]markup.Header, 0, 20)
|
||||||
if rc.yamlNode != nil {
|
if rc.yamlNode != nil {
|
||||||
metaNode := rc.toMetaNode()
|
metaNode := rc.toMetaNode()
|
||||||
if metaNode != nil {
|
if metaNode != nil {
|
||||||
node.InsertBefore(node, firstChild, metaNode)
|
node.InsertBefore(node, firstChild, metaNode)
|
||||||
}
|
}
|
||||||
createTOC = rc.TOC
|
tocMode = rc.TOC
|
||||||
ctx.TableOfContents = make([]markup.Header, 0, 100)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attentionMarkedBlockquotes := make(container.Set[*ast.Blockquote])
|
attentionMarkedBlockquotes := make(container.Set[*ast.Blockquote])
|
||||||
|
@ -59,15 +60,15 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||||
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
|
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text := n.Text(reader.Source())
|
txt := n.Text(reader.Source())
|
||||||
header := markup.Header{
|
header := markup.Header{
|
||||||
Text: util.BytesToReadOnlyString(text),
|
Text: util.BytesToReadOnlyString(txt),
|
||||||
Level: v.Level,
|
Level: v.Level,
|
||||||
}
|
}
|
||||||
if id, found := v.AttributeString("id"); found {
|
if id, found := v.AttributeString("id"); found {
|
||||||
header.ID = util.BytesToReadOnlyString(id.([]byte))
|
header.ID = util.BytesToReadOnlyString(id.([]byte))
|
||||||
}
|
}
|
||||||
ctx.TableOfContents = append(ctx.TableOfContents, header)
|
tocList = append(tocList, header)
|
||||||
case *ast.Image:
|
case *ast.Image:
|
||||||
// Images need two things:
|
// Images need two things:
|
||||||
//
|
//
|
||||||
|
@ -201,14 +202,15 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||||
return ast.WalkContinue, nil
|
return ast.WalkContinue, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if createTOC && len(ctx.TableOfContents) > 0 {
|
showTocInMain := tocMode == "true" /* old behavior, in main view */ || tocMode == "main"
|
||||||
lang := rc.Lang
|
showTocInSidebar := !showTocInMain && tocMode != "false" // not hidden, not main, then show it in sidebar
|
||||||
if len(lang) == 0 {
|
if len(tocList) > 0 && (showTocInMain || showTocInSidebar) {
|
||||||
lang = setting.Langs[0]
|
if showTocInMain {
|
||||||
}
|
tocNode := createTOCNode(tocList, rc.Lang, nil)
|
||||||
tocNode := createTOCNode(ctx.TableOfContents, lang)
|
|
||||||
if tocNode != nil {
|
|
||||||
node.InsertBefore(node, firstChild, tocNode)
|
node.InsertBefore(node, firstChild, tocNode)
|
||||||
|
} else {
|
||||||
|
tocNode := createTOCNode(tocList, rc.Lang, map[string]string{"open": "open"})
|
||||||
|
ctx.SidebarTocNode = tocNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +375,11 @@ func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.
|
||||||
func (r *HTMLRenderer) renderDetails(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
func (r *HTMLRenderer) renderDetails(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||||
var err error
|
var err error
|
||||||
if entering {
|
if entering {
|
||||||
_, err = w.WriteString("<details>")
|
if _, err = w.WriteString("<details"); err != nil {
|
||||||
|
return ast.WalkStop, err
|
||||||
|
}
|
||||||
|
html.RenderAttributes(w, node, nil)
|
||||||
|
_, err = w.WriteString(">")
|
||||||
} else {
|
} else {
|
||||||
_, err = w.WriteString("</details>")
|
_, err = w.WriteString("</details>")
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
converter goldmark.Markdown
|
specMarkdown goldmark.Markdown
|
||||||
once = sync.Once{}
|
specMarkdownOnce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -56,7 +56,7 @@ func (l *limitWriter) Write(data []byte) (int, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
return n, fmt.Errorf("Rendered content too large - truncating render")
|
return n, fmt.Errorf("rendered content too large - truncating render")
|
||||||
}
|
}
|
||||||
n, err := l.w.Write(data)
|
n, err := l.w.Write(data)
|
||||||
l.sum += int64(n)
|
l.sum += int64(n)
|
||||||
|
@ -73,10 +73,10 @@ func newParserContext(ctx *markup.RenderContext) parser.Context {
|
||||||
return pc
|
return pc
|
||||||
}
|
}
|
||||||
|
|
||||||
// actualRender renders Markdown to HTML without handling special links.
|
// SpecializedMarkdown sets up the Gitea specific markdown extensions
|
||||||
func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
func SpecializedMarkdown() goldmark.Markdown {
|
||||||
once.Do(func() {
|
specMarkdownOnce.Do(func() {
|
||||||
converter = goldmark.New(
|
specMarkdown = goldmark.New(
|
||||||
goldmark.WithExtensions(
|
goldmark.WithExtensions(
|
||||||
extension.NewTable(
|
extension.NewTable(
|
||||||
extension.WithTableCellAlignMethod(extension.TableCellAlignAttribute)),
|
extension.WithTableCellAlignMethod(extension.TableCellAlignAttribute)),
|
||||||
|
@ -139,13 +139,18 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Override the original Tasklist renderer!
|
// Override the original Tasklist renderer!
|
||||||
converter.Renderer().AddOptions(
|
specMarkdown.Renderer().AddOptions(
|
||||||
renderer.WithNodeRenderers(
|
renderer.WithNodeRenderers(
|
||||||
util.Prioritized(NewHTMLRenderer(), 10),
|
util.Prioritized(NewHTMLRenderer(), 10),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
return specMarkdown
|
||||||
|
}
|
||||||
|
|
||||||
|
// actualRender renders Markdown to HTML without handling special links.
|
||||||
|
func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||||
|
converter := SpecializedMarkdown()
|
||||||
lw := &limitWriter{
|
lw := &limitWriter{
|
||||||
w: output,
|
w: output,
|
||||||
limit: setting.UI.MaxDisplayFileSize * 3,
|
limit: setting.UI.MaxDisplayFileSize * 3,
|
||||||
|
@ -174,7 +179,7 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
|
||||||
buf = giteautil.NormalizeEOL(buf)
|
buf = giteautil.NormalizeEOL(buf)
|
||||||
|
|
||||||
rc := &RenderConfig{
|
rc := &RenderConfig{
|
||||||
Meta: "table",
|
Meta: renderMetaModeFromString(string(ctx.RenderMetaAs)),
|
||||||
Icon: "table",
|
Icon: "table",
|
||||||
Lang: "",
|
Lang: "",
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,32 +7,42 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenderConfig represents rendering configuration for this file
|
// RenderConfig represents rendering configuration for this file
|
||||||
type RenderConfig struct {
|
type RenderConfig struct {
|
||||||
Meta string
|
Meta markup.RenderMetaMode
|
||||||
Icon string
|
Icon string
|
||||||
TOC bool
|
TOC string // "false": hide, "side"/empty: in sidebar, "main"/"true": in main view
|
||||||
Lang string
|
Lang string
|
||||||
yamlNode *yaml.Node
|
yamlNode *yaml.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func renderMetaModeFromString(s string) markup.RenderMetaMode {
|
||||||
|
switch strings.TrimSpace(strings.ToLower(s)) {
|
||||||
|
case "none":
|
||||||
|
return markup.RenderMetaAsNone
|
||||||
|
case "table":
|
||||||
|
return markup.RenderMetaAsTable
|
||||||
|
default: // "details"
|
||||||
|
return markup.RenderMetaAsDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalYAML implement yaml.v3 UnmarshalYAML
|
// UnmarshalYAML implement yaml.v3 UnmarshalYAML
|
||||||
func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||||
if rc == nil {
|
if rc == nil {
|
||||||
rc = &RenderConfig{
|
return nil
|
||||||
Meta: "table",
|
|
||||||
Icon: "table",
|
|
||||||
Lang: "",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.yamlNode = value
|
rc.yamlNode = value
|
||||||
|
|
||||||
type commonRenderConfig struct {
|
type commonRenderConfig struct {
|
||||||
TOC bool `yaml:"include_toc"`
|
TOC string `yaml:"include_toc"`
|
||||||
Lang string `yaml:"lang"`
|
Lang string `yaml:"lang"`
|
||||||
}
|
}
|
||||||
var basic commonRenderConfig
|
var basic commonRenderConfig
|
||||||
|
@ -54,58 +64,45 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
|
||||||
if err := value.Decode(&stringBasic); err == nil {
|
if err := value.Decode(&stringBasic); err == nil {
|
||||||
if stringBasic.Gitea != "" {
|
if stringBasic.Gitea != "" {
|
||||||
switch strings.TrimSpace(strings.ToLower(stringBasic.Gitea)) {
|
rc.Meta = renderMetaModeFromString(stringBasic.Gitea)
|
||||||
case "none":
|
|
||||||
rc.Meta = "none"
|
|
||||||
case "table":
|
|
||||||
rc.Meta = "table"
|
|
||||||
default: // "details"
|
|
||||||
rc.Meta = "details"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type giteaControl struct {
|
type yamlRenderConfig struct {
|
||||||
Meta *string `yaml:"meta"`
|
Meta *string `yaml:"meta"`
|
||||||
Icon *string `yaml:"details_icon"`
|
Icon *string `yaml:"details_icon"`
|
||||||
TOC *bool `yaml:"include_toc"`
|
TOC *string `yaml:"include_toc"`
|
||||||
Lang *string `yaml:"lang"`
|
Lang *string `yaml:"lang"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type complexGiteaConfig struct {
|
type yamlRenderConfigWrapper struct {
|
||||||
Gitea *giteaControl `yaml:"gitea"`
|
Gitea *yamlRenderConfig `yaml:"gitea"`
|
||||||
}
|
|
||||||
var complex complexGiteaConfig
|
|
||||||
if err := value.Decode(&complex); err != nil {
|
|
||||||
return fmt.Errorf("unable to decode into complexRenderConfig %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if complex.Gitea == nil {
|
var cfg yamlRenderConfigWrapper
|
||||||
|
if err := value.Decode(&cfg); err != nil {
|
||||||
|
return fmt.Errorf("unable to decode into yamlRenderConfigWrapper %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Gitea == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if complex.Gitea.Meta != nil {
|
if cfg.Gitea.Meta != nil {
|
||||||
switch strings.TrimSpace(strings.ToLower(*complex.Gitea.Meta)) {
|
rc.Meta = renderMetaModeFromString(*cfg.Gitea.Meta)
|
||||||
case "none":
|
|
||||||
rc.Meta = "none"
|
|
||||||
case "table":
|
|
||||||
rc.Meta = "table"
|
|
||||||
default: // "details"
|
|
||||||
rc.Meta = "details"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if complex.Gitea.Icon != nil {
|
if cfg.Gitea.Icon != nil {
|
||||||
rc.Icon = strings.TrimSpace(strings.ToLower(*complex.Gitea.Icon))
|
rc.Icon = strings.TrimSpace(strings.ToLower(*cfg.Gitea.Icon))
|
||||||
}
|
}
|
||||||
|
|
||||||
if complex.Gitea.Lang != nil && *complex.Gitea.Lang != "" {
|
if cfg.Gitea.Lang != nil && *cfg.Gitea.Lang != "" {
|
||||||
rc.Lang = *complex.Gitea.Lang
|
rc.Lang = *cfg.Gitea.Lang
|
||||||
}
|
}
|
||||||
|
|
||||||
if complex.Gitea.TOC != nil {
|
if cfg.Gitea.TOC != nil {
|
||||||
rc.TOC = *complex.Gitea.TOC
|
rc.TOC = *cfg.Gitea.TOC
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -116,9 +113,9 @@ func (rc *RenderConfig) toMetaNode() ast.Node {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch rc.Meta {
|
switch rc.Meta {
|
||||||
case "table":
|
case markup.RenderMetaAsTable:
|
||||||
return nodeToTable(rc.yamlNode)
|
return nodeToTable(rc.yamlNode)
|
||||||
case "details":
|
case markup.RenderMetaAsDetails:
|
||||||
return nodeToDetails(rc.yamlNode, rc.Icon)
|
return nodeToDetails(rc.yamlNode, rc.Icon)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -60,7 +60,7 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"toc", &RenderConfig{
|
"toc", &RenderConfig{
|
||||||
TOC: true,
|
TOC: "true",
|
||||||
Meta: "table",
|
Meta: "table",
|
||||||
Icon: "table",
|
Icon: "table",
|
||||||
Lang: "",
|
Lang: "",
|
||||||
|
@ -68,7 +68,7 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tocfalse", &RenderConfig{
|
"tocfalse", &RenderConfig{
|
||||||
TOC: false,
|
TOC: "false",
|
||||||
Meta: "table",
|
Meta: "table",
|
||||||
Icon: "table",
|
Icon: "table",
|
||||||
Lang: "",
|
Lang: "",
|
||||||
|
@ -78,7 +78,7 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||||
"toclang", &RenderConfig{
|
"toclang", &RenderConfig{
|
||||||
Meta: "table",
|
Meta: "table",
|
||||||
Icon: "table",
|
Icon: "table",
|
||||||
TOC: true,
|
TOC: "true",
|
||||||
Lang: "testlang",
|
Lang: "testlang",
|
||||||
}, `
|
}, `
|
||||||
include_toc: true
|
include_toc: true
|
||||||
|
@ -120,7 +120,7 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||||
"complex2", &RenderConfig{
|
"complex2", &RenderConfig{
|
||||||
Lang: "two",
|
Lang: "two",
|
||||||
Meta: "table",
|
Meta: "table",
|
||||||
TOC: true,
|
TOC: "true",
|
||||||
Icon: "smiley",
|
Icon: "smiley",
|
||||||
}, `
|
}, `
|
||||||
lang: one
|
lang: one
|
||||||
|
@ -155,7 +155,7 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||||
t.Errorf("Lang Expected %s Got %s", tt.expected.Lang, got.Lang)
|
t.Errorf("Lang Expected %s Got %s", tt.expected.Lang, got.Lang)
|
||||||
}
|
}
|
||||||
if got.TOC != tt.expected.TOC {
|
if got.TOC != tt.expected.TOC {
|
||||||
t.Errorf("TOC Expected %t Got %t", tt.expected.TOC, got.TOC)
|
t.Errorf("TOC Expected %q Got %q", tt.expected.TOC, got.TOC)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,14 @@ import (
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createTOCNode(toc []markup.Header, lang string) ast.Node {
|
func createTOCNode(toc []markup.Header, lang string, detailsAttrs map[string]string) ast.Node {
|
||||||
details := NewDetails()
|
details := NewDetails()
|
||||||
summary := NewSummary()
|
summary := NewSummary()
|
||||||
|
|
||||||
|
for k, v := range detailsAttrs {
|
||||||
|
details.SetAttributeString(k, []byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
summary.AppendChild(summary, ast.NewString([]byte(translation.NewLocale(lang).Tr("toc"))))
|
summary.AppendChild(summary, ast.NewString([]byte(translation.NewLocale(lang).Tr("toc"))))
|
||||||
details.AppendChild(details, summary)
|
details.AppendChild(details, summary)
|
||||||
ul := ast.NewList('-')
|
ul := ast.NewList('-')
|
||||||
|
@ -40,7 +44,7 @@ func createTOCNode(toc []markup.Header, lang string) ast.Node {
|
||||||
}
|
}
|
||||||
li := ast.NewListItem(currentLevel * 2)
|
li := ast.NewListItem(currentLevel * 2)
|
||||||
a := ast.NewLink()
|
a := ast.NewLink()
|
||||||
a.Destination = []byte(fmt.Sprintf("#%s", url.PathEscape(header.ID)))
|
a.Destination = []byte(fmt.Sprintf("#%s", url.QueryEscape(header.ID)))
|
||||||
a.AppendChild(a, ast.NewString([]byte(header.Text)))
|
a.AppendChild(a, ast.NewString([]byte(header.Text)))
|
||||||
li.AppendChild(li, a)
|
li.AppendChild(li, a)
|
||||||
ul.AppendChild(ul, li)
|
ul.AppendChild(ul, li)
|
||||||
|
|
|
@ -16,6 +16,16 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/yuin/goldmark/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RenderMetaMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RenderMetaAsDetails RenderMetaMode = "details" // default
|
||||||
|
RenderMetaAsNone RenderMetaMode = "none"
|
||||||
|
RenderMetaAsTable RenderMetaMode = "table"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProcessorHelper struct {
|
type ProcessorHelper struct {
|
||||||
|
@ -63,7 +73,8 @@ type RenderContext struct {
|
||||||
GitRepo *git.Repository
|
GitRepo *git.Repository
|
||||||
ShaExistCache map[string]bool
|
ShaExistCache map[string]bool
|
||||||
cancelFn func()
|
cancelFn func()
|
||||||
TableOfContents []Header
|
SidebarTocNode ast.Node
|
||||||
|
RenderMetaAs RenderMetaMode
|
||||||
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
|
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,13 +171,6 @@ func NewFuncMap() []template.FuncMap {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
"Iterate": func(arg interface{}) (items []int64) {
|
|
||||||
count, _ := util.ToInt64(arg)
|
|
||||||
for i := int64(0); i < count; i++ {
|
|
||||||
items = append(items, i)
|
|
||||||
}
|
|
||||||
return items
|
|
||||||
},
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
// setting
|
// setting
|
||||||
|
|
|
@ -143,31 +143,29 @@ func Recovery(ctx goctx.Context) func(next http.Handler) http.Handler {
|
||||||
"locale": lc,
|
"locale": lc,
|
||||||
}
|
}
|
||||||
|
|
||||||
user := context.GetContextUser(req)
|
// TODO: this recovery handler is usually called without Gitea's web context, so we shouldn't touch that context too much
|
||||||
|
// Otherwise, the 500 page may cause new panics, eg: cache.GetContextWithData, it makes the developer&users couldn't find the original panic
|
||||||
|
user := context.GetContextUser(req) // almost always nil
|
||||||
if user == nil {
|
if user == nil {
|
||||||
// Get user from session if logged in - do not attempt to sign-in
|
// Get user from session if logged in - do not attempt to sign-in
|
||||||
user = auth.SessionUser(sessionStore)
|
user = auth.SessionUser(sessionStore)
|
||||||
}
|
}
|
||||||
if user != nil {
|
|
||||||
store["IsSigned"] = true
|
|
||||||
store["SignedUser"] = user
|
|
||||||
store["SignedUserID"] = user.ID
|
|
||||||
store["SignedUserName"] = user.Name
|
|
||||||
store["IsAdmin"] = user.IsAdmin
|
|
||||||
} else {
|
|
||||||
store["SignedUserID"] = int64(0)
|
|
||||||
store["SignedUserName"] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
|
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
|
||||||
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
|
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
|
||||||
|
|
||||||
if !setting.IsProd {
|
if !setting.IsProd || (user != nil && user.IsAdmin) {
|
||||||
store["ErrorMsg"] = combinedErr
|
store["ErrorMsg"] = combinedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Error("HTML render in Recovery handler panics again: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
err = rnd.HTML(w, http.StatusInternalServerError, "status/500", templates.BaseVars().Merge(store))
|
err = rnd.HTML(w, http.StatusInternalServerError, "status/500", templates.BaseVars().Merge(store))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("%v", err)
|
log.Error("HTML render in Recovery handler fails again: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -298,7 +298,15 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
ctx.Data["footerPresent"] = false
|
ctx.Data["footerPresent"] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["toc"] = rctx.TableOfContents
|
if rctx.SidebarTocNode != nil {
|
||||||
|
sb := &strings.Builder{}
|
||||||
|
err = markdown.SpecializedMarkdown().Renderer().Render(sb, nil, rctx.SidebarTocNode)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to render wiki sidebar TOC: %v", err)
|
||||||
|
} else {
|
||||||
|
ctx.Data["sidebarTocContent"] = sb.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get commit count - wiki revisions
|
// get commit count - wiki revisions
|
||||||
commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename)
|
commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename)
|
||||||
|
|
|
@ -65,28 +65,16 @@
|
||||||
<p>{{.FormatWarning}}</p>
|
<p>{{.FormatWarning}}</p>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui gt-mt-0 {{if or .sidebarPresent .toc}}grid equal width{{end}}">
|
<div class="ui gt-mt-0 {{if or .sidebarPresent .sidebarTocContent}}grid equal width{{end}}">
|
||||||
<div class="ui {{if or .sidebarPresent .toc}}eleven wide column{{else}}gt-ml-0{{end}} segment markup wiki-content-main">
|
<div class="ui {{if or .sidebarPresent .sidebarTocContent}}eleven wide column{{else}}gt-ml-0{{end}} segment markup wiki-content-main">
|
||||||
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
|
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
|
||||||
{{.content | Safe}}
|
{{.content | Safe}}
|
||||||
</div>
|
</div>
|
||||||
{{if or .sidebarPresent .toc}}
|
{{if or .sidebarPresent .sidebarTocContent}}
|
||||||
<div class="column" style="padding-top: 0;">
|
<div class="column gt-pt-0">
|
||||||
{{if .toc}}
|
{{if .sidebarTocContent}}
|
||||||
<div class="ui segment wiki-content-toc">
|
<div class="ui segment wiki-content-toc">
|
||||||
<details open>
|
{{.sidebarTocContent | Safe}}
|
||||||
<summary>
|
|
||||||
<div class="ui header">{{.locale.Tr "toc"}}</div>
|
|
||||||
</summary>
|
|
||||||
{{$level := 0}}
|
|
||||||
{{range .toc}}
|
|
||||||
{{if lt $level .Level}}{{range Iterate (Eval .Level "-" $level)}}<ul>{{end}}{{end}}
|
|
||||||
{{if gt $level .Level}}{{range Iterate (Eval $level "-" .Level)}}</ul>{{end}}{{end}}
|
|
||||||
{{$level = .Level}}
|
|
||||||
<li><a href="#{{.ID}}">{{.Text}}</a></li>
|
|
||||||
{{end}}
|
|
||||||
{{range Iterate $level}}</ul>{{end}}
|
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .sidebarPresent}}
|
{{if .sidebarPresent}}
|
||||||
|
|
|
@ -3261,14 +3261,15 @@ td.blob-excerpt {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-content-toc > ul > li {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-content-toc ul {
|
.wiki-content-toc ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding-left: 1em;
|
padding: 5px 0 5px 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-content-toc ul ul {
|
||||||
|
border-left: 1px var(--color-secondary);
|
||||||
|
border-left-style: dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fomantic's last-child selector does not work with hidden last child */
|
/* fomantic's last-child selector does not work with hidden last child */
|
||||||
|
|
Loading…
Reference in a new issue