forked from gitea/gitea
		
	Allow to set organization visibility (public, internal, private) (#1763)
This commit is contained in:
		
							parent
							
								
									ae3a913122
								
							
						
					
					
						commit
						64ce159a6e
					
				| @ -351,6 +351,11 @@ DEFAULT_KEEP_EMAIL_PRIVATE = false | ||||
| ; Default value for AllowCreateOrganization | ||||
| ; Every new user will have rights set to create organizations depending on this setting | ||||
| DEFAULT_ALLOW_CREATE_ORGANIZATION = true | ||||
| ; Either "public", "limited" or "private", default is "public" | ||||
| ; Limited is for signed user only | ||||
| ; Private is only for member of the organization | ||||
| ; Public is for everyone | ||||
| DEFAULT_ORG_VISIBILITY = public | ||||
| ; Default value for EnableDependencies | ||||
| ; Repositories will use dependencies by default depending on this setting | ||||
| DEFAULT_ENABLE_DEPENDENCIES = true | ||||
|  | ||||
| @ -147,7 +147,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | ||||
| - `PATH`: **data/gitea.db**: For SQLite3 only, the database file path. | ||||
| - `LOG_SQL`: **true**: Log the executed SQL. | ||||
| - `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed. | ||||
| - `DB_RETRY_BACKOFF`: **3s*: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured. | ||||
| - `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured. | ||||
| 
 | ||||
| ## Indexer (`indexer`) | ||||
| 
 | ||||
| @ -203,12 +203,13 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | ||||
| - `CAPTCHA_TYPE`: **image**: \[image, recaptcha\] | ||||
| - `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha. | ||||
| - `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha. | ||||
| - `DEFAULT_ENABLE_DEPENDENCIES`: **true** Enable this to have dependencies enabled by default. | ||||
| - `ENABLE_USER_HEATMAP`: **true** Enable this to display the heatmap on users profiles. | ||||
| - `DEFAULT_ENABLE_DEPENDENCIES`: **true**: Enable this to have dependencies enabled by default. | ||||
| - `ENABLE_USER_HEATMAP`: **true**: Enable this to display the heatmap on users profiles. | ||||
| - `EMAIL_DOMAIN_WHITELIST`: **\<empty\>**: If non-empty, list of domain names that can only be used to register | ||||
|   on this instance. | ||||
| - `SHOW_REGISTRATION_BUTTON`: **! DISABLE\_REGISTRATION**: Show Registration Button | ||||
| - `AUTO_WATCH_NEW_REPOS`: **true** Enable this to let all organisation users watch new repos when they are created | ||||
| - `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created | ||||
| - `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private". | ||||
| 
 | ||||
| ## Webhook (`webhook`) | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2015 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2017 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| @ -11,6 +12,7 @@ import ( | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 
 | ||||
| 	"github.com/Unknwon/com" | ||||
| 	"github.com/go-xorm/builder" | ||||
| @ -366,6 +368,40 @@ func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) { | ||||
| 		Find(&orgs) | ||||
| } | ||||
| 
 | ||||
