Skip to content

Commit c86bc8e

Browse files
zeripathlafriks
andauthored
Add paging and archive/private repository filtering to dashboard list (#11321)
* Add archived options to SearchRepository Signed-off-by: Andrew Thornton <[email protected]> * Add only-private search Signed-off-by: Andrew Thornton <[email protected]> * Add filter options and paging to dashboard repository page Signed-off-by: Andrew Thornton <[email protected]> * swagger generate Signed-off-by: Andrew Thornton <[email protected]> * fix-swagger-again Signed-off-by: Andrew Thornton <[email protected]> * as per @mrsdizzie also remember state Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: Lauris BH <[email protected]>
1 parent c3d9a5f commit c86bc8e

File tree

8 files changed

+371
-34
lines changed

8 files changed

+371
-34
lines changed

models/repo_list.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ type SearchRepoOptions struct {
140140
PriorityOwnerID int64
141141
OrderBy SearchOrderBy
142142
Private bool // Include private repositories in results
143+
OnlyPrivate bool // Include only private repositories in results
143144
StarredByID int64
144145
AllPublic bool // Include also all public repositories of users and public organisations
145146
AllLimited bool // Include also all public repositories of limited organisations
@@ -159,6 +160,10 @@ type SearchRepoOptions struct {
159160
// True -> include just mirrors
160161
// False -> include just non-mirrors
161162
Mirror util.OptionalBool
163+
// None -> include archived AND non-archived
164+
// True -> include just archived
165+
// False -> include just non-archived
166+
Archived util.OptionalBool
162167
// only search topic name
163168
TopicOnly bool
164169
// include description in keyword search
@@ -205,14 +210,26 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
205210
}
206211
} else {
207212
// Not looking at private organisations
208-
// We should be able to see all non-private repositories that either:
209-
cond = cond.And(builder.Eq{"is_private": false})
210-
accessCond := builder.Or(
211-
// A. Aren't in organisations __OR__
212-
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
213-
// B. Isn't a private or limited organisation.
214-
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}))))
215-
cond = cond.And(accessCond)
213+
// We should be able to see all non-private repositories that
214+
// isn't in a private or limited organisation.
215+
cond = cond.And(
216+
builder.Eq{"is_private": false},
217+
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(
218+
builder.And(
219+
builder.Eq{"type": UserTypeOrganization},
220+
builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
221+
))))
222+
}
223+
224+
if opts.OnlyPrivate {
225+
cond = cond.And(
226+
builder.Or(
227+
builder.Eq{"is_private": true},
228+
builder.In("owner_id", builder.Select("id").From("`user`").Where(
229+
builder.And(
230+
builder.Eq{"type": UserTypeOrganization},
231+
builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
232+
)))))
216233
}
217234

218235
if opts.Template != util.OptionalBoolNone {
@@ -299,6 +316,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
299316
cond = cond.And(accessibleRepositoryCondition(opts.Actor))
300317
}
301318

