mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-03-12 00:12:37 +01:00
feat(issue search): query string for boolean operators and phrase search (#6952)
closes #6909 related to forgejo/design#14 # Description Adds the following boolean operators for issues when using an indexer (with minor caveats) - `+term`: `term` MUST be present for any result - `-term`: negation; exclude results that contain `term` - `"this is a term"`: matches the exact phrase `this is a term` In all cases the special characters may be escaped by the prefix `\` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6952 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Otto <otto@codeberg.org> Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com> Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
This commit is contained in:
parent
eaa641c21e
commit
cddf608cb9
19 changed files with 451 additions and 192 deletions
|
@ -156,25 +156,32 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
var queries []query.Query
|
||||
|
||||
if options.Keyword != "" {
|
||||
if options.IsFuzzyKeyword {
|
||||
fuzziness := 1
|
||||
if kl := len(options.Keyword); kl > 3 {
|
||||
fuzziness = 2
|
||||
} else if kl < 2 {
|
||||
fuzziness = 0
|
||||
}
|
||||
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
|
||||
inner_bleve.MatchQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
|
||||
}...))
|
||||
} else {
|
||||
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, 0),
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, 0),
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, 0),
|
||||
}...))
|
||||
tokens, err := options.Tokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := bleve.NewBooleanQuery()
|
||||
for _, token := range tokens {
|
||||
fuzziness := 0
|
||||
if token.Fuzzy {
|
||||
// TODO: replace with "auto" after bleve update
|
||||
fuzziness = min(len(token.Term)/4, 2)
|
||||
}
|
||||
innerQ := bleve.NewDisjunctionQuery(
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "title", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "content", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "comments", issueIndexerAnalyzer, fuzziness))
|
||||
|
||||
switch token.Kind {
|
||||
case internal.BoolOptMust:
|
||||
q.AddMust(innerQ)
|
||||
case internal.BoolOptShould:
|
||||
q.AddShould(innerQ)
|
||||
case internal.BoolOptNot:
|
||||
q.AddMustNot(innerQ)
|
||||
}
|
||||
}
|
||||
queries = append(queries, q)
|
||||
}
|
||||
|
||||
if len(options.RepoIDs) > 0 || options.AllPublic {
|
||||
|
|
|
@ -23,6 +23,10 @@ const (
|
|||
// Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types
|
||||
esMultiMatchTypeBestFields = "best_fields"
|
||||
esMultiMatchTypePhrasePrefix = "phrase_prefix"
|
||||
|
||||
// fuzziness options
|
||||
// Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/common-options.html#fuzziness
|
||||
esFuzzyAuto = "AUTO"
|
||||
)
|
||||
|
||||
var _ internal.Indexer = &Indexer{}
|
||||
|
@ -145,12 +149,30 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
query := elastic.NewBoolQuery()
|
||||
|
||||
if options.Keyword != "" {
|
||||
searchType := esMultiMatchTypePhrasePrefix
|
||||
if options.IsFuzzyKeyword {
|
||||
searchType = esMultiMatchTypeBestFields
|
||||
q := elastic.NewBoolQuery()
|
||||
tokens, err := options.Tokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, token := range tokens {
|
||||
innerQ := elastic.NewMultiMatchQuery(token.Term, "title", "content", "comments")
|
||||
if token.Fuzzy {
|
||||
// If the term is not a phrase use fuzziness set to AUTO
|
||||
innerQ = innerQ.Type(esMultiMatchTypeBestFields).Fuzziness(esFuzzyAuto)
|
||||
} else {
|
||||
innerQ = innerQ.Type(esMultiMatchTypePhrasePrefix)
|
||||
}
|
||||
|
||||
query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(searchType))
|
||||
switch token.Kind {
|
||||
case internal.BoolOptMust:
|
||||
q.Must(innerQ)
|
||||
case internal.BoolOptShould:
|
||||
q.Should(innerQ)
|
||||
case internal.BoolOptNot:
|
||||
q.MustNot(innerQ)
|
||||
}
|
||||
}
|
||||
query.Must(q)
|
||||
}
|
||||
|
||||
if len(options.RepoIDs) > 0 {
|
||||
|
|
|
@ -74,8 +74,6 @@ type SearchResult struct {
|
|||
type SearchOptions struct {
|
||||
Keyword string // keyword to search
|
||||
|
||||
IsFuzzyKeyword bool // if false the levenshtein distance is 0
|
||||
|
||||
RepoIDs []int64 // repository IDs which the issues belong to
|
||||
AllPublic bool // if include all public repositories
|
||||
|
||||
|
|
112
modules/indexer/issues/internal/qstring.go
Normal file
112
modules/indexer/issues/internal/qstring.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BoolOpt int
|
||||
|
||||
const (
|
||||
BoolOptMust BoolOpt = iota
|
||||
BoolOptShould
|
||||
BoolOptNot
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Term string
|
||||
Kind BoolOpt
|
||||
Fuzzy bool
|
||||
}
|
||||
|
||||
type Tokenizer struct {
|
||||
in *strings.Reader
|
||||
}
|
||||
|
||||
func (t *Tokenizer) next() (tk Token, err error) {
|
||||
var (
|
||||
sb strings.Builder
|
||||
r rune
|
||||
)
|
||||
tk.Kind = BoolOptShould
|
||||
tk.Fuzzy = true
|
||||
|
||||
// skip all leading white space
|
||||
for {
|
||||
if r, _, err = t.in.ReadRune(); err == nil && r == ' ' {
|
||||
//nolint:staticcheck,wastedassign // SA4006 the variable is used after the loop
|
||||
r, _, err = t.in.ReadRune()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// check for +/- op, increment to the next rune in both cases
|
||||
switch r {
|
||||
case '+':
|
||||
tk.Kind = BoolOptMust
|
||||
r, _, err = t.in.ReadRune()
|
||||
case '-':
|
||||
tk.Kind = BoolOptNot
|
||||
r, _, err = t.in.ReadRune()
|
||||
}
|
||||
if err != nil {
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// parse the string, escaping special characters
|
||||
for esc := false; err == nil; r, _, err = t.in.ReadRune() {
|
||||
if esc {
|
||||
if !strings.ContainsRune("+-\\\"", r) {
|
||||
sb.WriteRune('\\')
|
||||
}
|
||||
sb.WriteRune(r)
|
||||
esc = false
|
||||
continue
|
||||
}
|
||||
switch r {
|
||||
case '\\':
|
||||
esc = true
|
||||
case '"':
|
||||
if !tk.Fuzzy {
|
||||
goto nextEnd
|
||||
}
|
||||
tk.Fuzzy = false
|
||||
case ' ', '\t':
|
||||
if tk.Fuzzy {
|
||||
goto nextEnd
|
||||
}
|
||||
sb.WriteRune(r)
|
||||
default:
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
nextEnd:
|
||||
|
||||
tk.Term = sb.String()
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
} // do not consider EOF as an error at the end
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// Tokenize the keyword
|
||||
func (o *SearchOptions) Tokens() (tokens []Token, err error) {
|
||||
in := strings.NewReader(o.Keyword)
|
||||
it := Tokenizer{in: in}
|
||||
|
||||
for token, err := it.next(); err == nil; token, err = it.next() {
|
||||
tokens = append(tokens, token)
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
171
modules/indexer/issues/internal/qstring_test.go
Normal file
171
modules/indexer/issues/internal/qstring_test.go
Normal file
|
@ -0,0 +1,171 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testIssueQueryStringOpt struct {
|
||||
Keyword string
|
||||
Results []Token
|
||||
}
|
||||
|
||||
var testOpts = []testIssueQueryStringOpt{
|
||||
{
|
||||
Keyword: "Hello",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "Hello World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+Hello +World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+Hello World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+Hello -World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptNot,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "-\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptNot,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\"+Hello -World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "+Hello -World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\\+Hello", // \+Hello => +Hello
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "+Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\\\\Hello", // \\Hello => \Hello
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "\\Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\\\"Hello", // \"Hello => "Hello
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "\"Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestIssueQueryString(t *testing.T) {
|
||||
var opt SearchOptions
|
||||
for _, res := range testOpts {
|
||||
t.Run(opt.Keyword, func(t *testing.T) {
|
||||
opt.Keyword = res.Keyword
|
||||
tokens, err := opt.Tokens()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Results, tokens)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -131,6 +131,20 @@ var cases = []*testIndexerCase{
|
|||
ExpectedIDs: []int64{1002, 1001, 1000},
|
||||
ExpectedTotal: 3,
|
||||
},
|
||||
{
|
||||
Name: "Keyword Exclude",
|
||||
ExtraData: []*internal.IndexerData{
|
||||
{ID: 1000, Title: "hi hello world"},
|
||||
{ID: 1001, Content: "hi hello world"},
|
||||
{ID: 1002, Comments: []string{"hello", "hello world"}},
|
||||
},
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Keyword: "hello world -hi",
|
||||
SortBy: internal.SortByCreatedDesc,
|
||||
},
|
||||
ExpectedIDs: []int64{1002},
|
||||
ExpectedTotal: 1,
|
||||
},
|
||||
{
|
||||
Name: "Keyword Fuzzy",
|
||||
ExtraData: []*internal.IndexerData{
|
||||
|
@ -139,9 +153,8 @@ var cases = []*testIndexerCase{
|
|||
{ID: 1002, Comments: []string{"hi", "hello world"}},
|
||||
},
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Keyword: "hello world",
|
||||
SortBy: internal.SortByCreatedDesc,
|
||||
IsFuzzyKeyword: true,
|
||||
Keyword: "hello world",
|
||||
SortBy: internal.SortByCreatedDesc,
|
||||
},
|
||||
ExpectedIDs: []int64{1002, 1001, 1000},
|
||||
ExpectedTotal: 3,
|
||||
|
|
|
@ -232,20 +232,36 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
limit = 1
|
||||
}
|
||||
|
||||
keyword := options.Keyword
|
||||
if !options.IsFuzzyKeyword {
|
||||
// to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s)
|
||||
// https://www.meilisearch.com/docs/reference/api/search#phrase-search
|
||||
keyword = doubleQuoteKeyword(keyword)
|
||||
var keywords []string
|
||||
if options.Keyword != "" {
|
||||
tokens, err := options.Tokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, token := range tokens {
|
||||
if !token.Fuzzy {
|
||||
// to make it a phrase search, we have to quote the keyword(s)
|
||||
// https://www.meilisearch.com/docs/reference/api/search#phrase-search
|
||||
token.Term = doubleQuoteKeyword(token.Term)
|
||||
}
|
||||
|
||||
// internal.BoolOptShould (Default, requires no modifications)
|
||||
// internal.BoolOptMust (Not supported by meilisearch)
|
||||
if token.Kind == internal.BoolOptNot {
|
||||
token.Term = "-" + token.Term
|
||||
}
|
||||
keywords = append(keywords, token.Term)
|
||||
}
|
||||
}
|
||||
|
||||
searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(keyword, &meilisearch.SearchRequest{
|
||||
Filter: query.Statement(),
|
||||
Limit: int64(limit),
|
||||
Offset: int64(skip),
|
||||
Sort: sortBy,
|
||||
MatchingStrategy: meilisearch.All,
|
||||
})
|
||||
searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).
|
||||
Search(strings.Join(keywords, " "), &meilisearch.SearchRequest{
|
||||
Filter: query.Statement(),
|
||||
Limit: int64(limit),
|
||||
Offset: int64(skip),
|
||||
Sort: sortBy,
|
||||
MatchingStrategy: meilisearch.All,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -204,8 +204,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
keyword = ""
|
||||
}
|
||||
|
||||
isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
|
||||
|
||||
var mileIDs []int64
|
||||
if milestoneID > 0 || milestoneID == db.NoConditionID { // -1 to get those issues which have no any milestone assigned
|
||||
mileIDs = []int64{milestoneID}
|
||||
|
@ -226,7 +224,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
IssueIDs: nil,
|
||||
}
|
||||
if keyword != "" {
|
||||
allIssueIDs, err := issueIDsFromSearch(ctx, keyword, isFuzzy, statsOpts)
|
||||
allIssueIDs, err := issueIDsFromSearch(ctx, keyword, statsOpts)
|
||||
if err != nil {
|
||||
if issue_indexer.IsAvailable(ctx) {
|
||||
ctx.ServerError("issueIDsFromSearch", err)
|
||||
|
@ -294,7 +292,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
|
||||
var issues issues_model.IssueList
|
||||
{
|
||||
ids, err := issueIDsFromSearch(ctx, keyword, isFuzzy, &issues_model.IssuesOptions{
|
||||
ids, err := issueIDsFromSearch(ctx, keyword, &issues_model.IssuesOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
Page: pager.Paginater.Current(),
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
|
@ -458,16 +456,16 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
ctx.Data["OpenCount"] = issueStats.OpenCount
|
||||
ctx.Data["ClosedCount"] = issueStats.ClosedCount
|
||||
ctx.Data["AllCount"] = issueStats.AllCount
|
||||
linkStr := "?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&fuzzy=%t&archived=%t"
|
||||
linkStr := "?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&archived=%t"
|
||||
ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr,
|
||||
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "all", url.QueryEscape(selectLabels),
|
||||
milestoneID, projectID, assigneeID, posterID, isFuzzy, archived)
|
||||
milestoneID, projectID, assigneeID, posterID, archived)
|
||||
ctx.Data["OpenLink"] = fmt.Sprintf(linkStr,
|
||||
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "open", url.QueryEscape(selectLabels),
|
||||
milestoneID, projectID, assigneeID, posterID, isFuzzy, archived)
|
||||
milestoneID, projectID, assigneeID, posterID, archived)
|
||||
ctx.Data["ClosedLink"] = fmt.Sprintf(linkStr,
|
||||
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "closed", url.QueryEscape(selectLabels),
|
||||
milestoneID, projectID, assigneeID, posterID, isFuzzy, archived)
|
||||
milestoneID, projectID, assigneeID, posterID, archived)
|
||||
ctx.Data["SelLabelIDs"] = labelIDs
|
||||
ctx.Data["SelectLabels"] = selectLabels
|
||||
ctx.Data["ViewType"] = viewType
|
||||
|
@ -476,7 +474,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
ctx.Data["ProjectID"] = projectID
|
||||
ctx.Data["AssigneeID"] = assigneeID
|
||||
ctx.Data["PosterID"] = posterID
|
||||
ctx.Data["IsFuzzy"] = isFuzzy
|
||||
ctx.Data["Keyword"] = keyword
|
||||
ctx.Data["IsShowClosed"] = isShowClosed
|
||||
switch {
|
||||
|
@ -499,17 +496,12 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
pager.AddParam(ctx, "assignee", "AssigneeID")
|
||||
pager.AddParam(ctx, "poster", "PosterID")
|
||||
pager.AddParam(ctx, "archived", "ShowArchivedLabels")
|
||||
pager.AddParam(ctx, "fuzzy", "IsFuzzy")
|
||||
|
||||
ctx.Data["Page"] = pager
|
||||
}
|
||||
|
||||
func issueIDsFromSearch(ctx *context.Context, keyword string, fuzzy bool, opts *issues_model.IssuesOptions) ([]int64, error) {
|
||||
ids, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
|
||||
func(o *issue_indexer.SearchOptions) {
|
||||
o.IsFuzzyKeyword = fuzzy
|
||||
},
|
||||
))
|
||||
func issueIDsFromSearch(ctx *context.Context, keyword string, opts *issues_model.IssuesOptions) ([]int64, error) {
|
||||
ids, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SearchIssues: %w", err)
|
||||
}
|
||||
|
|
|
@ -463,8 +463,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
|||
User: ctx.Doer,
|
||||
}
|
||||
|
||||
isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(true)
|
||||
|
||||
// Search all repositories which
|
||||
//
|
||||
// As user:
|
||||
|
@ -594,9 +592,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
|||
// USING FINAL STATE OF opts FOR A QUERY.
|
||||
var issues issues_model.IssueList
|
||||
{
|
||||
issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
|
||||
func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
|
||||
))
|
||||
issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
|
||||
if err != nil {
|
||||
ctx.ServerError("issueIDsFromSearch", err)
|
||||
return
|
||||
|
@ -622,9 +618,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
|||
// -------------------------------
|
||||
// Fill stats to post to ctx.Data.
|
||||
// -------------------------------
|
||||
issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts).Copy(
|
||||
func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
|
||||
))
|
||||
issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts))
|
||||
if err != nil {
|
||||
ctx.ServerError("getUserIssueStats", err)
|
||||
return
|
||||
|
@ -679,7 +673,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
|||
ctx.Data["IsShowClosed"] = isShowClosed
|
||||
ctx.Data["SelectLabels"] = selectedLabels
|
||||
ctx.Data["PageIsOrgIssues"] = org != nil
|
||||
ctx.Data["IsFuzzy"] = isFuzzy
|
||||
|
||||
if isShowClosed {
|
||||
ctx.Data["State"] = "closed"
|
||||
|
@ -695,7 +688,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
|||
pager.AddParam(ctx, "labels", "SelectLabels")
|
||||
pager.AddParam(ctx, "milestone", "MilestoneID")
|
||||
pager.AddParam(ctx, "assignee", "AssigneeID")
|
||||
pager.AddParam(ctx, "fuzzy", "IsFuzzy")
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
ctx.HTML(http.StatusOK, tplIssues)
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_milestone"}}">
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<a rel="nofollow" class="{{if not $.MilestoneID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=0&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_all"}}</a>
|
||||
<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID -1}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=-1&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_none"}}</a>
|
||||
<a rel="nofollow" class="{{if not $.MilestoneID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=0&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_all"}}</a>
|
||||
<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID -1}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=-1&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_none"}}</a>
|
||||
{{if .OpenMilestones}}
|
||||
<div class="divider"></div>
|
||||
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_open"}}</div>
|
||||
{{range .OpenMilestones}}
|
||||
<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
{{svg "octicon-milestone" 16 "mr-2"}}
|
||||
{{.Name}}
|
||||
</a>
|
||||
|
@ -30,7 +30,7 @@
|
|||
<div class="divider"></div>
|
||||
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_closed"}}</div>
|
||||
{{range .ClosedMilestones}}
|
||||
<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
{{svg "octicon-milestone" 16 "mr-2"}}
|
||||
{{.Name}}
|
||||
</a>
|
||||
|
@ -51,15 +51,15 @@
|
|||
<i class="icon">{{svg "octicon-search" 16}}</i>
|
||||
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_project"}}">
|
||||
</div>
|
||||
<a rel="nofollow" class="{{if not .ProjectID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project=&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_all"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ProjectID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_none"}}</a>
|
||||
<a rel="nofollow" class="{{if not .ProjectID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project=&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_all"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ProjectID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_none"}}</a>
|
||||
{{if .OpenProjects}}
|
||||
<div class="divider"></div>
|
||||
<div class="header">
|
||||
{{ctx.Locale.Tr "repo.issues.new.open_projects"}}
|
||||
</div>
|
||||
{{range .OpenProjects}}
|
||||
<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item tw-flex" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item tw-flex" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
{{svg .IconName 18 "tw-mr-2 tw-shrink-0"}}<span class="gt-ellipsis">{{.Title}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -70,7 +70,7 @@
|
|||
{{ctx.Locale.Tr "repo.issues.new.closed_projects"}}
|
||||
</div>
|
||||
{{range .ClosedProjects}}
|
||||
<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
{{svg .IconName 18 "tw-mr-2"}}{{.Title}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -82,7 +82,7 @@
|
|||
<div class="list-header-author ui dropdown jump item user-remote-search" data-tooltip-content="{{ctx.Locale.Tr "repo.author_search_tooltip"}}"
|
||||
data-search-url="{{if .Milestone}}{{$.RepoLink}}/issues/posters{{else}}{{$.Link}}/posters{{end}}"
|
||||
data-selected-user-id="{{$.PosterID}}"
|
||||
data-action-jump-url="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&fuzzy={{$.IsFuzzy}}&poster={user_id}{{if $.ShowArchivedLabels}}&archived=true{{end}}"
|
||||
data-action-jump-url="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={user_id}{{if $.ShowArchivedLabels}}&archived=true{{end}}"
|
||||
>
|
||||
<span class="text">
|
||||
{{ctx.Locale.Tr "repo.issues.filter_poster"}}
|
||||
|
@ -108,11 +108,11 @@
|
|||
<i class="icon">{{svg "octicon-search" 16}}</i>
|
||||
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_assignee"}}">
|
||||
</div>
|
||||
<a rel="nofollow" class="{{if not .AssigneeID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .AssigneeID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=-1&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee"}}</a>
|
||||
<a rel="nofollow" class="{{if not .AssigneeID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .AssigneeID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=-1&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee"}}</a>
|
||||
<div class="divider"></div>
|
||||
{{range .Assignees}}
|
||||
<a rel="nofollow" class="{{if eq $.AssigneeID .ID}}active selected{{end}} item tw-flex" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
<a rel="nofollow" class="{{if eq $.AssigneeID .ID}}active selected{{end}} item tw-flex" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
|
||||
{{ctx.AvatarUtils.Avatar . 20}}{{template "repo/search_name" .}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -127,14 +127,14 @@
|
|||
</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
<a rel="nofollow" class="{{if eq .ViewType "all"}}active {{end}}item" href="?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.all_issues"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "assigned"}}active {{end}}item" href="?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "created_by"}}active {{end}}item" href="?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "all"}}active {{end}}item" href="?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.all_issues"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "assigned"}}active {{end}}item" href="?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "created_by"}}active {{end}}item" href="?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
|
||||
{{if .PageIsPullList}}
|
||||
<a rel="nofollow" class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
|
||||
{{end}}
|
||||
<a rel="nofollow" class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
|
||||
<a rel="nofollow" class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -146,11 +146,11 @@
|
|||
</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
<a rel="nofollow" class="{{if or (eq .SortType "relevance") (not .SortType)}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=relevency&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.relevance"}}</a>
|
||||
<a rel="nofollow" class="{{if or (eq .SortType "relevance") (not .SortType)}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=relevency&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.relevance"}}</a>
|
||||
{{$o := .}}
|
||||
{{range $opt := StringUtils.Make "latest" "oldest" "recentupdate" "leastupdate" "mostcomment" "leastcomment" "nearduedate" "farduedate"}}
|
||||
{{$text := ctx.Locale.Tr (printf "repo.issues.filter_sort.%s" $opt)}}
|
||||
<a rel="nofollow" class="{{if eq $o.SortType $opt}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$opt}}&state={{$.State}}&labels={{$o.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{$text}}</a>
|
||||
<a rel="nofollow" class="{{if eq $o.SortType $opt}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$opt}}&state={{$.State}}&labels={{$o.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{$text}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
<input type="hidden" name="poster" value="{{$.PosterID}}">
|
||||
{{end}}
|
||||
{{if .PageIsPullList}}
|
||||
{{template "shared/search/combo_fuzzy" dict "Value" .Keyword "IsFuzzy" .IsFuzzy "Placeholder" (ctx.Locale.Tr "search.pull_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.pull_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{else if .PageIsMilestones}}
|
||||
{{template "shared/search/combo_fuzzy" dict "Value" .Keyword "IsFuzzy" .IsFuzzy "Placeholder" (ctx.Locale.Tr "search.milestone_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.milestone_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{else}}
|
||||
{{template "shared/search/combo_fuzzy" dict "Value" .Keyword "IsFuzzy" .IsFuzzy "Placeholder" (ctx.Locale.Tr "search.issue_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.issue_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{end}}
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
{{end}}
|
||||
<span class="labels-list tw-ml-1">
|
||||
{{range .Labels}}
|
||||
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" rel="nofollow">{{RenderLabel $.Context ctx.Locale .}}</a>
|
||||
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" rel="nofollow">{{RenderLabel $.Context ctx.Locale .}}</a>
|
||||
{{end}}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
</div>
|
||||
<span class="info">{{ctx.Locale.Tr "repo.issues.filter_label_exclude"}}</span>
|
||||
<div class="divider"></div>
|
||||
<a rel="nofollow" class="{{if .AllLabels}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_no_select"}}</a>
|
||||
<a rel="nofollow" class="{{if .NoLabel}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
|
||||
<a rel="nofollow" class="{{if .AllLabels}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_no_select"}}</a>
|
||||
<a rel="nofollow" class="{{if .NoLabel}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
|
||||
{{$previousExclusiveScope := "_no_scope"}}
|
||||
{{range .Labels}}
|
||||
{{$exclusiveScope := .ExclusiveScope}}
|
||||
|
@ -32,7 +32,7 @@
|
|||
<div class="divider"></div>
|
||||
{{end}}
|
||||
{{$previousExclusiveScope = $exclusiveScope}}
|
||||
<a rel="nofollow" class="item label-filter-item tw-flex tw-items-center" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&labels={{.QueryString}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
|
||||
<a rel="nofollow" class="item label-filter-item tw-flex tw-items-center" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&labels={{.QueryString}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
|
||||
{{if .IsExcluded}}
|
||||
{{svg "octicon-circle-slash"}}
|
||||
{{else if .IsSelected}}
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
{{/* Placeholder (optional) - placeholder text to be used */}}
|
||||
{{/* Tooltip (optional) - a tooltip to be displayed on button hover */}}
|
||||
<div class="ui small fluid action input">
|
||||
{{template "shared/search/input" dict "Value" .Value "Disabled" .Disabled "Placeholder" .Placeholder}}
|
||||
{{template "shared/search/input"
|
||||
dict
|
||||
"Value" .Value
|
||||
"Disabled" .Disabled
|
||||
"Placeholder" .Placeholder}}
|
||||
{{template "shared/search/button" dict "Disabled" .Disabled "Tooltip" .Tooltip}}
|
||||
</div>
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
{{/* Value - value of the search field (for search results page) */}}
|
||||
{{/* Disabled (optional) - if search field/button has to be disabled */}}
|
||||
{{/* Placeholder (optional) - placeholder text to be used */}}
|
||||
{{/* IsFuzzy - state of the fuzzy/union search toggle */}}
|
||||
{{/* Tooltip (optional) - a tooltip to be displayed on button hover */}}
|
||||
<div class="ui small fluid action input">
|
||||
{{template "shared/search/input" dict "Value" .Value "Disabled" .Disabled "Placeholder" .Placeholder}}
|
||||
{{template "shared/search/fuzzy"
|
||||
dict
|
||||
"Disabled" .Disabled
|
||||
"IsFuzzy" .IsFuzzy}}
|
||||
{{template "shared/search/button" dict "Disabled" .Disabled "Tooltip" .Tooltip}}
|
||||
</div>
|
|
@ -1,15 +0,0 @@
|
|||
{{/* Disabled (optional) - if dropdown has to be disabled */}}
|
||||
{{/* IsFuzzy - state of the fuzzy search toggle */}}
|
||||
<div class="ui small dropdown selection {{if .Disabled}} disabled{{end}}" data-tooltip-content="{{ctx.Locale.Tr "search.type_tooltip"}}">
|
||||
<input name="fuzzy" type="hidden"{{if .Disabled}} disabled{{end}} value="{{.IsFuzzy}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="text">{{if .IsFuzzy}}{{/*
|
||||
*/}}{{ctx.Locale.Tr "search.fuzzy"}}{{/*
|
||||
*/}}{{else}}{{/*
|
||||
*/}}{{ctx.Locale.Tr "search.exact"}}{{/*
|
||||
*/}}{{end}}</div>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="true" data-tooltip-content="{{ctx.Locale.Tr "search.fuzzy_tooltip"}}">{{/*
|
||||
*/}}{{ctx.Locale.Tr "search.fuzzy"}}</div>
|
||||
<div class="item" data-value="false" data-tooltip-content="{{ctx.Locale.Tr "search.exact_tooltip"}}">{{ctx.Locale.Tr "search.exact"}}</div>
|
||||
</div>
|
||||
</div>
|
|
@ -5,11 +5,11 @@
|
|||
{{template "base/alert" .}}
|
||||
<div class="list-header">
|
||||
<div class="switch list-header-toggle">
|
||||
<a class="item{{if not .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="item{{if not .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&q={{$.Keyword}}">
|
||||
{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
|
||||
{{ctx.Locale.PrettyNumber .IssueStats.OpenCount}} {{ctx.Locale.Tr "repo.issues.open_title"}}
|
||||
</a>
|
||||
<a class="item{{if .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="item{{if .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&labels={{.SelectLabels}}&q={{$.Keyword}}">
|
||||
{{svg "octicon-issue-closed" 16 "tw-mr-2"}}
|
||||
{{ctx.Locale.PrettyNumber .IssueStats.ClosedCount}} {{ctx.Locale.Tr "repo.issues.closed_title"}}
|
||||
</a>
|
||||
|
@ -20,9 +20,9 @@
|
|||
<input type="hidden" name="sort" value="{{$.SortType}}">
|
||||
<input type="hidden" name="state" value="{{$.State}}">
|
||||
{{if .PageIsPulls}}
|
||||
{{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr "search.pull_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{template "shared/search/combo" dict "Value" $.Keyword "Placeholder" (ctx.Locale.Tr "search.pull_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{else}}
|
||||
{{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr "search.issue_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{template "shared/search/combo" dict "Value" $.Keyword "Placeholder" (ctx.Locale.Tr "search.issue_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
|
||||
{{end}}
|
||||
</div>
|
||||
</form>
|
||||
|
@ -38,29 +38,29 @@
|
|||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</span>
|
||||
<div class="ui menu">
|
||||
<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
|
||||
<div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.CreateCount}}</div>
|
||||
{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}
|
||||
</a>
|
||||
<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
|
||||
<div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.YourRepositoriesCount}}</div>
|
||||
{{ctx.Locale.Tr "home.issues.in_your_repos"}}
|
||||
</a>
|
||||
<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
|
||||
<div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.AssignCount}}</div>
|
||||
{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}
|
||||
</a>
|
||||
{{if .PageIsPulls}}
|
||||
<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
|
||||
<div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.ReviewRequestedCount}}</div>
|
||||
{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}
|
||||
</a>
|
||||
<a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
|
||||
<div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.ReviewedCount}}</div>
|
||||
{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
|
||||
<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
|
||||
<div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.MentionCount}}</div>
|
||||
{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}
|
||||
</a>
|
||||
|
@ -73,14 +73,13 @@
|
|||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</span>
|
||||
<div class="menu">
|
||||
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
|
||||
<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
|
||||
<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
|
||||
<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
|
||||
{{$o := .}}
|
||||
{{range $opt := StringUtils.Make "recentupdate" "leastupdate" "latest" "oldest" "mostcomment" "leastcomment" "nearduedate" "farduedate"}}
|
||||
{{$text := ctx.Locale.Tr (printf "repo.issues.filter_sort.%s" $opt)}}
|
||||
<a class="{{if or (eq $o.SortType $opt) (and (eq $opt "latest") (not $o.SortType))}}active {{end}}item" href="?type={{$.ViewType}}&sort={{$opt}}&state={{$.State}}&labels={{$o.SelectLabels}}&q={{$.Keyword}}">{{
|
||||
$text
|
||||
}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -138,27 +138,25 @@ func TestViewIssuesKeyword(t *testing.T) {
|
|||
})
|
||||
|
||||
// keyword: 'firstt'
|
||||
// should not match when fuzzy searching is disabled
|
||||
req = NewRequestf(t, "GET", "%s/issues?q=%st&fuzzy=false", repo.Link(), keyword)
|
||||
// should not match when using phrase search
|
||||
req = NewRequestf(t, "GET", "%s/issues?q=\"%st\"", repo.Link(), keyword)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||
issuesSelection = getIssuesSelection(t, htmlDoc)
|
||||
assert.EqualValues(t, 0, issuesSelection.Length())
|
||||
|
||||
// should match as 'first' when fuzzy seaeching is enabled
|
||||
for _, fmt := range []string{"%s/issues?q=%st&fuzzy=true", "%s/issues?q=%st"} {
|
||||
req = NewRequestf(t, "GET", fmt, repo.Link(), keyword)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||
issuesSelection = getIssuesSelection(t, htmlDoc)
|
||||
assert.EqualValues(t, 1, issuesSelection.Length())
|
||||
issuesSelection.Each(func(_ int, selection *goquery.Selection) {
|
||||
issue := getIssue(t, repo.ID, selection)
|
||||
assert.False(t, issue.IsClosed)
|
||||
assert.False(t, issue.IsPull)
|
||||
assertMatch(t, issue, keyword)
|
||||
})
|
||||
}
|
||||
// should match as 'first' when using a standard query
|
||||
req = NewRequestf(t, "GET", "%s/issues?q=%st", repo.Link(), keyword)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||
issuesSelection = getIssuesSelection(t, htmlDoc)
|
||||
assert.EqualValues(t, 1, issuesSelection.Length())
|
||||
issuesSelection.Each(func(_ int, selection *goquery.Selection) {
|
||||
issue := getIssue(t, repo.ID, selection)
|
||||
assert.False(t, issue.IsClosed)
|
||||
assert.False(t, issue.IsPull)
|
||||
assertMatch(t, issue, keyword)
|
||||
})
|
||||
}
|
||||
|
||||
func TestViewIssuesSearchOptions(t *testing.T) {
|
||||
|
|
|
@ -1120,7 +1120,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1145,32 +1144,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
||||
t.Run("Fuzzy", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo1/issues?fuzzy=false")
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
called := false
|
||||
htmlDoc.Find("#issue-filters a[href^='?']").Each(func(_ int, s *goquery.Selection) {
|
||||
called = true
|
||||
href, _ := s.Attr("href")
|
||||
assert.Contains(t, href, "?q=&")
|
||||
assert.Contains(t, href, "&type=")
|
||||
assert.Contains(t, href, "&sort=")
|
||||
assert.Contains(t, href, "&state=")
|
||||
assert.Contains(t, href, "&labels=")
|
||||
assert.Contains(t, href, "&milestone=")
|
||||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=false")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1195,7 +1168,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1220,7 +1192,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1245,7 +1216,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1270,7 +1240,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1295,7 +1264,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1320,7 +1288,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=1")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1345,7 +1312,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=1")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1370,7 +1336,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=1")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1395,7 +1360,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
})
|
||||
assert.True(t, called)
|
||||
})
|
||||
|
@ -1420,7 +1384,6 @@ func TestRepoIssueFilterLinks(t *testing.T) {
|
|||
assert.Contains(t, href, "&project=")
|
||||
assert.Contains(t, href, "&assignee=")
|
||||
assert.Contains(t, href, "&poster=")
|
||||
assert.Contains(t, href, "&fuzzy=")
|
||||
assert.Contains(t, href, "&archived=true")
|
||||
})
|
||||
assert.True(t, called)
|
||||
|
|
Loading…
Add table
Reference in a new issue