feat: add folder-based tree sidebar to org wiki (#680)
Deploy MokoGitea / deploy (push) Successful in 3m31s
Deploy MokoGitea / deploy (push) Successful in 3m31s
Replace flat page list with hierarchical folder tree in org wiki sidebar. _Sidebar.md takes precedence when present; otherwise auto-generates collapsible folder menus up to 2 levels deep.
This commit is contained in:
+73
-25
@@ -29,6 +29,14 @@ type OrgWikiPage struct {
|
||||
SubURL string
|
||||
}
|
||||
|
||||
// OrgWikiTreeNode represents a node in the org wiki folder tree for sidebar navigation.
|
||||
type OrgWikiTreeNode struct {
|
||||
Name string
|
||||
SubURL string
|
||||
IsDir bool
|
||||
Children []*OrgWikiTreeNode
|
||||
}
|
||||
|
||||
// Wiki renders the org wiki tab.
|
||||
func Wiki(ctx *context.Context) {
|
||||
org := ctx.Org.Organization
|
||||
@@ -71,31 +79,9 @@ func Wiki(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["WikiRepoLink"] = wikiRepo.Link()
|
||||
|
||||
// Build page list from repo root.
|
||||
entries, err := commit.ListEntries()
|
||||
if err != nil {
|
||||
ctx.ServerError("ListEntries", err)
|
||||
return
|
||||
}
|
||||
pages := make([]OrgWikiPage, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if !entry.IsRegular() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if !isMarkdownFile(name) {
|
||||
continue
|
||||
}
|
||||
displayName := strings.TrimSuffix(name, path.Ext(name))
|
||||
if strings.EqualFold(displayName, "_sidebar") || strings.EqualFold(displayName, "_footer") {
|
||||
continue
|
||||
}
|
||||
pages = append(pages, OrgWikiPage{
|
||||
Name: displayName,
|
||||
SubURL: displayName,
|
||||
})
|
||||
}
|
||||
ctx.Data["Pages"] = pages
|
||||
// Build folder tree for sidebar navigation.
|
||||
wikiTree := buildOrgWikiTree(commit)
|
||||
ctx.Data["WikiTree"] = wikiTree
|
||||
|
||||
// Determine which page to render.
|
||||
pageName := ctx.PathParamRaw("*")
|
||||
@@ -157,6 +143,68 @@ func Wiki(ctx *context.Context) {
|
||||
ctx.HTML(http.StatusOK, tplOrgWiki)
|
||||
}
|
||||
|
||||
// buildOrgWikiTree builds a hierarchical folder tree from the org wiki git repo.
|
||||
// Shows up to 2 levels deep (folders and their immediate children).
|
||||
func buildOrgWikiTree(commit *git.Commit) []*OrgWikiTreeNode {
|
||||
if commit == nil {
|
||||
return nil
|
||||
}
|
||||
entries, err := commit.ListEntries()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var topLevel []*OrgWikiTreeNode
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if entry.IsDir() {
|
||||
node := &OrgWikiTreeNode{
|
||||
Name: name,
|
||||
SubURL: name,
|
||||
IsDir: true,
|
||||
}
|
||||
// List children of this directory (1 level deep).
|
||||
subTree := entry.Tree()
|
||||
if subTree != nil {
|
||||
children, _ := subTree.ListEntries()
|
||||
for _, child := range children {
|
||||
childName := child.Name()
|
||||
if child.IsDir() {
|
||||
node.Children = append(node.Children, &OrgWikiTreeNode{
|
||||
Name: childName,
|
||||
SubURL: name + "/" + childName,
|
||||
IsDir: true,
|
||||
})
|
||||
} else if isMarkdownFile(childName) {
|
||||
displayName := strings.TrimSuffix(childName, path.Ext(childName))
|
||||
if strings.EqualFold(displayName, "_sidebar") || strings.EqualFold(displayName, "_footer") {
|
||||
continue
|
||||
}
|
||||
node.Children = append(node.Children, &OrgWikiTreeNode{
|
||||
Name: displayName,
|
||||
SubURL: name + "/" + displayName,
|
||||
IsDir: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
topLevel = append(topLevel, node)
|
||||
} else if isMarkdownFile(name) {
|
||||
displayName := strings.TrimSuffix(name, path.Ext(name))
|
||||
if strings.EqualFold(displayName, "_sidebar") || strings.EqualFold(displayName, "_footer") {
|
||||
continue
|
||||
}
|
||||
topLevel = append(topLevel, &OrgWikiTreeNode{
|
||||
Name: displayName,
|
||||
SubURL: displayName,
|
||||
IsDir: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
return topLevel
|
||||
}
|
||||
|
||||
// findOrgWikiCommit locates the profile repo's wiki and returns its HEAD commit.
|
||||
// The org wiki lives in the .wiki.git sidecar of the profile repo (e.g. .mokogitea.wiki.git).
|
||||
// Tries fallback repo names (.profile, .github) if the primary doesn't exist.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
This organization doesn't have a wiki yet.
|
||||
</div>
|
||||
<p class="tw-text-center">
|
||||
Enable the wiki on the <code>.profile</code> (public) or <code>.profile-private</code> (members-only)
|
||||
Enable the wiki on the <code>.mokogitea</code> (public) or <code>.mokogitea-private</code> (members-only)
|
||||
repository to get started.
|
||||
</p>
|
||||
</div>
|
||||
@@ -47,34 +47,59 @@
|
||||
<p>The page "{{.CurrentPage}}" does not exist in this wiki.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pages}}
|
||||
{{if .WikiTree}}
|
||||
<h4>Available pages:</h4>
|
||||
<ul>
|
||||
{{range .Pages}}
|
||||
<li><a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}">{{.Name}}</a></li>
|
||||
{{range .WikiTree}}
|
||||
{{if .IsDir}}
|
||||
{{range .Children}}
|
||||
<li><a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}">{{.Name}}</a></li>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<li><a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}">{{.Name}}</a></li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="wiki-content-parts">
|
||||
<div class="render-content markup wiki-content-main {{if or .WikiSidebarHTML .Pages}}with-sidebar{{end}}">
|
||||
<div class="render-content markup wiki-content-main {{if or .WikiSidebarHTML .WikiTree}}with-sidebar{{end}}">
|
||||
{{.WikiContent}}
|
||||
</div>
|
||||
|
||||
{{if or .WikiSidebarHTML .Pages}}
|
||||
{{if or .WikiSidebarHTML .WikiTree}}
|
||||
<div class="render-content markup wiki-content-sidebar">
|
||||
{{if .WikiSidebarHTML}}
|
||||
{{.WikiSidebarHTML}}
|
||||
<div class="ui divider"></div>
|
||||
{{end}}
|
||||
{{if .Pages}}
|
||||
{{else if .WikiTree}}
|
||||
<strong>{{svg "octicon-list-unordered" 14}} Pages</strong>
|
||||
<ul class="wiki-tree-list">
|
||||
{{range .Pages}}
|
||||
{{range .WikiTree}}
|
||||
<li>
|
||||
{{svg "octicon-file" 14}}
|
||||
<a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}" {{if eq $.CurrentPage .Name}}class="active"{{end}}>{{.Name}}</a>
|
||||
{{if .IsDir}}
|
||||
<details open>
|
||||
<summary>{{svg "octicon-file-directory" 14}} <strong>{{.Name}}</strong></summary>
|
||||
{{if .Children}}
|
||||
<ul>
|
||||
{{range .Children}}
|
||||
<li>
|
||||
{{if .IsDir}}
|
||||
{{svg "octicon-file-directory" 14}}
|
||||
<a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}"><strong>{{.Name}}</strong></a>
|
||||
{{else}}
|
||||
{{svg "octicon-file" 14}}
|
||||
<a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}" {{if eq $.CurrentPage .Name}}class="active"{{end}}>{{.Name}}</a>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</details>
|
||||
{{else}}
|
||||
{{svg "octicon-file" 14}}
|
||||
<a href="{{$.Org.HomeLink}}/-/wiki/{{.SubURL}}" {{if eq $.CurrentPage .Name}}class="active"{{end}}>{{.Name}}</a>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user