basic user manager search
reduce template sizes add PaginatorMod struct UserStore: add SearchOffset method add CountSearch method phrases: add panel_users_search_head add panel_users_search_name add panel_users_search_name_placeholder add panel_users_search_email add panel_users_search_email_placeholder add panel_users_search_button
This commit is contained in:
parent
e792dbc2c7
commit
c9e99c075d
|
@ -154,6 +154,13 @@ type Paginator struct {
|
|||
LastPage int
|
||||
}
|
||||
|
||||
type PaginatorMod struct {
|
||||
Params template.URL
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type CustomPagePage struct {
|
||||
*Header
|
||||
Page *CustomPage
|
||||
|
@ -604,10 +611,15 @@ type PanelMenuItemPage struct {
|
|||
Item MenuItem
|
||||
}
|
||||
|
||||
type PanelUserPageSearch struct {
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
type PanelUserPage struct {
|
||||
*BasePanelPage
|
||||
ItemList []*User
|
||||
Paginator
|
||||
Search PanelUserPageSearch
|
||||
PaginatorMod
|
||||
}
|
||||
|
||||
type PanelGroupPage struct {
|
||||
|
|
|
@ -21,6 +21,7 @@ type UserStore interface {
|
|||
Getn(id int) *User
|
||||
GetByName(name string) (*User, error)
|
||||
Exists(id int) bool
|
||||
SearchOffset(name, email string, offset, perPage int) (users []*User, err error)
|
||||
GetOffset(offset, perPage int) ([]*User, error)
|
||||
Each(f func(*User) error) error
|
||||
//BulkGet(ids []int) ([]*User, error)
|
||||
|
@ -29,6 +30,7 @@ type UserStore interface {
|
|||
Create(name, password, email string, group int, active bool) (int, error)
|
||||
Reload(id int) error
|
||||
Count() int
|
||||
CountSearch(name, email string) int
|
||||
|
||||
SetCache(cache UserCache)
|
||||
GetCache() UserCache
|
||||
|
@ -37,14 +39,17 @@ type UserStore interface {
|
|||
type DefaultUserStore struct {
|
||||
cache UserCache
|
||||
|
||||
get *sql.Stmt
|
||||
getByName *sql.Stmt
|
||||
getOffset *sql.Stmt
|
||||
getAll *sql.Stmt
|
||||
exists *sql.Stmt
|
||||
register *sql.Stmt
|
||||
nameExists *sql.Stmt
|
||||
count *sql.Stmt
|
||||
get *sql.Stmt
|
||||
getByName *sql.Stmt
|
||||
searchOffset *sql.Stmt
|
||||
getOffset *sql.Stmt
|
||||
getAll *sql.Stmt
|
||||
exists *sql.Stmt
|
||||
register *sql.Stmt
|
||||
nameExists *sql.Stmt
|
||||
|
||||
count *sql.Stmt
|
||||
countSearch *sql.Stmt
|
||||
}
|
||||
|
||||
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
||||
|
@ -57,15 +62,20 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
|||
allCols := "uid,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"
|
||||
// TODO: Add an admin version of registerStmt with more flexibility?
|
||||
return &DefaultUserStore{
|
||||
cache: cache,
|
||||
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(),
|
||||
getOffset: acc.Select(u).Columns(allCols).Orderby("uid ASC").Limit("?,?").Prepare(),
|
||||
getAll: acc.Select(u).Columns(allCols).Prepare(),
|
||||
cache: cache,
|
||||
|
||||
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(),
|
||||
searchOffset: acc.Select(u).Columns(allCols).Where("(name=? OR ?='') AND (email=? OR ?='')").Orderby("uid ASC").Limit("?,?").Prepare(),
|
||||
getOffset: acc.Select(u).Columns(allCols).Orderby("uid ASC").Limit("?,?").Prepare(),
|
||||
getAll: acc.Select(u).Columns(allCols).Prepare(),
|
||||
|
||||
exists: acc.Exists(u, "uid").Prepare(),
|
||||
register: acc.Insert(u).Columns("name,email,password,salt,group,is_super_admin,session,active,message,createdAt,lastActiveAt,lastLiked,oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here
|
||||
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(),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
|
@ -170,6 +180,30 @@ func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*User, err er
|
|||
}
|
||||
return users, rows.Err()
|
||||
}
|
||||
func (s *DefaultUserStore) SearchOffset(name, email string, offset, perPage int) (users []*User, err error) {
|
||||
rows, err := s.searchOffset.Query(name, name, email, email, offset, perPage)
|
||||
if err != nil {
|
||||
return users, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var embeds int
|
||||
for rows.Next() {
|
||||
u := &User{Loggedin: true}
|
||||
err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if embeds != -1 {
|
||||
u.ParseSettings = DefaultParseSettings.CopyPtr()
|
||||
u.ParseSettings.NoEmbed = embeds == 0
|
||||
}
|
||||
u.Init()
|
||||
s.cache.Set(u)
|
||||
users = append(users, u)
|
||||
}
|
||||
return users, rows.Err()
|
||||
}
|
||||
func (s *DefaultUserStore) Each(f func(*User) error) error {
|
||||
rows, e := s.getAll.Query()
|
||||
if e != nil {
|
||||
|
@ -350,6 +384,10 @@ func (s *DefaultUserStore) Count() (count int) {
|
|||
return Countf(s.count)
|
||||
}
|
||||
|
||||
func (s *DefaultUserStore) CountSearch(name, email string) (count int) {
|
||||
return Countf(s.countSearch, name, name, email, email)
|
||||
}
|
||||
|
||||
func (s *DefaultUserStore) SetCache(cache UserCache) {
|
||||
s.cache = cache
|
||||
}
|
||||
|
|
|
@ -895,6 +895,13 @@
|
|||
"panel_users_ban":"Ban",
|
||||
"panel_users_activate":"Activate",
|
||||
|
||||
"panel_users_search_head":"Search",
|
||||
"panel_users_search_name":"Name",
|
||||
"panel_users_search_name_placeholder":"John Doe",
|
||||
"panel_users_search_email":"Email",
|
||||
"panel_users_search_email_placeholder":"john.doe@example.com",
|
||||
"panel_users_search_button":"Search",
|
||||
|
||||
"panel_user_head":"User Editor",
|
||||
"panel_user_avatar":"Avatar",
|
||||
"panel_user_avatar_select":"Select",
|
||||
|
|
|
@ -2,7 +2,9 @@ package panel
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
c "github.com/Azareal/Gosora/common"
|
||||
|
@ -13,17 +15,45 @@ func Users(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError {
|
|||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
name := r.FormValue("s-name")
|
||||
email := r.FormValue("s-email")
|
||||
hasParam := name != "" || email != ""
|
||||
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
perPage := 15
|
||||
offset, page, lastPage := c.PageOffset(basePage.Stats.Users, page, perPage)
|
||||
userCount := basePage.Stats.Users
|
||||
if hasParam {
|
||||
userCount = c.Users.CountSearch(name, email)
|
||||
}
|
||||
offset, page, lastPage := c.PageOffset(userCount, page, perPage)
|
||||
|
||||
users, err := c.Users.GetOffset(offset, perPage)
|
||||
if err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
var users []*c.User
|
||||
var e error
|
||||
if hasParam {
|
||||
users, e = c.Users.SearchOffset(name, email, offset, perPage)
|
||||
} else {
|
||||
users, e = c.Users.GetOffset(offset, perPage)
|
||||
}
|
||||
if e != nil {
|
||||
return c.InternalError(e, w, r)
|
||||
}
|
||||
|
||||
name = url.QueryEscape(name)
|
||||
email = url.QueryEscape(email)
|
||||
search := c.PanelUserPageSearch{name, email}
|
||||
|
||||
var params string
|
||||
if hasParam {
|
||||
if name != "" {
|
||||
params += "s-name=" + name + "&"
|
||||
}
|
||||
if email != "" {
|
||||
params += "s-email=" + email + "&"
|
||||
}
|
||||
}
|
||||
pageList := c.Paginate(page, lastPage, 5)
|
||||
pi := c.PanelUserPage{basePage, users, c.Paginator{pageList, page, lastPage}}
|
||||
pi := c.PanelUserPage{basePage, users, search, c.PaginatorMod{template.URL(params), pageList, page, lastPage}}
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_users", &pi})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_themes_primary_themes"}}</h1></div>
|
||||
</div>
|
||||
<div id="panel_primary_themes" class="colstack_item panel_themes complex_rowlist">
|
||||
<div id="panel_primary_themes"class="colstack_item panel_themes complex_rowlist">
|
||||
{{range .PrimaryThemes}}
|
||||
<div class="theme_row rowitem editable_parent"{{if .FullImage}} style="background-image:url('/s/{{.FullImage}}');background-position:center;background-size:50%;background-repeat:no-repeat;"{{end}}>
|
||||
<div class="theme_row rowitem editable_parent"{{if .FullImage}}style="background-image:url('/s/{{.FullImage}}');background-position:center;background-size:50%;background-repeat:no-repeat;"{{end}}>
|
||||
<span style="float:left;">
|
||||
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size:17px;">{{.FriendlyName}}</a><br>
|
||||
<a href="/panel/themes/{{.Name}}"class="editable_block"style="font-size:17px;">{{.FriendlyName}}</a><br>
|
||||
<small class="panel_theme_author" style="margin-left:2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
|
||||
</span>
|
||||
<span class="panel_floater">
|
||||
{{if .MobileFriendly}}<span class="panel_tag panel_theme_mobile" title="{{lang "panel_themes_mobile_friendly_tooltip"}}" aria-label="{{lang "panel_themes_mobile_friendly_aria"}}">📱</span>{{end}}
|
||||
{{if .MobileFriendly}}<span class="panel_tag panel_theme_mobile"title="{{lang "panel_themes_mobile_friendly_tooltip"}}" aria-label="{{lang "panel_themes_mobile_friendly_aria"}}">📱</span>{{end}}
|
||||
{{if .Tag}}<span class="panel_tag panel_theme_tag">{{.Tag}}</span>{{end}}
|
||||
{{if .Active}}<span class="panel_tag panel_right_button">{{lang "panel_themes_default"}}</span>{{else}}<a href="/panel/themes/default/{{.Name}}?s={{$.CurrentUser.Session}}" class="panel_tag panel_right_button">{{lang "panel_themes_make_default"}}</a>{{end}}
|
||||
{{if .Active}}<span class="panel_tag panel_right_button">{{lang "panel_themes_default"}}</span>{{else}}<a href="/panel/themes/default/{{.Name}}?s={{$.CurrentUser.Session}}"class="panel_tag panel_right_button">{{lang "panel_themes_make_default"}}</a>{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -20,17 +20,17 @@
|
|||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_themes_variant_themes"}}</h1></div>
|
||||
</div>
|
||||
<div id="panel_variant_themes" class="colstack_item panel_themes">
|
||||
<div id="panel_variant_themes"class="colstack_item panel_themes">
|
||||
{{range .VariantThemes}}
|
||||
<div class="theme_row rowitem editable_parent"{{if .FullImage}} style="background-image:url('/s/{{.FullImage}}');background-position:center;background-size:50%;background-repeat:no-repeat;"{{end}}>
|
||||
<div class="theme_row rowitem editable_parent"{{if .FullImage}}style="background-image:url('/s/{{.FullImage}}');background-position:center;background-size:50%;background-repeat:no-repeat;"{{end}}>
|
||||
<span style="float:left;">
|
||||
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size:17px;">{{.FriendlyName}}</a><br>
|
||||
<small class="panel_theme_author" style="margin-left:2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
|
||||
<a href="/panel/themes/{{.Name}}"class="editable_block"style="font-size:17px;">{{.FriendlyName}}</a><br>
|
||||
<small class="panel_theme_author"style="margin-left:2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
|
||||
</span>
|
||||
<span class="panel_floater">
|
||||
{{if .MobileFriendly}}<span class="panel_tag panel_theme_mobile" title="{{lang "panel_themes_mobile_friendly_tooltip"}}" aria-label="{{lang "panel_themes_mobile_friendly_aria"}}">📱</span>{{end}}
|
||||
{{if .MobileFriendly}}<span class="panel_tag panel_theme_mobile"title="{{lang "panel_themes_mobile_friendly_tooltip"}}" aria-label="{{lang "panel_themes_mobile_friendly_aria"}}">📱</span>{{end}}
|
||||
{{if .Tag}}<span class="panel_tag panel_theme_tag">{{.Tag}}</span>{{end}}
|
||||
{{if .Active}}<span class="panel_tag panel_right_button">{{lang "panel_themes_default"}}</span>{{else}}<a href="/panel/themes/default/{{.Name}}?s={{$.CurrentUser.Session}}" class="panel_tag panel_right_button">{{lang "panel_themes_make_default"}}</a>{{end}}
|
||||
{{if .Active}}<span class="panel_tag panel_right_button">{{lang "panel_themes_default"}}</span>{{else}}<a href="/panel/themes/default/{{.Name}}?s={{$.CurrentUser.Session}}"class="panel_tag panel_right_button">{{lang "panel_themes_make_default"}}</a>{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_user_name"}}</a></div>
|
||||
<div class="formitem"><input form="user_form"name="name"type="text"value="{{.User.Name}}"placeholder="{{lang "panel_user_name_placeholder"}}" autocomplete="off"></div>
|
||||
<div class="formitem"><input form="user_form"name="name"type="text"value="{{.User.Name}}"placeholder="{{lang "panel_user_name_placeholder"}}"autocomplete="off"></div>
|
||||
</div>
|
||||
{{if .CurrentUser.Perms.EditUserPassword}}<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_user_password"}}</a></div>
|
||||
|
|
|
@ -1,20 +1,39 @@
|
|||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_users_head"}}</h1></div>
|
||||
</div>
|
||||
<div id="panel_users" class="colstack_item rowlist bgavatars">
|
||||
<div id="panel_users"class="colstack_item rowlist bgavatars">
|
||||
{{range .ItemList}}
|
||||
<div class="rowitem" style="background-image:url('{{.Avatar}}');">
|
||||
<div class="rowitem"style="background-image:url('{{.Avatar}}');">
|
||||
<a class="rowAvatar"{{if $.CurrentUser.Perms.EditUser}}href="/panel/users/edit/{{.ID}}"{{end}}>
|
||||
<img class="bgsub"src="{{.Avatar}}"alt="Avatar"aria-hidden="true">
|
||||
</a>
|
||||
<a class="rowTitle"{{if $.CurrentUser.Perms.EditUser}}href="/panel/users/edit/{{.ID}}"{{end}}>{{.Name}}</a>
|
||||
<span class="panel_floater">
|
||||
<a href="{{.Link}}" class="tag-mini profile_url">{{lang "panel_users_profile"}}</a>
|
||||
<a href="{{.Link}}"class="tag-mini profile_url">{{lang "panel_users_profile"}}</a>
|
||||
{{if (.Tag) and (.IsSuperMod)}}<span class="panel_tag">{{.Tag}}</span></span>{{end}}
|
||||
{{if .IsBanned}}<a href="/users/unban/{{.ID}}?s={{$.CurrentUser.Session}}" class="panel_tag panel_right_button ban_button">{{lang "panel_users_unban"}}</a>{{else if not .IsSuperMod}}<a href="/user/{{.ID}}#ban_user" class="panel_tag panel_right_button ban_button">{{lang "panel_users_ban"}}</a>{{end}}
|
||||
{{if not .Active}}<a href="/users/activate/{{.ID}}?s={{$.CurrentUser.Session}}" class="panel_tag panel_right_button">{{lang "panel_users_activate"}}</a>{{end}}
|
||||
{{if .IsBanned}}<a href="/users/unban/{{.ID}}?s={{$.CurrentUser.Session}}"class="panel_tag panel_right_button ban_button">{{lang "panel_users_unban"}}</a>{{else if not .IsSuperMod}}<a href="/user/{{.ID}}#ban_user"class="panel_tag panel_right_button ban_button">{{lang "panel_users_ban"}}</a>{{end}}
|
||||
{{if not .Active}}<a href="/users/activate/{{.ID}}?s={{$.CurrentUser.Session}}"class="panel_tag panel_right_button">{{lang "panel_users_activate"}}</a>{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "paginator.html" . }}
|
||||
{{template "paginator_mod.html" . }}
|
||||
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_users_search_head"}}</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item the_form">
|
||||
<form action="/panel/users/"method="get">
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_users_search_name"}}</a></div>
|
||||
<div class="formitem"><input name="s-name"type="text"{{if .Search.Name}}value="{{.Search.Name}}"{{end}}placeholder="{{lang "panel_users_search_name_placeholder"}}"></div>
|
||||
</div>
|
||||
{{if .CurrentUser.Perms.EditUserEmail}}<div class="formrow">
|
||||
<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>{{end}}
|
||||
<div class="formrow form_button_row">
|
||||
<div class="formitem"><button class="formbutton">{{lang "panel_users_search_button"}}</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
Loading…
Reference in New Issue