Browse Source

Add communities

The functionality is complete. Only missing the static website support.
But the WUI could be heavily improved.
communities
meskio 1 year ago
parent
commit
fd12aca4c9
Signed by: meskio
GPG Key ID: 52B8F5AC97A2DA86
  1. 8
      ldap/errors.go
  2. 42
      ldap/group.go
  3. 4
      ldap/group_test.go
  4. 4
      ldap/user.go
  5. 27
      server/add_user.go
  6. 112
      server/admin.go
  7. 204
      server/community.go
  8. 19
      server/server.go
  9. 5
      server/template.go
  10. 33
      tmpl/add_community.html
  11. 34
      tmpl/communities.html
  12. 29
      tmpl/community.html
  13. 48
      tmpl/groups.html
  14. 2
      tmpl/index.html
  15. 11
      tmpl/navbar.html

8
ldap/errors.go

@ -0,0 +1,8 @@
package ldap
import (
"errors"
)
var ErrAlreadyExist = errors.New("Already exist an entry with this uid/cn")
var ErrNotFound = errors.New("Can't find the record in the ldap")

42
ldap/group.go

@ -10,9 +10,10 @@ import (
// Group has the ldap data of the group
type Group struct {
Name string
GID int
Members []string
Name string
GID int
Description string
Members []string
}
// InGroup checks if user is part of group
@ -46,7 +47,7 @@ func (l Ldap) GetGroup(name string) (Group, error) {
return Group{}, err
}
if len(groups) == 0 {
return Group{}, errors.New("Can't find group " + name)
return Group{}, ErrNotFound
}
return groups[0], nil
@ -78,9 +79,9 @@ func (l Ldap) UserGroups(user string) ([]Group, error) {
}
// AddGroup adds the group to ldap
func (l Ldap) AddGroup(group string) error {
func (l Ldap) AddGroup(group string, description string) error {
if _, err := l.GetGroup(group); err == nil {
return errors.New("Group '" + group + "' already exist, can't create it")
return ErrAlreadyExist
}
gid, err := l.getLastID("gidNumber")
@ -100,9 +101,29 @@ func (l Ldap) AddGroup(group string) error {
addRequest.Attribute("cn", []string{ldap.EscapeFilter(group)})
addRequest.Attribute("objectClass", []string{"top", "posixGroup"})
addRequest.Attribute("gidNumber", []string{strconv.Itoa(gid)})
if description != "" {
addRequest.Attribute("description", []string{ldap.EscapeFilter(description)})
}
return conn.Add(addRequest)
}
// UpdateGroupDescription set a new description for the group
func (l Ldap) UpdateGroupDescription(group string, description string) error {
conn, err := l.connect()
if err != nil {
return err
}
defer conn.Close()
modifyRequest := ldap.NewModifyRequest(l.groupDN(group), nil)
if description == "" {
modifyRequest.Replace("description", []string{})
} else {
modifyRequest.Replace("description", []string{ldap.EscapeFilter(description)})
}
return conn.Modify(modifyRequest)
}
// DelGroup removes the group in ldap
func (l Ldap) DelGroup(group string) error {
return l.del(l.groupDN(group))
@ -150,7 +171,7 @@ func (l Ldap) searchGroup(filter string) ([]Group, error) {
"ou=group,"+l.DC,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
filter,
[]string{"cn", "memberUid", "gidNumber"},
[]string{"cn", "memberUid", "gidNumber", "description"},
nil,
)
sr, err := conn.Search(searchRequest)
@ -167,8 +188,9 @@ func (l Ldap) searchGroup(filter string) ([]Group, error) {
func newGroup(entry *ldap.Entry) Group {
gid, _ := strconv.Atoi(entry.GetAttributeValue("gidNumber"))
return Group{
Name: entry.GetAttributeValue("cn"),
Members: entry.GetAttributeValues("memberUid"),
GID: gid,
Name: entry.GetAttributeValue("cn"),
Members: entry.GetAttributeValues("memberUid"),
Description: entry.GetAttributeValue("description"),
GID: gid,
}
}

4
ldap/group_test.go

@ -106,7 +106,7 @@ func TestAddDelGroups(t *testing.T) {
t.Errorf("group %s allready exist", name)
}
err = l.AddGroup(name)
err = l.AddGroup(name, "")
if err != nil {
t.Fatalf("CreateGroup(\"%s\") failed: %v", name, err)
}
@ -123,7 +123,7 @@ func TestAddDelGroups(t *testing.T) {
func TestAddExistingGroup(t *testing.T) {
l := testLdap(t)
err := l.AddGroup("adm")
err := l.AddGroup("adm", "")
if err == nil {
t.Errorf("Create group 'adm' didn't fail")
}

4
ldap/user.go

@ -184,7 +184,7 @@ func (l *Ldap) AddUser(user string, pass string, gid int) error {
entry, err := l.searchUser(user, conn)
if entry != nil {
return errors.New("User name already exist: " + user)
return ErrAlreadyExist
}
uid, err := l.getLastID("uidNumber")
@ -297,7 +297,7 @@ func (l Ldap) searchUser(user string, conn *ldap.Conn) (entry *ldap.Entry, err e
entry = sr.Entries[0]
return entry, nil
case 0:
return entry, errors.New("No user found")
return entry, ErrNotFound
default:
return entry, errors.New("More than one user found!!!")
}

27
server/add_user.go

@ -3,6 +3,7 @@ package server
import (
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"log"
"net/http"
@ -129,7 +130,7 @@ func (s *server) createInviteHandler(w http.ResponseWriter, r *http.Request) {
_, err := rand.Read(buff)
if err != nil {
log.Printf("An error has ocurred generating a random invite: %v", err)
s.addUserGroupHandler(w, r)
s.errorHandler(w, r)
return
}
invite := base64.URLEncoding.EncodeToString(buff)
@ -137,7 +138,7 @@ func (s *server) createInviteHandler(w http.ResponseWriter, r *http.Request) {
err = s.db.AddInvite(invite, response.User)
if err != nil {
log.Printf("An error has ocurred storing the invite (%s - %s): %v", invite, response.User, err)
s.addUserGroupHandler(w, r)
s.errorHandler(w, r)
return
}
@ -182,16 +183,26 @@ func (s *server) addUserHandler(w http.ResponseWriter, r *http.Request) {
}
_, err := s.ldap.GetUser(name)
if err == nil {
log.Println("Can't create user ", name, ": already exists")
response.execute("exists")
if err != nil {
if !errors.Is(err, ldap.ErrNotFound) {
log.Println("Can't create user ", name, ": already exists")
response.execute("exists")
} else {
log.Println("Error getting user: ", err)
s.errorHandler(w, r)
}
return
}
err = s.ldap.AddGroup(name)
err = s.ldap.AddGroup(name, "")
if err != nil {
log.Println("Error adding group: ", err)
s.errorHandler(w, r)
if errors.Is(err, ldap.ErrAlreadyExist) {
log.Println("Can't create user ", name, " there is already a community with this name")
response.execute("exists")
} else {
log.Println("Error adding group: ", err)
s.errorHandler(w, r)
}
return
}
group, err := s.ldap.GetGroup(name)

112
server/admin.go

@ -61,78 +61,6 @@ func (s *server) userHandler(w http.ResponseWriter, r *http.Request) {
response.execute(data)
}
func (s *server) addGroupHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("", w, r)
if !response.IsAdmin {
log.Println("Non admin attemp to add group")
s.forbiddenHandler(w, r)
return
}
groupName := r.FormValue("group")
err := s.ldap.AddGroup(groupName)
if err != nil {
log.Println("An error ocurred adding group '", groupName, "': ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/groups/"+groupName, http.StatusFound)
}
func (s *server) delGroupHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("", w, r)
if !response.IsAdmin {
log.Println("Non admin attemp to del group")
s.forbiddenHandler(w, r)
return
}
groupName := r.FormValue("group")
err := s.ldap.DelGroup(groupName)
if err != nil {
log.Println("An error ocurred deleting group '", groupName, "': ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/groups/", http.StatusFound)
}
func (s *server) groupsHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("groups", w, r)
if !response.IsAdmin {
log.Println("Non admin attemp to list users")
s.forbiddenHandler(w, r)
return
}
groups, err := s.ldap.ListGroups()
if err != nil {
log.Println("An error ocurred retrieving group list: ", err)
s.errorHandler(w, r)
return
}
response.execute(groups)
}
func (s *server) groupHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupName := vars["name"]
response := s.newResponse("group", w, r)
if !response.IsAdmin && !s.ldap.InGroup(response.User, groupName) {
log.Println("Non admin attemp to list users")
s.forbiddenHandler(w, r)
return
}
group, err := s.ldap.GetGroup(groupName)
if err != nil {
log.Println("An error ocurred retrieving group '", groupName, "': ", err)
s.errorHandler(w, r)
return
}
response.execute(group)
}
func (s *server) roleHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("", w, r)
if !response.IsAdmin {
@ -243,43 +171,3 @@ func (s *server) shellHandler(w http.ResponseWriter, r *http.Request) {
}
http.Redirect(w, r, "/users/"+userName, http.StatusFound)
}
func (s *server) addUserGroupHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("", w, r)
if !response.IsAdmin {
log.Println("Non admin attemp to add user to group")
s.forbiddenHandler(w, r)
return
}
vars := mux.Vars(r)
groupName := vars["name"]
user := r.FormValue("user")
err := s.ldap.AddUserGroup(user, groupName)
if err != nil {
log.Println("An error ocurred adding user '", user, "' to ", groupName, ": ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/groups/"+groupName, http.StatusFound)
}
func (s *server) delUserGroupHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("", w, r)
if !response.IsAdmin {
log.Println("Non admin attemp to del user to group")
s.forbiddenHandler(w, r)
return
}
vars := mux.Vars(r)
groupName := vars["name"]
user := r.FormValue("user")
err := s.ldap.DelUserGroup(user, groupName)
if err != nil {
log.Println("An error ocurred del user '", user, "' to ", groupName, ": ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/groups/"+groupName, http.StatusFound)
}

204
server/community.go

@ -0,0 +1,204 @@
package server
import (
"errors"
"log"
"net/http"
"git.sindominio.net/sindominio/lowry/ldap"
"github.com/gorilla/mux"
)
const (
maxCommunities = 4
)
func (s *server) addCommunityHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("add_community", w, r)
ok, err := s.canCreateCommunity(response)
if err != nil {
log.Println("An error ocurred checking if can create community: ", err)
s.errorHandler(w, r)
return
}
status := ""
if !ok {
status = "quota"
}
response.execute(status)
}
func (s *server) postAddCommunityHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("add_community", w, r)
ok, err := s.canCreateCommunity(response)
if err != nil {
log.Println("An error ocurred checking if can create community: ", err)
s.errorHandler(w, r)
return
}
if !ok {
response.execute("quota")
return
}
communityName := r.FormValue("community")
description := r.FormValue("description")
err = s.ldap.AddGroup(communityName, description)
if err != nil {
if errors.Is(err, ldap.ErrAlreadyExist) {
response.execute("exists")
} else {
log.Println("An error ocurred adding community '", communityName, "': ", err)
s.errorHandler(w, r)
}
return
}
err = s.ldap.AddUserGroup(response.User, communityName)
if err != nil {
log.Println("An error ocurred adding user ", response.User, " to community '", communityName, "': ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/community/"+communityName, http.StatusFound)
}
func (s *server) delCommunityHandler(w http.ResponseWriter, r *http.Request) {
communityName := r.FormValue("community")
response := s.newResponse("", w, r)
if !response.IsAdmin && !s.ldap.InGroup(response.User, communityName) {
log.Println(response.User, "don't have rights to delete", communityName, "community")
s.forbiddenHandler(w, r)
return
}
err := s.ldap.DelGroup(communityName)
if err != nil {
log.Println("An error ocurred deleting community '", communityName, "': ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/communities/mine/", http.StatusFound)
}
func (s *server) communitiesHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("communities", w, r)
if !response.IsAdmin {
log.Println("Non admin attemp to list communities")
s.forbiddenHandler(w, r)
return
}
communities, err := s.ldap.ListGroups()
if err != nil {
log.Println("An error ocurred retrieving community list: ", err)
s.errorHandler(w, r)
return
}
response.execute(communities)
}
func (s *server) userCommunitiesHandler(w http.ResponseWriter, r *http.Request) {
response := s.newResponse("communities", w, r)
communities, err := s.ldap.UserGroups(response.User)
if err != nil {
log.Println("An error ocurred retrieving community list: ", err)
s.errorHandler(w, r)
return
}
response.execute(communities)
}
func (s *server) communityHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
communityName := vars["name"]
response := s.newResponse("community", w, r)
if !response.IsAdmin && !s.ldap.InGroup(response.User, communityName) {
log.Println("Attemp to list community without rights")
s.forbiddenHandler(w, r)
return
}
community, err := s.ldap.GetGroup(communityName)
if err != nil {
log.Println("An error ocurred retrieving community '", communityName, "': ", err)
s.errorHandler(w, r)
return
}
response.execute(community)
}
func (s *server) addUserCommunityHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
communityName := vars["name"]
response := s.newResponse("", w, r)
if !response.IsAdmin && !s.ldap.InGroup(response.User, communityName) {
log.Println("Attemp to add user to community without rights")
s.forbiddenHandler(w, r)
return
}
user := r.FormValue("user")
err := s.ldap.AddUserGroup(user, communityName)
if err != nil {
log.Println("An error ocurred adding user '", user, "' to ", communityName, ": ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/community/"+communityName, http.StatusFound)
}
func (s *server) delUserCommunityHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
communityName := vars["name"]
response := s.newResponse("", w, r)
if !response.IsAdmin && !s.ldap.InGroup(response.User, communityName) {
log.Println("Attemp to del user to community without rights")
s.forbiddenHandler(w, r)
return
}
user := r.FormValue("user")
err := s.ldap.DelUserGroup(user, communityName)
if err != nil {
log.Println("An error ocurred del user '", user, "' to ", communityName, ": ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/community/"+communityName, http.StatusFound)
}
func (s *server) descriptionCommunityHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
communityName := vars["name"]
response := s.newResponse("", w, r)
if !response.IsAdmin && !s.ldap.InGroup(response.User, communityName) {
log.Println("Attemp to edit description without rights of community", communityName)
s.forbiddenHandler(w, r)
return
}
description := r.FormValue("description")
err := s.ldap.UpdateGroupDescription(communityName, description)
if err != nil {
log.Println("An error ocurred to set description to ", communityName, ": ", err)
s.errorHandler(w, r)
return
}
http.Redirect(w, r, "/community/"+communityName, http.StatusFound)
}
func (s *server) canCreateCommunity(r responseT) (bool, error) {
if r.User == "" {
return false, nil
}
if r.Role == ldap.Sindominante {
return true, nil
}
count, err := s.db.CountCommunities(r.User)
return count <= maxCommunities, err
}

19
server/server.go

@ -41,23 +41,30 @@ func Serve(addr string, l *ldap.Ldap, m *mail.Mail, ldb *db.DB, g *gitea.Gitea)
r.HandleFunc("/login/", s.loginHandler)
r.HandleFunc("/logout/", s.logoutHandler).Methods("POST")
r.HandleFunc("/password/", s.passwordHandler)
r.HandleFunc("/users/", s.usersHandler)
r.HandleFunc("/users/{name}", s.userHandler)
r.HandleFunc("/users/{name}/role/", s.roleHandler).Methods("POST")
r.HandleFunc("/users/{name}/password/", s.passwdadmHandler).Methods("POST")
r.HandleFunc("/users/{name}/shell/", s.shellHandler).Methods("POST")
r.HandleFunc("/users/{name}/lock/", s.lockHandler).Methods("POST")
r.HandleFunc("/unlock/", s.unlockHandler)
r.HandleFunc("/invites/", s.listInvitesHandler)
r.HandleFunc("/invites/{invite}/del/", s.deleteInviteHandler)
r.HandleFunc("/adduser/", s.createInviteHandler)
r.HandleFunc("/adduser/{invite}", s.addUserHandler)
r.HandleFunc("/groups/", s.groupsHandler)
r.HandleFunc("/groups/add/", s.addGroupHandler).Methods("POST")
r.HandleFunc("/groups/del/", s.delGroupHandler).Methods("POST")
r.HandleFunc("/groups/{name}", s.groupHandler)
r.HandleFunc("/groups/{name}/add/", s.addUserGroupHandler).Methods("POST")
r.HandleFunc("/groups/{name}/del/", s.delUserGroupHandler).Methods("POST")
r.HandleFunc("/communities/", s.communitiesHandler)
r.HandleFunc("/communities/mine/", s.userCommunitiesHandler)
r.HandleFunc("/communities/add/", s.addCommunityHandler).Methods("GET")
r.HandleFunc("/communities/add/", s.postAddCommunityHandler).Methods("POST")
r.HandleFunc("/communities/del/", s.delCommunityHandler).Methods("POST")
r.HandleFunc("/community/{name}", s.communityHandler)
r.HandleFunc("/community/{name}/add/", s.addUserCommunityHandler).Methods("POST")
r.HandleFunc("/community/{name}/del/", s.delUserCommunityHandler).Methods("POST")
r.HandleFunc("/community/{name}/description/", s.descriptionCommunityHandler).Methods("POST")
r.HandleFunc("/gitea/", s.giteaHandler)
r.HandleFunc("/bundle.js", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "dist/bundle.js") })

5
server/template.go

@ -41,8 +41,9 @@ func initTemplate() *template.Template {
"tmpl/invites.html",
"tmpl/adduser.html",
"tmpl/adduser_success.html",
"tmpl/group.html",
"tmpl/groups.html",
"tmpl/community.html",
"tmpl/communities.html",
"tmpl/add_community.html",
"tmpl/gitea.html",
"tmpl/load-password.js",
))

33
tmpl/add_community.html

@ -0,0 +1,33 @@
{{template "header.html"}}
{{template "header_close.html"}}
{{template "navbar.html" .}}
<div class="container">
<br />
{{if eq .Data "quota"}}
<p>Has excedido el maximo de colectividades que puedes crear. Solo se pueden crear 4 colectividades cada 3 meses. Si tienes alguna razón importante para crear mas contacta con la asamblea de sindominio para ver si pueden hacer algo. O si no esperate unas semanas a ver si vuelves a poder crear colectividades.</p>
{{else if eq .Data "exists"}}
<div class="alert alert-warning" role="alert">
El nombre de comunidad que habías elegido ya existe.
</div>
{{end}}
{{if ne .Data "quota"}}
<h3>Añade una colectividad</h3>
<form action="/communities/add/" method="post">
<div class="form-group">
<label for="community">Nombre:</label>
<input type="text" class="form-control" id="community" name="community" placeholder="Nombre">
</div>
<div class="form-group">
<label for="community">Description:</label>
<textarea class="form-control" id="description" name="description"></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">añadir</button>
</div>
</form>
{{end}}
</div>
{{template "footer.html"}}

34
tmpl/communities.html

@ -0,0 +1,34 @@
{{template "header.html"}}
{{template "header_close.html"}}
{{template "navbar.html" .}}
<br />
<div class="float-right">
<a class="btn btn-primary" href="/communities/add/">Crea una colectividad</a>
</div>
<br />
<br />
<table class="table">
<thead>
<tr>
<th scope="col">Nombre</th>
<th scope="col">Cuentas</th>
<th scope="col">GID</th>
</tr>
</thead>
<tbody>
{{range .Data}}
<tr>
<th scope="row"><a href="/community/{{.Name}}">{{.Name}}</a></th>
<td>
{{range .Members}}
<a href="/users/{{.}}">{{.}}</a>,
{{end}}
</td>
<td>{{.GID}}</a></th>
</tr>
{{end}}
</tbody>
</table>
{{template "footer.html"}}

29
tmpl/group.html → tmpl/community.html

@ -2,12 +2,12 @@
{{template "header_close.html"}}
{{template "navbar.html" .}}
{{$isAdmin := .IsAdmin}}
{{with .Data}}
<br />
<div class="row justify-content-center">
<dl>
<dt>Nombre</dt><dd>{{.Name}}</dd>
<dt>Descripcion</dt><dd>{{.Description}}</dd>
<dt>Cuentas</dt><dd>
{{range .Members}}
<a href="/users/{{.}}">{{.}}</a>,
@ -17,10 +17,9 @@
</dl>
</div>
{{if $isAdmin}}
<br />
<div class="row justify-content-center">
<form action="/groups/{{.Name}}/add/" method="post">
<form action="/community/{{.Name}}/add/" method="post">
<div class="row">
<div class="col">
<label for="user">Añade cuenta:</label>
@ -35,7 +34,7 @@
</form>
</div>
<div class="row justify-content-center">
<form action="/groups/{{.Name}}/del/" method="post">
<form action="/community/{{.Name}}/del/" method="post">
<div class="row">
<div class="col">
<label for="user">Elimina cuenta:</label>
@ -49,16 +48,30 @@
</div>
</form>
</div>
<div class="row justify-content-center">
<form action="/community/{{.Name}}/description/" method="post">
<div class="row">
<div class="col">
<label for="user">Cambia la descripción:</label>
</div>
<div class="col">
<textarea class="form-control" id="description" name="description">{{.Description}}</textarea>
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Enviar</button>
</div>
</div>
</form>
</div>
<br />
<div class="row justify-content-center">
<form action="/groups/del/" method="post" onsubmit="return confirm('Do you really want to submit the form?');">
<input type="hidden" class="form-control" id="group" name="group" value="{{.Name}}">
<form action="/communities/del/" method="post" onsubmit="return confirm('¿de verdad quieres borrar la colectividad {{.Name}}?');">
<input type="hidden" class="form-control" id="community" name="community" value="{{.Name}}">
<div class="row">
<button type="submit" class="btn btn-lg btn-block btn-danger">Elimina el grupo {{.Name}}</button>
<button type="submit" class="btn btn-lg btn-block btn-danger">Elimina el colectividad {{.Name}}</button>
</div>
</form>
</div>
{{end}}
{{end}}
{{template "footer.html"}}

48
tmpl/groups.html

@ -1,48 +0,0 @@
{{template "header.html"}}
{{template "header_close.html"}}
{{template "navbar.html" .}}
{{if .IsAdmin}}
<br />
<div class="row justify-content-end">
<form action="/groups/add/" method="post">
<div class="row">
<div class="col">
<label for="group">Añade grupo:</label>
</div>
<div class="col">
<input type="text" class="form-control" id="group" name="group" placeholder="Grupo">
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Enviar</button>
</div>
</div>
</form>
</div>
{{end}}
<br />
<table class="table">
<thead>
<tr>
<th scope="col">Nombre</th>
<th scope="col">Cuentas</th>
<th scope="col">GID</th>
</tr>
</thead>
<tbody>
{{range .Data}}
<tr>
<th scope="row"><a href="/groups/{{.Name}}">{{.Name}}</a></th>
<td>
{{range .Members}}
<a href="/users/{{.}}">{{.}}</a>,
{{end}}
</td>
<td>{{.GID}}</a></th>
</tr>
{{end}}
</tbody>
</table>
{{template "footer.html"}}

2
tmpl/index.html

@ -44,6 +44,8 @@
<p>Bienvenida a lowry, nuestro burócrata preferido. ¿qué quieres hacer hoy?</p>
<ul class="list-group">
<li class="list-group-item"><a href="/password/">Cambiar la contraseña</a></li>
<li class="list-group-item"><a href="/community/">Ver mis colectividades</a></li>
<li class="list-group-item"><a href="/community/add/">Crear una colectividad</a></li>
{{if eq (printf "%v" .Role) "sindominante"}}
<li class="list-group-item"><a href="/adduser/">Invitar amiga a SinDominio</a></li>
{{end}}

11
tmpl/navbar.html

@ -13,21 +13,22 @@
<li class="nav-item {{if eq .Section "password"}}active{{end}}">
<a class="nav-link" href="/password/">Contraseña</a>
</li>
<li class="nav-item {{if eq .Section "community" "communities" "add_community"}}active{{end}}">
<a class="nav-link" href="/communities/mine/">Mis colectividades</a>
</li>
</ul>
<hr />
{{if eq (printf "%v" .Role) "sindominante"}}
<ul class="navbar-nav ml-mr-auto">
<li class="nav-item {{if eq .Section "invites"}}active{{end}}">
<a class="nav-link" href="/invites/">Invitaciones</a>
</li>
<li class="nav-item {{if eq .Section "adduser"}}active{{end}}">
<a class="nav-link" href="/adduser/">Invitar amiga</a>
</li>
{{if .IsAdmin}}
<li class="nav-item {{if eq .Section "user" "users"}}active{{end}}">
<a class="nav-link" href="/users/">Cuentas</a>
</li>
<li class="nav-item {{if eq .Section "group" "groups"}}active{{end}}">
<a class="nav-link" href="/groups/">Grupos</a>
<li class="nav-item">
<a class="nav-link" href="/communities/">Colectividades</a>
</li>
{{end}}
</ul>

Loading…
Cancel
Save