319+
if opts.Archived != util.OptionalBoolNone {
320+
cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue})
321+
}
322+
302323
switch opts.HasMilestones {
303324
case util.OptionalBoolTrue:
304325
cond = cond.And(builder.Gt{"num_milestones": 0})

options/locale/locale_en-US.ini

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@ my_orgs = My Organizations
206206
my_mirrors = My Mirrors
207207
view_home = View %s
208208
search_repos = Find a repository…
209+
filter = Other Filters
210+
211+
show_archived = Archived
212+
show_both_archived_unarchived = Showing both archived and unarchived
213+
show_only_archived = Showing only archived
214+
show_only_unarchived = Showing only unarchived
215+
216+
show_private = Private
217+
show_both_private_public = Showing both public and private
218+
show_only_private = Showing only private
219+
show_only_public = Showing only public
209220
210221
issues.in_your_repos = In your repositories
211222

routers/api/v1/repo/repo.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,18 @@ func Search(ctx *context.APIContext) {
7878
// in: query
7979
// description: include private repositories this user has access to (defaults to true)
8080
// type: boolean
81+
// - name: onlyPrivate
82+
// in: query
83+
// description: only include private repositories this user has access to (defaults to false)
84+
// type: boolean
8185
// - name: template
8286
// in: query
8387
// description: include template repositories this user has access to (defaults to true)
8488
// type: boolean
89+
// - name: archived
90+
// in: query
91+
// description: show only archived, non-archived or all repositories (defaults to all)
92+
// type: boolean
8593
// - name: mode
8694
// in: query
8795
// description: type of repository to search for. Supported values are
@@ -125,6 +133,7 @@ func Search(ctx *context.APIContext) {
125133
TopicOnly: ctx.QueryBool("topic"),
126134
Collaborate: util.OptionalBoolNone,
127135
Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
136+
OnlyPrivate: ctx.IsSigned && ctx.QueryBool("onlyPrivate"),
128137
Template: util.OptionalBoolNone,
129138
StarredByID: ctx.QueryInt64("starredBy"),
130139
IncludeDescription: ctx.QueryBool("includeDesc"),
@@ -156,6 +165,10 @@ func Search(ctx *context.APIContext) {
156165
return
157166
}
158167

168+
if ctx.Query("archived") != "" {
169+
opts.Archived = util.OptionalBoolOf(ctx.QueryBool("archived"))
170+
}
171+
159172
var sortMode = ctx.Query("sort")
160173
if len(sortMode) > 0 {
161174
var sortOrder = ctx.Query("order")

templates/swagger/v1_json.tmpl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,12 +1769,24 @@
17691769
"name": "private",
17701770
"in": "query"
17711771
},
1772+
{
1773+
"type": "boolean",
1774+
"description": "only include private repositories this user has access to (defaults to false)",
1775+
"name": "onlyPrivate",
1776+
"in": "query"
1777+
},
17721778
{
17731779
"type": "boolean",
17741780
"description": "include template repositories this user has access to (defaults to true)",
17751781
"name": "template",
17761782
"in": "query"
17771783
},
1784+
{
1785+
"type": "boolean",
1786+
"description": "show only archived, non-archived or all repositories (defaults to all)",
1787+
"name": "archived",
1788+
"in": "query"
1789+
},
17781790
{
17791791
"type": "string",
17801792
"description": "type of repository to search for. Supported values are \"fork\", \"source\", \"mirror\" and \"collaborative\"",

templates/user/dashboard/repolist.tmpl

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,46 @@
3535
{{end}}
3636
</h4>
3737
<div class="ui attached secondary segment repos-search">
38-
<div class="ui fluid icon input" :class="{loading: isLoading}">
38+
<div class="ui fluid right action left icon input" :class="{loading: isLoading}">
3939
<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}">
4040
<i class="search icon"></i>
41+
<div class="ui dropdown button" title="{{.i18n.Tr "home.filter"}}">
42+
<i class="icon filter"></i>
43+
<div class="menu">
44+
<div class="item">
45+
<a @click="toggleArchivedFilter()">
46+
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_both_archived_unarchived"}}" v-if="archivedFilter === 'both'">
47+
<input type="checkbox">
48+
<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label>
49+
</div>
50+
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_only_unarchived"}}" v-if="archivedFilter === 'unarchived'">
51+
<input type="checkbox">
52+
<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label>
53+
</div>
54+
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_only_archived"}}" v-if="archivedFilter === 'archived'">
55+
<input type="checkbox">
56+
<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label>
57+
</div>
58+
</a>
59+
</div>
60+
<div class="item">
61+
<a @click="togglePrivateFilter()">
62+
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_both_private_public"}}" v-if="privateFilter === 'both'">
63+
<input type="checkbox">
64+
<label><svg class="svg octicon-lock" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-lock" /></svg>{{.i18n.Tr "home.show_private"}}</label>
65+
</div>
66+
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_only_public"}}" v-if="privateFilter === 'public'">
67+
<input type="checkbox">
68+
<label><svg class="svg octicon-lock" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-lock" /></svg>{{.i18n.Tr "home.show_private"}}</label>
69+
</div>
70+
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_only_private"}}" v-if="privateFilter === 'private'">
71+
<input type="checkbox">
72+
<label><svg class="svg octicon-lock" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-lock" /></svg>{{.i18n.Tr "home.show_private"}}</label>
73+
</div>
74+
</a>
75+
</div>
76+
</div>
77+
</div>
4178
</div>
4279
<div class="ui secondary tiny pointing borderless menu center aligned grid repos-filter">
4380
<a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
@@ -64,7 +101,7 @@
64101
</div>
65102
<div class="ui attached table segment">
66103
<ul class="repo-owner-name-list">
67-
<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo, reposFilter)">
104+
<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo)">
68105
<a :href="suburl + '/' + repo.full_name">
69106
<svg :class="'svg ' + repoClass(repo)" width="16" height="16" aria-hidden="true"><use :xlink:href="'#' + repoClass(repo)" /></svg>
70107
<strong class="text truncate item-name">${repo.full_name}</strong>
@@ -75,7 +112,27 @@
75112
</a>
76113
</li>
77114
<li v-if="showMoreReposLink">
78-
<a :href="moreReposLink">{{.i18n.Tr "home.show_more_repos"}}</a>
115+
<div class="center">
116+
<div class="ui borderless pagination menu narrow">
117+
<a class="item navigation" :class="{'disabled': page === 1}"
118+
@click="changePage(1)" title="{{$.i18n.Tr "admin.first_page"}}">
119+
<i class="angle double left icon"></i>
120+
</a>
121+
<a class="item navigation" :class="{'disabled': page === 1}"
122+
@click="changePage(page - 1)" title="{{$.i18n.Tr "repo.issues.previous"}}">
123+
<i class="left arrow icon"></i>
124+
</a>
125+
<a class="active item">${page}</a>
126+
<a class="item navigation" :class="{'disabled': page === finalPage}"
127+
@click="changePage(page + 1)" title="{{$.i18n.Tr "repo.issues.next"}}">
128+
<i class="icon right arrow"></i>
129+
</a>
130+
<a class="item navigation" :class="{'disabled': page === finalPage}"
131+
@click="changePage(finalPage)" title="{{$.i18n.Tr "admin.last_page"}}">
132+
<i class="angle double right icon"></i>
133+
</a>
134+
</div>
135+
</div>
79136
</li>
80137
</ul>
81138
</div>

0 commit comments

Comments
 (0)