mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-24 22:45:11 +00:00
Merge branch 'main' into fix-pom-parent
This commit is contained in:
commit
5050f7f45a
@ -1,5 +1,5 @@
|
||||
# Build stage
|
||||
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||
FROM docker.io/library/golang:1.23-alpine3.21 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||
|
||||
FROM docker.io/library/alpine:3.20
|
||||
FROM docker.io/library/alpine:3.21
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
@ -78,7 +78,7 @@ ENV GITEA_CUSTOM=/data/gitea
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY --from=build-env /tmp/local /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Build stage
|
||||
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||
FROM docker.io/library/golang:1.23-alpine3.21 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||
|
||||
FROM docker.io/library/alpine:3.20
|
||||
FROM docker.io/library/alpine:3.21
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 2222 3000
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
@ -51,7 +52,7 @@ func GetRunnerToken(ctx context.Context, token string) (*ActionRunnerToken, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, fmt.Errorf("runner token %q: %w", token, util.ErrNotExist)
|
||||
return nil, fmt.Errorf(`runner token "%s...": %w`, base.TruncateString(token, 3), util.ErrNotExist)
|
||||
}
|
||||
return &runnerToken, nil
|
||||
}
|
||||
@ -68,19 +69,15 @@ func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string
|
||||
return err
|
||||
}
|
||||
|
||||
// NewRunnerToken creates a new active runner token and invalidate all old tokens
|
||||
// NewRunnerTokenWithValue creates a new active runner token and invalidate all old tokens
|
||||
// ownerID will be ignored and treated as 0 if repoID is non-zero.
|
||||
func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
func NewRunnerTokenWithValue(ctx context.Context, ownerID, repoID int64, token string) (*ActionRunnerToken, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
// It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
||||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
ownerID = 0
|
||||
}
|
||||
|
||||
token, err := util.CryptoRandomString(40)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runnerToken := &ActionRunnerToken{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
@ -95,11 +92,19 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerTo
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.GetEngine(ctx).Insert(runnerToken)
|
||||
_, err := db.GetEngine(ctx).Insert(runnerToken)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
token, err := util.CryptoRandomString(40)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRunnerTokenWithValue(ctx, ownerID, repoID, token)
|
||||
}
|
||||
|
||||
// GetLatestRunnerToken returns the latest runner token
|
||||
func GetLatestRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
|
@ -13,9 +13,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/testlogger"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -92,10 +92,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
|
||||
func MainTest(m *testing.M) {
|
||||
testlogger.Init()
|
||||
|
||||
giteaRoot := base.SetupGiteaRoot()
|
||||
if giteaRoot == "" {
|
||||
testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
|
||||
}
|
||||
giteaRoot := test.SetupGiteaRoot()
|
||||
giteaBinary := "gitea"
|
||||
if runtime.GOOS == "windows" {
|
||||
giteaBinary += ".exe"
|
||||
|
@ -14,13 +14,13 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/setting/config"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -206,7 +206,7 @@ func CreateTestEngine(opts FixturesOptions) error {
|
||||
x, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=immediate")
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "unknown driver") {
|
||||
return fmt.Errorf(`sqlite3 requires: import _ "github.com/mattn/go-sqlite3" or -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
|
||||
return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -235,5 +235,5 @@ func PrepareTestEnv(t testing.TB) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
metaPath := filepath.Join(giteaRoot, "tests", "gitea-repositories-meta")
|
||||
assert.NoError(t, SyncDirs(metaPath, setting.RepoRootPath))
|
||||
base.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
|
||||
test.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package base
|
||||
|
||||
type (
|
||||
// TplName template relative path type
|
||||
TplName string
|
||||
)
|
@ -13,9 +13,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -189,49 +186,3 @@ func EntryIcon(entry *git.TreeEntry) string {
|
||||
|
||||
return "file"
|
||||
}
|
||||
|
||||
// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value
|
||||
func SetupGiteaRoot() string {
|
||||
giteaRoot := os.Getenv("GITEA_ROOT")
|
||||
if giteaRoot == "" {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
giteaRoot = strings.TrimSuffix(filename, "modules/base/tool.go")
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
rel, err := filepath.Rel(giteaRoot, wd)
|
||||
if err != nil && strings.HasPrefix(filepath.ToSlash(rel), "../") {
|
||||
giteaRoot = wd
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(giteaRoot, "gitea")); os.IsNotExist(err) {
|
||||
giteaRoot = ""
|
||||
} else if err := os.Setenv("GITEA_ROOT", giteaRoot); err != nil {
|
||||
giteaRoot = ""
|
||||
}
|
||||
}
|
||||
return giteaRoot
|
||||
}
|
||||
|
||||
// FormatNumberSI format a number
|
||||
func FormatNumberSI(data any) string {
|
||||
var num int64
|
||||
if num1, ok := data.(int64); ok {
|
||||
num = num1
|
||||
} else if num1, ok := data.(int); ok {
|
||||
num = int64(num1)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
||||
if num < 1000 {
|
||||
return fmt.Sprintf("%d", num)
|
||||
} else if num < 1000000 {
|
||||
num2 := float32(num) / float32(1000.0)
|
||||
return fmt.Sprintf("%.1fk", num2)
|
||||
} else if num < 1000000000 {
|
||||
num2 := float32(num) / float32(1000000.0)
|
||||
return fmt.Sprintf("%.1fM", num2)
|
||||
}
|
||||
num2 := float32(num) / float32(1000000000.0)
|
||||
return fmt.Sprintf("%.1fG", num2)
|
||||
}
|
||||
|
@ -169,18 +169,3 @@ func TestInt64sToStrings(t *testing.T) {
|
||||
}
|
||||
|
||||
// TODO: Test EntryIcon
|
||||
|
||||
func TestSetupGiteaRoot(t *testing.T) {
|
||||
t.Setenv("GITEA_ROOT", "test")
|
||||
assert.Equal(t, "test", SetupGiteaRoot())
|
||||
t.Setenv("GITEA_ROOT", "")
|
||||
assert.NotEqual(t, "test", SetupGiteaRoot())
|
||||
}
|
||||
|
||||
func TestFormatNumberSI(t *testing.T) {
|
||||
assert.Equal(t, "125", FormatNumberSI(int(125)))
|
||||
assert.Equal(t, "1.3k", FormatNumberSI(int64(1317)))
|
||||
assert.Equal(t, "21.3M", FormatNumberSI(21317675))
|
||||
assert.Equal(t, "45.7G", FormatNumberSI(45721317675))
|
||||
assert.Equal(t, "", FormatNumberSI("test"))
|
||||
}
|
||||
|
@ -216,8 +216,6 @@ type CommitsByFileAndRangeOptions struct {
|
||||
|
||||
// CommitsByFileAndRange return the commits according revision file and the page
|
||||
func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) ([]*Commit, error) {
|
||||
skip := (opts.Page - 1) * setting.Git.CommitsRangeSize
|
||||
|
||||
stdoutReader, stdoutWriter := io.Pipe()
|
||||
defer func() {
|
||||
_ = stdoutReader.Close()
|
||||
@ -226,8 +224,8 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
|
||||
go func() {
|
||||
stderr := strings.Builder{}
|
||||
gitCmd := NewCommand(repo.Ctx, "rev-list").
|
||||
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize*opts.Page).
|
||||
AddOptionFormat("--skip=%d", skip)
|
||||
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
|
||||
AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize)
|
||||
gitCmd.AddDynamicArguments(opts.Revision)
|
||||
|
||||
if opts.Not != "" {
|
||||
|
@ -8,7 +8,11 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRepository_GetCommitBranches(t *testing.T) {
|
||||
@ -126,3 +130,21 @@ func TestGetRefCommitID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitsByFileAndRange(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Git.CommitsRangeSize, 2)()
|
||||
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
bareRepo1, err := openRepositoryWithDefaultContext(bareRepo1Path)
|
||||
require.NoError(t, err)
|
||||
defer bareRepo1.Close()
|
||||
|
||||
// "foo" has 3 commits in "master" branch
|
||||
commits, err := bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "foo", Page: 1})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, commits, 2)
|
||||
|
||||
commits, err = bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{Revision: "master", File: "foo", Page: 2})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, commits, 1)
|
||||
}
|
||||
|
@ -233,72 +233,34 @@ func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int,
|
||||
return numFiles, totalAdditions, totalDeletions, err
|
||||
}
|
||||
|
||||
// GetDiffOrPatch generates either diff or formatted patch data between given revisions
|
||||
func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, patch, binary bool) error {
|
||||
if patch {
|
||||
return repo.GetPatch(base, head, w)
|
||||
}
|
||||
if binary {
|
||||
return repo.GetDiffBinary(base, head, w)
|
||||
}
|
||||
return repo.GetDiff(base, head, w)
|
||||
}
|
||||
|
||||
// GetDiff generates and returns patch data between given revisions, optimized for human readability
|
||||
func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
|
||||
func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
err := NewCommand(repo.Ctx, "diff", "-p").AddDynamicArguments(base + "..." + head).
|
||||
return NewCommand(repo.Ctx, "diff", "-p").AddDynamicArguments(compareArg).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
Stderr: stderr,
|
||||
})
|
||||
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
|
||||
return NewCommand(repo.Ctx, "diff", "-p").AddDynamicArguments(base, head).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
|
||||
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
err := NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram").AddDynamicArguments(base + "..." + head).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
Stderr: stderr,
|
||||
})
|
||||
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
|
||||
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram").AddDynamicArguments(base, head).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
})
|
||||
}
|
||||
return err
|
||||
func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
|
||||
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
})
|
||||
}
|
||||
|
||||
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
|
||||
func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
|
||||
func (repo *Repository) GetPatch(compareArg string, w io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
err := NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(base + "..." + head).
|
||||
return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
Stderr: stderr,
|
||||
})
|
||||
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
|
||||
return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(base, head).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetFilesChangedBetween returns a list of all files that have been changed between the given commits
|
||||
@ -329,21 +291,6 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err
|
||||
return split, err
|
||||
}
|
||||
|
||||
// GetDiffFromMergeBase generates and return patch data from merge base to head
|
||||
func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
err := NewCommand(repo.Ctx, "diff", "-p", "--binary").AddDynamicArguments(base + "..." + head).
|
||||
Run(&RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: w,
|
||||
Stderr: stderr,
|
||||
})
|
||||
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
|
||||
return repo.GetDiffBinary(base, head, w)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadPatchCommit will check if a diff patch exists and return stats
|
||||
func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error) {
|
||||
// Migrated repositories download patches to "pulls" location
|
||||
|
@ -28,7 +28,7 @@ func TestGetFormatPatch(t *testing.T) {
|
||||
defer repo.Close()
|
||||
|
||||
rd := &bytes.Buffer{}
|
||||
err = repo.GetPatch("8d92fc95^", "8d92fc95", rd)
|
||||
err = repo.GetPatch("8d92fc95^...8d92fc95", rd)
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
return
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
@ -38,63 +39,32 @@ func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository,
|
||||
|
||||
// contextKey is a value for use with context.WithValue.
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// RepositoryContextKey is a context key. It is used with context.Value() to get the current Repository for the context
|
||||
var RepositoryContextKey = &contextKey{"repository"}
|
||||
|
||||
// RepositoryFromContext attempts to get the repository from the context
|
||||
func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository {
|
||||
value := ctx.Value(RepositoryContextKey)
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if gitRepo, ok := value.(*git.Repository); ok && gitRepo != nil {
|
||||
if gitRepo.Path == repoPath(repo) {
|
||||
return gitRepo
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
repoPath string
|
||||
}
|
||||
|
||||
// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
|
||||
func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) {
|
||||
gitRepo := repositoryFromContext(ctx, repo)
|
||||
if gitRepo != nil {
|
||||
return gitRepo, util.NopCloser{}, nil
|
||||
ds := reqctx.GetRequestDataStore(ctx)
|
||||
if ds != nil {
|
||||
gitRepo, err := RepositoryFromRequestContextOrOpen(ctx, ds, repo)
|
||||
return gitRepo, util.NopCloser{}, err
|
||||
}
|
||||
|
||||
gitRepo, err := OpenRepository(ctx, repo)
|
||||
return gitRepo, gitRepo, err
|
||||
}
|
||||
|
||||
// repositoryFromContextPath attempts to get the repository from the context
|
||||
func repositoryFromContextPath(ctx context.Context, path string) *git.Repository {
|
||||
value := ctx.Value(RepositoryContextKey)
|
||||
if value == nil {
|
||||
return nil
|
||||
// RepositoryFromRequestContextOrOpen opens the repository at the given relative path in the provided request context
|
||||
// The repo will be automatically closed when the request context is done
|
||||
func RepositoryFromRequestContextOrOpen(ctx context.Context, ds reqctx.RequestDataStore, repo Repository) (*git.Repository, error) {
|
||||
ck := contextKey{repoPath: repoPath(repo)}
|
||||
if gitRepo, ok := ctx.Value(ck).(*git.Repository); ok {
|
||||
return gitRepo, nil
|
||||
}
|
||||
|
||||
if repo, ok := value.(*git.Repository); ok && repo != nil {
|
||||
if repo.Path == path {
|
||||
return repo
|
||||
}
|
||||
gitRepo, err := git.OpenRepository(ctx, ck.repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RepositoryFromContextOrOpenPath attempts to get the repository from the context or just opens it
|
||||
// Deprecated: Use RepositoryFromContextOrOpen instead
|
||||
func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Repository, io.Closer, error) {
|
||||
gitRepo := repositoryFromContextPath(ctx, path)
|
||||
if gitRepo != nil {
|
||||
return gitRepo, util.NopCloser{}, nil
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, path)
|
||||
return gitRepo, gitRepo, err
|
||||
ds.AddCloser(gitRepo)
|
||||
ds.SetContextValue(ck, gitRepo)
|
||||
return gitRepo, nil
|
||||
}
|
||||
|
@ -14,15 +14,11 @@ import (
|
||||
// WalkReferences walks all the references from the repository
|
||||
// refname is empty, ObjectTag or ObjectBranch. All other values should be treated as equivalent to empty.
|
||||
func WalkReferences(ctx context.Context, repo Repository, walkfn func(sha1, refname string) error) (int, error) {
|
||||
gitRepo := repositoryFromContext(ctx, repo)
|
||||
if gitRepo == nil {
|
||||
var err error
|
||||
gitRepo, err = OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
gitRepo, closer, err := RepositoryFromContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
i := 0
|
||||
iter, err := gitRepo.GoGitRepo().References()
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
@ -194,3 +195,21 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
node = node.NextSibling.NextSibling
|
||||
}
|
||||
}
|
||||
|
||||
func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
next := node.NextSibling
|
||||
|
||||
for node != nil && node != next {
|
||||
found, ref := references.FindRenderizableCommitCrossReference(node.Data)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
|
||||
linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha), LinkTypeApp)
|
||||
link := createLink(ctx, linkHref, reftext, "commit")
|
||||
|
||||
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
|
||||
node = node.NextSibling.NextSibling
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
@ -16,8 +16,16 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
type RenderIssueIconTitleOptions struct {
|
||||
OwnerName string
|
||||
RepoName string
|
||||
LinkHref string
|
||||
IssueIndex int64
|
||||
}
|
||||
|
||||
func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
if ctx.RenderOptions.Metas == nil {
|
||||
return
|
||||
@ -66,6 +74,27 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func createIssueLinkContentWithSummary(ctx *RenderContext, linkHref string, ref *references.RenderizableReference) *html.Node {
|
||||
if DefaultRenderHelperFuncs.RenderRepoIssueIconTitle == nil {
|
||||
return nil
|
||||
}
|
||||
issueIndex, _ := strconv.ParseInt(ref.Issue, 10, 64)
|
||||
h, err := DefaultRenderHelperFuncs.RenderRepoIssueIconTitle(ctx, RenderIssueIconTitleOptions{
|
||||
OwnerName: ref.Owner,
|
||||
RepoName: ref.Name,
|
||||
LinkHref: linkHref,
|
||||
IssueIndex: issueIndex,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("RenderRepoIssueIconTitle failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
if h == "" {
|
||||
return nil
|
||||
}
|
||||
return &html.Node{Type: html.RawNode, Data: string(ctx.RenderInternal.ProtectSafeAttrs(h))}
|
||||
}
|
||||
|
||||
func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
if ctx.RenderOptions.Metas == nil {
|
||||
return
|
||||
@ -76,32 +105,28 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
// old logic: crossLinkOnly := ctx.RenderOptions.Metas["mode"] == "document" && !ctx.IsWiki
|
||||
crossLinkOnly := ctx.RenderOptions.Metas["markupAllowShortIssuePattern"] != "true"
|
||||
|
||||
var (
|
||||
found bool
|
||||
ref *references.RenderizableReference
|
||||
)
|
||||
var ref *references.RenderizableReference
|
||||
|
||||
next := node.NextSibling
|
||||
|
||||
for node != nil && node != next {
|
||||
_, hasExtTrackFormat := ctx.RenderOptions.Metas["format"]
|
||||
|
||||
// Repos with external issue trackers might still need to reference local PRs
|
||||
// We need to concern with the first one that shows up in the text, whichever it is
|
||||
isNumericStyle := ctx.RenderOptions.Metas["style"] == "" || ctx.RenderOptions.Metas["style"] == IssueNameStyleNumeric
|
||||
foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly)
|
||||
refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly)
|
||||
|
||||
switch ctx.RenderOptions.Metas["style"] {
|
||||
case "", IssueNameStyleNumeric:
|
||||
found, ref = foundNumeric, refNumeric
|
||||
ref = refNumeric
|
||||
case IssueNameStyleAlphanumeric:
|
||||
found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
|
||||
ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
|
||||
case IssueNameStyleRegexp:
|
||||
pattern, err := regexplru.GetCompiled(ctx.RenderOptions.Metas["regexp"])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
found, ref = references.FindRenderizableReferenceRegexp(node.Data, pattern)
|
||||
ref = references.FindRenderizableReferenceRegexp(node.Data, pattern)
|
||||
}
|
||||
|
||||
// Repos with external issue trackers might still need to reference local PRs
|
||||
@ -109,17 +134,17 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
if hasExtTrackFormat && !isNumericStyle && refNumeric != nil {
|
||||
// If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that
|
||||
// Allow a free-pass when non-numeric pattern wasn't found.
|
||||
if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) {
|
||||
found = foundNumeric
|
||||
if ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start {
|
||||
ref = refNumeric
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
|
||||
if ref == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var link *html.Node
|
||||
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
|
||||
refText := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
|
||||
if hasExtTrackFormat && !ref.IsPull {
|
||||
ctx.RenderOptions.Metas["index"] = ref.Issue
|
||||
|
||||
@ -129,18 +154,23 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
|
||||
}
|
||||
|
||||
link = createLink(ctx, res, reftext, "ref-issue ref-external-issue")
|
||||
link = createLink(ctx, res, refText, "ref-issue ref-external-issue")
|
||||
} else {
|
||||
// Path determines the type of link that will be rendered. It's unknown at this point whether
|
||||
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
|
||||
// Gitea will redirect on click as appropriate.
|
||||
issueOwner := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["user"], ref.Owner)
|
||||
issueRepo := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["repo"], ref.Name)
|
||||
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
|
||||
if ref.Owner == "" {
|
||||
linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], issuePath, ref.Issue), LinkTypeApp)
|
||||
link = createLink(ctx, linkHref, reftext, "ref-issue")
|
||||
} else {
|
||||
linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, issuePath, ref.Issue), LinkTypeApp)
|
||||
link = createLink(ctx, linkHref, reftext, "ref-issue")
|
||||
linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(issueOwner, issueRepo, issuePath, ref.Issue), LinkTypeApp)
|
||||
|
||||
// at the moment, only render the issue index in a full line (or simple line) as icon+title
|
||||
// otherwise it would be too noisy for "take #1 as an example" in a sentence
|
||||
if node.Parent.DataAtom == atom.Li && ref.RefLocation.Start < 20 && ref.RefLocation.End == len(node.Data) {
|
||||
link = createIssueLinkContentWithSummary(ctx, linkHref, ref)
|
||||
}
|
||||
if link == nil {
|
||||
link = createLink(ctx, linkHref, refText, "ref-issue")
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,21 +198,3 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
node = node.NextSibling.NextSibling.NextSibling.NextSibling
|
||||
}
|
||||
}
|
||||
|
||||
func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
next := node.NextSibling
|
||||
|
||||
for node != nil && node != next {
|
||||
found, ref := references.FindRenderizableCommitCrossReference(node.Data)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
|
||||
linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha), LinkTypeApp)
|
||||
link := createLink(ctx, linkHref, reftext, "commit")
|
||||
|
||||
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
|
||||
node = node.NextSibling.NextSibling
|
||||
}
|
||||
}
|
||||
|
72
modules/markup/html_issue_test.go
Normal file
72
modules/markup/html_issue_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package markup_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
testModule "code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRender_IssueList(t *testing.T) {
|
||||
defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
|
||||
markup.Init(&markup.RenderHelperFuncs{
|
||||
RenderRepoIssueIconTitle: func(ctx context.Context, opts markup.RenderIssueIconTitleOptions) (template.HTML, error) {
|
||||
return htmlutil.HTMLFormat("<div>issue #%d</div>", opts.IssueIndex), nil
|
||||
},
|
||||
})
|
||||
|
||||
test := func(input, expected string) {
|
||||
rctx := markup.NewTestRenderContext(markup.TestAppURL, map[string]string{
|
||||
"user": "test-user", "repo": "test-repo",
|
||||
"markupAllowShortIssuePattern": "true",
|
||||
})
|
||||
out, err := markdown.RenderString(rctx, input)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
|
||||
}
|
||||
|
||||
t.Run("NormalIssueRef", func(t *testing.T) {
|
||||
test(
|
||||
"#12345",
|
||||
`<p><a href="http://localhost:3000/test-user/test-repo/issues/12345" class="ref-issue" rel="nofollow">#12345</a></p>`,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("ListIssueRef", func(t *testing.T) {
|
||||
test(
|
||||
"* #12345",
|
||||
`<ul>
|
||||
<li><div>issue #12345</div></li>
|
||||
</ul>`,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("ListIssueRefNormal", func(t *testing.T) {
|
||||
test(
|
||||
"* foo #12345 bar",
|
||||
`<ul>
|
||||
<li>foo <a href="http://localhost:3000/test-user/test-repo/issues/12345" class="ref-issue" rel="nofollow">#12345</a> bar</li>
|
||||
</ul>`,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("ListTodoIssueRef", func(t *testing.T) {
|
||||
test(
|
||||
"* [ ] #12345",
|
||||
`<ul>
|
||||
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="2"/><div>issue #12345</div></li>
|
||||
</ul>`,
|
||||
)
|
||||
})
|
||||
}
|
@ -38,6 +38,7 @@ type RenderHelper interface {
|
||||
type RenderHelperFuncs struct {
|
||||
IsUsernameMentionable func(ctx context.Context, username string) bool
|
||||
RenderRepoFileCodePreview func(ctx context.Context, options RenderCodePreviewOptions) (template.HTML, error)
|
||||
RenderRepoIssueIconTitle func(ctx context.Context, options RenderIssueIconTitleOptions) (template.HTML, error)
|
||||
}
|
||||
|
||||
var DefaultRenderHelperFuncs *RenderHelperFuncs
|
||||
|
@ -32,7 +32,7 @@ var (
|
||||
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
||||
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
|
||||
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\')`)
|
||||
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\'|,)`)
|
||||
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
||||
// e.g. org/repo#12345
|
||||
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
@ -330,22 +330,22 @@ func FindAllIssueReferences(content string) []IssueReference {
|
||||
}
|
||||
|
||||
// FindRenderizableReferenceNumeric returns the first unvalidated reference found in a string.
|
||||
func FindRenderizableReferenceNumeric(content string, prOnly, crossLinkOnly bool) (bool, *RenderizableReference) {
|
||||
func FindRenderizableReferenceNumeric(content string, prOnly, crossLinkOnly bool) *RenderizableReference {
|
||||
var match []int
|
||||
if !crossLinkOnly {
|
||||
match = issueNumericPattern.FindStringSubmatchIndex(content)
|
||||
}
|
||||
if match == nil {
|
||||
if match = crossReferenceIssueNumericPattern.FindStringSubmatchIndex(content); match == nil {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
r := getCrossReference(util.UnsafeStringToBytes(content), match[2], match[3], false, prOnly)
|
||||
if r == nil {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
return true, &RenderizableReference{
|
||||
return &RenderizableReference{
|
||||
Issue: r.issue,
|
||||
Owner: r.owner,
|
||||
Name: r.name,
|
||||
@ -372,15 +372,14 @@ func FindRenderizableCommitCrossReference(content string) (bool, *RenderizableRe
|
||||
}
|
||||
|
||||
// FindRenderizableReferenceRegexp returns the first regexp unvalidated references found in a string.
|
||||
func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) (bool, *RenderizableReference) {
|
||||
func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) *RenderizableReference {
|
||||
match := pattern.FindStringSubmatchIndex(content)
|
||||
if len(match) < 4 {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
action, location := findActionKeywords([]byte(content), match[2])
|
||||
|
||||
return true, &RenderizableReference{
|
||||
return &RenderizableReference{
|
||||
Issue: content[match[2]:match[3]],
|
||||
RefLocation: &RefSpan{Start: match[0], End: match[1]},
|
||||
Action: action,
|
||||
@ -390,15 +389,14 @@ func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) (bo
|
||||
}
|
||||
|
||||
// FindRenderizableReferenceAlphanumeric returns the first alphanumeric unvalidated references found in a string.
|
||||
func FindRenderizableReferenceAlphanumeric(content string) (bool, *RenderizableReference) {
|
||||
func FindRenderizableReferenceAlphanumeric(content string) *RenderizableReference {
|
||||
match := issueAlphanumericPattern.FindStringSubmatchIndex(content)
|
||||
if match == nil {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
action, location := findActionKeywords([]byte(content), match[2])
|
||||
|
||||
return true, &RenderizableReference{
|
||||
return &RenderizableReference{
|
||||
Issue: content[match[2]:match[3]],
|
||||
RefLocation: &RefSpan{Start: match[2], End: match[3]},
|
||||
Action: action,
|
||||
|
@ -249,11 +249,10 @@ func TestFindAllIssueReferences(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, fixture := range alnumFixtures {
|
||||
found, ref := FindRenderizableReferenceAlphanumeric(fixture.input)
|
||||
ref := FindRenderizableReferenceAlphanumeric(fixture.input)
|
||||
if fixture.issue == "" {
|
||||
assert.False(t, found, "Failed to parse: {%s}", fixture.input)
|
||||
assert.Nil(t, ref, "Failed to parse: {%s}", fixture.input)
|
||||
} else {
|
||||
assert.True(t, found, "Failed to parse: {%s}", fixture.input)
|
||||
assert.Equal(t, fixture.issue, ref.Issue, "Failed to parse: {%s}", fixture.input)
|
||||
assert.Equal(t, fixture.refLocation, ref.RefLocation, "Failed to parse: {%s}", fixture.input)
|
||||
assert.Equal(t, fixture.action, ref.Action, "Failed to parse: {%s}", fixture.input)
|
||||
@ -463,6 +462,7 @@ func TestRegExp_issueAlphanumericPattern(t *testing.T) {
|
||||
"ABC-123:",
|
||||
"\"ABC-123\"",
|
||||
"'ABC-123'",
|
||||
"ABC-123, unknown PR",
|
||||
}
|
||||
falseTestCases := []string{
|
||||
"RC-08",
|
||||
|
@ -31,12 +31,7 @@ func Test_getLicense(t *testing.T) {
|
||||
|
||||
Copyright (c) 2023 Gitea
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`,
|
||||
Permission is hereby granted`,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
@ -53,7 +48,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetLicense(%v, %v)", tt.args.name, tt.args.values)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.want, string(got), "GetLicense(%v, %v)", tt.args.name, tt.args.values)
|
||||
assert.Contains(t, string(got), tt.want, "GetLicense(%v, %v)", tt.args.name, tt.args.values)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
123
modules/reqctx/datastore.go
Normal file
123
modules/reqctx/datastore.go
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package reqctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
)
|
||||
|
||||
type ContextDataProvider interface {
|
||||
GetData() ContextData
|
||||
}
|
||||
|
||||
type ContextData map[string]any
|
||||
|
||||
func (ds ContextData) GetData() ContextData {
|
||||
return ds
|
||||
}
|
||||
|
||||
func (ds ContextData) MergeFrom(other ContextData) ContextData {
|
||||
for k, v := range other {
|
||||
ds[k] = v
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
// RequestDataStore is a short-lived context-related object that is used to store request-specific data.
|
||||
type RequestDataStore interface {
|
||||
GetData() ContextData
|
||||
SetContextValue(k, v any)
|
||||
GetContextValue(key any) any
|
||||
AddCleanUp(f func())
|
||||
AddCloser(c io.Closer)
|
||||
}
|
||||
|
||||
type requestDataStoreKeyType struct{}
|
||||
|
||||
var RequestDataStoreKey requestDataStoreKeyType
|
||||
|
||||
type requestDataStore struct {
|
||||
data ContextData
|
||||
|
||||
mu sync.RWMutex
|
||||
values map[any]any
|
||||
cleanUpFuncs []func()
|
||||
}
|
||||
|
||||
func (r *requestDataStore) GetContextValue(key any) any {
|
||||
if key == RequestDataStoreKey {
|
||||
return r
|
||||
}
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.values[key]
|
||||
}
|
||||
|
||||
func (r *requestDataStore) SetContextValue(k, v any) {
|
||||
r.mu.Lock()
|
||||
r.values[k] = v
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetData and the underlying ContextData are not thread-safe, callers should ensure thread-safety.
|
||||
func (r *requestDataStore) GetData() ContextData {
|
||||
if r.data == nil {
|
||||
r.data = make(ContextData)
|
||||
}
|
||||
return r.data
|
||||
}
|
||||
|
||||
func (r *requestDataStore) AddCleanUp(f func()) {
|
||||
r.mu.Lock()
|
||||
r.cleanUpFuncs = append(r.cleanUpFuncs, f)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *requestDataStore) AddCloser(c io.Closer) {
|
||||
r.AddCleanUp(func() { _ = c.Close() })
|
||||
}
|
||||
|
||||
func (r *requestDataStore) cleanUp() {
|
||||
for _, f := range r.cleanUpFuncs {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
func GetRequestDataStore(ctx context.Context) RequestDataStore {
|
||||
if req, ok := ctx.Value(RequestDataStoreKey).(*requestDataStore); ok {
|
||||
return req
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type requestContext struct {
|
||||
context.Context
|
||||
dataStore *requestDataStore
|
||||
}
|
||||
|
||||
func (c *requestContext) Value(key any) any {
|
||||
if v := c.dataStore.GetContextValue(key); v != nil {
|
||||
return v
|
||||
}
|
||||
return c.Context.Value(key)
|
||||
}
|
||||
|
||||
func NewRequestContext(parentCtx context.Context, profDesc string) (_ context.Context, finished func()) {
|
||||
ctx, _, processFinished := process.GetManager().AddTypedContext(parentCtx, profDesc, process.RequestProcessType, true)
|
||||
reqCtx := &requestContext{Context: ctx, dataStore: &requestDataStore{values: make(map[any]any)}}
|
||||
return reqCtx, func() {
|
||||
reqCtx.dataStore.cleanUp()
|
||||
processFinished()
|
||||
}
|
||||
}
|
||||
|
||||
// NewRequestContextForTest creates a new RequestContext for testing purposes
|
||||
// It doesn't add the context to the process manager, nor do cleanup
|
||||
func NewRequestContextForTest(parentCtx context.Context) context.Context {
|
||||
return &requestContext{Context: parentCtx, dataStore: &requestDataStore{values: make(map[any]any)}}
|
||||
}
|
@ -235,3 +235,9 @@ func checkOverlappedPath(name, path string) {
|
||||
}
|
||||
configuredPaths[path] = name
|
||||
}
|
||||
|
||||
func PanicInDevOrTesting(msg string, a ...any) {
|
||||
if !IsProd || IsInTesting {
|
||||
panic(fmt.Sprintf(msg, a...))
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type normalizeVarsStruct struct {
|
||||
type globalVarsStruct struct {
|
||||
reXMLDoc,
|
||||
reComment,
|
||||
reAttrXMLNs,
|
||||
@ -18,26 +18,23 @@ type normalizeVarsStruct struct {
|
||||
reAttrClassPrefix *regexp.Regexp
|
||||
}
|
||||
|
||||
var (
|
||||
normalizeVars *normalizeVarsStruct
|
||||
normalizeVarsOnce sync.Once
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() *globalVarsStruct {
|
||||
return &globalVarsStruct{
|
||||
reXMLDoc: regexp.MustCompile(`(?s)<\?xml.*?>`),
|
||||
reComment: regexp.MustCompile(`(?s)<!--.*?-->`),
|
||||
|
||||
reAttrXMLNs: regexp.MustCompile(`(?s)\s+xmlns\s*=\s*"[^"]*"`),
|
||||
reAttrSize: regexp.MustCompile(`(?s)\s+(width|height)\s*=\s*"[^"]+"`),
|
||||
reAttrClassPrefix: regexp.MustCompile(`(?s)\s+class\s*=\s*"`),
|
||||
}
|
||||
})
|
||||
|
||||
// Normalize normalizes the SVG content: set default width/height, remove unnecessary tags/attributes
|
||||
// It's designed to work with valid SVG content. For invalid SVG content, the returned content is not guaranteed.
|
||||
func Normalize(data []byte, size int) []byte {
|
||||
normalizeVarsOnce.Do(func() {
|
||||
normalizeVars = &normalizeVarsStruct{
|
||||
reXMLDoc: regexp.MustCompile(`(?s)<\?xml.*?>`),
|
||||
reComment: regexp.MustCompile(`(?s)<!--.*?-->`),
|
||||
|
||||
reAttrXMLNs: regexp.MustCompile(`(?s)\s+xmlns\s*=\s*"[^"]*"`),
|
||||
reAttrSize: regexp.MustCompile(`(?s)\s+(width|height)\s*=\s*"[^"]+"`),
|
||||
reAttrClassPrefix: regexp.MustCompile(`(?s)\s+class\s*=\s*"`),
|
||||
}
|
||||
})
|
||||
data = normalizeVars.reXMLDoc.ReplaceAll(data, nil)
|
||||
data = normalizeVars.reComment.ReplaceAll(data, nil)
|
||||
vars := globalVars()
|
||||
data = vars.reXMLDoc.ReplaceAll(data, nil)
|
||||
data = vars.reComment.ReplaceAll(data, nil)
|
||||
|
||||
data = bytes.TrimSpace(data)
|
||||
svgTag, svgRemaining, ok := bytes.Cut(data, []byte(">"))
|
||||
@ -45,9 +42,9 @@ func Normalize(data []byte, size int) []byte {
|
||||
return data
|
||||
}
|
||||
normalized := bytes.Clone(svgTag)
|
||||
normalized = normalizeVars.reAttrXMLNs.ReplaceAll(normalized, nil)
|
||||
normalized = normalizeVars.reAttrSize.ReplaceAll(normalized, nil)
|
||||
normalized = normalizeVars.reAttrClassPrefix.ReplaceAll(normalized, []byte(` class="`))
|
||||
normalized = vars.reAttrXMLNs.ReplaceAll(normalized, nil)
|
||||
normalized = vars.reAttrSize.ReplaceAll(normalized, nil)
|
||||
normalized = vars.reAttrClassPrefix.ReplaceAll(normalized, []byte(` class="`))
|
||||
normalized = bytes.TrimSpace(normalized)
|
||||
normalized = fmt.Appendf(normalized, ` width="%d" height="%d"`, size, size)
|
||||
if !bytes.Contains(normalized, []byte(` class="`)) {
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"html"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -69,7 +68,7 @@ func NewFuncMap() template.FuncMap {
|
||||
// -----------------------------------------------------------------
|
||||
// time / number / format
|
||||
"FileSize": base.FileSize,
|
||||
"CountFmt": base.FormatNumberSI,
|
||||
"CountFmt": countFmt,
|
||||
"Sec2Time": util.SecToTime,
|
||||
|
||||
"TimeEstimateString": timeEstimateString,
|
||||
@ -239,29 +238,8 @@ func iif(condition any, vals ...any) any {
|
||||
}
|
||||
|
||||
func isTemplateTruthy(v any) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Bool:
|
||||
return rv.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int() != 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return rv.Uint() != 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float() != 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return rv.Complex() != 0
|
||||
case reflect.String, reflect.Slice, reflect.Array, reflect.Map:
|
||||
return rv.Len() > 0
|
||||
case reflect.Struct:
|
||||
return true
|
||||
default:
|
||||
return !rv.IsNil()
|
||||
}
|
||||
truth, _ := template.IsTrue(v)
|
||||
return truth
|
||||
}
|
||||
|
||||
// evalTokens evaluates the expression by tokens and returns the result, see the comment of eval.Expr for details.
|
||||
@ -286,14 +264,6 @@ func userThemeName(user *user_model.User) string {
|
||||
return setting.UI.DefaultTheme
|
||||
}
|
||||
|
||||
func timeEstimateString(timeSec any) string {
|
||||
v, _ := util.ToInt64(timeSec)
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.TimeEstimateString(v)
|
||||
}
|
||||
|
||||
// QueryBuild builds a query string from a list of key-value pairs.
|
||||
// It omits the nil and empty strings, but it doesn't omit other zero values,
|
||||
// because the zero value of number types may have a meaning.
|
||||
@ -361,7 +331,5 @@ func QueryBuild(a ...any) template.URL {
|
||||
}
|
||||
|
||||
func panicIfDevOrTesting() {
|
||||
if !setting.IsProd || setting.IsInTesting {
|
||||
panic("legacy template functions are for backward compatibility only, do not use them in new code")
|
||||
}
|
||||
setting.PanicInDevOrTesting("legacy template functions are for backward compatibility only, do not use them in new code")
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -65,31 +66,12 @@ func TestSanitizeHTML(t *testing.T) {
|
||||
assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`))
|
||||
}
|
||||
|
||||
func TestTemplateTruthy(t *testing.T) {
|
||||
func TestTemplateIif(t *testing.T) {
|
||||
tmpl := template.New("test")
|
||||
tmpl.Funcs(template.FuncMap{"Iif": iif})
|
||||
template.Must(tmpl.Parse(`{{if .Value}}true{{else}}false{{end}}:{{Iif .Value "true" "false"}}`))
|
||||
|
||||
cases := []any{
|
||||
nil, false, true, "", "string", 0, 1,
|
||||
byte(0), byte(1), int64(0), int64(1), float64(0), float64(1),
|
||||
complex(0, 0), complex(1, 0),
|
||||
(chan int)(nil), make(chan int),
|
||||
(func())(nil), func() {},
|
||||
util.ToPointer(0), util.ToPointer(util.ToPointer(0)),
|
||||
util.ToPointer(1), util.ToPointer(util.ToPointer(1)),
|
||||
[0]int{},
|
||||
[1]int{0},
|
||||
[]int(nil),
|
||||
[]int{},
|
||||
[]int{0},
|
||||
map[any]any(nil),
|
||||
map[any]any{},
|
||||
map[any]any{"k": "v"},
|
||||
(*struct{})(nil),
|
||||
struct{}{},
|
||||
util.ToPointer(struct{}{}),
|
||||
}
|
||||
cases := []any{nil, false, true, "", "string", 0, 1}
|
||||
w := &strings.Builder{}
|
||||
truthyCount := 0
|
||||
for i, v := range cases {
|
||||
@ -102,3 +84,37 @@ func TestTemplateTruthy(t *testing.T) {
|
||||
}
|
||||
assert.True(t, truthyCount != 0 && truthyCount != len(cases))
|
||||
}
|
||||
|
||||
func TestTemplateEscape(t *testing.T) {
|
||||
execTmpl := func(code string) string {
|
||||
tmpl := template.New("test")
|
||||
tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlutil.HTMLFormat})
|
||||
template.Must(tmpl.Parse(code))
|
||||
w := &strings.Builder{}
|
||||
assert.NoError(t, tmpl.Execute(w, nil))
|
||||
return w.String()
|
||||
}
|
||||
|
||||
t.Run("Golang URL Escape", func(t *testing.T) {
|
||||
// Golang template considers "href", "*src*", "*uri*", "*url*" (and more) ... attributes as contentTypeURL and does auto-escaping
|
||||
actual := execTmpl(`<a href="?a={{"%"}}"></a>`)
|
||||
assert.Equal(t, `<a href="?a=%25"></a>`, actual)
|
||||
actual = execTmpl(`<a data-xxx-url="?a={{"%"}}"></a>`)
|
||||
assert.Equal(t, `<a data-xxx-url="?a=%25"></a>`, actual)
|
||||
})
|
||||
t.Run("Golang URL No-escape", func(t *testing.T) {
|
||||
// non-URL content isn't auto-escaped
|
||||
actual := execTmpl(`<a data-link="?a={{"%"}}"></a>`)
|
||||
assert.Equal(t, `<a data-link="?a=%"></a>`, actual)
|
||||
})
|
||||
t.Run("QueryBuild", func(t *testing.T) {
|
||||
actual := execTmpl(`<a href="{{QueryBuild "?" "a" "%"}}"></a>`)
|
||||
assert.Equal(t, `<a href="?a=%25"></a>`, actual)
|
||||
actual = execTmpl(`<a href="?{{QueryBuild "a" "%"}}"></a>`)
|
||||
assert.Equal(t, `<a href="?a=%25"></a>`, actual)
|
||||
})
|
||||
t.Run("HTMLFormat", func(t *testing.T) {
|
||||
actual := execTmpl("{{HTMLFormat `<a k=\"%s\">%s</a>` `\"` `<>`}}")
|
||||
assert.Equal(t, `<a k="""><></a>`, actual)
|
||||
})
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ import (
|
||||
|
||||
type TemplateExecutor scopedtmpl.TemplateExecutor
|
||||
|
||||
type TplName string
|
||||
|
||||
type HTMLRender struct {
|
||||
templates atomic.Pointer[scopedtmpl.ScopedTemplate]
|
||||
}
|
||||
@ -40,7 +42,8 @@ var (
|
||||
|
||||
var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors")
|
||||
|
||||
func (h *HTMLRender) HTML(w io.Writer, status int, name string, data any, ctx context.Context) error { //nolint:revive
|
||||
func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ctx context.Context) error { //nolint:revive
|
||||
name := string(tplName)
|
||||
if respWriter, ok := w.(http.ResponseWriter); ok {
|
||||
if respWriter.Header().Get("Content-Type") == "" {
|
||||
respWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
37
modules/templates/util_format.go
Normal file
37
modules/templates/util_format.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
func timeEstimateString(timeSec any) string {
|
||||
v, _ := util.ToInt64(timeSec)
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.TimeEstimateString(v)
|
||||
}
|
||||
|
||||
func countFmt(data any) string {
|
||||
// legacy code, not ideal, still used in some places
|
||||
num, err := util.ToInt64(data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if num < 1000 {
|
||||
return fmt.Sprintf("%d", num)
|
||||
} else if num < 1_000_000 {
|
||||
num2 := float32(num) / 1000.0
|
||||
return fmt.Sprintf("%.1fk", num2)
|
||||
} else if num < 1_000_000_000 {
|
||||
num2 := float32(num) / 1_000_000.0
|
||||
return fmt.Sprintf("%.1fM", num2)
|
||||
}
|
||||
num2 := float32(num) / 1_000_000_000.0
|
||||
return fmt.Sprintf("%.1fG", num2)
|
||||
}
|
18
modules/templates/util_format_test.go
Normal file
18
modules/templates/util_format_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCountFmt(t *testing.T) {
|
||||
assert.Equal(t, "125", countFmt(125))
|
||||
assert.Equal(t, "1.3k", countFmt(int64(1317)))
|
||||
assert.Equal(t, "21.3M", countFmt(21317675))
|
||||
assert.Equal(t, "45.7G", countFmt(45721317675))
|
||||
assert.Equal(t, "", countFmt("test"))
|
||||
}
|
@ -4,11 +4,16 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// RedirectURL returns the redirect URL of a http response.
|
||||
@ -41,3 +46,19 @@ func MockVariableValue[T any](p *T, v ...T) (reset func()) {
|
||||
}
|
||||
return func() { *p = old }
|
||||
}
|
||||
|
||||
// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value
|
||||
func SetupGiteaRoot() string {
|
||||
giteaRoot := os.Getenv("GITEA_ROOT")
|
||||
if giteaRoot != "" {
|
||||
return giteaRoot
|
||||
}
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
|
||||
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
|
||||
if exist, _ := util.IsDir(fixturesDir); !exist {
|
||||
panic(fmt.Sprintf("fixtures directory not found: %s", fixturesDir))
|
||||
}
|
||||
_ = os.Setenv("GITEA_ROOT", giteaRoot)
|
||||
return giteaRoot
|
||||
}
|
||||
|
17
modules/test/utils_test.go
Normal file
17
modules/test/utils_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetupGiteaRoot(t *testing.T) {
|
||||
t.Setenv("GITEA_ROOT", "test")
|
||||
assert.Equal(t, "test", SetupGiteaRoot())
|
||||
t.Setenv("GITEA_ROOT", "")
|
||||
assert.NotEqual(t, "test", SetupGiteaRoot())
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
goctx "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@ -51,7 +50,6 @@ func (r *responseWriter) WriteHeader(statusCode int) {
|
||||
var (
|
||||
httpReqType = reflect.TypeOf((*http.Request)(nil))
|
||||
respWriterType = reflect.TypeOf((*http.ResponseWriter)(nil)).Elem()
|
||||
cancelFuncType = reflect.TypeOf((*goctx.CancelFunc)(nil)).Elem()
|
||||
)
|
||||
|
||||
// preCheckHandler checks whether the handler is valid, developers could get first-time feedback, all mistakes could be found at startup
|
||||
@ -65,11 +63,8 @@ func preCheckHandler(fn reflect.Value, argsIn []reflect.Value) {
|
||||
if !hasStatusProvider {
|
||||
panic(fmt.Sprintf("handler should have at least one ResponseStatusProvider argument, but got %s", fn.Type()))
|
||||
}
|
||||
if fn.Type().NumOut() != 0 && fn.Type().NumIn() != 1 {
|
||||
panic(fmt.Sprintf("handler should have no return value or only one argument, but got %s", fn.Type()))
|
||||
}
|
||||
if fn.Type().NumOut() == 1 && fn.Type().Out(0) != cancelFuncType {
|
||||
panic(fmt.Sprintf("handler should return a cancel function, but got %s", fn.Type()))
|
||||
if fn.Type().NumOut() != 0 {
|
||||
panic(fmt.Sprintf("handler should have no return value other than registered ones, but got %s", fn.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,16 +100,10 @@ func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect
|
||||
return argsIn
|
||||
}
|
||||
|
||||
func handleResponse(fn reflect.Value, ret []reflect.Value) goctx.CancelFunc {
|
||||
if len(ret) == 1 {
|
||||
if cancelFunc, ok := ret[0].Interface().(goctx.CancelFunc); ok {
|
||||
return cancelFunc
|
||||
}
|
||||
panic(fmt.Sprintf("unsupported return type: %s", ret[0].Type()))
|
||||
} else if len(ret) > 1 {
|
||||
func handleResponse(fn reflect.Value, ret []reflect.Value) {
|
||||
if len(ret) != 0 {
|
||||
panic(fmt.Sprintf("unsupported return values: %s", fn.Type()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasResponseBeenWritten(argsIn []reflect.Value) bool {
|
||||
@ -171,11 +160,8 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||
ret := fn.Call(argsIn)
|
||||
|
||||
// handle the return value, and defer the cancel function if there is one
|
||||
cancelFunc := handleResponse(fn, ret)
|
||||
if cancelFunc != nil {
|
||||
defer cancelFunc()
|
||||
}
|
||||
// handle the return value (no-op at the moment)
|
||||
handleResponse(fn, ret)
|
||||
|
||||
// if the response has not been written, call the next handler
|
||||
if next != nil && !hasResponseBeenWritten(argsIn) {
|
||||
|
@ -7,46 +7,21 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// ContextDataStore represents a data store
|
||||
type ContextDataStore interface {
|
||||
GetData() ContextData
|
||||
}
|
||||
|
||||
type ContextData map[string]any
|
||||
|
||||
func (ds ContextData) GetData() ContextData {
|
||||
return ds
|
||||
}
|
||||
|
||||
func (ds ContextData) MergeFrom(other ContextData) ContextData {
|
||||
for k, v := range other {
|
||||
ds[k] = v
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
const ContextDataKeySignedUser = "SignedUser"
|
||||
|
||||
type contextDataKeyType struct{}
|
||||
|
||||
var contextDataKey contextDataKeyType
|
||||
|
||||
func WithContextData(c context.Context) context.Context {
|
||||
return context.WithValue(c, contextDataKey, make(ContextData, 10))
|
||||
}
|
||||
|
||||
func GetContextData(c context.Context) ContextData {
|
||||
if ds, ok := c.Value(contextDataKey).(ContextData); ok {
|
||||
return ds
|
||||
func GetContextData(c context.Context) reqctx.ContextData {
|
||||
if rc := reqctx.GetRequestDataStore(c); rc != nil {
|
||||
return rc.GetData()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CommonTemplateContextData() ContextData {
|
||||
return ContextData{
|
||||
func CommonTemplateContextData() reqctx.ContextData {
|
||||
return reqctx.ContextData{
|
||||
"IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
|
||||
|
||||
"ShowRegistrationButton": setting.Service.ShowRegistrationButton,
|
||||
|
@ -7,11 +7,13 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
)
|
||||
|
||||
// Flash represents a one time data transfer between two requests.
|
||||
type Flash struct {
|
||||
DataStore ContextDataStore
|
||||
DataStore reqctx.RequestDataStore
|
||||
url.Values
|
||||
ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
|
||||
@ -29,12 +30,12 @@ func Bind[T any](_ T) http.HandlerFunc {
|
||||
}
|
||||
|
||||
// SetForm set the form object
|
||||
func SetForm(dataStore middleware.ContextDataStore, obj any) {
|
||||
func SetForm(dataStore reqctx.ContextDataProvider, obj any) {
|
||||
dataStore.GetData()["__form"] = obj
|
||||
}
|
||||
|
||||
// GetForm returns the validate form information
|
||||
func GetForm(dataStore middleware.ContextDataStore) any {
|
||||
func GetForm(dataStore reqctx.RequestDataStore) any {
|
||||
return dataStore.GetData()["__form"]
|
||||
}
|
||||
|
||||
|
31
options/gitignore/AutomationStudio
Normal file
31
options/gitignore/AutomationStudio
Normal file
@ -0,0 +1,31 @@
|
||||
# gitignore template for B&R Automation Studio (AS) 4
|
||||
# website: https://www.br-automation.com/en-us/products/software/automation-software/automation-studio/
|
||||
|
||||
# AS temporary directories
|
||||
Binaries/
|
||||
Diagnosis/
|
||||
Temp/
|
||||
TempObjects/
|
||||
|
||||
# AS transfer files
|
||||
*artransfer.br
|
||||
*arTrsfmode.nv
|
||||
|
||||
# 'ignored' directory
|
||||
ignored/
|
||||
|
||||
# ARNC0ext
|
||||
*arnc0ext.br
|
||||
|
||||
# AS File types
|
||||
*.bak
|
||||
*.isopen
|
||||
*.orig
|
||||
*.log
|
||||
*.asar
|
||||
*.csvlog*
|
||||
*.set
|
||||
!**/Physical/**/*.set
|
||||
|
||||
# RevInfo variables
|
||||
*RevInfo.var
|
28
options/gitignore/Firebase
Normal file
28
options/gitignore/Firebase
Normal file
@ -0,0 +1,28 @@
|
||||
# Firebase build and deployment files
|
||||
/firebase-debug.log
|
||||
/firebase-debug.*.log
|
||||
.firebaserc
|
||||
|
||||
# Firebase Hosting
|
||||
/firebase.json
|
||||
*.cache
|
||||
hosting/.cache
|
||||
|
||||
# Firebase Functions
|
||||
/functions/node_modules/
|
||||
/functions/.env
|
||||
/functions/package-lock.json
|
||||
|
||||
# Firebase Emulators
|
||||
/firebase-*.zip
|
||||
/.firebase/
|
||||
/emulator-ui/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Environment files (local configs)
|
||||
/.env.*
|
42
options/gitignore/Modelica
Normal file
42
options/gitignore/Modelica
Normal file
@ -0,0 +1,42 @@
|
||||
# Modelica - an object-oriented language for modeling of cyber-physical systems
|
||||
# https://modelica.org/
|
||||
# Ignore temporary files, build results, simulation files
|
||||
|
||||
## Modelica-specific files
|
||||
*~
|
||||
*.bak
|
||||
*.bak-mo
|
||||
*.mof
|
||||
\#*\#
|
||||
*.moe
|
||||
*.mol
|
||||
|
||||
## Build artefacts
|
||||
*.exe
|
||||
*.exp
|
||||
*.o
|
||||
*.pyc
|
||||
|
||||
## Simulation files
|
||||
*.mat
|
||||
|
||||
## Package files
|
||||
*.gz
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
## Dymola-specific files
|
||||
buildlog.txt
|
||||
dsfinal.txt
|
||||
dsin.txt
|
||||
dslog.txt
|
||||
dsmodel*
|
||||
dsres.txt
|
||||
dymosim*
|
||||
request
|
||||
stat
|
||||
status
|
||||
stop
|
||||
success
|
||||
*.
|
@ -166,3 +166,6 @@ cython_debug/
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
## Bibliography auxiliary files (bibtex/biblatex/biber):
|
||||
*.bbl
|
||||
*.bbl-SAVE-ERROR
|
||||
*.bcf
|
||||
*.blg
|
||||
*-blx.aux
|
||||
|
@ -2,18 +2,18 @@ Elastic License 2.0
|
||||
|
||||
URL: https://www.elastic.co/licensing/elastic-license
|
||||
|
||||
## Acceptance
|
||||
Acceptance
|
||||
|
||||
By using the software, you agree to all of the terms and conditions below.
|
||||
|
||||
## Copyright License
|
||||
Copyright License
|
||||
|
||||
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
||||
non-sublicensable, non-transferable license to use, copy, distribute, make
|
||||
available, and prepare derivative works of the software, in each case subject to
|
||||
the limitations and conditions below.
|
||||
|
||||
## Limitations
|
||||
Limitations
|
||||
|
||||
You may not provide the software to third parties as a hosted or managed
|
||||
service, where the service provides users with access to any substantial set of
|
||||
@ -27,7 +27,7 @@ You may not alter, remove, or obscure any licensing, copyright, or other notices
|
||||
of the licensor in the software. Any use of the licensor’s trademarks is subject
|
||||
to applicable law.
|
||||
|
||||
## Patents
|
||||
Patents
|
||||
|
||||
The licensor grants you a license, under any patent claims the licensor can
|
||||
license, or becomes able to license, to make, have made, use, sell, offer for
|
||||
@ -40,7 +40,7 @@ the software granted under these terms ends immediately. If your company makes
|
||||
such a claim, your patent license ends immediately for work on behalf of your
|
||||
company.
|
||||
|
||||
## Notices
|
||||
Notices
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of the software from you
|
||||
also gets a copy of these terms.
|
||||
@ -53,7 +53,7 @@ software prominent notices stating that you have modified the software.
|
||||
These terms do not imply any licenses other than those expressly granted in
|
||||
these terms.
|
||||
|
||||
## Termination
|
||||
Termination
|
||||
|
||||
If you use the software in violation of these terms, such use is not licensed,
|
||||
and your licenses will automatically terminate. If the licensor provides you
|
||||
@ -63,31 +63,31 @@ reinstated retroactively. However, if you violate these terms after such
|
||||
reinstatement, any additional violation of these terms will cause your licenses
|
||||
to terminate automatically and permanently.
|
||||
|
||||
## No Liability
|
||||
No Liability
|
||||
|
||||
*As far as the law allows, the software comes as is, without any warranty or
|
||||
As far as the law allows, the software comes as is, without any warranty or
|
||||
condition, and the licensor will not be liable to you for any damages arising
|
||||
out of these terms or the use or nature of the software, under any kind of
|
||||
legal claim.*
|
||||
legal claim.
|
||||
|
||||
## Definitions
|
||||
Definitions
|
||||
|
||||
The **licensor** is the entity offering these terms, and the **software** is the
|
||||
The licensor is the entity offering these terms, and the software is the
|
||||
software the licensor makes available under these terms, including any portion
|
||||
of it.
|
||||
|
||||
**you** refers to the individual or entity agreeing to these terms.
|
||||
you refers to the individual or entity agreeing to these terms.
|
||||
|
||||
**your company** is any legal entity, sole proprietorship, or other kind of
|
||||
your company is any legal entity, sole proprietorship, or other kind of
|
||||
organization that you work for, plus all organizations that have control over,
|
||||
are under the control of, or are under common control with that
|
||||
organization. **control** means ownership of substantially all the assets of an
|
||||
organization. control means ownership of substantially all the assets of an
|
||||
entity, or the power to direct its management and policies by vote, contract, or
|
||||
otherwise. Control can be direct or indirect.
|
||||
|
||||
**your licenses** are all the licenses granted to you for the software under
|
||||
your licenses are all the licenses granted to you for the software under
|
||||
these terms.
|
||||
|
||||
**use** means anything you do with the software requiring one of your licenses.
|
||||
use means anything you do with the software requiring one of your licenses.
|
||||
|
||||
**trademark** means trademarks, service marks, and similar rights.
|
||||
trademark means trademarks, service marks, and similar rights.
|
||||
|
@ -2,8 +2,17 @@ MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
@ -3742,6 +3742,7 @@ variables.creation.success=Proměnná „%s“ byla přidána.
|
||||
variables.update.failed=Úprava proměnné se nezdařila.
|
||||
variables.update.success=Proměnná byla upravena.
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Odstraněný projekt
|
||||
type-1.display_name=Samostatný projekt
|
||||
|
@ -3556,6 +3556,7 @@ variables.creation.success=Die Variable „%s“ wurde hinzugefügt.
|
||||
variables.update.failed=Fehler beim Bearbeiten der Variable.
|
||||
variables.update.success=Die Variable wurde bearbeitet.
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Individuelles Projekt
|
||||
type-2.display_name=Repository-Projekt
|
||||
|
@ -3439,6 +3439,7 @@ variables.creation.success=Η μεταβλητή "%s" έχει προστεθε
|
||||
variables.update.failed=Αποτυχία επεξεργασίας μεταβλητής.
|
||||
variables.update.success=Η μεταβλητή έχει τροποποιηθεί.
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Ατομικό Έργο
|
||||
type-2.display_name=Έργο Αποθετηρίου
|
||||
|
@ -1946,8 +1946,8 @@ pulls.delete.title = Delete this pull request?
|
||||
pulls.delete.text = Do you really want to delete this pull request? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived)
|
||||
|
||||
pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1 = This branch is %d commit behind %s
|
||||
pulls.upstream_diverging_prompt_behind_n = This branch is %d commits behind %s
|
||||
pulls.upstream_diverging_prompt_behind_1 = This branch is %[1]d commit behind %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_n = This branch is %[1]d commits behind %[2]s
|
||||
pulls.upstream_diverging_prompt_base_newer = The base branch %s has new changes
|
||||
pulls.upstream_diverging_merge = Sync fork
|
||||
|
||||
@ -3722,6 +3722,7 @@ runners.status.active = Active
|
||||
runners.status.offline = Offline
|
||||
runners.version = Version
|
||||
runners.reset_registration_token = Reset registration token
|
||||
runners.reset_registration_token_confirm = Would you like to invalidate the current token and generate a new one?
|
||||
runners.reset_registration_token_success = Runner registration token reset successfully
|
||||
|
||||
runs.all_workflows = All Workflows
|
||||
@ -3773,6 +3774,9 @@ variables.creation.success = The variable "%s" has been added.
|
||||
variables.update.failed = Failed to edit variable.
|
||||
variables.update.success = The variable has been edited.
|
||||
|
||||
logs.always_auto_scroll = Always auto scroll logs
|
||||
logs.always_expand_running = Always expand running logs
|
||||
|
||||
[projects]
|
||||
deleted.display_name = Deleted Project
|
||||
type-1.display_name = Individual Project
|
||||
|
@ -3415,6 +3415,7 @@ variables.creation.success=La variable "%s" ha sido añadida.
|
||||
variables.update.failed=Error al editar la variable.
|
||||
variables.update.success=La variable ha sido editada.
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Proyecto individual
|
||||
type-2.display_name=Proyecto repositorio
|
||||
|
@ -2529,6 +2529,7 @@ runs.commit=کامیت
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -1707,6 +1707,7 @@ runs.commit=Commit
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -3772,6 +3772,7 @@ variables.creation.success=La variable « %s » a été ajoutée.
|
||||
variables.update.failed=Impossible d’éditer la variable.
|
||||
variables.update.success=La variable a bien été modifiée.
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Projet supprimé
|
||||
type-1.display_name=Projet personnel
|
||||
|
@ -1945,8 +1945,6 @@ pulls.delete.title=Scrios an t-iarratas tarraingthe seo?
|
||||
pulls.delete.text=An bhfuil tú cinnte gur mhaith leat an t-iarratas tarraingthe seo a scriosadh? (Bainfidh sé seo an t-inneachar go léir go buan. Smaoinigh ar é a dhúnadh ina ionad sin, má tá sé i gceist agat é a choinneáil i gcartlann)
|
||||
|
||||
pulls.recently_pushed_new_branches=Bhrúigh tú ar bhrainse <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=Tá an brainse seo %d tiomantas taobh thiar de %s
|
||||
pulls.upstream_diverging_prompt_behind_n=Tá an brainse seo %d geallta taobh thiar de %s
|
||||
pulls.upstream_diverging_prompt_base_newer=Tá athruithe nua ar an mbunbhrainse %s
|
||||
pulls.upstream_diverging_merge=Forc sionc
|
||||
|
||||
@ -3772,6 +3770,7 @@ variables.creation.success=Tá an athróg "%s" curtha leis.
|
||||
variables.update.failed=Theip ar athróg a chur in eagar.
|
||||
variables.update.success=Tá an t-athróg curtha in eagar.
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Tionscadal scriosta
|
||||
type-1.display_name=Tionscadal Aonair
|
||||
|
@ -1615,6 +1615,7 @@ runs.commit=Commit
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -1444,6 +1444,7 @@ variables.creation.success=Variabel "%s" telah ditambahkan.
|
||||
variables.update.failed=Gagal mengedit variabel.
|
||||
variables.update.success=Variabel telah diedit.
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Proyek Individu
|
||||
type-2.display_name=Proyek Repositori
|
||||
|
@ -1342,6 +1342,7 @@ runs.commit=Framlag
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -2807,6 +2807,7 @@ runs.commit=Commit
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -1940,8 +1940,6 @@ pulls.delete.title=このプルリクエストを削除しますか?
|
||||
pulls.delete.text=本当にこのプルリクエストを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください)
|
||||
|
||||
pulls.recently_pushed_new_branches=%[2]s 、あなたはブランチ <strong>%[1]s</strong> にプッシュしました
|
||||
pulls.upstream_diverging_prompt_behind_1=このブランチは %[2]s よりも %[1]d コミット遅れています
|
||||
pulls.upstream_diverging_prompt_behind_n=このブランチは %[2]s よりも %[1]d コミット遅れています
|
||||
pulls.upstream_diverging_prompt_base_newer=ベースブランチ %s に新しい変更があります
|
||||
pulls.upstream_diverging_merge=フォークを同期
|
||||
|
||||
@ -3764,6 +3762,7 @@ variables.creation.success=変数 "%s" を追加しました。
|
||||
variables.update.failed=変数を更新できませんでした。
|
||||
variables.update.success=変数を更新しました。
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=削除されたプロジェクト
|
||||
type-1.display_name=個人プロジェクト
|
||||
|
@ -1563,6 +1563,7 @@ runs.commit=커밋
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -3443,6 +3443,7 @@ variables.creation.success=Mainīgais "%s" tika pievienots.
|
||||
variables.update.failed=Neizdevās labot mainīgo.
|
||||
variables.update.success=Mainīgais tika labots.
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Individuālais projekts
|
||||
type-2.display_name=Repozitorija projekts
|
||||
|
@ -2537,6 +2537,7 @@ runs.commit=Commit
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -2430,6 +2430,7 @@ runs.commit=Commit
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -3353,6 +3353,7 @@ runs.empty_commit_message=(mensagem de commit vazia)
|
||||
need_approval_desc=Precisa de aprovação para executar workflows para pull request do fork.
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Projeto individual
|
||||
type-2.display_name=Projeto do repositório
|
||||
|
@ -1945,8 +1945,6 @@ pulls.delete.title=Eliminar este pedido de integração?
|
||||
pulls.delete.text=Tem a certeza que quer eliminar este pedido de integração? Isso irá remover todo o conteúdo permanentemente. Como alternativa considere fechá-lo, se pretender mantê-lo em arquivo.
|
||||
|
||||
pulls.recently_pushed_new_branches=Enviou para o ramo <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=Este ramo está %d cometimento atrás de %s
|
||||
pulls.upstream_diverging_prompt_behind_n=Este ramo está %d cometimentos atrás de %s
|
||||
pulls.upstream_diverging_prompt_base_newer=O ramo base %s tem novas modificações
|
||||
pulls.upstream_diverging_merge=Sincronizar derivação
|
||||
|
||||
@ -3772,6 +3770,9 @@ variables.creation.success=A variável "%s" foi adicionada.
|
||||
variables.update.failed=Falha ao editar a variável.
|
||||
variables.update.success=A variável foi editada.
|
||||
|
||||
logs.always_auto_scroll=Rolar registos de forma automática e permanente
|
||||
logs.always_expand_running=Expandir sempre os registos que vão rolando
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Planeamento eliminado
|
||||
type-1.display_name=Planeamento individual
|
||||
|
@ -3373,6 +3373,7 @@ variables.creation.success=Переменная «%s» добавлена.
|
||||
variables.update.failed=Не удалось изменить переменную.
|
||||
variables.update.success=Переменная изменена.
|
||||
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Индивидуальный проект
|
||||
type-2.display_name=Проект репозитория
|
||||
|
@ -2470,6 +2470,7 @@ runs.commit=කැප
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -1328,6 +1328,7 @@ runners.labels=Štítky
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -2005,6 +2005,7 @@ runs.commit=Commit
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -3633,6 +3633,7 @@ variables.creation.success=`"%s" değişkeni eklendi.`
|
||||
variables.update.failed=Değişken düzenlenemedi.
|
||||
variables.update.success=Değişken düzenlendi.
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Silinmiş Proje
|
||||
type-1.display_name=Kişisel Proje
|
||||
|
@ -2538,6 +2538,7 @@ runs.commit=Коміт
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -1945,8 +1945,8 @@ pulls.delete.title=删除此合并请求?
|
||||
pulls.delete.text=你真的要删除这个合并请求吗? (这将永久删除所有内容。如果你打算将内容存档,请考虑关闭它)
|
||||
|
||||
pulls.recently_pushed_new_branches=您已经于%[2]s推送了分支 <strong>%[1]s</strong>
|
||||
pulls.upstream_diverging_prompt_behind_1=该分支落后于 %s %d 个提交
|
||||
pulls.upstream_diverging_prompt_behind_n=该分支落后于 %s %d 个提交
|
||||
pulls.upstream_diverging_prompt_behind_1=该分支落后于 %[2]s %[1]d 个提交
|
||||
pulls.upstream_diverging_prompt_behind_n=该分支落后于 %[2]s %[1]d 个提交
|
||||
pulls.upstream_diverging_prompt_base_newer=基础分支 %s 有新的更改
|
||||
pulls.upstream_diverging_merge=同步派生
|
||||
|
||||
@ -3772,6 +3772,7 @@ variables.creation.success=变量 “%s” 添加成功。
|
||||
variables.update.failed=编辑变量失败。
|
||||
variables.update.success=该变量已被编辑。
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=已删除项目
|
||||
type-1.display_name=个人项目
|
||||
|
@ -975,6 +975,7 @@ runners.task_list.repository=儲存庫
|
||||
|
||||
|
||||
|
||||
|
||||
[projects]
|
||||
|
||||
[git.filemode]
|
||||
|
@ -1938,8 +1938,6 @@ pulls.delete.title=刪除此合併請求?
|
||||
pulls.delete.text=您真的要刪除此合併請求嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。)
|
||||
|
||||
pulls.recently_pushed_new_branches=您在分支 <strong>%[1]s</strong> 上推送了 %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=此分支落後 %s %d 次提交
|
||||
pulls.upstream_diverging_prompt_behind_n=此分支落後 %s %d 次提交
|
||||
pulls.upstream_diverging_prompt_base_newer=基底分支 %s 有新變更
|
||||
pulls.upstream_diverging_merge=同步 fork
|
||||
|
||||
@ -3765,6 +3763,7 @@ variables.creation.success=已新增變數「%s」。
|
||||
variables.update.failed=編輯變數失敗。
|
||||
variables.update.success=已編輯變數。
|
||||
|
||||
|
||||
[projects]
|
||||
deleted.display_name=已刪除的專案
|
||||
type-1.display_name=個人專案
|
||||
|
@ -126,11 +126,10 @@ func ArtifactsRoutes(prefix string) *web.Router {
|
||||
func ArtifactContexter() func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := context.NewBaseContext(resp, req)
|
||||
defer baseCleanUp()
|
||||
base := context.NewBaseContext(resp, req)
|
||||
|
||||
ctx := &ArtifactContext{Base: base}
|
||||
ctx.AppendContextValue(artifactContextKey, ctx)
|
||||
ctx.SetContextValue(artifactContextKey, ctx)
|
||||
|
||||
// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
|
||||
// we should verify the ACTIONS_RUNTIME_TOKEN
|
||||
|
@ -126,12 +126,9 @@ type artifactV4Routes struct {
|
||||
func ArtifactV4Contexter() func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := context.NewBaseContext(resp, req)
|
||||
defer baseCleanUp()
|
||||
|
||||
base := context.NewBaseContext(resp, req)
|
||||
ctx := &ArtifactContext{Base: base}
|
||||
ctx.AppendContextValue(artifactContextKey, ctx)
|
||||
|
||||
ctx.SetContextValue(artifactContextKey, ctx)
|
||||
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||
})
|
||||
}
|
||||
|
@ -80,8 +80,8 @@ func AdoptRepository(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
ownerName := ctx.PathParam(":username")
|
||||
repoName := ctx.PathParam(":reponame")
|
||||
ownerName := ctx.PathParam("username")
|
||||
repoName := ctx.PathParam("reponame")
|
||||
|
||||
ctxUser, err := user_model.GetUserByName(ctx, ownerName)
|
||||
if err != nil {
|
||||
@ -142,8 +142,8 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
ownerName := ctx.PathParam(":username")
|
||||
repoName := ctx.PathParam(":reponame")
|
||||
ownerName := ctx.PathParam("username")
|
||||
repoName := ctx.PathParam("reponame")
|
||||
|
||||
ctxUser, err := user_model.GetUserByName(ctx, ownerName)
|
||||
if err != nil {
|
||||
|
@ -74,7 +74,7 @@ func PostCronTask(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
task := cron.GetTask(ctx.PathParam(":task"))
|
||||
task := cron.GetTask(ctx.PathParam("task"))
|
||||
if task == nil {
|
||||
ctx.NotFound()
|
||||
return
|
||||
|
@ -38,7 +38,7 @@ func GetAllEmails(ctx *context.APIContext) {
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
emails, maxResults, err := user_model.SearchEmails(ctx, &user_model.SearchEmailOptions{
|
||||
Keyword: ctx.PathParam(":email"),
|
||||
Keyword: ctx.PathParam("email"),
|
||||
ListOptions: listOptions,
|
||||
})
|
||||
if err != nil {
|
||||
@ -82,6 +82,6 @@ func SearchEmail(ctx *context.APIContext) {
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
ctx.SetPathParam(":email", ctx.FormTrim("q"))
|
||||
ctx.SetPathParam("email", ctx.FormTrim("q"))
|
||||
GetAllEmails(ctx)
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ func GetHook(ctx *context.APIContext) {
|
||||
// "200":
|
||||
// "$ref": "#/responses/Hook"
|
||||
|
||||
hookID := ctx.PathParamInt64(":id")
|
||||
hookID := ctx.PathParamInt64("id")
|
||||
hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
@ -142,7 +142,7 @@ func EditHook(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*api.EditHookOption)
|
||||
|
||||
// TODO in body params
|
||||
hookID := ctx.PathParamInt64(":id")
|
||||
hookID := ctx.PathParamInt64("id")
|
||||
utils.EditSystemHook(ctx, form, hookID)
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ func DeleteHook(ctx *context.APIContext) {
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
hookID := ctx.PathParamInt64(":id")
|
||||
hookID := ctx.PathParamInt64("id")
|
||||
if err := webhook.DeleteDefaultSystemWebhook(ctx, hookID); err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound()
|
||||
|
@ -375,7 +375,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if err := asymkey_service.DeletePublicKey(ctx, ctx.ContextUser, ctx.PathParamInt64(":id")); err != nil {
|
||||
if err := asymkey_service.DeletePublicKey(ctx, ctx.ContextUser, ctx.PathParamInt64("id")); err != nil {
|
||||
if asymkey_model.IsErrKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else if asymkey_model.IsErrKeyAccessDenied(err) {
|
||||
|
@ -596,12 +596,12 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
|
||||
|
||||
var err error
|
||||
if assignOrg {
|
||||
ctx.Org.Organization, err = organization.GetOrgByName(ctx, ctx.PathParam(":org"))
|
||||
ctx.Org.Organization, err = organization.GetOrgByName(ctx, ctx.PathParam("org"))
|
||||
if err != nil {
|
||||
if organization.IsErrOrgNotExist(err) {
|
||||
redirectUserID, err := user_model.LookupUserRedirect(ctx, ctx.PathParam(":org"))
|
||||
redirectUserID, err := user_model.LookupUserRedirect(ctx, ctx.PathParam("org"))
|
||||
if err == nil {
|
||||
context.RedirectToUser(ctx.Base, ctx.PathParam(":org"), redirectUserID)
|
||||
context.RedirectToUser(ctx.Base, ctx.PathParam("org"), redirectUserID)
|
||||
} else if user_model.IsErrUserRedirectNotExist(err) {
|
||||
ctx.NotFound("GetOrgByName", err)
|
||||
} else {
|
||||
@ -616,7 +616,7 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if assignTeam {
|
||||
ctx.Org.Team, err = organization.GetTeamByID(ctx, ctx.PathParamInt64(":teamid"))
|
||||
ctx.Org.Team, err = organization.GetTeamByID(ctx, ctx.PathParamInt64("teamid"))
|
||||
if err != nil {
|
||||
if organization.IsErrTeamNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
@ -101,7 +101,7 @@ func ReadThread(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func getThread(ctx *context.APIContext) *activities_model.Notification {
|
||||
n, err := activities_model.GetNotificationByID(ctx, ctx.PathParamInt64(":id"))
|
||||
n, err := activities_model.GetNotificationByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if db.IsErrNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "GetNotificationByID", err)
|
||||
|
@ -139,7 +139,7 @@ func GetLabel(ctx *context.APIContext) {
|
||||
label *issues_model.Label
|
||||
err error
|
||||
)
|
||||
strID := ctx.PathParam(":id")
|
||||
strID := ctx.PathParam("id")
|
||||
if intID, err2 := strconv.ParseInt(strID, 10, 64); err2 != nil {
|
||||
label, err = issues_model.GetLabelInOrgByName(ctx, ctx.Org.Organization.ID, strID)
|
||||
} else {
|
||||
@ -190,7 +190,7 @@ func EditLabel(ctx *context.APIContext) {
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
form := web.GetForm(ctx).(*api.EditLabelOption)
|
||||
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64(":id"))
|
||||
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrOrgLabelNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -249,7 +249,7 @@ func DeleteLabel(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if err := issues_model.DeleteLabel(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64(":id")); err != nil {
|
||||
if err := issues_model.DeleteLabel(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id")); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteLabel", err)
|
||||
return
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ func IsMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// description: user is not a member
|
||||
|
||||
userToCheck := user.GetUserByParams(ctx)
|
||||
userToCheck := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -194,7 +194,7 @@ func IsPublicMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// description: user is not a public member
|
||||
|
||||
userToCheck := user.GetUserByParams(ctx)
|
||||
userToCheck := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -236,7 +236,7 @@ func PublicizeMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
userToPublicize := user.GetUserByParams(ctx)
|
||||
userToPublicize := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -278,7 +278,7 @@ func ConcealMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
userToConceal := user.GetUserByParams(ctx)
|
||||
userToConceal := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -318,7 +318,7 @@ func DeleteMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
member := user.GetUserByParams(ctx)
|
||||
member := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
var o *user_model.User
|
||||
if o = user.GetUserByParamsName(ctx, ":org"); o == nil {
|
||||
if o = user.GetUserByPathParam(ctx, "org"); o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -449,7 +449,7 @@ func GetTeamMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
u := user.GetUserByParams(ctx)
|
||||
u := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -492,7 +492,7 @@ func AddTeamMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
u := user.GetUserByParams(ctx)
|
||||
u := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -532,7 +532,7 @@ func RemoveTeamMember(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
u := user.GetUserByParams(ctx)
|
||||
u := user.GetContextUserByPathParam(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@ -645,7 +645,7 @@ func GetTeamRepo(ctx *context.APIContext) {
|
||||
|
||||
// getRepositoryByParams get repository by a team's organization ID and repo name
|
||||
func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository {
|
||||
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam(":reponame"))
|
||||
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam("reponame"))
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
@ -487,7 +487,7 @@ func GetBranchProtection(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
bpName := ctx.PathParam(":name")
|
||||
bpName := ctx.PathParam("name")
|
||||
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
|
||||
@ -729,15 +729,11 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
} else {
|
||||
if !isPlainRule {
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
ctx.Repo.GitRepo = nil
|
||||
}()
|
||||
}
|
||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, ruleName)
|
||||
@ -809,7 +805,7 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/repoArchivedError"
|
||||
form := web.GetForm(ctx).(*api.EditBranchProtectionOption)
|
||||
repo := ctx.Repo.Repository
|
||||
bpName := ctx.PathParam(":name")
|
||||
bpName := ctx.PathParam("name")
|
||||
protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
|
||||
@ -1061,15 +1057,11 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
} else {
|
||||
if !isPlainRule {
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
ctx.Repo.GitRepo = nil
|
||||
}()
|
||||
}
|
||||
|
||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||
@ -1132,7 +1124,7 @@ func DeleteBranchProtection(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
bpName := ctx.PathParam(":name")
|
||||
bpName := ctx.PathParam("name")
|
||||
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
|
||||
|
@ -103,7 +103,7 @@ func IsCollaborator(ctx *context.APIContext) {
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
user, err := user_model.GetUserByName(ctx, ctx.PathParam(":collaborator"))
|
||||
user, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
@ -163,7 +163,7 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) {
|
||||
|
||||
form := web.GetForm(ctx).(*api.AddCollaboratorOption)
|
||||
|
||||
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam(":collaborator"))
|
||||
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
@ -226,7 +226,7 @@ func DeleteCollaborator(ctx *context.APIContext) {
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam(":collaborator"))
|
||||
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
@ -274,12 +274,12 @@ func GetRepoPermissions(ctx *context.APIContext) {
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.PathParam(":collaborator") && !ctx.IsUserRepoAdmin() {
|
||||
if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.PathParam("collaborator") && !ctx.IsUserRepoAdmin() {
|
||||
ctx.Error(http.StatusForbidden, "User", "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
|
||||
return
|
||||
}
|
||||
|
||||
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam(":collaborator"))
|
||||
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "GetUserByName", err)
|
||||
|
@ -63,7 +63,7 @@ func GetSingleCommit(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
sha := ctx.PathParam(":sha")
|
||||
sha := ctx.PathParam("sha")
|
||||
if !git.IsValidRefPattern(sha) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha))
|
||||
return
|
||||
@ -312,8 +312,8 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/string"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
sha := ctx.PathParam(":sha")
|
||||
diffType := git.RawDiffType(ctx.PathParam(":diffType"))
|
||||
sha := ctx.PathParam("sha")
|
||||
diffType := git.RawDiffType(ctx.PathParam("diffType"))
|
||||
|
||||
if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, diffType, ctx.Resp); err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
|
@ -44,13 +44,12 @@ func CompareDiff(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
var err error
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
defer gitRepo.Close()
|
||||
}
|
||||
|
||||
infoPath := ctx.PathParam("*")
|
||||
|
@ -28,13 +28,12 @@ func DownloadArchive(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
var err error
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
defer gitRepo.Close()
|
||||
}
|
||||
|
||||
r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"), tp)
|
||||
|
@ -287,13 +287,12 @@ func GetArchive(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
var err error
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
defer gitRepo.Close()
|
||||
}
|
||||
|
||||
archiveDownload(ctx)
|
||||
|
@ -79,7 +79,7 @@ func GetGitHook(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
hookID := ctx.PathParam(":id")
|
||||
hookID := ctx.PathParam("id")
|
||||
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
|
||||
if err != nil {
|
||||
if err == git.ErrNotValidHook {
|
||||
@ -126,7 +126,7 @@ func EditGitHook(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
form := web.GetForm(ctx).(*api.EditGitHookOption)
|
||||
hookID := ctx.PathParam(":id")
|
||||
hookID := ctx.PathParam("id")
|
||||
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
|
||||
if err != nil {
|
||||
if err == git.ErrNotValidHook {
|
||||
@ -175,7 +175,7 @@ func DeleteGitHook(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
hookID := ctx.PathParam(":id")
|
||||
hookID := ctx.PathParam("id")
|
||||
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
|
||||
if err != nil {
|
||||
if err == git.ErrNotValidHook {
|
||||
|
@ -109,7 +109,7 @@ func GetHook(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repo := ctx.Repo
|
||||
hookID := ctx.PathParamInt64(":id")
|
||||
hookID := ctx.PathParamInt64("id")
|
||||
hook, err := utils.GetRepoHook(ctx, repo.Repository.ID, hookID)
|
||||
if err != nil {
|
||||
return
|
||||
@ -168,7 +168,7 @@ func TestHook(ctx *context.APIContext) {
|
||||
ref = r
|
||||
}
|
||||
|
||||
hookID := ctx.PathParamInt64(":id")
|
||||
hookID := ctx.PathParamInt64("id")
|
||||
hook, err := utils.GetRepoHook(ctx, ctx.Repo.Repository.ID, hookID)
|
||||
if err != nil {
|
||||
return
|
||||
@ -263,7 +263,7 @@ func EditHook(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
form := web.GetForm(ctx).(*api.EditHookOption)
|
||||
hookID := ctx.PathParamInt64(":id")
|
||||
hookID := ctx.PathParamInt64("id")
|
||||
utils.EditRepoHook(ctx, form, hookID)
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ func DeleteHook(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":id")); err != nil {
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id")); err != nil {
|
||||
if webhook.IsErrWebhookNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
|
@ -18,7 +18,7 @@ func TestTestHook(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
|
||||
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/wiki/_pages")
|
||||
ctx.SetPathParam(":id", "1")
|
||||
ctx.SetPathParam("id", "1")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
|
@ -613,7 +613,7 @@ func GetIssue(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -793,7 +793,7 @@ func EditIssue(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/error"
|
||||
|
||||
form := web.GetForm(ctx).(*api.EditIssueOption)
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -976,7 +976,7 @@ func DeleteIssue(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@ -1032,7 +1032,7 @@ func UpdateIssueDeadline(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
form := web.GetForm(ctx).(*api.EditDeadlineOption)
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
@ -68,7 +68,7 @@ func ListIssueComments(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
|
||||
return
|
||||
@ -172,7 +172,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
|
||||
return
|
||||
@ -380,7 +380,7 @@ func CreateIssueComment(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/repoArchivedError"
|
||||
|
||||
form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
return
|
||||
@ -445,7 +445,7 @@ func GetIssueComment(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64(":id"))
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@ -579,7 +579,7 @@ func EditIssueCommentDeprecated(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64(":id"))
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@ -696,7 +696,7 @@ func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func deleteIssueComment(ctx *context.APIContext) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64(":id"))
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
|
@ -61,7 +61,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound("IsErrIssueNotExist", err)
|
||||
@ -499,7 +499,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound("IsErrIssueNotExist", err)
|
||||
|
@ -47,7 +47,7 @@ func ListIssueLabels(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -163,7 +163,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -178,7 +178,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
label, err := issues_model.GetLabelByID(ctx, ctx.PathParamInt64(":id"))
|
||||
label, err := issues_model.GetLabelByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrLabelNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
@ -285,7 +285,7 @@ func ClearIssueLabels(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -309,7 +309,7 @@ func ClearIssueLabels(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (*issues_model.Issue, []*issues_model.Label, error) {
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
@ -41,7 +41,7 @@ func PinIssue(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -98,7 +98,7 @@ func UnpinIssue(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -159,7 +159,7 @@ func MoveIssuePin(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -169,7 +169,7 @@ func MoveIssuePin(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
err = issue.MovePin(ctx, int(ctx.PathParamInt64(":position")))
|
||||
err = issue.MovePin(ctx, int(ctx.PathParamInt64("position")))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "MovePin", err)
|
||||
return
|
||||
|
@ -51,7 +51,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64(":id"))
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@ -188,7 +188,7 @@ func DeleteIssueCommentReaction(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64(":id"))
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@ -295,7 +295,7 @@ func GetIssueReactions(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@ -419,7 +419,7 @@ func DeleteIssueReaction(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
||||
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
@ -161,7 +161,7 @@ func DeleteIssueStopwatch(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func prepareIssueStopwatch(ctx *context.APIContext, shouldExist bool) (*issues_model.Issue, error) {
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user