gitea源码

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