Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User details page #26713

Merged
merged 12 commits into from
Aug 31, 2023
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,7 @@ users.list_status_filter.is_prohibit_login = Prohibit Login
users.list_status_filter.not_prohibit_login = Allow Login
users.list_status_filter.is_2fa_enabled = 2FA Enabled
users.list_status_filter.not_2fa_enabled = 2FA Disabled
users.details = User Details

emails.email_manage_panel = User Email Management
emails.primary = Primary
Expand Down
58 changes: 58 additions & 0 deletions routers/web/admin/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password"
Expand All @@ -32,6 +34,7 @@ import (
const (
tplUsers base.TplName = "admin/user/list"
tplUserNew base.TplName = "admin/user/new"
tplUserView base.TplName = "admin/user/view"
tplUserEdit base.TplName = "admin/user/edit"
)

Expand Down Expand Up @@ -249,6 +252,61 @@ func prepareUserInfo(ctx *context.Context) *user_model.User {
return u
}

func ViewUser(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.users.details")
ctx.Data["PageIsAdminUsers"] = true
ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()

u := prepareUserInfo(ctx)
if ctx.Written() {
return
}

repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
ListAll: true,
},
OwnerID: u.ID,
OrderBy: db.SearchOrderByAlphabetically,
Private: true,
Collaborate: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("SearchRepository", err)
return
}

ctx.Data["Repos"] = repos
ctx.Data["ReposTotal"] = int(count)

emails, err := user_model.GetEmailAddresses(ctx.Doer.ID)
if err != nil {
ctx.ServerError("GetEmailAddresses", err)
return
}
ctx.Data["Emails"] = emails
ctx.Data["EmailsTotal"] = len(emails)

orgs, err := org_model.FindOrgs(org_model.FindOrgOptions{
ListOptions: db.ListOptions{
ListAll: true,
},
UserID: u.ID,
IncludePrivate: true,
})
if err != nil {
ctx.ServerError("FindOrgs", err)
return
}

ctx.Data["Users"] = orgs // needed to be able to use explore/user_list template
ctx.Data["OrgsTotal"] = len(orgs)

ctx.HTML(http.StatusOK, tplUserView)
}

