summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--custom/conf/app.ini.sample5
-rw-r--r--docs/content/doc/advanced/config-cheat-sheet.en-us.md9
-rw-r--r--models/migrations/migrations.go1
-rw-r--r--models/org.go36
-rw-r--r--models/org_test.go71
-rw-r--r--models/repo_list.go35
-rw-r--r--models/user.go64
-rw-r--r--modules/auth/org.go6
-rw-r--r--modules/setting/service.go10
-rw-r--r--modules/structs/org_type.go49
-rw-r--r--options/locale/locale_en-US.ini6
-rw-r--r--routers/api/v1/org/org.go4
-rw-r--r--routers/api/v1/repo/repo.go5
-rw-r--r--routers/home.go9
-rw-r--r--routers/org/org.go9
-rw-r--r--routers/org/setting.go4
-rw-r--r--routers/repo/view.go5
-rw-r--r--routers/user/home.go6
-rw-r--r--routers/user/profile.go2
-rw-r--r--templates/admin/config.tmpl3
-rw-r--r--templates/admin/org/list.tmpl7
-rw-r--r--templates/admin/repo/list.tmpl7
-rw-r--r--templates/explore/organizations.tmpl7
-rw-r--r--templates/explore/repo_list.tmpl5
-rw-r--r--templates/org/create.tmpl22
-rw-r--r--templates/org/settings/options.tmpl23
-rw-r--r--templates/user/profile.tmpl6
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}}