| // HasOrgVisible tells if the given user can see the given org | ||||
| func HasOrgVisible(org *User, user *User) bool { | ||||
| 	// Not SignedUser | ||||
| 	if user == nil { | ||||
| 		if org.Visibility == structs.VisibleTypePublic { | ||||
| 			return true | ||||
| 		} | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if user.IsAdmin { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if org.Visibility == structs.VisibleTypePrivate && !org.IsUserPartOfOrg(user.ID) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // HasOrgsVisible tells if the given user can see at least one of the orgs provided | ||||
| func HasOrgsVisible(orgs []*User, user *User) bool { | ||||
| 	if len(orgs) == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	for _, org := range orgs { | ||||
| 		if HasOrgVisible(org, user) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID. | ||||
| func GetOwnedOrgsByUserID(userID int64) ([]*User, error) { | ||||
| 	sess := x.NewSession() | ||||
|  | ||||
| @ -7,6 +7,8 @@ package models | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| @ -545,3 +547,72 @@ func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { | ||||
| 	testSuccess(2, []int64{5}) | ||||
| 	testSuccess(4, []int64{}) | ||||
| } | ||||
| 
 | ||||
| func TestHasOrgVisibleTypePublic(t *testing.T) { | ||||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 	owner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 	user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) | ||||
| 
 | ||||
| 	const newOrgName = "test-org-public" | ||||
| 	org := &User{ | ||||
| 		Name:       newOrgName, | ||||
| 		Visibility: structs.VisibleTypePublic, | ||||
| 	} | ||||
| 
 | ||||
| 	AssertNotExistsBean(t, &User{Name: org.Name, Type: UserTypeOrganization}) | ||||
| 	assert.NoError(t, CreateOrganization(org, owner)) | ||||
| 	org = AssertExistsAndLoadBean(t, | ||||
| 		&User{Name: org.Name, Type: UserTypeOrganization}).(*User) | ||||
| 	test1 := HasOrgVisible(org, owner) | ||||
| 	test2 := HasOrgVisible(org, user3) | ||||
| 	test3 := HasOrgVisible(org, nil) | ||||
| 	assert.Equal(t, test1, true) // owner of org | ||||
| 	assert.Equal(t, test2, true) // user not a part of org | ||||
| 	assert.Equal(t, test3, true) // logged out user | ||||
| } | ||||
| 
 | ||||
| func TestHasOrgVisibleTypeLimited(t *testing.T) { | ||||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 	owner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 	user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) | ||||
| 
 | ||||
| 	const newOrgName = "test-org-limited" | ||||
| 	org := &User{ | ||||
| 		Name:       newOrgName, | ||||
| 		Visibility: structs.VisibleTypeLimited, | ||||
| 	} | ||||
| 
 | ||||
| 	AssertNotExistsBean(t, &User{Name: org.Name, Type: UserTypeOrganization}) | ||||
| 	assert.NoError(t, CreateOrganization(org, owner)) | ||||
| 	org = AssertExistsAndLoadBean(t, | ||||
| 		&User{Name: org.Name, Type: UserTypeOrganization}).(*User) | ||||
| 	test1 := HasOrgVisible(org, owner) | ||||
| 	test2 := HasOrgVisible(org, user3) | ||||
| 	test3 := HasOrgVisible(org, nil) | ||||
| 	assert.Equal(t, test1, true)  // owner of org | ||||
| 	assert.Equal(t, test2, true)  // user not a part of org | ||||
| 	assert.Equal(t, test3, false) // logged out user | ||||
| } | ||||
| 
 | ||||
| func TestHasOrgVisibleTypePrivate(t *testing.T) { | ||||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 	owner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||
| 	user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) | ||||
| 
 | ||||
| 	const newOrgName = "test-org-private" | ||||
| 	org := &User{ | ||||
| 		Name:       newOrgName, | ||||
| 		Visibility: structs.VisibleTypePrivate, | ||||
| 	} | ||||
| 
 | ||||
| 	AssertNotExistsBean(t, &User{Name: org.Name, Type: UserTypeOrganization}) | ||||
| 	assert.NoError(t, CreateOrganization(org, owner)) | ||||
| 	org = AssertExistsAndLoadBean(t, | ||||
| 		&User{Name: org.Name, Type: UserTypeOrganization}).(*User) | ||||
| 	test1 := HasOrgVisible(org, owner) | ||||
| 	test2 := HasOrgVisible(org, user3) | ||||
| 	test3 := HasOrgVisible(org, nil) | ||||
| 	assert.Equal(t, test1, true)  // owner of org | ||||
| 	assert.Equal(t, test2, false) // user not a part of org | ||||
| 	assert.Equal(t, test3, false) // logged out user | ||||
| } | ||||
|  | ||||
| @ -8,9 +8,11 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 
 | ||||
| 	"github.com/go-xorm/builder" | ||||
| 	"github.com/go-xorm/core" | ||||
| ) | ||||
| 
 | ||||
| // RepositoryListDefaultPageSize is the default number of repositories | ||||
| @ -171,6 +173,10 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | ||||
| 
 | ||||
| 	if !opts.Private { | ||||
| 		cond = cond.And(builder.Eq{"is_private": false}) | ||||
| 		accessCond := builder.Or( | ||||
| 			builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}))), | ||||
| 			builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization}))) | ||||
| 		cond = cond.And(accessCond) | ||||
| 	} | ||||
| 
 | ||||
