working sections version
This commit is contained in:
parent
16ea95bf09
commit
9424084a36
28 changed files with 367 additions and 251 deletions
|
|
@ -11,4 +11,5 @@ const (
|
||||||
LoginURI = "/login"
|
LoginURI = "/login"
|
||||||
LogoutURI = "/logout"
|
LogoutURI = "/logout"
|
||||||
TotpURI = "/totp"
|
TotpURI = "/totp"
|
||||||
|
SectionsURI = "/sections"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ func New(cfg core.Config) (db *DB, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return db.RunMigrations()
|
err = db.RunMigrations()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Dump(w io.Writer) error {
|
func (db *DB) Dump(w io.Writer) error {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"git.giftfish.de/ston1th/gowiki/pkg/core"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -14,10 +13,11 @@ var reservedNames = []string{
|
||||||
"logout",
|
"logout",
|
||||||
"new",
|
"new",
|
||||||
"search",
|
"search",
|
||||||
|
"section",
|
||||||
"sections",
|
"sections",
|
||||||
"user",
|
"user",
|
||||||
|
"users",
|
||||||
"view",
|
"view",
|
||||||
"wiki",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parts taken from strings.TrimPrefix
|
// Parts taken from strings.TrimPrefix
|
||||||
|
|
@ -28,20 +28,6 @@ func hasTrimPrefix(s, prefix string) (string, bool) {
|
||||||
return s, false
|
return s, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func addOrRemove(s string, a []string) []string {
|
|
||||||
i, _ := core.Contains(s, a)
|
|
||||||
switch i {
|
|
||||||
case -1:
|
|
||||||
return append(a, s)
|
|
||||||
case 0:
|
|
||||||
return a[1:]
|
|
||||||
case len(a):
|
|
||||||
return a[:i]
|
|
||||||
default:
|
|
||||||
return append(a[:i], a[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validatePassword(pw string) (b []byte) {
|
func validatePassword(pw string) (b []byte) {
|
||||||
b = []byte(pw)
|
b = []byte(pw)
|
||||||
if len(pw) <= 56 {
|
if len(pw) <= 56 {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ package db
|
||||||
import (
|
import (
|
||||||
"git.giftfish.de/ston1th/gowiki/pkg/core"
|
"git.giftfish.de/ston1th/gowiki/pkg/core"
|
||||||
"git.giftfish.de/ston1th/gowiki/pkg/log"
|
"git.giftfish.de/ston1th/gowiki/pkg/log"
|
||||||
|
"git.giftfish.de/ston1th/gowiki/pkg/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
versionKey = "version/version"
|
versionKey = "version/version"
|
||||||
currentVersion = 2
|
currentVersion = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
type migrator func(*DB) (int, error)
|
type migrator func(*DB) (int, error)
|
||||||
|
|
@ -18,10 +19,6 @@ var migrators = []migrator{
|
||||||
},
|
},
|
||||||
func(db *DB) (version int, err error) {
|
func(db *DB) (version int, err error) {
|
||||||
version = 1
|
version = 1
|
||||||
return
|
|
||||||
},
|
|
||||||
func(db *DB) (version int, err error) {
|
|
||||||
version = 2
|
|
||||||
// recreate all pages with new format
|
// recreate all pages with new format
|
||||||
var pages []string
|
var pages []string
|
||||||
db.store.ForEach(func(k string, _ []byte) error {
|
db.store.ForEach(func(k string, _ []byte) error {
|
||||||
|
|
@ -38,12 +35,25 @@ var migrators = []migrator{
|
||||||
log.Printf("db: migrator[%d]: pageGet %s: %s", version, k, err)
|
log.Printf("db: migrator[%d]: pageGet %s: %s", version, k, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if p.Perm == 3 {
|
||||||
|
p.Perm = core.Private
|
||||||
|
}
|
||||||
err = db.store.Set(k, &p)
|
err = db.store.Set(k, &p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("db: migrator[%d]: pageSet %s: %s", version, k, err)
|
log.Printf("db: migrator[%d]: pageSet %s: %s", version, k, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
users, err := db.GetUsers()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range users {
|
||||||
|
err = db.CreateSection(u.Username, []string{u.Username}, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("db: migrator[%d]: userSection %s: %s", version, u.Username, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -51,16 +61,21 @@ var migrators = []migrator{
|
||||||
func (db *DB) RunMigrations() (err error) {
|
func (db *DB) RunMigrations() (err error) {
|
||||||
var version int
|
var version int
|
||||||
err = db.store.Get(versionKey, &version)
|
err = db.store.Get(versionKey, &version)
|
||||||
if err == ErrKeyNotFound {
|
if err == store.ErrKeyNotFound {
|
||||||
err = db.store.Set(versionKey, 0)
|
err = db.store.Set(versionKey, currentVersion)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = db.store.Get(versionKey, &version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("db: detected database version %d", version)
|
log.Printf("db: database version %d", version)
|
||||||
for version < currentVersion {
|
for version < currentVersion {
|
||||||
log.Printf("db: running migration %d", version)
|
v := version + 1
|
||||||
version, err = migrators[version](db)
|
log.Printf("db: running migration %d", v)
|
||||||
|
version, err = migrators[v](db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -69,4 +84,5 @@ func (db *DB) RunMigrations() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ var (
|
||||||
errDeleteIndexPage = errors.New("db: index page can not be deleted")
|
errDeleteIndexPage = errors.New("db: index page can not be deleted")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *DB) GetAllSections(username string) (sections core.SectionPages) {
|
func (db *DB) GetAllSections(username string, admin bool) (sections core.SectionPages) {
|
||||||
m := make(map[string]core.UpdatedPages)
|
m := make(map[string]core.UpdatedPages)
|
||||||
db.store.ForEach(func(k string, _ []byte) error {
|
db.store.ForEach(func(k string, _ []byte) error {
|
||||||
if trim, ok := hasTrimPrefix(k, pagePrefix); ok {
|
if trim, ok := hasTrimPrefix(k, pagePrefix); ok {
|
||||||
|
|
@ -39,6 +39,16 @@ func (db *DB) GetAllSections(username string) (sections core.SectionPages) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if admin {
|
||||||
|
secs, err := db.GetSections()
|
||||||
|
if err == nil {
|
||||||
|
for _, s := range secs {
|
||||||
|
if _, ok := m[s.Name]; !ok {
|
||||||
|
m[s.Name] = core.UpdatedPages{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for s, p := range m {
|
for s, p := range m {
|
||||||
sort.Sort(p)
|
sort.Sort(p)
|
||||||
l := len(p)
|
l := len(p)
|
||||||
|
|
@ -51,7 +61,11 @@ func (db *DB) GetAllSections(username string) (sections core.SectionPages) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) GetAllSectionPages(section, username string) (pages core.Pages, err error) {
|
func (db *DB) GetAllSectionPages(section, username string, admin bool) (pages core.Pages, err error) {
|
||||||
|
err = db.SectionExists(section)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
db.store.ForEach(func(k string, _ []byte) error {
|
db.store.ForEach(func(k string, _ []byte) error {
|
||||||
if trim, ok := hasTrimPrefix(k, pagePrefix); ok {
|
if trim, ok := hasTrimPrefix(k, pagePrefix); ok {
|
||||||
if !strings.HasPrefix(trim, section+"/") {
|
if !strings.HasPrefix(trim, section+"/") {
|
||||||
|
|
@ -67,7 +81,7 @@ func (db *DB) GetAllSectionPages(section, username string) (pages core.Pages, er
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if len(pages) == 0 {
|
if len(pages) == 0 && !admin {
|
||||||
err = errPageNotFound
|
err = errPageNotFound
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -128,6 +142,14 @@ func (db *DB) CreatePage(title, section, markdown, username string, p core.Permi
|
||||||
if err != store.ErrKeyNotFound {
|
if err != store.ErrKeyNotFound {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s, err := db.GetSection(section)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := core.Contains(username, s.Members); !ok {
|
||||||
|
err = errPermissionDenied
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
page = &core.Page{
|
page = &core.Page{
|
||||||
Title: title,
|
Title: title,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.giftfish.de/ston1th/gowiki/pkg/core"
|
"git.giftfish.de/ston1th/gowiki/pkg/core"
|
||||||
|
"git.giftfish.de/ston1th/gowiki/pkg/log"
|
||||||
"git.giftfish.de/ston1th/gowiki/pkg/render"
|
"git.giftfish.de/ston1th/gowiki/pkg/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ func (db *DB) ReadPerm(st, username string, p core.Permission) bool {
|
||||||
section, _ := render.SplitStoreTitle(st)
|
section, _ := render.SplitStoreTitle(st)
|
||||||
s, err := db.GetSection(section)
|
s, err := db.GetSection(section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("db: read: %s", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return core.ReadPerm(username, p, s)
|
return core.ReadPerm(username, p, s)
|
||||||
|
|
@ -20,6 +22,7 @@ func (db *DB) WritePerm(st, username string, p core.Permission) bool {
|
||||||
section, _ := render.SplitStoreTitle(st)
|
section, _ := render.SplitStoreTitle(st)
|
||||||
s, err := db.GetSection(section)
|
s, err := db.GetSection(section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("db: read: %s", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return core.WritePerm(username, p, s)
|
return core.WritePerm(username, p, s)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,20 @@ func (db *DB) GetSections() (secs core.Sections, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetUserSections(username string) (sections map[string]bool, err error) {
|
||||||
|
sections = make(map[string]bool)
|
||||||
|
secs, err := db.GetSections()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, s := range secs {
|
||||||
|
if _, ok := core.Contains(username, s.Members); ok {
|
||||||
|
sections[s.Name] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) GetSection(section string) (s core.Section, err error) {
|
func (db *DB) GetSection(section string) (s core.Section, err error) {
|
||||||
err = db.store.Get(sectionPrefix+section, &s)
|
err = db.store.Get(sectionPrefix+section, &s)
|
||||||
return
|
return
|
||||||
|
|
@ -68,11 +82,11 @@ func (db *DB) UpdateSection(section string, members []string, user bool) error {
|
||||||
return db.store.Set(sectionPrefix+section, &core.Section{section, user, m})
|
return db.store.Set(sectionPrefix+section, &core.Section{section, user, m})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) DeleteSection(section string, user bool) error {
|
func (db *DB) DeleteSection(section string) error {
|
||||||
if section == core.WikiSection {
|
if section == core.WikiSection {
|
||||||
return errors.New("section '" + core.WikiSection + "' cannot be deleted")
|
return errors.New("section '" + core.WikiSection + "' cannot be deleted")
|
||||||
}
|
}
|
||||||
s, err := db.GetSection(section)
|
err := db.SectionExists(section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -80,9 +94,6 @@ func (db *DB) DeleteSection(section string, user bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !s.User && !user {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
section += "/"
|
section += "/"
|
||||||
var pages []string
|
var pages []string
|
||||||
db.store.ForEach(func(k string, _ []byte) error {
|
db.store.ForEach(func(k string, _ []byte) error {
|
||||||
|
|
|
||||||
|
|
@ -220,5 +220,5 @@ func (db *DB) DeleteUser(username string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return db.DeleteSection(username, true)
|
return db.DeleteSection(username)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,11 @@ func StoreTitle(section, title string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SplitStoreTitle(st string) (section, title string) {
|
func SplitStoreTitle(st string) (section, title string) {
|
||||||
if a := strings.Split(st, "/"); len(a) == 2 {
|
a := strings.Split(st, "/")
|
||||||
|
switch len(a) {
|
||||||
|
case 1:
|
||||||
|
return a[0], ""
|
||||||
|
case 2:
|
||||||
return a[0], a[1]
|
return a[0], a[1]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@ func TestTitleFunctions(t *testing.T) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
t.Run("SplitStoreTitleSingle", func(t *testing.T) {
|
||||||
|
se, ti := SplitStoreTitle(section)
|
||||||
|
if se != section || ti != "" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
})
|
||||||
t.Run("Title", func(t *testing.T) {
|
t.Run("Title", func(t *testing.T) {
|
||||||
if Title("9ßw$%4h4t-8v74 287(G /SV&3[45v}=94#+?)") != "9w4h4t-8v74+287G+SV345v94" {
|
if Title("9ßw$%4h4t-8v74 287(G /SV&3[45v}=94#+?)") != "9w4h4t-8v74+287G+SV345v94" {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,16 @@ type loginData struct {
|
||||||
Referer string
|
Referer string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sectionData struct {
|
||||||
|
Name string
|
||||||
|
Members map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type pageData struct {
|
||||||
|
Page core.Page
|
||||||
|
Sections map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) Exec() {
|
func (c *Context) Exec() {
|
||||||
defer c.log()
|
defer c.log()
|
||||||
if c.T == nil {
|
if c.T == nil {
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func sectionsHandler(ctx *Context) {
|
||||||
BodyTitle: "All Sections",
|
BodyTitle: "All Sections",
|
||||||
Admin: ctx.Admin(),
|
Admin: ctx.Admin(),
|
||||||
}
|
}
|
||||||
ctx.Data.Data = ctx.Srv.DB.GetAllSections(ctx.User())
|
ctx.Data.Data = ctx.Srv.DB.GetAllSections(ctx.User(), ctx.Admin())
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ func sectionHandler(ctx *Context) {
|
||||||
Admin: ctx.Admin(),
|
Admin: ctx.Admin(),
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
ctx.Data.Data, err = ctx.Srv.DB.GetAllSectionPages(section, ctx.User())
|
ctx.Data.Data, err = ctx.Srv.DB.GetAllSectionPages(section, ctx.User(), ctx.Admin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
return
|
return
|
||||||
|
|
@ -238,61 +238,140 @@ func sectionHandler(ctx *Context) {
|
||||||
|
|
||||||
func sectionNewHandler(ctx *Context) {
|
func sectionNewHandler(ctx *Context) {
|
||||||
ctx.Template("sectionNewHandler")
|
ctx.Template("sectionNewHandler")
|
||||||
section := ctx.Var("section")
|
|
||||||
ctx.Data = webData{
|
ctx.Data = webData{
|
||||||
Title: section,
|
Title: "New Section",
|
||||||
BodyTitle: section,
|
BodyTitle: "New Section",
|
||||||
Admin: ctx.Admin(),
|
|
||||||
}
|
}
|
||||||
var err error
|
users, err := ctx.Srv.DB.GetUsers()
|
||||||
ctx.Data.Data, err = ctx.Srv.DB.GetAllSectionPages(section, ctx.User())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.NotFound()
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Exec()
|
sd := sectionData{Members: make(map[string]bool)}
|
||||||
}
|
for _, u := range users {
|
||||||
|
sd.Members[u.Username] = false
|
||||||
func sectionDelHandler(ctx *Context) {
|
|
||||||
ctx.Template("sectionDelHandler")
|
|
||||||
section := ctx.Var("section")
|
|
||||||
ctx.Data = webData{
|
|
||||||
Title: section,
|
|
||||||
BodyTitle: section,
|
|
||||||
Admin: ctx.Admin(),
|
|
||||||
}
|
}
|
||||||
var err error
|
switch ctx.Method() {
|
||||||
ctx.Data.Data, err = ctx.Srv.DB.GetAllSectionPages(section, ctx.User())
|
case "GET":
|
||||||
if err != nil {
|
ctx.Data.Data = sd
|
||||||
ctx.NotFound()
|
ctx.Exec()
|
||||||
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Exec()
|
section := ctx.Form("section")
|
||||||
|
members := ctx.FormSlice("members")
|
||||||
|
for _, m := range members {
|
||||||
|
sd.Members[m] = true
|
||||||
|
}
|
||||||
|
ctx.Data.Data = sd
|
||||||
|
if section == "" {
|
||||||
|
ctx.Error("empty section")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ctx.Srv.DB.CreateSection(section, members, false)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("create: section %s created by %s\n", section, ctx.User())
|
||||||
|
ctx.Redirect(core.SectionsURI, http.StatusFound)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sectionEditHandler(ctx *Context) {
|
func sectionEditHandler(ctx *Context) {
|
||||||
ctx.Template("sectionEditHandler")
|
ctx.Template("sectionEditHandler")
|
||||||
section := ctx.Var("section")
|
|
||||||
ctx.Data = webData{
|
ctx.Data = webData{
|
||||||
Title: section,
|
Title: "Edit Section",
|
||||||
BodyTitle: section,
|
BodyTitle: "Edit Section",
|
||||||
Admin: ctx.Admin(),
|
|
||||||
}
|
}
|
||||||
var err error
|
users, err := ctx.Srv.DB.GetUsers()
|
||||||
ctx.Data.Data, err = ctx.Srv.DB.GetAllSectionPages(section, ctx.User())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.NotFound()
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
sd := sectionData{Members: make(map[string]bool)}
|
||||||
|
for _, u := range users {
|
||||||
|
sd.Members[u.Username] = false
|
||||||
|
}
|
||||||
|
s, err := ctx.Srv.DB.GetSection(ctx.Var("section"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sd.Name = s.Name
|
||||||
|
switch ctx.Method() {
|
||||||
|
case "GET":
|
||||||
|
for _, m := range s.Members {
|
||||||
|
sd.Members[m] = true
|
||||||
|
}
|
||||||
|
ctx.Data.Data = sd
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
members := ctx.FormSlice("members")
|
||||||
|
for _, m := range members {
|
||||||
|
sd.Members[m] = true
|
||||||
|
}
|
||||||
|
ctx.Data.Data = sd
|
||||||
|
err = ctx.Srv.DB.UpdateSection(s.Name, members, s.User)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("update: section %s updated by %s\n", s.Name, ctx.User())
|
||||||
|
ctx.Redirect(core.SectionsURI, http.StatusFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sectionDelHandler(ctx *Context) {
|
||||||
|
ctx.Template("sectionDelHandler")
|
||||||
|
ctx.Data = webData{
|
||||||
|
Title: "Delete Section",
|
||||||
|
BodyTitle: "Delete Section",
|
||||||
|
}
|
||||||
|
s, err := ctx.Srv.DB.GetSection(ctx.Var("section"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.User {
|
||||||
|
ctx.Error("user section can not be deleted")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch ctx.Method() {
|
||||||
|
case "GET":
|
||||||
|
ctx.Data.Data = s.Name
|
||||||
|
ctx.Exec()
|
||||||
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := ctx.Srv.DB.DeleteSection(s.Name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("delete: section %s deleted by %s\n", s.Name, ctx.User())
|
||||||
|
ctx.Redirect(core.SectionsURI, http.StatusFound)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pageNewHandler(ctx *Context) {
|
func pageNewHandler(ctx *Context) {
|
||||||
ctx.Template("pageNewHandler")
|
ctx.Template("pageNewHandler")
|
||||||
ctx.Data = webData{Title: "New Page"}
|
ctx.Data = webData{Title: "New Page"}
|
||||||
|
user := ctx.User()
|
||||||
|
secs, err := ctx.Srv.DB.GetUserSections(user)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
switch ctx.Method() {
|
switch ctx.Method() {
|
||||||
case "GET":
|
case "GET":
|
||||||
ctx.Data.Data = core.Page{Perm: core.Public}
|
secs[user] = true
|
||||||
|
ctx.Data.Data = pageData{core.Page{Perm: core.Public}, secs}
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
case "POST":
|
case "POST":
|
||||||
if !ctx.CheckXsrf() {
|
if !ctx.CheckXsrf() {
|
||||||
|
|
@ -307,7 +386,8 @@ func pageNewHandler(ctx *Context) {
|
||||||
Markdown: markdown,
|
Markdown: markdown,
|
||||||
Perm: perm,
|
Perm: perm,
|
||||||
}
|
}
|
||||||
ctx.Data.Data = p
|
secs[section] = true
|
||||||
|
ctx.Data.Data = pageData{p, secs}
|
||||||
if title == "" {
|
if title == "" {
|
||||||
ctx.Error("empty title")
|
ctx.Error("empty title")
|
||||||
return
|
return
|
||||||
|
|
@ -317,10 +397,7 @@ func pageNewHandler(ctx *Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if section != core.WikiSection {
|
page, err := ctx.Srv.DB.CreatePage(title, section, markdown, user, perm)
|
||||||
section = ctx.User()
|
|
||||||
}
|
|
||||||
page, err := ctx.Srv.DB.CreatePage(title, section, markdown, ctx.User(), perm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
|
|
@ -540,12 +617,12 @@ func pageDelHandler(ctx *Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func userHandler(ctx *Context) {
|
func usersHandler(ctx *Context) {
|
||||||
if !ctx.LoggedOn() {
|
if !ctx.LoggedOn() {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Template("userHandler")
|
ctx.Template("usersHandler")
|
||||||
ctx.Data = webData{
|
ctx.Data = webData{
|
||||||
Title: "Users",
|
Title: "Users",
|
||||||
BodyTitle: "Users",
|
BodyTitle: "Users",
|
||||||
|
|
@ -593,8 +670,8 @@ func userNewHandler(ctx *Context) {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("create: %s created by %s\n", user, ctx.User())
|
log.Printf("create: user %s created by %s\n", user, ctx.User())
|
||||||
ctx.Redirect("/user", http.StatusFound)
|
ctx.Redirect("/users", http.StatusFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,8 +712,8 @@ func userEditHandler(ctx *Context) {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("update: %s updated by %s\n", user, ctx.User())
|
log.Printf("update: user %s updated by %s\n", user, ctx.User())
|
||||||
ctx.Redirect("/user", http.StatusFound)
|
ctx.Redirect("/users", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := ctx.Srv.DB.UpdateUserPassword(user, password); err != nil {
|
if err := ctx.Srv.DB.UpdateUserPassword(user, password); err != nil {
|
||||||
|
|
@ -724,7 +801,7 @@ func userUnlockHandler(ctx *Context) {
|
||||||
log.Println("unlock:", err)
|
log.Println("unlock:", err)
|
||||||
}
|
}
|
||||||
log.Printf("unlock: %s unlocked by %s\n", user, ctx.User())
|
log.Printf("unlock: %s unlocked by %s\n", user, ctx.User())
|
||||||
ctx.Redirect("/user", http.StatusFound)
|
ctx.Redirect("/users", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func userDelHandler(ctx *Context) {
|
func userDelHandler(ctx *Context) {
|
||||||
|
|
@ -751,12 +828,12 @@ func userDelHandler(ctx *Context) {
|
||||||
if err := ctx.Srv.DB.DeleteUser(user); err != nil {
|
if err := ctx.Srv.DB.DeleteUser(user); err != nil {
|
||||||
log.Println("delete:", err)
|
log.Println("delete:", err)
|
||||||
}
|
}
|
||||||
log.Printf("delete: %s deleted by %s\n", user, self)
|
log.Printf("delete: user %s deleted by %s\n", user, self)
|
||||||
if user == self {
|
if user == self {
|
||||||
ctx.Redirect("/logout", http.StatusFound)
|
ctx.Redirect("/logout", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Redirect("/user", http.StatusFound)
|
ctx.Redirect("/users", http.StatusFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,23 +35,23 @@ var routes = []route{
|
||||||
[]string{"GET"},
|
[]string{"GET"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"/sections/new",
|
"/section/new",
|
||||||
adminAuthHandler(
|
adminAuthHandler(
|
||||||
sectionNewHandler),
|
sectionNewHandler),
|
||||||
[]string{"GET", "POST"},
|
[]string{"GET", "POST"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"/section/del/{section:[a-zA-Z0-9]+$}",
|
|
||||||
adminAuthHandler(
|
|
||||||
sectionDelHandler),
|
|
||||||
[]string{"GET", "POST"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"/section/edit/{section:[a-zA-Z0-9]+$}",
|
"/section/edit/{section:[a-zA-Z0-9]+$}",
|
||||||
adminAuthHandler(
|
adminAuthHandler(
|
||||||
sectionEditHandler),
|
sectionEditHandler),
|
||||||
[]string{"GET", "POST"},
|
[]string{"GET", "POST"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"/section/del/{section:[a-zA-Z0-9]+$}",
|
||||||
|
adminAuthHandler(
|
||||||
|
sectionDelHandler),
|
||||||
|
[]string{"GET", "POST"},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"/search",
|
"/search",
|
||||||
searchHandler,
|
searchHandler,
|
||||||
|
|
@ -81,9 +81,9 @@ var routes = []route{
|
||||||
[]string{"GET", "POST"},
|
[]string{"GET", "POST"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"/user",
|
"/users",
|
||||||
adminAuthHandler(
|
adminAuthHandler(
|
||||||
userHandler),
|
usersHandler),
|
||||||
[]string{"GET"},
|
[]string{"GET"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -92,18 +92,18 @@ var routes = []route{
|
||||||
userNewHandler),
|
userNewHandler),
|
||||||
[]string{"GET", "POST"},
|
[]string{"GET", "POST"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"/user/del/{user:[a-zA-Z0-9]+$}",
|
|
||||||
userAuthHandler(
|
|
||||||
userDelHandler),
|
|
||||||
[]string{"GET", "POST"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"/user/edit/{user:[a-zA-Z0-9]+$}",
|
"/user/edit/{user:[a-zA-Z0-9]+$}",
|
||||||
userAuthHandler(
|
userAuthHandler(
|
||||||
userEditHandler),
|
userEditHandler),
|
||||||
[]string{"GET", "POST"},
|
[]string{"GET", "POST"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"/user/del/{user:[a-zA-Z0-9]+$}",
|
||||||
|
userAuthHandler(
|
||||||
|
userDelHandler),
|
||||||
|
[]string{"GET", "POST"},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"/user/totp/{user:[a-zA-Z0-9]+$}",
|
"/user/totp/{user:[a-zA-Z0-9]+$}",
|
||||||
userAuthHandler(
|
userAuthHandler(
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const (
|
||||||
<title>GoWiki | {{.Title}}</title>
|
<title>GoWiki | {{.Title}}</title>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
|
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-o0xveJH8qNnWr/39Jnx97yDlYrvjuUnzE4q3p+94Yp4="></link>
|
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-5/Vbh+c9BC042HeSbJXZZVIc1dD2b9cy6uH9LFAOSfo="></link>
|
||||||
<meta name="theme-color" content="#375a7f">
|
<meta name="theme-color" content="#375a7f">
|
||||||
<meta name="description" content="{{.Title}}">
|
<meta name="description" content="{{.Title}}">
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -150,7 +150,7 @@ const (
|
||||||
<ul class="nav navbar-nav ml-auto">
|
<ul class="nav navbar-nav ml-auto">
|
||||||
{{if .Login}}
|
{{if .Login}}
|
||||||
{{if .Admin}}
|
{{if .Admin}}
|
||||||
<li class="nav-item"><a class="nav-link" href="/user">Users</a></li>
|
<li class="nav-item"><a class="nav-link" href="/users">Users</a></li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li class="nav-item"><a class="nav-link" href="/user/edit/{{.User}}">Profile</a></li>
|
<li class="nav-item"><a class="nav-link" href="/user/edit/{{.User}}">Profile</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
@ -243,11 +243,7 @@ const (
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-control custom-radio">
|
<div class="custom-control custom-radio">
|
||||||
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Perm 2}}checked=""{{end}}>
|
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Perm 2}}checked=""{{end}}>
|
||||||
<label class="custom-control-label" for="perm2">Internal</label>
|
<label class="custom-control-label" for="perm2">Private</label>
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="perm3" name="perm" value="3" class="custom-control-input" {{if eq .Data.Perm 3}}checked=""{{end}}>
|
|
||||||
<label class="custom-control-label" for="perm3">Private</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
||||||
|
|
@ -271,9 +267,6 @@ const (
|
||||||
<span class="badge badge-primary">Public</span>
|
<span class="badge badge-primary">Public</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .Data.Perm 2}}
|
{{if eq .Data.Perm 2}}
|
||||||
<span class="badge badge-warning">Internal</span>
|
|
||||||
{{end}}
|
|
||||||
{{if eq .Data.Perm 3}}
|
|
||||||
<span class="badge badge-danger">Private</span>
|
<span class="badge badge-danger">Private</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
<br>
|
<br>
|
||||||
|
|
@ -321,30 +314,31 @@ const (
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="section">Section</label>
|
<label class="col-form-label" for="section">Section</label>
|
||||||
<select class="form-control input-sm" id="section" name="section" required>
|
<select class="form-control input-sm" id="section" name="section" required>
|
||||||
<option>wiki</option>
|
{{range $section, $selected := .Data.Sections}}
|
||||||
<option>{{.User}}</option>
|
{{if $selected}}
|
||||||
|
<option value="{{$section}}" selected>{{$section}}</option>
|
||||||
|
{{else}}
|
||||||
|
<option value="{{$section}}">{{$section}}</option>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="title">Title</label>
|
<label class="col-form-label" for="title">Title</label>
|
||||||
<input class="form-control input-sm" type="text" id="title" name="title" placeholder="Title" value="{{.Data.Title}}" spellcheck="false" autocomplete="off" autofocus required>
|
<input class="form-control input-sm" type="text" id="title" name="title" placeholder="Title" value="{{.Data.Page.Title}}" spellcheck="false" autocomplete="off" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="markdown">Markdown</label>
|
<label class="col-form-label" for="markdown">Markdown</label>
|
||||||
<textarea class="form-control input-sm md-text" id="markdown" name="markdown" rows="30" spellcheck="false">{{.Data.Markdown}}</textarea>
|
<textarea class="form-control input-sm md-text" id="markdown" name="markdown" rows="30" spellcheck="false">{{.Data.Page.Markdown}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="custom-control custom-radio">
|
<div class="custom-control custom-radio">
|
||||||
<input type="radio" id="perm1" name="perm" value="1" class="custom-control-input" {{if eq .Data.Perm 1}}checked=""{{end}}>
|
<input type="radio" id="perm1" name="perm" value="1" class="custom-control-input" {{if eq .Data.Page.Perm 1}}checked=""{{end}}>
|
||||||
<label class="custom-control-label" for="perm1">Public</label>
|
<label class="custom-control-label" for="perm1">Public</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-control custom-radio">
|
<div class="custom-control custom-radio">
|
||||||
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Perm 2}}checked=""{{end}}>
|
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Page.Perm 2}}checked=""{{end}}>
|
||||||
<label class="custom-control-label" for="perm2">Internal</label>
|
<label class="custom-control-label" for="perm2">Private</label>
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="perm3" name="perm" value="3" class="custom-control-input" {{if eq .Data.Perm 3}}checked=""{{end}}>
|
|
||||||
<label class="custom-control-label" for="perm3">Private</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
|
|
@ -410,9 +404,6 @@ const (
|
||||||
<span class="badge badge-primary">Public</span>
|
<span class="badge badge-primary">Public</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .Data.Perm 2}}
|
{{if eq .Data.Perm 2}}
|
||||||
<span class="badge badge-warning">Internal</span>
|
|
||||||
{{end}}
|
|
||||||
{{if eq .Data.Perm 3}}
|
|
||||||
<span class="badge badge-danger">Private</span>
|
<span class="badge badge-danger">Private</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
<br>
|
<br>
|
||||||
|
|
@ -467,14 +458,14 @@ const (
|
||||||
<div class="col-xs-4 offset-4">
|
<div class="col-xs-4 offset-4">
|
||||||
<div class="card border-danger mx-auto">
|
<div class="card border-danger mx-auto">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<strong>Delete {{.Data.Username}}?</strong>
|
<strong>Delete {{.Data}}?</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>You are about to delete user <a href="/{{.Data.Username}}">{{.Data.Username}}</a>.<br>This will also remove all private pages of this user.</p>
|
<p>You are about to delete section <a href="/{{.Data}}">{{.Data}}</a>.<br>This will also remove all pages of this section.</p>
|
||||||
<form class="form-horizontal" action="/user/del/{{.Data.Username}}" method="post">
|
<form class="form-horizontal" action="/section/del/{{.Data}}" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/{{.Data}}" class="btn btn-sm btn-primary">Back</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -484,73 +475,53 @@ const (
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}`
|
{{end}}`
|
||||||
sectionEdit = `{{define "body"}}
|
sectionEdit = `{{define "body"}}
|
||||||
{{if .Data}}
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-4 offset-4">
|
<div class="col-xs-4 offset-4">
|
||||||
<div class="card border-primary mx-auto">
|
<div class="card border-primary mx-auto">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<strong>{{.BodyTitle}}</strong>
|
<strong>{{.BodyTitle}}</strong>
|
||||||
{{if .Data.Secret}}
|
|
||||||
<a href="/user/totp/{{.Data.Username}}" class="btn btn-sm btn-danger float-right">Disable TOTP</a>
|
|
||||||
{{else}}
|
|
||||||
<a href="/user/totp/{{.Data.Username}}" class="btn btn-sm btn-success float-right">Enable TOTP</a>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form class="form-horizontal" action="/user/edit/{{.Data.Username}}" method="post">
|
<form class="form-horizontal" action="/section/edit/{{.Data.Name}}" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="user">Username</label>
|
<label class="col-form-label" for="section">Section</label>
|
||||||
<input class="form-control input-sm" type="text" id="user" name="user" value="{{.Data.Username}}" disabled>
|
<input class="form-control input-sm" type="text" id="section" name="section" value="{{.Data.Name}}" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="password">Password</label>
|
<label class="col-form-label" for="members">Members</label>
|
||||||
<input class="form-control input-sm" type="password" id="password" name="password" autofocus>
|
<select class="form-control input-sm sections" id="members" name="members" multiple required>
|
||||||
</div>
|
{{range $user, $member := .Data.Members}}
|
||||||
<div class="form-group">
|
{{if $member}}
|
||||||
<label class="col-form-label" for="repeat">Repeat</label>
|
<option value="{{$user}}" selected>{{$user}}</option>
|
||||||
<input class="form-control input-sm" type="password" id="repeat" name="repeat">
|
|
||||||
</div>
|
|
||||||
{{if .Admin}}
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label" for="admin">Admin Privileges</label>
|
|
||||||
{{if .Data.Admin}}
|
|
||||||
<input type="checkbox" id="admin" name="admin" value="0" checked>
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<input type="checkbox" id="admin" name="admin" value="0">
|
<option value="{{$user}}">{{$user}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
||||||
{{if .Admin}}
|
<a href="/{{.Data.Name}}" class="btn btn-sm btn-primary">Back</a>
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
|
||||||
{{else}}
|
|
||||||
<a href="/" class="btn btn-sm btn-primary">Back</a>
|
|
||||||
{{end}}
|
|
||||||
<a class="btn btn-sm btn-danger" href="/user/del/{{.Data.Username}}">Delete</a>
|
|
||||||
</form>
|
</form>
|
||||||
{{if .Admin}}
|
|
||||||
{{if eq .Data.Locked 3}}
|
|
||||||
<form class="form-horizontal" action="/user/unlock/{{.Data.Username}}" method="post">
|
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label" for="unlock">Locked</label>
|
|
||||||
<button class="btn btn-sm btn-warning" type="submit" id="unlock">Unlock</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
|
||||||
{{end}}`
|
{{end}}`
|
||||||
section = `{{define "body"}}
|
section = `{{define "body"}}
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
<h2>{{.BodyTitle}}</h2>
|
<h2>{{.BodyTitle}}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
{{if .Admin}}
|
||||||
|
<div class="btn-group float-right">
|
||||||
|
<a class="btn btn-sm btn-primary" href="/section/edit/{{.BodyTitle}}">Edit</a>
|
||||||
|
<a class="btn btn-sm btn-danger" href="/section/del/{{.BodyTitle}}">Delete</a>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if .Data}}
|
{{if .Data}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -570,26 +541,26 @@ const (
|
||||||
<strong>{{.BodyTitle}}</strong>
|
<strong>{{.BodyTitle}}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form class="form-horizontal" action="/user/new" method="post">
|
<form class="form-horizontal" action="/section/new" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="user">Username</label>
|
<label class="col-form-label" for="section">Section</label>
|
||||||
<input class="form-control input-sm" type="text" id="user" name="user" autocomplete="off" autofocus required>
|
<input class="form-control input-sm" type="text" id="section" name="section" autocomplete="off" value="{{.Data.Name}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="password">Password</label>
|
<label class="col-form-label" for="members">Members</label>
|
||||||
<input class="form-control input-sm" type="password" id="password" name="password" required>
|
<select class="form-control input-sm sections" id="members" name="members" multiple required>
|
||||||
</div>
|
{{range $user, $member := .Data.Members}}
|
||||||
<div class="form-group">
|
{{if $member}}
|
||||||
<label class="col-form-label" for="repeat">Repeat</label>
|
<option value="{{$user}}" selected>{{$user}}</option>
|
||||||
<input class="form-control input-sm" type="password" id="repeat" name="repeat" required>
|
{{else}}
|
||||||
</div>
|
<option value="{{$user}}">{{$user}}</option>
|
||||||
<div class="form-group">
|
{{end}}
|
||||||
<label class="col-form-label" for="admin">Admin Privileges</label>
|
{{end}}
|
||||||
<input type="checkbox" id="admin" name="admin" value="0">
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/sections" class="btn btn-sm btn-primary">Back</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -602,6 +573,13 @@ const (
|
||||||
<h2>{{.BodyTitle}}</h2>
|
<h2>{{.BodyTitle}}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{if .Admin}}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-xs-5">
|
||||||
|
<a class="btn btn-sm btn-primary" href="/section/new"><b>New Section</b></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
{{if .Data}}
|
{{if .Data}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{range $index, $item := .Data}}
|
{{range $index, $item := .Data}}
|
||||||
|
|
@ -632,11 +610,11 @@ const (
|
||||||
<strong>Delete {{.Data.Username}}?</strong>
|
<strong>Delete {{.Data.Username}}?</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>You are about to delete user <a href="/{{.Data.Username}}">{{.Data.Username}}</a>.<br>This will also remove all private pages of this user.</p>
|
<p>You are about to delete user <a href="/{{.Data.Username}}">{{.Data.Username}}</a>.<br>This will also remove all pages of this user.</p>
|
||||||
<form class="form-horizontal" action="/user/del/{{.Data.Username}}" method="post">
|
<form class="form-horizontal" action="/user/del/{{.Data.Username}}" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/users" class="btn btn-sm btn-primary">Back</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -685,7 +663,7 @@ const (
|
||||||
{{end}}
|
{{end}}
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
||||||
{{if .Admin}}
|
{{if .Admin}}
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/users" class="btn btn-sm btn-primary">Back</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="/" class="btn btn-sm btn-primary">Back</a>
|
<a href="/" class="btn btn-sm btn-primary">Back</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
@ -708,7 +686,41 @@ const (
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}`
|
{{end}}`
|
||||||
user = `{{define "body"}}
|
userNew = `{{define "body"}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-4 offset-4">
|
||||||
|
<div class="card border-primary mx-auto">
|
||||||
|
<div class="card-header">
|
||||||
|
<strong>{{.BodyTitle}}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form class="form-horizontal" action="/user/new" method="post">
|
||||||
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-form-label" for="user">Username</label>
|
||||||
|
<input class="form-control input-sm" type="text" id="user" name="user" autocomplete="off" autofocus required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-form-label" for="password">Password</label>
|
||||||
|
<input class="form-control input-sm" type="password" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-form-label" for="repeat">Repeat</label>
|
||||||
|
<input class="form-control input-sm" type="password" id="repeat" name="repeat" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-form-label" for="admin">Admin Privileges</label>
|
||||||
|
<input type="checkbox" id="admin" name="admin" value="0">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
|
<a href="/users" class="btn btn-sm btn-primary">Back</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}`
|
||||||
|
users = `{{define "body"}}
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h2>{{.BodyTitle}}</h2>
|
<h2>{{.BodyTitle}}</h2>
|
||||||
|
|
@ -762,40 +774,6 @@ const (
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}`
|
|
||||||
userNew = `{{define "body"}}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-4 offset-4">
|
|
||||||
<div class="card border-primary mx-auto">
|
|
||||||
<div class="card-header">
|
|
||||||
<strong>{{.BodyTitle}}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<form class="form-horizontal" action="/user/new" method="post">
|
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label" for="user">Username</label>
|
|
||||||
<input class="form-control input-sm" type="text" id="user" name="user" autocomplete="off" autofocus required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label" for="password">Password</label>
|
|
||||||
<input class="form-control input-sm" type="password" id="password" name="password" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label" for="repeat">Repeat</label>
|
|
||||||
<input class="form-control input-sm" type="password" id="repeat" name="repeat" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label" for="admin">Admin Privileges</label>
|
|
||||||
<input type="checkbox" id="admin" name="admin" value="0">
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}`
|
{{end}}`
|
||||||
userTotp = `{{define "body"}}
|
userTotp = `{{define "body"}}
|
||||||
{{if .Data}}
|
{{if .Data}}
|
||||||
|
|
@ -955,6 +933,9 @@ textarea,input,select {
|
||||||
color: #e2e2e2 !important;
|
color: #e2e2e2 !important;
|
||||||
background-color: #444444 !important;
|
background-color: #444444 !important;
|
||||||
}
|
}
|
||||||
|
select.sections {
|
||||||
|
height: 120px !important;
|
||||||
|
}
|
||||||
input.menu {
|
input.menu {
|
||||||
color: #e2e2e2 !important;
|
color: #e2e2e2 !important;
|
||||||
background-color: #222222 !important;
|
background-color: #222222 !important;
|
||||||
|
|
@ -1045,7 +1026,7 @@ func (s *HTTPServer) loadTemplates() {
|
||||||
s.templ["sectionEditHandler"] = parse(index, menu, sectionEdit)
|
s.templ["sectionEditHandler"] = parse(index, menu, sectionEdit)
|
||||||
s.templ["sectionDelHandler"] = parse(index, menu, sectionDel)
|
s.templ["sectionDelHandler"] = parse(index, menu, sectionDel)
|
||||||
// users
|
// users
|
||||||
s.templ["userHandler"] = parse(index, menu, user)
|
s.templ["usersHandler"] = parse(index, menu, users)
|
||||||
s.templ["userNewHandler"] = parse(index, menu, userNew)
|
s.templ["userNewHandler"] = parse(index, menu, userNew)
|
||||||
s.templ["userEditHandler"] = parse(index, menu, userEdit)
|
s.templ["userEditHandler"] = parse(index, menu, userEdit)
|
||||||
s.templ["userDelHandler"] = parse(index, menu, userDel)
|
s.templ["userDelHandler"] = parse(index, menu, userDel)
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func (s *HTTPServer) loadTemplates() {
|
||||||
s.templ["sectionEditHandler"] = parse(index, menu, sectionEdit)
|
s.templ["sectionEditHandler"] = parse(index, menu, sectionEdit)
|
||||||
s.templ["sectionDelHandler"] = parse(index, menu, sectionDel)
|
s.templ["sectionDelHandler"] = parse(index, menu, sectionDel)
|
||||||
// users
|
// users
|
||||||
s.templ["userHandler"] = parse(index, menu, user)
|
s.templ["usersHandler"] = parse(index, menu, users)
|
||||||
s.templ["userNewHandler"] = parse(index, menu, userNew)
|
s.templ["userNewHandler"] = parse(index, menu, userNew)
|
||||||
s.templ["userEditHandler"] = parse(index, menu, userEdit)
|
s.templ["userEditHandler"] = parse(index, menu, userEdit)
|
||||||
s.templ["userDelHandler"] = parse(index, menu, userDel)
|
s.templ["userDelHandler"] = parse(index, menu, userDel)
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,9 @@ textarea,input,select {
|
||||||
color: #e2e2e2 !important;
|
color: #e2e2e2 !important;
|
||||||
background-color: #444444 !important;
|
background-color: #444444 !important;
|
||||||
}
|
}
|
||||||
|
select.sections {
|
||||||
|
height: 120px !important;
|
||||||
|
}
|
||||||
input.menu {
|
input.menu {
|
||||||
color: #e2e2e2 !important;
|
color: #e2e2e2 !important;
|
||||||
background-color: #222222 !important;
|
background-color: #222222 !important;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<title>GoWiki | {{.Title}}</title>
|
<title>GoWiki | {{.Title}}</title>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
|
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-o0xveJH8qNnWr/39Jnx97yDlYrvjuUnzE4q3p+94Yp4="></link>
|
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-5/Vbh+c9BC042HeSbJXZZVIc1dD2b9cy6uH9LFAOSfo="></link>
|
||||||
<meta name="theme-color" content="#375a7f">
|
<meta name="theme-color" content="#375a7f">
|
||||||
<meta name="description" content="{{.Title}}">
|
<meta name="description" content="{{.Title}}">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<ul class="nav navbar-nav ml-auto">
|
<ul class="nav navbar-nav ml-auto">
|
||||||
{{if .Login}}
|
{{if .Login}}
|
||||||
{{if .Admin}}
|
{{if .Admin}}
|
||||||
<li class="nav-item"><a class="nav-link" href="/user">Users</a></li>
|
<li class="nav-item"><a class="nav-link" href="/users">Users</a></li>
|
||||||
{{else}}
|
{{else}}
|
||||||
<li class="nav-item"><a class="nav-link" href="/user/edit/{{.User}}">Profile</a></li>
|
<li class="nav-item"><a class="nav-link" href="/user/edit/{{.User}}">Profile</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@
|
||||||
<span class="badge badge-primary">Public</span>
|
<span class="badge badge-primary">Public</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .Data.Perm 2}}
|
{{if eq .Data.Perm 2}}
|
||||||
<span class="badge badge-warning">Internal</span>
|
|
||||||
{{end}}
|
|
||||||
{{if eq .Data.Perm 3}}
|
|
||||||
<span class="badge badge-danger">Private</span>
|
<span class="badge badge-danger">Private</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
<br>
|
<br>
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-control custom-radio">
|
<div class="custom-control custom-radio">
|
||||||
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Perm 2}}checked=""{{end}}>
|
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Perm 2}}checked=""{{end}}>
|
||||||
<label class="custom-control-label" for="perm2">Internal</label>
|
<label class="custom-control-label" for="perm2">Private</label>
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="perm3" name="perm" value="3" class="custom-control-input" {{if eq .Data.Perm 3}}checked=""{{end}}>
|
|
||||||
<label class="custom-control-label" for="perm3">Private</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
||||||
|
|
|
||||||
|
|
@ -9,30 +9,31 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="section">Section</label>
|
<label class="col-form-label" for="section">Section</label>
|
||||||
<select class="form-control input-sm" id="section" name="section" required>
|
<select class="form-control input-sm" id="section" name="section" required>
|
||||||
<option>wiki</option>
|
{{range $section, $selected := .Data.Sections}}
|
||||||
<option>{{.User}}</option>
|
{{if $selected}}
|
||||||
|
<option value="{{$section}}" selected>{{$section}}</option>
|
||||||
|
{{else}}
|
||||||
|
<option value="{{$section}}">{{$section}}</option>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="title">Title</label>
|
<label class="col-form-label" for="title">Title</label>
|
||||||
<input class="form-control input-sm" type="text" id="title" name="title" placeholder="Title" value="{{.Data.Title}}" spellcheck="false" autocomplete="off" autofocus required>
|
<input class="form-control input-sm" type="text" id="title" name="title" placeholder="Title" value="{{.Data.Page.Title}}" spellcheck="false" autocomplete="off" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="markdown">Markdown</label>
|
<label class="col-form-label" for="markdown">Markdown</label>
|
||||||
<textarea class="form-control input-sm md-text" id="markdown" name="markdown" rows="30" spellcheck="false">{{.Data.Markdown}}</textarea>
|
<textarea class="form-control input-sm md-text" id="markdown" name="markdown" rows="30" spellcheck="false">{{.Data.Page.Markdown}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="custom-control custom-radio">
|
<div class="custom-control custom-radio">
|
||||||
<input type="radio" id="perm1" name="perm" value="1" class="custom-control-input" {{if eq .Data.Perm 1}}checked=""{{end}}>
|
<input type="radio" id="perm1" name="perm" value="1" class="custom-control-input" {{if eq .Data.Page.Perm 1}}checked=""{{end}}>
|
||||||
<label class="custom-control-label" for="perm1">Public</label>
|
<label class="custom-control-label" for="perm1">Public</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-control custom-radio">
|
<div class="custom-control custom-radio">
|
||||||
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Perm 2}}checked=""{{end}}>
|
<input type="radio" id="perm2" name="perm" value="2" class="custom-control-input" {{if eq .Data.Page.Perm 2}}checked=""{{end}}>
|
||||||
<label class="custom-control-label" for="perm2">Internal</label>
|
<label class="custom-control-label" for="perm2">Private</label>
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="perm3" name="perm" value="3" class="custom-control-input" {{if eq .Data.Perm 3}}checked=""{{end}}>
|
|
||||||
<label class="custom-control-label" for="perm3">Private</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@
|
||||||
<span class="badge badge-primary">Public</span>
|
<span class="badge badge-primary">Public</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .Data.Perm 2}}
|
{{if eq .Data.Perm 2}}
|
||||||
<span class="badge badge-warning">Internal</span>
|
|
||||||
{{end}}
|
|
||||||
{{if eq .Data.Perm 3}}
|
|
||||||
<span class="badge badge-danger">Private</span>
|
<span class="badge badge-danger">Private</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
<br>
|
<br>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="members">Members</label>
|
<label class="col-form-label" for="members">Members</label>
|
||||||
<select class="form-control input-sm" style="height:120px" id="members" name="members" multiple required>
|
<select class="form-control input-sm sections" id="members" name="members" multiple required>
|
||||||
{{range $user, $member := .Data.Members}}
|
{{range $user, $member := .Data.Members}}
|
||||||
{{if $member}}
|
{{if $member}}
|
||||||
<option value="{{$user}}" selected>{{$user}}</option>
|
<option value="{{$user}}" selected>{{$user}}</option>
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,14 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="members">Members</label>
|
<label class="col-form-label" for="members">Members</label>
|
||||||
<select class="form-control input-sm" style="height:120px" id="members" name="members" multiple required>
|
<select class="form-control input-sm sections" id="members" name="members" multiple required>
|
||||||
{{range $nil, $user := .Data.Members}}
|
{{range $user, $member := .Data.Members}}
|
||||||
|
{{if $member}}
|
||||||
|
<option value="{{$user}}" selected>{{$user}}</option>
|
||||||
|
{{else}}
|
||||||
<option value="{{$user}}">{{$user}}</option>
|
<option value="{{$user}}">{{$user}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<form class="form-horizontal" action="/user/del/{{.Data.Username}}" method="post">
|
<form class="form-horizontal" action="/user/del/{{.Data.Username}}" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/users" class="btn btn-sm btn-primary">Back</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
<button class="btn btn-sm btn-primary" type="submit">Update</button>
|
||||||
{{if .Admin}}
|
{{if .Admin}}
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/users" class="btn btn-sm btn-primary">Back</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="/" class="btn btn-sm btn-primary">Back</a>
|
<a href="/" class="btn btn-sm btn-primary">Back</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<input type="checkbox" id="admin" name="admin" value="0">
|
<input type="checkbox" id="admin" name="admin" value="0">
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
<a href="/user" class="btn btn-sm btn-primary">Back</a>
|
<a href="/users" class="btn btn-sm btn-primary">Back</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue