mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Add anonymous access support for private/unlisted repositories (#34051)
Follow #33127 Fix #8649, fix #639 This is a complete solution. A repo unit could be set to: * Anonymous read (non-signed-in user) * Everyone read (signed-in user) * Everyone write (wiki-only)
This commit is contained in:
		@@ -15,6 +15,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,7 +51,7 @@ func (p *Permission) HasAnyUnitAccess() bool {
 | 
				
			|||||||
	return p.AccessMode >= perm_model.AccessModeRead
 | 
						return p.AccessMode >= perm_model.AccessModeRead
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
 | 
					func (p *Permission) HasAnyUnitPublicAccess() bool {
 | 
				
			||||||
	for _, v := range p.anonymousAccessMode {
 | 
						for _, v := range p.anonymousAccessMode {
 | 
				
			||||||
		if v >= perm_model.AccessModeRead {
 | 
							if v >= perm_model.AccessModeRead {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
@@ -61,7 +62,11 @@ func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
 | 
				
			|||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return p.HasAnyUnitAccess()
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
 | 
				
			||||||
 | 
						return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HasUnits returns true if the permission contains attached units
 | 
					// HasUnits returns true if the permission contains attached units
 | 
				
			||||||
@@ -188,6 +193,9 @@ func (p *Permission) LogString() string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
 | 
					func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
 | 
				
			||||||
 | 
						if setting.Repository.ForcePrivate {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
 | 
						if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
 | 
				
			||||||
		if *modeMap == nil {
 | 
							if *modeMap == nil {
 | 
				
			||||||
			*modeMap = make(map[unit.Type]perm_model.AccessMode)
 | 
								*modeMap = make(map[unit.Type]perm_model.AccessMode)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -342,3 +342,9 @@ func UpdateRepoUnit(ctx context.Context, unit *RepoUnit) error {
 | 
				
			|||||||
	_, err := db.GetEngine(ctx).ID(unit.ID).Update(unit)
 | 
						_, err := db.GetEngine(ctx).ID(unit.ID).Update(unit)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UpdateRepoUnitPublicAccess(ctx context.Context, unit *RepoUnit) error {
 | 
				
			||||||
 | 
						_, err := db.GetEngine(ctx).Where("repo_id=? AND `type`=?", unit.RepoID, unit.Type).
 | 
				
			||||||
 | 
							Cols("anonymous_access_mode", "everyone_access_mode").Update(unit)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -926,6 +926,9 @@ permission_not_set = Not set
 | 
				
			|||||||
permission_no_access = No Access
 | 
					permission_no_access = No Access
 | 
				
			||||||
permission_read = Read
 | 
					permission_read = Read
 | 
				
			||||||
permission_write = Read and Write
 | 
					permission_write = Read and Write
 | 
				
			||||||
 | 
					permission_anonymous_read = Anonymous Read
 | 
				
			||||||
 | 
					permission_everyone_read = Everyone Read
 | 
				
			||||||
 | 
					permission_everyone_write = Everyone Write
 | 
				
			||||||
access_token_desc = Selected token permissions limit authorization only to the corresponding <a %s>API</a> routes. Read the <a %s>documentation</a> for more information.
 | 
					access_token_desc = Selected token permissions limit authorization only to the corresponding <a %s>API</a> routes. Read the <a %s>documentation</a> for more information.
 | 
				
			||||||
at_least_one_permission = You must select at least one permission to create a token
 | 
					at_least_one_permission = You must select at least one permission to create a token
 | 
				
			||||||
permissions_list = Permissions:
 | 
					permissions_list = Permissions:
 | 
				
			||||||
@@ -1138,6 +1141,7 @@ transfer.no_permission_to_reject = You do not have permission to reject this tra
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
desc.private = Private
 | 
					desc.private = Private
 | 
				
			||||||
desc.public = Public
 | 
					desc.public = Public
 | 
				
			||||||
 | 
					desc.public_access = Public Access
 | 
				
			||||||
desc.template = Template
 | 
					desc.template = Template
 | 
				
			||||||
desc.internal = Internal
 | 
					desc.internal = Internal
 | 
				
			||||||
desc.archived = Archived
 | 
					desc.archived = Archived
 | 
				
			||||||
@@ -2133,6 +2137,7 @@ contributors.contribution_type.deletions = Deletions
 | 
				
			|||||||
settings = Settings
 | 
					settings = Settings
 | 
				
			||||||
settings.desc = Settings is where you can manage the settings for the repository
 | 
					settings.desc = Settings is where you can manage the settings for the repository
 | 
				
			||||||
settings.options = Repository
 | 
					settings.options = Repository
 | 
				
			||||||
 | 
					settings.public_access = Public Access
 | 
				
			||||||
settings.collaboration = Collaborators
 | 
					settings.collaboration = Collaborators
 | 
				
			||||||
settings.collaboration.admin = Administrator
 | 
					settings.collaboration.admin = Administrator
 | 
				
			||||||
settings.collaboration.write = Write
 | 
					settings.collaboration.write = Write
 | 
				
			||||||
@@ -2179,7 +2184,6 @@ settings.advanced_settings = Advanced Settings
 | 
				
			|||||||
settings.wiki_desc = Enable Repository Wiki
 | 
					settings.wiki_desc = Enable Repository Wiki
 | 
				
			||||||
settings.use_internal_wiki = Use Built-In Wiki
 | 
					settings.use_internal_wiki = Use Built-In Wiki
 | 
				
			||||||
settings.default_wiki_branch_name = Default Wiki Branch Name
 | 
					settings.default_wiki_branch_name = Default Wiki Branch Name
 | 
				
			||||||
settings.default_permission_everyone_access = Default access permission for all signed-in users:
 | 
					 | 
				
			||||||
settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
 | 
					settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
 | 
				
			||||||
settings.use_external_wiki = Use External Wiki
 | 
					settings.use_external_wiki = Use External Wiki
 | 
				
			||||||
settings.external_wiki_url = External Wiki URL
 | 
					settings.external_wiki_url = External Wiki URL
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										155
									
								
								routers/web/repo/setting/public_access.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								routers/web/repo/setting/public_access.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,155 @@
 | 
				
			|||||||
 | 
					// Copyright 2025 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/perm"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/services/context"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tplRepoSettingsPublicAccess templates.TplName = "repo/settings/public_access"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parsePublicAccessMode(permission string, allowed []string) (ret struct {
 | 
				
			||||||
 | 
						AnonymousAccessMode, EveryoneAccessMode perm.AccessMode
 | 
				
			||||||
 | 
					},
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						ret.AnonymousAccessMode = perm.AccessModeNone
 | 
				
			||||||
 | 
						ret.EveryoneAccessMode = perm.AccessModeNone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if site admin forces repositories to be private, then do not allow any other access mode,
 | 
				
			||||||
 | 
						// otherwise the "force private" setting would be bypassed
 | 
				
			||||||
 | 
						if setting.Repository.ForcePrivate {
 | 
				
			||||||
 | 
							return ret
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !slices.Contains(allowed, permission) {
 | 
				
			||||||
 | 
							return ret
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch permission {
 | 
				
			||||||
 | 
						case paAnonymousRead:
 | 
				
			||||||
 | 
							ret.AnonymousAccessMode = perm.AccessModeRead
 | 
				
			||||||
 | 
						case paEveryoneRead:
 | 
				
			||||||
 | 
							ret.EveryoneAccessMode = perm.AccessModeRead
 | 
				
			||||||
 | 
						case paEveryoneWrite:
 | 
				
			||||||
 | 
							ret.EveryoneAccessMode = perm.AccessModeWrite
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						paNotSet        = "not-set"
 | 
				
			||||||
 | 
						paAnonymousRead = "anonymous-read"
 | 
				
			||||||
 | 
						paEveryoneRead  = "everyone-read"
 | 
				
			||||||
 | 
						paEveryoneWrite = "everyone-write"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type repoUnitPublicAccess struct {
 | 
				
			||||||
 | 
						UnitType          unit.Type
 | 
				
			||||||
 | 
						FormKey           string
 | 
				
			||||||
 | 
						DisplayName       string
 | 
				
			||||||
 | 
						PublicAccessTypes []string
 | 
				
			||||||
 | 
						UnitPublicAccess  string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func repoUnitPublicAccesses(ctx *context.Context) []*repoUnitPublicAccess {
 | 
				
			||||||
 | 
						accesses := []*repoUnitPublicAccess{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypeCode,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("repo.code"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypeIssues,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("issues"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypePullRequests,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("pull_requests"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypeReleases,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("repo.releases"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypeWiki,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("repo.wiki"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead, paEveryoneWrite},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypeProjects,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("repo.projects"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypePackages,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("repo.packages"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								UnitType:          unit.TypeActions,
 | 
				
			||||||
 | 
								DisplayName:       ctx.Locale.TrString("repo.actions"),
 | 
				
			||||||
 | 
								PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, ua := range accesses {
 | 
				
			||||||
 | 
							ua.FormKey = "repo-unit-access-" + strconv.Itoa(int(ua.UnitType))
 | 
				
			||||||
 | 
							for _, u := range ctx.Repo.Repository.Units {
 | 
				
			||||||
 | 
								if u.Type == ua.UnitType {
 | 
				
			||||||
 | 
									ua.UnitPublicAccess = paNotSet
 | 
				
			||||||
 | 
									switch {
 | 
				
			||||||
 | 
									case u.EveryoneAccessMode == perm.AccessModeWrite:
 | 
				
			||||||
 | 
										ua.UnitPublicAccess = paEveryoneWrite
 | 
				
			||||||
 | 
									case u.EveryoneAccessMode == perm.AccessModeRead:
 | 
				
			||||||
 | 
										ua.UnitPublicAccess = paEveryoneRead
 | 
				
			||||||
 | 
									case u.AnonymousAccessMode == perm.AccessModeRead:
 | 
				
			||||||
 | 
										ua.UnitPublicAccess = paAnonymousRead
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return slices.DeleteFunc(accesses, func(ua *repoUnitPublicAccess) bool {
 | 
				
			||||||
 | 
							return ua.UnitPublicAccess == ""
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func PublicAccess(ctx *context.Context) {
 | 
				
			||||||
 | 
						ctx.Data["PageIsSettingsPublicAccess"] = true
 | 
				
			||||||
 | 
						ctx.Data["RepoUnitPublicAccesses"] = repoUnitPublicAccesses(ctx)
 | 
				
			||||||
 | 
						ctx.Data["GlobalForcePrivate"] = setting.Repository.ForcePrivate
 | 
				
			||||||
 | 
						if setting.Repository.ForcePrivate {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.Tr("form.repository_force_private"), true)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.HTML(http.StatusOK, tplRepoSettingsPublicAccess)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func PublicAccessPost(ctx *context.Context) {
 | 
				
			||||||
 | 
						accesses := repoUnitPublicAccesses(ctx)
 | 
				
			||||||
 | 
						for _, ua := range accesses {
 | 
				
			||||||
 | 
							formVal := ctx.FormString(ua.FormKey)
 | 
				
			||||||
 | 
							parsed := parsePublicAccessMode(formVal, ua.PublicAccessTypes)
 | 
				
			||||||
 | 
							err := repo.UpdateRepoUnitPublicAccess(ctx, &repo.RepoUnit{
 | 
				
			||||||
 | 
								RepoID:              ctx.Repo.Repository.ID,
 | 
				
			||||||
 | 
								Type:                ua.UnitType,
 | 
				
			||||||
 | 
								AnonymousAccessMode: parsed.AnonymousAccessMode,
 | 
				
			||||||
 | 
								EveryoneAccessMode:  parsed.EveryoneAccessMode,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("UpdateRepoUnitPublicAccess", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
				
			||||||
 | 
						ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/public_access")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -13,7 +13,6 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/models/organization"
 | 
						"code.gitea.io/gitea/models/organization"
 | 
				
			||||||
	"code.gitea.io/gitea/models/perm"
 | 
					 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	unit_model "code.gitea.io/gitea/models/unit"
 | 
						unit_model "code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
@@ -37,6 +36,8 @@ import (
 | 
				
			|||||||
	mirror_service "code.gitea.io/gitea/services/mirror"
 | 
						mirror_service "code.gitea.io/gitea/services/mirror"
 | 
				
			||||||
	repo_service "code.gitea.io/gitea/services/repository"
 | 
						repo_service "code.gitea.io/gitea/services/repository"
 | 
				
			||||||
	wiki_service "code.gitea.io/gitea/services/wiki"
 | 
						wiki_service "code.gitea.io/gitea/services/wiki"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"xorm.io/xorm/convert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -48,15 +49,6 @@ const (
 | 
				
			|||||||
	tplDeployKeys      templates.TplName = "repo/settings/deploy_keys"
 | 
						tplDeployKeys      templates.TplName = "repo/settings/deploy_keys"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseEveryoneAccessMode(permission string, allowed ...perm.AccessMode) perm.AccessMode {
 | 
					 | 
				
			||||||
	// if site admin forces repositories to be private, then do not allow any other access mode,
 | 
					 | 
				
			||||||
	// otherwise the "force private" setting would be bypassed
 | 
					 | 
				
			||||||
	if setting.Repository.ForcePrivate {
 | 
					 | 
				
			||||||
		return perm.AccessModeNone
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return perm.ParseAccessMode(permission, allowed...)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SettingsCtxData is a middleware that sets all the general context data for the
 | 
					// SettingsCtxData is a middleware that sets all the general context data for the
 | 
				
			||||||
// settings template.
 | 
					// settings template.
 | 
				
			||||||
func SettingsCtxData(ctx *context.Context) {
 | 
					func SettingsCtxData(ctx *context.Context) {
 | 
				
			||||||
@@ -504,6 +496,17 @@ func handleSettingsPostPushMirrorAdd(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Redirect(repo.Link() + "/settings")
 | 
						ctx.Redirect(repo.Link() + "/settings")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newRepoUnit(repo *repo_model.Repository, unitType unit_model.Type, config convert.Conversion) repo_model.RepoUnit {
 | 
				
			||||||
 | 
						repoUnit := repo_model.RepoUnit{RepoID: repo.ID, Type: unitType, Config: config}
 | 
				
			||||||
 | 
						for _, u := range repo.Units {
 | 
				
			||||||
 | 
							if u.Type == unitType {
 | 
				
			||||||
 | 
								repoUnit.EveryoneAccessMode = u.EveryoneAccessMode
 | 
				
			||||||
 | 
								repoUnit.AnonymousAccessMode = u.AnonymousAccessMode
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return repoUnit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
					func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
				
			||||||
	form := web.GetForm(ctx).(*forms.RepoSettingForm)
 | 
						form := web.GetForm(ctx).(*forms.RepoSettingForm)
 | 
				
			||||||
	repo := ctx.Repo.Repository
 | 
						repo := ctx.Repo.Repository
 | 
				
			||||||
@@ -521,11 +524,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
 | 
						if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeCode, nil))
 | 
				
			||||||
			RepoID:             repo.ID,
 | 
					 | 
				
			||||||
			Type:               unit_model.TypeCode,
 | 
					 | 
				
			||||||
			EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultCodeEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	} else if !unit_model.TypeCode.UnitGlobalDisabled() {
 | 
						} else if !unit_model.TypeCode.UnitGlobalDisabled() {
 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -537,21 +536,12 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeExternalWiki, &repo_model.ExternalWikiConfig{
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypeExternalWiki,
 | 
					 | 
				
			||||||
			Config: &repo_model.ExternalWikiConfig{
 | 
					 | 
				
			||||||
			ExternalWikiURL: form.ExternalWikiURL,
 | 
								ExternalWikiURL: form.ExternalWikiURL,
 | 
				
			||||||
			},
 | 
							}))
 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
 | 
				
			||||||
	} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
 | 
						} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeWiki, new(repo_model.UnitConfig)))
 | 
				
			||||||
			RepoID:             repo.ID,
 | 
					 | 
				
			||||||
			Type:               unit_model.TypeWiki,
 | 
					 | 
				
			||||||
			Config:             new(repo_model.UnitConfig),
 | 
					 | 
				
			||||||
			EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
 | 
							if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
 | 
				
			||||||
@@ -580,28 +570,19 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
				
			|||||||
			ctx.Redirect(repo.Link() + "/settings")
 | 
								ctx.Redirect(repo.Link() + "/settings")
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeExternalTracker, &repo_model.ExternalTrackerConfig{
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypeExternalTracker,
 | 
					 | 
				
			||||||
			Config: &repo_model.ExternalTrackerConfig{
 | 
					 | 
				
			||||||
			ExternalTrackerURL:           form.ExternalTrackerURL,
 | 
								ExternalTrackerURL:           form.ExternalTrackerURL,
 | 
				
			||||||
			ExternalTrackerFormat:        form.TrackerURLFormat,
 | 
								ExternalTrackerFormat:        form.TrackerURLFormat,
 | 
				
			||||||
			ExternalTrackerStyle:         form.TrackerIssueStyle,
 | 
								ExternalTrackerStyle:         form.TrackerIssueStyle,
 | 
				
			||||||
			ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
 | 
								ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
 | 
				
			||||||
			},
 | 
							}))
 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
 | 
				
			||||||
	} else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
 | 
						} else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeIssues, &repo_model.IssuesConfig{
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypeIssues,
 | 
					 | 
				
			||||||
			Config: &repo_model.IssuesConfig{
 | 
					 | 
				
			||||||
			EnableTimetracker:                form.EnableTimetracker,
 | 
								EnableTimetracker:                form.EnableTimetracker,
 | 
				
			||||||
			AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
 | 
								AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
 | 
				
			||||||
			EnableDependencies:               form.EnableIssueDependencies,
 | 
								EnableDependencies:               form.EnableIssueDependencies,
 | 
				
			||||||
			},
 | 
							}))
 | 
				
			||||||
			EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultIssuesEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
 | 
							if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
 | 
				
			||||||
@@ -613,49 +594,33 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
 | 
						if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeProjects, &repo_model.ProjectsConfig{
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypeProjects,
 | 
					 | 
				
			||||||
			Config: &repo_model.ProjectsConfig{
 | 
					 | 
				
			||||||
			ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
 | 
								ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
 | 
				
			||||||
			},
 | 
							}))
 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
 | 
						} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
 | 
						if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeReleases, nil))
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypeReleases,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	} else if !unit_model.TypeReleases.UnitGlobalDisabled() {
 | 
						} else if !unit_model.TypeReleases.UnitGlobalDisabled() {
 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
 | 
						if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypePackages, nil))
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypePackages,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	} else if !unit_model.TypePackages.UnitGlobalDisabled() {
 | 
						} else if !unit_model.TypePackages.UnitGlobalDisabled() {
 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
 | 
						if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypeActions, nil))
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypeActions,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	} else if !unit_model.TypeActions.UnitGlobalDisabled() {
 | 
						} else if !unit_model.TypeActions.UnitGlobalDisabled() {
 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
 | 
						if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
 | 
				
			||||||
		units = append(units, repo_model.RepoUnit{
 | 
							units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, &repo_model.PullRequestsConfig{
 | 
				
			||||||
			RepoID: repo.ID,
 | 
					 | 
				
			||||||
			Type:   unit_model.TypePullRequests,
 | 
					 | 
				
			||||||
			Config: &repo_model.PullRequestsConfig{
 | 
					 | 
				
			||||||
			IgnoreWhitespaceConflicts:     form.PullsIgnoreWhitespace,
 | 
								IgnoreWhitespaceConflicts:     form.PullsIgnoreWhitespace,
 | 
				
			||||||
			AllowMerge:                    form.PullsAllowMerge,
 | 
								AllowMerge:                    form.PullsAllowMerge,
 | 
				
			||||||
			AllowRebase:                   form.PullsAllowRebase,
 | 
								AllowRebase:                   form.PullsAllowRebase,
 | 
				
			||||||
@@ -668,8 +633,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 | 
				
			|||||||
			DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
 | 
								DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
 | 
				
			||||||
			DefaultMergeStyle:             repo_model.MergeStyle(form.PullsDefaultMergeStyle),
 | 
								DefaultMergeStyle:             repo_model.MergeStyle(form.PullsDefaultMergeStyle),
 | 
				
			||||||
			DefaultAllowMaintainerEdit:    form.DefaultAllowMaintainerEdit,
 | 
								DefaultAllowMaintainerEdit:    form.DefaultAllowMaintainerEdit,
 | 
				
			||||||
			},
 | 
							}))
 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
 | 
						} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
 | 
				
			||||||
		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
 | 
							deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1078,6 +1078,8 @@ func registerRoutes(m *web.Router) {
 | 
				
			|||||||
		m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
 | 
							m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
 | 
				
			||||||
		m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
 | 
							m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m.Combo("/public_access").Get(repo_setting.PublicAccess).Post(repo_setting.PublicAccessPost)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("/collaboration", func() {
 | 
							m.Group("/collaboration", func() {
 | 
				
			||||||
			m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
 | 
								m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
 | 
				
			||||||
			m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
 | 
								m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,16 +111,13 @@ type RepoSettingForm struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Advanced settings
 | 
						// Advanced settings
 | 
				
			||||||
	EnableCode bool
 | 
						EnableCode bool
 | 
				
			||||||
	DefaultCodeEveryoneAccess string
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EnableWiki         bool
 | 
						EnableWiki         bool
 | 
				
			||||||
	EnableExternalWiki bool
 | 
						EnableExternalWiki bool
 | 
				
			||||||
	DefaultWikiBranch  string
 | 
						DefaultWikiBranch  string
 | 
				
			||||||
	DefaultWikiEveryoneAccess string
 | 
					 | 
				
			||||||
	ExternalWikiURL    string
 | 
						ExternalWikiURL    string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EnableIssues                          bool
 | 
						EnableIssues                          bool
 | 
				
			||||||
	DefaultIssuesEveryoneAccess           string
 | 
					 | 
				
			||||||
	EnableExternalTracker                 bool
 | 
						EnableExternalTracker                 bool
 | 
				
			||||||
	ExternalTrackerURL                    string
 | 
						ExternalTrackerURL                    string
 | 
				
			||||||
	TrackerURLFormat                      string
 | 
						TrackerURLFormat                      string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,9 @@
 | 
				
			|||||||
							<div class="repo-icon only-mobile" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.internal"}}">{{svg "octicon-shield-lock" 18}}</div>
 | 
												<div class="repo-icon only-mobile" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.internal"}}">{{svg "octicon-shield-lock" 18}}</div>
 | 
				
			||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 | 
										{{if $.Permission.HasAnyUnitPublicAccess}}
 | 
				
			||||||
 | 
											<span class="ui basic orange label">{{ctx.Locale.Tr "repo.desc.public_access"}}</span>
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
					{{if .IsTemplate}}
 | 
										{{if .IsTemplate}}
 | 
				
			||||||
						<span class="ui basic label not-mobile">{{ctx.Locale.Tr "repo.desc.template"}}</span>
 | 
											<span class="ui basic label not-mobile">{{ctx.Locale.Tr "repo.desc.template"}}</span>
 | 
				
			||||||
						<div class="repo-icon only-mobile" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.template"}}">{{svg "octicon-repo-template" 18}}</div>
 | 
											<div class="repo-icon only-mobile" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.template"}}">{{svg "octicon-repo-template" 18}}</div>
 | 
				
			||||||
@@ -208,7 +211,7 @@
 | 
				
			|||||||
						</a>
 | 
											</a>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					{{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
 | 
										{{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo)}}
 | 
				
			||||||
						<a class="{{if .PageIsActivity}}active {{end}}item" href="{{.RepoLink}}/activity">
 | 
											<a class="{{if .PageIsActivity}}active {{end}}item" href="{{.RepoLink}}/activity">
 | 
				
			||||||
							{{svg "octicon-pulse"}} {{ctx.Locale.Tr "repo.activity"}}
 | 
												{{svg "octicon-pulse"}} {{ctx.Locale.Tr "repo.activity"}}
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
{{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 | 
					{{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="ui fluid vertical menu">
 | 
					<div class="ui fluid vertical menu">
 | 
				
			||||||
 | 
						{{/* the default activity page "pulse" could work with any permission: code, issue, pr, release*/}}
 | 
				
			||||||
	<a class="{{if .PageIsPulse}}active {{end}}item" href="{{.RepoLink}}/activity">
 | 
						<a class="{{if .PageIsPulse}}active {{end}}item" href="{{.RepoLink}}/activity">
 | 
				
			||||||
		{{ctx.Locale.Tr "repo.activity.navbar.pulse"}}
 | 
							{{ctx.Locale.Tr "repo.activity.navbar.pulse"}}
 | 
				
			||||||
	</a>
 | 
						</a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,11 @@
 | 
				
			|||||||
		<a class="{{if .PageIsSettingsOptions}}active {{end}}item" href="{{.RepoLink}}/settings">
 | 
							<a class="{{if .PageIsSettingsOptions}}active {{end}}item" href="{{.RepoLink}}/settings">
 | 
				
			||||||
			{{ctx.Locale.Tr "repo.settings.options"}}
 | 
								{{ctx.Locale.Tr "repo.settings.options"}}
 | 
				
			||||||
		</a>
 | 
							</a>
 | 
				
			||||||
 | 
							{{if or .Repository.IsPrivate .Permission.HasAnyUnitPublicAccess}}
 | 
				
			||||||
 | 
							<a class="{{if .PageIsSettingsPublicAccess}}active {{end}}item" href="{{.RepoLink}}/settings/public_access">
 | 
				
			||||||
 | 
								{{ctx.Locale.Tr "repo.settings.public_access"}}
 | 
				
			||||||
 | 
							</a>
 | 
				
			||||||
 | 
							{{end}}
 | 
				
			||||||
		<a class="{{if .PageIsSettingsCollaboration}}active {{end}}item" href="{{.RepoLink}}/settings/collaboration">
 | 
							<a class="{{if .PageIsSettingsCollaboration}}active {{end}}item" href="{{.RepoLink}}/settings/collaboration">
 | 
				
			||||||
			{{ctx.Locale.Tr "repo.settings.collaboration"}}
 | 
								{{ctx.Locale.Tr "repo.settings.collaboration"}}
 | 
				
			||||||
		</a>
 | 
							</a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -310,15 +310,6 @@
 | 
				
			|||||||
						<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
 | 
											<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
 | 
				
			||||||
						<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
 | 
											<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="inline field tw-pl-4">
 | 
					 | 
				
			||||||
						{{$unitCode := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeCode}}
 | 
					 | 
				
			||||||
						<label>{{ctx.Locale.Tr "repo.settings.default_permission_everyone_access"}}</label>
 | 
					 | 
				
			||||||
						<select name="default_code_everyone_access" class="ui selection dropdown">
 | 
					 | 
				
			||||||
							{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
 | 
					 | 
				
			||||||
							<option value="none" {{Iif (eq $unitCode.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
 | 
					 | 
				
			||||||
							<option value="read" {{Iif (eq $unitCode.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
 | 
					 | 
				
			||||||
						</select>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
 | 
									{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
 | 
				
			||||||
@@ -346,16 +337,6 @@
 | 
				
			|||||||
							<label>{{ctx.Locale.Tr "repo.settings.default_wiki_branch_name"}}</label>
 | 
												<label>{{ctx.Locale.Tr "repo.settings.default_wiki_branch_name"}}</label>
 | 
				
			||||||
							<input name="default_wiki_branch" value="{{.Repository.DefaultWikiBranch}}">
 | 
												<input name="default_wiki_branch" value="{{.Repository.DefaultWikiBranch}}">
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<div class="inline field">
 | 
					 | 
				
			||||||
							{{$unitInternalWiki := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeWiki}}
 | 
					 | 
				
			||||||
							<label>{{ctx.Locale.Tr "repo.settings.default_permission_everyone_access"}}</label>
 | 
					 | 
				
			||||||
							<select name="default_wiki_everyone_access" class="ui selection dropdown">
 | 
					 | 
				
			||||||
								{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
 | 
					 | 
				
			||||||
								<option value="none" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
 | 
					 | 
				
			||||||
								<option value="read" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
 | 
					 | 
				
			||||||
								<option value="write" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 2) "selected"}}>{{ctx.Locale.Tr "settings.permission_write"}}</option>
 | 
					 | 
				
			||||||
							</select>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="field">
 | 
										<div class="field">
 | 
				
			||||||
						<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
 | 
											<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
 | 
				
			||||||
@@ -391,15 +372,6 @@
 | 
				
			|||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="field tw-pl-4 {{if (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
 | 
										<div class="field tw-pl-4 {{if (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
 | 
				
			||||||
						<div class="inline field">
 | 
					 | 
				
			||||||
							{{$unitIssue := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeIssues}}
 | 
					 | 
				
			||||||
							<label>{{ctx.Locale.Tr "repo.settings.default_permission_everyone_access"}}</label>
 | 
					 | 
				
			||||||
							<select name="default_issues_everyone_access" class="ui selection dropdown">
 | 
					 | 
				
			||||||
								{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
 | 
					 | 
				
			||||||
								<option value="none" {{Iif (eq $unitIssue.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
 | 
					 | 
				
			||||||
								<option value="read" {{Iif (eq $unitIssue.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
 | 
					 | 
				
			||||||
							</select>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
						{{if .Repository.CanEnableTimetracker}}
 | 
											{{if .Repository.CanEnableTimetracker}}
 | 
				
			||||||
							<div class="field">
 | 
												<div class="field">
 | 
				
			||||||
								<div class="ui checkbox">
 | 
													<div class="ui checkbox">
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								templates/repo/settings/public_access.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								templates/repo/settings/public_access.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings")}}
 | 
				
			||||||
 | 
					<div class="repo-setting-content">
 | 
				
			||||||
 | 
						{{$paNotSet := "not-set"}}
 | 
				
			||||||
 | 
						{{$paAnonymousRead := "anonymous-read"}}
 | 
				
			||||||
 | 
						{{$paEveryoneRead := "everyone-read"}}
 | 
				
			||||||
 | 
						{{$paEveryoneWrite := "everyone-write"}}
 | 
				
			||||||
 | 
						<form class="ui form" method="post">
 | 
				
			||||||
 | 
							{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
							<table class="ui table unstackable tw-my-2">
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<th></th>
 | 
				
			||||||
 | 
									<th>{{ctx.Locale.Tr "settings.permission_not_set"}}</th>
 | 
				
			||||||
 | 
									<th>{{ctx.Locale.Tr "settings.permission_anonymous_read"}}</th>
 | 
				
			||||||
 | 
									<th>{{ctx.Locale.Tr "settings.permission_everyone_read"}}</th>
 | 
				
			||||||
 | 
									<th>{{ctx.Locale.Tr "settings.permission_everyone_write"}}</th>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								{{range $ua := .RepoUnitPublicAccesses}}
 | 
				
			||||||
 | 
									<tr>
 | 
				
			||||||
 | 
										<td>{{$ua.DisplayName}}</td>
 | 
				
			||||||
 | 
										<td class="tw-text-center"><label><input type="radio" name="{{$ua.FormKey}}" value="{{$paNotSet}}" {{Iif (eq $paNotSet $ua.UnitPublicAccess) "checked"}}></label></td>
 | 
				
			||||||
 | 
										<td class="tw-text-center"><label><input type="radio" name="{{$ua.FormKey}}" value="{{$paAnonymousRead}}" {{Iif (eq $paAnonymousRead $ua.UnitPublicAccess) "checked"}}></label></td>
 | 
				
			||||||
 | 
										<td class="tw-text-center"><label><input type="radio" name="{{$ua.FormKey}}" value="{{$paEveryoneRead}}" {{Iif (eq $paEveryoneRead $ua.UnitPublicAccess) "checked"}}></label></td>
 | 
				
			||||||
 | 
										<td class="tw-text-center">
 | 
				
			||||||
 | 
											{{if SliceUtils.Contains $ua.PublicAccessTypes $paEveryoneWrite}}
 | 
				
			||||||
 | 
												<label><input type="radio" name="{{$ua.FormKey}}" value="{{$paEveryoneWrite}}" {{Iif (eq $paEveryoneWrite $ua.UnitPublicAccess) "checked"}}></label>
 | 
				
			||||||
 | 
											{{else}}
 | 
				
			||||||
 | 
												-
 | 
				
			||||||
 | 
											{{end}}
 | 
				
			||||||
 | 
										</td>
 | 
				
			||||||
 | 
									</tr>
 | 
				
			||||||
 | 
								{{end}}
 | 
				
			||||||
 | 
							</table>
 | 
				
			||||||
 | 
							<button class="ui primary button {{if .GlobalForcePrivate}}disabled{{end}}">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
 | 
				
			||||||
 | 
						</form>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "repo/settings/layout_footer" .}}
 | 
				
			||||||
@@ -7,10 +7,12 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/test"
 | 
						"code.gitea.io/gitea/modules/test"
 | 
				
			||||||
	"code.gitea.io/gitea/tests"
 | 
						"code.gitea.io/gitea/tests"
 | 
				
			||||||
@@ -19,8 +21,26 @@ import (
 | 
				
			|||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewRepo(t *testing.T) {
 | 
					func TestRepoView(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
						t.Run("ViewRepoPublic", testViewRepoPublic)
 | 
				
			||||||
 | 
						t.Run("ViewRepoWithCache", testViewRepoWithCache)
 | 
				
			||||||
 | 
						t.Run("ViewRepoPrivate", testViewRepoPrivate)
 | 
				
			||||||
 | 
						t.Run("ViewRepo1CloneLinkAnonymous", testViewRepo1CloneLinkAnonymous)
 | 
				
			||||||
 | 
						t.Run("ViewRepo1CloneLinkAuthorized", testViewRepo1CloneLinkAuthorized)
 | 
				
			||||||
 | 
						t.Run("ViewRepoWithSymlinks", testViewRepoWithSymlinks)
 | 
				
			||||||
 | 
						t.Run("ViewFileInRepo", testViewFileInRepo)
 | 
				
			||||||
 | 
						t.Run("BlameFileInRepo", testBlameFileInRepo)
 | 
				
			||||||
 | 
						t.Run("ViewRepoDirectory", testViewRepoDirectory)
 | 
				
			||||||
 | 
						t.Run("ViewRepoDirectoryReadme", testViewRepoDirectoryReadme)
 | 
				
			||||||
 | 
						t.Run("MarkDownReadmeImage", testMarkDownReadmeImage)
 | 
				
			||||||
 | 
						t.Run("MarkDownReadmeImageSubfolder", testMarkDownReadmeImageSubfolder)
 | 
				
			||||||
 | 
						t.Run("GeneratedSourceLink", testGeneratedSourceLink)
 | 
				
			||||||
 | 
						t.Run("ViewCommit", testViewCommit)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testViewRepoPublic(t *testing.T) {
 | 
				
			||||||
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,9 +61,9 @@ func TestViewRepo(t *testing.T) {
 | 
				
			|||||||
	session.MakeRequest(t, req, http.StatusNotFound)
 | 
						session.MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func testViewRepo(t *testing.T) {
 | 
					func testViewRepoWithCache(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
						testView := func(t *testing.T) {
 | 
				
			||||||
		req := NewRequest(t, "GET", "/org3/repo3")
 | 
							req := NewRequest(t, "GET", "/org3/repo3")
 | 
				
			||||||
		session := loginUser(t, "user2")
 | 
							session := loginUser(t, "user2")
 | 
				
			||||||
		resp := session.MakeRequest(t, req, http.StatusOK)
 | 
							resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
@@ -98,30 +118,61 @@ func testViewRepo(t *testing.T) {
 | 
				
			|||||||
		}, items)
 | 
							}, items)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewRepo2(t *testing.T) {
 | 
						// FIXME: these test don't seem quite right, no enough assert
 | 
				
			||||||
	// no last commit cache
 | 
						// no last commit cache
 | 
				
			||||||
	testViewRepo(t)
 | 
						testView(t)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// enable last commit cache for all repositories
 | 
						// enable last commit cache for all repositories
 | 
				
			||||||
	oldCommitsCount := setting.CacheService.LastCommit.CommitsCount
 | 
						oldCommitsCount := setting.CacheService.LastCommit.CommitsCount
 | 
				
			||||||
	setting.CacheService.LastCommit.CommitsCount = 0
 | 
						setting.CacheService.LastCommit.CommitsCount = 0
 | 
				
			||||||
	// first view will not hit the cache
 | 
						// first view will not hit the cache
 | 
				
			||||||
	testViewRepo(t)
 | 
						testView(t)
 | 
				
			||||||
	// second view will hit the cache
 | 
						// second view will hit the cache
 | 
				
			||||||
	testViewRepo(t)
 | 
						testView(t)
 | 
				
			||||||
	setting.CacheService.LastCommit.CommitsCount = oldCommitsCount
 | 
						setting.CacheService.LastCommit.CommitsCount = oldCommitsCount
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewRepo3(t *testing.T) {
 | 
					func testViewRepoPrivate(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := NewRequest(t, "GET", "/org3/repo3")
 | 
						req := NewRequest(t, "GET", "/org3/repo3")
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("OrgMemberAccess", func(t *testing.T) {
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", "/org3/repo3")
 | 
				
			||||||
		session := loginUser(t, "user4")
 | 
							session := loginUser(t, "user4")
 | 
				
			||||||
	session.MakeRequest(t, req, http.StatusOK)
 | 
							resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							assert.Contains(t, resp.Body.String(), `<div id="repo-files-table"`)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("PublicAccess-AnonymousAccess", func(t *testing.T) {
 | 
				
			||||||
 | 
							session := loginUser(t, "user1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// set unit code to "anonymous read"
 | 
				
			||||||
 | 
							req = NewRequestWithValues(t, "POST", "/org3/repo3/settings/public_access", map[string]string{
 | 
				
			||||||
 | 
								"_csrf": GetUserCSRFToken(t, session),
 | 
				
			||||||
 | 
								"repo-unit-access-" + strconv.Itoa(int(unit.TypeCode)): "anonymous-read",
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							session.MakeRequest(t, req, http.StatusSeeOther)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// try to "anonymous read" (ok)
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", "/org3/repo3")
 | 
				
			||||||
 | 
							resp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							assert.Contains(t, resp.Body.String(), `<span class="ui basic orange label">Public Access</span>`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// remove "anonymous read"
 | 
				
			||||||
 | 
							req = NewRequestWithValues(t, "POST", "/org3/repo3/settings/public_access", map[string]string{
 | 
				
			||||||
 | 
								"_csrf": GetUserCSRFToken(t, session),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							session.MakeRequest(t, req, http.StatusSeeOther)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// try to "anonymous read" (not found)
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", "/org3/repo3")
 | 
				
			||||||
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewRepo1CloneLinkAnonymous(t *testing.T) {
 | 
					func testViewRepo1CloneLinkAnonymous(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := NewRequest(t, "GET", "/user2/repo1")
 | 
						req := NewRequest(t, "GET", "/user2/repo1")
 | 
				
			||||||
	resp := MakeRequest(t, req, http.StatusOK)
 | 
						resp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
@@ -139,8 +190,8 @@ func TestViewRepo1CloneLinkAnonymous(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, "tea clone user2/repo1", link)
 | 
						assert.Equal(t, "tea clone user2/repo1", link)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
 | 
					func testViewRepo1CloneLinkAuthorized(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -162,8 +213,8 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, "tea clone user2/repo1", link)
 | 
						assert.Equal(t, "tea clone user2/repo1", link)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewRepoWithSymlinks(t *testing.T) {
 | 
					func testViewRepoWithSymlinks(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
	defer test.MockVariableValue(&setting.UI.FileIconTheme, "basic")()
 | 
						defer test.MockVariableValue(&setting.UI.FileIconTheme, "basic")()
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -186,8 +237,8 @@ func TestViewRepoWithSymlinks(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TestViewFileInRepo repo description, topics and summary should not be displayed when viewing a file
 | 
					// TestViewFileInRepo repo description, topics and summary should not be displayed when viewing a file
 | 
				
			||||||
func TestViewFileInRepo(t *testing.T) {
 | 
					func testViewFileInRepo(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -205,8 +256,8 @@ func TestViewFileInRepo(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TestBlameFileInRepo repo description, topics and summary should not be displayed when running blame on a file
 | 
					// TestBlameFileInRepo repo description, topics and summary should not be displayed when running blame on a file
 | 
				
			||||||
func TestBlameFileInRepo(t *testing.T) {
 | 
					func testBlameFileInRepo(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -224,8 +275,8 @@ func TestBlameFileInRepo(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TestViewRepoDirectory repo description, topics and summary should not be displayed when within a directory
 | 
					// TestViewRepoDirectory repo description, topics and summary should not be displayed when within a directory
 | 
				
			||||||
func TestViewRepoDirectory(t *testing.T) {
 | 
					func testViewRepoDirectory(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -246,8 +297,8 @@ func TestViewRepoDirectory(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ensure that the all the different ways to find and render a README work
 | 
					// ensure that the all the different ways to find and render a README work
 | 
				
			||||||
func TestViewRepoDirectoryReadme(t *testing.T) {
 | 
					func testViewRepoDirectoryReadme(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// there are many combinations:
 | 
						// there are many combinations:
 | 
				
			||||||
	// - READMEs can be .md, .txt, or have no extension
 | 
						// - READMEs can be .md, .txt, or have no extension
 | 
				
			||||||
@@ -353,8 +404,8 @@ func TestViewRepoDirectoryReadme(t *testing.T) {
 | 
				
			|||||||
	missing("symlink-loop", "/user2/readme-test/src/branch/symlink-loop/")
 | 
						missing("symlink-loop", "/user2/readme-test/src/branch/symlink-loop/")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMarkDownReadmeImage(t *testing.T) {
 | 
					func testMarkDownReadmeImage(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -375,8 +426,8 @@ func TestMarkDownReadmeImage(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, "/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg", src)
 | 
						assert.Equal(t, "/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg", src)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMarkDownReadmeImageSubfolder(t *testing.T) {
 | 
					func testMarkDownReadmeImageSubfolder(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -398,8 +449,8 @@ func TestMarkDownReadmeImageSubfolder(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, "/user2/repo1/media/branch/sub-home-md-img-check/docs/test-fake-img.jpg", src)
 | 
						assert.Equal(t, "/user2/repo1/media/branch/sub-home-md-img-check/docs/test-fake-img.jpg", src)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGeneratedSourceLink(t *testing.T) {
 | 
					func testGeneratedSourceLink(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("Rendered file", func(t *testing.T) {
 | 
						t.Run("Rendered file", func(t *testing.T) {
 | 
				
			||||||
		defer tests.PrintCurrentTest(t)()
 | 
							defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
@@ -434,8 +485,8 @@ func TestGeneratedSourceLink(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestViewCommit(t *testing.T) {
 | 
					func testViewCommit(t *testing.T) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := NewRequest(t, "GET", "/user2/repo1/commit/0123456789012345678901234567890123456789")
 | 
						req := NewRequest(t, "GET", "/user2/repo1/commit/0123456789012345678901234567890123456789")
 | 
				
			||||||
	req.Header.Add("Accept", "text/html")
 | 
						req.Header.Add("Accept", "text/html")
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user