diff options
27 files changed, 388 insertions, 28 deletions
diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 2f4386dc7d..8c5925298c 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -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 diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index aa3a491d2e..5321c4de9b 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -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`) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 174e7b5156..d6e7f31e46 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -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. diff --git a/models/org.go b/models/org.go index daff110cf5..2e22ae987b 100644 --- a/models/org.go +++ b/models/org.go @@ -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() diff --git a/models/org_test.go b/models/org_test.go index 4790f69971..b484208be1 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -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 +} diff --git a/models/repo_list.go b/models/repo_list.go index 049d1834f7..83d2665e8c 100644 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -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}) } diff --git a/models/user.go b/models/user.go index e50385d411..3fb1c3b59e 100644 --- a/models/user.go +++ b/models/user.go @@ -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}) } diff --git a/modules/auth/org.go b/modules/auth/org.go index d6c26b6336..e4921c2267 100644 --- a/modules/auth/org.go +++ b/modules/auth/org.go @@ -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 } diff --git a/modules/setting/service.go b/modules/setting/service.go index 4b9ddb055b..08bfb6c414 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -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) diff --git a/modules/structs/org_type.go b/modules/structs/org_type.go new file mode 100644 index 0000000000..86dc5c81cd --- /dev/null +++ b/modules/structs/org_type.go @@ -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 +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1e708b1762..9e51278edd 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -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 diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 59351e20d1..e3916046f0 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -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)) } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index d78700c6b0..f69cbee0c0 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -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 { diff --git a/routers/home.go b/routers/home.go index 7a23e8765e..a09d025d23 100644 --- a/routers/home.go +++ b/routers/home.go @@ -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) } diff --git a/routers/org/org.go b/routers/org/org.go index bb7540277c..3821b32216 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -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 { diff --git a/routers/org/setting.go b/routers/org/setting.go index 7f652c11d6..1c4e75288a 100644 --- a/routers/org/setting.go +++ b/routers/org/setting.go @@ -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 diff --git a/routers/repo/view.go b/routers/repo/view.go index 02cf2cc0bd..da6c527242 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -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 { diff --git a/routers/user/home.go b/routers/user/home.go index 67a882af9c..c4e169befd 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -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 diff --git a/routers/user/profile.go b/routers/user/profile.go index afb74fe8c3..b1daa9e496 100644 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -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 diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 2d660117d5..becaca61e5 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -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> diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl index 141628c5b3..e9dbc7f877 100644 --- a/templates/admin/org/list.tmpl +++ b/templates/admin/org/list.tmpl @@ -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> diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index 5d78e8c84e..423de6a3d8 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -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> diff --git a/templates/explore/organizations.tmpl b/templates/explore/organizations.tmpl index b977da4e4d..4e2bfc9fd9 100644 --- a/templates/explore/organizations.tmpl +++ b/templates/explore/organizations.tmpl @@ -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}} diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index 660299157b..94ce293389 100644 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -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> diff --git a/templates/org/create.tmpl b/templates/org/create.tmpl index 765ef240e5..acf914c9d4 100644 --- a/templates/org/create.tmpl +++ b/templates/org/create.tmpl @@ -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"> diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl index 8041365434..e8cfd97d4e 100644 --- a/templates/org/settings/options.tmpl +++ b/templates/org/settings/options.tmpl @@ -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> diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 5e6fbf4bb8..b3a500e9f6 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -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}} |