You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

users.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2020 The Gitea Authors.
  3. // SPDX-License-Identifier: MIT
  4. package admin
  5. import (
  6. "errors"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/models/auth"
  13. "code.gitea.io/gitea/models/db"
  14. org_model "code.gitea.io/gitea/models/organization"
  15. repo_model "code.gitea.io/gitea/models/repo"
  16. user_model "code.gitea.io/gitea/models/user"
  17. "code.gitea.io/gitea/modules/auth/password"
  18. "code.gitea.io/gitea/modules/base"
  19. "code.gitea.io/gitea/modules/log"
  20. "code.gitea.io/gitea/modules/optional"
  21. "code.gitea.io/gitea/modules/setting"
  22. "code.gitea.io/gitea/modules/util"
  23. "code.gitea.io/gitea/modules/web"
  24. "code.gitea.io/gitea/routers/web/explore"
  25. user_setting "code.gitea.io/gitea/routers/web/user/setting"
  26. "code.gitea.io/gitea/services/context"
  27. "code.gitea.io/gitea/services/forms"
  28. "code.gitea.io/gitea/services/mailer"
  29. user_service "code.gitea.io/gitea/services/user"
  30. )
  31. const (
  32. tplUsers base.TplName = "admin/user/list"
  33. tplUserNew base.TplName = "admin/user/new"
  34. tplUserView base.TplName = "admin/user/view"
  35. tplUserEdit base.TplName = "admin/user/edit"
  36. )
  37. // UserSearchDefaultAdminSort is the default sort type for admin view
  38. const UserSearchDefaultAdminSort = "alphabetically"
  39. // Users show all the users
  40. func Users(ctx *context.Context) {
  41. ctx.Data["Title"] = ctx.Tr("admin.users")
  42. ctx.Data["PageIsAdminUsers"] = true
  43. extraParamStrings := map[string]string{}
  44. statusFilterKeys := []string{"is_active", "is_admin", "is_restricted", "is_2fa_enabled", "is_prohibit_login"}
  45. statusFilterMap := map[string]string{}
  46. for _, filterKey := range statusFilterKeys {
  47. paramKey := "status_filter[" + filterKey + "]"
  48. paramVal := ctx.FormString(paramKey)
  49. statusFilterMap[filterKey] = paramVal
  50. if paramVal != "" {
  51. extraParamStrings[paramKey] = paramVal
  52. }
  53. }
  54. sortType := ctx.FormString("sort")
  55. if sortType == "" {
  56. sortType = UserSearchDefaultAdminSort
  57. ctx.SetFormString("sort", sortType)
  58. }
  59. ctx.PageData["adminUserListSearchForm"] = map[string]any{
  60. "StatusFilterMap": statusFilterMap,
  61. "SortType": sortType,
  62. }
  63. explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
  64. Actor: ctx.Doer,
  65. Type: user_model.UserTypeIndividual,
  66. ListOptions: db.ListOptions{
  67. PageSize: setting.UI.Admin.UserPagingNum,
  68. },
  69. SearchByEmail: true,
  70. IsActive: util.OptionalBoolParse(statusFilterMap["is_active"]),
  71. IsAdmin: util.OptionalBoolParse(statusFilterMap["is_admin"]),
  72. IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]),
  73. IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
  74. IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
  75. IncludeReserved: true, // administrator needs to list all acounts include reserved, bot, remote ones
  76. ExtraParamStrings: extraParamStrings,
  77. }, tplUsers)
  78. }
  79. // NewUser render adding a new user page
  80. func NewUser(ctx *context.Context) {
  81. ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
  82. ctx.Data["PageIsAdminUsers"] = true
  83. ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
  84. ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
  85. ctx.Data["login_type"] = "0-0"
  86. sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
  87. IsActive: optional.Some(true),
  88. })
  89. if err != nil {
  90. ctx.ServerError("auth.Sources", err)
  91. return
  92. }
  93. ctx.Data["Sources"] = sources
  94. ctx.Data["CanSendEmail"] = setting.MailService != nil
  95. ctx.HTML(http.StatusOK, tplUserNew)
  96. }
  97. // NewUserPost response for adding a new user
  98. func NewUserPost(ctx *context.Context) {
  99. form := web.GetForm(ctx).(*forms.AdminCreateUserForm)
  100. ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
  101. ctx.Data["PageIsAdminUsers"] = true
  102. ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
  103. ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
  104. sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
  105. IsActive: optional.Some(true),
  106. })
  107. if err != nil {
  108. ctx.ServerError("auth.Sources", err)
  109. return
  110. }
  111. ctx.Data["Sources"] = sources
  112. ctx.Data["CanSendEmail"] = setting.MailService != nil
  113. if ctx.HasError() {
  114. ctx.HTML(http.StatusOK, tplUserNew)
  115. return
  116. }
  117. u := &user_model.User{
  118. Name: form.UserName,
  119. Email: form.Email,
  120. Passwd: form.Password,
  121. LoginType: auth.Plain,
  122. }
  123. overwriteDefault := &user_model.CreateUserOverwriteOptions{
  124. IsActive: optional.Some(true),
  125. Visibility: &form.Visibility,
  126. }
  127. if len(form.LoginType) > 0 {
  128. fields := strings.Split(form.LoginType, "-")
  129. if len(fields) == 2 {
  130. lType, _ := strconv.ParseInt(fields[0], 10, 0)
  131. u.LoginType = auth.Type(lType)
  132. u.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64)
  133. u.LoginName = form.LoginName
  134. }
  135. }
  136. if u.LoginType == auth.NoType || u.LoginType == auth.Plain {
  137. if len(form.Password) < setting.MinPasswordLength {
  138. ctx.Data["Err_Password"] = true
  139. ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserNew, &form)
  140. return
  141. }
  142. if !password.IsComplexEnough(form.Password) {
  143. ctx.Data["Err_Password"] = true
  144. ctx.RenderWithErr(password.BuildComplexityError(ctx.Locale), tplUserNew, &form)
  145. return
  146. }
  147. if err := password.IsPwned(ctx, form.Password); err != nil {
  148. ctx.Data["Err_Password"] = true
  149. errMsg := ctx.Tr("auth.password_pwned")
  150. if password.IsErrIsPwnedRequest(err) {
  151. log.Error(err.Error())
  152. errMsg = ctx.Tr("auth.password_pwned_err")
  153. }
  154. ctx.RenderWithErr(errMsg, tplUserNew, &form)
  155. return
  156. }
  157. u.MustChangePassword = form.MustChangePassword
  158. }
  159. if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
  160. switch {
  161. case user_model.IsErrUserAlreadyExist(err):
  162. ctx.Data["Err_UserName"] = true
  163. ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplUserNew, &form)
  164. case user_model.IsErrEmailAlreadyUsed(err):
  165. ctx.Data["Err_Email"] = true
  166. ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserNew, &form)
  167. case user_model.IsErrEmailInvalid(err), user_model.IsErrEmailCharIsNotSupported(err):
  168. ctx.Data["Err_Email"] = true
  169. ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form)
  170. case db.IsErrNameReserved(err):
  171. ctx.Data["Err_UserName"] = true
  172. ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(db.ErrNameReserved).Name), tplUserNew, &form)
  173. case db.IsErrNamePatternNotAllowed(err):
  174. ctx.Data["Err_UserName"] = true
  175. ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
  176. case db.IsErrNameCharsNotAllowed(err):
  177. ctx.Data["Err_UserName"] = true
  178. ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
  179. default:
  180. ctx.ServerError("CreateUser", err)
  181. }
  182. return
  183. }
  184. log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name)
  185. // Send email notification.
  186. if form.SendNotify {
  187. mailer.SendRegisterNotifyMail(u)
  188. }
  189. ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
  190. ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
  191. }
  192. func prepareUserInfo(ctx *context.Context) *user_model.User {
  193. u, err := user_model.GetUserByID(ctx, ctx.ParamsInt64(":userid"))
  194. if err != nil {
  195. if user_model.IsErrUserNotExist(err) {
  196. ctx.Redirect(setting.AppSubURL + "/admin/users")
  197. } else {
  198. ctx.ServerError("GetUserByID", err)
  199. }
  200. return nil
  201. }
  202. ctx.Data["User"] = u
  203. if u.LoginSource > 0 {
  204. ctx.Data["LoginSource"], err = auth.GetSourceByID(ctx, u.LoginSource)
  205. if err != nil {
  206. ctx.ServerError("auth.GetSourceByID", err)
  207. return nil
  208. }
  209. } else {
  210. ctx.Data["LoginSource"] = &auth.Source{}
  211. }
  212. sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{})
  213. if err != nil {
  214. ctx.ServerError("auth.Sources", err)
  215. return nil
  216. }
  217. ctx.Data["Sources"] = sources
  218. hasTOTP, err := auth.HasTwoFactorByUID(ctx, u.ID)
  219. if err != nil {
  220. ctx.ServerError("auth.HasTwoFactorByUID", err)
  221. return nil
  222. }
  223. hasWebAuthn, err := auth.HasWebAuthnRegistrationsByUID(ctx, u.ID)
  224. if err != nil {
  225. ctx.ServerError("auth.HasWebAuthnRegistrationsByUID", err)
  226. return nil
  227. }
  228. ctx.Data["TwoFactorEnabled"] = hasTOTP || hasWebAuthn
  229. return u
  230. }
  231. func ViewUser(ctx *context.Context) {
  232. ctx.Data["Title"] = ctx.Tr("admin.users.details")
  233. ctx.Data["PageIsAdminUsers"] = true
  234. ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
  235. ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
  236. ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
  237. u := prepareUserInfo(ctx)
  238. if ctx.Written() {
  239. return
  240. }
  241. repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
  242. ListOptions: db.ListOptions{
  243. ListAll: true,
  244. },
  245. OwnerID: u.ID,
  246. OrderBy: db.SearchOrderByAlphabetically,
  247. Private: true,
  248. Collaborate: optional.Some(false),
  249. })
  250. if err != nil {
  251. ctx.ServerError("SearchRepository", err)
  252. return
  253. }
  254. ctx.Data["Repos"] = repos
  255. ctx.Data["ReposTotal"] = int(count)
  256. emails, err := user_model.GetEmailAddresses(ctx, u.ID)
  257. if err != nil {
  258. ctx.ServerError("GetEmailAddresses", err)
  259. return
  260. }
  261. ctx.Data["Emails"] = emails
  262. ctx.Data["EmailsTotal"] = len(emails)
  263. orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
  264. ListOptions: db.ListOptions{
  265. ListAll: true,
  266. },
  267. UserID: u.ID,
  268. IncludePrivate: true,
  269. })
  270. if err != nil {
  271. ctx.ServerError("FindOrgs", err)
  272. return
  273. }
  274. ctx.Data["Users"] = orgs // needed to be able to use explore/user_list template
  275. ctx.Data["OrgsTotal"] = len(orgs)
  276. ctx.HTML(http.StatusOK, tplUserView)
  277. }
  278. func editUserCommon(ctx *context.Context) {
  279. ctx.Data["Title"] = ctx.Tr("admin.users.edit_account")
  280. ctx.Data["PageIsAdminUsers"] = true
  281. ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
  282. ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
  283. ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
  284. ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
  285. }
  286. // EditUser show editing user page
  287. func EditUser(ctx *context.Context) {
  288. editUserCommon(ctx)
  289. prepareUserInfo(ctx)
  290. if ctx.Written() {
  291. return
  292. }
  293. ctx.HTML(http.StatusOK, tplUserEdit)
  294. }
  295. // EditUserPost response for editing user
  296. func EditUserPost(ctx *context.Context) {
  297. editUserCommon(ctx)
  298. u := prepareUserInfo(ctx)
  299. if ctx.Written() {
  300. return
  301. }
  302. form := web.GetForm(ctx).(*forms.AdminEditUserForm)
  303. if ctx.HasError() {
  304. ctx.HTML(http.StatusOK, tplUserEdit)
  305. return
  306. }
  307. if form.UserName != "" {
  308. if err := user_service.RenameUser(ctx, u, form.UserName); err != nil {
  309. switch {
  310. case user_model.IsErrUserIsNotLocal(err):
  311. ctx.Data["Err_UserName"] = true
  312. ctx.RenderWithErr(ctx.Tr("form.username_change_not_local_user"), tplUserEdit, &form)
  313. case user_model.IsErrUserAlreadyExist(err):
  314. ctx.Data["Err_UserName"] = true
  315. ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplUserEdit, &form)
  316. case db.IsErrNameReserved(err):
  317. ctx.Data["Err_UserName"] = true
  318. ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", form.UserName), tplUserEdit, &form)
  319. case db.IsErrNamePatternNotAllowed(err):
  320. ctx.Data["Err_UserName"] = true
  321. ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", form.UserName), tplUserEdit, &form)
  322. case db.IsErrNameCharsNotAllowed(err):
  323. ctx.Data["Err_UserName"] = true
  324. ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", form.UserName), tplUserEdit, &form)
  325. default:
  326. ctx.ServerError("RenameUser", err)
  327. }
  328. return
  329. }
  330. }
  331. authOpts := &user_service.UpdateAuthOptions{
  332. Password: optional.FromNonDefault(form.Password),
  333. LoginName: optional.Some(form.LoginName),
  334. }
  335. // skip self Prohibit Login
  336. if ctx.Doer.ID == u.ID {
  337. authOpts.ProhibitLogin = optional.Some(false)
  338. } else {
  339. authOpts.ProhibitLogin = optional.Some(form.ProhibitLogin)
  340. }
  341. fields := strings.Split(form.LoginType, "-")
  342. if len(fields) == 2 {
  343. authSource, _ := strconv.ParseInt(fields[1], 10, 64)
  344. authOpts.LoginSource = optional.Some(authSource)
  345. }
  346. if err := user_service.UpdateAuth(ctx, u, authOpts); err != nil {
  347. switch {
  348. case errors.Is(err, password.ErrMinLength):
  349. ctx.Data["Err_Password"] = true
  350. ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserEdit, &form)
  351. case errors.Is(err, password.ErrComplexity):
  352. ctx.Data["Err_Password"] = true
  353. ctx.RenderWithErr(password.BuildComplexityError(ctx.Locale), tplUserEdit, &form)
  354. case errors.Is(err, password.ErrIsPwned):
  355. ctx.Data["Err_Password"] = true
  356. ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplUserEdit, &form)
  357. case password.IsErrIsPwnedRequest(err):
  358. log.Error("%s", err.Error())
  359. ctx.Data["Err_Password"] = true
  360. ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form)
  361. default:
  362. ctx.ServerError("UpdateUser", err)
  363. }
  364. return
  365. }
  366. if form.Email != "" {
  367. if err := user_service.AddOrSetPrimaryEmailAddress(ctx, u, form.Email); err != nil {
  368. switch {
  369. case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err):
  370. ctx.Data["Err_Email"] = true
  371. ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form)
  372. case user_model.IsErrEmailAlreadyUsed(err):
  373. ctx.Data["Err_Email"] = true
  374. ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form)
  375. default:
  376. ctx.ServerError("AddOrSetPrimaryEmailAddress", err)
  377. }
  378. return
  379. }
  380. }
  381. opts := &user_service.UpdateOptions{
  382. FullName: optional.Some(form.FullName),
  383. Website: optional.Some(form.Website),
  384. Location: optional.Some(form.Location),
  385. IsActive: optional.Some(form.Active),
  386. IsAdmin: optional.Some(form.Admin),
  387. AllowGitHook: optional.Some(form.AllowGitHook),
  388. AllowImportLocal: optional.Some(form.AllowImportLocal),
  389. MaxRepoCreation: optional.Some(form.MaxRepoCreation),
  390. AllowCreateOrganization: optional.Some(form.AllowCreateOrganization),
  391. IsRestricted: optional.Some(form.Restricted),
  392. Visibility: optional.Some(form.Visibility),
  393. Language: optional.Some(form.Language),
  394. }
  395. if err := user_service.UpdateUser(ctx, u, opts); err != nil {
  396. if models.IsErrDeleteLastAdminUser(err) {
  397. ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
  398. } else {
  399. ctx.ServerError("UpdateUser", err)
  400. }
  401. return
  402. }
  403. log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, u.Name)
  404. if form.Reset2FA {
  405. tf, err := auth.GetTwoFactorByUID(ctx, u.ID)
  406. if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
  407. ctx.ServerError("auth.GetTwoFactorByUID", err)
  408. return
  409. } else if tf != nil {
  410. if err := auth.DeleteTwoFactorByID(ctx, tf.ID, u.ID); err != nil {
  411. ctx.ServerError("auth.DeleteTwoFactorByID", err)
  412. return
  413. }
  414. }
  415. wn, err := auth.GetWebAuthnCredentialsByUID(ctx, u.ID)
  416. if err != nil {
  417. ctx.ServerError("auth.GetTwoFactorByUID", err)
  418. return
  419. }
  420. for _, cred := range wn {
  421. if _, err := auth.DeleteCredential(ctx, cred.ID, u.ID); err != nil {
  422. ctx.ServerError("auth.DeleteCredential", err)
  423. return
  424. }
  425. }
  426. }
  427. ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
  428. ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
  429. }
  430. // DeleteUser response for deleting a user
  431. func DeleteUser(ctx *context.Context) {
  432. u, err := user_model.GetUserByID(ctx, ctx.ParamsInt64(":userid"))
  433. if err != nil {
  434. ctx.ServerError("GetUserByID", err)
  435. return
  436. }
  437. // admin should not delete themself
  438. if u.ID == ctx.Doer.ID {
  439. ctx.Flash.Error(ctx.Tr("admin.users.cannot_delete_self"))
  440. ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
  441. return
  442. }
  443. if err = user_service.DeleteUser(ctx, u, ctx.FormBool("purge")); err != nil {
  444. switch {
  445. case models.IsErrUserOwnRepos(err):
  446. ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
  447. ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
  448. case models.IsErrUserHasOrgs(err):
  449. ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
  450. ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
  451. case models.IsErrUserOwnPackages(err):
  452. ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
  453. ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
  454. case models.IsErrDeleteLastAdminUser(err):
  455. ctx.Flash.Error(ctx.Tr("auth.last_admin"))
  456. ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
  457. default:
  458. ctx.ServerError("DeleteUser", err)
  459. }
  460. return
  461. }
  462. log.Trace("Account deleted by admin (%s): %s", ctx.Doer.Name, u.Name)
  463. ctx.Flash.Success(ctx.Tr("admin.users.deletion_success"))
  464. ctx.Redirect(setting.AppSubURL + "/admin/users")
  465. }
  466. // AvatarPost response for change user's avatar request
  467. func AvatarPost(ctx *context.Context) {
  468. u := prepareUserInfo(ctx)
  469. if ctx.Written() {
  470. return
  471. }
  472. form := web.GetForm(ctx).(*forms.AvatarForm)
  473. if err := user_setting.UpdateAvatarSetting(ctx, form, u); err != nil {
  474. ctx.Flash.Error(err.Error())
  475. } else {
  476. ctx.Flash.Success(ctx.Tr("settings.update_user_avatar_success"))
  477. }
  478. ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
  479. }
  480. // DeleteAvatar render delete avatar page
  481. func DeleteAvatar(ctx *context.Context) {
  482. u := prepareUserInfo(ctx)
  483. if ctx.Written() {
  484. return
  485. }
  486. if err := user_service.DeleteAvatar(ctx, u); err != nil {
  487. ctx.Flash.Error(err.Error())
  488. }
  489. ctx.JSONRedirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
  490. }