2018-05-17 07:05:00 +03:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2018-05-17 07:05:00 +03:00
package setting
import (
"errors"
"fmt"
2021-09-22 08:38:34 +03:00
"io"
2022-01-21 20:59:26 +03:00
"math/big"
2021-04-05 18:30:52 +03:00
"net/http"
2020-09-25 07:09:23 +03:00
"os"
"path/filepath"
2018-05-17 07:05:00 +03:00
"strings"
2023-12-28 12:38:59 +03:00
"code.gitea.io/gitea/models/avatars"
2021-09-24 14:32:56 +03:00
"code.gitea.io/gitea/models/db"
2022-03-29 09:29:02 +03:00
"code.gitea.io/gitea/models/organization"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-11 10:03:30 +03:00
user_model "code.gitea.io/gitea/models/user"
2018-05-17 07:05:00 +03:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
2024-02-04 16:29:09 +03:00
"code.gitea.io/gitea/modules/optional"
2018-05-17 07:05:00 +03:00
"code.gitea.io/gitea/modules/setting"
2022-06-26 17:19:22 +03:00
"code.gitea.io/gitea/modules/translation"
2021-06-05 15:32:19 +03:00
"code.gitea.io/gitea/modules/typesniffer"
2020-12-04 09:20:30 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2021-03-07 11:12:43 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2024-02-27 10:12:22 +03:00
"code.gitea.io/gitea/services/context"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2021-11-22 18:21:55 +03:00
user_service "code.gitea.io/gitea/services/user"
2024-04-23 19:18:41 +03:00
"code.gitea.io/gitea/services/webtheme"
2018-05-17 07:05:00 +03:00
)
const (
tplSettingsProfile base . TplName = "user/settings/profile"
2021-10-27 18:40:08 +03:00
tplSettingsAppearance base . TplName = "user/settings/appearance"
2018-05-17 07:05:00 +03:00
tplSettingsOrganization base . TplName = "user/settings/organization"
tplSettingsRepositories base . TplName = "user/settings/repos"
)
// Profile render user's profile page
func Profile ( ctx * context . Context ) {
2023-02-02 01:56:10 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings.profile" )
2018-05-17 07:05:00 +03:00
ctx . Data [ "PageIsSettingsProfile" ] = true
2021-06-27 21:47:35 +03:00
ctx . Data [ "AllowedUserVisibilityModes" ] = setting . Service . AllowedUserVisibilityModesSlice . ToVisibleTypeSlice ( )
2023-10-05 04:08:19 +03:00
ctx . Data [ "DisableGravatar" ] = setting . Config ( ) . Picture . DisableGravatar . Value ( ctx )
2018-06-18 21:24:45 +03:00
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsProfile )
2018-05-17 07:05:00 +03:00
}
// ProfilePost response for change user's profile
2021-01-26 18:36:53 +03:00
func ProfilePost ( ctx * context . Context ) {
2018-05-17 07:05:00 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsProfile" ] = true
2023-08-08 11:29:14 +03:00
ctx . Data [ "AllowedUserVisibilityModes" ] = setting . Service . AllowedUserVisibilityModesSlice . ToVisibleTypeSlice ( )
2023-10-05 04:08:19 +03:00
ctx . Data [ "DisableGravatar" ] = setting . Config ( ) . Picture . DisableGravatar . Value ( ctx )
2018-05-17 07:05:00 +03:00
if ctx . HasError ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsProfile )
2018-05-17 07:05:00 +03:00
return
}
2024-02-04 16:29:09 +03:00
form := web . GetForm ( ctx ) . ( * forms . UpdateProfileForm )
if form . Name != "" {
if err := user_service . RenameUser ( ctx , ctx . Doer , form . Name ) ; err != nil {
switch {
case user_model . IsErrUserIsNotLocal ( err ) :
ctx . Flash . Error ( ctx . Tr ( "form.username_change_not_local_user" ) )
case user_model . IsErrUserAlreadyExist ( err ) :
ctx . Flash . Error ( ctx . Tr ( "form.username_been_taken" ) )
case db . IsErrNameReserved ( err ) :
ctx . Flash . Error ( ctx . Tr ( "user.form.name_reserved" , form . Name ) )
case db . IsErrNamePatternNotAllowed ( err ) :
ctx . Flash . Error ( ctx . Tr ( "user.form.name_pattern_not_allowed" , form . Name ) )
case db . IsErrNameCharsNotAllowed ( err ) :
ctx . Flash . Error ( ctx . Tr ( "user.form.name_chars_not_allowed" , form . Name ) )
default :
ctx . ServerError ( "RenameUser" , err )
return
}
2021-01-10 15:14:02 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
return
}
2018-05-17 07:05:00 +03:00
}
2024-02-04 16:29:09 +03:00
opts := & user_service . UpdateOptions {
FullName : optional . Some ( form . FullName ) ,
KeepEmailPrivate : optional . Some ( form . KeepEmailPrivate ) ,
Description : optional . Some ( form . Description ) ,
Website : optional . Some ( form . Website ) ,
Location : optional . Some ( form . Location ) ,
Visibility : optional . Some ( form . Visibility ) ,
KeepActivityPrivate : optional . Some ( form . KeepActivityPrivate ) ,
}
if err := user_service . UpdateUser ( ctx , ctx . Doer , opts ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . ServerError ( "UpdateUser" , err )
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "User settings updated: %s" , ctx . Doer . Name )
2023-02-05 19:06:26 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.update_profile_success" ) )
2018-05-17 07:05:00 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
}
// UpdateAvatarSetting update user's avatar
// FIXME: limit size.
2021-11-24 12:49:20 +03:00
func UpdateAvatarSetting ( ctx * context . Context , form * forms . AvatarForm , ctxUser * user_model . User ) error {
2021-04-06 22:44:05 +03:00
ctxUser . UseCustomAvatar = form . Source == forms . AvatarLocal
2018-05-17 07:05:00 +03:00
if len ( form . Gravatar ) > 0 {
2020-10-23 20:55:10 +03:00
if form . Avatar != nil {
2023-12-28 12:38:59 +03:00
ctxUser . Avatar = avatars . HashEmail ( form . Gravatar )
2020-10-23 20:55:10 +03:00
} else {
ctxUser . Avatar = ""
}
2018-05-17 07:05:00 +03:00
ctxUser . AvatarEmail = form . Gravatar
}
2018-08-01 12:38:56 +03:00
if form . Avatar != nil && form . Avatar . Filename != "" {
2018-05-17 07:05:00 +03:00
fr , err := form . Avatar . Open ( )
if err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Avatar.Open: %w" , err )
2018-05-17 07:05:00 +03:00
}
defer fr . Close ( )
2020-10-14 16:07:51 +03:00
if form . Avatar . Size > setting . Avatar . MaxFileSize {
2024-02-15 00:48:45 +03:00
return errors . New ( ctx . Locale . TrString ( "settings.uploaded_avatar_is_too_big" , form . Avatar . Size / 1024 , setting . Avatar . MaxFileSize / 1024 ) )
2019-05-30 05:22:26 +03:00
}
2021-09-22 08:38:34 +03:00
data , err := io . ReadAll ( fr )
2018-05-17 07:05:00 +03:00
if err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "io.ReadAll: %w" , err )
2018-05-17 07:05:00 +03:00
}
2021-06-05 15:32:19 +03:00
st := typesniffer . DetectContentType ( data )
if ! ( st . IsImage ( ) && ! st . IsSvgImage ( ) ) {
2024-02-15 00:48:45 +03:00
return errors . New ( ctx . Locale . TrString ( "settings.uploaded_avatar_not_a_image" ) )
2018-05-17 07:05:00 +03:00
}
2023-10-11 07:24:07 +03:00
if err = user_service . UploadAvatar ( ctx , ctxUser , data ) ; err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "UploadAvatar: %w" , err )
2018-05-17 07:05:00 +03:00
}
2020-10-14 16:07:51 +03:00
} else if ctxUser . UseCustomAvatar && ctxUser . Avatar == "" {
2018-05-17 07:05:00 +03:00
// No avatar is uploaded but setting has been changed to enable,
// generate a random one when needed.
2022-05-20 17:08:52 +03:00
if err := user_model . GenerateRandomAvatar ( ctx , ctxUser ) ; err != nil {
2019-06-12 22:41:28 +03:00
log . Error ( "GenerateRandomAvatar[%d]: %v" , ctxUser . ID , err )
2018-05-17 07:05:00 +03:00
}
}
2022-03-22 18:22:54 +03:00
if err := user_model . UpdateUserCols ( ctx , ctxUser , "avatar" , "avatar_email" , "use_custom_avatar" ) ; err != nil {
2024-02-04 16:29:09 +03:00
return fmt . Errorf ( "UpdateUserCols: %w" , err )
2018-05-17 07:05:00 +03:00
}
return nil
}
// AvatarPost response for change user's avatar request
2021-01-26 18:36:53 +03:00
func AvatarPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . AvatarForm )
2022-03-22 10:03:22 +03:00
if err := UpdateAvatarSetting ( ctx , form , ctx . Doer ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . Flash . Error ( err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.update_avatar_success" ) )
}
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
}
// DeleteAvatar render delete avatar page
func DeleteAvatar ( ctx * context . Context ) {
2023-10-11 07:24:07 +03:00
if err := user_service . DeleteAvatar ( ctx , ctx . Doer ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . Flash . Error ( err . Error ( ) )
}
2023-08-23 12:36:57 +03:00
ctx . JSONRedirect ( setting . AppSubURL + "/user/settings" )
2018-05-17 07:05:00 +03:00
}
// Organization render all the organization of the user
func Organization ( ctx * context . Context ) {
2023-02-02 01:56:10 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings.organization" )
2018-05-17 07:05:00 +03:00
ctx . Data [ "PageIsSettingsOrganization" ] = true
2021-11-22 16:51:45 +03:00
2022-03-29 09:29:02 +03:00
opts := organization . FindOrgOptions {
2021-11-22 16:51:45 +03:00
ListOptions : db . ListOptions {
PageSize : setting . UI . Admin . UserPagingNum ,
Page : ctx . FormInt ( "page" ) ,
} ,
2022-03-22 10:03:22 +03:00
UserID : ctx . Doer . ID ,
2021-11-22 16:51:45 +03:00
IncludePrivate : ctx . IsSigned ,
}
if opts . Page <= 0 {
opts . Page = 1
}
2023-11-24 06:49:41 +03:00
orgs , total , err := db . FindAndCount [ organization . Organization ] ( ctx , opts )
2021-11-22 16:51:45 +03:00
if err != nil {
ctx . ServerError ( "FindOrgs" , err )
return
}
2023-11-24 06:49:41 +03:00
2018-05-17 07:05:00 +03:00
ctx . Data [ "Orgs" ] = orgs
2021-11-22 16:51:45 +03:00
pager := context . NewPagination ( int ( total ) , opts . PageSize , opts . Page , 5 )
pager . SetDefaultParams ( ctx )
ctx . Data [ "Page" ] = pager
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsOrganization )
2018-05-17 07:05:00 +03:00
}
// Repos display a list of all repositories of the user
func Repos ( ctx * context . Context ) {
2023-02-02 01:56:10 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings.repos" )
2018-05-17 07:05:00 +03:00
ctx . Data [ "PageIsSettingsRepos" ] = true
2020-09-25 07:09:23 +03:00
ctx . Data [ "allowAdopt" ] = ctx . IsUserSiteAdmin ( ) || setting . Repository . AllowAdoptionOfUnadoptedRepositories
ctx . Data [ "allowDelete" ] = ctx . IsUserSiteAdmin ( ) || setting . Repository . AllowDeleteOfUnadoptedRepositories
2018-05-17 07:05:00 +03:00
2021-09-24 14:32:56 +03:00
opts := db . ListOptions {
2020-09-25 07:09:23 +03:00
PageSize : setting . UI . Admin . UserPagingNum ,
2021-07-29 04:42:15 +03:00
Page : ctx . FormInt ( "page" ) ,
2020-09-25 07:09:23 +03:00
}
if opts . Page <= 0 {
opts . Page = 1
2018-05-17 07:05:00 +03:00
}
2020-09-25 07:09:23 +03:00
start := ( opts . Page - 1 ) * opts . PageSize
end := start + opts . PageSize
2018-05-17 07:05:00 +03:00
2020-09-25 07:09:23 +03:00
adoptOrDelete := ctx . IsUserSiteAdmin ( ) || ( setting . Repository . AllowAdoptionOfUnadoptedRepositories && setting . Repository . AllowDeleteOfUnadoptedRepositories )
2022-03-22 10:03:22 +03:00
ctxUser := ctx . Doer
2020-09-25 07:09:23 +03:00
count := 0
if adoptOrDelete {
repoNames := make ( [ ] string , 0 , setting . UI . Admin . UserPagingNum )
2021-12-10 04:27:50 +03:00
repos := map [ string ] * repo_model . Repository { }
2020-09-25 07:09:23 +03:00
// We're going to iterate by pagesize.
2021-11-24 12:49:20 +03:00
root := user_model . UserPath ( ctxUser . Name )
2023-01-16 19:21:44 +03:00
if err := filepath . WalkDir ( root , func ( path string , d os . DirEntry , err error ) error {
2018-05-17 07:05:00 +03:00
if err != nil {
2020-12-31 10:45:54 +03:00
if os . IsNotExist ( err ) {
return nil
}
2020-09-25 07:09:23 +03:00
return err
2018-05-17 07:05:00 +03:00
}
2023-01-16 19:21:44 +03:00
if ! d . IsDir ( ) || path == root {
2020-09-25 07:09:23 +03:00
return nil
2018-05-17 07:05:00 +03:00
}
2023-01-16 19:21:44 +03:00
name := d . Name ( )
2020-09-25 07:09:23 +03:00
if ! strings . HasSuffix ( name , ".git" ) {
return filepath . SkipDir
}
name = name [ : len ( name ) - 4 ]
2021-12-12 18:48:20 +03:00
if repo_model . IsUsableRepoName ( name ) != nil || strings . ToLower ( name ) != name {
2020-09-25 07:09:23 +03:00
return filepath . SkipDir
}
if count >= start && count < end {
repoNames = append ( repoNames , name )
}
count ++
return filepath . SkipDir
} ) ; err != nil {
2023-01-16 19:21:44 +03:00
ctx . ServerError ( "filepath.WalkDir" , err )
2020-09-25 07:09:23 +03:00
return
2018-05-17 07:05:00 +03:00
}
2023-10-15 18:46:06 +03:00
userRepos , _ , err := repo_model . GetUserRepositories ( ctx , & repo_model . SearchRepoOptions {
2021-11-22 18:21:55 +03:00
Actor : ctxUser ,
Private : true ,
ListOptions : db . ListOptions {
Page : 1 ,
PageSize : setting . UI . Admin . UserPagingNum ,
} ,
LowerNames : repoNames ,
} )
if err != nil {
ctx . ServerError ( "GetUserRepositories" , err )
2020-09-25 07:09:23 +03:00
return
}
2021-11-22 18:21:55 +03:00
for _ , repo := range userRepos {
2020-09-25 07:09:23 +03:00
if repo . IsFork {
2022-12-03 05:48:26 +03:00
if err := repo . GetBaseRepo ( ctx ) ; err != nil {
2020-09-25 07:09:23 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
return
}
}
repos [ repo . LowerName ] = repo
}
ctx . Data [ "Dirs" ] = repoNames
ctx . Data [ "ReposMap" ] = repos
} else {
2023-10-15 18:46:06 +03:00
repos , count64 , err := repo_model . GetUserRepositories ( ctx , & repo_model . SearchRepoOptions { Actor : ctxUser , Private : true , ListOptions : opts } )
2020-09-25 07:09:23 +03:00
if err != nil {
2021-11-22 18:21:55 +03:00
ctx . ServerError ( "GetUserRepositories" , err )
2020-09-25 07:09:23 +03:00
return
}
count = int ( count64 )
for i := range repos {
if repos [ i ] . IsFork {
2022-12-03 05:48:26 +03:00
if err := repos [ i ] . GetBaseRepo ( ctx ) ; err != nil {
2020-09-25 07:09:23 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
return
}
}
}
ctx . Data [ "Repos" ] = repos
}
2023-04-20 20:33:30 +03:00
ctx . Data [ "ContextUser" ] = ctxUser
2022-06-20 13:02:49 +03:00
pager := context . NewPagination ( count , opts . PageSize , opts . Page , 5 )
2020-09-25 07:09:23 +03:00
pager . SetDefaultParams ( ctx )
ctx . Data [ "Page" ] = pager
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsRepositories )
2018-05-17 07:05:00 +03:00
}
2021-10-27 18:40:08 +03:00
// Appearance render user's appearance settings
func Appearance ( ctx * context . Context ) {
2023-02-02 01:56:10 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings.appearance" )
2021-10-27 18:40:08 +03:00
ctx . Data [ "PageIsSettingsAppearance" ] = true
2024-04-23 19:18:41 +03:00
allThemes := webtheme . GetAvailableThemes ( )
if webtheme . IsThemeAvailable ( setting . UI . DefaultTheme ) {
allThemes = util . SliceRemoveAll ( allThemes , setting . UI . DefaultTheme )
allThemes = append ( [ ] string { setting . UI . DefaultTheme } , allThemes ... ) // move the default theme to the top
}
ctx . Data [ "AllThemes" ] = allThemes
2022-01-21 20:59:26 +03:00
var hiddenCommentTypes * big . Int
2023-09-15 09:13:19 +03:00
val , err := user_model . GetUserSetting ( ctx , ctx . Doer . ID , user_model . SettingsKeyHiddenCommentTypes )
2022-01-21 20:59:26 +03:00
if err != nil {
ctx . ServerError ( "GetUserSetting" , err )
return
}
hiddenCommentTypes , _ = new ( big . Int ) . SetString ( val , 10 ) // we can safely ignore the failed conversion here
ctx . Data [ "IsCommentTypeGroupChecked" ] = func ( commentTypeGroup string ) bool {
return forms . IsUserHiddenCommentTypeGroupChecked ( commentTypeGroup , hiddenCommentTypes )
}
2021-10-27 18:40:08 +03:00
ctx . HTML ( http . StatusOK , tplSettingsAppearance )
}
// UpdateUIThemePost is used to update users' specific theme
func UpdateUIThemePost ( ctx * context . Context ) {
form := web . GetForm ( ctx ) . ( * forms . UpdateThemeForm )
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAppearance" ] = true
if ctx . HasError ( ) {
2024-04-23 19:18:41 +03:00
ctx . Flash . Error ( ctx . GetErrMsg ( ) )
2021-10-27 18:40:08 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/appearance" )
return
}
2024-04-23 19:18:41 +03:00
if ! webtheme . IsThemeAvailable ( form . Theme ) {
2021-10-27 18:40:08 +03:00
ctx . Flash . Error ( ctx . Tr ( "settings.theme_update_error" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/appearance" )
return
}
2024-02-04 16:29:09 +03:00
opts := & user_service . UpdateOptions {
Theme : optional . Some ( form . Theme ) ,
}
if err := user_service . UpdateUser ( ctx , ctx . Doer , opts ) ; err != nil {
2021-10-27 18:40:08 +03:00
ctx . Flash . Error ( ctx . Tr ( "settings.theme_update_error" ) )
2024-02-04 16:29:09 +03:00
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.theme_update_success" ) )
2021-10-27 18:40:08 +03:00
}
ctx . Redirect ( setting . AppSubURL + "/user/settings/appearance" )
}
// UpdateUserLang update a user's language
func UpdateUserLang ( ctx * context . Context ) {
form := web . GetForm ( ctx ) . ( * forms . UpdateLanguageForm )
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAppearance" ] = true
2024-02-04 16:29:09 +03:00
if form . Language != "" {
Improve utils of slices (#22379)
- Move the file `compare.go` and `slice.go` to `slice.go`.
- Fix `ExistsInSlice`, it's buggy
- It uses `sort.Search`, so it assumes that the input slice is sorted.
- It passes `func(i int) bool { return slice[i] == target })` to
`sort.Search`, that's incorrect, check the doc of `sort.Search`.
- Conbine `IsInt64InSlice(int64, []int64)` and `ExistsInSlice(string,
[]string)` to `SliceContains[T]([]T, T)`.
- Conbine `IsSliceInt64Eq([]int64, []int64)` and `IsEqualSlice([]string,
[]string)` to `SliceSortedEqual[T]([]T, T)`.
- Add `SliceEqual[T]([]T, T)` as a distinction from
`SliceSortedEqual[T]([]T, T)`.
- Redesign `RemoveIDFromList([]int64, int64) ([]int64, bool)` to
`SliceRemoveAll[T]([]T, T) []T`.
- Add `SliceContainsFunc[T]([]T, func(T) bool)` and
`SliceRemoveAllFunc[T]([]T, func(T) bool)` for general use.
- Add comments to explain why not `golang.org/x/exp/slices`.
- Add unit tests.
2023-01-11 08:31:16 +03:00
if ! util . SliceContainsString ( setting . Langs , form . Language ) {
2021-10-27 18:40:08 +03:00
ctx . Flash . Error ( ctx . Tr ( "settings.update_language_not_found" , form . Language ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/appearance" )
return
}
}
2024-02-04 16:29:09 +03:00
opts := & user_service . UpdateOptions {
Language : optional . Some ( form . Language ) ,
}
if err := user_service . UpdateUser ( ctx , ctx . Doer , opts ) ; err != nil {
ctx . ServerError ( "UpdateUser" , err )
2021-10-27 18:40:08 +03:00
return
}
// Update the language to the one we just set
2022-03-22 10:03:22 +03:00
middleware . SetLocaleCookie ( ctx . Resp , ctx . Doer . Language , 0 )
2021-10-27 18:40:08 +03:00
2022-03-22 10:03:22 +03:00
log . Trace ( "User settings updated: %s" , ctx . Doer . Name )
2024-02-15 00:48:45 +03:00
ctx . Flash . Success ( translation . NewLocale ( ctx . Doer . Language ) . TrString ( "settings.update_language_success" ) )
2021-10-27 18:40:08 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/appearance" )
}
2022-01-21 20:59:26 +03:00
// UpdateUserHiddenComments update a user's shown comment types
func UpdateUserHiddenComments ( ctx * context . Context ) {
2023-09-15 09:13:19 +03:00
err := user_model . SetUserSetting ( ctx , ctx . Doer . ID , user_model . SettingsKeyHiddenCommentTypes , forms . UserHiddenCommentTypesFromRequest ( ctx ) . String ( ) )
2022-01-21 20:59:26 +03:00
if err != nil {
ctx . ServerError ( "SetUserSetting" , err )
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "User settings updated: %s" , ctx . Doer . Name )
2022-01-21 20:59:26 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.saved_successfully" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/appearance" )
}