Make allowed Visiblity modes configurable for Users (#16271)
Now that #16069 is merged, some sites may wish to enforce that users are all public, limited or private, and/or disallow users from becoming private. This PR adds functionality and settings to constrain a user's ability to change their visibility. Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		
							parent
							
								
									2a98ec1c3c
								
							
						
					
					
						commit
						0b27b93728
					
				
					 11 changed files with 146 additions and 63 deletions
				
			
		|  | @ -656,6 +656,9 @@ PATH = | |||
| ;; Public is for users visible for everyone | ||||
| ;DEFAULT_USER_VISIBILITY = public | ||||
| ;; | ||||
| ;; Set whitch visibibilty modes a user can have | ||||
| ;ALLOWED_USER_VISIBILITY_MODES = public,limited,private | ||||
| ;; | ||||
| ;; Either "public", "limited" or "private", default is "public" | ||||
| ;; Limited is for organizations visible only to signed users | ||||
| ;; Private is for organizations visible only to members of the organization | ||||
|  |  | |||
|  | @ -513,6 +513,7 @@ relation to port exhaustion. | |||
| - `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created | ||||
| - `AUTO_WATCH_ON_CHANGES`: **false**: Enable this to make users watch a repository after their first commit to it | ||||
| - `DEFAULT_USER_VISIBILITY`: **public**: Set default visibility mode for users, either "public", "limited" or "private". | ||||
| - `ALLOWED_USER_VISIBILITY_MODES`: **public,limited,private**: Set whitch visibibilty modes a user can have | ||||
| - `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private". | ||||
| - `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation. | ||||
| - `ALLOW_ONLY_INTERNAL_REGISTRATION`: **false** Set to true to force registration only via gitea. | ||||
|  |  | |||
|  | @ -863,12 +863,31 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// set system defaults
 | ||||
| 	u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate | ||||
| 	u.Visibility = setting.Service.DefaultUserVisibilityMode | ||||
| 	u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation | ||||
| 	u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification | ||||
| 	u.MaxRepoCreation = -1 | ||||
| 	u.Theme = setting.UI.DefaultTheme | ||||
| 
 | ||||
| 	// overwrite defaults if set
 | ||||
| 	if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { | ||||
| 		u.Visibility = overwriteDefault[0].Visibility | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// validate data
 | ||||
| 
 | ||||
| 	if err := validateUser(u); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	isExist, err := isUserExist(sess, 0, u.Name) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|  | @ -876,15 +895,6 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
| 		return ErrUserAlreadyExist{u.Name} | ||||
| 	} | ||||
| 
 | ||||
| 	if err = deleteUserRedirect(sess, u.Name); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	u.Email = strings.ToLower(u.Email) | ||||
| 	if err = ValidateEmail(u.Email); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	isExist, err = isEmailUsed(sess, u.Email) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|  | @ -892,6 +902,8 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
| 		return ErrEmailAlreadyUsed{u.Email} | ||||
| 	} | ||||
| 
 | ||||
| 	// prepare for database
 | ||||
| 
 | ||||
| 	u.LowerName = strings.ToLower(u.Name) | ||||
| 	u.AvatarEmail = u.Email | ||||
| 	if u.Rands, err = GetUserSalt(); err != nil { | ||||
|  | @ -901,16 +913,10 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// set system defaults
 | ||||
| 	u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate | ||||
| 	u.Visibility = setting.Service.DefaultUserVisibilityMode | ||||
| 	u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation | ||||
| 	u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification | ||||
| 	u.MaxRepoCreation = -1 | ||||
| 	u.Theme = setting.UI.DefaultTheme | ||||
| 	// overwrite defaults if set
 | ||||
| 	if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { | ||||
| 		u.Visibility = overwriteDefault[0].Visibility | ||||
| 	// save changes to database
 | ||||
| 
 | ||||
| 	if err = deleteUserRedirect(sess, u.Name); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err = sess.Insert(u); err != nil { | ||||
|  | @ -1056,12 +1062,22 @@ func checkDupEmail(e Engine, u *User) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func updateUser(e Engine, u *User) (err error) { | ||||
| // validateUser check if user is valide to insert / update into database
 | ||||
| func validateUser(u *User) error { | ||||
| 	if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) { | ||||
| 		return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	u.Email = strings.ToLower(u.Email) | ||||
| 	if err = ValidateEmail(u.Email); err != nil { | ||||
| 	return ValidateEmail(u.Email) | ||||
| } | ||||
| 
 | ||||
| func updateUser(e Engine, u *User) error { | ||||
| 	if err := validateUser(u); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	_, err = e.ID(u.ID).AllCols().Update(u) | ||||
| 
 | ||||
| 	_, err := e.ID(u.ID).AllCols().Update(u) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
|  | @ -1076,6 +1092,10 @@ func UpdateUserCols(u *User, cols ...string) error { | |||
| } | ||||
| 
 | ||||
| func updateUserCols(e Engine, u *User, cols ...string) error { | ||||
| 	if err := validateUser(u); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err := e.ID(u.ID).Cols(cols...).Update(u) | ||||
| 	return err | ||||
| } | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import ( | |||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
|  | @ -189,6 +190,7 @@ func TestDeleteUser(t *testing.T) { | |||
| 
 | ||||
| func TestEmailNotificationPreferences(t *testing.T) { | ||||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 
 | ||||
| 	for _, test := range []struct { | ||||
| 		expected string | ||||
| 		userID   int64 | ||||
|  | @ -467,3 +469,23 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib | |||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestUpdateUser(t *testing.T) { | ||||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 	user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 
 | ||||
| 	user.KeepActivityPrivate = true | ||||
| 	assert.NoError(t, UpdateUser(user)) | ||||
| 	user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 	assert.True(t, user.KeepActivityPrivate) | ||||
| 
 | ||||
| 	setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false} | ||||
| 	user.KeepActivityPrivate = false | ||||
| 	user.Visibility = structs.VisibleTypePrivate | ||||
| 	assert.Error(t, UpdateUser(user)) | ||||
| 	user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 	assert.True(t, user.KeepActivityPrivate) | ||||
| 
 | ||||
| 	user.Email = "no mail@mail.org" | ||||
| 	assert.Error(t, UpdateUser(user)) | ||||
| } | ||||
|  |  | |||
|  | @ -14,9 +14,11 @@ import ( | |||
| ) | ||||
| 
 | ||||
| // Service settings
 | ||||
| var Service struct { | ||||
| var Service = struct { | ||||
| 	DefaultUserVisibility                   string | ||||
| 	DefaultUserVisibilityMode               structs.VisibleType | ||||
| 	AllowedUserVisibilityModes              []string | ||||
| 	AllowedUserVisibilityModesSlice         AllowedVisibility `ini:"-"` | ||||
| 	DefaultOrgVisibility                    string | ||||
| 	DefaultOrgVisibilityMode                structs.VisibleType | ||||
| 	ActiveCodeLives                         int | ||||
|  | @ -71,6 +73,29 @@ var Service struct { | |||
| 		RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` | ||||
| 		DisableUsersPage  bool `ini:"DISABLE_USERS_PAGE"` | ||||
| 	} `ini:"service.explore"` | ||||
| }{ | ||||
| 	AllowedUserVisibilityModesSlice: []bool{true, true, true}, | ||||
| } | ||||
| 
 | ||||
