This moves app config to a "Settings" page and the application monitor to a "Monitor" page. It also reworks the admin navigation bar a bit and adds some instance stats on the dashboard. Ref T694admin-dashboard-redesign
@@ -1,5 +1,5 @@ | |||
/* | |||
* Copyright © 2018-2019 A Bunch Tell LLC. | |||
* Copyright © 2018-2020 A Bunch Tell LLC. | |||
* | |||
* This file is part of WriteFreely. | |||
* | |||
@@ -100,6 +100,33 @@ func (c instanceContent) UpdatedFriendly() string { | |||
} | |||
func handleViewAdminDash(app *App, u *User, w http.ResponseWriter, r *http.Request) error { | |||
p := struct { | |||
*UserPage | |||
Message string | |||
UsersCount, CollectionsCount, PostsCount int64 | |||
}{ | |||
UserPage: NewUserPage(app, r, u, "Admin", nil), | |||
Message: r.FormValue("m"), | |||
} | |||
// Get user stats | |||
p.UsersCount = app.db.GetAllUsersCount() | |||
var err error | |||
p.CollectionsCount, err = app.db.GetTotalCollections() | |||
if err != nil { | |||
return err | |||
} | |||
p.PostsCount, err = app.db.GetTotalPosts() | |||
if err != nil { | |||
return err | |||
} | |||
showUserPage(w, "admin", p) | |||
return nil | |||
} | |||
func handleViewAdminMonitor(app *App, u *User, w http.ResponseWriter, r *http.Request) error { | |||
updateAppStats() | |||
p := struct { | |||
*UserPage | |||
@@ -116,7 +143,25 @@ func handleViewAdminDash(app *App, u *User, w http.ResponseWriter, r *http.Reque | |||
ConfigMessage: r.FormValue("cm"), | |||
} | |||
showUserPage(w, "admin", p) | |||
showUserPage(w, "monitor", p) | |||
return nil | |||
} | |||
func handleViewAdminSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) error { | |||
p := struct { | |||
*UserPage | |||
Config config.AppCfg | |||
Message, ConfigMessage string | |||
}{ | |||
UserPage: NewUserPage(app, r, u, "Admin", nil), | |||
Config: app.cfg.App, | |||
Message: r.FormValue("m"), | |||
ConfigMessage: r.FormValue("cm"), | |||
} | |||
showUserPage(w, "app-settings", p) | |||
return nil | |||
} | |||
@@ -475,7 +520,7 @@ func handleAdminUpdateConfig(apper Apper, u *User, w http.ResponseWriter, r *htt | |||
if err != nil { | |||
m = "?cm=" + err.Error() | |||
} | |||
return impart.HTTPError{http.StatusFound, "/admin" + m + "#config"} | |||
return impart.HTTPError{http.StatusFound, "/admin/settings" + m + "#config"} | |||
} | |||
func updateAppStats() { | |||
@@ -13,11 +13,11 @@ nav#admin { | |||
display: block; | |||
margin: 0.5em 0; | |||
a { | |||
color: @primary; | |||
&:first-child { | |||
margin-left: 0; | |||
} | |||
margin-left: 0; | |||
.rounded(.25em); | |||
border: 0; | |||
&.selected { | |||
background: #dedede; | |||
font-weight: bold; | |||
} | |||
} | |||
@@ -152,6 +152,8 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { | |||
write.HandleFunc("/auth/login", handler.Web(webLogin, UserLevelNoneRequired)).Methods("POST") | |||
write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET") | |||
write.HandleFunc("/admin/monitor", handler.Admin(handleViewAdminMonitor)).Methods("GET") | |||
write.HandleFunc("/admin/settings", handler.Admin(handleViewAdminSettings)).Methods("GET") | |||
write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET") | |||
write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET") | |||
write.HandleFunc("/admin/user/{username}/status", handler.Admin(handleAdminToggleUserStatus)).Methods("POST") | |||
@@ -35,6 +35,14 @@ form dt { | |||
p.docs { | |||
font-size: 0.86em; | |||
} | |||
.stats { | |||
font-size: 1.2em; | |||
margin: 1em 0; | |||
} | |||
.num { | |||
font-weight: bold; | |||
font-size: 1.5em; | |||
} | |||
</style> | |||
<div class="content-container snug"> | |||
@@ -42,142 +50,12 @@ p.docs { | |||
{{if .Message}}<p>{{.Message}}</p>{{end}} | |||
<h2>On this page</h2> | |||
<ul class="pagenav"> | |||
<li><a href="#config">Configuration</a></li> | |||
<li><a href="#monitor">Application monitor</a></li> | |||
</ul> | |||
<h2>Resources</h2> | |||
<ul class="pagenav"> | |||
<li><a href="https://writefreely.org/docs/{{.OfficialVersion}}/admin">Admin Guide</a></li> | |||
</ul> | |||
<hr /> | |||
<h2><a name="config"></a>App Configuration</h2> | |||
<p class="docs">Read more in the <a href="https://writefreely.org/docs/{{.OfficialVersion}}/admin/config">configuration docs</a>.</p> | |||
{{if .ConfigMessage}}<p class="success" style="text-align: center">{{.ConfigMessage}}</p>{{end}} | |||
<form action="/admin/update/config" method="post"> | |||
<div class="ui attached table segment"> | |||
<dl class="dl-horizontal admin-dl-horizontal"> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}>Site Name</dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="text" name="site_name" id="site_name" class="inline" value="{{.Config.SiteName}}" style="width: 14em;" /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}>Site Description</dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="text" name="site_desc" id="site_desc" class="inline" value="{{.Config.SiteDesc}}" style="width: 14em;" /></dd> | |||
<dt>Host</dt> | |||
<dd>{{.Config.Host}}</dd> | |||
<dt>User Mode</dt> | |||
<dd>{{if .Config.SingleUser}}Single user{{else}}Multiple users{{end}}</dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}>Landing Page</dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="text" name="landing" id="landing" class="inline" value="{{.Config.Landing}}" style="width: 14em;" /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="open_registration">Open Registrations</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="checkbox" name="open_registration" id="open_registration" {{if .Config.OpenRegistration}}checked="checked"{{end}} /></dd> | |||
<dt><label for="min_username_len">Minimum Username Length</label></dt> | |||
<dd><input type="number" name="min_username_len" id="min_username_len" class="inline" min="1" max="100" value="{{.Config.MinUsernameLen}}" /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="max_blogs">Maximum Blogs per User</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="number" name="max_blogs" id="max_blogs" class="inline" min="1" value="{{.Config.MaxBlogs}}" /></dd> | |||
<dt><label for="federation">Federation</label></dt> | |||
<dd><input type="checkbox" name="federation" id="federation" {{if .Config.Federation}}checked="checked"{{end}} /></dd> | |||
<dt><label for="public_stats">Public Stats</label></dt> | |||
<dd><input type="checkbox" name="public_stats" id="public_stats" {{if .Config.PublicStats}}checked="checked"{{end}} /></dd> | |||
<dt><label for="private">Private Instance</label></dt> | |||
<dd><input type="checkbox" name="private" id="private" {{if .Config.Private}}checked="checked"{{end}} /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="local_timeline">Local Timeline</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="checkbox" name="local_timeline" id="local_timeline" {{if .Config.LocalTimeline}}checked="checked"{{end}} /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="user_invites">Allow sending invitations by</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}> | |||
<select name="user_invites" id="user_invites"> | |||
<option value="none" {{if eq .Config.UserInvites ""}}selected="selected"{{end}}>No one</option> | |||
<option value="user" {{if eq .Config.UserInvites "user"}}selected="selected"{{end}}>Users</option> | |||
<option value="admin" {{if eq .Config.UserInvites "admin"}}selected="selected"{{end}}>Admins</option> | |||
</select> | |||
</dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="default_visibility">Default blog visibility</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}> | |||
<select name="default_visibility" id="default_visibility"> | |||
<option value="unlisted" {{if eq .Config.DefaultVisibility "unlisted"}}selected="selected"{{end}}>Unlisted</option> | |||
<option value="public" {{if eq .Config.DefaultVisibility "public"}}selected="selected"{{end}}>Public</option> | |||
<option value="private" {{if eq .Config.DefaultVisibility "private"}}selected="selected"{{end}}>Private</option> | |||
</select> | |||
</dd> | |||
</dl> | |||
<input type="submit" value="Save Configuration" /> | |||
<div class="row stats"> | |||
<div><span class="num">{{largeNumFmt .UsersCount}}</span> {{pluralize "user" "users" .UsersCount}}</div> | |||
<div><span class="num">{{largeNumFmt .CollectionsCount}}</span> {{pluralize "blog" "blogs" .CollectionsCount}}</div> | |||
<div><span class="num">{{largeNumFmt .PostsCount}}</span> {{pluralize "post" "posts" .PostsCount}}</div> | |||
</div> | |||
</form> | |||
<hr /> | |||
<h2><a name="monitor"></a>Application</h2> | |||
<div class="ui attached table segment"> | |||
<dl class="dl-horizontal admin-dl-horizontal"> | |||
<dt>WriteFreely</dt> | |||
<dd>{{.Version}}</dd> | |||
<dt>Server Uptime</dt> | |||
<dd>{{.SysStatus.Uptime}}</dd> | |||
<dt>Current Goroutines</dt> | |||
<dd>{{.SysStatus.NumGoroutine}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Current memory usage</dt> | |||
<dd>{{.SysStatus.MemAllocated}}</dd> | |||
<dt>Total mem allocated</dt> | |||
<dd>{{.SysStatus.MemTotal}}</dd> | |||
<dt>Memory obtained</dt> | |||
<dd>{{.SysStatus.MemSys}}</dd> | |||
<dt>Pointer lookup times</dt> | |||
<dd>{{.SysStatus.Lookups}}</dd> | |||
<dt>Memory allocate times</dt> | |||
<dd>{{.SysStatus.MemMallocs}}</dd> | |||
<dt>Memory free times</dt> | |||
<dd>{{.SysStatus.MemFrees}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Current heap usage</dt> | |||
<dd>{{.SysStatus.HeapAlloc}}</dd> | |||
<dt>Heap memory obtained</dt> | |||
<dd>{{.SysStatus.HeapSys}}</dd> | |||
<dt>Heap memory idle</dt> | |||
<dd>{{.SysStatus.HeapIdle}}</dd> | |||
<dt>Heap memory in use</dt> | |||
<dd>{{.SysStatus.HeapInuse}}</dd> | |||
<dt>Heap memory released</dt> | |||
<dd>{{.SysStatus.HeapReleased}}</dd> | |||
<dt>Heap objects</dt> | |||
<dd>{{.SysStatus.HeapObjects}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Bootstrap stack usage</dt> | |||
<dd>{{.SysStatus.StackInuse}}</dd> | |||
<dt>Stack memory obtained</dt> | |||
<dd>{{.SysStatus.StackSys}}</dd> | |||
<dt>MSpan structures in use</dt> | |||
<dd>{{.SysStatus.MSpanInuse}}</dd> | |||
<dt>MSpan structures obtained</dt> | |||
<dd>{{.SysStatus.HeapSys}}</dd> | |||
<dt>MCache structures in use</dt> | |||
<dd>{{.SysStatus.MCacheInuse}}</dd> | |||
<dt>MCache structures obtained</dt> | |||
<dd>{{.SysStatus.MCacheSys}}</dd> | |||
<dt>Profiling bucket hash table obtained</dt> | |||
<dd>{{.SysStatus.BuckHashSys}}</dd> | |||
<dt>GC metadata obtained</dt> | |||
<dd>{{.SysStatus.GCSys}}</dd> | |||
<dt>Other system allocation obtained</dt> | |||
<dd>{{.SysStatus.OtherSys}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Next GC recycle</dt> | |||
<dd>{{.SysStatus.NextGC}}</dd> | |||
<dt>Since last GC</dt> | |||
<dd>{{.SysStatus.LastGC}}</dd> | |||
<dt>Total GC pause</dt> | |||
<dd>{{.SysStatus.PauseTotalNs}}</dd> | |||
<dt>Last GC pause</dt> | |||
<dd>{{.SysStatus.PauseNs}}</dd> | |||
<dt>GC times</dt> | |||
<dd>{{.SysStatus.NumGC}}</dd> | |||
</dl> | |||
</div> | |||
</div> | |||
<script> | |||
@@ -0,0 +1,85 @@ | |||
{{define "app-settings"}} | |||
{{template "header" .}} | |||
<style type="text/css"> | |||
h2 {font-weight: normal;} | |||
form { | |||
margin: 0 0 2em; | |||
} | |||
form dt { | |||
line-height: inherit; | |||
} | |||
.invisible { | |||
display: none; | |||
} | |||
p.docs { | |||
font-size: 0.86em; | |||
} | |||
</style> | |||
<div class="content-container snug"> | |||
{{template "admin-header" .}} | |||
{{if .Message}}<p><a name="config"></a>{{.Message}}</p>{{end}} | |||
{{if .ConfigMessage}}<p class="success" style="text-align: center">{{.ConfigMessage}}</p>{{end}} | |||
<p class="docs">Read more in the <a href="https://writefreely.org/docs/{{.OfficialVersion}}/admin/config">configuration docs</a>.</p> | |||
<form action="/admin/update/config" method="post"> | |||
<div class="ui attached table segment"> | |||
<dl class="dl-horizontal admin-dl-horizontal"> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}>Site Name</dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="text" name="site_name" id="site_name" class="inline" value="{{.Config.SiteName}}" style="width: 14em;" /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}>Site Description</dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="text" name="site_desc" id="site_desc" class="inline" value="{{.Config.SiteDesc}}" style="width: 14em;" /></dd> | |||
<dt>Host</dt> | |||
<dd>{{.Config.Host}}</dd> | |||
<dt>User Mode</dt> | |||
<dd>{{if .Config.SingleUser}}Single user{{else}}Multiple users{{end}}</dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}>Landing Page</dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="text" name="landing" id="landing" class="inline" value="{{.Config.Landing}}" style="width: 14em;" /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="open_registration">Open Registrations</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="checkbox" name="open_registration" id="open_registration" {{if .Config.OpenRegistration}}checked="checked"{{end}} /></dd> | |||
<dt><label for="min_username_len">Minimum Username Length</label></dt> | |||
<dd><input type="number" name="min_username_len" id="min_username_len" class="inline" min="1" max="100" value="{{.Config.MinUsernameLen}}" /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="max_blogs">Maximum Blogs per User</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="number" name="max_blogs" id="max_blogs" class="inline" min="1" value="{{.Config.MaxBlogs}}" /></dd> | |||
<dt><label for="federation">Federation</label></dt> | |||
<dd><input type="checkbox" name="federation" id="federation" {{if .Config.Federation}}checked="checked"{{end}} /></dd> | |||
<dt><label for="public_stats">Public Stats</label></dt> | |||
<dd><input type="checkbox" name="public_stats" id="public_stats" {{if .Config.PublicStats}}checked="checked"{{end}} /></dd> | |||
<dt><label for="private">Private Instance</label></dt> | |||
<dd><input type="checkbox" name="private" id="private" {{if .Config.Private}}checked="checked"{{end}} /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="local_timeline">Local Timeline</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}><input type="checkbox" name="local_timeline" id="local_timeline" {{if .Config.LocalTimeline}}checked="checked"{{end}} /></dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="user_invites">Allow sending invitations by</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}> | |||
<select name="user_invites" id="user_invites"> | |||
<option value="none" {{if eq .Config.UserInvites ""}}selected="selected"{{end}}>No one</option> | |||
<option value="user" {{if eq .Config.UserInvites "user"}}selected="selected"{{end}}>Users</option> | |||
<option value="admin" {{if eq .Config.UserInvites "admin"}}selected="selected"{{end}}>Admins</option> | |||
</select> | |||
</dd> | |||
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="default_visibility">Default blog visibility</label></dt> | |||
<dd{{if .Config.SingleUser}} class="invisible"{{end}}> | |||
<select name="default_visibility" id="default_visibility"> | |||
<option value="unlisted" {{if eq .Config.DefaultVisibility "unlisted"}}selected="selected"{{end}}>Unlisted</option> | |||
<option value="public" {{if eq .Config.DefaultVisibility "public"}}selected="selected"{{end}}>Public</option> | |||
<option value="private" {{if eq .Config.DefaultVisibility "private"}}selected="selected"{{end}}>Private</option> | |||
</select> | |||
</dd> | |||
</dl> | |||
<input type="submit" value="Save Settings" /> | |||
</div> | |||
</form> | |||
</div> | |||
<script> | |||
history.replaceState(null, "", "/admin/settings"+window.location.hash); | |||
</script> | |||
{{template "footer" .}} | |||
{{template "body-end" .}} | |||
{{end}} |
@@ -0,0 +1,105 @@ | |||
{{define "monitor"}} | |||
{{template "header" .}} | |||
<style type="text/css"> | |||
h2 {font-weight: normal;} | |||
.ui.divider:not(.vertical):not(.horizontal) { | |||
border-top: 1px solid rgba(34,36,38,.15); | |||
border-bottom: 1px solid rgba(255,255,255,.1); | |||
} | |||
.ui.divider { | |||
margin: 1rem 0; | |||
line-height: 1; | |||
height: 0; | |||
font-weight: 700; | |||
text-transform: uppercase; | |||
letter-spacing: .05em; | |||
color: rgba(0,0,0,.85); | |||
-webkit-user-select: none; | |||
-moz-user-select: none; | |||
-ms-user-select: none; | |||
user-select: none; | |||
-webkit-tap-highlight-color: transparent; | |||
font-size: 1rem; | |||
} | |||
</style> | |||
<div class="content-container snug"> | |||
{{template "admin-header" .}} | |||
{{if .Message}}<p>{{.Message}}</p>{{end}} | |||
<h2><a name="monitor"></a>Application Monitor</h2> | |||
<div class="ui attached table segment"> | |||
<dl class="dl-horizontal admin-dl-horizontal"> | |||
<dt>WriteFreely</dt> | |||
<dd>{{.Version}}</dd> | |||
<dt>Server Uptime</dt> | |||
<dd>{{.SysStatus.Uptime}}</dd> | |||
<dt>Current Goroutines</dt> | |||
<dd>{{.SysStatus.NumGoroutine}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Current memory usage</dt> | |||
<dd>{{.SysStatus.MemAllocated}}</dd> | |||
<dt>Total mem allocated</dt> | |||
<dd>{{.SysStatus.MemTotal}}</dd> | |||
<dt>Memory obtained</dt> | |||
<dd>{{.SysStatus.MemSys}}</dd> | |||
<dt>Pointer lookup times</dt> | |||
<dd>{{.SysStatus.Lookups}}</dd> | |||
<dt>Memory allocate times</dt> | |||
<dd>{{.SysStatus.MemMallocs}}</dd> | |||
<dt>Memory free times</dt> | |||
<dd>{{.SysStatus.MemFrees}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Current heap usage</dt> | |||
<dd>{{.SysStatus.HeapAlloc}}</dd> | |||
<dt>Heap memory obtained</dt> | |||
<dd>{{.SysStatus.HeapSys}}</dd> | |||
<dt>Heap memory idle</dt> | |||
<dd>{{.SysStatus.HeapIdle}}</dd> | |||
<dt>Heap memory in use</dt> | |||
<dd>{{.SysStatus.HeapInuse}}</dd> | |||
<dt>Heap memory released</dt> | |||
<dd>{{.SysStatus.HeapReleased}}</dd> | |||
<dt>Heap objects</dt> | |||
<dd>{{.SysStatus.HeapObjects}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Bootstrap stack usage</dt> | |||
<dd>{{.SysStatus.StackInuse}}</dd> | |||
<dt>Stack memory obtained</dt> | |||
<dd>{{.SysStatus.StackSys}}</dd> | |||
<dt>MSpan structures in use</dt> | |||
<dd>{{.SysStatus.MSpanInuse}}</dd> | |||
<dt>MSpan structures obtained</dt> | |||
<dd>{{.SysStatus.HeapSys}}</dd> | |||
<dt>MCache structures in use</dt> | |||
<dd>{{.SysStatus.MCacheInuse}}</dd> | |||
<dt>MCache structures obtained</dt> | |||
<dd>{{.SysStatus.MCacheSys}}</dd> | |||
<dt>Profiling bucket hash table obtained</dt> | |||
<dd>{{.SysStatus.BuckHashSys}}</dd> | |||
<dt>GC metadata obtained</dt> | |||
<dd>{{.SysStatus.GCSys}}</dd> | |||
<dt>Other system allocation obtained</dt> | |||
<dd>{{.SysStatus.OtherSys}}</dd> | |||
<div class="ui divider"></div> | |||
<dt>Next GC recycle</dt> | |||
<dd>{{.SysStatus.NextGC}}</dd> | |||
<dt>Since last GC</dt> | |||
<dd>{{.SysStatus.LastGC}}</dd> | |||
<dt>Total GC pause</dt> | |||
<dd>{{.SysStatus.PauseTotalNs}}</dd> | |||
<dt>Last GC pause</dt> | |||
<dd>{{.SysStatus.PauseNs}}</dd> | |||
<dt>GC times</dt> | |||
<dd>{{.SysStatus.NumGC}}</dd> | |||
</dl> | |||
</div> | |||
</div> | |||
{{template "footer" .}} | |||
{{template "body-end" .}} | |||
{{end}} |
@@ -97,12 +97,14 @@ | |||
{{define "admin-header"}} | |||
<header class="admin"> | |||
<h1>Admin</h1> | |||
<nav id="admin"> | |||
<nav id="admin" class="pager"> | |||
<a href="/admin" {{if eq .Path "/admin"}}class="selected"{{end}}>Dashboard</a> | |||
<a href="/admin/settings" {{if eq .Path "/admin/settings"}}class="selected"{{end}}>Settings</a> | |||
{{if not .SingleUser}} | |||
<a href="/admin/users" {{if eq .Path "/admin/users"}}class="selected"{{end}}>Users</a> | |||
<a href="/admin/pages" {{if eq .Path "/admin/pages"}}class="selected"{{end}}>Pages</a> | |||
{{end}} | |||
<a href="/admin/monitor" {{if eq .Path "/admin/monitor"}}class="selected"{{end}}>Monitor</a> | |||
</nav> | |||
</header> | |||
{{end}} |