| 	if opts.OwnerID > 0 { | ||||
| @ -193,6 +199,35 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | ||||
| 				accessCond = accessCond.Or(collaborateCond) | ||||
| 			} | ||||
| 
 | ||||
| 			var exprCond builder.Cond | ||||
| 			if DbCfg.Type == core.POSTGRES { | ||||
| 				exprCond = builder.Expr("org_user.org_id = \"user\".id") | ||||
| 			} else if DbCfg.Type == core.MSSQL { | ||||
| 				exprCond = builder.Expr("org_user.org_id = [user].id") | ||||
| 			} else { | ||||
| 				exprCond = builder.Eq{"org_user.org_id": "user.id"} | ||||
| 			} | ||||
| 
 | ||||
| 			visibilityCond := builder.Or( | ||||
| 				builder.In("owner_id", | ||||
| 					builder.Select("org_id").From("org_user"). | ||||
| 						LeftJoin("`user`", exprCond). | ||||
| 						Where( | ||||
| 							builder.And( | ||||
| 								builder.Eq{"uid": opts.OwnerID}, | ||||
| 								builder.Eq{"visibility": structs.VisibleTypePrivate})), | ||||
| 				), | ||||
| 				builder.In("owner_id", | ||||
| 					builder.Select("id").From("`user`"). | ||||
| 						Where( | ||||
| 							builder.Or( | ||||
| 								builder.Eq{"visibility": structs.VisibleTypePublic}, | ||||
| 								builder.Eq{"visibility": structs.VisibleTypeLimited})), | ||||
| 				), | ||||
| 				builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})), | ||||
| 			) | ||||
| 			cond = cond.And(visibilityCond) | ||||
| 
 | ||||