// EditUser show editing user page
func EditUser(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.users.edit_account")
Expand Down
3 changes: 2 additions & 1 deletion routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,8 @@ func registerRoutes(m *web.Route) {
m.Group("/users", func() {
m.Get("", admin.Users)
m.Combo("/new").Get(admin.NewUser).Post(web.Bind(forms.AdminCreateUserForm{}), admin.NewUserPost)
m.Combo("/{userid}").Get(admin.EditUser).Post(web.Bind(forms.AdminEditUserForm{}), admin.EditUserPost)
m.Get("/{userid}", admin.ViewUser)
m.Combo("/{userid}/edit").Get(admin.EditUser).Post(web.Bind(forms.AdminEditUserForm{}), admin.EditUserPost)
m.Post("/{userid}/delete", admin.DeleteUser)
m.Post("/{userid}/avatar", web.Bind(forms.AvatarForm{}), admin.AvatarPost)
m.Post("/{userid}/avatar/delete", admin.DeleteAvatar)
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/layout_head.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{template "base/head" .ctxData}}
<div role="main" aria-label="{{.ctxData.Title}}" class="page-content {{.pageClass}}">
<div class="ui container">
<div class="ui container gt-mb-4">
{{template "base/alert" .ctxData}}
</div>
<div class="ui container flex-container">
Expand Down
13 changes: 6 additions & 7 deletions templates/admin/user/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -68,36 +68,35 @@
</th>
<th>{{.locale.Tr "email"}}</th>
<th>{{.locale.Tr "admin.users.activated"}}</th>
<th>{{.locale.Tr "admin.users.admin"}}</th>
<th>{{.locale.Tr "admin.users.restricted"}}</th>
<th>{{.locale.Tr "admin.users.2fa"}}</th>
denyskon marked this conversation as resolved.
Show resolved Hide resolved
<th>{{.locale.Tr "admin.users.repos"}}</th>
<th>{{.locale.Tr "admin.users.created"}}</th>
<th data-sortt-asc="lastlogin" data-sortt-desc="reverselastlogin">
{{.locale.Tr "admin.users.last_login"}}
{{SortArrow "lastlogin" "reverselastlogin" $.SortType false}}
</th>
<th>{{.locale.Tr "admin.users.edit"}}</th>
</tr>
</thead>
<tbody>
{{range .Users}}
<tr>
<td>{{.ID}}</td>
<td><a href="{{.HomeLink}}">{{.Name}}</a></td>
<td>
<a href="{{$.Link}}/{{.ID}}">{{.Name}}</a>
{{if .IsAdmin}}
<span class="ui basic label">{{$.locale.Tr "admin.users.admin"}}</span>
{{end}}
</td>
<td class="gt-ellipsis gt-max-width-12rem">{{.Email}}</td>
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if .IsAdmin}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{.NumRepos}}</td>
<td>{{DateTime "short" .CreatedUnix}}</td>
{{if .LastLoginUnix}}
<td>{{DateTime "short" .LastLoginUnix}}</td>
{{else}}
<td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td>
{{end}}
<td><a href="{{$.Link}}/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
</tr>
{{end}}
</tbody>
Expand Down
48 changes: 48 additions & 0 deletions templates/admin/user/view.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin view user")}}

<div class="admin-setting-content">
<div class="admin-responsive-columns">
<div class="gt-f1">
<h4 class="ui top attached header">
{{.Title}}
<div class="ui right">
<a class="ui primary tiny button" href="{{.Link}}/edit">{{ctx.Locale.Tr "admin.users.edit"}}</a>
</div>
</h4>
<div class="ui attached segment">
{{template "admin/user/view_details" .}}
</div>
</div>
<div class="gt-f1">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "admin.emails"}}
<div class="ui right">
{{.EmailsTotal}}
</div>
</h4>
<div class="ui attached segment">
{{template "admin/user/view_emails" .}}
</div>
</div>
</div>
<h4 class="ui top attached header">
{{ctx.Locale.Tr "admin.repositories"}}
<div class="ui right">
{{.ReposTotal}}
</div>
</h4>
<div class="ui attached segment">
{{template "explore/repo_list" .}}
</div>
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.organization"}}
<div class="ui right">
{{.OrgsTotal}}
</div>
</h4>
<div class="ui attached segment">
{{template "explore/user_list" .}}
</div>
</div>

