mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	Add new browse sort - namedirfirst (#1551)
* Revert "browse: sort listing by dir first (#1527)" commit 4e1229e7c9bd5643ed079f3dafa25df4e426da56. * Add new browse sort order namedirfirst. Make namedirfirst default sort
This commit is contained in:
		
							parent
							
								
									ce47cf51be
								
							
						
					
					
						commit
						464ade1da7
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -16,6 +16,4 @@ Caddyfile
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
og_static/
 | 
					og_static/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.vscode/
 | 
					.vscode/
 | 
				
			||||||
 | 
					 | 
				
			||||||
caddyhttp/browse/temp*
 | 
					 | 
				
			||||||
@ -21,9 +21,10 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	sortByName = "name"
 | 
						sortByName         = "name"
 | 
				
			||||||
	sortBySize = "size"
 | 
						sortByNameDirFirst = "namedirfirst"
 | 
				
			||||||
	sortByTime = "time"
 | 
						sortBySize         = "size"
 | 
				
			||||||
 | 
						sortByTime         = "time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Browse is an http.Handler that can show a file listing when
 | 
					// 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
 | 
					// Implement sorting for Listing
 | 
				
			||||||
type byName Listing
 | 
					type byName Listing
 | 
				
			||||||
 | 
					type byNameDirFirst Listing
 | 
				
			||||||
type bySize Listing
 | 
					type bySize Listing
 | 
				
			||||||
type byTime 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
 | 
					// Treat upper and lower case equally
 | 
				
			||||||
func (l byName) Less(i, j int) bool {
 | 
					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 both are dir or file sort normally
 | 
				
			||||||
	if l.Items[i].IsDir == l.Items[j].IsDir {
 | 
						if l.Items[i].IsDir == l.Items[j].IsDir {
 | 
				
			||||||
@ -176,6 +187,8 @@ func (l Listing) applySort() {
 | 
				
			|||||||
		switch l.Sort {
 | 
							switch l.Sort {
 | 
				
			||||||
		case sortByName:
 | 
							case sortByName:
 | 
				
			||||||
			sort.Sort(sort.Reverse(byName(l)))
 | 
								sort.Sort(sort.Reverse(byName(l)))
 | 
				
			||||||
 | 
							case sortByNameDirFirst:
 | 
				
			||||||
 | 
								sort.Sort(sort.Reverse(byNameDirFirst(l)))
 | 
				
			||||||
		case sortBySize:
 | 
							case sortBySize:
 | 
				
			||||||
			sort.Sort(sort.Reverse(bySize(l)))
 | 
								sort.Sort(sort.Reverse(bySize(l)))
 | 
				
			||||||
		case sortByTime:
 | 
							case sortByTime:
 | 
				
			||||||
@ -188,6 +201,8 @@ func (l Listing) applySort() {
 | 
				
			|||||||
		switch l.Sort {
 | 
							switch l.Sort {
 | 
				
			||||||
		case sortByName:
 | 
							case sortByName:
 | 
				
			||||||
			sort.Sort(byName(l))
 | 
								sort.Sort(byName(l))
 | 
				
			||||||
 | 
							case sortByNameDirFirst:
 | 
				
			||||||
 | 
								sort.Sort(byNameDirFirst(l))
 | 
				
			||||||
		case sortBySize:
 | 
							case sortBySize:
 | 
				
			||||||
			sort.Sort(bySize(l))
 | 
								sort.Sort(bySize(l))
 | 
				
			||||||
		case sortByTime:
 | 
							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
 | 
						// If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies
 | 
				
			||||||
	switch sort {
 | 
						switch sort {
 | 
				
			||||||
	case "":
 | 
						case "":
 | 
				
			||||||
		sort = sortByName
 | 
							sort = sortByNameDirFirst
 | 
				
			||||||
		if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil {
 | 
							if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil {
 | 
				
			||||||
			sort = sortCookie.Value
 | 
								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})
 | 
							http.SetCookie(w, &http.Cookie{Name: "sort", Value: sort, Path: scope, Secure: r.TLS != nil})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -70,6 +70,13 @@ func TestSort(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("The listing isn't time sorted: %v", listing.Items)
 | 
							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
 | 
						// reverse by name
 | 
				
			||||||
	listing.Sort = "name"
 | 
						listing.Sort = "name"
 | 
				
			||||||
	listing.Order = "desc"
 | 
						listing.Order = "desc"
 | 
				
			||||||
@ -93,6 +100,14 @@ func TestSort(t *testing.T) {
 | 
				
			|||||||
	if !isReversed(byTime(listing)) {
 | 
						if !isReversed(byTime(listing)) {
 | 
				
			||||||
		t.Errorf("The listing isn't reversed by time: %v", listing.Items)
 | 
							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) {
 | 
					func TestBrowseHTTPMethods(t *testing.T) {
 | 
				
			||||||
@ -257,6 +272,9 @@ func TestBrowseJson(t *testing.T) {
 | 
				
			|||||||
			Mode:    f.Mode(),
 | 
								Mode:    f.Mode(),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test that sort=name returns correct listing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	listing := Listing{Items: fileinfos} // this listing will be used for validation inside the tests
 | 
						listing := Listing{Items: fileinfos} // this listing will be used for validation inside the tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tests := []struct {
 | 
						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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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
 | 
							//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 {
 | 
						for i, test := range tests {
 | 
				
			||||||
 | 
				
			|||||||
@ -342,12 +342,20 @@ footer {
 | 
				
			|||||||
					<thead>
 | 
										<thead>
 | 
				
			||||||
					<tr>
 | 
										<tr>
 | 
				
			||||||
						<th>
 | 
											<th>
 | 
				
			||||||
							{{- if and (eq .Sort "name") (ne .Order "desc")}}
 | 
												{{- if and (eq .Sort "namedirfirst") (ne .Order "desc")}}
 | 
				
			||||||
							<a href="?sort=name&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
 | 
												<a href="?sort=namedirfirst&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
 | 
				
			||||||
							{{- else if and (eq .Sort "name") (ne .Order "asc")}}
 | 
												{{- else if and (eq .Sort "namedirfirst") (ne .Order "asc")}}
 | 
				
			||||||
							<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
 | 
												<a href="?sort=namedirfirst&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
 | 
				
			||||||
							{{- else}}
 | 
												{{- else}}
 | 
				
			||||||
							<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name</a>
 | 
												<a href="?sort=namedirfirst&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name</a>
 | 
				
			||||||
 | 
												{{- end}}
 | 
				
			||||||
 | 
												|
 | 
				
			||||||
 | 
												{{- if and (eq .Sort "name") (ne .Order "desc")}}
 | 
				
			||||||
 | 
												<a href="?sort=name&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">(a-z) <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
 | 
				
			||||||
 | 
												{{- else if and (eq .Sort "name") (ne .Order "asc")}}
 | 
				
			||||||
 | 
												<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">(a-z) <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
 | 
				
			||||||
 | 
												{{- else}}
 | 
				
			||||||
 | 
												<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">(a-z)</a>
 | 
				
			||||||
							{{- end}}
 | 
												{{- end}}
 | 
				
			||||||
						</th>
 | 
											</th>
 | 
				
			||||||
						<th>
 | 
											<th>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user