// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
RequestedUser *User // the user we want activity for
+ RequestedTeam *Team // the team we want activity for
Actor *User // the user viewing the activity
IncludePrivate bool // include private actions
OnlyPerformedBy bool // only actions performed by requested user
}
}
+ if opts.RequestedTeam != nil {
+ env := opts.RequestedUser.AccessibleTeamReposEnv(opts.RequestedTeam)
+ teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
+ if err != nil {
+ return nil, fmt.Errorf("GetTeamRepositories: %v", err)
+ }
+ cond = cond.And(builder.In("repo_id", teamRepoIDs))
+ }
+
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
if opts.OnlyPerformedBy {
type accessibleReposEnv struct {
org *User
user *User
+ team *Team
teamIDs []int64
e Engine
keyword string
}, nil
}
+// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
+// that are accessible to the specified team.
+func (org *User) AccessibleTeamReposEnv(team *Team) AccessibleReposEnvironment {
+ return &accessibleReposEnv{
+ org: org,
+ team: team,
+ e: x,
+ orderBy: SearchOrderByRecentUpdated,
+ }
+}
+
func (env *accessibleReposEnv) cond() builder.Cond {
var cond = builder.NewCond()
- if env.user == nil || !env.user.IsRestricted {
- cond = cond.Or(builder.Eq{
- "`repository`.owner_id": env.org.ID,
- "`repository`.is_private": false,
- })
- }
- if len(env.teamIDs) > 0 {
- cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
+ if env.team != nil {
+ cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
+ } else {
+ if env.user == nil || !env.user.IsRestricted {
+ cond = cond.Or(builder.Eq{
+ "`repository`.owner_id": env.org.ID,
+ "`repository`.is_private": false,
+ })
+ }
+ if len(env.teamIDs) > 0 {
+ cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
+ }
}
if env.keyword != "" {
cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
Keyword string
OwnerID int64
PriorityOwnerID int64
+ TeamID int64
OrderBy SearchOrderBy
Private bool // Include private repositories in results
StarredByID int64
cond = cond.And(accessCond)
}
+ if opts.TeamID > 0 {
+ cond = cond.And(builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").From("team_repo").Where(builder.Eq{"`team_repo`.team_id": opts.TeamID})))
+ }
+
if opts.Keyword != "" {
// separate keyword
var subQueryCond = builder.NewCond()
// GetUserHeatmapDataByUser returns an array of UserHeatmapData
func GetUserHeatmapDataByUser(user *User, doer *User) ([]*UserHeatmapData, error) {
+ return getUserHeatmapData(user, nil, doer)
+}
+
+// GetUserHeatmapDataByUserTeam returns an array of UserHeatmapData
+func GetUserHeatmapDataByUserTeam(user *User, team *Team, doer *User) ([]*UserHeatmapData, error) {
+ return getUserHeatmapData(user, team, doer)
+}
+
+func getUserHeatmapData(user *User, team *Team, doer *User) ([]*UserHeatmapData, error) {
hdata := make([]*UserHeatmapData, 0)
if !activityReadable(user, doer) {
cond, err := activityQueryCondition(GetFeedsOptions{
RequestedUser: user,
+ RequestedTeam: team,
Actor: doer,
IncludePrivate: true, // don't filter by private, as we already filter by repo access
IncludeDeleted: true,
view_home = View %s
search_repos = Find a repository…
filter = Other Filters
+filter_by_team_repositories = Filter by team repositories
show_archived = Archived
show_both_archived_unarchived = Showing both archived and unarchived
// description: repo owner to prioritize in the results
// type: integer
// format: int64
+ // - name: team_id
+ // in: query
+ // description: search only for repos that belong to the given team id
+ // type: integer
+ // format: int64
// - name: starredBy
// in: query
// description: search only for repos that the user with the given id has starred
Keyword: strings.Trim(ctx.Query("q"), " "),
OwnerID: ctx.QueryInt64("uid"),
PriorityOwnerID: ctx.QueryInt64("priority_owner_id"),
+ TeamID: ctx.QueryInt64("team_id"),
TopicOnly: ctx.QueryBool("topic"),
Collaborate: util.OptionalBoolNone,
Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
m.Group("/:org", func() {
m.Get("/dashboard", user.Dashboard)
+ m.Get("/dashboard/:team", user.Dashboard)
m.Get("/^:type(issues|pulls)$", user.Issues)
+ m.Get("/^:type(issues|pulls)$/:team", user.Issues)
m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones)
+ m.Get("/milestones/:team", reqMilestonesDashboardPageEnabled, user.Milestones)
m.Get("/members", org.Members)
m.Post("/members/action/:action", org.MembersAction)
-
m.Get("/teams", org.Teams)
- }, context.OrgAssignment(true))
+ }, context.OrgAssignment(true, false, true))
m.Group("/:org", func() {
m.Get("/teams/:team", org.TeamMembers)
ctxUser := ctx.User
orgName := ctx.Params(":org")
if len(orgName) > 0 {
- // Organization.
- org, err := models.GetUserByName(orgName)
- if err != nil {
- if models.IsErrUserNotExist(err) {
- ctx.NotFound("GetUserByName", err)
- } else {
- ctx.ServerError("GetUserByName", err)
- }
- return nil
- }
- ctxUser = org
+ ctxUser = ctx.Org.Organization
+ ctx.Data["Teams"] = ctx.Org.Organization.Teams
}
ctx.Data["ContextUser"] = ctxUser
ctx.Data["PageIsDashboard"] = true
ctx.Data["PageIsNews"] = true
ctx.Data["SearchLimit"] = setting.UI.User.RepoPagingNum
+
// no heatmap access for admins; GetUserHeatmapDataByUser ignores the calling user
// so everyone would get the same empty heatmap
if setting.Service.EnableUserHeatmap && !ctxUser.KeepActivityPrivate {
- data, err := models.GetUserHeatmapDataByUser(ctxUser, ctx.User)
+ data, err := models.GetUserHeatmapDataByUserTeam(ctxUser, ctx.Org.Team, ctx.User)
if err != nil {
- ctx.ServerError("GetUserHeatmapDataByUser", err)
+ ctx.ServerError("GetUserHeatmapDataByUserTeam", err)
return
}
ctx.Data["HeatmapData"] = data
var err error
var mirrors []*models.Repository
if ctxUser.IsOrganization() {
- env, err := ctxUser.AccessibleReposEnv(ctx.User.ID)
- if err != nil {
- ctx.ServerError("AccessibleReposEnv", err)
- return
+ var env models.AccessibleReposEnvironment
+ if ctx.Org.Team != nil {
+ env = ctxUser.AccessibleTeamReposEnv(ctx.Org.Team)
+ } else {
+ env, err = ctxUser.AccessibleReposEnv(ctx.User.ID)
+ if err != nil {
+ ctx.ServerError("AccessibleReposEnv", err)
+ return
+ }
}
-
mirrors, err = env.MirrorRepos()
if err != nil {
ctx.ServerError("env.MirrorRepos", err)
retrieveFeeds(ctx, models.GetFeedsOptions{
RequestedUser: ctxUser,
+ RequestedTeam: ctx.Org.Team,
Actor: ctx.User,
IncludePrivate: true,
OnlyPerformedBy: false,
return
}
- var (
- repoOpts = models.SearchRepoOptions{
- Actor: ctxUser,
- OwnerID: ctxUser.ID,
- Private: true,
- AllPublic: false, // Include also all public repositories of users and public organisations
- AllLimited: false, // Include also all public repositories of limited organisations
- HasMilestones: util.OptionalBoolTrue, // Just needs display repos has milestones
- }
+ repoOpts := models.SearchRepoOptions{
+ Actor: ctxUser,
+ OwnerID: ctxUser.ID,
+ Private: true,
+ AllPublic: false, // Include also all public repositories of users and public organisations
+ AllLimited: false, // Include also all public repositories of limited organisations
+ HasMilestones: util.OptionalBoolTrue, // Just needs display repos has milestones
+ }
+
+ if ctxUser.IsOrganization() && ctx.Org.Team != nil {
+ repoOpts.TeamID = ctx.Org.Team.ID
+ }
+ var (
userRepoCond = models.SearchRepositoryCondition(&repoOpts) // all repo condition user could visit
repoCond = userRepoCond
repoIDs []int64
var err error
var userRepoIDs []int64
if ctxUser.IsOrganization() {
- env, err := ctxUser.AccessibleReposEnv(ctx.User.ID)
- if err != nil {
- ctx.ServerError("AccessibleReposEnv", err)
- return
+ var env models.AccessibleReposEnvironment
+ if ctx.Org.Team != nil {
+ env = ctxUser.AccessibleTeamReposEnv(ctx.Org.Team)
+ } else {
+ env, err = ctxUser.AccessibleReposEnv(ctx.User.ID)
+ if err != nil {
+ ctx.ServerError("AccessibleReposEnv", err)
+ return
+ }
}
userRepoIDs, err = env.RepoIDs(1, ctxUser.NumRepos)
if err != nil {
"name": "priority_owner_id",
"in": "query"
},
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "search only for repos that belong to the given team id",
+ "name": "team_id",
+ "in": "query"
+ },
{
"type": "integer",
"format": "int64",
{{if .ContextUser.IsOrganization}}
<div class="right stackable menu">
- <a class="{{if .PageIsNews}}active{{end}} item" style="margin-left: auto" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/dashboard">
+ <div class="item">
+ <div class="ui floating dropdown link jump">
+ <span class="text">
+ {{svg "octicon-people" 18}}
+ {{if .Team}}
+ {{.Team.Name}}
+ {{else}}
+ {{.i18n.Tr "org.teams"}}
+ {{end}}
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ </span>
+ <div class="context user overflow menu" tabindex="-1">
+ <div class="ui header">
+ {{.i18n.Tr "home.filter_by_team_repositories"}}
+ </div>
+ <div class="scrolling menu items">
+ <a class="{{if not $.Team}}active selected{{end}} item" title="{{.i18n.Tr "all"}}" href="{{AppSubUrl}}/org/{{$.Org.Name}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}">
+ {{.i18n.Tr "all"}}
+ </a>
+ {{range .Org.Teams}}
+ {{if not .IncludesAllRepositories}}
+ <a class="{{if $.Team}}{{if eq $.Team.ID .ID}}active selected{{end}}{{end}} item" title="{{.Name}}" href="{{AppSubUrl}}/org/{{$.Org.Name}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}/{{.Name}}">
+ {{.Name}}
+ </a>
+ {{end}}
+ {{end}}
+ </div>
+ </div>
+ </div>
+ </div>
+ <a class="{{if .PageIsNews}}active{{end}} item" style="margin-left: auto" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/dashboard{{if .Team}}/{{.Team.Name}}{{end}}">
{{svg "octicon-rss"}} {{.i18n.Tr "activities"}}
</a>
{{if not .UnitIssuesGlobalDisabled}}
- <a class="{{if .PageIsIssues}}active{{end}} item" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/issues">
+ <a class="{{if .PageIsIssues}}active{{end}} item" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/issues{{if .Team}}/{{.Team.Name}}{{end}}">
{{svg "octicon-issue-opened"}} {{.i18n.Tr "issues"}}
</a>
{{end}}
{{if not .UnitPullsGlobalDisabled}}
- <a class="{{if .PageIsPulls}}active{{end}} item" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/pulls">
+ <a class="{{if .PageIsPulls}}active{{end}} item" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/pulls{{if .Team}}/{{.Team.Name}}{{end}}">
{{svg "octicon-git-pull-request"}} {{.i18n.Tr "pull_requests"}}
</a>
{{end}}
{{if and .ShowMilestonesDashboardPage (not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled))}}
- <a class="{{if .PageIsMilestonesDashboard}}active{{end}} item" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/milestones">
+ <a class="{{if .PageIsMilestonesDashboard}}active{{end}} item" href="{{AppSubUrl}}/org/{{.ContextUser.Name}}/milestones{{if .Team}}/{{.Team.Name}}{{end}}">
{{svg "octicon-milestone"}} {{.i18n.Tr "milestones"}}
</a>
{{end}}
:search-limit="searchLimit"
:suburl="suburl"
:uid="uid"
+ {{if .Team}}
+ :team-id="{{.Team.ID}}"
+ {{end}}
:more-repos-link="'{{.ContextUser.HomeLink}}'"
{{if not .ContextUser.IsOrganization}}
:organizations="[
type: Number,
required: true
},
+ teamId: {
+ type: Number,
+ required: false,
+ default: 0
+ },
organizations: {
type: Array,
default: () => [],
return this.repos.length > 0 && this.repos.length < this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`];
},
searchURL() {
- return `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&q=${this.searchQuery
+ return `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery
}&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode
}${this.reposFilter !== 'all' ? '&exclusive=1' : ''
}${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : ''
this.isLoading = true;
if (!this.reposTotalCount) {
- const totalCountSearchURL = `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&q=&page=1&mode=`;
+ const totalCountSearchURL = `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
$.getJSON(totalCountSearchURL, (_result, _textStatus, request) => {
self.reposTotalCount = request.getResponseHeader('X-Total-Count');
});