{{template "admin/layout_footer" .}}
65 changes: 65 additions & 0 deletions templates/admin/user/view_details.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<div class="flex-list">
<div class="flex-item">
<div class="flex-item-leading">
{{ctx.AvatarUtils.Avatar .User 48}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">
{{template "shared/user/name" .User}}
{{if .User.IsAdmin}}
<span class="ui basic label">{{ctx.Locale.Tr "admin.users.admin"}}</span>
{{end}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.auth_source"}}:</b>
{{if eq .LoginSource.ID 0}}
{{ctx.Locale.Tr "admin.users.local"}}
{{else}}
{{.LoginSource.Name}}
{{end}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.activated"}}:</b>
{{if .User.IsActive}}
{{svg "octicon-check"}}
{{else}}
{{svg "octicon-x"}}
{{end}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.restricted"}}:</b>
{{if .User.IsRestricted}}
{{svg "octicon-check"}}
{{else}}
{{svg "octicon-x"}}
{{end}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "settings.visibility"}}:</b>
{{if .User.Visibility.IsLimited}}{{ctx.Locale.Tr "settings.visibility.limited"}}{{end}}
{{if .User.Visibility.IsPrivate}}{{ctx.Locale.Tr "settings.visibility.private"}}{{end}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.2fa"}}:</b>
{{if .TwoFactorEnabled}}
<span class="text green">{{svg "octicon-check"}}</span>
{{else}}
{{svg "octicon-x"}}
{{end}}
</div>
{{if .User.Location}}
<div class="flex-item-body">
<span class="flex-text-inline">{{svg "octicon-location"}}{{.User.Location}}</span>
</div>
{{end}}
{{if .User.Website}}
<div class="flex-item-body">
<span class="flex-text-inline">
{{svg "octicon-link"}}
<a target="_blank" href="{{.User.Website}}">{{.User.Website}}</a>
</span>
</div>
{{end}}
</div>
</div>
</div>
19 changes: 19 additions & 0 deletions templates/admin/user/view_emails.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="flex-list">
{{range .Emails}}
<div class="flex-item">
<div class="flex-item-main">
<div class="flex-text-block">
{{.Email}}
{{if .IsPrimary}}
<div class="ui primary label">{{ctx.Locale.Tr "settings.primary"}}</div>
{{end}}
{{if .IsActivated}}
<div class="ui green label">{{ctx.Locale.Tr "settings.activated"}}</div>
{{else}}
<div class="ui label">{{ctx.Locale.Tr "settings.requires_activation"}}</div>
{{end}}
</div>
</div>
</div>
{{end}}
</div>
31 changes: 31 additions & 0 deletions templates/explore/user_list.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="flex-list">
{{range .Users}}
<div class="flex-item flex-item-center">
<div class="flex-item-leading">
{{ctx.AvatarUtils.Avatar . 48}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">
{{template "shared/user/name" .}}
{{if .Visibility.IsPrivate}}
<span class="ui basic tiny label">{{ctx.Locale.Tr "repo.desc.private"}}</span>
{{end}}
</div>
<div class="flex-item-body">
{{if .Location}}
<span class="flex-text-inline">{{svg "octicon-location"}}{{.Location}}</span>
{{end}}
{{if and .Email (or (and $.ShowUserEmail $.IsSigned (not .KeepEmailPrivate)) $.PageIsAdminUsers)}}
<span class="flex-text-inline">
{{svg "octicon-mail"}}
<a href="mailto:{{.Email}}">{{.Email}}</a>
</span>
{{end}}
<span class="flex-text-inline">{{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix) | Safe}}</span>
</div>
</div>
</div>
{{else}}
<div class="flex-item">{{ctx.Locale.Tr "explore.user_no_results"}}</div>
{{end}}
</div>
32 changes: 1 addition & 31 deletions templates/explore/users.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,7 @@
<div class="ui container">
{{template "explore/search" .}}

<div class="flex-list">
{{range .Users}}
<div class="flex-item flex-item-center">
<div class="flex-item-leading">
{{ctx.AvatarUtils.Avatar . 48}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">
{{template "shared/user/name" .}}
{{if .Visibility.IsPrivate}}
<span class="ui basic tiny label">{{$.locale.Tr "repo.desc.private"}}</span>
{{end}}
</div>
<div class="flex-item-body">
{{if .Location}}
<span class="flex-text-inline">{{svg "octicon-location"}}{{.Location}}</span>
{{end}}
{{if and $.ShowUserEmail .Email $.IsSigned (not .KeepEmailPrivate)}}
<span class="flex-text-inline">
{{svg "octicon-mail"}}
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
</span>
{{end}}
<span class="flex-text-inline">{{svg "octicon-calendar"}}{{$.locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix) | Safe}}</span>
</div>
</div>
</div>
{{else}}
<div class="flex-item">{{$.locale.Tr "explore.user_no_results"}}</div>
{{end}}
</div>
{{template "explore/user_list" .}}

{{template "base/paginate" .}}
</div>
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/admin_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func testSuccessfullEdit(t *testing.T, formData user_model.User) {

func makeRequest(t *testing.T, formData user_model.User, headerCode int) {
session := loginUser(t, "user1")
csrf := GetCSRF(t, session, "/admin/users/"+strconv.Itoa(int(formData.ID)))
req := NewRequestWithValues(t, "POST", "/admin/users/"+strconv.Itoa(int(formData.ID)), map[string]string{
csrf := GetCSRF(t, session, "/admin/users/"+strconv.Itoa(int(formData.ID))+"/edit")
req := NewRequestWithValues(t, "POST", "/admin/users/"+strconv.Itoa(int(formData.ID))+"/edit", map[string]string{
"_csrf": csrf,
"user_name": formData.Name,
"login_name": formData.LoginName,
Expand All @@ -72,7 +72,7 @@ func TestAdminDeleteUser(t *testing.T) {

session := loginUser(t, "user1")

csrf := GetCSRF(t, session, "/admin/users/8")
csrf := GetCSRF(t, session, "/admin/users/8/edit")
req := NewRequestWithValues(t, "POST", "/admin/users/8/delete", map[string]string{
"_csrf": csrf,
})
Expand Down
7 changes: 7 additions & 0 deletions web_src/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@
.admin .table th {
white-space: nowrap;
}

.admin-responsive-columns {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1rem;
}