mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-05-17 23:00:02 +02:00
Sync with Forgejo
This commit is contained in:
commit
cf868695f5
50 changed files with 762 additions and 254 deletions
|
@ -164,7 +164,7 @@ jobs:
|
|||
|
||||
- name: build container & release
|
||||
if: ${{ secrets.TOKEN != '' }}
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.1
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4
|
||||
with:
|
||||
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||
|
@ -183,7 +183,7 @@ jobs:
|
|||
|
||||
- name: build rootless container
|
||||
if: ${{ secrets.TOKEN != '' }}
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.1
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4
|
||||
with:
|
||||
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||
|
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
- name: copy & sign
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.1
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.4
|
||||
with:
|
||||
from-forgejo: ${{ vars.FORGEJO }}
|
||||
to-forgejo: ${{ vars.FORGEJO }}
|
||||
|
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
|
||||
runs-on: docker
|
||||
container:
|
||||
image: data.forgejo.org/renovate/renovate:39.171.2
|
||||
image: data.forgejo.org/renovate/renovate:39.178.1
|
||||
|
||||
steps:
|
||||
- name: Load renovate repo cache
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
|
||||
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.21 AS build-env
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
|
||||
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.21 AS build-env
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
|
@ -50,6 +50,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
|||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||
|
||||
FROM data.forgejo.org/oci/alpine:3.21
|
||||
ARG RELEASE_VERSION
|
||||
LABEL maintainer="contact@forgejo.org" \
|
||||
org.opencontainers.image.authors="Forgejo" \
|
||||
org.opencontainers.image.url="https://forgejo.org" \
|
||||
|
|
4
Makefile
4
Makefile
|
@ -48,8 +48,8 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour
|
|||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
|
||||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.30.0 # renovate: datasource=go
|
||||
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go
|
||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.0 # renovate: datasource=go
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@39.171.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@39.178.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
|
||||
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
|
||||
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
|
||||
|
|
10
go.mod
10
go.mod
|
@ -1,8 +1,8 @@
|
|||
module code.gitea.io/gitea
|
||||
|
||||
go 1.23
|
||||
go 1.24
|
||||
|
||||
toolchain go1.23.5
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
code.forgejo.org/f3/gof3/v3 v3.10.2
|
||||
|
@ -84,7 +84,7 @@ require (
|
|||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/prometheus/client_golang v1.21.0
|
||||
github.com/redis/go-redis/v9 v9.7.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||
|
@ -101,7 +101,7 @@ require (
|
|||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go v0.119.0
|
||||
go.uber.org/mock v0.4.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/crypto v0.35.0
|
||||
golang.org/x/image v0.23.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
|
@ -239,7 +239,7 @@ require (
|
|||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rhysd/actionlint v1.6.27 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
|
|
12
go.sum
12
go.sum
|
@ -1331,15 +1331,15 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
|||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
|
@ -1507,8 +1507,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
|
1
models/fixtures/secret.yml
Normal file
1
models/fixtures/secret.yml
Normal file
|
@ -0,0 +1 @@
|
|||
[] # empty
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
@ -145,6 +147,20 @@ func LoadServiceSetting() {
|
|||
loadServiceFrom(CfgProvider)
|
||||
}
|
||||
|
||||
func appURLAsGlob(fqdn string) (glob.Glob, error) {
|
||||
localFqdn, err := url.ParseRequestURI(fqdn)
|
||||
if err != nil {
|
||||
log.Error("Error in EmailDomainAllowList: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
appFqdn, err := glob.Compile(localFqdn.Hostname(), ',')
|
||||
if err != nil {
|
||||
log.Error("Error in EmailDomainAllowList: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return appFqdn, nil
|
||||
}
|
||||
|
||||
func loadServiceFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("service")
|
||||
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
|
||||
|
@ -164,7 +180,15 @@ func loadServiceFrom(rootCfg ConfigProvider) {
|
|||
if sec.HasKey("EMAIL_DOMAIN_WHITELIST") {
|
||||
deprecatedSetting(rootCfg, "service", "EMAIL_DOMAIN_WHITELIST", "service", "EMAIL_DOMAIN_ALLOWLIST", "1.21")
|
||||
}
|
||||
Service.EmailDomainAllowList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_WHITELIST", "EMAIL_DOMAIN_ALLOWLIST")
|
||||
emailDomainAllowList := CompileEmailGlobList(sec, "EMAIL_DOMAIN_WHITELIST", "EMAIL_DOMAIN_ALLOWLIST")
|
||||
|
||||
if len(emailDomainAllowList) > 0 && Federation.Enabled {
|
||||
appURL, err := appURLAsGlob(AppURL)
|
||||
if err == nil {
|
||||
emailDomainAllowList = append(emailDomainAllowList, appURL)
|
||||
}
|
||||
}
|
||||
Service.EmailDomainAllowList = emailDomainAllowList
|
||||
Service.EmailDomainBlockList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_BLOCKLIST")
|
||||
Service.EmailDomainBlockDisposable = sec.Key("EMAIL_DOMAIN_BLOCK_DISPOSABLE").MustBool(false)
|
||||
if Service.EmailDomainBlockDisposable {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
@ -210,6 +211,7 @@ func LoadSettings() {
|
|||
initAllLoggers()
|
||||
|
||||
loadDBSetting(CfgProvider)
|
||||
loadFederationFrom(CfgProvider)
|
||||
loadServiceFrom(CfgProvider)
|
||||
loadOAuth2ClientFrom(CfgProvider)
|
||||
loadCacheFrom(CfgProvider)
|
||||
|
@ -224,7 +226,6 @@ func LoadSettings() {
|
|||
LoadQueueSettings()
|
||||
loadProjectFrom(CfgProvider)
|
||||
loadMimeTypeMapFrom(CfgProvider)
|
||||
loadFederationFrom(CfgProvider)
|
||||
loadF3From(CfgProvider)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMakeAbsoluteAssetURL(t *testing.T) {
|
||||
|
@ -30,3 +32,84 @@ func TestMakeManifestData(t *testing.T) {
|
|||
jsonBytes := MakeManifestData(`Example App '\"`, "https://example.com", "https://example.com/foo/bar")
|
||||
assert.True(t, json.Valid(jsonBytes))
|
||||
}
|
||||
|
||||
func TestLoadServiceDomainListsForFederation(t *testing.T) {
|
||||
oldAppURL := AppURL
|
||||
oldFederation := Federation
|
||||
oldService := Service
|
||||
|
||||
defer func() {
|
||||
AppURL = oldAppURL
|
||||
Federation = oldFederation
|
||||
Service = oldService
|
||||
}()
|
||||
|
||||
cfg, err := NewConfigProviderFromData(`
|
||||
[federation]
|
||||
ENABLED = true
|
||||
[service]
|
||||
EMAIL_DOMAIN_ALLOWLIST = *.allow.random
|
||||
EMAIL_DOMAIN_BLOCKLIST = *.block.random
|
||||
`)
|
||||
|
||||
require.NoError(t, err)
|
||||
loadServerFrom(cfg)
|
||||
loadFederationFrom(cfg)
|
||||
loadServiceFrom(cfg)
|
||||
|
||||
assert.True(t, match(Service.EmailDomainAllowList, "d1.allow.random"))
|
||||
assert.True(t, match(Service.EmailDomainAllowList, "localhost"))
|
||||
}
|
||||
|
||||
func TestLoadServiceDomainListsNoFederation(t *testing.T) {
|
||||
oldAppURL := AppURL
|
||||
oldFederation := Federation
|
||||
oldService := Service
|
||||
|
||||
defer func() {
|
||||
AppURL = oldAppURL
|
||||
Federation = oldFederation
|
||||
Service = oldService
|
||||
}()
|
||||
|
||||
cfg, err := NewConfigProviderFromData(`
|
||||
[federation]
|
||||
ENABLED = false
|
||||
[service]
|
||||
EMAIL_DOMAIN_ALLOWLIST = *.allow.random
|
||||
EMAIL_DOMAIN_BLOCKLIST = *.block.random
|
||||
`)
|
||||
|
||||
require.NoError(t, err)
|
||||
loadServerFrom(cfg)
|
||||
loadFederationFrom(cfg)
|
||||
loadServiceFrom(cfg)
|
||||
|
||||
assert.True(t, match(Service.EmailDomainAllowList, "d1.allow.random"))
|
||||
}
|
||||
|
||||
func TestLoadServiceDomainListsFederationEmptyAllowList(t *testing.T) {
|
||||
oldAppURL := AppURL
|
||||
oldFederation := Federation
|
||||
oldService := Service
|
||||
|
||||
defer func() {
|
||||
AppURL = oldAppURL
|
||||
Federation = oldFederation
|
||||
Service = oldService
|
||||
}()
|
||||
|
||||
cfg, err := NewConfigProviderFromData(`
|
||||
[federation]
|
||||
ENABLED = true
|
||||
[service]
|
||||
EMAIL_DOMAIN_BLOCKLIST = *.block.random
|
||||
`)
|
||||
|
||||
require.NoError(t, err)
|
||||
loadServerFrom(cfg)
|
||||
loadFederationFrom(cfg)
|
||||
loadServiceFrom(cfg)
|
||||
|
||||
assert.Empty(t, Service.EmailDomainAllowList)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2024 The Forgejo Authors. All rights reserved
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package validation
|
||||
|
@ -100,11 +101,25 @@ func validateEmailDomain(email string) error {
|
|||
}
|
||||
|
||||
func IsEmailDomainAllowed(email string) bool {
|
||||
if len(setting.Service.EmailDomainAllowList) == 0 {
|
||||
return !isEmailDomainListed(setting.Service.EmailDomainBlockList, email)
|
||||
}
|
||||
return isEmailDomainAllowedInternal(
|
||||
email,
|
||||
setting.Service.EmailDomainAllowList,
|
||||
setting.Service.EmailDomainBlockList)
|
||||
}
|
||||
|
||||
return isEmailDomainListed(setting.Service.EmailDomainAllowList, email)
|
||||
func isEmailDomainAllowedInternal(
|
||||
email string,
|
||||
emailDomainAllowList []glob.Glob,
|
||||
emailDomainBlockList []glob.Glob,
|
||||
) bool {
|
||||
var result bool
|
||||
|
||||
if len(emailDomainAllowList) == 0 {
|
||||
result = !isEmailDomainListed(emailDomainBlockList, email)
|
||||
} else {
|
||||
result = isEmailDomainListed(emailDomainAllowList, email)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isEmailDomainListed checks whether the domain of an email address
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2024 The Forgejo Authors. All rights reserved
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package validation
|
||||
|
@ -65,3 +66,8 @@ func TestEmailAddressValidate(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailDomainAllowList(t *testing.T) {
|
||||
res := IsEmailDomainAllowed("someuser@localhost.localdomain")
|
||||
assert.True(t, res)
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ preview = Preview
|
|||
loading = Loading…
|
||||
|
||||
error = Error
|
||||
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
|
||||
error404 = The page you are trying to reach either <strong>does not exist</strong>, <strong>has been removed</strong> or <strong>you are not authorized</strong> to view it.
|
||||
error413 = You have exhausted your quota.
|
||||
go_back = Go Back
|
||||
invalid_data = Invalid data: %v
|
||||
|
@ -1190,8 +1190,8 @@ template.issue_labels = Issue labels
|
|||
template.one_item = Must select at least one template item
|
||||
template.invalid = Must select a template repository
|
||||
|
||||
archive.title = This repository is archived. You can view files and clone it, but you cannot make any changes to the state of this repository, such as pushing and creating new issues, pull requests or comments.
|
||||
archive.title_date = This repository has been archived on %s. You can view files and clone it, but you cannot make any changes to the state of this repository, such as pushing and creating new issues, pull requests or comments.
|
||||
archive.title = This repository is archived. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
|
||||
archive.title_date = This repository has been archived on %s. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
|
||||
archive.issue.nocomment = This repository is archived. You cannot comment on issues.
|
||||
archive.pull.nocomment = This repository is archived. You cannot comment on pull requests.
|
||||
archive.pull.noreview = This repository is archived. You cannot review pull requests.
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"home.welcome.no_activity": "No activity",
|
||||
"home.welcome.activity_hint": "There is nothing in your feed yet. Your actions and activity from repositories that you watch will show up here.",
|
||||
"home.explore_repos": "Explore repositories",
|
||||
"home.explore_users": "Explore users",
|
||||
"home.explore_orgs": "Explore organizations",
|
||||
"repo.pulls.merged_title_desc": {
|
||||
"one": "merged %[1]d commit from <code>%[2]s</code> into <code>%[3]s</code> %[4]s",
|
||||
"other": "merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s"
|
||||
|
|
|
@ -34,13 +34,6 @@ func GetRegistrationToken(ctx *context.APIContext, ownerID, repoID int64) {
|
|||
ctx.JSON(http.StatusOK, RegistrationToken{Token: token.Token})
|
||||
}
|
||||
|
||||
// RunJobList is a list of action run jobs
|
||||
// swagger:response RunJobList
|
||||
type RunJobList struct {
|
||||
// in:body
|
||||
Body []*structs.ActionRunJob `json:"body"`
|
||||
}
|
||||
|
||||
func GetActionRunJobs(ctx *context.APIContext, ownerID, repoID int64) {
|
||||
labels := strings.Split(ctx.FormTrim("labels"), ",")
|
||||
|
||||
|
@ -54,8 +47,7 @@ func GetActionRunJobs(ctx *context.APIContext, ownerID, repoID int64) {
|
|||
return
|
||||
}
|
||||
|
||||
res := new(RunJobList)
|
||||
res.Body = fromRunJobModelToResponse(total, labels)
|
||||
res := fromRunJobModelToResponse(total, labels)
|
||||
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
|
|
@ -32,3 +32,10 @@ type swaggerResponseVariableList struct {
|
|||
// in:body
|
||||
Body []api.ActionVariable `json:"body"`
|
||||
}
|
||||
|
||||
// RunJobList is a list of action run jobs
|
||||
// swagger:response RunJobList
|
||||
type swaggerRunJobList struct {
|
||||
// in:body
|
||||
Body []*api.ActionRunJob `json:"body"`
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -90,6 +90,8 @@ func Dashboard(ctx *context.Context) {
|
|||
cnt, _ := organization.GetOrganizationCount(ctx, ctxUser)
|
||||
ctx.Data["UserOrgsCount"] = cnt
|
||||
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
|
||||
ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage
|
||||
ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage
|
||||
ctx.Data["Date"] = date
|
||||
|
||||
var uid int64
|
||||
|
@ -463,8 +465,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 +594,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 +620,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 +675,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 +690,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)
|
||||
|
|
|
@ -141,9 +141,20 @@
|
|||
{{template "repo/cite/cite_modal" .}}
|
||||
{{end}}
|
||||
{{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
|
||||
<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
|
||||
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
|
||||
</a>
|
||||
<div class="button-row folder-actions">
|
||||
<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
|
||||
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
|
||||
</a>
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
<button class="ui jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
|
||||
{{svg "octicon-kebab-horizontal"}}
|
||||
<div class="menu">
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments (printf "%s:%s" $.RefName .TreePath)}}.zip" rel="nofollow" type="application/zip">{{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_zip"}}</a>
|
||||
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments (printf "%s:%s" $.RefName .TreePath)}}.tar.gz" rel="nofollow" type="application/gzip">{{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_tar"}}</a>
|
||||
</div>
|
||||
</button>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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,7 +5,11 @@
|
|||
<div class="flex-container-main">
|
||||
{{template "base/alert" .}}
|
||||
{{template "user/heatmap" .}}
|
||||
{{template "user/dashboard/feeds" .}}
|
||||
{{if .Feeds}}
|
||||
{{template "user/dashboard/feeds" .}}
|
||||
{{else}}
|
||||
{{template "user/dashboard/guide" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "user/dashboard/repolist" .}}
|
||||
</div>
|
||||
|
|
16
templates/user/dashboard/guide.tmpl
Normal file
16
templates/user/dashboard/guide.tmpl
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div id="empty-feed" class="tw-text-center tw-p-8">
|
||||
{{svg "octicon-inbox" 64 "tw-text-placeholder-text"}}
|
||||
<h2>{{ctx.Locale.Tr "home.welcome.no_activity"}}</h2>
|
||||
<p class="help">{{ctx.Locale.Tr "home.welcome.activity_hint"}}</p>
|
||||
<div>
|
||||
<a href="{{AppSubUrl}}/explore/repos">{{ctx.Locale.Tr "home.explore_repos"}}</a>
|
||||
{{if not .UsersPageIsDisabled}}
|
||||
<span>·</span>
|
||||
<a href="{{AppSubUrl}}/explore/users">{{ctx.Locale.Tr "home.explore_users"}}</a>
|
||||
{{end}}
|
||||
{{if not .OrganizationsPageIsDisabled}}
|
||||
<span>·</span>
|
||||
<a href="{{AppSubUrl}}/explore/organizations">{{ctx.Locale.Tr "home.explore_orgs"}}</a>
|
||||
{{end}}
|
||||
</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>
|
||||
|
|
|
@ -30,8 +30,7 @@ test('External Release Attachments', async ({page, isMobile}) => {
|
|||
await validate_form({page}, 'fieldset');
|
||||
const textarea = page.locator('input[name=tag_name]');
|
||||
await textarea.pressSequentially('2.0');
|
||||
await expect(page.locator('input[name=title]')).toHaveAttribute('placeholder', '2.0');
|
||||
await page.fill('input[name=title]', '2.0');
|
||||
await expect(page.locator('input[name=title]')).toHaveValue('2.0');
|
||||
await page.click('#add-external-link');
|
||||
await page.click('#add-external-link');
|
||||
await page.fill('input[name=attachment-new-name-2]', 'Test');
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -31,9 +31,9 @@ func TestAPISearchActionJobs_GlobalRunner(t *testing.T) {
|
|||
).AddTokenAuth(token)
|
||||
res := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var jobs shared.RunJobList
|
||||
var jobs []*api.ActionRunJob
|
||||
DecodeJSON(t, res, &jobs)
|
||||
|
||||
assert.Len(t, jobs.Body, 1)
|
||||
assert.EqualValues(t, job.ID, jobs.Body[0].ID)
|
||||
assert.Len(t, jobs, 1)
|
||||
assert.EqualValues(t, job.ID, jobs[0].ID)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -30,9 +30,9 @@ func TestAPISearchActionJobs_OrgRunner(t *testing.T) {
|
|||
AddTokenAuth(token)
|
||||
res := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var jobs shared.RunJobList
|
||||
var jobs []*api.ActionRunJob
|
||||
DecodeJSON(t, res, &jobs)
|
||||
|
||||
assert.Len(t, jobs.Body, 1)
|
||||
assert.EqualValues(t, job.ID, jobs.Body[0].ID)
|
||||
assert.Len(t, jobs, 1)
|
||||
assert.EqualValues(t, job.ID, jobs[0].ID)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -35,9 +35,9 @@ func TestAPISearchActionJobs_RepoRunner(t *testing.T) {
|
|||
).AddTokenAuth(token)
|
||||
res := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var jobs shared.RunJobList
|
||||
var jobs []*api.ActionRunJob
|
||||
DecodeJSON(t, res, &jobs)
|
||||
|
||||
assert.Len(t, jobs.Body, 1)
|
||||
assert.EqualValues(t, job.ID, jobs.Body[0].ID)
|
||||
assert.Len(t, jobs, 1)
|
||||
assert.EqualValues(t, job.ID, jobs[0].ID)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -30,9 +30,9 @@ func TestAPISearchActionJobs_UserRunner(t *testing.T) {
|
|||
AddTokenAuth(token)
|
||||
res := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var jobs shared.RunJobList
|
||||
var jobs []*api.ActionRunJob
|
||||
DecodeJSON(t, res, &jobs)
|
||||
|
||||
assert.Len(t, jobs.Body, 1)
|
||||
assert.EqualValues(t, job.ID, jobs.Body[0].ID)
|
||||
assert.Len(t, jobs, 1)
|
||||
assert.EqualValues(t, job.ID, jobs[0].ID)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/routers"
|
||||
|
@ -32,3 +40,48 @@ func TestRepoDownloadArchive(t *testing.T) {
|
|||
assert.Empty(t, resp.Header().Get("Content-Encoding"))
|
||||
assert.Len(t, bs, 320)
|
||||
}
|
||||
|
||||
func TestRepoDownloadArchiveSubdir(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
defer test.MockVariableValue(&setting.EnableGzip, true)()
|
||||
defer test.MockVariableValue(&web.GzipMinSize, 10)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
|
||||
// Create a subdirectory
|
||||
err := createOrReplaceFileInBranch(user, repo, "subdir/test.txt", "master", "Test")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Frontend", func(t *testing.T) {
|
||||
resp := MakeRequest(t, NewRequestf(t, "GET", "/%s/src/branch/master/subdir", repo.FullName()), http.StatusOK)
|
||||
page := NewHTMLParser(t, resp.Body)
|
||||
|
||||
page.AssertElement(t, fmt.Sprintf(".folder-actions a.archive-link[href='/%s/archive/master:subdir.zip'][type='application/zip']", repo.FullName()), true)
|
||||
page.AssertElement(t, fmt.Sprintf(".folder-actions a.archive-link[href='/%s/archive/master:subdir.tar.gz'][type='application/gzip']", repo.FullName()), true)
|
||||
})
|
||||
|
||||
t.Run("Backend", func(t *testing.T) {
|
||||
resp := MakeRequest(t, NewRequestf(t, "GET", "/%s/archive/master:subdir.tar.gz", repo.FullName()), http.StatusOK)
|
||||
|
||||
uncompressedStream, err := gzip.NewReader(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
tarReader := tar.NewReader(uncompressedStream)
|
||||
|
||||
header, err := tarReader.Next()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tar.TypeDir, int32(header.Typeflag))
|
||||
assert.Equal(t, fmt.Sprintf("%s/", repo.Name), header.Name)
|
||||
|
||||
header, err = tarReader.Next()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tar.TypeReg, int32(header.Typeflag))
|
||||
assert.Equal(t, fmt.Sprintf("%s/test.txt", repo.Name), header.Name)
|
||||
|
||||
_, err = tarReader.Next()
|
||||
assert.Equal(t, io.EOF, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package integration
|
||||
|
||||
|
@ -38,6 +38,25 @@ func TestUserDashboardActionLinks(t *testing.T) {
|
|||
assert.EqualValues(t, locale.TrString("new_org.link"), strings.TrimSpace(links.Find("a[href='/org/create']").Text()))
|
||||
}
|
||||
|
||||
func TestUserDashboardFeedWelcome(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// User2 has some activity in feed
|
||||
session := loginUser(t, "user2")
|
||||
page := NewHTMLParser(t, session.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK).Body)
|
||||
testUserDashboardFeedType(t, page, false)
|
||||
|
||||
// User1 doesn't have any activity in feed
|
||||
session = loginUser(t, "user1")
|
||||
page = NewHTMLParser(t, session.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK).Body)
|
||||
testUserDashboardFeedType(t, page, true)
|
||||
}
|
||||
|
||||
func testUserDashboardFeedType(t *testing.T, page *HTMLDoc, isEmpty bool) {
|
||||
page.AssertElement(t, "#activity-feed", !isEmpty)
|
||||
page.AssertElement(t, "#empty-feed", isEmpty)
|
||||
}
|
||||
|
||||
func TestDashboardTitleRendering(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
|
|
|
@ -79,3 +79,7 @@
|
|||
.dashboard .secondary-nav .ui.dropdown {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.dashboard .help {
|
||||
color: var(--color-secondary-dark-8);
|
||||
}
|
||||
|
|
|
@ -153,7 +153,9 @@
|
|||
border-radius: 0 var(--border-radius) var(--border-radius) 0 !important;
|
||||
}
|
||||
|
||||
.repository .clone-panel .dropdown .menu {
|
||||
/* Dropdown alignment */
|
||||
.repository .clone-panel .dropdown .menu,
|
||||
.repository .folder-actions .dropdown .menu {
|
||||
right: 0 !important;
|
||||
left: auto !important;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ function initTagNameEditor() {
|
|||
const newTagHelperText = el.getAttribute('data-tag-helper-new');
|
||||
const existingTagHelperText = el.getAttribute('data-tag-helper-existing');
|
||||
|
||||
let previousTag = '';
|
||||
document.getElementById('tag-name').addEventListener('keyup', (e) => {
|
||||
const value = e.target.value;
|
||||
const tagHelper = document.getElementById('tag-helper');
|
||||
|
@ -45,7 +46,10 @@ function initTagNameEditor() {
|
|||
}
|
||||
|
||||
const title_input = document.getElementById('release-title');
|
||||
title_input.placeholder = value;
|
||||
if (!title_input.value || previousTag === title_input.value) {
|
||||
title_input.value = value;
|
||||
}
|
||||
previousTag = value;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue