Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Team dashboards #14159

Merged
merged 3 commits into from
Dec 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions models/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ func (a *Action) GetIssueContent() string {
// 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
Expand Down Expand Up @@ -357,6 +358,15 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
}
}

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 {
Expand Down
32 changes: 24 additions & 8 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ type AccessibleReposEnvironment interface {
type accessibleReposEnv struct {
org *User
user *User
team *Team
teamIDs []int64
e Engine
keyword string
Expand Down Expand Up @@ -782,16 +783,31 @@ func (org *User) accessibleReposEnv(e Engine, userID int64) (AccessibleReposEnvi
}, 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)})
Expand Down
5 changes: 5 additions & 0 deletions models/repo_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ type SearchRepoOptions struct {
Keyword string
OwnerID int64
PriorityOwnerID int64
TeamID int64
OrderBy SearchOrderBy
Private bool // Include private repositories in results
StarredByID int64
Expand Down Expand Up @@ -294,6 +295,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
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()
Expand Down
10 changes: 10 additions & 0 deletions models/user_heatmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ type UserHeatmapData struct {

// 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) {
Expand All @@ -39,6 +48,7 @@ func GetUserHeatmapDataByUser(user *User, doer *User) ([]*UserHeatmapData, error

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,
Expand Down
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ my_mirrors = My Mirrors
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
Expand Down
6 changes: 6 additions & 0 deletions routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ func Search(ctx *context.APIContext) {
// 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
Expand Down Expand Up @@ -131,6 +136,7 @@ func Search(ctx *context.APIContext) {
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")),
Expand Down
6 changes: 4 additions & 2 deletions routers/routes/macaron.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,13 +444,15 @@ func RegisterMacaronRoutes(m *macaron.Macaron) {

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)
Expand Down
68 changes: 37 additions & 31 deletions routers/user/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,8 @@ func getDashboardContextUser(ctx *context.Context) *models.User {
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

Expand Down Expand Up @@ -112,12 +103,13 @@ func Dashboard(ctx *context.Context) {
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
Expand All @@ -126,12 +118,16 @@ func Dashboard(ctx *context.Context) {
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)
Expand All @@ -155,6 +151,7 @@ func Dashboard(ctx *context.Context) {

retrieveFeeds(ctx, models.GetFeedsOptions{
RequestedUser: ctxUser,
RequestedTeam: ctx.Org.Team,
Actor: ctx.User,
IncludePrivate: true,
OnlyPerformedBy: false,
Expand Down Expand Up @@ -183,16 +180,20 @@ func Milestones(ctx *context.Context) {
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
Expand Down Expand Up @@ -412,10 +413,15 @@ func Issues(ctx *context.Context) {
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 {
Expand Down
7 changes: 7 additions & 0 deletions templates/swagger/v1_json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,13 @@
"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",
Expand Down
38 changes: 34 additions & 4 deletions templates/user/dashboard/navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,51 @@

{{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"}}&nbsp;{{.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"}}&nbsp;{{.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"}}&nbsp;{{.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"}}&nbsp;{{.i18n.Tr "milestones"}}
</a>
{{end}}
Expand Down
3 changes: 3 additions & 0 deletions templates/user/dashboard/repolist.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
:search-limit="searchLimit"
:suburl="suburl"
:uid="uid"
{{if .Team}}
:team-id="{{.Team.ID}}"
{{end}}
:more-repos-link="'{{.ContextUser.HomeLink}}'"
{{if not .ContextUser.IsOrganization}}
:organizations="[
Expand Down
9 changes: 7 additions & 2 deletions web_src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2755,6 +2755,11 @@ function initVueComponents() {
type: Number,
required: true
},
teamId: {
type: Number,
required: false,
default: 0
},
organizations: {
type: Array,
default: () => [],
Expand Down Expand Up @@ -2853,7 +2858,7 @@ function initVueComponents() {
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' : ''
Expand Down Expand Up @@ -3034,7 +3039,7 @@ function initVueComponents() {
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');
});
Expand Down