| 			if opts.AllPublic { | ||||
| 				accessCond = accessCond.Or(builder.Eq{"is_private": false}) | ||||
| 			} | ||||
|  | ||||
| @ -25,22 +25,23 @@ import ( | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/Unknwon/com" | ||||
| 	"github.com/go-xorm/builder" | ||||
| 	"github.com/go-xorm/xorm" | ||||
| 	"github.com/nfnt/resize" | ||||
| 	"golang.org/x/crypto/pbkdf2" | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| 
 | ||||
| 	"code.gitea.io/git" | ||||
| 	api "code.gitea.io/sdk/gitea" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/avatar" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/generate" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	api "code.gitea.io/sdk/gitea" | ||||
| 
 | ||||
| 	"github.com/Unknwon/com" | ||||
| 	"github.com/go-xorm/builder" | ||||
| 	"github.com/go-xorm/core" | ||||
| 	"github.com/go-xorm/xorm" | ||||
| 	"github.com/nfnt/resize" | ||||
| 	"golang.org/x/crypto/pbkdf2" | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
| 
 | ||||
| // UserType defines the user type | ||||
| @ -136,8 +137,9 @@ type User struct { | ||||
| 	Description string | ||||
| 	NumTeams    int | ||||
| 	NumMembers  int | ||||
| 	Teams       []*Team `xorm:"-"` | ||||
| 	Members     []*User `xorm:"-"` | ||||
| 	Teams       []*Team             `xorm:"-"` | ||||
| 	Members     []*User             `xorm:"-"` | ||||
| 	Visibility  structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` | ||||
| 
 | ||||
| 	// Preferences | ||||
| 	DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` | ||||
| @ -526,6 +528,16 @@ func (u *User) IsUserOrgOwner(orgID int64) bool { | ||||
| 	return isOwner | ||||
| } | ||||
| 
 | ||||
| // IsUserPartOfOrg returns true if user with userID is part of the u organisation. | ||||
| func (u *User) IsUserPartOfOrg(userID int64) bool { | ||||
| 	isMember, err := IsOrganizationMember(u.ID, userID) | ||||
| 	if err != nil { | ||||
| 		log.Error(4, "IsOrganizationMember: %v", err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return isMember | ||||
| } | ||||
| 
 | ||||
| // IsPublicMember returns true if user public his/her membership in given organization. | ||||
| func (u *User) IsPublicMember(orgID int64) bool { | ||||
| 	isMember, err := IsPublicMembership(orgID, u.ID) | ||||
| @ -1341,13 +1353,18 @@ type SearchUserOptions struct { | ||||
| 	UID           int64 | ||||
| 	OrderBy       SearchOrderBy | ||||
| 	Page          int | ||||
| 	PageSize      int // Can be smaller than or equal to setting.UI.ExplorePagingNum | ||||
| 	Private       bool  // Include private orgs in search | ||||
| 	OwnerID       int64 // id of user for visibility calculation | ||||
| 	PageSize      int   // Can be smaller than or equal to setting.UI.ExplorePagingNum | ||||
| 	IsActive      util.OptionalBool | ||||
| 	SearchByEmail bool // Search by email as well as username/full name | ||||
| } | ||||
| 
 | ||||
| func (opts *SearchUserOptions) toConds() builder.Cond { | ||||
| 	var cond builder.Cond = builder.Eq{"type": opts.Type} | ||||
| 
 | ||||
| 	var cond = builder.NewCond() | ||||
| 	cond = cond.And(builder.Eq{"type": opts.Type}) | ||||
| 
 | ||||
| 	if len(opts.Keyword) > 0 { | ||||
| 		lowerKeyword := strings.ToLower(opts.Keyword) | ||||
| 		keywordCond := builder.Or( | ||||
| @ -1361,6 +1378,27 @@ func (opts *SearchUserOptions) toConds() builder.Cond { | ||||
| 		cond = cond.And(keywordCond) | ||||
| 	} | ||||
| 
 | ||||
| 	if !opts.Private { | ||||
| 		// user not logged in and so they won't be allowed to see non-public orgs | ||||
| 		cond = cond.And(builder.In("visibility", structs.VisibleTypePublic)) | ||||
| 	} | ||||
| 
 | ||||
| 	if opts.OwnerID > 0 { | ||||
| 		var exprCond builder.Cond | ||||
| 		if DbCfg.Type == core.MYSQL { | ||||
| 			exprCond = builder.Expr("org_user.org_id = user.id") | ||||
| 		} else if DbCfg.Type == core.MSSQL { | ||||
| 			exprCond = builder.Expr("org_user.org_id = [user].id") | ||||
| 		} else { | ||||
| 			exprCond = builder.Expr("org_user.org_id = \"user\".id") | ||||
| 		} | ||||
| 		var accessCond = builder.NewCond() | ||||
| 		accessCond = builder.Or( | ||||
| 			builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.OwnerID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))), | ||||
| 			builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited)) | ||||
| 		cond = cond.And(accessCond) | ||||
| 	} | ||||
| 
 | ||||
| 	if opts.UID > 0 { | ||||
| 		cond = cond.And(builder.Eq{"id": opts.UID}) | ||||
| 	} | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| @ -6,6 +7,7 @@ package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 
 | ||||
| 	"github.com/go-macaron/binding" | ||||
| 	"gopkg.in/macaron.v1" | ||||
| @ -20,7 +22,8 @@ import ( | ||||
| 
 | ||||
| // CreateOrgForm form for creating organization | ||||
| type CreateOrgForm struct { | ||||
| 	OrgName string `binding:"Required;AlphaDashDot;MaxSize(35)" locale:"org.org_name_holder"` | ||||
| 	OrgName    string `binding:"Required;AlphaDashDot;MaxSize(35)" locale:"org.org_name_holder"` | ||||
| 	Visibility structs.VisibleType | ||||
| } | ||||
| 
 | ||||
| // Validate validates the fields | ||||
| @ -35,6 +38,7 @@ type UpdateOrgSettingForm struct { | ||||
| 	Description     string `binding:"MaxSize(255)"` | ||||
| 	Website         string `binding:"ValidUrl;MaxSize(255)"` | ||||
| 	Location        string `binding:"MaxSize(50)"` | ||||
| 	Visibility      structs.VisibleType | ||||
| 	MaxRepoCreation int | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -4,10 +4,16 @@ | ||||
| 
 | ||||
| package setting | ||||
| 
 | ||||
| import "regexp" | ||||
| import ( | ||||
| 	"regexp" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| ) | ||||
| 
 | ||||
| // Service settings | ||||
| var Service struct { | ||||
| 	DefaultOrgVisibility                    string | ||||
| 	DefaultOrgVisibilityMode                structs.VisibleType | ||||
| 	ActiveCodeLives                         int | ||||
| 	ResetPwdCodeLives                       int | ||||
| 	RegisterEmailConfirm                    bool | ||||
| @ -68,6 +74,8 @@ func newService() { | ||||
| 	Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org") | ||||
| 	Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true) | ||||
| 	Service.AutoWatchNewRepos = sec.Key("AUTO_WATCH_NEW_REPOS").MustBool(true) | ||||
| 	Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes)) | ||||
| 	Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility] | ||||
| 
 | ||||
| 	sec = Cfg.Section("openid") | ||||
| 	Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) | ||||
|  | ||||
							
								
								
									
										49
									
								
								modules/structs/org_type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								modules/structs/org_type.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package structs | ||||
| 
 | ||||
| // VisibleType defines the visibility (Organization only) | ||||
| type VisibleType int | ||||
| 
 | ||||
| const ( | ||||
| 	// VisibleTypePublic Visible for everyone | ||||
| 	VisibleTypePublic VisibleType = iota | ||||
| 
 | ||||
| 	// VisibleTypeLimited Visible for every connected user | ||||
| 	VisibleTypeLimited | ||||
| 
 | ||||
| 	// VisibleTypePrivate Visible only for organization's members | ||||
| 	VisibleTypePrivate | ||||
| ) | ||||
| 
 | ||||
| // VisibilityModes is a map of org Visibility types | ||||
| var VisibilityModes = map[string]VisibleType{ | ||||
| 	"public":  VisibleTypePublic, | ||||
| 	"limited": VisibleTypeLimited, | ||||
| 	"private": VisibleTypePrivate, | ||||
| } | ||||
| 
 | ||||
| // IsPublic returns true if VisibleType is public | ||||
| func (vt VisibleType) IsPublic() bool { | ||||
| 	return vt == VisibleTypePublic | ||||
| } | ||||
| 
 | ||||
| // IsLimited returns true if VisibleType is limited | ||||
| func (vt VisibleType) IsLimited() bool { | ||||
| 	return vt == VisibleTypeLimited | ||||
| } | ||||
| 
 | ||||
| // IsPrivate returns true if VisibleType is private | ||||
| func (vt VisibleType) IsPrivate() bool { | ||||
| 	return vt == VisibleTypePrivate | ||||
| } | ||||
| 
 | ||||
| // ExtractKeysFromMapString provides a slice of keys from map | ||||
| func ExtractKeysFromMapString(in map[string]VisibleType) (keys []string) { | ||||
| 	for k := range in { | ||||
| 		keys = append(keys, k) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| @ -1314,6 +1314,11 @@ settings.options = Organization | ||||
| settings.full_name = Full Name | ||||
| settings.website = Website | ||||
| settings.location = Location | ||||
| settings.visibility = Visibility | ||||
| settings.visibility.public = Public | ||||
| settings.visibility.limited = Limited (Visible to logged in users only) | ||||
| settings.visibility.private = Private (Visible only to organization members) | ||||
| 
 | ||||
| settings.update_settings = Update Settings | ||||
| settings.update_setting_success = Organization settings have been updated. | ||||
| settings.change_orgname_prompt = Note: changing the organization name also changes the organization's URL. | ||||
| @ -1617,6 +1622,7 @@ config.enable_timetracking = Enable Time Tracking | ||||
| config.default_enable_timetracking = Enable Time Tracking by Default | ||||
| config.default_allow_only_contributors_to_track_time = Let Only Contributors Track Time | ||||
| config.no_reply_address = Hidden Email Domain | ||||
| config.default_visibility_organization = Default visibility for new Organizations | ||||
| config.default_enable_dependencies = Enable Issue Dependencies by Default | ||||
| 
 | ||||
| config.webhook_config = Webhook Configuration | ||||
|  | ||||
| @ -129,6 +129,10 @@ func Get(ctx *context.APIContext) { | ||||
| 	// responses: | ||||
| 	//   "200": | ||||
| 	//     "$ref": "#/responses/Organization" | ||||
| 	if !models.HasOrgVisible(ctx.Org.Organization, ctx.User) { | ||||
| 		ctx.NotFound("HasOrgVisible", nil) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.JSON(200, convert.ToOrganization(ctx.Org.Organization)) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -302,6 +302,11 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !models.HasOrgVisible(org, ctx.User) { | ||||
| 		ctx.NotFound("HasOrgVisible", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.User.IsAdmin { | ||||
| 		isOwner, err := org.IsOwnedBy(ctx.User.ID) | ||||
| 		if err != nil { | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| @ -230,6 +231,7 @@ func ExploreUsers(ctx *context.Context) { | ||||
| 		Type:     models.UserTypeIndividual, | ||||
| 		PageSize: setting.UI.ExplorePagingNum, | ||||
| 		IsActive: util.OptionalBoolTrue, | ||||
| 		Private:  true, | ||||
| 	}, tplExploreUsers) | ||||
| } | ||||
| 
 | ||||
| @ -240,9 +242,16 @@ func ExploreOrganizations(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsExploreOrganizations"] = true | ||||
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||
| 
 | ||||
| 	var ownerID int64 | ||||
| 	if ctx.User != nil && !ctx.User.IsAdmin { | ||||
| 		ownerID = ctx.User.ID | ||||
| 	} | ||||
| 
 | ||||
| 	RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||
| 		Type:     models.UserTypeOrganization, | ||||
| 		PageSize: setting.UI.ExplorePagingNum, | ||||
| 		Private:  ctx.User != nil, | ||||
| 		OwnerID:  ownerID, | ||||
| 	}, tplExploreOrganizations) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2018 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| @ -23,6 +24,7 @@ const ( | ||||
| // Create render the page for create organization | ||||
| func Create(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("new_org") | ||||
| 	ctx.Data["DefaultOrgVisibilityMode"] = setting.Service.DefaultOrgVisibilityMode | ||||
| 	if !ctx.User.CanCreateOrganization() { | ||||
| 		ctx.ServerError("Not allowed", errors.New(ctx.Tr("org.form.create_org_not_allowed"))) | ||||
| 		return | ||||
| @ -45,9 +47,10 @@ func CreatePost(ctx *context.Context, form auth.CreateOrgForm) { | ||||
| 	} | ||||
| 
 | ||||
| 	org := &models.User{ | ||||
| 		Name:     form.OrgName, | ||||
| 		IsActive: true, | ||||
| 		Type:     models.UserTypeOrganization, | ||||
| 		Name:       form.OrgName, | ||||
| 		IsActive:   true, | ||||
| 		Type:       models.UserTypeOrganization, | ||||
| 		Visibility: form.Visibility, | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.CreateOrganization(org, ctx.User); err != nil { | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| @ -13,6 +14,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	userSetting "code.gitea.io/gitea/routers/user/setting" | ||||
| ) | ||||
| 
 | ||||
| @ -29,6 +31,7 @@ const ( | ||||
| func Settings(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("org.settings") | ||||
| 	ctx.Data["PageIsSettingsOptions"] = true | ||||
| 	ctx.Data["CurrentVisibility"] = structs.VisibleType(ctx.Org.Organization.Visibility) | ||||
| 	ctx.HTML(200, tplSettingsOptions) | ||||
| } | ||||
| 
 | ||||
| @ -79,6 +82,7 @@ func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) { | ||||
| 	org.Description = form.Description | ||||
| 	org.Website = form.Website | ||||
| 	org.Location = form.Location | ||||
| 	org.Visibility = form.Visibility | ||||
| 	if err := models.UpdateUser(org); err != nil { | ||||
| 		ctx.ServerError("UpdateUser", err) | ||||
| 		return | ||||
|  | ||||
| @ -303,6 +303,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st | ||||
| 
 | ||||
| // Home render repository home page | ||||
| func Home(ctx *context.Context) { | ||||
| 	if !models.HasOrgVisible(ctx.Repo.Repository.Owner, ctx.User) { | ||||
| 		ctx.NotFound("HasOrgVisible", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if len(ctx.Repo.Units) > 0 { | ||||
| 		var firstUnit *models.Unit | ||||
| 		for _, repoUnit := range ctx.Repo.Units { | ||||
|  | ||||
| @ -386,6 +386,12 @@ func showOrgProfile(ctx *context.Context) { | ||||
| 	} | ||||
| 
 | ||||
| 	org := ctx.Org.Organization | ||||
| 
 | ||||
| 	if !models.HasOrgVisible(org, ctx.User) { | ||||
| 		ctx.NotFound("HasOrgVisible", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Data["Title"] = org.DisplayName() | ||||
| 
 | ||||
| 	var orderBy models.SearchOrderBy | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| // Copyright 2015 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| @ -98,6 +99,7 @@ func Profile(ctx *context.Context) { | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Data["Orgs"] = orgs | ||||
| 	ctx.Data["HasOrgsVisible"] = models.HasOrgsVisible(orgs, ctx.User) | ||||
| 
 | ||||
| 	tab := ctx.Query("tab") | ||||
| 	ctx.Data["TabName"] = tab | ||||
|  | ||||
| @ -148,6 +148,9 @@ | ||||
| 					<dt>{{.i18n.Tr "admin.config.default_allow_only_contributors_to_track_time"}}</dt> | ||||
| 					<dd><i class="fa fa{{if .Service.DefaultAllowOnlyContributorsToTrackTime}}-check{{end}}-square-o"></i></dd> | ||||
| 				{{end}} | ||||
| 				<dt>{{.i18n.Tr "admin.config.default_visibility_organization"}}</dt> | ||||
| 				<dd>{{.Service.DefaultOrgVisibility}}</dd> | ||||
| 
 | ||||
| 				<dt>{{.i18n.Tr "admin.config.no_reply_address"}}</dt> | ||||
| 				<dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd> | ||||
| 				<dt>{{.i18n.Tr "admin.config.default_enable_dependencies"}}</dt> | ||||
|  | ||||
| @ -29,7 +29,12 @@ | ||||
| 					{{range .Users}} | ||||
| 						<tr> | ||||
| 							<td>{{.ID}}</td> | ||||
| 							<td><a href="{{.HomeLink}}">{{.Name}}</a></td> | ||||
| 							<td> | ||||
| 								<a href="{{.HomeLink}}">{{.Name}}</a> | ||||
| 								{{if .Visibility.IsPrivate}} | ||||
| 									<span class="text gold"><i class="octicon octicon-lock"></i></span> | ||||
| 								{{end}} | ||||
| 							</td> | ||||
| 							<td>{{.NumTeams}}</td> | ||||
| 							<td>{{.NumMembers}}</td> | ||||
| 							<td>{{.NumRepos}}</td> | ||||
|  | ||||
| @ -30,7 +30,12 @@ | ||||
| 					{{range .Repos}} | ||||
| 						<tr> | ||||
| 							<td>{{.ID}}</td> | ||||
| 							<td><a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a></td> | ||||
| 							<td> | ||||
| 								<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a> | ||||
| 								{{if .Owner.Visibility.IsPrivate}} | ||||
| 									<span class="text gold"><i class="octicon octicon-lock"></i></span> | ||||
| 								{{end}} | ||||
| 							</td> | ||||
| 							<td><a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Name}}</a></td> | ||||
| 							<td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td> | ||||
| 							<td>{{.NumWatches}}</td> | ||||
|  | ||||
| @ -9,7 +9,12 @@ | ||||
| 				<div class="item"> | ||||
| 				  <img class="ui avatar image" src="{{.RelAvatarLink}}"> | ||||
| 				  <div class="content"> | ||||
| 					<span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span> | ||||
| 					<span class="header"> | ||||
| 						<a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}} | ||||
| 						{{if .Visibility.IsPrivate}} | ||||
| 							<span class="text gold"><i class="octicon octicon-lock"></i></span> | ||||
| 						{{end}} | ||||
| 					</span> | ||||
| 					<div class="description"> | ||||
| 							{{if .Location}} | ||||
| 								<i class="octicon octicon-location"></i> {{.Location}} | ||||
|  | ||||
| @ -12,8 +12,11 @@ | ||||
| 					<span><i class="octicon octicon-repo-forked"></i></span> | ||||
| 				{{else if .IsMirror}} | ||||
| 					<span><i class="octicon octicon-repo-clone"></i></span> | ||||
| 				{{else if .Owner}} | ||||
| 					{{if .Owner.Visibility.IsPrivate}} | ||||
| 			          <span class="text gold"><i class="octicon octicon-lock"></i></span> | ||||
| 					{{end}} | ||||
| 				{{end}} | ||||
| 
 | ||||
| 				<div class="ui right metas"> | ||||
| 					<span class="text grey"><i class="octicon octicon-star"></i> {{.NumStars}}</span> | ||||
| 					<span class="text grey"><i class="octicon octicon-git-branch"></i> {{.NumForks}}</span> | ||||
|  | ||||
| @ -15,6 +15,28 @@ | ||||
| 						<span class="help">{{.i18n.Tr "org.org_name_helper"}}</span> | ||||
| 					</div> | ||||
| 
 | ||||
| 					<div class="inline required field {{if .Err_OrgVisibility}}error{{end}}"> | ||||
| 						<label for="visibility">{{.i18n.Tr "org.settings.visibility"}}</label> | ||||
| 						<div class="field"> | ||||
| 							<div class="ui radio checkbox"> | ||||
| 								<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}/> | ||||
| 								<label>{{.i18n.Tr "org.settings.visibility.public"}}</label> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="field"> | ||||
| 							<div class="ui radio checkbox"> | ||||
| 								<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}/> | ||||
| 								<label>{{.i18n.Tr "org.settings.visibility.limited"}}</label> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="field"> | ||||
| 							<div class="ui radio checkbox"> | ||||
| 								<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}/> | ||||
| 								<label>{{.i18n.Tr "org.settings.visibility.private"}}</label> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 
 | ||||
| 					<div class="inline field"> | ||||
| 						<label></label> | ||||
| 						<button class="ui green button"> | ||||
|  | ||||
| @ -33,6 +33,29 @@ | ||||
| 							<input id="location" name="location"  value="{{.Org.Location}}"> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<div class="ui divider"></div> | ||||
| 						<div class="field" id="visibility_box"> | ||||
| 							<label for="visibility">{{.i18n.Tr "org.settings.visibility"}}</label> | ||||
| 							<div class="field"> | ||||
| 								<div class="ui radio checkbox"> | ||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}/> | ||||
| 									<label>{{.i18n.Tr "org.settings.visibility.public"}}</label> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 							<div class="field"> | ||||
| 								<div class="ui radio checkbox"> | ||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}/> | ||||
| 									<label>{{.i18n.Tr "org.settings.visibility.limited"}}</label> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 							<div class="field"> | ||||
| 								<div class="ui radio checkbox"> | ||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}/> | ||||
| 									<label>{{.i18n.Tr "org.settings.visibility.private"}}</label> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 
 | ||||
| 						{{if .SignedUser.IsAdmin}} | ||||
| 						<div class="ui divider"></div> | ||||
| 
 | ||||
|  | ||||
| @ -61,10 +61,12 @@ | ||||
| 								</a> | ||||
| 							</li> | ||||
| 							*/}} | ||||
| 							{{if .Orgs}} | ||||
| 							{{if and .Orgs .HasOrgsVisible}} | ||||
| 							<li> | ||||
| 								{{range .Orgs}} | ||||
| 									<a href="{{.HomeLink}}"><img class="ui mini image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a> | ||||
| 									{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.IsUserPartOfOrg $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}} | ||||
| 										<a href="{{.HomeLink}}"><img class="ui mini image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a> | ||||
| 									{{end}} | ||||
| 								{{end}} | ||||
| 							</li> | ||||
| 							{{end}} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Rémy Boulanouar
						Rémy Boulanouar