mirror of
https://github.com/go-gitea/gitea.git
synced 2025-02-02 15:09:33 -05:00
Merge branch 'go-gitea:main' into telackey/sort
This commit is contained in:
commit
6feba525f1
@ -88,7 +88,7 @@ func (run *ActionRun) RefLink() string {
|
||||
if refName.IsPull() {
|
||||
return run.Repo.Link() + "/pulls/" + refName.ShortName()
|
||||
}
|
||||
return git.RefURL(run.Repo.Link(), run.Ref)
|
||||
return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
|
||||
}
|
||||
|
||||
// PrettyRef return #id for pull ref or ShortName for others
|
||||
|
@ -355,7 +355,7 @@ func (a *Action) GetBranch() string {
|
||||
|
||||
// GetRefLink returns the action's ref link.
|
||||
func (a *Action) GetRefLink(ctx context.Context) string {
|
||||
return git.RefURL(a.GetRepoLink(ctx), a.RefName)
|
||||
return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
|
||||
}
|
||||
|
||||
// GetTag returns the action's repository tag.
|
||||
|
@ -22,6 +22,7 @@
|
||||
content_type: 1 # json
|
||||
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
|
||||
is_active: true
|
||||
|
||||
-
|
||||
id: 4
|
||||
repo_id: 2
|
||||
@ -29,3 +30,23 @@
|
||||
content_type: 1 # json
|
||||
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
|
||||
is_active: true
|
||||
|
||||
-
|
||||
id: 5
|
||||
repo_id: 0
|
||||
owner_id: 0
|
||||
url: www.example.com/url5
|
||||
content_type: 1 # json
|
||||
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
|
||||
is_active: true
|
||||
is_system_webhook: true
|
||||
|
||||
-
|
||||
id: 6
|
||||
repo_id: 0
|
||||
owner_id: 0
|
||||
url: www.example.com/url6
|
||||
content_type: 1 # json
|
||||
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
|
||||
is_active: true
|
||||
is_system_webhook: false
|
||||
|
@ -112,8 +112,8 @@ const (
|
||||
CommentTypePRScheduledToAutoMerge // 34 pr was scheduled to auto merge when checks succeed
|
||||
CommentTypePRUnScheduledToAutoMerge // 35 pr was un scheduled to auto merge when checks succeed
|
||||
|
||||
CommentTypePin // 36 pin Issue
|
||||
CommentTypeUnpin // 37 unpin Issue
|
||||
CommentTypePin // 36 pin Issue/PullRequest
|
||||
CommentTypeUnpin // 37 unpin Issue/PullRequest
|
||||
|
||||
CommentTypeChangeTimeEstimate // 38 Change time estimate
|
||||
)
|
||||
|
@ -175,10 +175,14 @@ func (p *Permission) LogString() string {
|
||||
return fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
||||
func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
|
||||
if user == nil || user.ID <= 0 {
|
||||
// for anonymous access, it could be:
|
||||
// AccessMode is None or Read, units has repo units, unitModes is nil
|
||||
return
|
||||
}
|
||||
|
||||
// apply everyone access permissions
|
||||
for _, u := range perm.units {
|
||||
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
|
||||
if perm.everyoneAccessMode == nil {
|
||||
@ -187,17 +191,40 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
||||
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
|
||||
}
|
||||
}
|
||||
|
||||
if perm.unitsMode == nil {
|
||||
// if unitsMode is not set, then it means that the default p.AccessMode applies to all units
|
||||
return
|
||||
}
|
||||
|
||||
// remove no permission units
|
||||
origPermUnits := perm.units
|
||||
perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
|
||||
for _, u := range origPermUnits {
|
||||
shouldKeep := false
|
||||
for t := range perm.unitsMode {
|
||||
if shouldKeep = u.Type == t; shouldKeep {
|
||||
break
|
||||
}
|
||||
}
|
||||
for t := range perm.everyoneAccessMode {
|
||||
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
|
||||
break
|
||||
}
|
||||
}
|
||||
if shouldKeep {
|
||||
perm.units = append(perm.units, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserRepoPermission returns the user permissions to the repository
|
||||
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
applyEveryoneRepoPermission(user, &perm)
|
||||
}
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
|
||||
finalProcessRepoUnitPermission(user, &perm)
|
||||
}
|
||||
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
|
||||
}()
|
||||
|
||||
if err = repo.LoadUnits(ctx); err != nil {
|
||||
@ -294,16 +321,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
||||
}
|
||||
}
|
||||
|
||||
// remove no permission units
|
||||
perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
|
||||
for t := range perm.unitsMode {
|
||||
for _, u := range repo.Units {
|
||||
if u.Type == t {
|
||||
perm.units = append(perm.units, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return perm, err
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(nil, &perm)
|
||||
finalProcessRepoUnitPermission(nil, &perm)
|
||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
@ -59,7 +59,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 0}, &perm)
|
||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
@ -68,7 +68,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
|
||||
assert.True(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
@ -77,20 +77,22 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
|
||||
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
|
||||
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeCode}, // will be removed
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{
|
||||
unit.TypeWiki: perm_model.AccessModeWrite,
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
|
||||
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||
assert.Len(t, perm.units, 1)
|
||||
}
|
||||
|
||||
func TestUnitAccessMode(t *testing.T) {
|
||||
|
@ -56,16 +56,11 @@ func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) {
|
||||
if err != nil {
|
||||
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
|
||||
}
|
||||
nameExts := strings.SplitN(parts[2], ".", 2)
|
||||
if len(nameExts) != 2 {
|
||||
commitID, archiveType := git.SplitArchiveNameType(parts[2])
|
||||
if archiveType == git.ArchiveUnknown {
|
||||
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
|
||||
}
|
||||
|
||||
return &RepoArchiver{
|
||||
RepoID: repoID,
|
||||
CommitID: parts[1] + nameExts[0],
|
||||
Type: git.ToArchiveType(nameExts[1]),
|
||||
}, nil
|
||||
return &RepoArchiver{RepoID: repoID, CommitID: commitID, Type: archiveType}, nil
|
||||
}
|
||||
|
||||
// GetRepoArchiver get an archiver
|
||||
|
@ -54,6 +54,7 @@ func UpdateRepoLicenses(ctx context.Context, repo *Repository, commitID string,
|
||||
for _, o := range oldLicenses {
|
||||
// Update already existing license
|
||||
if o.License == license {
|
||||
o.CommitID = commitID
|
||||
if _, err := db.GetEngine(ctx).ID(o.ID).Cols("`commit_id`").Update(o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -11,35 +11,13 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Copy copies file from source to target path.
|
||||
func Copy(src, dest string) error {
|
||||
// Gather file information to set back later.
|
||||
si, err := os.Lstat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle symbolic link.
|
||||
if si.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
|
||||
// which will lead "no such file or directory" error.
|
||||
return os.Symlink(target, dest)
|
||||
}
|
||||
|
||||
return util.CopyFile(src, dest)
|
||||
}
|
||||
|
||||
// Sync synchronizes the two files. This is skipped if both files
|
||||
// SyncFile synchronizes the two files. This is skipped if both files
|
||||
// exist and the size, modtime, and mode match.
|
||||
func Sync(srcPath, destPath string) error {
|
||||
func SyncFile(srcPath, destPath string) error {
|
||||
dest, err := os.Stat(destPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return Copy(srcPath, destPath)
|
||||
return util.CopyFile(srcPath, destPath)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -55,7 +33,7 @@ func Sync(srcPath, destPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Copy(srcPath, destPath)
|
||||
return util.CopyFile(srcPath, destPath)
|
||||
}
|
||||
|
||||
// SyncDirs synchronizes files recursively from source to target directory.
|
||||
@ -66,6 +44,10 @@ func SyncDirs(srcPath, destPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// the keep file is used to keep the directory in a git repository, it doesn't need to be synced
|
||||
// and go-git doesn't work with the ".keep" file (it would report errors like "ref is empty")
|
||||
const keepFile = ".keep"
|
||||
|
||||
// find and delete all untracked files
|
||||
destFiles, err := util.ListDirRecursively(destPath, &util.ListDirOptions{IncludeDir: true})
|
||||
if err != nil {
|
||||
@ -73,16 +55,20 @@ func SyncDirs(srcPath, destPath string) error {
|
||||
}
|
||||
for _, destFile := range destFiles {
|
||||
destFilePath := filepath.Join(destPath, destFile)
|
||||
shouldRemove := filepath.Base(destFilePath) == keepFile
|
||||
if _, err = os.Stat(filepath.Join(srcPath, destFile)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// if src file does not exist, remove dest file
|
||||
if err = os.RemoveAll(destFilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
shouldRemove = true
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// if src file does not exist, remove dest file
|
||||
if shouldRemove {
|
||||
if err = os.RemoveAll(destFilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sync src files to dest
|
||||
@ -95,8 +81,8 @@ func SyncDirs(srcPath, destPath string) error {
|
||||
// util.ListDirRecursively appends a slash to the directory name
|
||||
if strings.HasSuffix(srcFile, "/") {
|
||||
err = os.MkdirAll(destFilePath, os.ModePerm)
|
||||
} else {
|
||||
err = Sync(filepath.Join(srcPath, srcFile), destFilePath)
|
||||
} else if filepath.Base(destFilePath) != keepFile {
|
||||
err = SyncFile(filepath.Join(srcPath, srcFile), destFilePath)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -11,6 +11,19 @@ import (
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
)
|
||||
|
||||
// GetSystemOrDefaultWebhooks returns webhooks by given argument or all if argument is missing.
|
||||
func GetSystemOrDefaultWebhooks(ctx context.Context, isSystemWebhook optional.Option[bool]) ([]*Webhook, error) {
|
||||
webhooks := make([]*Webhook, 0, 5)
|
||||
if !isSystemWebhook.Has() {
|
||||
return webhooks, db.GetEngine(ctx).Where("repo_id=? AND owner_id=?", 0, 0).
|
||||
Find(&webhooks)
|
||||
}
|
||||
|
||||
return webhooks, db.GetEngine(ctx).
|
||||
Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook.Value()).
|
||||
Find(&webhooks)
|
||||
}
|
||||
|
||||
// GetDefaultWebhooks returns all admin-default webhooks.
|
||||
func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
|
||||
webhooks := make([]*Webhook, 0, 5)
|
||||
|
37
models/webhook/webhook_system_test.go
Normal file
37
models/webhook/webhook_system_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetSystemOrDefaultWebhooks(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
hooks, err := GetSystemOrDefaultWebhooks(db.DefaultContext, optional.None[bool]())
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, hooks, 2) {
|
||||
assert.Equal(t, int64(5), hooks[0].ID)
|
||||
assert.Equal(t, int64(6), hooks[1].ID)
|
||||
}
|
||||
|
||||
hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(true))
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, hooks, 1) {
|
||||
assert.Equal(t, int64(5), hooks[0].ID)
|
||||
}
|
||||
|
||||
hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(false))
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, hooks, 1) {
|
||||
assert.Equal(t, int64(6), hooks[0].ID)
|
||||
}
|
||||
}
|
9
modules/cache/cache.go
vendored
9
modules/cache/cache.go
vendored
@ -37,10 +37,15 @@ func Init() error {
|
||||
}
|
||||
|
||||
const (
|
||||
testCacheKey = "DefaultCache.TestKey"
|
||||
SlowCacheThreshold = 100 * time.Microsecond
|
||||
testCacheKey = "DefaultCache.TestKey"
|
||||
// SlowCacheThreshold marks cache tests as slow
|
||||
// set to 30ms per discussion: https://github.com/go-gitea/gitea/issues/33190
|
||||
// TODO: Replace with metrics histogram
|
||||
SlowCacheThreshold = 30 * time.Millisecond
|
||||
)
|
||||
|
||||
// Test performs delete, put and get operations on a predefined key
|
||||
// returns
|
||||
func Test() (time.Duration, error) {
|
||||
if defaultCache == nil {
|
||||
return 0, fmt.Errorf("default cache not initialized")
|
||||
|
3
modules/cache/cache_test.go
vendored
3
modules/cache/cache_test.go
vendored
@ -43,7 +43,8 @@ func TestTest(t *testing.T) {
|
||||
elapsed, err := Test()
|
||||
assert.NoError(t, err)
|
||||
// mem cache should take from 300ns up to 1ms on modern hardware ...
|
||||
assert.Less(t, elapsed, time.Millisecond)
|
||||
assert.Positive(t, elapsed)
|
||||
assert.Less(t, elapsed, SlowCacheThreshold)
|
||||
}
|
||||
|
||||
func TestGetCache(t *testing.T) {
|
||||
|
@ -476,8 +476,12 @@ func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSetting
|
||||
}
|
||||
|
||||
func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool {
|
||||
minLen := util.OptionalArg(minLength, objFmt.FullLength())
|
||||
if len(s) < minLen || len(s) > objFmt.FullLength() {
|
||||
maxLen := 64 // sha256
|
||||
if objFmt != nil {
|
||||
maxLen = objFmt.FullLength()
|
||||
}
|
||||
minLen := util.OptionalArg(minLength, maxLen)
|
||||
if len(s) < minLen || len(s) > maxLen {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
|
@ -80,6 +80,10 @@ func RefNameFromTag(shortName string) RefName {
|
||||
return RefName(TagPrefix + shortName)
|
||||
}
|
||||
|
||||
func RefNameFromCommit(shortName string) RefName {
|
||||
return RefName(shortName)
|
||||
}
|
||||
|
||||
func (ref RefName) String() string {
|
||||
return string(ref)
|
||||
}
|
||||
@ -181,32 +185,38 @@ func (ref RefName) RefGroup() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// RefType is a simple ref type of the reference, it is used for UI and webhooks
|
||||
type RefType string
|
||||
|
||||
const (
|
||||
RefTypeBranch RefType = "branch"
|
||||
RefTypeTag RefType = "tag"
|
||||
RefTypeCommit RefType = "commit"
|
||||
)
|
||||
|
||||
// RefType returns the simple ref type of the reference, e.g. branch, tag
|
||||
// It's different from RefGroup, which is using the name of the directory under .git/refs
|
||||
// Here we using branch but not heads, using tag but not tags
|
||||
func (ref RefName) RefType() string {
|
||||
var refType string
|
||||
if ref.IsBranch() {
|
||||
refType = "branch"
|
||||
} else if ref.IsTag() {
|
||||
refType = "tag"
|
||||
func (ref RefName) RefType() RefType {
|
||||
switch {
|
||||
case ref.IsBranch():
|
||||
return RefTypeBranch
|
||||
case ref.IsTag():
|
||||
return RefTypeTag
|
||||
case IsStringLikelyCommitID(nil, string(ref), 6):
|
||||
return RefTypeCommit
|
||||
}
|
||||
return refType
|
||||
return ""
|
||||
}
|
||||
|
||||
// RefURL returns the absolute URL for a ref in a repository
|
||||
func RefURL(repoURL, ref string) string {
|
||||
refFullName := RefName(ref)
|
||||
refName := util.PathEscapeSegments(refFullName.ShortName())
|
||||
switch {
|
||||
case refFullName.IsBranch():
|
||||
return repoURL + "/src/branch/" + refName
|
||||
case refFullName.IsTag():
|
||||
return repoURL + "/src/tag/" + refName
|
||||
case !Sha1ObjectFormat.IsValid(ref):
|
||||
// assume they mean a branch
|
||||
return repoURL + "/src/branch/" + refName
|
||||
default:
|
||||
return repoURL + "/src/commit/" + refName
|
||||
// RefWebLinkPath returns a path for the reference that can be used in a web link:
|
||||
// * "branch/<branch_name>"
|
||||
// * "tag/<tag_name>"
|
||||
// * "commit/<commit_id>"
|
||||
// It returns an empty string if the reference is not a branch, tag or commit.
|
||||
func (ref RefName) RefWebLinkPath() string {
|
||||
refType := ref.RefType()
|
||||
if refType == "" {
|
||||
return ""
|
||||
}
|
||||
return string(refType) + "/" + util.PathEscapeSegments(ref.ShortName())
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ func TestRefName(t *testing.T) {
|
||||
|
||||
// Test pull names
|
||||
assert.Equal(t, "1", RefName("refs/pull/1/head").PullName())
|
||||
assert.True(t, RefName("refs/pull/1/head").IsPull())
|
||||
assert.True(t, RefName("refs/pull/1/merge").IsPull())
|
||||
assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName())
|
||||
|
||||
// Test for branch names
|
||||
@ -30,9 +32,8 @@ func TestRefName(t *testing.T) {
|
||||
assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName())
|
||||
}
|
||||
|
||||
func TestRefURL(t *testing.T) {
|
||||
repoURL := "/user/repo"
|
||||
assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo"))
|
||||
assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo"))
|
||||
assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
|
||||
func TestRefWebLinkPath(t *testing.T) {
|
||||
assert.Equal(t, "branch/foo", RefName("refs/heads/foo").RefWebLinkPath())
|
||||
assert.Equal(t, "tag/foo", RefName("refs/tags/foo").RefWebLinkPath())
|
||||
assert.Equal(t, "commit/c0ffee", RefName("c0ffee").RefWebLinkPath())
|
||||
}
|
||||
|
@ -16,37 +16,35 @@ import (
|
||||
type ArchiveType int
|
||||
|
||||
const (
|
||||
// ZIP zip archive type
|
||||
ZIP ArchiveType = iota + 1
|
||||
// TARGZ tar gz archive type
|
||||
TARGZ
|
||||
// BUNDLE bundle archive type
|
||||
BUNDLE
|
||||
ArchiveUnknown ArchiveType = iota
|
||||
ArchiveZip // 1
|
||||
ArchiveTarGz // 2
|
||||
ArchiveBundle // 3
|
||||
)
|
||||
|
||||
// String converts an ArchiveType to string
|
||||
// String converts an ArchiveType to string: the extension of the archive file without prefix dot
|
||||
func (a ArchiveType) String() string {
|
||||
switch a {
|
||||
case ZIP:
|
||||
case ArchiveZip:
|
||||
return "zip"
|
||||
case TARGZ:
|
||||
case ArchiveTarGz:
|
||||
return "tar.gz"
|
||||
case BUNDLE:
|
||||
case ArchiveBundle:
|
||||
return "bundle"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func ToArchiveType(s string) ArchiveType {
|
||||
switch s {
|
||||
case "zip":
|
||||
return ZIP
|
||||
case "tar.gz":
|
||||
return TARGZ
|
||||
case "bundle":
|
||||
return BUNDLE
|
||||
func SplitArchiveNameType(s string) (string, ArchiveType) {
|
||||
switch {
|
||||
case strings.HasSuffix(s, ".zip"):
|
||||
return strings.TrimSuffix(s, ".zip"), ArchiveZip
|
||||
case strings.HasSuffix(s, ".tar.gz"):
|
||||
return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
|
||||
case strings.HasSuffix(s, ".bundle"):
|
||||
return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
|
||||
}
|
||||
return 0
|
||||
return s, ArchiveUnknown
|
||||
}
|
||||
|
||||
// CreateArchive create archive content to the target path
|
||||
|
32
modules/git/repo_archive_test.go
Normal file
32
modules/git/repo_archive_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestArchiveType(t *testing.T) {
|
||||
name, archiveType := SplitArchiveNameType("test.tar.gz")
|
||||
assert.Equal(t, "test", name)
|
||||
assert.Equal(t, "tar.gz", archiveType.String())
|
||||
|
||||
name, archiveType = SplitArchiveNameType("a/b/test.zip")
|
||||
assert.Equal(t, "a/b/test", name)
|
||||
assert.Equal(t, "zip", archiveType.String())
|
||||
|
||||
name, archiveType = SplitArchiveNameType("1234.bundle")
|
||||
assert.Equal(t, "1234", name)
|
||||
assert.Equal(t, "bundle", archiveType.String())
|
||||
|
||||
name, archiveType = SplitArchiveNameType("test")
|
||||
assert.Equal(t, "test", name)
|
||||
assert.Equal(t, "unknown", archiveType.String())
|
||||
|
||||
name, archiveType = SplitArchiveNameType("test.xz")
|
||||
assert.Equal(t, "test.xz", name)
|
||||
assert.Equal(t, "unknown", archiveType.String())
|
||||
}
|
@ -46,7 +46,7 @@ func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer)
|
||||
setting.AppSubURL,
|
||||
url.PathEscape(ctx.RenderOptions.Metas["user"]),
|
||||
url.PathEscape(ctx.RenderOptions.Metas["repo"]),
|
||||
ctx.RenderOptions.Metas["BranchNameSubURL"],
|
||||
ctx.RenderOptions.Metas["RefTypeNameSubURL"],
|
||||
url.PathEscape(ctx.RenderOptions.RelativePath),
|
||||
)
|
||||
return ctx.RenderInternal.FormatWithSafeAttrs(output, `<div class="%s" %s="%s"></div>`, playerClassName, playerSrcAttr, rawURL)
|
||||
|
@ -44,7 +44,7 @@ type RenderOptions struct {
|
||||
MarkupType string
|
||||
|
||||
// user&repo, format&style®exp (for external issue pattern), teams&org (for mention)
|
||||
// BranchNameSubURL (for iframe&asciicast)
|
||||
// RefTypeNameSubURL (for iframe&asciicast)
|
||||
// markupAllowShortIssuePattern
|
||||
// markdownLineBreakStyle (comment, document)
|
||||
Metas map[string]string
|
||||
@ -170,7 +170,7 @@ sandbox="allow-scripts"
|
||||
setting.AppSubURL,
|
||||
url.PathEscape(ctx.RenderOptions.Metas["user"]),
|
||||
url.PathEscape(ctx.RenderOptions.Metas["repo"]),
|
||||
ctx.RenderOptions.Metas["BranchNameSubURL"],
|
||||
ctx.RenderOptions.Metas["RefTypeNameSubURL"],
|
||||
url.PathEscape(ctx.RenderOptions.RelativePath),
|
||||
))
|
||||
return err
|
||||
|
@ -1115,9 +1115,6 @@ blame.ignore_revs=Ignorování revizí v <a href="%s">.git-blame-ignorerevs</a>.
|
||||
blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Zobrazí maximálně 30 uživatelů
|
||||
|
||||
tree_path_not_found_commit=Cesta %[1]s v commitu %[2]s neexistuje
|
||||
tree_path_not_found_branch=Cesta %[1]s ve větvi %[2]s neexistuje
|
||||
tree_path_not_found_tag=Cesta %[1]s ve značce %[2]s neexistuje
|
||||
|
||||
transfer.accept=Přijmout převod
|
||||
transfer.accept_desc=Převést do „%s“
|
||||
@ -1651,7 +1648,7 @@ issues.attachment.open_tab=`Klikněte pro zobrazení „%s“ v nové záložce`
|
||||
issues.attachment.download=`Klikněte pro stažení „%s“`
|
||||
issues.subscribe=Odebírat
|
||||
issues.unsubscribe=Zrušit odběr
|
||||
issues.unpin_issue=Odepnout úkol
|
||||
issues.unpin=Odepnout
|
||||
issues.max_pinned=Nemůžete připnout další úkoly
|
||||
issues.pin_comment=připnuto %s
|
||||
issues.unpin_comment=odepnul/a tento %s
|
||||
|
@ -1111,9 +1111,6 @@ blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden i
|
||||
blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Zeigt maximal 30 Benutzer
|
||||
|
||||
tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
|
||||
tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
|
||||
tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
|
||||
|
||||
transfer.accept=Übertragung Akzeptieren
|
||||
transfer.accept_desc=`Übertragung nach "%s"`
|
||||
@ -1646,7 +1643,7 @@ issues.attachment.open_tab=`Klicken, um „%s“ in einem neuen Tab zu öffnen`
|
||||
issues.attachment.download=`Klicken, um „%s“ herunterzuladen`
|
||||
issues.subscribe=Abonnieren
|
||||
issues.unsubscribe=Abbestellen
|
||||
issues.unpin_issue=Issue abheften
|
||||
issues.unpin=Loslösen
|
||||
issues.max_pinned=Du kannst keine weiteren Issues anheften
|
||||
issues.pin_comment=hat das %s angeheftet
|
||||
issues.unpin_comment=hat das %s abgeheftet
|
||||
|
@ -992,9 +992,6 @@ blame_prior=Προβολή ευθύνης πριν από αυτή την αλλ
|
||||
blame.ignore_revs=Αγνόηση των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>. Πατήστε <a href="%s">εδώ</a> για να το παρακάμψετε και να δείτε την κανονική προβολή ευθυνών.
|
||||
blame.ignore_revs.failed=Αποτυχία αγνόησης των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>.
|
||||
|
||||
tree_path_not_found_commit=Η διαδρομή %[1]s δεν υπάρχει στην υποβολή %[2]s
|
||||
tree_path_not_found_branch=Η διαδρομή %[1]s δεν υπάρχει στον κλάδο %[2]s
|
||||
tree_path_not_found_tag=Η διαδρομή %[1]s δεν υπάρχει στην ετικέτα %[2]s
|
||||
|
||||
transfer.accept=Αποδοχή Μεταφοράς
|
||||
transfer.reject=Απόρριψη Μεταφοράς
|
||||
@ -1495,7 +1492,7 @@ issues.attachment.open_tab=`Κάντε κλικ για να δείτε το "%s"
|
||||
issues.attachment.download=`Κάντε κλικ για να λάβετε το "%s"`
|
||||
issues.subscribe=Εγγραφή
|
||||
issues.unsubscribe=Διαγραφή
|
||||
issues.unpin_issue=Άφεση Ζητήματος
|
||||
issues.unpin=Άφεση
|
||||
issues.max_pinned=Δεν μπορείτε να διατηρήσετε περισσότερα ζητήματα
|
||||
issues.pin_comment=διατήρησε αυτό %s
|
||||
issues.unpin_comment=άφησε αυτό %s
|
||||
|
@ -1115,9 +1115,7 @@ blame.ignore_revs = Ignoring revisions in <a href="%s">.git-blame-ignore-revs</a
|
||||
blame.ignore_revs.failed = Failed to ignore revisions in <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip = Shows a maximum of 30 users
|
||||
|
||||
tree_path_not_found_commit = Path %[1]s doesn't exist in commit %[2]s
|
||||
tree_path_not_found_branch = Path %[1]s doesn't exist in branch %[2]s
|
||||
tree_path_not_found_tag = Path %[1]s doesn't exist in tag %[2]s
|
||||
tree_path_not_found = Path %[1]s doesn't exist in %[2]s
|
||||
|
||||
transfer.accept = Accept Transfer
|
||||
transfer.accept_desc = Transfer to "%s"
|
||||
@ -1654,7 +1652,7 @@ issues.attachment.open_tab = `Click to see "%s" in a new tab`
|
||||
issues.attachment.download = `Click to download "%s"`
|
||||
issues.subscribe = Subscribe
|
||||
issues.unsubscribe = Unsubscribe
|
||||
issues.unpin_issue = Unpin Issue
|
||||
issues.unpin = Unpin
|
||||
issues.max_pinned = "You can't pin more issues"
|
||||
issues.pin_comment = "pinned this %s"
|
||||
issues.unpin_comment = "unpinned this %s"
|
||||
@ -2162,7 +2160,7 @@ settings.advanced_settings = Advanced Settings
|
||||
settings.wiki_desc = Enable Repository Wiki
|
||||
settings.use_internal_wiki = Use Built-In Wiki
|
||||
settings.default_wiki_branch_name = Default Wiki Branch Name
|
||||
settings.default_wiki_everyone_access = Default Access Permission for signed-in users:
|
||||
settings.default_permission_everyone_access = Default access permission for all signed-in users:
|
||||
settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
|
||||
settings.use_external_wiki = Use External Wiki
|
||||
settings.external_wiki_url = External Wiki URL
|
||||
|
@ -982,9 +982,6 @@ blame_prior=Ver la culpa antes de este cambio
|
||||
blame.ignore_revs=Ignorando revisiones en <a href="%s">.git-blame-ignore-revs</a>. Haga clic <a href="%s">aquí para saltar</a> y para a la vista normal.
|
||||
blame.ignore_revs.failed=No se pudieron ignorar las revisiones en <a href="%s">.git-blame-ignore-revs</a>.
|
||||
|
||||
tree_path_not_found_commit=La ruta %[1]s no existe en el commit %[2]s
|
||||
tree_path_not_found_branch=La ruta %[1]s no existe en la rama %[2]s
|
||||
tree_path_not_found_tag=La ruta %[1]s no existe en la etiqueta %[2]s
|
||||
|
||||
transfer.accept=Aceptar transferencia
|
||||
transfer.reject=Rechazar transferencia
|
||||
@ -1485,7 +1482,7 @@ issues.attachment.open_tab='Haga clic para ver "%s" en una pestaña nueva'
|
||||
issues.attachment.download=`Haga clic para descargar "%s"`
|
||||
issues.subscribe=Suscribir
|
||||
issues.unsubscribe=Desuscribirse
|
||||
issues.unpin_issue=Desanclar incidencia
|
||||
issues.unpin=Desanclar
|
||||
issues.max_pinned=No puedes anclar más incidencias
|
||||
issues.pin_comment=anclado este %s
|
||||
issues.unpin_comment=desanclado este %s
|
||||
|
@ -46,7 +46,7 @@ webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charg
|
||||
webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer.
|
||||
webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"`
|
||||
webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande.
|
||||
webauthn_error_duplicated=La clé de sécurité n'est pas autorisée pour cette demande. Veuillez vous assurer que la clé n'est pas déjà enregistrée.
|
||||
webauthn_error_duplicated=La clé de sécurité n’est pas autorisée pour cette demande. Veuillez vous assurer que la clé n’est pas déjà enregistrée.
|
||||
webauthn_error_empty=Vous devez définir un nom pour cette clé.
|
||||
webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer.
|
||||
webauthn_reload=Recharger
|
||||
@ -469,7 +469,7 @@ sspi_auth_failed=Échec de l'authentification SSPI
|
||||
password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous l’utilisez ailleurs.
|
||||
password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned
|
||||
last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis.
|
||||
signin_passkey=Se connecter avec une clé d’identification (passkey)
|
||||
signin_passkey=Se connecter avec une clé d’accès (passkey)
|
||||
back_to_sign_in=Revenir à la page de connexion
|
||||
|
||||
[mail]
|
||||
@ -818,7 +818,7 @@ manage_ssh_keys=Gérer les clés SSH
|
||||
manage_ssh_principals=Gérer les certificats principaux SSH
|
||||
manage_gpg_keys=Gérer les clés GPG
|
||||
add_key=Ajouter une clé
|
||||
ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos.
|
||||
ssh_desc=Ces clés SSH publiques sont associées à votre compte. Les clés privées correspondantes permettent l’accès complet à vos dépôts.
|
||||
principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts.
|
||||
gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions.
|
||||
ssh_helper=<strong>Besoin d'aide ?</strong> Consultez le guide de GitHub pour <a href="%s">créer vos propres clés SSH</a> ou <a href="%s">résoudre les problèmes courants</a> que vous pourriez rencontrer en utilisant SSH.
|
||||
@ -969,7 +969,7 @@ passcode_invalid=Le mot de passe est invalide. Réessayez.
|
||||
twofa_enrolled=L’authentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.
|
||||
twofa_failed_get_secret=Impossible d'obtenir le secret.
|
||||
|
||||
webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clef de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
|
||||
webauthn_desc=Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clé de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
|
||||
webauthn_register_key=Ajouter une clé de sécurité
|
||||
webauthn_nickname=Pseudonyme
|
||||
webauthn_delete_key=Retirer la clé de sécurité
|
||||
@ -1115,9 +1115,6 @@ blame.ignore_revs=Les révisions dans <a href="%s">.git-blame-ignore-revs</a> so
|
||||
blame.ignore_revs.failed=Impossible d'ignorer les révisions dans <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Affiche un maximum de 30 utilisateurs
|
||||
|
||||
tree_path_not_found_commit=Le chemin %[1]s n’existe pas dans la révision %[2]s.
|
||||
tree_path_not_found_branch=Le chemin %[1]s n’existe pas dans la branche %[2]s.
|
||||
tree_path_not_found_tag=Le chemin %[1]s n’existe pas dans l’étiquette %[2]s.
|
||||
|
||||
transfer.accept=Accepter le transfert
|
||||
transfer.accept_desc=Transférer à « %s »
|
||||
@ -1651,7 +1648,7 @@ issues.attachment.open_tab=`Cliquez ici pour voir « %s » dans un nouvel ongl
|
||||
issues.attachment.download=`Cliquez pour télécharger « %s ».`
|
||||
issues.subscribe=S’abonner
|
||||
issues.unsubscribe=Se désabonner
|
||||
issues.unpin_issue=Désépingler le ticket
|
||||
issues.unpin=Désépingler
|
||||
issues.max_pinned=Vous ne pouvez pas épingler plus de tickets
|
||||
issues.pin_comment=a épinglé ça %s.
|
||||
issues.unpin_comment=a désépinglé ça %s.
|
||||
@ -2400,17 +2397,17 @@ settings.packagist_api_token=Jeton API
|
||||
settings.packagist_package_url=URL du paquet Packagist
|
||||
settings.deploy_keys=Clés de déploiement
|
||||
settings.add_deploy_key=Ajouter une clé de déploiement
|
||||
settings.deploy_key_desc=Les clefs de déploiement ont un accès en lecture seule au dépôt.
|
||||
settings.deploy_key_desc=Les clés de déploiement ont un accès en lecture seule au dépôt.
|
||||
settings.is_writable=Activer l'accès en écriture
|
||||
settings.is_writable_info=Autoriser cette clé de déploiement à <strong>soumettre</strong> sur le dépôt.
|
||||
settings.no_deploy_keys=Il n'y a pas encore de clefs de déploiement.
|
||||
settings.no_deploy_keys=Il n’y a pas encore de clés de déploiement.
|
||||
settings.title=Titre
|
||||
settings.deploy_key_content=Contenu
|
||||
settings.key_been_used=Une clef de déploiement identique est déjà en cours d'utilisation.
|
||||
settings.key_name_used=Une clef de déploiement du même nom existe déjà.
|
||||
settings.add_key_success=La clé de déploiement "%s" a été ajoutée.
|
||||
settings.deploy_key_deletion=Supprimer une clef de déploiement
|
||||
settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ?
|
||||
settings.key_been_used=Une clé de déploiement identique est déjà en cours d’utilisation.
|
||||
settings.key_name_used=Une clé de déploiement du même nom existe déjà.
|
||||
settings.add_key_success=La clé de déploiement « %s » a été ajoutée.
|
||||
settings.deploy_key_deletion=Supprimer une clé de déploiement
|
||||
settings.deploy_key_deletion_desc=La suppression d’une clé de déploiement révoque son accès à ce dépôt. Continuer ?
|
||||
settings.deploy_key_deletion_success=La clé de déploiement a été supprimée.
|
||||
settings.branches=Branches
|
||||
settings.protected_branch=Protection de branche
|
||||
@ -2627,6 +2624,9 @@ diff.image.overlay=Superposition
|
||||
diff.has_escaped=Cette ligne contient des caractères Unicode cachés
|
||||
diff.show_file_tree=Afficher l’arborescence des fichiers
|
||||
diff.hide_file_tree=Masquer l’arborescence des fichiers
|
||||
diff.submodule_added=Sous-module %[1]s ajouté à %[2]s
|
||||
diff.submodule_deleted=Sous-module %[1]s supprimé de %[2]s
|
||||
diff.submodule_updated=Sous-module %[1]s mis-à-jour : %[2]s
|
||||
|
||||
releases.desc=Suivi des publications et des téléchargements.
|
||||
release.releases=Publications
|
||||
@ -3116,7 +3116,7 @@ auths.attribute_username_placeholder=Laisser vide afin d'utiliser le nom d'utili
|
||||
auths.attribute_name=Attribut prénom
|
||||
auths.attribute_surname=Attribut nom de famille
|
||||
auths.attribute_mail=Attribut e-mail
|
||||
auths.attribute_ssh_public_key=Attribut clef SSH publique
|
||||
auths.attribute_ssh_public_key=Attribut clé SSH publique
|
||||
auths.attribute_avatar=Attribut de l'avatar
|
||||
auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN
|
||||
auths.allow_deactivate_all=Permettre à un résultat de recherche vide de désactiver tous les utilisateurs
|
||||
@ -3240,7 +3240,7 @@ config.ssh_port=Port
|
||||
config.ssh_listen_port=Port d'écoute
|
||||
config.ssh_root_path=Emplacement racine
|
||||
config.ssh_key_test_path=Chemin de test des clés
|
||||
config.ssh_keygen_path=Chemin vers le générateur de clefs ("ssh-keygen")
|
||||
config.ssh_keygen_path=Chemin vers le générateur de clés (« ssh-keygen »)
|
||||
config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale
|
||||
config.ssh_minimum_key_sizes=Tailles de clé minimales
|
||||
|
||||
|
@ -244,6 +244,7 @@ license_desc=Téigh go bhfaighidh <a target="_blank" rel="noopener noreferrer" h
|
||||
|
||||
[install]
|
||||
install=Suiteáil
|
||||
installing_desc=Suiteáil anois, fan go fóill...
|
||||
title=Cumraíocht Tosaigh
|
||||
docker_helper=Má ritheann tú Gitea taobh istigh de Docker, léigh an <a target="_blank" rel="noopener noreferrer" href="%s">doiciméadúchán</a> roimh aon socruithe a athrú.
|
||||
require_db_desc=Éilíonn Gitea MySQL, PostgreSQL, MSSQL, SQLite3 nó TiDB (prótacal MySQL).
|
||||
@ -1015,6 +1016,9 @@ new_repo_helper=Tá gach comhad tionscadail i stór, lena n-áirítear stair ath
|
||||
owner=Úinéir
|
||||
owner_helper=B'fhéidir nach dtaispeánfar roinnt eagraíochtaí sa anuas mar gheall ar theorainn uasta comhaireamh stórais.
|
||||
repo_name=Ainm Stórais
|
||||
repo_name_profile_public_hint=Is stóras speisialta é .profile is féidir leat a úsáid chun README.md a chur le do phróifíl eagraíochta poiblí, le feiceáil ag aon duine. Cinntigh go bhfuil sé poiblí agus tosaigh é le README san eolaire próifíle le tosú.
|
||||
repo_name_profile_private_hint=Is stóras speisialta é .profile-private is féidir leat a úsáid chun README.md a chur le do phróifíl bhall eagraíochta, nach féidir a fheiceáil ach ag baill eagraíochta. Cinntigh go bhfuil sé príobháideach agus tosaigh le README sa eolaire próifíle chun tús a chur leis.
|
||||
repo_name_helper=Úsáideann ainmneacha maith stóras focail eochair gairide, áithnid agus uathúla. D'fhéadfaí stóras darbh ainm '.profile' nó '.profile-private' a úsáid chun README.md a chur leis an bpróifíl úsáideora/eagraíochta.
|
||||
repo_size=Méid an Stóras
|
||||
template=Teimpléad
|
||||
template_select=Roghnaigh teimpléad.
|
||||
@ -1111,9 +1115,6 @@ blame.ignore_revs=Ag déanamh neamhairde de leasuithe i <a href="%s">.git-blame-
|
||||
blame.ignore_revs.failed=Theip ar neamhaird a dhéanamh ar leasuithe i <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Taispeáint uasmhéid de 30 úsáideoir
|
||||
|
||||
tree_path_not_found_commit=Níl cosán %[1]s ann i dtiomantas %[2]s
|
||||
tree_path_not_found_branch=Níl cosán %[1]s ann i mbrainse %[2]s
|
||||
tree_path_not_found_tag=Níl cosán %[1]s ann i gclib %[2]s
|
||||
|
||||
transfer.accept=Glac le hAistriú
|
||||
transfer.accept_desc=Aistriú chuig “%s”
|
||||
@ -1231,6 +1232,7 @@ create_new_repo_command=Stóras nua a chruthú ar an líne ordaithe
|
||||
push_exist_repo=Stóras atá ann cheana a bhrú ón líne ordaithe
|
||||
empty_message=Níl aon ábhar sa stóras seo.
|
||||
broken_message=Ní féidir na sonraí Git atá mar bhunús leis an stóras seo a léamh. Déan teagmháil le riarthóir an chás seo nó scrios an stóras seo.
|
||||
no_branch=Níl aon bhrainsí ag an stóras seo.
|
||||
|
||||
code=Cód
|
||||
code.desc=Rochtain ar chód foinse, comhaid, gealltanais agus brainsí.
|
||||
@ -1646,7 +1648,7 @@ issues.attachment.open_tab=`Cliceáil chun "%s" a fheiceáil i gcluaisín nua`
|
||||
issues.attachment.download=`Cliceáil chun "%s" a íoslódáil
|
||||
issues.subscribe=Liostáil
|
||||
issues.unsubscribe=Díliostáil
|
||||
issues.unpin_issue=Bain pionna an t-eagrán
|
||||
issues.unpin=Díphoráil
|
||||
issues.max_pinned=Ní féidir leat níos mó saincheisteanna a phionadh
|
||||
issues.pin_comment=phionnáil an %s seo
|
||||
issues.unpin_comment=bain pionna an %s seo
|
||||
@ -2622,6 +2624,9 @@ diff.image.overlay=Forleagan
|
||||
diff.has_escaped=Tá carachtair Unicode i bhfolach ag an líne seo
|
||||
diff.show_file_tree=Taispeáin crann comhad
|
||||
diff.hide_file_tree=Folaigh crann comhad
|
||||
diff.submodule_added=Fomhodúl %[1]s curtha leis ag %[2]s
|
||||
diff.submodule_deleted=Scriosadh fomhodúl %[1]s ó %[2]s
|
||||
diff.submodule_updated=Nuashonraíodh fomhodúl %[1]s: %[2]s
|
||||
|
||||
releases.desc=Rian leaganacha tionscadal agus íoslódálacha.
|
||||
release.releases=Eisiúintí
|
||||
@ -2860,6 +2865,9 @@ teams.invite.title=Tugadh cuireadh duit dul isteach i bhfoireann <strong>%s</str
|
||||
teams.invite.by=Ar cuireadh ó %s
|
||||
teams.invite.description=Cliceáil ar an gcnaipe thíos le do thoil chun dul isteach san fhoireann.
|
||||
|
||||
view_as_role=Féach mar: %s
|
||||
view_as_public_hint=Tá tú ag féachaint ar an README mar úsáideoir poiblí.
|
||||
view_as_member_hint=Tá tú ag féachaint ar an README mar bhall den eagraíocht seo.
|
||||
|
||||
[admin]
|
||||
maintenance=Cothabháil
|
||||
@ -3530,6 +3538,7 @@ versions=Leaganacha
|
||||
versions.view_all=Féach ar gach
|
||||
dependency.id=ID
|
||||
dependency.version=Leagan
|
||||
search_in_external_registry=Cuardaigh i %s
|
||||
alpine.registry=Socraigh an chlár seo tríd an url a chur i do chomhad <code>/etc/apk/repositories</code>:
|
||||
alpine.registry.key=Íoslódáil eochair RSA poiblí na clárlainne isteach san fhillteán <code>/etc/apk/keys/</code> chun an síniú innéacs a fhíorú:
|
||||
alpine.registry.info=Roghnaigh $branch agus $repository ón liosta thíos.
|
||||
@ -3755,6 +3764,7 @@ workflow.not_found=Níor aimsíodh sreabhadh oibre '%s'.
|
||||
workflow.run_success=Ritheann sreabhadh oibre '%s' go rathúil.
|
||||
workflow.from_ref=Úsáid sreabhadh oibre ó
|
||||
workflow.has_workflow_dispatch=Tá comhoibriú ag an gcur i bhfeidhm seo le himeacht workflow_dispatch.
|
||||
workflow.has_no_workflow_dispatch=Níl aon truicear teagmhais workflow_dispatch ag sreabhadh oibre '%s'.
|
||||
|
||||
need_approval_desc=Teastaíonn faomhadh chun sreafaí oibre a rith le haghaidh iarratas tarraingt forc.
|
||||
|
||||
|
@ -818,6 +818,7 @@ issues.attachment.open_tab=`Klik untuk melihat "%s" di tab baru`
|
||||
issues.attachment.download=`Klik untuk mengunduh "%s"`
|
||||
issues.subscribe=Berlangganan
|
||||
issues.unsubscribe=Berhenti berlangganan
|
||||
issues.unpin=Lepas sematan
|
||||
issues.delete=Hapus
|
||||
|
||||
|
||||
|
@ -1015,6 +1015,7 @@ new_repo_helper=リポジトリには、プロジェクトのすべてのファ
|
||||
owner=オーナー
|
||||
owner_helper=リポジトリ数の上限により、一部の組織はドロップダウンに表示されない場合があります。
|
||||
repo_name=リポジトリ名
|
||||
repo_name_helper=リポジトリ名は、短く、覚えやすく、他と重複しないキーワードを使用しましょう。 リポジトリ名を ".profile" または ".profile-private" にして README.md を追加すると、ユーザーや組織のプロフィールとなります。
|
||||
repo_size=リポジトリサイズ
|
||||
template=テンプレート
|
||||
template_select=テンプレートを選択してください。
|
||||
@ -1108,9 +1109,6 @@ blame_prior=この変更より前のBlameを表示
|
||||
blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> で指定されたリビジョンは除外しています。 これを迂回して通常のBlame表示を見るには <a href="%s">ここ</a>をクリック。
|
||||
blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> によるリビジョンの無視は失敗しました。
|
||||
|
||||
tree_path_not_found_commit=パス %[1]s はコミット %[2]s に存在しません
|
||||
tree_path_not_found_branch=パス %[1]s はブランチ %[2]s に存在しません
|
||||
tree_path_not_found_tag=パス %[1]s はタグ %[2]s に存在しません
|
||||
|
||||
transfer.accept=移転を承認
|
||||
transfer.accept_desc=`"%s" に移転`
|
||||
@ -1641,7 +1639,7 @@ issues.attachment.open_tab=`クリックして新しいタブで "%s" を見る`
|
||||
issues.attachment.download=`クリックして "%s" をダウンロード`
|
||||
issues.subscribe=購読する
|
||||
issues.unsubscribe=購読を解除
|
||||
issues.unpin_issue=イシューのピン留めを解除
|
||||
issues.unpin=ピン留め解除
|
||||
issues.max_pinned=これ以上イシューをピン留めできません
|
||||
issues.pin_comment=がピン留め %s
|
||||
issues.unpin_comment=がピン留めを解除 %s
|
||||
@ -2852,6 +2850,8 @@ teams.invite.title=あなたは組織 <strong>%[2]s</strong> 内のチーム <st
|
||||
teams.invite.by=%s からの招待
|
||||
teams.invite.description=下のボタンをクリックしてチームに参加してください。
|
||||
|
||||
view_as_public_hint=READMEを公開ユーザーとして見ています。
|
||||
view_as_member_hint=READMEをこの組織のメンバーとして見ています。
|
||||
|
||||
[admin]
|
||||
maintenance=メンテナンス
|
||||
@ -3530,6 +3530,8 @@ alpine.repository=リポジトリ情報
|
||||
alpine.repository.branches=Branches
|
||||
alpine.repository.repositories=Repositories
|
||||
alpine.repository.architectures=Architectures
|
||||
arch.registry=<code>/etc/pacman.conf</code> にリポジトリとアーキテクチャを含めてサーバーを追加します:
|
||||
arch.install=pacmanでパッケージを同期します:
|
||||
arch.repository=リポジトリ情報
|
||||
arch.repository.repositories=リポジトリ
|
||||
arch.repository.architectures=Architectures
|
||||
@ -3712,6 +3714,7 @@ runners.status.active=稼働中
|
||||
runners.status.offline=オフライン
|
||||
runners.version=バージョン
|
||||
runners.reset_registration_token=登録トークンをリセット
|
||||
runners.reset_registration_token_confirm=現在のトークンを無効にして、新しいトークンを生成しますか?
|
||||
runners.reset_registration_token_success=ランナー登録トークンをリセットしました
|
||||
|
||||
runs.all_workflows=すべてのワークフロー
|
||||
|
@ -997,9 +997,6 @@ blame_prior=Aplūkot vainīgo par izmaiņām pirms šīs revīzijas
|
||||
blame.ignore_revs=Neņem vērā izmaiņas no <a href="%s">.git-blame-ignore-revs</a>. Nospiediet <a href="%s">šeit, lai to apietu</a> un redzētu visu izmaiņu skatu.
|
||||
blame.ignore_revs.failed=Neizdevās neņemt vērā izmaiņas no <a href="%s">.git-blam-ignore-revs</a>.
|
||||
|
||||
tree_path_not_found_commit=Revīzijā %[2]s neeksistē ceļš %[1]s
|
||||
tree_path_not_found_branch=Atzarā %[2]s nepastāv ceļš %[1]s
|
||||
tree_path_not_found_tag=Tagā %[2]s nepastāv ceļš %[1]s
|
||||
|
||||
transfer.accept=Apstiprināt īpašnieka maiņu
|
||||
transfer.reject=Noraidīt īpašnieka maiņu
|
||||
@ -1501,7 +1498,7 @@ issues.attachment.open_tab=`Noklikšķiniet, lai apskatītos "%s" jaunā logā`
|
||||
issues.attachment.download=`Noklikšķiniet, lai lejupielādētu "%s"`
|
||||
issues.subscribe=Abonēt
|
||||
issues.unsubscribe=Atrakstīties
|
||||
issues.unpin_issue=Atspraust problēmu
|
||||
issues.unpin=Atspraust
|
||||
issues.max_pinned=Nevar piespraust vairāk problēmas
|
||||
issues.pin_comment=piesprauda šo %s
|
||||
issues.unpin_comment=atsprauda šo %s
|
||||
|
@ -1491,7 +1491,7 @@ issues.attachment.open_tab=`Clique para ver "%s" em uma nova aba`
|
||||
issues.attachment.download=`Clique para baixar "%s"`
|
||||
issues.subscribe=Inscrever-se
|
||||
issues.unsubscribe=Desinscrever
|
||||
issues.unpin_issue=Desfixar issue
|
||||
issues.unpin=Desfixar
|
||||
issues.max_pinned=Você não pode fixar mais issues
|
||||
issues.pin_comment=fixou isto %s
|
||||
issues.unpin_comment=desafixou isto %s
|
||||
|
@ -1115,9 +1115,6 @@ blame.ignore_revs=Ignorando as revisões em <a href="%s">.git-blame-ignore-revs<
|
||||
blame.ignore_revs.failed=Falhou ao ignorar as revisões em <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Mostra um máximo de 30 utilizadores
|
||||
|
||||
tree_path_not_found_commit=A localização %[1]s não existe no cometimento %[2]s
|
||||
tree_path_not_found_branch=A localização %[1]s não existe no ramo %[2]s
|
||||
tree_path_not_found_tag=A localização %[1]s não existe na etiqueta %[2]s
|
||||
|
||||
transfer.accept=Aceitar transferência
|
||||
transfer.accept_desc=`Transferir para "%s"`
|
||||
@ -1651,7 +1648,7 @@ issues.attachment.open_tab=`Clique para ver "%s" num separador novo`
|
||||
issues.attachment.download=`Clique para descarregar "%s"`
|
||||
issues.subscribe=Subscrever
|
||||
issues.unsubscribe=Anular subscrição
|
||||
issues.unpin_issue=Desafixar questão
|
||||
issues.unpin=Desafixar
|
||||
issues.max_pinned=Já não pode fixar mais questões
|
||||
issues.pin_comment=fixou isto %s
|
||||
issues.unpin_comment=desafixou isto %s
|
||||
|
@ -978,8 +978,6 @@ delete_preexisting_content=Удалить файлы из %s
|
||||
delete_preexisting_success=Удалены непринятые файлы в %s
|
||||
blame_prior=Показать авторство предшествующих изменений
|
||||
|
||||
tree_path_not_found_commit=Путь %[1]s не существует в коммите %[2]s
|
||||
tree_path_not_found_branch=Путь %[1]s не существует в ветке %[2]s
|
||||
|
||||
transfer.accept=Принять трансфер
|
||||
transfer.reject=Отказаться от перемещения
|
||||
@ -1471,7 +1469,7 @@ issues.attachment.open_tab=`Нажмите, чтобы увидеть «%s» в
|
||||
issues.attachment.download=`Нажмите, чтобы скачать «%s»`
|
||||
issues.subscribe=Подписаться
|
||||
issues.unsubscribe=Отказаться от подписки
|
||||
issues.unpin_issue=Открепить задачу
|
||||
issues.unpin=Открепить
|
||||
issues.max_pinned=Нельзя закрепить больше задач
|
||||
issues.pin_comment=закрепил(а) эту задачу %s
|
||||
issues.unpin_comment=открепил(а) эту задачу %s
|
||||
|
@ -1068,6 +1068,7 @@ issues.dismiss_review=Zamietnuť revíziu
|
||||
issues.dismiss_review_warning=Naozaj chcete zrušiť túto revíziu?
|
||||
issues.cancel=Zrušiť
|
||||
issues.save=Uložiť
|
||||
issues.unpin=Odopnúť
|
||||
|
||||
|
||||
|
||||
|
@ -1083,9 +1083,6 @@ blame_prior=Bu değişiklikten önceki suçu görüntüle
|
||||
blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için <a href="%s">buraya tıklayın</a>.
|
||||
blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılamadı.
|
||||
|
||||
tree_path_not_found_commit=%[1] yolu, %[2]s işlemesinde mevcut değil
|
||||
tree_path_not_found_branch=%[1] yolu, %[2]s dalında mevcut değil
|
||||
tree_path_not_found_tag=%[1] yolu, %[2]s etiketinde mevcut değil
|
||||
|
||||
transfer.accept=Aktarımı Kabul Et
|
||||
transfer.reject=Aktarımı Reddet
|
||||
@ -1605,7 +1602,7 @@ issues.attachment.open_tab=`Yeni bir sekmede "%s" görmek için tıkla`
|
||||
issues.attachment.download=`"%s" indirmek için tıkla`
|
||||
issues.subscribe=Abone Ol
|
||||
issues.unsubscribe=Abonelikten Çık
|
||||
issues.unpin_issue=Konuyu Sabitlemeden Kaldır
|
||||
issues.unpin=Sabitlemeyi kaldır
|
||||
issues.max_pinned=Daha fazla konuyu sabitleyemezsiniz
|
||||
issues.pin_comment=%s sabitlendi
|
||||
issues.unpin_comment=%s sabitlenmesi kaldırıldı
|
||||
|
@ -1111,9 +1111,6 @@ blame.ignore_revs=忽略 <a href="%s">.git-blame-ignore-revs</a> 的修订。点
|
||||
blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 版本失败。
|
||||
user_search_tooltip=最多显示30名用户
|
||||
|
||||
tree_path_not_found_commit=路径%[1]s 在提交 %[2]s 中不存在
|
||||
tree_path_not_found_branch=路径 %[1]s 不存在于分支 %[2]s 中。
|
||||
tree_path_not_found_tag=路径 %[1]s 不存在于标签 %[2]s 中
|
||||
|
||||
transfer.accept=接受转移
|
||||
transfer.accept_desc=`转移到 "%s"`
|
||||
@ -1646,7 +1643,7 @@ issues.attachment.open_tab=`在新的标签页中查看 '%s'`
|
||||
issues.attachment.download=`点击下载 '%s'`
|
||||
issues.subscribe=订阅
|
||||
issues.unsubscribe=取消订阅
|
||||
issues.unpin_issue=取消置顶
|
||||
issues.unpin=取消置顶
|
||||
issues.max_pinned=您不能置顶更多工单
|
||||
issues.pin_comment=于 %s 被置顶
|
||||
issues.unpin_comment=于 %s 取消置顶
|
||||
|
@ -1108,9 +1108,6 @@ blame.ignore_revs=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂。
|
||||
blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂失敗。
|
||||
user_search_tooltip=顯示最多 30 個使用者
|
||||
|
||||
tree_path_not_found_commit=路徑 %[1]s 在提交 %[2]s 中不存在
|
||||
tree_path_not_found_branch=路徑 %[1]s 在分支 %[2]s 中不存在
|
||||
tree_path_not_found_tag=路徑 %[1]s 在標籤 %[2]s 中不存在
|
||||
|
||||
transfer.accept=同意轉移
|
||||
transfer.accept_desc=轉移到「%s」
|
||||
@ -1640,7 +1637,7 @@ issues.attachment.open_tab=`在新分頁中查看「%s」`
|
||||
issues.attachment.download=`點擊下載「%s」`
|
||||
issues.subscribe=訂閱
|
||||
issues.unsubscribe=取消訂閱
|
||||
issues.unpin_issue=取消固定問題
|
||||
issues.unpin=取消固定
|
||||
issues.max_pinned=您不能固定更多問題
|
||||
issues.pin_comment=固定於 %s
|
||||
issues.unpin_comment=取消固定於 %s
|
||||
|
55
package-lock.json
generated
55
package-lock.json
generated
@ -67,7 +67,6 @@
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@silverwind/vue-tsc": "2.1.13",
|
||||
"@stoplight/spectral-cli": "6.14.2",
|
||||
"@stylistic/eslint-plugin-js": "2.12.1",
|
||||
"@stylistic/stylelint-plugin": "3.1.1",
|
||||
@ -112,7 +111,8 @@
|
||||
"type-fest": "4.30.2",
|
||||
"updates": "16.4.1",
|
||||
"vite-string-plugin": "1.3.4",
|
||||
"vitest": "2.1.8"
|
||||
"vitest": "2.1.8",
|
||||
"vue-tsc": "2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
@ -3643,24 +3643,6 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@silverwind/vue-tsc": {
|
||||
"version": "2.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@silverwind/vue-tsc/-/vue-tsc-2.1.13.tgz",
|
||||
"integrity": "sha512-ejFxz1KZiUGAESbC+eURnjqt0N95qkU9eZU7W15wgF9zV+v2FEu3ZLduuXTC7D/Sg6lL1R/QjPfUbxbAbBQOsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/typescript": "~2.4.11",
|
||||
"@vue/language-core": "2.1.10",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"bin": {
|
||||
"vue-tsc": "bin/vue-tsc.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@silverwind/vue3-calendar-heatmap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@silverwind/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.6.tgz",
|
||||
@ -5251,17 +5233,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/language-core": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz",
|
||||
"integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz",
|
||||
"integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-core": "~2.4.8",
|
||||
"@volar/language-core": "~2.4.11",
|
||||
"@vue/compiler-dom": "^3.5.0",
|
||||
"@vue/compiler-vue2": "^2.7.16",
|
||||
"@vue/shared": "^3.5.0",
|
||||
"alien-signals": "^0.2.0",
|
||||
"alien-signals": "^0.4.9",
|
||||
"minimatch": "^9.0.3",
|
||||
"muggle-string": "^0.4.1",
|
||||
"path-browserify": "^1.0.1"
|
||||
@ -5664,9 +5646,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/alien-signals": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz",
|
||||
"integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==",
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz",
|
||||
"integrity": "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -14526,6 +14508,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-tsc": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.0.tgz",
|
||||
"integrity": "sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/typescript": "~2.4.11",
|
||||
"@vue/language-core": "2.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"vue-tsc": "bin/vue-tsc.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
|
@ -66,7 +66,6 @@
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@silverwind/vue-tsc": "2.1.13",
|
||||
"@stoplight/spectral-cli": "6.14.2",
|
||||
"@stylistic/eslint-plugin-js": "2.12.1",
|
||||
"@stylistic/stylelint-plugin": "3.1.1",
|
||||
@ -111,7 +110,8 @@
|
||||
"type-fest": "4.30.2",
|
||||
"updates": "16.4.1",
|
||||
"vite-string-plugin": "1.3.4",
|
||||
"vitest": "2.1.8"
|
||||
"vitest": "2.1.8",
|
||||
"vue-tsc": "2.2.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
|
@ -120,7 +120,7 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
|
||||
"ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
|
||||
"ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
|
||||
"ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
|
||||
"ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
|
||||
"ref_type": string(refName.RefType()), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
|
||||
"path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
|
||||
"repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
|
||||
"repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.
|
||||
|
@ -34,11 +34,30 @@ func ListHooks(ctx *context.APIContext) {
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// - type: string
|
||||
// enum:
|
||||
// - system
|
||||
// - default
|
||||
// - all
|
||||
// description: system, default or both kinds of webhooks
|
||||
// name: type
|
||||
// default: system
|
||||
// in: query
|
||||
//
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/HookList"
|
||||
|
||||
sysHooks, err := webhook.GetSystemWebhooks(ctx, optional.None[bool]())
|
||||
// for compatibility the default value is true
|
||||
isSystemWebhook := optional.Some(true)
|
||||
typeValue := ctx.FormString("type")
|
||||
if typeValue == "default" {
|
||||
isSystemWebhook = optional.Some(false)
|
||||
} else if typeValue == "all" {
|
||||
isSystemWebhook = optional.None[bool]()
|
||||
}
|
||||
|
||||
sysHooks, err := webhook.GetSystemOrDefaultWebhooks(ctx, isSystemWebhook)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err)
|
||||
return
|
||||
|
@ -17,11 +17,11 @@ func DownloadArchive(ctx *context.APIContext) {
|
||||
var tp git.ArchiveType
|
||||
switch ballType := ctx.PathParam("ball_type"); ballType {
|
||||
case "tarball":
|
||||
tp = git.TARGZ
|
||||
tp = git.ArchiveTarGz
|
||||
case "zipball":
|
||||
tp = git.ZIP
|
||||
tp = git.ArchiveZip
|
||||
case "bundle":
|
||||
tp = git.BUNDLE
|
||||
tp = git.ArchiveBundle
|
||||
default:
|
||||
ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType))
|
||||
return
|
||||
@ -36,7 +36,7 @@ func DownloadArchive(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"), tp)
|
||||
r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")+"."+tp.String())
|
||||
if err != nil {
|
||||
ctx.ServerError("NewRequest", err)
|
||||
return
|
||||
|
@ -293,14 +293,7 @@ func GetArchive(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func archiveDownload(ctx *context.APIContext) {
|
||||
uri := ctx.PathParam("*")
|
||||
ext, tp, err := archiver_service.ParseFileName(uri)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "ParseFileName", err)
|
||||
return
|
||||
}
|
||||
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
if err != nil {
|
||||
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
|
||||
ctx.Error(http.StatusBadRequest, "unknown archive format", err)
|
||||
|
@ -23,7 +23,7 @@ func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType stri
|
||||
}
|
||||
|
||||
title := fmt.Sprintf("Latest commits for branch %s", ctx.Repo.BranchName)
|
||||
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL()}
|
||||
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.RefTypeNameSubURL()}
|
||||
|
||||
feed := &feeds.Feed{
|
||||
Title: title,
|
||||
|
@ -24,7 +24,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
|
||||
}
|
||||
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: ctx.Repo.RefName,
|
||||
Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
|
||||
File: fileName,
|
||||
Page: 1,
|
||||
})
|
||||
@ -35,7 +35,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
|
||||
|
||||
title := fmt.Sprintf("Latest commits for file %s", ctx.Repo.TreePath)
|
||||
|
||||
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)}
|
||||
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)}
|
||||
|
||||
feed := &feeds.Feed{
|
||||
Title: title,
|
||||
|
@ -46,9 +46,9 @@ func RefBlame(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
treeLink := branchLink
|
||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
|
||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL()
|
||||
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
@ -185,7 +185,7 @@ func CreateBranch(ctx *context.Context) {
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.Flash.Error(ctx.GetErrMsg())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
@ -205,25 +205,25 @@ func CreateBranch(ctx *context.Context) {
|
||||
if err != nil {
|
||||
if release_service.IsErrProtectedTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
if release_service.IsErrTagAlreadyExists(err) {
|
||||
e := err.(release_service.ErrTagAlreadyExists)
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
if git_model.IsErrBranchNameConflict(err) {
|
||||
e := err.(git_model.ErrBranchNameConflict)
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
if git.IsErrPushRejected(err) {
|
||||
@ -242,7 +242,7 @@ func CreateBranch(ctx *context.Context) {
|
||||
}
|
||||
ctx.Flash.Error(flashError)
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ func CherryPick(ctx *context.Context) {
|
||||
ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
|
||||
ctx.Data["last_commit"] = ctx.Repo.CommitID
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
|
||||
ctx.HTML(200, tplCherryPick)
|
||||
}
|
||||
@ -85,7 +85,7 @@ func CherryPickPost(ctx *context.Context) {
|
||||
ctx.Data["new_branch_name"] = form.NewBranchName
|
||||
ctx.Data["last_commit"] = ctx.Repo.CommitID
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(200, tplCherryPick)
|
||||
|
@ -191,7 +191,7 @@ func SearchCommits(ctx *context.Context) {
|
||||
|
||||
query := ctx.FormTrim("q")
|
||||
if len(query) == 0 {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchNameSubURL())
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.RefTypeNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ func FileHistory(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefName, fileName)
|
||||
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), fileName) // FIXME: legacy code used ShortName
|
||||
if err != nil {
|
||||
ctx.ServerError("FileCommitsCount", err)
|
||||
return
|
||||
@ -238,7 +238,7 @@ func FileHistory(ctx *context.Context) {
|
||||
|
||||
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: ctx.Repo.RefName,
|
||||
Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
|
||||
File: fileName,
|
||||
Page: page,
|
||||
})
|
||||
|
@ -180,7 +180,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
||||
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["TreePaths"] = treePaths
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["commit_summary"] = ""
|
||||
ctx.Data["commit_message"] = ""
|
||||
if canCommit {
|
||||
@ -428,7 +428,7 @@ func DiffPreviewPost(ctx *context.Context) {
|
||||
// DeleteFile render delete file page
|
||||
func DeleteFile(ctx *context.Context) {
|
||||
ctx.Data["PageIsDelete"] = true
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
||||
|
||||
if treePath != ctx.Repo.TreePath {
|
||||
@ -462,7 +462,7 @@ func DeleteFilePost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Data["PageIsDelete"] = true
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
||||
ctx.Data["commit_summary"] = form.CommitSummary
|
||||
ctx.Data["commit_message"] = form.CommitMessage
|
||||
@ -604,7 +604,7 @@ func UploadFile(ctx *context.Context) {
|
||||
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["TreePaths"] = treePaths
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["commit_summary"] = ""
|
||||
ctx.Data["commit_message"] = ""
|
||||
if canCommit {
|
||||
|
@ -4,25 +4,14 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
func HandleGitError(ctx *context.Context, msg string, err error) {
|
||||
if git.IsErrNotExist(err) {
|
||||
refType := ""
|
||||
switch {
|
||||
case ctx.Repo.IsViewBranch:
|
||||
refType = "branch"
|
||||
case ctx.Repo.IsViewTag:
|
||||
refType = "tag"
|
||||
case ctx.Repo.IsViewCommit:
|
||||
refType = "commit"
|
||||
}
|
||||
ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found_"+refType, ctx.Repo.TreePath, url.PathEscape(ctx.Repo.RefName))
|
||||
ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + refType + "/" + url.PathEscape(ctx.Repo.RefName)
|
||||
ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found", ctx.Repo.TreePath, ctx.Repo.RefTypeNameSubURL())
|
||||
ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.NotFound(msg, err)
|
||||
} else {
|
||||
ctx.ServerError(msg, err)
|
||||
|
@ -26,13 +26,9 @@ type userSearchResponse struct {
|
||||
Results []*userSearchInfo `json:"results"`
|
||||
}
|
||||
|
||||
// IssuePosters get posters for current repo's issues/pull requests
|
||||
func IssuePosters(ctx *context.Context) {
|
||||
issuePosters(ctx, false)
|
||||
}
|
||||
|
||||
func PullPosters(ctx *context.Context) {
|
||||
issuePosters(ctx, true)
|
||||
func IssuePullPosters(ctx *context.Context) {
|
||||
isPullList := ctx.PathParam("type") == "pulls"
|
||||
issuePosters(ctx, isPullList)
|
||||
}
|
||||
|
||||
func issuePosters(ctx *context.Context, isPullList bool) {
|
||||
|
@ -4,12 +4,9 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
@ -22,12 +19,9 @@ func SetEditorconfigIfExists(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ec, _, err := ctx.Repo.GetEditorconfig()
|
||||
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
description := fmt.Sprintf("Error while getting .editorconfig file: %v", err)
|
||||
if err := system_model.CreateRepositoryNotice(description); err != nil {
|
||||
ctx.ServerError("ErrCreatingReporitoryNotice", err)
|
||||
}
|
||||
if err != nil {
|
||||
// it used to check `!git.IsErrNotExist(err)` and create a system notice, but it is quite annoying and useless
|
||||
// because network errors also happen frequently, so we just ignore it
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -62,9 +62,7 @@ func Packages(ctx *context.Context) {
|
||||
ctx.Data["PackageType"] = packageType
|
||||
ctx.Data["AvailableTypes"] = packages.TypeList
|
||||
ctx.Data["HasPackages"] = hasPackages
|
||||
if ctx.Repo != nil {
|
||||
ctx.Data["CanWritePackages"] = ctx.IsUserRepoWriter([]unit.Type{unit.TypePackages}) || ctx.IsUserSiteAdmin()
|
||||
}
|
||||
ctx.Data["CanWritePackages"] = ctx.Repo.CanWrite(unit.TypePackages) || ctx.IsUserSiteAdmin()
|
||||
ctx.Data["PackageDescriptors"] = pds
|
||||
ctx.Data["Total"] = total
|
||||
ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository
|
||||
|
@ -37,7 +37,7 @@ func NewDiffPatch(ctx *context.Context) {
|
||||
ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
|
||||
ctx.Data["last_commit"] = ctx.Repo.CommitID
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
|
||||
ctx.HTML(200, tplPatchFile)
|
||||
}
|
||||
@ -52,7 +52,7 @@ func NewDiffPatchPost(ctx *context.Context) {
|
||||
branchName = form.NewBranchName
|
||||
}
|
||||
ctx.Data["PageIsPatch"] = true
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["FileContent"] = form.Content
|
||||
ctx.Data["commit_summary"] = form.CommitSummary
|
||||
ctx.Data["commit_message"] = form.CommitMessage
|
||||
|
@ -17,7 +17,6 @@ import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -330,34 +329,17 @@ func LatestRelease(ctx *context.Context) {
|
||||
ctx.Redirect(release.Link())
|
||||
}
|
||||
|
||||
// NewRelease render creating or edit release page
|
||||
func NewRelease(ctx *context.Context) {
|
||||
func newReleaseCommon(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
|
||||
ctx.Data["PageIsReleaseList"] = true
|
||||
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
|
||||
if tagName := ctx.FormString("tag"); len(tagName) > 0 {
|
||||
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.ServerError("GetRelease", err)
|
||||
return
|
||||
}
|
||||
|
||||
if rel != nil {
|
||||
rel.Repo = ctx.Repo.Repository
|
||||
if err := rel.LoadAttributes(ctx); err != nil {
|
||||
ctx.ServerError("LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["tag_name"] = rel.TagName
|
||||
if rel.Target != "" {
|
||||
ctx.Data["tag_target"] = rel.Target
|
||||
}
|
||||
ctx.Data["title"] = rel.Title
|
||||
ctx.Data["content"] = rel.Note
|
||||
ctx.Data["attachments"] = rel.Attachments
|
||||
}
|
||||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTagNamesByRepoID", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Tags"] = tags
|
||||
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
@ -368,35 +350,74 @@ func NewRelease(ctx *context.Context) {
|
||||
|
||||
upload.AddUploadContext(ctx, "release")
|
||||
|
||||
// For New Release page
|
||||
PrepareBranchList(ctx)
|
||||
PrepareBranchList(ctx) // for New Release page
|
||||
}
|
||||
|
||||
// NewRelease render creating or edit release page
|
||||
func NewRelease(ctx *context.Context) {
|
||||
newReleaseCommon(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTagNamesByRepoID", err)
|
||||
return
|
||||
ctx.Data["ShowCreateTagOnlyButton"] = true
|
||||
|
||||
// pre-fill the form with the tag name, target branch and the existing release (if exists)
|
||||
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
|
||||
if tagName := ctx.FormString("tag"); tagName != "" {
|
||||
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.ServerError("GetRelease", err)
|
||||
return
|
||||
}
|
||||
|
||||
if rel != nil {
|
||||
rel.Repo = ctx.Repo.Repository
|
||||
if err = rel.LoadAttributes(ctx); err != nil {
|
||||
ctx.ServerError("LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["ShowCreateTagOnlyButton"] = false
|
||||
ctx.Data["tag_name"] = rel.TagName
|
||||
ctx.Data["tag_target"] = rel.Target
|
||||
ctx.Data["title"] = rel.Title
|
||||
ctx.Data["content"] = rel.Note
|
||||
ctx.Data["attachments"] = rel.Attachments
|
||||
}
|
||||
}
|
||||
ctx.Data["Tags"] = tags
|
||||
|
||||
ctx.HTML(http.StatusOK, tplReleaseNew)
|
||||
}
|
||||
|
||||
// NewReleasePost response for creating a release
|
||||
func NewReleasePost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.NewReleaseForm)
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
|
||||
ctx.Data["PageIsReleaseList"] = true
|
||||
|
||||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTagNamesByRepoID", err)
|
||||
newReleaseCommon(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.Data["Tags"] = tags
|
||||
|
||||
form := web.GetForm(ctx).(*forms.NewReleaseForm)
|
||||
|
||||
// first, check whether the release exists, and prepare "ShowCreateTagOnlyButton"
|
||||
// the logic should be done before the form error check to make the tmpl has correct variables
|
||||
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.ServerError("GetRelease", err)
|
||||
return
|
||||
}
|
||||
|
||||
// We should still show the "tag only" button if the user clicks it, no matter the release exists or not.
|
||||
// Because if error occurs, end users need to have the chance to edit the name and submit the form with "tag-only" again.
|
||||
// It is still not completely right, because there could still be cases like this:
|
||||
// * user visit "new release" page, see the "tag only" button
|
||||
// * input something, click other buttons but not "tag only"
|
||||
// * error occurs, the "new release" page is rendered again, but the "tag only" button is gone
|
||||
// Such cases are not able to be handled by current code, it needs frontend code to toggle the "tag-only" button if the input changes.
|
||||
// Or another choice is "always show the tag-only button" if error occurs.
|
||||
ctx.Data["ShowCreateTagOnlyButton"] = form.TagOnly || rel == nil
|
||||
|
||||
// do some form checks
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplReleaseNew)
|
||||
return
|
||||
@ -407,59 +428,49 @@ func NewReleasePost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Title of release cannot be empty
|
||||
if len(form.TagOnly) == 0 && len(form.Title) == 0 {
|
||||
if !form.TagOnly && form.Title == "" {
|
||||
// if not "tag only", then the title of the release cannot be empty
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.title_empty"), tplReleaseNew, &form)
|
||||
return
|
||||
}
|
||||
|
||||
var attachmentUUIDs []string
|
||||
if setting.Attachment.Enabled {
|
||||
attachmentUUIDs = form.Files
|
||||
handleTagReleaseError := func(err error) {
|
||||
ctx.Data["Err_TagName"] = true
|
||||
switch {
|
||||
case release_service.IsErrTagAlreadyExists(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.branch.tag_collision", form.TagName), tplReleaseNew, &form)
|
||||
case repo_model.IsErrReleaseAlreadyExist(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
|
||||
case release_service.IsErrInvalidTagName(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
|
||||
case release_service.IsErrProtectedTagName(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
|
||||
default:
|
||||
ctx.ServerError("handleTagReleaseError", err)
|
||||
}
|
||||
}
|
||||
|
||||
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName)
|
||||
if err != nil {
|
||||
if !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.ServerError("GetRelease", err)
|
||||
// prepare the git message for creating a new tag
|
||||
newTagMsg := ""
|
||||
if form.Title != "" && form.AddTagMsg {
|
||||
newTagMsg = form.Title + "\n\n" + form.Content
|
||||
}
|
||||
|
||||
// no release, and tag only
|
||||
if rel == nil && form.TagOnly {
|
||||
if err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, newTagMsg); err != nil {
|
||||
handleTagReleaseError(err)
|
||||
return
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
|
||||
return
|
||||
}
|
||||
|
||||
msg := ""
|
||||
if len(form.Title) > 0 && form.AddTagMsg {
|
||||
msg = form.Title + "\n\n" + form.Content
|
||||
}
|
||||
|
||||
if len(form.TagOnly) > 0 {
|
||||
if err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil {
|
||||
if release_service.IsErrTagAlreadyExists(err) {
|
||||
e := err.(release_service.ErrTagAlreadyExists)
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
if release_service.IsErrInvalidTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_invalid"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
if release_service.IsErrProtectedTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
ctx.ServerError("release_service.CreateNewTag", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
|
||||
return
|
||||
}
|
||||
attachmentUUIDs := util.Iif(setting.Attachment.Enabled, form.Files, nil)
|
||||
|
||||
// no existing release, create a new release
|
||||
if rel == nil {
|
||||
rel = &repo_model.Release{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Repo: ctx.Repo.Repository,
|
||||
@ -469,48 +480,39 @@ func NewReleasePost(ctx *context.Context) {
|
||||
TagName: form.TagName,
|
||||
Target: form.Target,
|
||||
Note: form.Content,
|
||||
IsDraft: len(form.Draft) > 0,
|
||||
IsDraft: form.Draft,
|
||||
IsPrerelease: form.Prerelease,
|
||||
IsTag: false,
|
||||
}
|
||||
|
||||
if err = release_service.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
|
||||
ctx.Data["Err_TagName"] = true
|
||||
switch {
|
||||
case repo_model.IsErrReleaseAlreadyExist(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
|
||||
case release_service.IsErrInvalidTagName(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
|
||||
case release_service.IsErrProtectedTagName(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
|
||||
default:
|
||||
ctx.ServerError("CreateRelease", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !rel.IsTag {
|
||||
ctx.Data["Err_TagName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
|
||||
return
|
||||
}
|
||||
|
||||
rel.Title = form.Title
|
||||
rel.Note = form.Content
|
||||
rel.Target = form.Target
|
||||
rel.IsDraft = len(form.Draft) > 0
|
||||
rel.IsPrerelease = form.Prerelease
|
||||
rel.PublisherID = ctx.Doer.ID
|
||||
rel.IsTag = false
|
||||
|
||||
if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
|
||||
ctx.Data["Err_TagName"] = true
|
||||
ctx.ServerError("UpdateRelease", err)
|
||||
if err = release_service.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, newTagMsg); err != nil {
|
||||
handleTagReleaseError(err)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
|
||||
return
|
||||
}
|
||||
log.Trace("Release created: %s/%s:%s", ctx.Doer.LowerName, ctx.Repo.Repository.Name, form.TagName)
|
||||
|
||||
// tag exists, try to convert it to a real release
|
||||
// old logic: if the release is not a tag (it is a real release), do not update it on the "new release" page
|
||||
// add new logic: if tag-only, do not convert the tag to a release
|
||||
if form.TagOnly || !rel.IsTag {
|
||||
ctx.Data["Err_TagName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
|
||||
return
|
||||
}
|
||||
|
||||
// convert a tag to a real release (set is_tag=false)
|
||||
rel.Title = form.Title
|
||||
rel.Note = form.Content
|
||||
rel.Target = form.Target
|
||||
rel.IsDraft = form.Draft
|
||||
rel.IsPrerelease = form.Prerelease
|
||||
rel.PublisherID = ctx.Doer.ID
|
||||
rel.IsTag = false
|
||||
if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
|
||||
handleTagReleaseError(err)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
|
||||
}
|
||||
|
||||
|
@ -11,60 +11,135 @@ import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewReleasePost(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
RepoID int64
|
||||
UserID int64
|
||||
TagName string
|
||||
Form forms.NewReleaseForm
|
||||
}{
|
||||
{
|
||||
RepoID: 1,
|
||||
UserID: 2,
|
||||
TagName: "v1.1", // pre-existing tag
|
||||
Form: forms.NewReleaseForm{
|
||||
TagName: "newtag",
|
||||
Target: "master",
|
||||
Title: "title",
|
||||
Content: "content",
|
||||
},
|
||||
},
|
||||
{
|
||||
RepoID: 1,
|
||||
UserID: 2,
|
||||
TagName: "newtag",
|
||||
Form: forms.NewReleaseForm{
|
||||
TagName: "newtag",
|
||||
Target: "master",
|
||||
Title: "title",
|
||||
Content: "content",
|
||||
},
|
||||
},
|
||||
} {
|
||||
unittest.PrepareTestEnv(t)
|
||||
unittest.PrepareTestEnv(t)
|
||||
|
||||
get := func(t *testing.T, tagName string) *context.Context {
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1/releases/new?tag="+tagName)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
NewRelease(ctx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
t.Run("NewReleasePage", func(t *testing.T) {
|
||||
ctx := get(t, "v1.1")
|
||||
assert.Empty(t, ctx.Data["ShowCreateTagOnlyButton"])
|
||||
ctx = get(t, "new-tag-name")
|
||||
assert.NotEmpty(t, ctx.Data["ShowCreateTagOnlyButton"])
|
||||
})
|
||||
|
||||
post := func(t *testing.T, form forms.NewReleaseForm) *context.Context {
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1/releases/new")
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
web.SetForm(ctx, &testCase.Form)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
web.SetForm(ctx, &form)
|
||||
NewReleasePost(ctx)
|
||||
unittest.AssertExistsAndLoadBean(t, &repo_model.Release{
|
||||
RepoID: 1,
|
||||
PublisherID: 2,
|
||||
TagName: testCase.Form.TagName,
|
||||
Target: testCase.Form.Target,
|
||||
Title: testCase.Form.Title,
|
||||
Note: testCase.Form.Content,
|
||||
}, unittest.Cond("is_draft=?", len(testCase.Form.Draft) > 0))
|
||||
ctx.Repo.GitRepo.Close()
|
||||
return ctx
|
||||
}
|
||||
|
||||
loadRelease := func(t *testing.T, tagName string) *repo_model.Release {
|
||||
return unittest.GetBean(t, &repo_model.Release{}, unittest.Cond("repo_id=1 AND tag_name=?", tagName))
|
||||
}
|
||||
|
||||
t.Run("NewTagRelease", func(t *testing.T) {
|
||||
post(t, forms.NewReleaseForm{
|
||||
TagName: "newtag",
|
||||
Target: "master",
|
||||
Title: "title",
|
||||
Content: "content",
|
||||
})
|
||||
rel := loadRelease(t, "newtag")
|
||||
require.NotNil(t, rel)
|
||||
assert.False(t, rel.IsTag)
|
||||
assert.Equal(t, "master", rel.Target)
|
||||
assert.Equal(t, "title", rel.Title)
|
||||
assert.Equal(t, "content", rel.Note)
|
||||
})
|
||||
|
||||
t.Run("ReleaseExistsDoUpdate(non-tag)", func(t *testing.T) {
|
||||
ctx := post(t, forms.NewReleaseForm{
|
||||
TagName: "v1.1",
|
||||
Target: "master",
|
||||
Title: "updated-title",
|
||||
Content: "updated-content",
|
||||
})
|
||||
rel := loadRelease(t, "v1.1")
|
||||
require.NotNil(t, rel)
|
||||
assert.False(t, rel.IsTag)
|
||||
assert.Equal(t, "testing-release", rel.Title)
|
||||
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
|
||||
})
|
||||
|
||||
t.Run("ReleaseExistsDoUpdate(tag-only)", func(t *testing.T) {
|
||||
ctx := post(t, forms.NewReleaseForm{
|
||||
TagName: "delete-tag", // a strange name, but it is the only "is_tag=true" fixture
|
||||
Target: "master",
|
||||
Title: "updated-title",
|
||||
Content: "updated-content",
|
||||
TagOnly: true,
|
||||
})
|
||||
rel := loadRelease(t, "delete-tag")
|
||||
require.NotNil(t, rel)
|
||||
assert.True(t, rel.IsTag) // the record should not be updated because the request is "tag-only". TODO: need to improve the logic?
|
||||
assert.Equal(t, "delete-tag", rel.Title)
|
||||
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
|
||||
assert.NotEmpty(t, ctx.Data["ShowCreateTagOnlyButton"]) // still show the "tag-only" button
|
||||
})
|
||||
|
||||
t.Run("ReleaseExistsDoUpdate(tag-release)", func(t *testing.T) {
|
||||
ctx := post(t, forms.NewReleaseForm{
|
||||
TagName: "delete-tag", // a strange name, but it is the only "is_tag=true" fixture
|
||||
Target: "master",
|
||||
Title: "updated-title",
|
||||
Content: "updated-content",
|
||||
})
|
||||
rel := loadRelease(t, "delete-tag")
|
||||
require.NotNil(t, rel)
|
||||
assert.False(t, rel.IsTag) // the tag has been "updated" to be a real "release"
|
||||
assert.Equal(t, "updated-title", rel.Title)
|
||||
assert.Empty(t, ctx.Flash.ErrorMsg)
|
||||
})
|
||||
|
||||
t.Run("TagOnly", func(t *testing.T) {
|
||||
ctx := post(t, forms.NewReleaseForm{
|
||||
TagName: "new-tag-only",
|
||||
Target: "master",
|
||||
Title: "title",
|
||||
Content: "content",
|
||||
TagOnly: true,
|
||||
})
|
||||
rel := loadRelease(t, "new-tag-only")
|
||||
require.NotNil(t, rel)
|
||||
assert.True(t, rel.IsTag)
|
||||
assert.Empty(t, ctx.Flash.ErrorMsg)
|
||||
})
|
||||
|
||||
t.Run("TagOnlyConflict", func(t *testing.T) {
|
||||
ctx := post(t, forms.NewReleaseForm{
|
||||
TagName: "v1.1",
|
||||
Target: "master",
|
||||
Title: "title",
|
||||
Content: "content",
|
||||
TagOnly: true,
|
||||
})
|
||||
rel := loadRelease(t, "v1.1")
|
||||
require.NotNil(t, rel)
|
||||
assert.False(t, rel.IsTag)
|
||||
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCalReleaseNumCommitsBehind(t *testing.T) {
|
||||
|
@ -21,7 +21,13 @@ import (
|
||||
|
||||
// RenderFile renders a file by repos path
|
||||
func RenderFile(ctx *context.Context) {
|
||||
blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
|
||||
var blob *git.Blob
|
||||
var err error
|
||||
if ctx.Repo.TreePath != "" {
|
||||
blob, err = ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
|
||||
} else {
|
||||
blob, err = ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
|
||||
}
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
ctx.NotFound("GetBlobByPath", err)
|
||||
@ -58,7 +64,7 @@ func RenderFile(ctx *context.Context) {
|
||||
}
|
||||
|
||||
rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
|
||||
CurrentRefPath: ctx.Repo.BranchNameSubURL(),
|
||||
CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
|
||||
CurrentTreePath: path.Dir(ctx.Repo.TreePath),
|
||||
}).WithRelativePath(ctx.Repo.TreePath).WithInStandalonePage(true)
|
||||
|
||||
|
@ -51,7 +51,7 @@ func MustBeNotEmpty(ctx *context.Context) {
|
||||
|
||||
// MustBeEditable check that repo can be edited
|
||||
func MustBeEditable(ctx *context.Context) {
|
||||
if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
|
||||
if !ctx.Repo.Repository.CanEnableEditor() {
|
||||
ctx.NotFound("", nil)
|
||||
return
|
||||
}
|
||||
@ -463,13 +463,7 @@ func RedirectDownload(ctx *context.Context) {
|
||||
|
||||
// Download an archive of a repository
|
||||
func Download(ctx *context.Context) {
|
||||
uri := ctx.PathParam("*")
|
||||
ext, tp, err := archiver_service.ParseFileName(uri)
|
||||
if err != nil {
|
||||
ctx.ServerError("ParseFileName", err)
|
||||
return
|
||||
}
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
if err != nil {
|
||||
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
|
||||
ctx.Error(http.StatusBadRequest, err.Error())
|
||||
@ -527,15 +521,9 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
|
||||
// a request that's already in-progress, but the archiver service will just
|
||||
// kind of drop it on the floor if this is the case.
|
||||
func InitiateDownload(ctx *context.Context) {
|
||||
uri := ctx.PathParam("*")
|
||||
ext, tp, err := archiver_service.ParseFileName(uri)
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
if err != nil {
|
||||
ctx.ServerError("ParseFileName", err)
|
||||
return
|
||||
}
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
|
||||
if err != nil {
|
||||
ctx.ServerError("archiver_service.NewRequest", err)
|
||||
ctx.Error(http.StatusBadRequest, "invalid archive request")
|
||||
return
|
||||
}
|
||||
if aReq == nil {
|
||||
|
@ -49,6 +49,15 @@ const (
|
||||
tplDeployKeys templates.TplName = "repo/settings/deploy_keys"
|
||||
)
|
||||
|
||||
func parseEveryoneAccessMode(permission string, allowed ...perm.AccessMode) perm.AccessMode {
|
||||
// if site admin forces repositories to be private, then do not allow any other access mode,
|
||||
// otherwise the "force private" setting would be bypassed
|
||||
if setting.Repository.ForcePrivate {
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
return perm.ParseAccessMode(permission, allowed...)
|
||||
}
|
||||
|
||||
// SettingsCtxData is a middleware that sets all the general context data for the
|
||||
// settings template.
|
||||
func SettingsCtxData(ctx *context.Context) {
|
||||
@ -447,8 +456,9 @@ func SettingsPost(ctx *context.Context) {
|
||||
|
||||
if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeCode,
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeCode,
|
||||
EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultCodeEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead),
|
||||
})
|
||||
} else if !unit_model.TypeCode.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
|
||||
@ -474,7 +484,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
EveryoneAccessMode: perm.ParseAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
|
||||
EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
|
||||
} else {
|
||||
@ -524,6 +534,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
|
||||
EnableDependencies: form.EnableIssueDependencies,
|
||||
},
|
||||
EveryoneAccessMode: parseEveryoneAccessMode(form.DefaultIssuesEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead),
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
|
||||
} else {
|
||||
|
@ -243,7 +243,7 @@ func LastCommit(ctx *context.Context) {
|
||||
ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
|
||||
}
|
||||
}
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["BranchLink"] = branchLink
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoViewList)
|
||||
@ -301,7 +301,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
|
||||
return nil
|
||||
}
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
treeLink := branchLink
|
||||
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
|
@ -42,10 +42,10 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
}
|
||||
defer dataRc.Close()
|
||||
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName())
|
||||
ctx.Data["FileIsSymlink"] = entry.IsLink()
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
||||
commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
@ -92,7 +92,7 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
isDisplayingRendered := !isDisplayingSource
|
||||
|
||||
if fInfo.isLFSFile {
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
|
||||
isRepresentableAsText := fInfo.st.IsRepresentableAsText()
|
||||
@ -170,9 +170,9 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
ctx.Data["IsMarkup"] = true
|
||||
ctx.Data["MarkupType"] = markupType
|
||||
metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx)
|
||||
metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
|
||||
metas["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL()
|
||||
rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
|
||||
CurrentRefPath: ctx.Repo.BranchNameSubURL(),
|
||||
CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
|
||||
CurrentTreePath: path.Dir(ctx.Repo.TreePath),
|
||||
}).
|
||||
WithMarkupType(markupType).
|
||||
@ -262,7 +262,7 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
ctx.Data["MarkupType"] = markupType
|
||||
|
||||
rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
|
||||
CurrentRefPath: ctx.Repo.BranchNameSubURL(),
|
||||
CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
|
||||
CurrentTreePath: path.Dir(ctx.Repo.TreePath),
|
||||
}).
|
||||
WithMarkupType(markupType).
|
||||
|
@ -135,7 +135,7 @@ func prepareToRenderDirectory(ctx *context.Context) {
|
||||
|
||||
if ctx.Repo.TreePath != "" {
|
||||
ctx.Data["HideRepoInfo"] = true
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName())
|
||||
}
|
||||
|
||||
subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
|
||||
@ -249,7 +249,7 @@ func handleRepoEmptyOrBroken(ctx *context.Context) {
|
||||
} else if reallyEmpty {
|
||||
showEmpty = true // the repo is really empty
|
||||
updateContextRepoEmptyAndStatus(ctx, true, repo_model.RepositoryReady)
|
||||
} else if ctx.Repo.Commit == nil {
|
||||
} else if branches, _, _ := ctx.Repo.GitRepo.GetBranches(0, 1); len(branches) == 0 {
|
||||
showEmpty = true // it is not really empty, but there is no branch
|
||||
// at the moment, other repo units like "actions" are not able to handle such case,
|
||||
// so we just mark the repo as empty to prevent from displaying these units.
|
||||
@ -346,7 +346,7 @@ func Home(ctx *context.Context) {
|
||||
|
||||
// prepare the tree path
|
||||
var treeNames, paths []string
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
treeLink := branchLink
|
||||
if ctx.Repo.TreePath != "" {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
@ -189,7 +189,7 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil
|
||||
ctx.Data["MarkupType"] = markupType
|
||||
|
||||
rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
|
||||
CurrentRefPath: ctx.Repo.BranchNameSubURL(),
|
||||
CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
|
||||
CurrentTreePath: path.Join(ctx.Repo.TreePath, subfolder),
|
||||
}).
|
||||
WithMarkupType(markupType).
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/metrics"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
@ -101,7 +102,7 @@ func buildAuthGroup() *auth_service.Group {
|
||||
group.Add(&auth_service.Basic{}) // FIXME: this should be removed and only applied in download and git/lfs routers
|
||||
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
group.Add(&auth_service.ReverseProxy{}) // reverseproxy should before Session, otherwise the header will be ignored if user has login
|
||||
group.Add(&auth_service.ReverseProxy{}) // reverse-proxy should before Session, otherwise the header will be ignored if user has login
|
||||
}
|
||||
group.Add(&auth_service.Session{})
|
||||
|
||||
@ -816,21 +817,23 @@ func registerRoutes(m *web.Router) {
|
||||
m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action)
|
||||
|
||||
reqRepoAdmin := context.RequireRepoAdmin()
|
||||
reqRepoCodeWriter := context.RequireRepoWriter(unit.TypeCode)
|
||||
canEnableEditor := context.CanEnableEditor()
|
||||
reqRepoCodeReader := context.RequireRepoReader(unit.TypeCode)
|
||||
reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases)
|
||||
reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases)
|
||||
reqRepoWikiReader := context.RequireRepoReader(unit.TypeWiki)
|
||||
reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki)
|
||||
reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues)
|
||||
reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoProjectsReader := context.RequireRepoReader(unit.TypeProjects)
|
||||
reqRepoProjectsWriter := context.RequireRepoWriter(unit.TypeProjects)
|
||||
reqRepoActionsReader := context.RequireRepoReader(unit.TypeActions)
|
||||
reqRepoActionsWriter := context.RequireRepoWriter(unit.TypeActions)
|
||||
reqRepoCodeWriter := context.RequireUnitWriter(unit.TypeCode)
|
||||
reqRepoReleaseWriter := context.RequireUnitWriter(unit.TypeReleases)
|
||||
reqRepoReleaseReader := context.RequireUnitReader(unit.TypeReleases)
|
||||
reqRepoIssuesOrPullsWriter := context.RequireUnitWriter(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsReader := context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests)
|
||||
reqRepoProjectsReader := context.RequireUnitReader(unit.TypeProjects)
|
||||
reqRepoProjectsWriter := context.RequireUnitWriter(unit.TypeProjects)
|
||||
reqRepoActionsReader := context.RequireUnitReader(unit.TypeActions)
|
||||
reqRepoActionsWriter := context.RequireUnitWriter(unit.TypeActions)
|
||||
|
||||
// the legacy names "reqRepoXxx" should be renamed to the correct name "reqUnitXxx", these permissions are for units, not repos
|
||||
reqUnitsWithMarkdown := context.RequireUnitReader(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki)
|
||||
reqUnitCodeReader := context.RequireUnitReader(unit.TypeCode)
|
||||
reqUnitIssuesReader := context.RequireUnitReader(unit.TypeIssues)
|
||||
reqUnitPullsReader := context.RequireUnitReader(unit.TypePullRequests)
|
||||
reqUnitWikiReader := context.RequireUnitReader(unit.TypeWiki)
|
||||
reqUnitWikiWriter := context.RequireUnitWriter(unit.TypeWiki)
|
||||
|
||||
reqPackageAccess := func(accessMode perm.AccessMode) func(ctx *context.Context) {
|
||||
return func(ctx *context.Context) {
|
||||
@ -1053,7 +1056,7 @@ func registerRoutes(m *web.Router) {
|
||||
m.Group("/migrate", func() {
|
||||
m.Get("/status", repo.MigrateStatus)
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}/-": migrate
|
||||
|
||||
m.Group("/{username}/{reponame}/settings", func() {
|
||||
@ -1149,56 +1152,54 @@ func registerRoutes(m *web.Router) {
|
||||
// end "/{username}/{reponame}/settings"
|
||||
|
||||
// user/org home, including rss feeds
|
||||
m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
|
||||
m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRefByType(git.RefTypeBranch), repo.SetEditorconfigIfExists, repo.Home)
|
||||
|
||||
// TODO: maybe it should relax the permission to allow "any access"
|
||||
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup)
|
||||
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/find/*", repo.FindFiles)
|
||||
m.Group("/tree-list", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.TreeList)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.TreeList)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.TreeList)
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeList)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeList)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeList)
|
||||
})
|
||||
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
|
||||
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
|
||||
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
|
||||
Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
Post(reqSignIn, context.RepoMustNotBeArchived(), reqUnitPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo code: find, compare, list
|
||||
|
||||
addIssuesPullsViewRoutes := func() {
|
||||
// for /{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
|
||||
m.Get("/posters", repo.IssuePullPosters)
|
||||
m.Group("/{index}", func() {
|
||||
m.Get("/info", repo.GetIssueInfo)
|
||||
m.Get("/attachments", repo.GetIssueAttachments)
|
||||
m.Get("/attachments/{uuid}", repo.GetAttachment)
|
||||
m.Group("/content-history", func() {
|
||||
m.Get("/overview", repo.GetContentHistoryOverview)
|
||||
m.Get("/list", repo.GetContentHistoryList)
|
||||
m.Get("/detail", repo.GetContentHistoryDetail)
|
||||
})
|
||||
})
|
||||
}
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because it would conflict with other routes like "/pulls/{index}"
|
||||
m.Get("/pulls/posters", repo.PullPosters)
|
||||
m.Get("/comments/{id}/attachments", repo.GetCommentAttachments)
|
||||
m.Get("/labels", repo.RetrieveLabelsForList, repo.Labels)
|
||||
m.Get("/milestones", repo.Milestones)
|
||||
m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls)
|
||||
m.Group("/{type:issues|pulls}", func() {
|
||||
m.Group("/{index}", func() {
|
||||
m.Get("/info", repo.GetIssueInfo)
|
||||
m.Get("/attachments", repo.GetIssueAttachments)
|
||||
m.Get("/attachments/{uuid}", repo.GetAttachment)
|
||||
m.Group("/content-history", func() {
|
||||
m.Get("/overview", repo.GetContentHistoryOverview)
|
||||
m.Get("/list", repo.GetContentHistoryList)
|
||||
m.Get("/detail", repo.GetContentHistoryDetail)
|
||||
})
|
||||
})
|
||||
}, context.RepoRef())
|
||||
m.Get("/issues/suggestions", repo.IssueSuggestions)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) // issue/pull attachments, labels, milestones
|
||||
|
||||
m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitIssuesReader)
|
||||
m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader)
|
||||
// end "/{username}/{reponame}": view milestone, label, issue, pull, etc
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/{type:issues|pulls}", func() {
|
||||
m.Get("", repo.Issues)
|
||||
m.Group("/{index}", func() {
|
||||
m.Get("", repo.ViewIssue)
|
||||
})
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker))
|
||||
m.Group("/{username}/{reponame}/{type:issues}", func() {
|
||||
m.Get("", repo.Issues)
|
||||
m.Get("/{index}", repo.ViewIssue)
|
||||
}, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypeExternalTracker))
|
||||
// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
|
||||
@ -1209,11 +1210,10 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
|
||||
})
|
||||
m.Get("/search", repo.SearchRepoIssuesJSON)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssueReader)
|
||||
}, reqUnitIssuesReader)
|
||||
|
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/{type:issues|pulls}", func() {
|
||||
addIssuesPullsRoutes := func() {
|
||||
// for "/{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
|
||||
m.Group("/{index}", func() {
|
||||
m.Post("/title", repo.UpdateIssueTitle)
|
||||
m.Post("/content", repo.UpdateIssueContent)
|
||||
@ -1240,39 +1240,37 @@ func registerRoutes(m *web.Router) {
|
||||
m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue)
|
||||
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
|
||||
m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
|
||||
m.Group("/{index}", func() {
|
||||
m.Post("/content-history/soft-delete", repo.SoftDeleteContentHistory)
|
||||
})
|
||||
|
||||
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
||||
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
|
||||
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
|
||||
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
|
||||
m.Post("/request_review", repo.UpdatePullReviewRequest)
|
||||
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
|
||||
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
|
||||
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
|
||||
m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
|
||||
m.Post("/attachments", repo.UploadIssueAttachment)
|
||||
m.Post("/attachments/remove", repo.DeleteAttachment)
|
||||
|
||||
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
|
||||
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
|
||||
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
|
||||
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
|
||||
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
|
||||
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}
|
||||
m.Group("/{type:issues}", addIssuesPullsRoutes, reqUnitIssuesReader, context.RepoMustNotBeArchived())
|
||||
m.Group("/{type:pulls}", addIssuesPullsRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived())
|
||||
|
||||
m.Group("/comments/{id}", func() {
|
||||
m.Post("", repo.UpdateCommentContent)
|
||||
m.Post("/delete", repo.DeleteComment)
|
||||
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}, reqRepoIssuesOrPullsReader) // edit issue/pull comment
|
||||
|
||||
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
||||
m.Group("/labels", func() {
|
||||
m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel)
|
||||
m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
|
||||
m.Post("/delete", repo.DeleteLabel)
|
||||
m.Post("/initialize", web.Bind(forms.InitializeLabelsForm{}), repo.InitializeLabels)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
|
||||
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
|
||||
m.Group("/milestones", func() {
|
||||
m.Combo("/new").Get(repo.NewMilestone).
|
||||
Post(web.Bind(forms.CreateMilestoneForm{}), repo.NewMilestonePost)
|
||||
@ -1280,11 +1278,15 @@ func registerRoutes(m *web.Router) {
|
||||
m.Post("/{id}/edit", web.Bind(forms.CreateMilestoneForm{}), repo.EditMilestonePost)
|
||||
m.Post("/{id}/{action}", repo.ChangeMilestoneStatus)
|
||||
m.Post("/delete", repo.DeleteMilestone)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
m.Group("/pull", func() {
|
||||
m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}, reqSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
|
||||
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
|
||||
m.Group("", func() {
|
||||
m.Post("/request_review", repo.UpdatePullReviewRequest)
|
||||
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
|
||||
m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
|
||||
m.Post("/pull/{index}/target_branch", repo.UpdatePullRequestTarget)
|
||||
}, reqUnitPullsReader)
|
||||
}, reqSignIn, context.RepoAssignment, context.RepoMustNotBeArchived())
|
||||
// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo code
|
||||
@ -1304,18 +1306,18 @@ func registerRoutes(m *web.Router) {
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost)
|
||||
m.Combo("/_cherrypick/{sha:([a-f0-9]{7,64})}/*").Get(repo.CherryPick).
|
||||
Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost)
|
||||
}, repo.MustBeEditable)
|
||||
}, context.RepoRefByType(git.RefTypeBranch), context.CanWriteToBranch())
|
||||
m.Group("", func() {
|
||||
m.Post("/upload-file", repo.UploadFileToServer)
|
||||
m.Post("/upload-remove", web.Bind(forms.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
|
||||
}, repo.MustBeEditable, repo.MustBeAbleToUpload)
|
||||
}, context.RepoRef(), canEnableEditor, context.RepoMustNotBeArchived())
|
||||
}, repo.MustBeAbleToUpload, reqRepoCodeWriter)
|
||||
}, repo.MustBeEditable, context.RepoMustNotBeArchived())
|
||||
|
||||
m.Group("/branches", func() {
|
||||
m.Group("/_new", func() {
|
||||
m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch)
|
||||
m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch)
|
||||
m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch)
|
||||
m.Post("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.CreateBranch)
|
||||
m.Post("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.CreateBranch)
|
||||
m.Post("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.CreateBranch)
|
||||
}, web.Bind(forms.NewBranchForm{}))
|
||||
m.Post("/delete", repo.DeleteBranchPost)
|
||||
m.Post("/restore", repo.RestoreBranchPost)
|
||||
@ -1324,45 +1326,42 @@ func registerRoutes(m *web.Router) {
|
||||
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
|
||||
|
||||
m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
|
||||
}, reqSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
}, reqSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo code
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo tags
|
||||
m.Group("/tags", func() {
|
||||
m.Get("", repo.TagsList)
|
||||
m.Get("/list", repo.GetTagList)
|
||||
m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
|
||||
m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
|
||||
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
|
||||
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
|
||||
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
|
||||
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
m.Get("/list", repo.GetTagList)
|
||||
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed))
|
||||
m.Post("/tags/delete", reqSignIn, reqRepoCodeWriter, context.RepoMustNotBeArchived(), repo.DeleteTag)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo tags
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // repo releases
|
||||
m.Group("/releases", func() {
|
||||
m.Get("", repo.Releases)
|
||||
m.Get("/tag/*", repo.SingleRelease)
|
||||
m.Get("/latest", repo.LatestRelease)
|
||||
m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
|
||||
m.Get(".atom", feedEnabled, repo.ReleasesFeedAtom)
|
||||
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
|
||||
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
|
||||
m.Get("/releases/attachments/{uuid}", repo.MustBeNotEmpty, repo.GetAttachment)
|
||||
m.Get("/releases/download/{vTag}/{fileName}", repo.MustBeNotEmpty, repo.RedirectDownload)
|
||||
m.Get("/tag/*", repo.SingleRelease)
|
||||
m.Get("/latest", repo.LatestRelease)
|
||||
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed))
|
||||
m.Get("/releases/attachments/{uuid}", repo.GetAttachment)
|
||||
m.Get("/releases/download/{vTag}/{fileName}", repo.RedirectDownload)
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", web.Bind(forms.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Post("/delete", repo.DeleteRelease)
|
||||
m.Post("/attachments", repo.UploadReleaseAttachment)
|
||||
m.Post("/attachments/remove", repo.DeleteAttachment)
|
||||
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
|
||||
}, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoReleaseReader)
|
||||
}, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqRepoReleaseReader)
|
||||
// end "/{username}/{reponame}": repo releases
|
||||
|
||||
m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments
|
||||
@ -1440,43 +1439,48 @@ func registerRoutes(m *web.Router) {
|
||||
m.Group("/{username}/{reponame}/wiki", func() {
|
||||
m.Combo("").
|
||||
Get(repo.Wiki).
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqUnitWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
m.Combo("/*").
|
||||
Get(repo.Wiki).
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqUnitWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
m.Get("/blob_excerpt/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
|
||||
m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
|
||||
m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
|
||||
m.Get("/raw/*", repo.WikiRaw)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) {
|
||||
}, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqUnitWikiReader, func(ctx *context.Context) {
|
||||
ctx.Data["PageIsWiki"] = true
|
||||
ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink(ctx, ctx.Doer)
|
||||
})
|
||||
// end "/{username}/{reponame}/wiki"
|
||||
|
||||
m.Group("/{username}/{reponame}/activity", func() {
|
||||
// activity has its own permission checks
|
||||
m.Get("", repo.Activity)
|
||||
m.Get("/{period}", repo.Activity)
|
||||
m.Group("/contributors", func() {
|
||||
m.Get("", repo.Contributors)
|
||||
m.Get("/data", repo.ContributorsData)
|
||||
})
|
||||
m.Group("/code-frequency", func() {
|
||||
m.Get("", repo.CodeFrequency)
|
||||
m.Get("/data", repo.CodeFrequencyData)
|
||||
})
|
||||
m.Group("/recent-commits", func() {
|
||||
m.Get("", repo.RecentCommits)
|
||||
m.Get("/data", repo.RecentCommitsData)
|
||||
})
|
||||
|
||||
m.Group("", func() {
|
||||
m.Group("/contributors", func() {
|
||||
m.Get("", repo.Contributors)
|
||||
m.Get("/data", repo.ContributorsData)
|
||||
})
|
||||
m.Group("/code-frequency", func() {
|
||||
m.Get("", repo.CodeFrequency)
|
||||
m.Get("/data", repo.CodeFrequencyData)
|
||||
})
|
||||
m.Group("/recent-commits", func() {
|
||||
m.Get("", repo.RecentCommits)
|
||||
m.Get("/data", repo.RecentCommitsData)
|
||||
})
|
||||
}, reqUnitCodeReader)
|
||||
},
|
||||
optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases),
|
||||
context.RepoRef(), repo.MustBeNotEmpty,
|
||||
optSignIn, context.RepoAssignment, repo.MustBeNotEmpty,
|
||||
context.RequireUnitReader(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases),
|
||||
)
|
||||
// end "/{username}/{reponame}/activity"
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/pulls/{index}", func() {
|
||||
m.Get("/{type:pulls}", repo.Issues)
|
||||
m.Group("/{type:pulls}/{index}", func() {
|
||||
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
|
||||
m.Get(".diff", repo.DownloadPullDiff)
|
||||
m.Get(".patch", repo.DownloadPullPatch)
|
||||
@ -1501,7 +1505,7 @@ func registerRoutes(m *web.Router) {
|
||||
}, context.RepoMustNotBeArchived())
|
||||
})
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqRepoPullsReader)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqUnitPullsReader)
|
||||
// end "/{username}/{reponame}/pulls/{index}": repo pull request
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
@ -1521,42 +1525,39 @@ func registerRoutes(m *web.Router) {
|
||||
}, repo.MustBeNotEmpty, context.RepoRef())
|
||||
|
||||
m.Group("/media", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownloadOrLFS)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownloadOrLFS)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownloadOrLFS)
|
||||
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS)
|
||||
// "/*" route is deprecated, and kept for backward compatibility
|
||||
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.SingleDownloadOrLFS)
|
||||
m.Get("/blob/{sha}", repo.DownloadByIDOrLFS)
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.SingleDownloadOrLFS)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.SingleDownloadOrLFS)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.SingleDownloadOrLFS)
|
||||
m.Get("/*", context.RepoRefByType(""), repo.SingleDownloadOrLFS) // "/*" route is deprecated, and kept for backward compatibility
|
||||
}, repo.MustBeNotEmpty)
|
||||
|
||||
m.Group("/raw", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload)
|
||||
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID)
|
||||
// "/*" route is deprecated, and kept for backward compatibility
|
||||
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.SingleDownload)
|
||||
m.Get("/blob/{sha}", repo.DownloadByID)
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.SingleDownload)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.SingleDownload)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.SingleDownload)
|
||||
m.Get("/*", context.RepoRefByType(""), repo.SingleDownload) // "/*" route is deprecated, and kept for backward compatibility
|
||||
}, repo.MustBeNotEmpty)
|
||||
|
||||
m.Group("/render", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RenderFile)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RenderFile)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RenderFile)
|
||||
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.RenderFile)
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.RenderFile)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.RenderFile)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.RenderFile)
|
||||
m.Get("/blob/{sha}", repo.RenderFile)
|
||||
}, repo.MustBeNotEmpty)
|
||||
|
||||
m.Group("/commits", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
|
||||
// "/*" route is deprecated, and kept for backward compatibility
|
||||
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.RefCommits)
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.RefCommits)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.RefCommits)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.RefCommits)
|
||||
m.Get("/*", context.RepoRefByType(""), repo.RefCommits) // "/*" route is deprecated, and kept for backward compatibility
|
||||
}, repo.MustBeNotEmpty)
|
||||
|
||||
m.Group("/blame", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame)
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.RefBlame)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.RefBlame)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.RefBlame)
|
||||
}, repo.MustBeNotEmpty)
|
||||
|
||||
m.Get("/blob_excerpt/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
|
||||
@ -1568,26 +1569,27 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
|
||||
}, repo.MustBeNotEmpty, context.RepoRef())
|
||||
|
||||
m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
m.Get("/rss/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
m.Get("/atom/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
|
||||
m.Group("/src", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
|
||||
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
|
||||
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
|
||||
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
|
||||
m.Get("", func(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink) }) // there is no "{owner}/{repo}/src" page, so redirect to "{owner}/{repo}" to avoid 404
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.Home)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.Home)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.Home)
|
||||
m.Get("/*", context.RepoRefByType(""), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
|
||||
}, repo.SetEditorconfigIfExists)
|
||||
|
||||
m.Get("/forks", context.RepoRef(), repo.Forks)
|
||||
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff)
|
||||
m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit)
|
||||
}, optSignIn, context.RepoAssignment, reqRepoCodeReader)
|
||||
m.Post("/lastcommit/*", context.RepoRefByType(git.RefTypeCommit), repo.LastCommit)
|
||||
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
|
||||
// end "/{username}/{reponame}": repo code
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
m.Get("/watchers", repo.Watchers)
|
||||
m.Get("/search", reqRepoCodeReader, repo.Search)
|
||||
m.Get("/search", reqUnitCodeReader, repo.Search)
|
||||
m.Post("/action/{action}", reqSignIn, repo.Action)
|
||||
}, optSignIn, context.RepoAssignment, context.RepoRef())
|
||||
|
||||
|
@ -563,9 +563,9 @@ func (n *actionsNotifier) CreateRef(ctx context.Context, pusher *user_model.User
|
||||
newNotifyInput(repo, pusher, webhook_module.HookEventCreate).
|
||||
WithRef(refFullName.String()).
|
||||
WithPayload(&api.CreatePayload{
|
||||
Ref: refFullName.String(),
|
||||
Ref: refFullName.String(), // HINT: here is inconsistent with the Webhook's payload: webhook uses ShortName
|
||||
Sha: refID,
|
||||
RefType: refFullName.RefType(),
|
||||
RefType: string(refFullName.RefType()),
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
}).
|
||||
@ -580,8 +580,8 @@ func (n *actionsNotifier) DeleteRef(ctx context.Context, pusher *user_model.User
|
||||
|
||||
newNotifyInput(repo, pusher, webhook_module.HookEventDelete).
|
||||
WithPayload(&api.DeletePayload{
|
||||
Ref: refFullName.String(),
|
||||
RefType: refFullName.RefType(),
|
||||
Ref: refFullName.String(), // HINT: here is inconsistent with the Webhook's payload: webhook uses ShortName
|
||||
RefType: string(refFullName.RefType()),
|
||||
PusherType: api.PusherTypeUser,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
|
@ -3,27 +3,7 @@
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
)
|
||||
|
||||
// IsUserSiteAdmin returns true if current user is a site admin
|
||||
func (ctx *Context) IsUserSiteAdmin() bool {
|
||||
return ctx.IsSigned && ctx.Doer.IsAdmin
|
||||
}
|
||||
|
||||
// IsUserRepoAdmin returns true if current user is admin in current repo
|
||||
func (ctx *Context) IsUserRepoAdmin() bool {
|
||||
return ctx.Repo.IsAdmin()
|
||||
}
|
||||
|
||||
// IsUserRepoWriter returns true if current user has write privilege in current repo
|
||||
func (ctx *Context) IsUserRepoWriter(unitTypes []unit.Type) bool {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanWrite(unitType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -9,31 +9,20 @@ import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// RequireRepoAdmin returns a middleware for requiring repository admin permission
|
||||
func RequireRepoAdmin() func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
ctx.NotFound("RequireRepoAdmin denies the request", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriter returns a middleware for requiring repository write to the specify unitType
|
||||
func RequireRepoWriter(unitType unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanWrite(unitType) {
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CanEnableEditor checks if the user is allowed to write to the branch of the repo
|
||||
func CanEnableEditor() func(ctx *Context) {
|
||||
// CanWriteToBranch checks if the user is allowed to write to the branch of the repo
|
||||
func CanWriteToBranch() func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
|
||||
ctx.NotFound("CanWriteToBranch denies permission", nil)
|
||||
@ -42,75 +31,30 @@ func CanEnableEditor() func(ctx *Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriterOr returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireRepoWriterOr(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
// RequireUnitWriter returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireUnitWriter(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanWrite(unitType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
ctx.NotFound("RequireUnitWriter denies the request", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoReader returns a middleware for requiring repository read to the specify unitType
|
||||
func RequireRepoReader(unitType unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanRead(unitType) {
|
||||
if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
|
||||
return
|
||||
}
|
||||
if log.IsTrace() {
|
||||
if ctx.IsSigned {
|
||||
log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
|
||||
"User in Repo has Permissions: %-+v",
|
||||
ctx.Doer,
|
||||
unitType,
|
||||
ctx.Repo.Repository,
|
||||
ctx.Repo.Permission)
|
||||
} else {
|
||||
log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
|
||||
"Anonymous user in Repo has Permissions: %-+v",
|
||||
unitType,
|
||||
ctx.Repo.Repository,
|
||||
ctx.Repo.Permission)
|
||||
}
|
||||
}
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoReaderOr returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
// RequireUnitReader returns a middleware for requiring repository write to one of the unit permission
|
||||
func RequireUnitReader(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanRead(unitType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if log.IsTrace() {
|
||||
var format string
|
||||
var args []any
|
||||
if ctx.IsSigned {
|
||||
format = "Permission Denied: User %-v cannot read ["
|
||||
args = append(args, ctx.Doer)
|
||||
} else {
|
||||
format = "Permission Denied: Anonymous user cannot read ["
|
||||
if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
|
||||
return
|
||||
}
|
||||
for _, unit := range unitTypes {
|
||||
format += "%-v, "
|
||||
args = append(args, unit)
|
||||
}
|
||||
|
||||
format = format[:len(format)-2] + "] in Repo %-v\n" +
|
||||
"User in Repo has Permissions: %-+v"
|
||||
args = append(args, ctx.Repo.Repository, ctx.Repo.Permission)
|
||||
log.Trace(format, args...)
|
||||
}
|
||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||
ctx.NotFound("RequireUnitReader denies the request", nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,22 +46,27 @@ type PullRequest struct {
|
||||
// Repository contains information to operate a repository
|
||||
type Repository struct {
|
||||
access_model.Permission
|
||||
IsWatching bool
|
||||
|
||||
Repository *repo_model.Repository
|
||||
Owner *user_model.User
|
||||
|
||||
RepoLink string
|
||||
GitRepo *git.Repository
|
||||
|
||||
// these fields indicate the current ref type, for example: ".../src/branch/master" means IsViewBranch=true
|
||||
IsViewBranch bool
|
||||
IsViewTag bool
|
||||
IsViewCommit bool
|
||||
Repository *repo_model.Repository
|
||||
Owner *user_model.User
|
||||
Commit *git.Commit
|
||||
Tag *git.Tag
|
||||
GitRepo *git.Repository
|
||||
RefName string
|
||||
BranchName string
|
||||
TagName string
|
||||
TreePath string
|
||||
CommitID string
|
||||
RepoLink string
|
||||
CloneLink repo_model.CloneLink
|
||||
|
||||
RefFullName git.RefName
|
||||
BranchName string
|
||||
TagName string
|
||||
TreePath string
|
||||
|
||||
// Commit it is always set to the commit for the branch or tag
|
||||
Commit *git.Commit
|
||||
CommitID string
|
||||
|
||||
CommitsCount int64
|
||||
|
||||
PullRequest *PullRequest
|
||||
@ -149,7 +154,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use
|
||||
}, err
|
||||
}
|
||||
|
||||
// CanUseTimetracker returns whether or not a user can use the timetracker.
|
||||
// CanUseTimetracker returns whether a user can use the timetracker.
|
||||
func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model.Issue, user *user_model.User) bool {
|
||||
// Checking for following:
|
||||
// 1. Is timetracker enabled
|
||||
@ -199,33 +204,13 @@ func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool,
|
||||
})
|
||||
}
|
||||
|
||||
// BranchNameSubURL sub-URL for the BranchName field
|
||||
func (r *Repository) BranchNameSubURL() string {
|
||||
switch {
|
||||
case r.IsViewBranch:
|
||||
return "branch/" + util.PathEscapeSegments(r.BranchName)
|
||||
case r.IsViewTag:
|
||||
return "tag/" + util.PathEscapeSegments(r.TagName)
|
||||
case r.IsViewCommit:
|
||||
return "commit/" + util.PathEscapeSegments(r.CommitID)
|
||||
}
|
||||
log.Error("Unknown view type for repo: %v", r)
|
||||
return ""
|
||||
}
|
||||
|
||||
// FileExists returns true if a file exists in the given repo branch
|
||||
func (r *Repository) FileExists(path, branch string) (bool, error) {
|
||||
if branch == "" {
|
||||
branch = r.Repository.DefaultBranch
|
||||
}
|
||||
commit, err := r.GitRepo.GetBranchCommit(branch)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if _, err := commit.GetTreeEntryByPath(path); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
// RefTypeNameSubURL makes a sub-url for the current ref (branch/tag/commit) field, for example:
|
||||
// * "branch/master"
|
||||
// * "tag/v1.0.0"
|
||||
// * "commit/123456"
|
||||
// It is usually used to construct a link like ".../src/{{RefTypeNameSubURL}}/{{PathEscapeSegments TreePath}}"
|
||||
func (r *Repository) RefTypeNameSubURL() string {
|
||||
return r.RefFullName.RefWebLinkPath()
|
||||
}
|
||||
|
||||
// GetEditorconfig returns the .editorconfig definition if found in the
|
||||
@ -398,33 +383,25 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
|
||||
|
||||
// RepoAssignment returns a middleware to handle repository assignment
|
||||
func RepoAssignment(ctx *Context) {
|
||||
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
|
||||
// FIXME: it should panic in dev/test modes to have a clear behavior
|
||||
if !setting.IsProd || setting.IsInTesting {
|
||||
panic("RepoAssignment should not be executed twice")
|
||||
}
|
||||
return
|
||||
if ctx.Data["Repository"] != nil {
|
||||
setting.PanicInDevOrTesting("RepoAssignment should not be executed twice")
|
||||
}
|
||||
ctx.Data["repoAssignmentExecuted"] = true
|
||||
|
||||
var (
|
||||
owner *user_model.User
|
||||
err error
|
||||
)
|
||||
|
||||
var err error
|
||||
userName := ctx.PathParam("username")
|
||||
repoName := ctx.PathParam("reponame")
|
||||
repoName = strings.TrimSuffix(repoName, ".git")
|
||||
if setting.Other.EnableFeed {
|
||||
ctx.Data["EnableFeed"] = true
|
||||
repoName = strings.TrimSuffix(repoName, ".rss")
|
||||
repoName = strings.TrimSuffix(repoName, ".atom")
|
||||
}
|
||||
|
||||
// Check if the user is the same as the repository owner
|
||||
if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) {
|
||||
owner = ctx.Doer
|
||||
ctx.Repo.Owner = ctx.Doer
|
||||
} else {
|
||||
owner, err = user_model.GetUserByName(ctx, userName)
|
||||
ctx.Repo.Owner, err = user_model.GetUserByName(ctx, userName)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
// go-get does not support redirects
|
||||
@ -447,10 +424,8 @@ func RepoAssignment(ctx *Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Repo.Owner = owner
|
||||
ctx.ContextUser = owner
|
||||
ctx.ContextUser = ctx.Repo.Owner
|
||||
ctx.Data["ContextUser"] = ctx.ContextUser
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
|
||||
// redirect link to wiki
|
||||
if strings.HasSuffix(repoName, ".wiki") {
|
||||
@ -473,10 +448,10 @@ func RepoAssignment(ctx *Context) {
|
||||
}
|
||||
|
||||
// Get repository.
|
||||
repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
|
||||
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Repo.Owner.ID, repoName)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName)
|
||||
redirectRepoID, err := repo_model.LookupRedirect(ctx, ctx.Repo.Owner.ID, repoName)
|
||||
if err == nil {
|
||||
RedirectToRepo(ctx.Base, redirectRepoID)
|
||||
} else if repo_model.IsErrRedirectNotExist(err) {
|
||||
@ -493,7 +468,7 @@ func RepoAssignment(ctx *Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
repo.Owner = owner
|
||||
repo.Owner = ctx.Repo.Owner
|
||||
|
||||
repoAssignment(ctx, repo)
|
||||
if ctx.Written() {
|
||||
@ -502,12 +477,7 @@ func RepoAssignment(ctx *Context) {
|
||||
|
||||
ctx.Repo.RepoLink = repo.Link()
|
||||
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
|
||||
ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
|
||||
|
||||
if setting.Other.EnableFeed {
|
||||
ctx.Data["EnableFeed"] = true
|
||||
ctx.Data["FeedURL"] = ctx.Repo.RepoLink
|
||||
}
|
||||
ctx.Data["FeedURL"] = ctx.Repo.RepoLink
|
||||
|
||||
unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker)
|
||||
if err == nil {
|
||||
@ -534,12 +504,9 @@ func RepoAssignment(ctx *Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = owner.Name + "/" + repo.Name
|
||||
ctx.Data["Title"] = repo.Owner.Name + "/" + repo.Name
|
||||
ctx.Data["Repository"] = repo
|
||||
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
|
||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
|
||||
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
|
||||
ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization()
|
||||
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
|
||||
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
|
||||
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
|
||||
@ -607,7 +574,6 @@ func RepoAssignment(ctx *Context) {
|
||||
|
||||
// Disable everything when the repo is being created
|
||||
if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {
|
||||
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
||||
if !isHomeOrSettings {
|
||||
ctx.Redirect(ctx.Repo.RepoLink)
|
||||
}
|
||||
@ -615,9 +581,7 @@ func RepoAssignment(ctx *Context) {
|
||||
}
|
||||
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
if !setting.IsProd || setting.IsInTesting {
|
||||
panic("RepoAssignment: GitRepo should be nil")
|
||||
}
|
||||
setting.PanicInDevOrTesting("RepoAssignment: GitRepo should be nil")
|
||||
_ = ctx.Repo.GitRepo.Close()
|
||||
ctx.Repo.GitRepo = nil
|
||||
}
|
||||
@ -627,7 +591,6 @@ func RepoAssignment(ctx *Context) {
|
||||
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
|
||||
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
|
||||
ctx.Repo.Repository.MarkAsBrokenEmpty()
|
||||
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
||||
// Only allow access to base of repo or settings
|
||||
if !isHomeOrSettings {
|
||||
ctx.Redirect(ctx.Repo.RepoLink)
|
||||
@ -640,7 +603,6 @@ func RepoAssignment(ctx *Context) {
|
||||
|
||||
// Stop at this point when the repo is empty.
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
||||
return
|
||||
}
|
||||
|
||||
@ -666,22 +628,6 @@ func RepoAssignment(ctx *Context) {
|
||||
|
||||
ctx.Data["BranchesCount"] = branchesTotal
|
||||
|
||||
// If no branch is set in the request URL, try to guess a default one.
|
||||
if len(ctx.Repo.BranchName) == 0 {
|
||||
if len(ctx.Repo.Repository.DefaultBranch) > 0 && ctx.Repo.GitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
|
||||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
|
||||
} else {
|
||||
ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository)
|
||||
if ctx.Repo.BranchName == "" {
|
||||
// If it still can't get a default branch, fall back to default branch from setting.
|
||||
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
|
||||
ctx.Repo.BranchName = setting.Repository.DefaultBranch
|
||||
}
|
||||
}
|
||||
ctx.Repo.RefName = ctx.Repo.BranchName
|
||||
}
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
|
||||
// People who have push access or have forked repository can propose a new pull request.
|
||||
canPush := ctx.Repo.CanWrite(unit_model.TypeCode) ||
|
||||
(ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
|
||||
@ -724,32 +670,19 @@ func RepoAssignment(ctx *Context) {
|
||||
}
|
||||
|
||||
if ctx.FormString("go-get") == "1" {
|
||||
ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, owner.Name, repo.Name)
|
||||
ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, repo.Owner.Name, repo.Name)
|
||||
fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
|
||||
ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
|
||||
ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
|
||||
}
|
||||
}
|
||||
|
||||
// RepoRefType type of repo reference
|
||||
type RepoRefType int
|
||||
|
||||
const (
|
||||
// RepoRefUnknown is for legacy support, makes the code to "guess" the ref type
|
||||
RepoRefUnknown RepoRefType = iota
|
||||
RepoRefBranch
|
||||
RepoRefTag
|
||||
RepoRefCommit
|
||||
RepoRefBlob
|
||||
)
|
||||
|
||||
const headRefName = "HEAD"
|
||||
|
||||
// RepoRef handles repository reference names when the ref name is not
|
||||
// explicitly given
|
||||
func RepoRef() func(*Context) {
|
||||
// since no ref name is explicitly specified, ok to just use branch
|
||||
return RepoRefByType(RepoRefBranch)
|
||||
// old code does: return RepoRefByType(git.RefTypeBranch)
|
||||
// in most cases, it is an abuse, so we just disable it completely and fix the abuses one by one (if there is anything wrong)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string {
|
||||
@ -765,32 +698,29 @@ func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool
|
||||
return ""
|
||||
}
|
||||
|
||||
func getRefNameLegacy(ctx *Base, repo *Repository, reqPath, extraRef string) (string, RepoRefType) {
|
||||
func getRefNameLegacy(ctx *Base, repo *Repository, reqPath, extraRef string) (string, git.RefType) {
|
||||
reqRefPath := path.Join(extraRef, reqPath)
|
||||
reqRefPathParts := strings.Split(reqRefPath, "/")
|
||||
if refName := getRefName(ctx, repo, reqRefPath, RepoRefBranch); refName != "" {
|
||||
return refName, RepoRefBranch
|
||||
if refName := getRefName(ctx, repo, reqRefPath, git.RefTypeBranch); refName != "" {
|
||||
return refName, git.RefTypeBranch
|
||||
}
|
||||
if refName := getRefName(ctx, repo, reqRefPath, RepoRefTag); refName != "" {
|
||||
return refName, RepoRefTag
|
||||
if refName := getRefName(ctx, repo, reqRefPath, git.RefTypeTag); refName != "" {
|
||||
return refName, git.RefTypeTag
|
||||
}
|
||||
if git.IsStringLikelyCommitID(git.ObjectFormatFromName(repo.Repository.ObjectFormatName), reqRefPathParts[0]) {
|
||||
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
|
||||
repo.TreePath = strings.Join(reqRefPathParts[1:], "/")
|
||||
return reqRefPathParts[0], RepoRefCommit
|
||||
}
|
||||
if refName := getRefName(ctx, repo, reqPath, RepoRefBlob); refName != "" {
|
||||
return refName, RepoRefBlob
|
||||
return reqRefPathParts[0], git.RefTypeCommit
|
||||
}
|
||||
// FIXME: the old code falls back to default branch if "ref" doesn't exist, there could be an edge case:
|
||||
// "README?ref=no-such" would read the README file from the default branch, but the user might expect a 404
|
||||
repo.TreePath = reqPath
|
||||
return repo.Repository.DefaultBranch, RepoRefBranch
|
||||
return repo.Repository.DefaultBranch, git.RefTypeBranch
|
||||
}
|
||||
|
||||
func getRefName(ctx *Base, repo *Repository, path string, pathType RepoRefType) string {
|
||||
switch pathType {
|
||||
case RepoRefBranch:
|
||||
func getRefName(ctx *Base, repo *Repository, path string, refType git.RefType) string {
|
||||
switch refType {
|
||||
case git.RefTypeBranch:
|
||||
ref := getRefNameFromPath(repo, path, repo.GitRepo.IsBranchExist)
|
||||
if len(ref) == 0 {
|
||||
// check if ref is HEAD
|
||||
@ -820,9 +750,9 @@ func getRefName(ctx *Base, repo *Repository, path string, pathType RepoRefType)
|
||||
}
|
||||
|
||||
return ref
|
||||
case RepoRefTag:
|
||||
case git.RefTypeTag:
|
||||
return getRefNameFromPath(repo, path, repo.GitRepo.IsTagExist)
|
||||
case RepoRefCommit:
|
||||
case git.RefTypeCommit:
|
||||
parts := strings.Split(path, "/")
|
||||
if git.IsStringLikelyCommitID(repo.GetObjectFormat(), parts[0], 7) {
|
||||
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
|
||||
@ -839,67 +769,62 @@ func getRefName(ctx *Base, repo *Repository, path string, pathType RepoRefType)
|
||||
repo.TreePath = strings.Join(parts[1:], "/")
|
||||
return commit.ID.String()
|
||||
}
|
||||
case RepoRefBlob:
|
||||
_, err := repo.GitRepo.GetBlob(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
default:
|
||||
panic(fmt.Sprintf("Unrecognized path type: %v", pathType))
|
||||
panic(fmt.Sprintf("Unrecognized ref type: %v", refType))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type RepoRefByTypeOptions struct {
|
||||
IgnoreNotExistErr bool
|
||||
func repoRefFullName(typ git.RefType, shortName string) git.RefName {
|
||||
switch typ {
|
||||
case git.RefTypeBranch:
|
||||
return git.RefNameFromBranch(shortName)
|
||||
case git.RefTypeTag:
|
||||
return git.RefNameFromTag(shortName)
|
||||
case git.RefTypeCommit:
|
||||
return git.RefNameFromCommit(shortName)
|
||||
default:
|
||||
setting.PanicInDevOrTesting("Unknown RepoRefType: %v", typ)
|
||||
return git.RefNameFromBranch("main") // just a dummy result, it shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
// RepoRefByType handles repository reference name for a specific type
|
||||
// of repository reference
|
||||
func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) {
|
||||
opt := util.OptionalArg(opts)
|
||||
func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
return func(ctx *Context) {
|
||||
var err error
|
||||
refType := detectRefType
|
||||
// Empty repository does not have reference information.
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
// assume the user is viewing the (non-existent) default branch
|
||||
ctx.Repo.IsViewBranch = true
|
||||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch(ctx.Repo.BranchName)
|
||||
// these variables are used by the template to "add/upload" new files
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
ctx.Data["TreePath"] = ""
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
refName string
|
||||
err error
|
||||
)
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get default branch.
|
||||
var refShortName string
|
||||
reqPath := ctx.PathParam("*")
|
||||
if reqPath == "" {
|
||||
refName = ctx.Repo.Repository.DefaultBranch
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
refShortName = ctx.Repo.Repository.DefaultBranch
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(refShortName) {
|
||||
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
|
||||
if err == nil && len(brs) != 0 {
|
||||
refName = brs[0].Name
|
||||
refShortName = brs[0].Name
|
||||
} else if len(brs) == 0 {
|
||||
log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
|
||||
} else {
|
||||
log.Error("GetBranches error: %v", err)
|
||||
}
|
||||
}
|
||||
ctx.Repo.RefName = refName
|
||||
ctx.Repo.BranchName = refName
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
|
||||
ctx.Repo.BranchName = refShortName
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
|
||||
if err == nil {
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
|
||||
@ -911,37 +836,39 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
}
|
||||
ctx.Repo.IsViewBranch = true
|
||||
} else { // there is a path in request
|
||||
guessLegacyPath := refType == RepoRefUnknown
|
||||
guessLegacyPath := refType == ""
|
||||
if guessLegacyPath {
|
||||
refName, refType = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "")
|
||||
refShortName, refType = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "")
|
||||
} else {
|
||||
refName = getRefName(ctx.Base, ctx.Repo, reqPath, refType)
|
||||
refShortName = getRefName(ctx.Base, ctx.Repo, reqPath, refType)
|
||||
}
|
||||
ctx.Repo.RefName = refName
|
||||
ctx.Repo.RefFullName = repoRefFullName(refType, refShortName)
|
||||
isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
|
||||
if isRenamedBranch && has {
|
||||
renamedBranchName := ctx.Data["RenamedBranchName"].(string)
|
||||
ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
|
||||
link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1)
|
||||
ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refShortName, renamedBranchName))
|
||||
link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refShortName), util.PathEscapeSegments(renamedBranchName), 1)
|
||||
ctx.Redirect(link)
|
||||
return
|
||||
}
|
||||
|
||||
if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
if refType == git.RefTypeBranch && ctx.Repo.GitRepo.IsBranchExist(refShortName) {
|
||||
ctx.Repo.IsViewBranch = true
|
||||
ctx.Repo.BranchName = refName
|
||||
ctx.Repo.BranchName = refShortName
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
} else if refType == git.RefTypeTag && ctx.Repo.GitRepo.IsTagExist(refShortName) {
|
||||
ctx.Repo.IsViewTag = true
|
||||
ctx.Repo.TagName = refName
|
||||
ctx.Repo.RefFullName = git.RefNameFromTag(refShortName)
|
||||
ctx.Repo.TagName = refShortName
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refShortName)
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
ctx.NotFound("GetTagCommit", err)
|
||||
@ -951,25 +878,23 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) {
|
||||
} else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refShortName, 7) {
|
||||
ctx.Repo.IsViewCommit = true
|
||||
ctx.Repo.CommitID = refName
|
||||
ctx.Repo.RefFullName = git.RefNameFromCommit(refShortName)
|
||||
ctx.Repo.CommitID = refShortName
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refShortName)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetCommit", err)
|
||||
return
|
||||
}
|
||||
// If short commit ID add canonical link header
|
||||
if len(refName) < ctx.Repo.GetObjectFormat().FullLength() {
|
||||
canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
|
||||
if len(refShortName) < ctx.Repo.GetObjectFormat().FullLength() {
|
||||
canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refShortName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
|
||||
ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL))
|
||||
}
|
||||
} else {
|
||||
if opt.IgnoreNotExistErr {
|
||||
return
|
||||
}
|
||||
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
|
||||
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refShortName))
|
||||
return
|
||||
}
|
||||
|
||||
@ -979,22 +904,26 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
|
||||
redirect := path.Join(
|
||||
ctx.Repo.RepoLink,
|
||||
util.PathEscapeSegments(prefix),
|
||||
ctx.Repo.BranchNameSubURL(),
|
||||
ctx.Repo.RefTypeNameSubURL(),
|
||||
util.PathEscapeSegments(ctx.Repo.TreePath))
|
||||
ctx.Redirect(redirect)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
ctx.Data["RefName"] = ctx.Repo.RefName
|
||||
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["TagName"] = ctx.Repo.TagName
|
||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||
ctx.Data["RefFullName"] = ctx.Repo.RefFullName
|
||||
ctx.Data["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
||||
|
||||
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
|
||||
ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
|
||||
ctx.Data["TagName"] = ctx.Repo.TagName
|
||||
|
||||
ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
|
||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||
|
||||
ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef
|
||||
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
|
||||
|
@ -469,7 +469,7 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release
|
||||
Repo: rel.Repo,
|
||||
IsPrivate: rel.Repo.IsPrivate,
|
||||
Content: rel.Title,
|
||||
RefName: rel.TagName, // FIXME: use a full ref name?
|
||||
RefName: git.RefNameFromTag(rel.TagName).String(), // Other functions in this file all use "refFullName.String()"
|
||||
}); err != nil {
|
||||
log.Error("NotifyWatchers: %v", err)
|
||||
}
|
||||
|
@ -110,41 +110,51 @@ type RepoSettingForm struct {
|
||||
EnablePrune bool
|
||||
|
||||
// Advanced settings
|
||||
EnableCode bool
|
||||
EnableWiki bool
|
||||
EnableExternalWiki bool
|
||||
DefaultWikiBranch string
|
||||
DefaultWikiEveryoneAccess string
|
||||
ExternalWikiURL string
|
||||
EnableCode bool
|
||||
DefaultCodeEveryoneAccess string
|
||||
|
||||
EnableWiki bool
|
||||
EnableExternalWiki bool
|
||||
DefaultWikiBranch string
|
||||
DefaultWikiEveryoneAccess string
|
||||
ExternalWikiURL string
|
||||
|
||||
EnableIssues bool
|
||||
DefaultIssuesEveryoneAccess string
|
||||
EnableExternalTracker bool
|
||||
ExternalTrackerURL string
|
||||
TrackerURLFormat string
|
||||
TrackerIssueStyle string
|
||||
ExternalTrackerRegexpPattern string
|
||||
EnableCloseIssuesViaCommitInAnyBranch bool
|
||||
EnableProjects bool
|
||||
ProjectsMode string
|
||||
EnableReleases bool
|
||||
EnablePackages bool
|
||||
EnablePulls bool
|
||||
EnableActions bool
|
||||
PullsIgnoreWhitespace bool
|
||||
PullsAllowMerge bool
|
||||
PullsAllowRebase bool
|
||||
PullsAllowRebaseMerge bool
|
||||
PullsAllowSquash bool
|
||||
PullsAllowFastForwardOnly bool
|
||||
PullsAllowManualMerge bool
|
||||
PullsDefaultMergeStyle string
|
||||
EnableAutodetectManualMerge bool
|
||||
PullsAllowRebaseUpdate bool
|
||||
DefaultDeleteBranchAfterMerge bool
|
||||
DefaultAllowMaintainerEdit bool
|
||||
EnableTimetracker bool
|
||||
AllowOnlyContributorsToTrackTime bool
|
||||
EnableIssueDependencies bool
|
||||
IsArchived bool
|
||||
|
||||
EnableProjects bool
|
||||
ProjectsMode string
|
||||
|
||||
EnableReleases bool
|
||||
|
||||
EnablePackages bool
|
||||
|
||||
EnablePulls bool
|
||||
PullsIgnoreWhitespace bool
|
||||
PullsAllowMerge bool
|
||||
PullsAllowRebase bool
|
||||
PullsAllowRebaseMerge bool
|
||||
PullsAllowSquash bool
|
||||
PullsAllowFastForwardOnly bool
|
||||
PullsAllowManualMerge bool
|
||||
PullsDefaultMergeStyle string
|
||||
EnableAutodetectManualMerge bool
|
||||
PullsAllowRebaseUpdate bool
|
||||
DefaultDeleteBranchAfterMerge bool
|
||||
DefaultAllowMaintainerEdit bool
|
||||
EnableTimetracker bool
|
||||
AllowOnlyContributorsToTrackTime bool
|
||||
EnableIssueDependencies bool
|
||||
|
||||
EnableActions bool
|
||||
|
||||
IsArchived bool
|
||||
|
||||
// Signing Settings
|
||||
TrustModel string
|
||||
@ -652,8 +662,8 @@ type NewReleaseForm struct {
|
||||
Target string `form:"tag_target" binding:"Required;MaxSize(255)"`
|
||||
Title string `binding:"MaxSize(255)"`
|
||||
Content string
|
||||
Draft string
|
||||
TagOnly string
|
||||
Draft bool
|
||||
TagOnly bool
|
||||
Prerelease bool
|
||||
AddTagMsg bool
|
||||
Files []string
|
||||
|
@ -250,8 +250,9 @@ func GetRefEndNamesAndURLs(issues []*issues_model.Issue, repoLink string) (map[i
|
||||
issueRefURLs := make(map[int64]string, len(issues))
|
||||
for _, issue := range issues {
|
||||
if issue.Ref != "" {
|
||||
issueRefEndNames[issue.ID] = git.RefName(issue.Ref).ShortName()
|
||||
issueRefURLs[issue.ID] = git.RefURL(repoLink, issue.Ref)
|
||||
ref := git.RefName(issue.Ref)
|
||||
issueRefEndNames[issue.ID] = ref.ShortName()
|
||||
issueRefURLs[issue.ID] = repoLink + "/src/" + ref.RefWebLinkPath()
|
||||
}
|
||||
}
|
||||
return issueRefEndNames, issueRefURLs
|
||||
|
@ -87,6 +87,7 @@ type mirrorSyncResult struct {
|
||||
/*
|
||||
// * [new tag] v0.1.8 -> v0.1.8
|
||||
// * [new branch] master -> origin/master
|
||||
// * [new ref] refs/pull/2/head -> refs/pull/2/head"
|
||||
// - [deleted] (none) -> origin/test // delete a branch
|
||||
// - [deleted] (none) -> 1 // delete a tag
|
||||
// 957a993..a87ba5f test -> origin/test
|
||||
@ -117,6 +118,11 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
|
||||
refName: git.RefNameFromBranch(refName),
|
||||
oldCommitID: gitShortEmptySha,
|
||||
})
|
||||
case strings.HasPrefix(lines[i], " * [new ref]"): // new reference
|
||||
results = append(results, &mirrorSyncResult{
|
||||
refName: git.RefName(refName),
|
||||
oldCommitID: gitShortEmptySha,
|
||||
})
|
||||
case strings.HasPrefix(lines[i], " - "): // Delete reference
|
||||
isTag := !strings.HasPrefix(refName, remoteName+"/")
|
||||
var refFullName git.RefName
|
||||
@ -159,8 +165,15 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
|
||||
log.Error("Expect two SHAs but not what found: %q", lines[i])
|
||||
continue
|
||||
}
|
||||
var refFullName git.RefName
|
||||
if strings.HasPrefix(refName, "refs/") {
|
||||
refFullName = git.RefName(refName)
|
||||
} else {
|
||||
refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
|
||||
}
|
||||
|
||||
results = append(results, &mirrorSyncResult{
|
||||
refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")),
|
||||
refName: refFullName,
|
||||
oldCommitID: shas[0],
|
||||
newCommitID: shas[1],
|
||||
})
|
||||
|
@ -31,19 +31,20 @@ import (
|
||||
// handle elsewhere.
|
||||
type ArchiveRequest struct {
|
||||
RepoID int64
|
||||
refName string
|
||||
Type git.ArchiveType
|
||||
CommitID string
|
||||
|
||||
archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id"
|
||||
}
|
||||
|
||||
// ErrUnknownArchiveFormat request archive format is not supported
|
||||
type ErrUnknownArchiveFormat struct {
|
||||
RequestFormat string
|
||||
RequestNameType string
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (err ErrUnknownArchiveFormat) Error() string {
|
||||
return fmt.Sprintf("unknown format: %s", err.RequestFormat)
|
||||
return fmt.Sprintf("unknown format: %s", err.RequestNameType)
|
||||
}
|
||||
|
||||
// Is implements error
|
||||
@ -54,12 +55,12 @@ func (ErrUnknownArchiveFormat) Is(err error) bool {
|
||||
|
||||
// RepoRefNotFoundError is returned when a requested reference (commit, tag) was not found.
|
||||
type RepoRefNotFoundError struct {
|
||||
RefName string
|
||||
RefShortName string
|
||||
}
|
||||
|
||||
// Error implements error.
|
||||
func (e RepoRefNotFoundError) Error() string {
|
||||
return fmt.Sprintf("unrecognized repository reference: %s", e.RefName)
|
||||
return fmt.Sprintf("unrecognized repository reference: %s", e.RefShortName)
|
||||
}
|
||||
|
||||
func (e RepoRefNotFoundError) Is(err error) bool {
|
||||
@ -67,43 +68,23 @@ func (e RepoRefNotFoundError) Is(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func ParseFileName(uri string) (ext string, tp git.ArchiveType, err error) {
|
||||
switch {
|
||||
case strings.HasSuffix(uri, ".zip"):
|
||||
ext = ".zip"
|
||||
tp = git.ZIP
|
||||
case strings.HasSuffix(uri, ".tar.gz"):
|
||||
ext = ".tar.gz"
|
||||
tp = git.TARGZ
|
||||
case strings.HasSuffix(uri, ".bundle"):
|
||||
ext = ".bundle"
|
||||
tp = git.BUNDLE
|
||||
default:
|
||||
return "", 0, ErrUnknownArchiveFormat{RequestFormat: uri}
|
||||
}
|
||||
return ext, tp, nil
|
||||
}
|
||||
|
||||
// NewRequest creates an archival request, based on the URI. The
|
||||
// resulting ArchiveRequest is suitable for being passed to Await()
|
||||
// if it's determined that the request still needs to be satisfied.
|
||||
func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git.ArchiveType) (*ArchiveRequest, error) {
|
||||
if fileType < git.ZIP || fileType > git.BUNDLE {
|
||||
return nil, ErrUnknownArchiveFormat{RequestFormat: fileType.String()}
|
||||
}
|
||||
|
||||
r := &ArchiveRequest{
|
||||
RepoID: repoID,
|
||||
refName: refName,
|
||||
Type: fileType,
|
||||
func NewRequest(repoID int64, repo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) {
|
||||
// here the archiveRefShortName is not a clear ref, it could be a tag, branch or commit id
|
||||
archiveRefShortName, archiveType := git.SplitArchiveNameType(archiveRefExt)
|
||||
if archiveType == git.ArchiveUnknown {
|
||||
return nil, ErrUnknownArchiveFormat{archiveRefExt}
|
||||
}
|
||||
|
||||
// Get corresponding commit.
|
||||
commitID, err := repo.ConvertToGitID(r.refName)
|
||||
commitID, err := repo.ConvertToGitID(archiveRefShortName)
|
||||
if err != nil {
|
||||
return nil, RepoRefNotFoundError{RefName: r.refName}
|
||||
return nil, RepoRefNotFoundError{RefShortName: archiveRefShortName}
|
||||
}
|
||||
|
||||
r := &ArchiveRequest{RepoID: repoID, archiveRefShortName: archiveRefShortName, Type: archiveType}
|
||||
r.CommitID = commitID.String()
|
||||
return r, nil
|
||||
}
|
||||
@ -111,11 +92,11 @@ func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git
|
||||
// GetArchiveName returns the name of the caller, based on the ref used by the
|
||||
// caller to create this request.
|
||||
func (aReq *ArchiveRequest) GetArchiveName() string {
|
||||
return strings.ReplaceAll(aReq.refName, "/", "-") + "." + aReq.Type.String()
|
||||
return strings.ReplaceAll(aReq.archiveRefShortName, "/", "-") + "." + aReq.Type.String()
|
||||
}
|
||||
|
||||
// Await awaits the completion of an ArchiveRequest. If the archive has
|
||||
// already been prepared the method returns immediately. Otherwise an archiver
|
||||
// already been prepared the method returns immediately. Otherwise, an archiver
|
||||
// process will be started and its completion awaited. On success the returned
|
||||
// RepoArchiver may be used to download the archive. Note that even if the
|
||||
// context is cancelled/times out a started archiver will still continue to run
|
||||
@ -208,8 +189,8 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
|
||||
|
||||
rd, w := io.Pipe()
|
||||
defer func() {
|
||||
w.Close()
|
||||
rd.Close()
|
||||
_ = w.Close()
|
||||
_ = rd.Close()
|
||||
}()
|
||||
done := make(chan error, 1) // Ensure that there is some capacity which will ensure that the goroutine below can always finish
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, archiver.RepoID)
|
||||
@ -230,7 +211,7 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
|
||||
}
|
||||
}()
|
||||
|
||||
if archiver.Type == git.BUNDLE {
|
||||
if archiver.Type == git.ArchiveBundle {
|
||||
err = gitRepo.CreateBundle(
|
||||
ctx,
|
||||
archiver.CommitID,
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
|
||||
_ "code.gitea.io/gitea/models/actions"
|
||||
@ -31,47 +30,47 @@ func TestArchive_Basic(t *testing.T) {
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
|
||||
bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bogusReq)
|
||||
assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName())
|
||||
|
||||
// Check a series of bogus requests.
|
||||
// Step 1, valid commit with a bad extension.
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, 100)
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".unknown")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bogusReq)
|
||||
|
||||
// Step 2, missing commit.
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff", git.ZIP)
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bogusReq)
|
||||
|
||||
// Step 3, doesn't look like branch/tag/commit.
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db", git.ZIP)
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bogusReq)
|
||||
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master", git.ZIP)
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bogusReq)
|
||||
assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName())
|
||||
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive", git.ZIP)
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bogusReq)
|
||||
assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName())
|
||||
|
||||
// Now two valid requests, firstCommit with valid extensions.
|
||||
zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
|
||||
zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, zipReq)
|
||||
|
||||
tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.TARGZ)
|
||||
tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, tgzReq)
|
||||
|
||||
secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.ZIP)
|
||||
secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".bundle")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, secondReq)
|
||||
|
||||
@ -91,7 +90,7 @@ func TestArchive_Basic(t *testing.T) {
|
||||
// Sleep two seconds to make sure the queue doesn't change.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
|
||||
zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
// This zipReq should match what's sitting in the queue, as we haven't
|
||||
// let it release yet. From the consumer's point of view, this looks like
|
||||
@ -106,12 +105,12 @@ func TestArchive_Basic(t *testing.T) {
|
||||
// Now we'll submit a request and TimedWaitForCompletion twice, before and
|
||||
// after we release it. We should trigger both the timeout and non-timeout
|
||||
// cases.
|
||||
timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.TARGZ)
|
||||
timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, timedReq)
|
||||
doArchive(db.DefaultContext, timedReq)
|
||||
|
||||
zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
|
||||
zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
// Now, we're guaranteed to have released the original zipReq from the queue.
|
||||
// Ensure that we don't get handed back the released entry somehow, but they
|
||||
@ -129,6 +128,6 @@ func TestArchive_Basic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestErrUnknownArchiveFormat(t *testing.T) {
|
||||
err := ErrUnknownArchiveFormat{RequestFormat: "master"}
|
||||
err := ErrUnknownArchiveFormat{RequestNameType: "xxx"}
|
||||
assert.ErrorIs(t, err, ErrUnknownArchiveFormat{})
|
||||
}
|
||||
|
@ -763,12 +763,10 @@ func (m *webhookNotifier) PullRequestReviewRequest(ctx context.Context, doer *us
|
||||
func (m *webhookNotifier) CreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone})
|
||||
refName := refFullName.ShortName()
|
||||
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventCreate, &api.CreatePayload{
|
||||
Ref: refName, // FIXME: should it be a full ref name?
|
||||
Ref: refFullName.ShortName(), // FIXME: should it be a full ref name? But it will break the existing webhooks?
|
||||
Sha: refID,
|
||||
RefType: refFullName.RefType(),
|
||||
RefType: string(refFullName.RefType()),
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
@ -800,11 +798,9 @@ func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *use
|
||||
func (m *webhookNotifier) DeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner})
|
||||
refName := refFullName.ShortName()
|
||||
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventDelete, &api.DeletePayload{
|
||||
Ref: refName, // FIXME: should it be a full ref name?
|
||||
RefType: refFullName.RefType(),
|
||||
Ref: refFullName.ShortName(), // FIXME: should it be a full ref name? But it will break the existing webhooks?
|
||||
RefType: string(refFullName.RefType()),
|
||||
PusherType: api.PusherTypeUser,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
|
@ -84,9 +84,9 @@ func SlackLinkFormatter(url, text string) string {
|
||||
// SlackLinkToRef slack-formatter link to a repo ref
|
||||
func SlackLinkToRef(repoURL, ref string) string {
|
||||
// FIXME: SHA1 hardcoded here
|
||||
url := git.RefURL(repoURL, ref)
|
||||
refName := git.RefName(ref).ShortName()
|
||||
return SlackLinkFormatter(url, refName)
|
||||
refName := git.RefName(ref)
|
||||
url := repoURL + "/src/" + refName.RefWebLinkPath()
|
||||
return SlackLinkFormatter(url, refName.ShortName())
|
||||
}
|
||||
|
||||
// Create implements payloadConvertor Create method
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{if or .UsesIgnoreRevs .FaultyIgnoreRevsFile}}
|
||||
{{$revsFileLink := URLJoin .RepoLink "src" .BranchNameSubURL "/.git-blame-ignore-revs"}}
|
||||
{{$revsFileLink := URLJoin .RepoLink "src" .RefTypeNameSubURL "/.git-blame-ignore-revs"}}
|
||||
{{if .UsesIgnoreRevs}}
|
||||
<div class="ui info message">
|
||||
<p>{{ctx.Locale.Tr "repo.blame.ignore_revs" $revsFileLink "?bypass-blame-ignore=true"}}</p>
|
||||
@ -21,8 +21,8 @@
|
||||
{{if not .IsViewCommit}}
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/src/commit/{{.CommitID | PathEscape}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.file_permalink"}}</a>
|
||||
{{end}}
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.normal_view"}}</a>
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.file_history"}}</a>
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.normal_view"}}</a>
|
||||
<a class="ui tiny button" href="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.file_history"}}</a>
|
||||
<button class="ui tiny button unescape-button">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
|
||||
<button class="ui tiny button escape-button tw-hidden">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
|
||||
</div>
|
||||
|
@ -42,7 +42,7 @@
|
||||
</button>
|
||||
{{end}}
|
||||
{{if .EnableFeed}}
|
||||
<a role="button" class="btn interact-bg tw-p-2" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
|
||||
<a role="button" class="btn interact-bg tw-p-2" href="{{$.RepoLink}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
|
||||
{{end}}
|
||||
{{if not $.DisableDownloadSourceArchives}}
|
||||
<div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" ($.DefaultBranchBranch.DBBranch.Name)}}">
|
||||
@ -162,7 +162,7 @@
|
||||
</button>
|
||||
{{end}}
|
||||
{{if $.EnableFeed}}
|
||||
<a role="button" class="btn interact-bg tw-p-2" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
|
||||
<a role="button" class="btn interact-bg tw-p-2" href="{{$.RepoLink}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
|
||||
{{end}}
|
||||
{{if and (not .DBBranch.IsDeleted) (not $.DisableDownloadSourceArchives)}}
|
||||
<div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" (.DBBranch.Name)}}">
|
||||
|
@ -32,12 +32,14 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if and (not $.DisableDownloadSourceArchives) $.RefName}}
|
||||
{{if and (not $.DisableDownloadSourceArchives) $.RefFullName}}
|
||||
<div class="divider"></div>
|
||||
<div class="flex-items-block clone-panel-list">
|
||||
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a>
|
||||
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a>
|
||||
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a>
|
||||
{{/* FIXME: here it only uses the shortname in the ref to build the link, it can't distinguish the branch/tag/commit with the same name
|
||||
in the future, it's better to use something like "/archive/branch/the-name.zip", "/archive/tag/the-name.zip" */}}
|
||||
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a>
|
||||
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a>
|
||||
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
{{if .PageIsCommits}}
|
||||
<div class="ui attached segment">
|
||||
<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/search">
|
||||
<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/search">
|
||||
<div class="ui small fluid action input">
|
||||
{{template "shared/search/input" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.commit_kind")}}
|
||||
{{template "repo/commits_search_dropdown" .}}
|
||||
|
@ -32,7 +32,7 @@
|
||||
<div class="ui top attached header">
|
||||
<div class="ui compact small menu small-menu-items repo-editor-menu">
|
||||
<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
|
||||
<a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.BranchNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
|
||||
<a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
|
||||
{{if not .IsNewFile}}
|
||||
<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
|
||||
{{end}}
|
||||
|
@ -133,7 +133,7 @@
|
||||
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
|
||||
<div class="overflow-menu-items">
|
||||
{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
|
||||
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
|
||||
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.RefTypeNameSubURL}}{{end}}">
|
||||
{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -62,7 +62,7 @@
|
||||
|
||||
<!-- Show go to file if on home page -->
|
||||
{{if $isTreePathRoot}}
|
||||
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
|
||||
<a href="{{.Repository.Link}}/find/{{.RefTypeNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
|
||||
{{end}}
|
||||
|
||||
{{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}}
|
||||
@ -94,7 +94,7 @@
|
||||
{{if not $isTreePathRoot}}
|
||||
{{$treeNameIdxLast := Eval $treeNamesLen "-" 1}}
|
||||
<span class="breadcrumb repo-path tw-ml-1">
|
||||
<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
|
||||
<a class="section" href="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
|
||||
{{- range $i, $v := .TreeNames -}}
|
||||
<span class="breadcrumb-divider">/</span>
|
||||
{{- if eq $i $treeNameIdxLast -}}
|
||||
@ -114,7 +114,7 @@
|
||||
{{template "repo/clone_panel" .}}
|
||||
{{end}}
|
||||
{{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
|
||||
<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
|
||||
<a class="ui button" href="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
|
||||
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -16,7 +16,7 @@
|
||||
</div>
|
||||
<a class="issue-card-title muted issue-title tw-break-anywhere" href="{{.Link}}">{{.Title | ctx.RenderUtils.RenderIssueSimpleTitle}}</a>
|
||||
{{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}}
|
||||
<a role="button" class="issue-card-unpin muted tw-flex tw-items-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
|
||||
<a role="button" class="issue-card-unpin muted tw-flex tw-items-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
|
||||
{{svg "octicon-x" 16}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -24,7 +24,7 @@
|
||||
{{if $data.OpenMilestones}}
|
||||
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_open"}}</div>
|
||||
{{range $data.OpenMilestones}}
|
||||
<a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/issues?milestone={{.ID}}">
|
||||
<a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/milestone/{{.ID}}">
|
||||
{{svg "octicon-milestone" 18}} {{.Name}}
|
||||
</a>
|
||||
{{end}}
|
||||
@ -33,7 +33,7 @@
|
||||
{{if $data.ClosedMilestones}}
|
||||
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_closed"}}</div>
|
||||
{{range $data.ClosedMilestones}}
|
||||
<a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/issues?milestone={{.ID}}">
|
||||
<a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/milestone/{{.ID}}">
|
||||
{{svg "octicon-milestone" 18}} {{.Name}}
|
||||
</a>
|
||||
{{end}}
|
||||
@ -43,10 +43,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui list">
|
||||
<div class="ui list muted-links flex-items-block">
|
||||
<span class="item empty-list {{if $issueMilestone}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_milestone"}}</span>
|
||||
{{if $issueMilestone}}
|
||||
<a class="item muted" href="{{$pageMeta.RepoLink}}/milestone/{{$issueMilestone.ID}}">
|
||||
<a class="item" href="{{$pageMeta.RepoLink}}/milestone/{{$issueMilestone.ID}}">
|
||||
{{svg "octicon-milestone" 18}} {{$issueMilestone.Name}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -39,10 +39,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui list">
|
||||
<div class="ui list muted-links flex-items-block">
|
||||
<span class="item empty-list {{if $issueProject}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_projects"}}</span>
|
||||
{{if $issueProject}}
|
||||
<a class="item muted" href="{{$issueProject.Link ctx}}">
|
||||
<a class="item" href="{{$issueProject.Link ctx}}">
|
||||
{{svg $issueProject.IconName 18}} {{$issueProject.Title}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -51,9 +51,9 @@
|
||||
<div class="item">
|
||||
<div class="flex-text-inline tw-flex-1">
|
||||
{{if .User}}
|
||||
<a class="muted flex-text-inline" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}} {{.User.GetDisplayName}}</a>
|
||||
<a class="muted flex-text-inline tw-gap-2" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}} {{.User.GetDisplayName}}</a>
|
||||
{{else if .Team}}
|
||||
{{svg "octicon-people" 20}} {{$repoOwnerName}}/{{.Team.Name}}
|
||||
<span class="flex-text-inline tw-gap-2">{{svg "octicon-people" 20}} {{$repoOwnerName}}/{{.Team.Name}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="flex-text-inline">
|
||||
@ -92,7 +92,7 @@
|
||||
<div class="flex-text-inline tw-flex-1">
|
||||
{{$originalURLHostname := $pageMeta.Repository.GetOriginalURLHostname}}
|
||||
{{$originalURL := $pageMeta.Repository.OriginalURL}}
|
||||
<a class="muted flex-text-inline" href="{{$originalURL}}" data-tooltip-content="{{ctx.Locale.Tr "repo.migrated_from_fake" $originalURLHostname}}">
|
||||
<a class="muted flex-text-inline tw-gap-2" href="{{$originalURL}}" data-tooltip-content="{{ctx.Locale.Tr "repo.migrated_from_fake" $originalURLHostname}}">
|
||||
{{svg (MigrationIcon $originalURLHostname) 20}} {{.OriginalAuthor}}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -3,13 +3,15 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{template "base/disable_form_autofill"}}
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,13 +3,15 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{template "base/disable_form_autofill"}}
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,13 +3,15 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{template "base/disable_form_autofill"}}
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,13 +3,15 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{template "base/disable_form_autofill"}}
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,12 +3,14 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,12 +3,14 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,12 +3,14 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,12 +3,14 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -3,13 +3,15 @@
|
||||
<div class="ui container medium-width">
|
||||
<h3 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
{{template "base/alert" .}}
|
||||
<form class="ui form left-right-form" action="{{.Link}}" method="post">
|
||||
{{template "base/disable_form_autofill"}}
|
||||
{{.CsrfTokenHtml}}
|
||||
|
||||
<input id="service_type" type="hidden" name="service" value="{{.service}}">
|
||||
|
||||
<div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
|
||||
<label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
|
||||
<input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
|
||||
|
@ -49,7 +49,7 @@
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="stats-table">
|
||||
<a class="table-cell tiny background light grey"></a>
|
||||
<a class="table-cell tiny tw-bg-grey"></a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount}}
|
||||
|
@ -109,23 +109,17 @@
|
||||
{{ctx.Locale.Tr "repo.release.delete_release"}}
|
||||
</a>
|
||||
{{if .IsDraft}}
|
||||
<button class="ui small button" type="submit" name="draft" value="{{ctx.Locale.Tr "repo.release.save_draft"}}">{{ctx.Locale.Tr "repo.release.save_draft"}}</button>
|
||||
<button class="ui small primary button">
|
||||
{{ctx.Locale.Tr "repo.release.publish"}}
|
||||
</button>
|
||||
<button class="ui small button" type="submit" name="draft" value="1">{{ctx.Locale.Tr "repo.release.save_draft"}}</button>
|
||||
<button class="ui small primary button">{{ctx.Locale.Tr "repo.release.publish"}}</button>
|
||||
{{else}}
|
||||
<button class="ui small primary button">
|
||||
{{ctx.Locale.Tr "repo.release.edit_release"}}
|
||||
</button>
|
||||
<button class="ui small primary button">{{ctx.Locale.Tr "repo.release.edit_release"}}</button>
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if not .tag_name}}
|
||||
{{if .ShowCreateTagOnlyButton}}
|
||||
<button class="ui small button" name="tag_only" value="1">{{ctx.Locale.Tr "repo.release.add_tag"}}</button>
|
||||
{{end}}
|
||||
<button class="ui small button" name="draft" value="1">{{ctx.Locale.Tr "repo.release.save_draft"}}</button>
|
||||
<button class="ui small primary button">
|
||||
{{ctx.Locale.Tr "repo.release.publish"}}
|
||||
</button>
|
||||
<button class="ui small primary button">{{ctx.Locale.Tr "repo.release.publish"}}</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,58 +48,58 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{if .RepoOwnerIsOrganization}}
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.settings.teams"}}
|
||||
</h4>
|
||||
{{$allowedToChangeTeams := (or (.Org.RepoAdminChangeTeamAccess) (.Permission.IsOwner))}}
|
||||
{{if .Teams}}
|
||||
<div class="ui attached segment">
|
||||
<div class="flex-list">
|
||||
{{range $t, $team := .Teams}}
|
||||
<div class="flex-item">
|
||||
<div class="flex-item-main">
|
||||
<a class="flex-item-title text primary" href="{{AppSubUrl}}/org/{{$.OrgName|PathEscape}}/teams/{{.LowerName|PathEscape}}">
|
||||
{{.Name}}
|
||||
</a>
|
||||
<div class="flex-item-body flex-text-block">
|
||||
{{svg "octicon-shield-lock"}}
|
||||
{{if eq .AccessMode 1}}{{ctx.Locale.Tr "repo.settings.collaboration.read"}}{{else if eq .AccessMode 2}}{{ctx.Locale.Tr "repo.settings.collaboration.write"}}{{else if eq .AccessMode 3}}{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}{{else if eq .AccessMode 4}}{{ctx.Locale.Tr "repo.settings.collaboration.owner"}}{{else}}{{ctx.Locale.Tr "repo.settings.collaboration.undefined"}}{{end}}
|
||||
{{if $.Repository.Owner.IsOrganization}}
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.settings.teams"}}
|
||||
</h4>
|
||||
{{$allowedToChangeTeams := (or (.Org.RepoAdminChangeTeamAccess) (.Permission.IsOwner))}}
|
||||
{{if .Teams}}
|
||||
<div class="ui attached segment">
|
||||
<div class="flex-list">
|
||||
{{range $t, $team := .Teams}}
|
||||
<div class="flex-item">
|
||||
<div class="flex-item-main">
|
||||
<a class="flex-item-title text primary" href="{{AppSubUrl}}/org/{{$.OrgName|PathEscape}}/teams/{{.LowerName|PathEscape}}">
|
||||
{{.Name}}
|
||||
</a>
|
||||
<div class="flex-item-body flex-text-block">
|
||||
{{svg "octicon-shield-lock"}}
|
||||
{{if eq .AccessMode 1}}{{ctx.Locale.Tr "repo.settings.collaboration.read"}}{{else if eq .AccessMode 2}}{{ctx.Locale.Tr "repo.settings.collaboration.write"}}{{else if eq .AccessMode 3}}{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}{{else if eq .AccessMode 4}}{{ctx.Locale.Tr "repo.settings.collaboration.owner"}}{{else}}{{ctx.Locale.Tr "repo.settings.collaboration.undefined"}}{{end}}
|
||||
</div>
|
||||
{{if or (eq .AccessMode 1) (eq .AccessMode 2)}}
|
||||
{{$first := true}}
|
||||
<div class="flex-item-body" data-tooltip-content="{{ctx.Locale.Tr "repo.settings.change_team_permission_tip"}}">
|
||||
Sections: {{range $u, $unit := $.Units}}{{if and ($.Repo.UnitEnabled ctx $unit.Type) ($team.UnitEnabled ctx $unit.Type)}}{{if $first}}{{$first = false}}{{else}}, {{end}}{{ctx.Locale.Tr $unit.NameKey}}{{end}}{{end}} {{if $first}}None{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if or (eq .AccessMode 1) (eq .AccessMode 2)}}
|
||||
{{$first := true}}
|
||||
<div class="flex-item-body" data-tooltip-content="{{ctx.Locale.Tr "repo.settings.change_team_permission_tip"}}">
|
||||
Sections: {{range $u, $unit := $.Units}}{{if and ($.Repo.UnitEnabled ctx $unit.Type) ($team.UnitEnabled ctx $unit.Type)}}{{if $first}}{{$first = false}}{{else}}, {{end}}{{ctx.Locale.Tr $unit.NameKey}}{{end}}{{end}} {{if $first}}None{{end}}
|
||||
{{if $allowedToChangeTeams}}
|
||||
<div class="flex-item-trailing" {{if .IncludesAllRepositories}} data-tooltip-content="{{ctx.Locale.Tr "repo.settings.delete_team_tip"}}"{{end}}>
|
||||
<button class="ui red tiny button inline delete-button {{if .IncludesAllRepositories}}disabled{{end}}" data-url="{{$.Link}}/team/delete" data-id="{{.ID}}">
|
||||
{{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if $allowedToChangeTeams}}
|
||||
<div class="flex-item-trailing" {{if .IncludesAllRepositories}} data-tooltip-content="{{ctx.Locale.Tr "repo.settings.delete_team_tip"}}"{{end}}>
|
||||
<button class="ui red tiny button inline delete-button {{if .IncludesAllRepositories}}disabled{{end}}" data-url="{{$.Link}}/team/delete" data-id="{{.ID}}">
|
||||
{{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="ui bottom attached segment">
|
||||
{{if $allowedToChangeTeams}}
|
||||
<form class="ui form" id="repo-collab-team-form" action="{{.Link}}/team" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div id="search-team-box" class="ui search input tw-align-middle" data-org-name="{{.OrgName}}">
|
||||
<input class="prompt" name="team" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" autocomplete="off" autofocus required>
|
||||
</div>
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_team"}}</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<div class="item">
|
||||
{{ctx.Locale.Tr "repo.settings.change_team_access_not_allowed"}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="ui bottom attached segment">
|
||||
{{if $allowedToChangeTeams}}
|
||||
<form class="ui form" id="repo-collab-team-form" action="{{.Link}}/team" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div id="search-team-box" class="ui search input tw-align-middle" data-org-name="{{.OrgName}}">
|
||||
<input class="prompt" name="team" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" autocomplete="off" autofocus required>
|
||||
</div>
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_team"}}</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<div class="item">
|
||||
{{ctx.Locale.Tr "repo.settings.change_team_access_not_allowed"}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user