mirror of
https://github.com/kataras/iris.git
synced 2025-03-15 04:06:25 +01:00
HandleDir: customize file/dir listing page through registered view engine
Former-commit-id: 98bfd9d5a073c5bc7c2c167e2a72dd7b05bfb24a
This commit is contained in:
parent
87e08dbddc
commit
cba6351d62
|
@ -379,6 +379,7 @@ Other Improvements:
|
||||||
- Fix [#1552](https://github.com/kataras/iris/issues/1552).
|
- Fix [#1552](https://github.com/kataras/iris/issues/1552).
|
||||||
|
|
||||||
- Proper listing of root directories on `Party.HandleDir` when its `DirOptions.ShowList` was set to true.
|
- Proper listing of root directories on `Party.HandleDir` when its `DirOptions.ShowList` was set to true.
|
||||||
|
- Customize the file/directory listing page through views, see [example](https://github.com/kataras/iris/tree/master/_examples/file-server/file-server)
|
||||||
|
|
||||||
- Socket Sharding as requested at [#1544](https://github.com/kataras/iris/issues/1544). New `iris.WithSocketSharding` Configurator and `SocketSharding bool` setting.
|
- Socket Sharding as requested at [#1544](https://github.com/kataras/iris/issues/1544). New `iris.WithSocketSharding` Configurator and `SocketSharding bool` setting.
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"os"
|
"os"
|
||||||
|
@ -28,7 +27,22 @@ const (
|
||||||
func main() {
|
func main() {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
|
||||||
app.RegisterView(iris.HTML("./views", ".html"))
|
view := iris.HTML("./views", ".html")
|
||||||
|
view.AddFunc("formatBytes", func(b int64) string {
|
||||||
|
const unit = 1000
|
||||||
|
if b < unit {
|
||||||
|
return fmt.Sprintf("%d B", b)
|
||||||
|
}
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
for n := b / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f %cB",
|
||||||
|
float64(b)/float64(div), "kMGTPE"[exp])
|
||||||
|
})
|
||||||
|
app.RegisterView(view)
|
||||||
|
|
||||||
// Serve assets (e.g. javascript, css).
|
// Serve assets (e.g. javascript, css).
|
||||||
// app.HandleDir("/public", "./public")
|
// app.HandleDir("/public", "./public")
|
||||||
|
|
||||||
|
@ -44,7 +58,8 @@ func main() {
|
||||||
ShowList: true,
|
ShowList: true,
|
||||||
DirList: iris.DirListRich(iris.DirListRichOptions{
|
DirList: iris.DirListRich(iris.DirListRichOptions{
|
||||||
// Optionally, use a custom template for listing:
|
// Optionally, use a custom template for listing:
|
||||||
Tmpl: dirListRichTemplate,
|
// Tmpl: dirListRichTemplate,
|
||||||
|
TmplName: "dirlist.html",
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -107,136 +122,3 @@ func deleteFile(ctx iris.Context) {
|
||||||
|
|
||||||
ctx.Redirect("/files")
|
ctx.Redirect("/files")
|
||||||
}
|
}
|
||||||
|
|
||||||
var dirListRichTemplate = template.Must(template.New("dirlist").
|
|
||||||
Funcs(template.FuncMap{
|
|
||||||
"formatBytes": func(b int64) string {
|
|
||||||
const unit = 1000
|
|
||||||
if b < unit {
|
|
||||||
return fmt.Sprintf("%d B", b)
|
|
||||||
}
|
|
||||||
div, exp := int64(unit), 0
|
|
||||||
for n := b / unit; n >= unit; n /= unit {
|
|
||||||
div *= unit
|
|
||||||
exp++
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%.1f %cB",
|
|
||||||
float64(b)/float64(div), "kMGTPE"[exp])
|
|
||||||
},
|
|
||||||
}).Parse(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{.Title}}</title>
|
|
||||||
<style>
|
|
||||||
a {
|
|
||||||
padding: 8px 8px;
|
|
||||||
text-decoration:none;
|
|
||||||
cursor:pointer;
|
|
||||||
color: #10a2ff;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
empty-cells: show;
|
|
||||||
border: 1px solid #cbcbcb;
|
|
||||||
}
|
|
||||||
|
|
||||||
table caption {
|
|
||||||
color: #000;
|
|
||||||
font: italic 85%/1 arial, sans-serif;
|
|
||||||
padding: 1em 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td,
|
|
||||||
table th {
|
|
||||||
border-left: 1px solid #cbcbcb;
|
|
||||||
border-width: 0 0 0 1px;
|
|
||||||
font-size: inherit;
|
|
||||||
margin: 0;
|
|
||||||
overflow: visible;
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead {
|
|
||||||
background-color: #10a2ff;
|
|
||||||
color: #fff;
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-odd td {
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-bordered td {
|
|
||||||
border-bottom: 1px solid #cbcbcb;
|
|
||||||
}
|
|
||||||
.table-bordered tbody > tr:last-child > td {
|
|
||||||
border-bottom-width: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table class="table-bordered table-odd">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Size</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{ range $idx, $file := .Files }}
|
|
||||||
<tr>
|
|
||||||
<td>{{ $idx }}</td>
|
|
||||||
<td><a href="{{ $file.Path }}" title="{{ $file.ModTime }}">{{ $file.Name }}</a></td>
|
|
||||||
{{ if $file.Info.IsDir }}
|
|
||||||
<td>Dir</td>
|
|
||||||
{{ else }}
|
|
||||||
<td>{{ formatBytes $file.Info.Size }}</td>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
<td><input type="button" style="background-color:transparent;border:0px;cursor:pointer;" value="❌" onclick="deleteFile({{ $file.RelPath }})"/></td>
|
|
||||||
</tr>
|
|
||||||
{{ end }}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function deleteFile(filename) {
|
|
||||||
if (!confirm("Are you sure you want to delete "+filename+" ?")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch('/files/'+filename,
|
|
||||||
{
|
|
||||||
method: "DELETE",
|
|
||||||
// If you don't want server to prompt for username/password:
|
|
||||||
// credentials:"include",
|
|
||||||
headers: {
|
|
||||||
// "Authorization": "Basic " + btoa("myusername:mypassword")
|
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
|
||||||
},
|
|
||||||
}).
|
|
||||||
then(data => location.reload()).
|
|
||||||
catch(e => alert(e));
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body></html>
|
|
||||||
`))
|
|
||||||
|
|
121
_examples/file-server/file-server/views/dirlist.html
Normal file
121
_examples/file-server/file-server/views/dirlist.html
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{.Title}}</title>
|
||||||
|
<style>
|
||||||
|
a {
|
||||||
|
padding: 8px 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #10a2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
empty-cells: show;
|
||||||
|
border: 1px solid #cbcbcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
table caption {
|
||||||
|
color: #000;
|
||||||
|
font: italic 85%/1 arial, sans-serif;
|
||||||
|
padding: 1em 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td,
|
||||||
|
table th {
|
||||||
|
border-left: 1px solid #cbcbcb;
|
||||||
|
border-width: 0 0 0 1px;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: 0;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead {
|
||||||
|
background-color: #10a2ff;
|
||||||
|
color: #fff;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-odd td {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-bordered td {
|
||||||
|
border-bottom: 1px solid #cbcbcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-bordered tbody>tr:last-child>td {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<table class="table-bordered table-odd">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Size</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range $idx, $file := .Files }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ $idx }}</td>
|
||||||
|
<td><a href="{{ $file.Path }}" title="{{ $file.ModTime }}">{{ $file.Name }}</a></td>
|
||||||
|
{{ if $file.Info.IsDir }}
|
||||||
|
<td>Dir</td>
|
||||||
|
{{ else }}
|
||||||
|
<td>{{ formatBytes $file.Info.Size }}</td>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<td><input type="button" style="background-color:transparent;border:0px;cursor:pointer;" value="❌"
|
||||||
|
onclick="deleteFile({{ $file.RelPath }})" /></td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function deleteFile(filename) {
|
||||||
|
if (!confirm("Are you sure you want to delete " + filename + " ?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/files/' + filename,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
// If you don't want server to prompt for username/password:
|
||||||
|
// credentials:"include",
|
||||||
|
headers: {
|
||||||
|
// "Authorization": "Basic " + btoa("myusername:mypassword")
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
},
|
||||||
|
}).
|
||||||
|
then(data => location.reload()).
|
||||||
|
catch(e => alert(e));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -99,7 +99,6 @@ type (
|
||||||
// A shortcut for the `router.DirOptions`, useful when `FileServer` or `HandleDir` is being used.
|
// A shortcut for the `router.DirOptions`, useful when `FileServer` or `HandleDir` is being used.
|
||||||
DirOptions = router.DirOptions
|
DirOptions = router.DirOptions
|
||||||
// DirListRichOptions the options for the `DirListRich` helper function.
|
// DirListRichOptions the options for the `DirListRich` helper function.
|
||||||
// The Tmpl's "dirlist" template will be executed.
|
|
||||||
// A shortcut for the `router.DirListRichOptions`.
|
// A shortcut for the `router.DirListRichOptions`.
|
||||||
// Useful when `DirListRich` function is passed to `DirOptions.DirList` field.
|
// Useful when `DirListRich` function is passed to `DirOptions.DirList` field.
|
||||||
DirListRichOptions = router.DirListRichOptions
|
DirListRichOptions = router.DirListRichOptions
|
||||||
|
|
|
@ -567,9 +567,13 @@ func DirectoryExists(dir string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DirListRichOptions the options for the `DirListRich` helper function.
|
// DirListRichOptions the options for the `DirListRich` helper function.
|
||||||
// The Tmpl's "dirlist" template will be executed.
|
|
||||||
type DirListRichOptions struct {
|
type DirListRichOptions struct {
|
||||||
|
// If not nil then this template's "dirlist" is used to render the listing page.
|
||||||
Tmpl *template.Template
|
Tmpl *template.Template
|
||||||
|
// If not empty then this view file is used to render the listing page.
|
||||||
|
// The view should be registered with `Application.RegisterView`.
|
||||||
|
// E.g. "dirlist.html"
|
||||||
|
TmplName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DirListRich is a `DirListFunc` which can be passed to `DirOptions.DirList` field
|
// DirListRich is a `DirListFunc` which can be passed to `DirOptions.DirList` field
|
||||||
|
@ -580,8 +584,7 @@ func DirListRich(opts ...DirListRichOptions) DirListFunc {
|
||||||
if len(opts) > 0 {
|
if len(opts) > 0 {
|
||||||
options = opts[0]
|
options = opts[0]
|
||||||
}
|
}
|
||||||
|
if options.TmplName == "" && options.Tmpl == nil {
|
||||||
if options.Tmpl == nil {
|
|
||||||
options.Tmpl = DirListRichTemplate
|
options.Tmpl = DirListRichTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,6 +633,10 @@ func DirListRich(opts ...DirListRichOptions) DirListFunc {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.TmplName != "" {
|
||||||
|
return ctx.View(options.TmplName, pageData)
|
||||||
|
}
|
||||||
|
|
||||||
return options.Tmpl.ExecuteTemplate(ctx, "dirlist", pageData)
|
return options.Tmpl.ExecuteTemplate(ctx, "dirlist", pageData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user