diff --git a/.gitignore b/.gitignore index 46e04834..4f3845ed 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,4 @@ Caddyfile og_static/ -.vscode/ - -caddyhttp/browse/temp* \ No newline at end of file +.vscode/ \ No newline at end of file diff --git a/caddyhttp/browse/browse.go b/caddyhttp/browse/browse.go index 9a7e8e1c..b2214c9e 100644 --- a/caddyhttp/browse/browse.go +++ b/caddyhttp/browse/browse.go @@ -21,9 +21,10 @@ import ( ) const ( - sortByName = "name" - sortBySize = "size" - sortByTime = "time" + sortByName = "name" + sortByNameDirFirst = "namedirfirst" + sortBySize = "size" + sortByTime = "time" ) // Browse is an http.Handler that can show a file listing when @@ -128,6 +129,7 @@ func (fi FileInfo) HumanModTime(format string) string { // Implement sorting for Listing type byName Listing +type byNameDirFirst Listing type bySize Listing type byTime Listing @@ -137,6 +139,15 @@ func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] // Treat upper and lower case equally func (l byName) Less(i, j int) bool { + return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name) +} + +// By Name Dir First +func (l byNameDirFirst) Len() int { return len(l.Items) } +func (l byNameDirFirst) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] } + +// Treat upper and lower case equally +func (l byNameDirFirst) Less(i, j int) bool { // if both are dir or file sort normally if l.Items[i].IsDir == l.Items[j].IsDir { @@ -176,6 +187,8 @@ func (l Listing) applySort() { switch l.Sort { case sortByName: sort.Sort(sort.Reverse(byName(l))) + case sortByNameDirFirst: + sort.Sort(sort.Reverse(byNameDirFirst(l))) case sortBySize: sort.Sort(sort.Reverse(bySize(l))) case sortByTime: @@ -188,6 +201,8 @@ func (l Listing) applySort() { switch l.Sort { case sortByName: sort.Sort(byName(l)) + case sortByNameDirFirst: + sort.Sort(byNameDirFirst(l)) case sortBySize: sort.Sort(bySize(l)) case sortByTime: @@ -345,11 +360,11 @@ func (b Browse) handleSortOrder(w http.ResponseWriter, r *http.Request, scope st // If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies switch sort { case "": - sort = sortByName + sort = sortByNameDirFirst if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil { sort = sortCookie.Value } - case sortByName, sortBySize, sortByTime: + case sortByName, sortByNameDirFirst, sortBySize, sortByTime: http.SetCookie(w, &http.Cookie{Name: "sort", Value: sort, Path: scope, Secure: r.TLS != nil}) } diff --git a/caddyhttp/browse/browse_test.go b/caddyhttp/browse/browse_test.go index 2294d2cf..d3ab9730 100644 --- a/caddyhttp/browse/browse_test.go +++ b/caddyhttp/browse/browse_test.go @@ -70,6 +70,13 @@ func TestSort(t *testing.T) { t.Errorf("The listing isn't time sorted: %v", listing.Items) } + // sort by name dir first + listing.Sort = "namedirfirst" + listing.applySort() + if !sort.IsSorted(byNameDirFirst(listing)) { + t.Errorf("The listing isn't namedirfirst sorted: %v", listing.Items) + } + // reverse by name listing.Sort = "name" listing.Order = "desc" @@ -93,6 +100,14 @@ func TestSort(t *testing.T) { if !isReversed(byTime(listing)) { t.Errorf("The listing isn't reversed by time: %v", listing.Items) } + + // reverse by name dir first + listing.Sort = "namedirfirst" + listing.Order = "desc" + listing.applySort() + if !isReversed(byNameDirFirst(listing)) { + t.Errorf("The listing isn't reversed by namedirfirst: %v", listing.Items) + } } func TestBrowseHTTPMethods(t *testing.T) { @@ -257,6 +272,9 @@ func TestBrowseJson(t *testing.T) { Mode: f.Mode(), }) } + + // Test that sort=name returns correct listing. + listing := Listing{Items: fileinfos} // this listing will be used for validation inside the tests tests := []struct { @@ -269,33 +287,33 @@ func TestBrowseJson(t *testing.T) { }{ //test case 1: testing for default sort and order and without the limit parameter, default sort is by name and the default order is ascending //without the limit query entire listing will be produced - {"/", "", "", -1, false, listing.Items}, + {"/?sort=name", "", "", -1, false, listing.Items}, //test case 2: limit is set to 1, orderBy and sortBy is default - {"/?limit=1", "", "", 1, false, listing.Items[:1]}, + {"/?limit=1&sort=name", "", "", 1, false, listing.Items[:1]}, //test case 3 : if the listing request is bigger than total size of listing then it should return everything - {"/?limit=100000000", "", "", 100000000, false, listing.Items}, + {"/?limit=100000000&sort=name", "", "", 100000000, false, listing.Items}, //test case 4 : testing for negative limit - {"/?limit=-1", "", "", -1, false, listing.Items}, + {"/?limit=-1&sort=name", "", "", -1, false, listing.Items}, //test case 5 : testing with limit set to -1 and order set to descending - {"/?limit=-1&order=desc", "", "desc", -1, false, listing.Items}, + {"/?limit=-1&order=desc&sort=name", "", "desc", -1, false, listing.Items}, //test case 6 : testing with limit set to 2 and order set to descending - {"/?limit=2&order=desc", "", "desc", 2, false, listing.Items}, + {"/?limit=2&order=desc&sort=name", "", "desc", 2, false, listing.Items}, //test case 7 : testing with limit set to 3 and order set to descending - {"/?limit=3&order=desc", "", "desc", 3, false, listing.Items}, + {"/?limit=3&order=desc&sort=name", "", "desc", 3, false, listing.Items}, //test case 8 : testing with limit set to 3 and order set to ascending - {"/?limit=3&order=asc", "", "asc", 3, false, listing.Items}, + {"/?limit=3&order=asc&sort=name", "", "asc", 3, false, listing.Items}, //test case 9 : testing with limit set to 1111111 and order set to ascending - {"/?limit=1111111&order=asc", "", "asc", 1111111, false, listing.Items}, + {"/?limit=1111111&order=asc&sort=name", "", "asc", 1111111, false, listing.Items}, //test case 10 : testing with limit set to default and order set to ascending and sorting by size - {"/?order=asc&sort=size", "size", "asc", -1, false, listing.Items}, + {"/?order=asc&sort=size&sort=name", "size", "asc", -1, false, listing.Items}, //test case 11 : testing with limit set to default and order set to ascending and sorting by last modified - {"/?order=asc&sort=time", "time", "asc", -1, false, listing.Items}, + {"/?order=asc&sort=time&sort=name", "time", "asc", -1, false, listing.Items}, //test case 12 : testing with limit set to 1 and order set to ascending and sorting by last modified - {"/?order=asc&sort=time&limit=1", "time", "asc", 1, false, listing.Items}, + {"/?order=asc&sort=time&limit=1&sort=name", "time", "asc", 1, false, listing.Items}, //test case 13 : testing with limit set to -100 and order set to ascending and sorting by last modified - {"/?order=asc&sort=time&limit=-100", "time", "asc", -100, false, listing.Items}, + {"/?order=asc&sort=time&limit=-100&sort=name", "time", "asc", -100, false, listing.Items}, //test case 14 : testing with limit set to -100 and order set to ascending and sorting by size - {"/?order=asc&sort=size&limit=-100", "size", "asc", -100, false, listing.Items}, + {"/?order=asc&sort=size&limit=-100&sort=name", "size", "asc", -100, false, listing.Items}, } for i, test := range tests { diff --git a/caddyhttp/browse/setup.go b/caddyhttp/browse/setup.go index 33b34066..8a5a2fc9 100644 --- a/caddyhttp/browse/setup.go +++ b/caddyhttp/browse/setup.go @@ -342,12 +342,20 @@ footer { - {{- if and (eq .Sort "name") (ne .Order "desc")}} - Name - {{- else if and (eq .Sort "name") (ne .Order "asc")}} - Name + {{- if and (eq .Sort "namedirfirst") (ne .Order "desc")}} + Name + {{- else if and (eq .Sort "namedirfirst") (ne .Order "asc")}} + Name {{- else}} - Name + Name + {{- end}} + | + {{- if and (eq .Sort "name") (ne .Order "desc")}} + (a-z) + {{- else if and (eq .Sort "name") (ne .Order "asc")}} + (a-z) + {{- else}} + (a-z) {{- end}}