| @@ -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) | |||
| @@ -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}} | |||