code.gitea.io/gitea@v1.21.7/routers/web/admin/users.go (about)

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