| // AllowedVisibility store in a 3 item bool array what is allowed
 | ||||
| type AllowedVisibility []bool | ||||
| 
 | ||||
| // IsAllowedVisibility check if a AllowedVisibility allow a specific VisibleType
 | ||||
| func (a AllowedVisibility) IsAllowedVisibility(t structs.VisibleType) bool { | ||||
| 	if int(t) >= len(a) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return a[t] | ||||
| } | ||||
| 
 | ||||
| // ToVisibleTypeSlice convert a AllowedVisibility into a VisibleType slice
 | ||||
| func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) { | ||||
| 	for i, v := range a { | ||||
| 		if v { | ||||
| 			result = append(result, structs.VisibleType(i)) | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func newService() { | ||||
|  | @ -122,6 +147,13 @@ func newService() { | |||
| 	Service.AutoWatchOnChanges = sec.Key("AUTO_WATCH_ON_CHANGES").MustBool(false) | ||||
| 	Service.DefaultUserVisibility = sec.Key("DEFAULT_USER_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes)) | ||||
| 	Service.DefaultUserVisibilityMode = structs.VisibilityModes[Service.DefaultUserVisibility] | ||||
| 	Service.AllowedUserVisibilityModes = sec.Key("ALLOWED_USER_VISIBILITY_MODES").Strings(",") | ||||
| 	if len(Service.AllowedUserVisibilityModes) != 0 { | ||||
| 		Service.AllowedUserVisibilityModesSlice = []bool{false, false, false} | ||||
| 		for _, sMode := range Service.AllowedUserVisibilityModes { | ||||
| 			Service.AllowedUserVisibilityModesSlice[structs.VisibilityModes[sMode]] = true | ||||
| 		} | ||||
| 	} | ||||
| 	Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes)) | ||||
| 	Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility] | ||||
| 	Service.DefaultOrgMemberVisible = sec.Key("DEFAULT_ORG_MEMBER_VISIBLE").MustBool() | ||||
|  |  | |||
|  | @ -52,6 +52,7 @@ func NewUser(ctx *context.Context) { | |||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminUsers"] = true | ||||
| 	ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode | ||||
| 	ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() | ||||
| 
 | ||||
| 	ctx.Data["login_type"] = "0-0" | ||||
| 
 | ||||
|  | @ -211,6 +212,7 @@ func EditUser(ctx *context.Context) { | |||
| 	ctx.Data["PageIsAdminUsers"] = true | ||||
| 	ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation | ||||
| 	ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations | ||||
| 	ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() | ||||
| 
 | ||||
| 	prepareUserInfo(ctx) | ||||
| 	if ctx.Written() { | ||||
|  |  | |||
|  | @ -56,7 +56,6 @@ func TestNewUserPost_MustChangePassword(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNewUserPost_MustChangePasswordFalse(t *testing.T) { | ||||
| 
 | ||||
| 	models.PrepareTestEnv(t) | ||||
| 	ctx := test.MockContext(t, "admin/users/new") | ||||
| 
 | ||||
|  | @ -94,7 +93,6 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNewUserPost_InvalidEmail(t *testing.T) { | ||||
| 
 | ||||
| 	models.PrepareTestEnv(t) | ||||
| 	ctx := test.MockContext(t, "admin/users/new") | ||||
| 
 | ||||
|  | @ -125,7 +123,6 @@ func TestNewUserPost_InvalidEmail(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNewUserPost_VisiblityDefaultPublic(t *testing.T) { | ||||
| 
 | ||||
| 	models.PrepareTestEnv(t) | ||||
| 	ctx := test.MockContext(t, "admin/users/new") | ||||
| 
 | ||||
|  | @ -164,7 +161,6 @@ func TestNewUserPost_VisiblityDefaultPublic(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNewUserPost_VisibilityPrivate(t *testing.T) { | ||||
| 
 | ||||
| 	models.PrepareTestEnv(t) | ||||
| 	ctx := test.MockContext(t, "admin/users/new") | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,6 +38,7 @@ const ( | |||
| func Profile(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | ||||
| 	ctx.Data["PageIsSettingsProfile"] = true | ||||
| 	ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() | ||||
| 
 | ||||
| 	ctx.HTML(http.StatusOK, tplSettingsProfile) | ||||
| } | ||||
|  |  | |||
|  | @ -32,25 +32,25 @@ | |||
| 				<div class="inline field {{if .Err_Visibility}}error{{end}}"> | ||||
| 					<span class="inline required field"><label for="visibility">{{.i18n.Tr "settings.visibility"}}</label></span> | ||||
| 					<div class="ui selection type dropdown"> | ||||
| 						{{if .User.Visibility.IsPublic}} | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="0"> | ||||
| 						{{end}} | ||||
| 						{{if .User.Visibility.IsLimited}} | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="1"> | ||||
| 						{{end}} | ||||
| 						{{if .User.Visibility.IsPrivate}} | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="2"> | ||||
| 						{{end}} | ||||
| 						{{if .User.Visibility.IsPublic}}<input type="hidden" id="visibility" name="visibility" value="0">{{end}} | ||||
| 						{{if .User.Visibility.IsLimited}}<input type="hidden" id="visibility" name="visibility" value="1">{{end}} | ||||
| 						{{if .User.Visibility.IsPrivate}}<input type="hidden" id="visibility" name="visibility" value="2">{{end}} | ||||
| 						<div class="text"> | ||||
| 						{{if .User.Visibility.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}} | ||||
| 						{{if .User.Visibility.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}} | ||||
| 						{{if .User.Visibility.IsPrivate}}{{.i18n.Tr "settings.visibility.private"}}{{end}} | ||||
| 							{{if .User.Visibility.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}} | ||||
| 							{{if .User.Visibility.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}} | ||||
| 							{{if .User.Visibility.IsPrivate}}{{.i18n.Tr "settings.visibility.private"}}{{end}} | ||||
| 						</div> | ||||
| 						{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||||
| 						<div class="menu"> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{.i18n.Tr "settings.visibility.public"}}</div> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{.i18n.Tr "settings.visibility.limited"}}</div> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{.i18n.Tr "settings.visibility.private"}}</div> | ||||
| 							{{range $mode := .AllowedUserVisibilityModes}} | ||||
| 								{{if $mode.IsPublic}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{$.i18n.Tr "settings.visibility.public"}}</div> | ||||
| 								{{else if $mode.IsLimited}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{$.i18n.Tr "settings.visibility.limited"}}</div> | ||||
| 								{{else if $mode.IsPrivate}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{$.i18n.Tr "settings.visibility.private"}}</div> | ||||
| 								{{end}} | ||||
| 							{{end}} | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
|  |  | |||
|  | @ -30,15 +30,21 @@ | |||
| 					<div class="ui selection type dropdown"> | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="{{.visibility}}"> | ||||
| 						<div class="text"> | ||||
| 						{{if .DefaultUserVisibilityMode.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}} | ||||
| 						{{if .DefaultUserVisibilityMode.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}} | ||||
| 						{{if .DefaultUserVisibilityMode.IsPrivate}}{{.i18n.Tr "settings.visibility.private"}}{{end}} | ||||
| 							{{if .DefaultUserVisibilityMode.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}} | ||||
| 							{{if .DefaultUserVisibilityMode.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}} | ||||
| 							{{if .DefaultUserVisibilityMode.IsPrivate}}{{.i18n.Tr "settings.visibility.private"}}{{end}} | ||||
| 						</div> | ||||
| 						{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||||
| 						<div class="menu"> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{.i18n.Tr "settings.visibility.public"}}</div> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{.i18n.Tr "settings.visibility.limited"}}</div> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{.i18n.Tr "settings.visibility.private"}}</div> | ||||
| 							{{range $mode := .AllowedUserVisibilityModes}} | ||||
| 								{{if $mode.IsPublic}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{$.i18n.Tr "settings.visibility.public"}}</div> | ||||
| 								{{else if $mode.IsLimited}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{$.i18n.Tr "settings.visibility.limited"}}</div> | ||||
| 								{{else if $mode.IsPrivate}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{$.i18n.Tr "settings.visibility.private"}}</div> | ||||
| 								{{end}} | ||||
| 							{{end}} | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
|  |  | |||
|  | @ -71,25 +71,25 @@ | |||
| 				<div class="inline field {{if .Err_Visibility}}error{{end}}"> | ||||
| 					<span class="inline required field"><label for="visibility">{{.i18n.Tr "settings.visibility"}}</label></span> | ||||
| 					<div class="ui selection type dropdown"> | ||||
| 						{{if .SignedUser.Visibility.IsPublic}} | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="0"> | ||||
| 						{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsLimited}} | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="1"> | ||||
| 						{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsPrivate}} | ||||
| 						<input type="hidden" id="visibility" name="visibility" value="2"> | ||||
| 						{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsPublic}}<input type="hidden" id="visibility" name="visibility" value="0">{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsLimited}}<input type="hidden" id="visibility" name="visibility" value="1">{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsPrivate}}<input type="hidden" id="visibility" name="visibility" value="2">{{end}} | ||||
| 						<div class="text"> | ||||
| 						{{if .SignedUser.Visibility.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}} | ||||
| 						{{if .SignedUser.Visibility.IsPrivate}}{{.i18n.Tr "settings.visibility.private"}}{{end}} | ||||
| 							{{if .SignedUser.Visibility.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}} | ||||
| 							{{if .SignedUser.Visibility.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}} | ||||
| 							{{if .SignedUser.Visibility.IsPrivate}}{{.i18n.Tr "settings.visibility.private"}}{{end}} | ||||
| 						</div> | ||||
| 						{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||||
| 						<div class="menu"> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{.i18n.Tr "settings.visibility.public"}}</div> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{.i18n.Tr "settings.visibility.limited"}}</div> | ||||
| 							<div class="item poping up" data-content="{{.i18n.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{.i18n.Tr "settings.visibility.private"}}</div> | ||||
| 							{{range $mode := .AllowedUserVisibilityModes}} | ||||
| 								{{if $mode.IsPublic}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{$.i18n.Tr "settings.visibility.public"}}</div> | ||||
| 								{{else if $mode.IsLimited}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{$.i18n.Tr "settings.visibility.limited"}}</div> | ||||
| 								{{else if $mode.IsPrivate}} | ||||
| 									<div class="item poping up" data-content="{{$.i18n.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{$.i18n.Tr "settings.visibility.private"}}</div> | ||||
| 								{{end}} | ||||
| 							{{end}} | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue