Add paging and archive/private repository filtering to dashboard list (#11321)
* Add archived options to SearchRepository Signed-off-by: Andrew Thornton <art27@cantab.net> * Add only-private search Signed-off-by: Andrew Thornton <art27@cantab.net> * Add filter options and paging to dashboard repository page Signed-off-by: Andrew Thornton <art27@cantab.net> * swagger generate Signed-off-by: Andrew Thornton <art27@cantab.net> * fix-swagger-again Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @mrsdizzie also remember state Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
@ -140,6 +140,7 @@ type SearchRepoOptions struct {
|
||||
PriorityOwnerID int64
|
||||
OrderBy SearchOrderBy
|
||||
Private bool // Include private repositories in results
|
||||
OnlyPrivate bool // Include only private repositories in results
|
||||
StarredByID int64
|
||||
AllPublic bool // Include also all public repositories of users and public organisations
|
||||
AllLimited bool // Include also all public repositories of limited organisations
|
||||
@ -159,6 +160,10 @@ type SearchRepoOptions struct {
|
||||
// True -> include just mirrors
|
||||
// False -> include just non-mirrors
|
||||
Mirror util.OptionalBool
|
||||
// None -> include archived AND non-archived
|
||||
// True -> include just archived
|
||||
// False -> include just non-archived
|
||||
Archived util.OptionalBool
|
||||
// only search topic name
|
||||
TopicOnly bool
|
||||
// include description in keyword search
|
||||
@ -205,14 +210,26 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
||||
}
|
||||
} else {
|
||||
// Not looking at private organisations
|
||||
// We should be able to see all non-private repositories that either:
|
||||
cond = cond.And(builder.Eq{"is_private": false})
|
||||
accessCond := builder.Or(
|
||||
// A. Aren't in organisations __OR__
|
||||
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
|
||||
// B. Isn't a private or limited organisation.
|
||||
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}))))
|
||||
cond = cond.And(accessCond)
|
||||
// We should be able to see all non-private repositories that
|
||||
// isn't in a private or limited organisation.
|
||||
cond = cond.And(
|
||||
builder.Eq{"is_private": false},
|
||||
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(
|
||||
builder.And(
|
||||
builder.Eq{"type": UserTypeOrganization},
|
||||
builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
|
||||
))))
|
||||
}
|
||||
|
||||
if opts.OnlyPrivate {
|
||||
cond = cond.And(
|
||||
builder.Or(
|
||||
builder.Eq{"is_private": true},
|
||||
builder.In("owner_id", builder.Select("id").From("`user`").Where(
|
||||
builder.And(
|
||||
builder.Eq{"type": UserTypeOrganization},
|
||||
builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
|
||||
)))))
|
||||
}
|
||||
|
||||
if opts.Template != util.OptionalBoolNone {
|
||||
@ -299,6 +316,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
||||
cond = cond.And(accessibleRepositoryCondition(opts.Actor))
|
||||
}
|
||||
|
||||
if opts.Archived != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue})
|
||||
}
|
||||
|
||||
switch opts.HasMilestones {
|
||||
case util.OptionalBoolTrue:
|
||||
cond = cond.And(builder.Gt{"num_milestones": 0})
|
||||
|
@ -206,6 +206,17 @@ my_orgs = My Organizations
|
||||
my_mirrors = My Mirrors
|
||||
view_home = View %s
|
||||
search_repos = Find a repository…
|
||||
filter = Other Filters
|
||||
|
||||
show_archived = Archived
|
||||
show_both_archived_unarchived = Showing both archived and unarchived
|
||||
show_only_archived = Showing only archived
|
||||
show_only_unarchived = Showing only unarchived
|
||||
|
||||
show_private = Private
|
||||
show_both_private_public = Showing both public and private
|
||||
show_only_private = Showing only private
|
||||
show_only_public = Showing only public
|
||||
|
||||
issues.in_your_repos = In your repositories
|
||||
|
||||
|
@ -78,10 +78,18 @@ func Search(ctx *context.APIContext) {
|
||||
// in: query
|
||||
// description: include private repositories this user has access to (defaults to true)
|
||||
// type: boolean
|
||||
// - name: onlyPrivate
|
||||
// in: query
|
||||
// description: only include private repositories this user has access to (defaults to false)
|
||||
// type: boolean
|
||||
// - name: template
|
||||
// in: query
|
||||
// description: include template repositories this user has access to (defaults to true)
|
||||
// type: boolean
|
||||
// - name: archived
|
||||
// in: query
|
||||
// description: show only archived, non-archived or all repositories (defaults to all)
|
||||
// type: boolean
|
||||
// - name: mode
|
||||
// in: query
|
||||
// description: type of repository to search for. Supported values are
|
||||
@ -125,6 +133,7 @@ func Search(ctx *context.APIContext) {
|
||||
TopicOnly: ctx.QueryBool("topic"),
|
||||
Collaborate: util.OptionalBoolNone,
|
||||
Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
|
||||
OnlyPrivate: ctx.IsSigned && ctx.QueryBool("onlyPrivate"),
|
||||
Template: util.OptionalBoolNone,
|
||||
StarredByID: ctx.QueryInt64("starredBy"),
|
||||
IncludeDescription: ctx.QueryBool("includeDesc"),
|
||||
@ -156,6 +165,10 @@ func Search(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Query("archived") != "" {
|
||||
opts.Archived = util.OptionalBoolOf(ctx.QueryBool("archived"))
|
||||
}
|
||||
|
||||
var sortMode = ctx.Query("sort")
|
||||
if len(sortMode) > 0 {
|
||||
var sortOrder = ctx.Query("order")
|
||||
|
@ -1769,12 +1769,24 @@
|
||||
"name": "private",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "only include private repositories this user has access to (defaults to false)",
|
||||
"name": "onlyPrivate",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "include template repositories this user has access to (defaults to true)",
|
||||
"name": "template",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "show only archived, non-archived or all repositories (defaults to all)",
|
||||
"name": "archived",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "type of repository to search for. Supported values are \"fork\", \"source\", \"mirror\" and \"collaborative\"",
|
||||
|
@ -35,9 +35,46 @@
|
||||
{{end}}
|
||||
</h4>
|
||||
<div class="ui attached secondary segment repos-search">
|
||||
<div class="ui fluid icon input" :class="{loading: isLoading}">
|
||||
<div class="ui fluid right action left icon input" :class="{loading: isLoading}">
|
||||
<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}">
|
||||
<i class="search icon"></i>
|
||||
<div class="ui dropdown button" title="{{.i18n.Tr "home.filter"}}">
|
||||
<i class="icon filter"></i>
|
||||
<div class="menu">
|
||||
<div class="item">
|
||||
<a @click="toggleArchivedFilter()">
|
||||
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_both_archived_unarchived"}}" v-if="archivedFilter === 'both'">
|
||||
<input type="checkbox">
|
||||
<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label>
|
||||
</div>
|
||||
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_only_unarchived"}}" v-if="archivedFilter === 'unarchived'">
|
||||
<input type="checkbox">
|
||||
<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label>
|
||||
</div>
|
||||
<div class="ui checkbox" id="archivedFilterCheckbox" title="{{.i18n.Tr "home.show_only_archived"}}" v-if="archivedFilter === 'archived'">
|
||||
<input type="checkbox">
|
||||
<label><i class="archive icon archived-icon"></i>{{.i18n.Tr "home.show_archived"}}</label>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a @click="togglePrivateFilter()">
|
||||
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_both_private_public"}}" v-if="privateFilter === 'both'">
|
||||
<input type="checkbox">
|
||||
<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>
|
||||
</div>
|
||||
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_only_public"}}" v-if="privateFilter === 'public'">
|
||||
<input type="checkbox">
|
||||
<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>
|
||||
</div>
|
||||
<div class="ui checkbox" id="privateFilterCheckbox" title="{{.i18n.Tr "home.show_only_private"}}" v-if="privateFilter === 'private'">
|
||||
<input type="checkbox">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui secondary tiny pointing borderless menu center aligned grid repos-filter">
|
||||
<a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
|
||||
@ -64,7 +101,7 @@
|
||||
</div>
|
||||
<div class="ui attached table segment">
|
||||
<ul class="repo-owner-name-list">
|
||||
<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo, reposFilter)">
|
||||
<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo)">
|
||||
<a :href="suburl + '/' + repo.full_name">
|
||||
<svg :class="'svg ' + repoClass(repo)" width="16" height="16" aria-hidden="true"><use :xlink:href="'#' + repoClass(repo)" /></svg>
|
||||
<strong class="text truncate item-name">${repo.full_name}</strong>
|
||||
@ -75,7 +112,27 @@
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="showMoreReposLink">
|
||||
<a :href="moreReposLink">{{.i18n.Tr "home.show_more_repos"}}</a>
|
||||
<div class="center">
|
||||
<div class="ui borderless pagination menu narrow">
|
||||
<a class="item navigation" :class="{'disabled': page === 1}"
|
||||
@click="changePage(1)" title="{{$.i18n.Tr "admin.first_page"}}">
|
||||
<i class="angle double left icon"></i>
|
||||
</a>
|
||||
<a class="item navigation" :class="{'disabled': page === 1}"
|
||||
@click="changePage(page - 1)" title="{{$.i18n.Tr "repo.issues.previous"}}">
|
||||
<i class="left arrow icon"></i>
|
||||
</a>
|
||||
<a class="active item">${page}</a>
|
||||
<a class="item navigation" :class="{'disabled': page === finalPage}"
|
||||
@click="changePage(page + 1)" title="{{$.i18n.Tr "repo.issues.next"}}">
|
||||
<i class="icon right arrow"></i>
|
||||
</a>
|
||||
<a class="item navigation" :class="{'disabled': page === finalPage}"
|
||||
@click="changePage(finalPage)" title="{{$.i18n.Tr "admin.last_page"}}">
|
||||
<i class="angle double right icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -318,11 +318,11 @@ code,
|
||||
}
|
||||
|
||||
.ui {
|
||||
&.left {
|
||||
&.left:not(.action) {
|
||||
float: left;
|
||||
}
|
||||
|
||||
&.right {
|
||||
&.right:not(.action) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@ -727,6 +727,15 @@ code,
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.narrow .item {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
min-width: 1em;
|
||||
text-align: center;
|
||||
.icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
#privateFilterCheckbox .svg {
|
||||
color: #888888;
|
||||
margin-right: .25rem;
|
||||
}
|
||||
|
||||
.repo-owner-name-list {
|
||||
.item-name {
|
||||
max-width: 70%;
|
||||
|
Reference in New Issue
Block a user