user manager: search by group

UserStore:
modify SearchOffset method signature
modify CountSearch method signature

phrases:
add panel_users_search_group
add panel_users_search_group_none
add panel_users_search_title

add missing paginator_mod template x.x
eliminate bytes here and there
This commit is contained in:
Azareal 2021-01-20 09:47:08 +10:00
parent 0bf1b6dbd3
commit f69e077347
8 changed files with 66 additions and 19 deletions

View File

@ -612,13 +612,17 @@ type PanelMenuItemPage struct {
} }
type PanelUserPageSearch struct { type PanelUserPageSearch struct {
Name string Name string
Email string Email string
Group int
Any bool
} }
type PanelUserPage struct { type PanelUserPage struct {
*BasePanelPage *BasePanelPage
ItemList []*User ItemList []*User
Search PanelUserPageSearch Groups []*Group
Search PanelUserPageSearch
PaginatorMod PaginatorMod
} }

View File

@ -21,7 +21,7 @@ type UserStore interface {
Getn(id int) *User Getn(id int) *User
GetByName(name string) (*User, error) GetByName(name string) (*User, error)
Exists(id int) bool Exists(id int) bool
SearchOffset(name, email string, offset, perPage int) (users []*User, err error) SearchOffset(name, email string, gid, offset, perPage int) (users []*User, err error)
GetOffset(offset, perPage int) ([]*User, error) GetOffset(offset, perPage int) ([]*User, error)
Each(f func(*User) error) error Each(f func(*User) error) error
//BulkGet(ids []int) ([]*User, error) //BulkGet(ids []int) ([]*User, error)
@ -30,7 +30,7 @@ type UserStore interface {
Create(name, password, email string, group int, active bool) (int, error) Create(name, password, email string, group int, active bool) (int, error)
Reload(id int) error Reload(id int) error
Count() int Count() int
CountSearch(name, email string) int CountSearch(name, email string, gid int) int
SetCache(cache UserCache) SetCache(cache UserCache)
GetCache() UserCache GetCache() UserCache
@ -66,7 +66,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
get: acc.Select(u).Columns("name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds,profile_comments,who_can_convo").Where("uid=?").Prepare(), get: acc.Select(u).Columns("name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds,profile_comments,who_can_convo").Where("uid=?").Prepare(),
getByName: acc.Select(u).Columns(allCols).Where("name=?").Prepare(), getByName: acc.Select(u).Columns(allCols).Where("name=?").Prepare(),
searchOffset: acc.Select(u).Columns(allCols).Where("(name=? OR ?='') AND (email=? OR ?='')").Orderby("uid ASC").Limit("?,?").Prepare(), searchOffset: acc.Select(u).Columns(allCols).Where("(name=? OR ?='') AND (email=? OR ?='') AND (group=? OR ?='')").Orderby("uid ASC").Limit("?,?").Prepare(),
getOffset: acc.Select(u).Columns(allCols).Orderby("uid ASC").Limit("?,?").Prepare(), getOffset: acc.Select(u).Columns(allCols).Orderby("uid ASC").Limit("?,?").Prepare(),
getAll: acc.Select(u).Columns(allCols).Prepare(), getAll: acc.Select(u).Columns(allCols).Prepare(),
@ -75,7 +75,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
nameExists: acc.Exists(u, "name").Prepare(), nameExists: acc.Exists(u, "name").Prepare(),
count: acc.Count(u).Prepare(), count: acc.Count(u).Prepare(),
countSearch: acc.Count(u).Where("(name=? OR ?='') AND (email=? OR ?='')").Prepare(), countSearch: acc.Count(u).Where("(name=? OR ?='') AND (email=? OR ?='') AND (group=? OR ?='')").Prepare(),
}, acc.FirstError() }, acc.FirstError()
} }
@ -180,8 +180,8 @@ func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*User, err er
} }
return users, rows.Err() return users, rows.Err()
} }
func (s *DefaultUserStore) SearchOffset(name, email string, offset, perPage int) (users []*User, err error) { func (s *DefaultUserStore) SearchOffset(name, email string, gid, offset, perPage int) (users []*User, err error) {
rows, err := s.searchOffset.Query(name, name, email, email, offset, perPage) rows, err := s.searchOffset.Query(name, name, email, email, gid, gid, offset, perPage)
if err != nil { if err != nil {
return users, err return users, err
} }
@ -384,8 +384,8 @@ func (s *DefaultUserStore) Count() (count int) {
return Countf(s.count) return Countf(s.count)
} }
func (s *DefaultUserStore) CountSearch(name, email string) (count int) { func (s *DefaultUserStore) CountSearch(name, email string, gid int) (count int) {
return Countf(s.countSearch, name, name, email, email) return Countf(s.countSearch, name, name, email, email, gid, gid)
} }
func (s *DefaultUserStore) SetCache(cache UserCache) { func (s *DefaultUserStore) SetCache(cache UserCache) {

View File

@ -900,7 +900,10 @@
"panel_users_search_name_placeholder":"John Doe", "panel_users_search_name_placeholder":"John Doe",
"panel_users_search_email":"Email", "panel_users_search_email":"Email",
"panel_users_search_email_placeholder":"john.doe@example.com", "panel_users_search_email_placeholder":"john.doe@example.com",
"panel_users_search_group":"Group",
"panel_users_search_group_none":"None",
"panel_users_search_button":"Search", "panel_users_search_button":"Search",
"panel_users_search_title":"Search Users",
"panel_user_head":"User Editor", "panel_user_head":"User Editor",
"panel_user_avatar":"Avatar", "panel_user_avatar":"Avatar",

View File

@ -21,20 +21,38 @@ func Users(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError {
if !u.Perms.EditUserEmail && email != "" { if !u.Perms.EditUserEmail && email != "" {
return c.LocalError("Only users with the EditUserEmail permission can search by email.", w, r, u) return c.LocalError("Only users with the EditUserEmail permission can search by email.", w, r, u)
} }
hasParam := name != "" || email != "" group := r.FormValue("s-group")
f := func(l ...string) bool {
for _, ll := range l {
if ll != "" {
return true
}
}
return false
}
hasParam := f(name, email, group)
gid, _ := strconv.Atoi(group)
/*if group == "" {
gid = -1
}*/
hasParam = hasParam && gid > 0
page, _ := strconv.Atoi(r.FormValue("page")) page, _ := strconv.Atoi(r.FormValue("page"))
perPage := 15 perPage := 15
userCount := basePage.Stats.Users userCount := basePage.Stats.Users
if hasParam { if hasParam {
userCount = c.Users.CountSearch(name, email) userCount = c.Users.CountSearch(name, email, gid)
} }
offset, page, lastPage := c.PageOffset(userCount, page, perPage) offset, page, lastPage := c.PageOffset(userCount, page, perPage)
allGroups, e := c.Groups.GetAll()
if e != nil {
return c.InternalError(e, w, r)
}
var users []*c.User var users []*c.User
var e error
if hasParam { if hasParam {
users, e = c.Users.SearchOffset(name, email, offset, perPage) users, e = c.Users.SearchOffset(name, email, gid, offset, perPage)
} else { } else {
users, e = c.Users.GetOffset(offset, perPage) users, e = c.Users.GetOffset(offset, perPage)
} }
@ -44,7 +62,7 @@ func Users(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError {
name = url.QueryEscape(name) name = url.QueryEscape(name)
email = url.QueryEscape(email) email = url.QueryEscape(email)
search := c.PanelUserPageSearch{name, email} search := c.PanelUserPageSearch{name, email, gid, hasParam}
var params string var params string
if hasParam { if hasParam {
@ -56,7 +74,7 @@ func Users(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError {
} }
} }
pageList := c.Paginate(page, lastPage, 5) pageList := c.Paginate(page, lastPage, 5)
pi := c.PanelUserPage{basePage, users, search, c.PaginatorMod{template.URL(params), pageList, page, lastPage}} pi := c.PanelUserPage{basePage, users, allGroups, search, c.PaginatorMod{template.URL(params), pageList, page, lastPage}}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_users", &pi}) return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_users", &pi})
} }

View File

@ -6,7 +6,7 @@
<div class="rowblock"> <div class="rowblock">
{{range .GuildList}}<div class="rowitem datarow"> {{range .GuildList}}<div class="rowitem datarow">
<span style="float:left;"> <span style="float:left;">
<a href="{{.Link}}" style="">{{.Name}}</a> <a href="{{.Link}}"style="">{{.Name}}</a>
<br><span class="rowsmall">{{.Desc}}</span> <br><span class="rowsmall">{{.Desc}}</span>
</span> </span>
<span style="float:right;"> <span style="float:right;">

View File

@ -0,0 +1,14 @@
{{if gt .LastPage 1}}
<div class="pageset">
{{if gt .Page 1}}<div class="pageitem pagefirst"><a href="?{{.Params}}page=1"aria-label="{{lang "paginator.first_page_aria"}}">{{lang "paginator.first_page"}}</a></div>
<div class="pageitem pageprev"><a href="?{{.Params}}page={{subtract .Page 1}}"rel="prev"aria-label="{{lang "paginator.prev_page_aria"}}">{{lang "paginator.prev_page"}}</a></div>
<link rel="prev"href="?{{.Params}}page={{subtract .Page 1}}">{{end}}
{{range .PageList}}
<div class="pageitem{{if eq . $.Page}} pagecurrent{{end}}"><a href="?{{$.Params}}page={{.}}">{{.}}</a></div>
{{end}}
{{if ne .LastPage .Page}}
<link rel="next"href="?{{.Params}}page={{add .Page 1}}">
<div class="pageitem pagenext"><a href="?{{.Params}}page={{add .Page 1}}"rel="next"aria-label="{{lang "paginator.next_page_aria"}}">{{lang "paginator.next_page"}}</a></div>
<div class="pageitem pagelast"><a href="?{{.Params}}page={{.LastPage}}"aria-label="{{lang "paginator.last_page_aria"}}">{{lang "paginator.last_page"}}</a></div>{{end}}
</div>
{{end}}

View File

@ -10,7 +10,7 @@
<div class="formitem avataritem"> <div class="formitem avataritem">
{{if .User.RawAvatar}}<img src="{{.User.Avatar}}"height=56 width=56>{{end}} {{if .User.RawAvatar}}<img src="{{.User.Avatar}}"height=56 width=56>{{end}}
<div class="avatarbuttons"> <div class="avatarbuttons">
<input form="avatar_form"id="select_avatar"name="avatar_file"type="file" required class="auto_hide"> <input form="avatar_form"id="select_avatar"name="avatar_file"type="file"required class="auto_hide">
<label for="select_avatar"class="formbutton">{{lang "panel_user_avatar_select"}}</label> <label for="select_avatar"class="formbutton">{{lang "panel_user_avatar_select"}}</label>
<button form="avatar_form"name="avatar_action"value=0>{{lang "panel_user_avatar_upload"}}</button> <button form="avatar_form"name="avatar_action"value=0>{{lang "panel_user_avatar_upload"}}</button>
{{if .User.RawAvatar}}<button form="remove_avatar_form"name="avatar_action"value=1>{{lang "panel_user_avatar_remove"}}</button>{{end}} {{if .User.RawAvatar}}<button form="remove_avatar_form"name="avatar_action"value=1>{{lang "panel_user_avatar_remove"}}</button>{{end}}

View File

@ -1,5 +1,5 @@
<div class="colstack_item colstack_head"> <div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_users_head"}}</h1></div> <div class="rowitem"><h1>{{if .Search.Any}}{{lang "panel_users_search_title"}}{{else}}{{lang "panel_users_head"}}{{end}}</h1></div>
</div> </div>
<div id="panel_users"class="colstack_item rowlist bgavatars"> <div id="panel_users"class="colstack_item rowlist bgavatars">
{{range .ItemList}} {{range .ItemList}}
@ -32,6 +32,14 @@
<div class="formitem formlabel"><a>{{lang "panel_users_search_email"}}</a></div> <div class="formitem formlabel"><a>{{lang "panel_users_search_email"}}</a></div>
<div class="formitem"><input name="s-email"type="email"{{if .Search.Email}}value="{{.Search.Email}}"{{end}}placeholder="{{lang "panel_users_search_email_placeholder"}}"></div> <div class="formitem"><input name="s-email"type="email"{{if .Search.Email}}value="{{.Search.Email}}"{{end}}placeholder="{{lang "panel_users_search_email_placeholder"}}"></div>
</div>{{end}} </div>{{end}}
{{if .CurrentUser.Perms.EditUserGroup}}<div class="formrow">
<div class="formitem formlabel"><a>{{lang "panel_users_search_group"}}</a></div>
<div class="formitem"><select name="s-group">
<option value="0"{{if eq $.Search.Group 0}}selected{{end}}>{{lang "panel_users_search_group_none"}}</option>
{{range .Groups}}{{if ne .ID 0}}
<option value="{{.ID}}"{{if eq $.Search.Group .ID}}selected{{end}}>{{.Name}}</option>
{{end}}{{end}}</select></div>
</div>{{end}}
<div class="formrow form_button_row"> <div class="formrow form_button_row">
<div class="formitem"><button class="formbutton">{{lang "panel_users_search_button"}}</button></div> <div class="formitem"><button class="formbutton">{{lang "panel_users_search_button"}}</button></div>
</div> </div>