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
+
+
+
+
+ | Name |
+ Group |
+ Registered at |
+
+
+
+ {% for _, u := range userList %}
+
+ | {%s= u.Name %} |
+ {%s= u.Group %} |
+ {%s= u.RegisteredAt.Format("2006-01-02 15:04:05-0700") %} |
+
+ {% endfor %}
+
+
+
+
+{% 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
+
+
+
+
+ | Name |
+ Group |
+ Registered at |
+
+
+
+ `)
+//line views/admin.qtpl:23
+ for _, u := range userList {
+//line views/admin.qtpl:23
+ qw422016.N().S(`
+
+ | `)
+//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:29
+ }
+//line views/admin.qtpl:29
+ 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)
+ }
+ }
+}