diff --git a/user/files.go b/user/files.go index 72a7025..08b3a0d 100644 --- a/user/files.go +++ b/user/files.go @@ -2,14 +2,14 @@ package user import ( "encoding/json" + "golang.org/x/crypto/bcrypt" "io/ioutil" "log" "os" - "golang.org/x/crypto/bcrypt" "github.com/bouncepaw/mycorrhiza/cfg" - "github.com/bouncepaw/mycorrhiza/util" "github.com/bouncepaw/mycorrhiza/files" + "github.com/bouncepaw/mycorrhiza/util" ) // InitUserDatabase checks the configuration for auth methods and loads users diff --git a/user/net.go b/user/net.go index 3703087..09dd883 100644 --- a/user/net.go +++ b/user/net.go @@ -58,6 +58,7 @@ func Register(username, password string) error { Name: username, Group: "editor", HashedPassword: string(hash), + RegisteredAt: time.Now(), Source: SourceRegistration, } users.Store(username, &u) diff --git a/user/user.go b/user/user.go index e2d7235..0ce6103 100644 --- a/user/user.go +++ b/user/user.go @@ -2,6 +2,7 @@ package user import ( "sync" + "time" "golang.org/x/crypto/bcrypt" ) @@ -24,6 +25,7 @@ type User struct { Group string `json:"group"` Password string `json:"password"` // for fixed HashedPassword string `json:"hashed_password"` // for registered + RegisteredAt time.Time `json:"registered_on"` Source UserSource `json:"-"` sync.RWMutex diff --git a/views/admin.qtpl b/views/admin.qtpl new file mode 100644 index 0000000..8be7499 --- /dev/null +++ b/views/admin.qtpl @@ -0,0 +1,34 @@ +{% import "github.com/bouncepaw/mycorrhiza/user" %} + +{% func AdminUsersPanelHTML(userList []*user.User) %} +
+
+

Manage users

+ +
+ +
+ +

Users list

+ + + + + + + + + + + {% for _, u := range userList %} + + + + + + {% endfor %} + +
NameGroupRegistered at
{%s= u.Name %}{%s= u.Group %}{%s= u.RegisteredAt.Format("2006-01-02 15:04:05-0700") %}
+
+
+{% endfunc %} diff --git a/views/admin.qtpl.go b/views/admin.qtpl.go new file mode 100644 index 0000000..0dfc69c --- /dev/null +++ b/views/admin.qtpl.go @@ -0,0 +1,105 @@ +// Code generated by qtc from "admin.qtpl". DO NOT EDIT. +// See https://github.com/valyala/quicktemplate for details. + +//line views/admin.qtpl:1 +package views + +//line views/admin.qtpl:1 +import "github.com/bouncepaw/mycorrhiza/user" + +//line views/admin.qtpl:3 +import ( + qtio422016 "io" + + qt422016 "github.com/valyala/quicktemplate" +) + +//line views/admin.qtpl:3 +var ( + _ = qtio422016.Copy + _ = qt422016.AcquireByteBuffer +) + +//line views/admin.qtpl:3 +func StreamAdminUsersPanelHTML(qw422016 *qt422016.Writer, userList []*user.User) { +//line views/admin.qtpl:3 + qw422016.N().S(` +
+
+

Manage users

+ +
+ +
+ +

Users list

+ + + + + + + + + + + `) +//line views/admin.qtpl:23 + for _, u := range userList { +//line views/admin.qtpl:23 + qw422016.N().S(` + + + + + + `) +//line views/admin.qtpl:29 + } +//line views/admin.qtpl:29 + qw422016.N().S(` + +
NameGroupRegistered at
`) +//line views/admin.qtpl:25 + qw422016.N().S(u.Name) +//line views/admin.qtpl:25 + qw422016.N().S(``) +//line views/admin.qtpl:26 + qw422016.N().S(u.Group) +//line views/admin.qtpl:26 + qw422016.N().S(``) +//line views/admin.qtpl:27 + qw422016.N().S(u.RegisteredAt.Format("2006-01-02 15:04:05-0700")) +//line views/admin.qtpl:27 + qw422016.N().S(`
+
+
+`) +//line views/admin.qtpl:34 +} + +//line views/admin.qtpl:34 +func WriteAdminUsersPanelHTML(qq422016 qtio422016.Writer, userList []*user.User) { +//line views/admin.qtpl:34 + qw422016 := qt422016.AcquireWriter(qq422016) +//line views/admin.qtpl:34 + StreamAdminUsersPanelHTML(qw422016, userList) +//line views/admin.qtpl:34 + qt422016.ReleaseWriter(qw422016) +//line views/admin.qtpl:34 +} + +//line views/admin.qtpl:34 +func AdminUsersPanelHTML(userList []*user.User) string { +//line views/admin.qtpl:34 + qb422016 := qt422016.AcquireByteBuffer() +//line views/admin.qtpl:34 + WriteAdminUsersPanelHTML(qb422016, userList) +//line views/admin.qtpl:34 + qs422016 := string(qb422016.B) +//line views/admin.qtpl:34 + qt422016.ReleaseByteBuffer(qb422016) +//line views/admin.qtpl:34 + return qs422016 +//line views/admin.qtpl:34 +} diff --git a/views/stuff.qtpl b/views/stuff.qtpl index f0169d2..d84dd0a 100644 --- a/views/stuff.qtpl +++ b/views/stuff.qtpl @@ -137,7 +137,7 @@ for u := range user.YieldUsers() {

Safe things

@@ -171,4 +171,4 @@ for u := range user.YieldUsers() { {% for _, scriptPath := range cfg.OmnipresentScripts %} {% endfor %} -{% endfunc %} \ No newline at end of file +{% endfunc %} diff --git a/views/stuff.qtpl.go b/views/stuff.qtpl.go index 3696e10..1a8de2e 100644 --- a/views/stuff.qtpl.go +++ b/views/stuff.qtpl.go @@ -470,7 +470,7 @@ func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {

Safe things

diff --git a/web/admin.go b/web/admin.go index a78f884..5f05719 100644 --- a/web/admin.go +++ b/web/admin.go @@ -4,6 +4,7 @@ import ( "io" "log" "net/http" + "sort" "github.com/bouncepaw/mycorrhiza/cfg" "github.com/bouncepaw/mycorrhiza/user" @@ -17,6 +18,8 @@ func initAdmin() { http.HandleFunc("/admin", handlerAdmin) http.HandleFunc("/admin/shutdown", handlerAdminShutdown) http.HandleFunc("/admin/reindex-users", handlerAdminReindexUsers) + + http.HandleFunc("/admin/users", handlerAdminUsers) } } @@ -49,3 +52,28 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) { http.Redirect(w, rq, "/hypha/"+cfg.UserHypha, http.StatusSeeOther) } } + +func handlerAdminUsers(w http.ResponseWriter, r *http.Request) { + util.PrepareRq(r) + if user.CanProceed(r, "admin") { + // Get a sorted list of users + var userList []*user.User + for u := range user.YieldUsers() { + userList = append(userList, u) + } + + sort.Slice(userList, func(i, j int) bool { + less := userList[i].RegisteredAt.Before(userList[j].RegisteredAt) + return less + }) + + html := views.AdminUsersPanelHTML(userList) + html = views.BaseHTML("Manage users", html, user.FromRequest(r)) + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + _, err := io.WriteString(w, html) + if err != nil { + log.Println(err) + } + } +}