mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Backport #22705 by @yp05327
Fixes https://github.com/go-gitea/gitea/issues/22676
Context Data `IsOrganizationMember` and `IsOrganizationOwner` is used to
control the visibility of `people` and `team` tab.
2871ea0809/templates/org/menu.tmpl (L19-L40)
And because of the reuse of user projects page, User Context is changed
to Organization Context. But the value of `IsOrganizationMember` and
`IsOrganizationOwner` are not being given.
I reused func `HandleOrgAssignment` to add them to the ctx, but may have
some unnecessary variables, idk whether it is ok.
I found there is a missing `PageIsViewProjects` at create project page.
Co-authored-by: yp05327 <576951401@qq.com>
			
			
This commit is contained in:
		| @@ -239,6 +239,32 @@ func (org *Organization) CustomAvatarRelativePath() string { | |||||||
| 	return org.Avatar | 	return org.Avatar | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UnitPermission returns unit permission | ||||||
|  | func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode { | ||||||
|  | 	if doer != nil { | ||||||
|  | 		teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("GetUserOrgTeams: %v", err) | ||||||
|  | 			return perm.AccessModeNone | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := teams.LoadUnits(ctx); err != nil { | ||||||
|  | 			log.Error("LoadUnits: %v", err) | ||||||
|  | 			return perm.AccessModeNone | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if len(teams) > 0 { | ||||||
|  | 			return teams.UnitMaxAccess(unitType) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if org.Visibility.IsPublic() { | ||||||
|  | 		return perm.AccessModeRead | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return perm.AccessModeNone | ||||||
|  | } | ||||||
|  |  | ||||||
| // CreateOrganization creates record of a new organization. | // CreateOrganization creates record of a new organization. | ||||||
| func CreateOrganization(org *Organization, owner *user_model.User) (err error) { | func CreateOrganization(org *Organization, owner *user_model.User) (err error) { | ||||||
| 	if !owner.CanCreateOrganization() { | 	if !owner.CanCreateOrganization() { | ||||||
|   | |||||||
| @@ -393,6 +393,11 @@ func (u *User) IsOrganization() bool { | |||||||
| 	return u.Type == UserTypeOrganization | 	return u.Type == UserTypeOrganization | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // IsIndividual returns true if user is actually a individual user. | ||||||
|  | func (u *User) IsIndividual() bool { | ||||||
|  | 	return u.Type == UserTypeIndividual | ||||||
|  | } | ||||||
|  |  | ||||||
| // DisplayName returns full name if it's not empty, | // DisplayName returns full name if it's not empty, | ||||||
| // returns username otherwise. | // returns username otherwise. | ||||||
| func (u *User) DisplayName() string { | func (u *User) DisplayName() string { | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	"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/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
| ) | ) | ||||||
| @@ -31,29 +30,34 @@ type Organization struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool { | func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool { | ||||||
| 	if ctx.Doer == nil { | 	return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeWrite | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return org.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (org *Organization) UnitPermission(ctx *Context, doerID int64, unitType unit.Type) perm.AccessMode { | func (org *Organization) CanReadUnit(ctx *Context, unitType unit.Type) bool { | ||||||
| 	if doerID > 0 { | 	return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead | ||||||
| 		teams, err := organization.GetUserOrgTeams(ctx, org.Organization.ID, doerID) | } | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error("GetUserOrgTeams: %v", err) |  | ||||||
| 			return perm.AccessModeNone |  | ||||||
| 		} |  | ||||||
| 		if len(teams) > 0 { |  | ||||||
| 			return teams.UnitMaxAccess(unitType) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if org.Organization.Visibility == structs.VisibleTypePublic { | func GetOrganizationByParams(ctx *Context) { | ||||||
| 		return perm.AccessModeRead | 	orgName := ctx.Params(":org") | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return perm.AccessModeNone | 	var err error | ||||||
|  |  | ||||||
|  | 	ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if organization.IsErrOrgNotExist(err) { | ||||||
|  | 			redirectUserID, err := user_model.LookupUserRedirect(orgName) | ||||||
|  | 			if err == nil { | ||||||
|  | 				RedirectToUser(ctx, orgName, redirectUserID) | ||||||
|  | 			} else if user_model.IsErrUserRedirectNotExist(err) { | ||||||
|  | 				ctx.NotFound("GetUserByName", err) | ||||||
|  | 			} else { | ||||||
|  | 				ctx.ServerError("LookupUserRedirect", err) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			ctx.ServerError("GetUserByName", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // HandleOrgAssignment handles organization assignment | // HandleOrgAssignment handles organization assignment | ||||||
| @@ -77,25 +81,26 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { | |||||||
| 		requireTeamAdmin = args[3] | 		requireTeamAdmin = args[3] | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	orgName := ctx.Params(":org") |  | ||||||
|  |  | ||||||
| 	var err error | 	var err error | ||||||
| 	ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName) |  | ||||||
| 	if err != nil { | 	if ctx.ContextUser == nil { | ||||||
| 		if organization.IsErrOrgNotExist(err) { | 		// if Organization is not defined, get it from params | ||||||
| 			redirectUserID, err := user_model.LookupUserRedirect(orgName) | 		if ctx.Org.Organization == nil { | ||||||
| 			if err == nil { | 			GetOrganizationByParams(ctx) | ||||||
| 				RedirectToUser(ctx, orgName, redirectUserID) | 			if ctx.Written() { | ||||||
| 			} else if user_model.IsErrUserRedirectNotExist(err) { | 				return | ||||||
| 				ctx.NotFound("GetUserByName", err) |  | ||||||
| 			} else { |  | ||||||
| 				ctx.ServerError("LookupUserRedirect", err) |  | ||||||
| 			} | 			} | ||||||
| 		} else { |  | ||||||
| 			ctx.ServerError("GetUserByName", err) |  | ||||||
| 		} | 		} | ||||||
|  | 	} else if ctx.ContextUser.IsOrganization() { | ||||||
|  | 		if ctx.Org == nil { | ||||||
|  | 			ctx.Org = &Organization{} | ||||||
|  | 		} | ||||||
|  | 		ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser) | ||||||
|  | 	} else { | ||||||
|  | 		// ContextUser is an individual User | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	org := ctx.Org.Organization | 	org := ctx.Org.Organization | ||||||
|  |  | ||||||
| 	// Handle Visibility | 	// Handle Visibility | ||||||
| @@ -156,6 +161,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { | |||||||
| 	} | 	} | ||||||
| 	ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner | 	ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner | ||||||
| 	ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember | 	ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember | ||||||
|  | 	ctx.Data["IsProjectEnabled"] = true | ||||||
| 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | ||||||
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||||
| 	ctx.Data["IsPublicMember"] = func(uid int64) bool { | 	ctx.Data["IsPublicMember"] = func(uid int64) bool { | ||||||
| @@ -231,6 +237,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects) | ||||||
|  | 	ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages) | ||||||
|  | 	ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode) | ||||||
| } | } | ||||||
|  |  | ||||||
| // OrgAssignment returns a middleware to handle organization assignment | // OrgAssignment returns a middleware to handle organization assignment | ||||||
|   | |||||||
| @@ -156,6 +156,7 @@ func Home(ctx *context.Context) { | |||||||
| 	pager.SetDefaultParams(ctx) | 	pager.SetDefaultParams(ctx) | ||||||
| 	pager.AddParam(ctx, "language", "Language") | 	pager.AddParam(ctx, "language", "Language") | ||||||
| 	ctx.Data["Page"] = pager | 	ctx.Data["Page"] = pager | ||||||
|  | 	ctx.Data["ContextUser"] = ctx.ContextUser | ||||||
|  |  | ||||||
| 	ctx.HTML(http.StatusOK, tplOrgHome) | 	ctx.HTML(http.StatusOK, tplOrgHome) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -123,6 +123,7 @@ func NewProject(ctx *context.Context) { | |||||||
| 	ctx.Data["Title"] = ctx.Tr("repo.projects.new") | 	ctx.Data["Title"] = ctx.Tr("repo.projects.new") | ||||||
| 	ctx.Data["BoardTypes"] = project_model.GetBoardConfig() | 	ctx.Data["BoardTypes"] = project_model.GetBoardConfig() | ||||||
| 	ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) | 	ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) | ||||||
|  | 	ctx.Data["PageIsViewProjects"] = true | ||||||
| 	ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink() | 	ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink() | ||||||
| 	shared_user.RenderUserHeader(ctx) | 	shared_user.RenderUserHeader(ctx) | ||||||
| 	ctx.HTML(http.StatusOK, tplProjectsNew) | 	ctx.HTML(http.StatusOK, tplProjectsNew) | ||||||
|   | |||||||
| @@ -9,6 +9,8 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func RenderUserHeader(ctx *context.Context) { | func RenderUserHeader(ctx *context.Context) { | ||||||
|  | 	ctx.Data["IsProjectEnabled"] = true | ||||||
|  | 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | ||||||
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||||
| 	ctx.Data["ContextUser"] = ctx.ContextUser | 	ctx.Data["ContextUser"] = ctx.ContextUser | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ func CodeSearch(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["IsProjectEnabled"] = true | ||||||
| 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | ||||||
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||||
| 	ctx.Data["Title"] = ctx.Tr("explore.code") | 	ctx.Data["Title"] = ctx.Tr("explore.code") | ||||||
|   | |||||||
| @@ -288,6 +288,7 @@ func Profile(ctx *context.Context) { | |||||||
| 		pager.AddParam(ctx, "language", "Language") | 		pager.AddParam(ctx, "language", "Language") | ||||||
| 	} | 	} | ||||||
| 	ctx.Data["Page"] = pager | 	ctx.Data["Page"] = pager | ||||||
|  | 	ctx.Data["IsProjectEnabled"] = true | ||||||
| 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled | ||||||
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||||
|  |  | ||||||
|   | |||||||
| @@ -690,6 +690,21 @@ func RegisterRoutes(m *web.Route) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode) func(ctx *context.Context) { | ||||||
|  | 		return func(ctx *context.Context) { | ||||||
|  | 			if ctx.ContextUser == nil { | ||||||
|  | 				ctx.NotFound(unitType.String(), nil) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if ctx.ContextUser.IsOrganization() { | ||||||
|  | 				if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode { | ||||||
|  | 					ctx.NotFound(unitType.String(), nil) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// ***** START: Organization ***** | 	// ***** START: Organization ***** | ||||||
| 	m.Group("/org", func() { | 	m.Group("/org", func() { | ||||||
| 		m.Group("/{org}", func() { | 		m.Group("/{org}", func() { | ||||||
| @@ -869,8 +884,10 @@ func RegisterRoutes(m *web.Route) { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		m.Group("/projects", func() { | 		m.Group("/projects", func() { | ||||||
| 			m.Get("", org.Projects) | 			m.Group("", func() { | ||||||
| 			m.Get("/{id}", org.ViewProject) | 				m.Get("", org.Projects) | ||||||
|  | 				m.Get("/{id}", org.ViewProject) | ||||||
|  | 			}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead)) | ||||||
| 			m.Group("", func() { //nolint:dupl | 			m.Group("", func() { //nolint:dupl | ||||||
| 				m.Get("/new", org.NewProject) | 				m.Get("/new", org.NewProject) | ||||||
| 				m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost) | 				m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost) | ||||||
| @@ -890,25 +907,18 @@ func RegisterRoutes(m *web.Route) { | |||||||
| 						m.Post("/move", org.MoveIssues) | 						m.Post("/move", org.MoveIssues) | ||||||
| 					}) | 					}) | ||||||
| 				}) | 				}) | ||||||
| 			}, reqSignIn, func(ctx *context.Context) { | 			}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite), func(ctx *context.Context) { | ||||||
| 				if ctx.ContextUser == nil { | 				if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID { | ||||||
| 					ctx.NotFound("NewProject", nil) |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 				if ctx.ContextUser.IsOrganization() { |  | ||||||
| 					if !ctx.Org.CanWriteUnit(ctx, unit.TypeProjects) { |  | ||||||
| 						ctx.NotFound("NewProject", nil) |  | ||||||
| 						return |  | ||||||
| 					} |  | ||||||
| 				} else if ctx.ContextUser.ID != ctx.Doer.ID { |  | ||||||
| 					ctx.NotFound("NewProject", nil) | 					ctx.NotFound("NewProject", nil) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 			}) | 			}) | ||||||
| 		}, repo.MustEnableProjects) | 		}, repo.MustEnableProjects) | ||||||
|  |  | ||||||
| 		m.Get("/code", user.CodeSearch) | 		m.Group("", func() { | ||||||
| 	}, context_service.UserAssignmentWeb()) | 			m.Get("/code", user.CodeSearch) | ||||||
|  | 		}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead)) | ||||||
|  | 	}, context_service.UserAssignmentWeb(), context.OrgAssignment()) | ||||||
|  |  | ||||||
| 	// ***** Release Attachment Download without Signin | 	// ***** Release Attachment Download without Signin | ||||||
| 	m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload) | 	m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload) | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	org_model "code.gitea.io/gitea/models/organization" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| ) | ) | ||||||
| @@ -57,14 +56,6 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) | |||||||
| 			} else { | 			} else { | ||||||
| 				errCb(http.StatusInternalServerError, "GetUserByName", err) | 				errCb(http.StatusInternalServerError, "GetUserByName", err) | ||||||
| 			} | 			} | ||||||
| 		} else { |  | ||||||
| 			if ctx.ContextUser.IsOrganization() { |  | ||||||
| 				if ctx.Org == nil { |  | ||||||
| 					ctx.Org = &context.Organization{} |  | ||||||
| 				} |  | ||||||
| 				ctx.Org.Organization = (*org_model.Organization)(ctx.ContextUser) |  | ||||||
| 				ctx.Data["Org"] = ctx.Org.Organization |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,16 +3,18 @@ | |||||||
| 		<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}"> | 		<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}"> | ||||||
| 			{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} | 			{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} | ||||||
| 		</a> | 		</a> | ||||||
|  | 		{{if and .IsProjectEnabled .CanReadProjects}} | ||||||
| 		<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects"> | 		<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects"> | ||||||
| 			{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}} | 			{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}} | ||||||
| 		</a> | 		</a> | ||||||
| 		{{if .IsPackageEnabled}} | 		{{end}} | ||||||
|  | 		{{if and .IsPackageEnabled .CanReadPackages}} | ||||||
| 		<a class="item" href="{{$.Org.HomeLink}}/-/packages"> | 		<a class="item" href="{{$.Org.HomeLink}}/-/packages"> | ||||||
| 			{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} | 			{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} | ||||||
| 		</a> | 		</a> | ||||||
| 		{{end}} | 		{{end}} | ||||||
| 		{{if .IsRepoIndexerEnabled}} | 		{{if and .IsRepoIndexerEnabled .CanReadCode}} | ||||||
| 		<a class="{{if $.PageIsOrgCode}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code"> | 		<a class="item" href="{{$.Org.HomeLink}}/-/code"> | ||||||
| 			{{svg "octicon-code"}} {{$.locale.Tr "org.code"}} | 			{{svg "octicon-code"}} {{$.locale.Tr "org.code"}} | ||||||
| 		</a> | 		</a> | ||||||
| 		{{end}} | 		{{end}} | ||||||
|   | |||||||
| @@ -22,15 +22,17 @@ | |||||||
| 			<a class="item" href="{{.ContextUser.HomeLink}}"> | 			<a class="item" href="{{.ContextUser.HomeLink}}"> | ||||||
| 				{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} | 				{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} | ||||||
| 			</a> | 			</a> | ||||||
|  | 			{{if and .IsProjectEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects))}} | ||||||
| 			<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item"> | 			<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item"> | ||||||
| 				{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}} | 				{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}} | ||||||
| 			</a> | 			</a> | ||||||
| 			{{if (not .UnitPackagesGlobalDisabled)}} | 			{{end}} | ||||||
|  | 			{{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}} | ||||||
| 				<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item"> | 				<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item"> | ||||||
| 					{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} | 					{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} | ||||||
| 				</a> | 				</a> | ||||||
| 			{{end}} | 			{{end}} | ||||||
| 			{{if .IsRepoIndexerEnabled}} | 			{{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}} | ||||||
| 				<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item"> | 				<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item"> | ||||||
| 					{{svg "octicon-code"}} {{.locale.Tr "user.code"}} | 					{{svg "octicon-code"}} {{.locale.Tr "user.code"}} | ||||||
| 				</a> | 				</a> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Giteabot
					Giteabot