Lindenii Project Forge
Fix group path trailing slash
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileContributor: Runxi Yu <https://runxiyu.org> package main import ( "net/url" "path" "strings" ) func firstLine(s string) string { before, _, _ := strings.Cut(s, "\n") return before } func baseName(s string) string { return path.Base(s) } func pathEscape(s string) string { return url.PathEscape(s) } func queryEscape(s string) string { return url.QueryEscape(s) } func dereference[T any](p *T) T { return *p } func dereferenceOrZero[T any](p *T) T { if p != nil { return *p } var z T return z }
func minus(a, b int) int { return a - b }
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileContributor: Runxi Yu <https://runxiyu.org> package main import ( "embed" "html/template" "io/fs" "net/http" "github.com/tdewolff/minify/v2" "github.com/tdewolff/minify/v2/html" ) // We embed all source for easy AGPL compliance. // //go:embed .gitignore .gitattributes //go:embed LICENSE README.md //go:embed *.go go.mod go.sum //go:embed *.scfg //go:embed Makefile //go:embed static/* templates/* scripts/* sql/* //go:embed hookc/*.c //go:embed vendor/* var sourceFS embed.FS var sourceHandler = http.StripPrefix( "/:/source/", http.FileServer(http.FS(sourceFS)), ) //go:embed templates/* static/* hookc/hookc var resourcesFS embed.FS var templates *template.Template func loadTemplates() (err error) { minifier := minify.New() minifierOptions := html.Minifier{ TemplateDelims: [2]string{"{{", "}}"}, KeepDefaultAttrVals: true, } //exhaustruct:ignore minifier.Add("text/html", &minifierOptions) templates = template.New("templates").Funcs(template.FuncMap{ "first_line": firstLine, "base_name": baseName, "path_escape": pathEscape, "query_escape": queryEscape, "dereference_error": dereferenceOrZero[error],
"minus": minus,
}) err = fs.WalkDir(resourcesFS, "templates", func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if !d.IsDir() { content, err := fs.ReadFile(resourcesFS, path) if err != nil { return err } minified, err := minifier.Bytes("text/html", content) if err != nil { return err } _, err = templates.Parse(string(minified)) if err != nil { return err } } return nil }) return err } var staticHandler http.Handler func init() { staticFS, err := fs.Sub(resourcesFS, "static") if err != nil { panic(err) } staticHandler = http.StripPrefix("/:/static/", http.FileServer(http.FS(staticFS))) }
{{/* SPDX-License-Identifier: AGPL-3.0-only SPDX-FileContributor: Runxi Yu <https://runxiyu.org> */}} {{- define "group_path_plain" -}} {{- $p := . -}}
{{- range $i, $s := . -}}{{- $s -}}{{- if ne $i (len $p) -}}/{{- end -}}{{- end -}}
{{- range $i, $s := . -}}{{- $s -}}{{- if ne $i (minus (len $p) 1) -}}/{{- end -}}{{- end -}}
{{- end -}}
{{/* SPDX-License-Identifier: AGPL-3.0-only SPDX-FileContributor: Runxi Yu <https://runxiyu.org> */}} {{- define "group" -}} {{- $group_path := .group_path -}} <!DOCTYPE html> <html lang="en"> <head> {{- template "head_common" . -}} <title>{{- range $i, $s := .group_path -}}{{- $s -}}{{- if ne $i (len $group_path) -}}/{{- end -}}{{- end }} – {{ .global.forge_title -}}</title> </head> <body class="group"> {{- template "header" . -}} <div class="padding-wrapper">
<p>{{- range $i, $s := .group_path -}}{{- $s -}}{{- if ne $i (len $group_path) }} / {{ end -}}{{- end -}}
<p>{{- range $i, $s := .group_path -}}{{- $s -}}{{- if ne $i (minus (len $group_path) 1) }} / {{ end -}}{{- end -}}
{{- if .description -}} <p>{{- .description -}}</p> {{- end -}} {{- template "group_view" . -}} </div> {{- if .direct_access -}} <div class="padding-wrapper"> <form method="POST" enctype="application/x-www-form-urlencoded"> <table class="rounded-footed"> <thead> <tr> <th class="title-row" colspan="2"> Create repo </th> </tr> </thead> <tbody> <tr> <th scope="row">Name</th> <td class="tdinput"> <input id="repo-name-input" name="repo_name" type="text" /> </td> </tr> <tr> <th scope="row">Description</th> <td class="tdinput"> <input id="repo-desc-input" name="repo_desc" type="text" /> </td> </tr> <tr> <th scope="row">Contrib</th> <td class="tdinput"> <select id="repo-contrib-input" name="repo_contrib"> <option value="public">Public</option> <option value="ssh_pubkey">SSH public key</option> <option value="federated">Federated service</option> <option value="registered_user">Registered user</option> <option value="closed">Closed</option> </select> </td> </tr> </tbody> <tfoot> <tr> <td class="th-like" colspan="2"> <div class="flex-justify"> <div class="left"> </div> <div class="right"> <input class="btn-primary" type="submit" value="Create" /> </div> </div> </td> </tr> </tfoot> </table> </form> </div> {{- end -}} <footer> {{- template "footer" . -}} </footer> </body> </html> {{- end -}}