mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-05 06:15:45 +02:00
Merge branch 'main' into lunny/some_refactors
This commit is contained in:
@@ -20,7 +20,7 @@ jobs:
|
||||
- run: make generate-gitignore
|
||||
timeout-minutes: 40
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v1.0.0
|
||||
uses: appleboy/git-push-action@v1.2.0
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: update locales
|
||||
run: ./build/update-locales.sh
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v1.0.0
|
||||
uses: appleboy/git-push-action@v1.2.0
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
|
||||
@@ -101,10 +101,10 @@ func GetLFSLock(ctx context.Context, repo *repo_model.Repository, path string) (
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// GetLFSLockByID returns release by given id.
|
||||
func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
|
||||
// GetLFSLockByIDAndRepo returns lfs lock by given id and repository id.
|
||||
func GetLFSLockByIDAndRepo(ctx context.Context, id, repoID int64) (*LFSLock, error) {
|
||||
lock := new(LFSLock)
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(lock)
|
||||
has, err := db.GetEngine(ctx).ID(id).And("repo_id = ?", repoID).Get(lock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
@@ -153,7 +153,7 @@ func CountLFSLockByRepoID(ctx context.Context, repoID int64) (int64, error) {
|
||||
// DeleteLFSLockByID deletes a lock by given ID.
|
||||
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
|
||||
lock, err := GetLFSLockByID(ctx, id)
|
||||
lock, err := GetLFSLockByIDAndRepo(ctx, id, repo.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func createTestLock(t *testing.T, repo *repo_model.Repository, owner *user_model.User) *LFSLock {
|
||||
t.Helper()
|
||||
|
||||
path := fmt.Sprintf("%s-%d-%d", t.Name(), repo.ID, time.Now().UnixNano())
|
||||
lock, err := CreateLFSLock(t.Context(), repo, &LFSLock{
|
||||
OwnerID: owner.ID,
|
||||
Path: path,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return lock
|
||||
}
|
||||
|
||||
func TestGetLFSLockByIDAndRepo(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
|
||||
lockRepo1 := createTestLock(t, repo1, user2)
|
||||
lockRepo3 := createTestLock(t, repo3, user4)
|
||||
|
||||
fetched, err := GetLFSLockByIDAndRepo(t.Context(), lockRepo1.ID, repo1.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo1.ID, fetched.ID)
|
||||
assert.Equal(t, repo1.ID, fetched.RepoID)
|
||||
|
||||
_, err = GetLFSLockByIDAndRepo(t.Context(), lockRepo1.ID, repo3.ID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrLFSLockNotExist(err))
|
||||
|
||||
_, err = GetLFSLockByIDAndRepo(t.Context(), lockRepo3.ID, repo1.ID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrLFSLockNotExist(err))
|
||||
}
|
||||
|
||||
func TestDeleteLFSLockByIDRequiresRepoMatch(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
|
||||
lockRepo1 := createTestLock(t, repo1, user2)
|
||||
lockRepo3 := createTestLock(t, repo3, user4)
|
||||
|
||||
_, err := DeleteLFSLockByID(t.Context(), lockRepo3.ID, repo1, user2, true)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrLFSLockNotExist(err))
|
||||
|
||||
existing, err := GetLFSLockByIDAndRepo(t.Context(), lockRepo3.ID, repo3.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo3.ID, existing.ID)
|
||||
|
||||
deleted, err := DeleteLFSLockByID(t.Context(), lockRepo3.ID, repo3, user4, true)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo3.ID, deleted.ID)
|
||||
|
||||
deleted, err = DeleteLFSLockByID(t.Context(), lockRepo1.ID, repo1, user2, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo1.ID, deleted.ID)
|
||||
}
|
||||
@@ -46,8 +46,8 @@ func parseLsTreeLine(line []byte) (*LsTreeEntry, error) {
|
||||
entry.Size = optional.Some(size)
|
||||
}
|
||||
|
||||
entry.EntryMode, err = ParseEntryMode(string(entryMode))
|
||||
if err != nil || entry.EntryMode == EntryModeNoEntry {
|
||||
entry.EntryMode = ParseEntryMode(string(entryMode))
|
||||
if entry.EntryMode == EntryModeNoEntry {
|
||||
return nil, fmt.Errorf("invalid ls-tree output (invalid mode): %q, err: %w", line, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -55,21 +54,38 @@ func (e EntryMode) IsExecutable() bool {
|
||||
return e == EntryModeExec
|
||||
}
|
||||
|
||||
func ParseEntryMode(mode string) (EntryMode, error) {
|
||||
func ParseEntryMode(mode string) EntryMode {
|
||||
switch mode {
|
||||
case "000000":
|
||||
return EntryModeNoEntry, nil
|
||||
return EntryModeNoEntry
|
||||
case "100644":
|
||||
return EntryModeBlob, nil
|
||||
return EntryModeBlob
|
||||
case "100755":
|
||||
return EntryModeExec, nil
|
||||
return EntryModeExec
|
||||
case "120000":
|
||||
return EntryModeSymlink, nil
|
||||
return EntryModeSymlink
|
||||
case "160000":
|
||||
return EntryModeCommit, nil
|
||||
case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
|
||||
return EntryModeTree, nil
|
||||
return EntryModeCommit
|
||||
case "040000":
|
||||
return EntryModeTree
|
||||
default:
|
||||
return 0, fmt.Errorf("unparsable entry mode: %s", mode)
|
||||
// git uses 040000 for tree object, but some users may get 040755 from non-standard git implementations
|
||||
m, _ := strconv.ParseInt(mode, 8, 32)
|
||||
modeInt := EntryMode(m)
|
||||
switch modeInt & 0o770000 {
|
||||
case 0o040000:
|
||||
return EntryModeTree
|
||||
case 0o160000:
|
||||
return EntryModeCommit
|
||||
case 0o120000:
|
||||
return EntryModeSymlink
|
||||
case 0o100000:
|
||||
if modeInt&0o777 == 0o755 {
|
||||
return EntryModeExec
|
||||
}
|
||||
return EntryModeBlob
|
||||
default:
|
||||
return EntryModeNoEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,3 +27,30 @@ func TestEntriesCustomSort(t *testing.T) {
|
||||
entries.CustomSort(strings.Compare)
|
||||
assert.Equal(t, expected, entries)
|
||||
}
|
||||
|
||||
func TestParseEntryMode(t *testing.T) {
|
||||
tests := []struct {
|
||||
modeStr string
|
||||
expectMod EntryMode
|
||||
}{
|
||||
{"000000", EntryModeNoEntry},
|
||||
{"000755", EntryModeNoEntry},
|
||||
|
||||
{"100644", EntryModeBlob},
|
||||
{"100755", EntryModeExec},
|
||||
|
||||
{"120000", EntryModeSymlink},
|
||||
{"120755", EntryModeSymlink},
|
||||
{"160000", EntryModeCommit},
|
||||
{"160755", EntryModeCommit},
|
||||
|
||||
{"040000", EntryModeTree},
|
||||
{"040755", EntryModeTree},
|
||||
|
||||
{"777777", EntryModeNoEntry}, // invalid mode
|
||||
}
|
||||
for _, test := range tests {
|
||||
mod := ParseEntryMode(test.modeStr)
|
||||
assert.Equal(t, test.expectMod, mod, "modeStr: %s", test.modeStr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2542,8 +2542,8 @@
|
||||
"repo.diff.too_many_files": "Some files were not shown because too many files have changed in this diff",
|
||||
"repo.diff.show_more": "Show More",
|
||||
"repo.diff.load": "Load Diff",
|
||||
"repo.diff.generated": "generated",
|
||||
"repo.diff.vendored": "vendored",
|
||||
"repo.diff.generated": "Generated",
|
||||
"repo.diff.vendored": "Vendored",
|
||||
"repo.diff.comment.add_line_comment": "Add line comment",
|
||||
"repo.diff.comment.placeholder": "Leave a comment",
|
||||
"repo.diff.comment.add_single_comment": "Add single comment",
|
||||
@@ -3724,8 +3724,8 @@
|
||||
"projects.exit_fullscreen": "Exit Fullscreen",
|
||||
"git.filemode.changed_filemode": "%[1]s → %[2]s",
|
||||
"git.filemode.directory": "Directory",
|
||||
"git.filemode.normal_file": "Normal file",
|
||||
"git.filemode.executable_file": "Executable file",
|
||||
"git.filemode.symbolic_link": "Symbolic link",
|
||||
"git.filemode.normal_file": "Regular",
|
||||
"git.filemode.executable_file": "Executable",
|
||||
"git.filemode.symbolic_link": "Symlink",
|
||||
"git.filemode.submodule": "Submodule"
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"password": "Pasfhocal",
|
||||
"access_token": "Comhartha Rochtana",
|
||||
"re_type": "Deimhnigh Pasfhocal",
|
||||
"captcha": "CAPTCHA",
|
||||
"twofa": "Fíordheimhniú Dhá-Fhachtóir",
|
||||
"twofa_scratch": "Cód Scratch Dhá-Fhachtóra",
|
||||
"passcode": "Paschód",
|
||||
@@ -132,6 +133,7 @@
|
||||
"confirm_delete_selected": "Deimhnigh chun gach earra roghnaithe a scriosadh?",
|
||||
"name": "Ainm",
|
||||
"value": "Luach",
|
||||
"readme": "Léigh-mé",
|
||||
"filter_title": "Scagaire",
|
||||
"filter.clear": "Scagaire Soiléir",
|
||||
"filter.is_archived": "Cartlannaithe",
|
||||
@@ -229,6 +231,7 @@
|
||||
"install.db_name": "Ainm Bunachar Sonraí",
|
||||
"install.db_schema": "Scéim",
|
||||
"install.db_schema_helper": "Fág bán le haghaidh réamhshocraithe bunachar sonraí (\"poiblí\").",
|
||||
"install.ssl_mode": "SSL",
|
||||
"install.path": "Cosán",
|
||||
"install.sqlite_helper": "Conair comhad don bhunachar sonraí SQLite3. Cuir <br>isteach cosán iomlán má reáchtáil tú Gitea mar sheirbhís.",
|
||||
"install.reinstall_error": "Tá tú ag iarraidh a shuiteáil i mbunachar sonraí Gitea atá ann cheana",
|
||||
@@ -406,6 +409,7 @@
|
||||
"auth.twofa_scratch_token_incorrect": "Tá do chód scratch mícheart.",
|
||||
"auth.twofa_required": "Ní mór duit fíordheimhniú dhá fhachtóir a shocrú chun rochtain a fháil ar stórtha, nó iarracht a dhéanamh logáil isteach arís.",
|
||||
"auth.login_userpass": "Sínigh isteach",
|
||||
"auth.login_openid": "OpenID",
|
||||
"auth.oauth_signup_tab": "Cláraigh Cuntas Nua",
|
||||
"auth.oauth_signup_title": "Comhlánaigh Cuntas Nua",
|
||||
"auth.oauth_signup_submit": "Cuntas Comhlánaigh",
|
||||
@@ -654,6 +658,7 @@
|
||||
"settings.twofa": "Fíordheimhniú Dhá Fachtóir (TOTP)",
|
||||
"settings.account_link": "Cuntais Nasctha",
|
||||
"settings.organization": "Eagraíochtaí",
|
||||
"settings.uid": "UID",
|
||||
"settings.webauthn": "Fíordheimhniú Dhá-Fachtóir (Eochracha Slándála)",
|
||||
"settings.public_profile": "Próifíl Phoiblí",
|
||||
"settings.biography_placeholder": "Inis dúinn beagán fút féin! (Is féidir leat Markdown a úsáid)",
|
||||
@@ -991,6 +996,7 @@
|
||||
"repo.multiple_licenses": "Ceadúnais Iolracha",
|
||||
"repo.object_format": "Formáid Oibiacht",
|
||||
"repo.object_format_helper": "Formáid oibiacht an stór. Ní féidir é a athrú níos déanaí. Is é SHA1 an comhoiriúnacht is fearr.",
|
||||
"repo.readme": "LÉIGHMÉ",
|
||||
"repo.readme_helper": "Roghnaigh comhad teimpléad README.",
|
||||
"repo.readme_helper_desc": "Seo an áit inar féidir leat cur síos iomlán a scríobh do thionscadal.",
|
||||
"repo.auto_init": "Taisce a thionscnamh (Cuireann sé .gitignore, Ceadúnas agus README)",
|
||||
@@ -1055,6 +1061,7 @@
|
||||
"repo.desc.template": "Teimpléad",
|
||||
"repo.desc.internal": "Inmheánach",
|
||||
"repo.desc.archived": "Cartlannaithe",
|
||||
"repo.desc.sha256": "SHA256",
|
||||
"repo.template.items": "Míreanna Teimpléad",
|
||||
"repo.template.git_content": "Ábhar Git (Brainse Réamhshocraithe)",
|
||||
"repo.template.git_hooks": "Crúcanna Git",
|
||||
@@ -1083,6 +1090,7 @@
|
||||
"repo.migrate_options_lfs_endpoint.description.local": "Tacaítear le cosán freastalaí áitiúil freisin.",
|
||||
"repo.migrate_options_lfs_endpoint.placeholder": "Mura bhfágtar bán é, díorthófar an críochphointe ón URL clónála.",
|
||||
"repo.migrate_items": "Míreanna Imirce",
|
||||
"repo.migrate_items_wiki": "Vicí",
|
||||
"repo.migrate_items_milestones": "Clocha míle",
|
||||
"repo.migrate_items_labels": "Lipéid",
|
||||
"repo.migrate_items_issues": "Saincheisteanna",
|
||||
@@ -1728,8 +1736,11 @@
|
||||
"repo.issues.reference_link": "Tagairt: %s",
|
||||
"repo.compare.compare_base": "bonn",
|
||||
"repo.compare.compare_head": "déan comparáid",
|
||||
"repo.compare.title": "Athruithe a chur i gcomparáid",
|
||||
"repo.compare.description": "Roghnaigh dhá bhrainse nó clib chun a fheiceáil cad atá athraithe nó chun iarratas tarraingthe nua a thosú.",
|
||||
"repo.pulls.desc": "Cumasaigh iarratais tarraingthe agus athbhreithnithe cód.",
|
||||
"repo.pulls.new": "Iarratas Tarraingthe Nua",
|
||||
"repo.pulls.new.description": "Pléigh agus athbhreithnigh na hathruithe sa chomparáid seo le daoine eile.",
|
||||
"repo.pulls.new.blocked_user": "Ní féidir iarratas tarraingthe a chruthú toisc go bhfuil úinéir an stórais bac ort.",
|
||||
"repo.pulls.new.must_collaborator": "Caithfidh tú a bheith ina chomhoibritheoir chun iarratas tarraingthe a chruthú.",
|
||||
"repo.pulls.new.already_existed": "Tá iarratas tarraingthe idir na brainsí seo ann cheana féin",
|
||||
@@ -1739,7 +1750,6 @@
|
||||
"repo.pulls.allow_edits_from_maintainers": "Ceadaigh eagarthóirí ó chothabhálaí",
|
||||
"repo.pulls.allow_edits_from_maintainers_desc": "Is féidir le húsáideoirí a bhfuil rochtain scríofa acu ar an mbunbhrainse brú chuig an bhrainse",
|
||||
"repo.pulls.allow_edits_from_maintainers_err": "Theip ar nuashonrú",
|
||||
"repo.pulls.compare_changes_desc": "Roghnaigh an brainse le cumasc isteach agus an brainse le tarraingt uaidh.",
|
||||
"repo.pulls.has_viewed_file": "Breathnaithe",
|
||||
"repo.pulls.has_changed_since_last_review": "Athraithe ó d'athbhreithniú deire",
|
||||
"repo.pulls.viewed_files_label": "Breathnaíodh ar %[1]d / %[2]d comhaid",
|
||||
@@ -2313,8 +2323,19 @@
|
||||
"repo.settings.slack_domain": "Fearann",
|
||||
"repo.settings.slack_channel": "Cainéal",
|
||||
"repo.settings.add_web_hook_desc": "Comhtháthaigh <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">%s</a> isteach i do stóras.",
|
||||
"repo.settings.web_hook_name_gitea": "Gitea",
|
||||
"repo.settings.web_hook_name_gogs": "Gogs",
|
||||
"repo.settings.web_hook_name_slack": "Slack",
|
||||
"repo.settings.web_hook_name_discord": "Discord",
|
||||
"repo.settings.web_hook_name_dingtalk": "DingTalk",
|
||||
"repo.settings.web_hook_name_telegram": "Teileagram",
|
||||
"repo.settings.web_hook_name_matrix": "Maitrís",
|
||||
"repo.settings.web_hook_name_msteams": "Microsoft Teams",
|
||||
"repo.settings.web_hook_name_feishu_or_larksuite": "Feishu / Lark Suite",
|
||||
"repo.settings.web_hook_name_feishu": "Feishu",
|
||||
"repo.settings.web_hook_name_larksuite": "Lark Suite",
|
||||
"repo.settings.web_hook_name_wechatwork": "WeCom (Wechat Work)",
|
||||
"repo.settings.web_hook_name_packagist": "Packagist",
|
||||
"repo.settings.packagist_username": "Ainm úsáideora Pacagist",
|
||||
"repo.settings.packagist_api_token": "Comhartha API",
|
||||
"repo.settings.packagist_package_url": "URL pacáiste Packagist",
|
||||
@@ -2460,6 +2481,7 @@
|
||||
"repo.settings.unarchive.success": "Rinneadh an stóras a dhíchartlann go rathúil.",
|
||||
"repo.settings.unarchive.error": "Tharla earráid agus tú ag iarraidh an stóras a dhíchartlannú. Féach an logáil le haghaidh tuilleadh sonraí.",
|
||||
"repo.settings.update_avatar_success": "Nuashonraíodh avatar an stóras.",
|
||||
"repo.settings.lfs": "LFS",
|
||||
"repo.settings.lfs_filelist": "Comhaid LFS a stóráiltear sa stóras seo",
|
||||
"repo.settings.lfs_no_lfs_files": "Níl aon chomhaid LFS stóráilte sa stóras seo",
|
||||
"repo.settings.lfs_findcommits": "Aimsigh gealltanais",
|
||||
@@ -2479,6 +2501,7 @@
|
||||
"repo.settings.lfs_force_unlock": "Díghlasáil Fórsa",
|
||||
"repo.settings.lfs_pointers.found": "Fuarthas %d pointeoir(í) bloba — %d gaolmhar, %d neamhghaolmhar (%d ar iarraidh ón stóras)",
|
||||
"repo.settings.lfs_pointers.sha": "SHA Blob",
|
||||
"repo.settings.lfs_pointers.oid": "OID",
|
||||
"repo.settings.lfs_pointers.inRepo": "I Stóras",
|
||||
"repo.settings.lfs_pointers.exists": "Ann sa siopa",
|
||||
"repo.settings.lfs_pointers.accessible": "Inrochtana don Úsáideoir",
|
||||
@@ -2844,6 +2867,7 @@
|
||||
"admin.dashboard.task.finished": "Tasc: Tá %[1]s tosaithe ag %[2]s críochnaithe",
|
||||
"admin.dashboard.task.unknown": "Tasc anaithnid: %[1]s",
|
||||
"admin.dashboard.cron.started": "Cron tosaithe: %[1]s",
|
||||
"admin.dashboard.cron.process": "Cron: %[1]s",
|
||||
"admin.dashboard.cron.cancelled": "Cron: %[1]s cealaithe: %[3]s",
|
||||
"admin.dashboard.cron.error": "Earráid i gCron: %s: %[3]s",
|
||||
"admin.dashboard.cron.finished": "Cron: %[1]s críochnaithe",
|
||||
@@ -2923,6 +2947,7 @@
|
||||
"admin.users.reserved": "In áirithe",
|
||||
"admin.users.bot": "Bota",
|
||||
"admin.users.remote": "Iargúlta",
|
||||
"admin.users.2fa": "2FA",
|
||||
"admin.users.repos": "Stórais",
|
||||
"admin.users.created": "Cruthaithe",
|
||||
"admin.users.last_login": "Sínigh Isteach Deiridh",
|
||||
@@ -3044,6 +3069,7 @@
|
||||
"admin.auths.attribute_mail": "Tréith ríomhphoist",
|
||||
"admin.auths.attribute_ssh_public_key": "Tréith Eochair SSH Phoiblí",
|
||||
"admin.auths.attribute_avatar": "Tréith Avatar",
|
||||
"admin.auths.ssh_keys_are_verified": "Meastar gur fíoraithe iad eochracha SSH in LDAP",
|
||||
"admin.auths.attributes_in_bind": "Faigh tréithe i gComhthéacs Bind DN",
|
||||
"admin.auths.allow_deactivate_all": "Lig do thoradh cuardaigh folamh gach úsáideoir a dhíghníomhachtú",
|
||||
"admin.auths.use_paged_search": "Úsáid Cuardach Leathanaigh",
|
||||
@@ -3177,6 +3203,7 @@
|
||||
"admin.config.db_name": "Ainm",
|
||||
"admin.config.db_user": "Ainm úsáideora",
|
||||
"admin.config.db_schema": "Scéim",
|
||||
"admin.config.db_ssl_mode": "SSL",
|
||||
"admin.config.db_path": "Cosán",
|
||||
"admin.config.service_config": "Cumraíocht Seirbhíse",
|
||||
"admin.config.register_email_confirm": "Deimhniú Ríomhphost a éileamh chun Clárú",
|
||||
@@ -3430,6 +3457,7 @@
|
||||
"packages.assets": "Sócmhainní",
|
||||
"packages.versions": "Leaganacha",
|
||||
"packages.versions.view_all": "Féach ar gach",
|
||||
"packages.dependency.id": "ID",
|
||||
"packages.dependency.version": "Leagan",
|
||||
"packages.search_in_external_registry": "Cuardaigh i %s",
|
||||
"packages.alpine.registry": "Socraigh an clárlann seo tríd an URL a chur i do chomhad <code>/etc/apk/repositories</code>:",
|
||||
@@ -3594,6 +3622,7 @@
|
||||
"actions.runners.new": "Cruthaigh reathaí nua",
|
||||
"actions.runners.new_notice": "Conas reathaí a thosú",
|
||||
"actions.runners.status": "Stádas",
|
||||
"actions.runners.id": "ID",
|
||||
"actions.runners.name": "Ainm",
|
||||
"actions.runners.owner_type": "Cineál",
|
||||
"actions.runners.description": "Cur síos",
|
||||
@@ -3693,6 +3722,7 @@
|
||||
"projects.type-3.display_name": "Tionscadal Eagrúcháin",
|
||||
"projects.enter_fullscreen": "Lánscáileán",
|
||||
"projects.exit_fullscreen": "Scoir Lánscáileáin",
|
||||
"git.filemode.changed_filemode": "%[1]s → %[2]s",
|
||||
"git.filemode.directory": "Eolaire",
|
||||
"git.filemode.normal_file": "Comhad gnáth",
|
||||
"git.filemode.executable_file": "Comhad infheidhmithe",
|
||||
|
||||
@@ -398,7 +398,6 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
|
||||
|
||||
if err := repo_model.DeleteAttachment(ctx, attach, true); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/httpcache"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -40,7 +41,7 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
|
||||
|
||||
file, header, err := ctx.Req.FormFile("file")
|
||||
if err != nil {
|
||||
ctx.HTTPError(http.StatusInternalServerError, fmt.Sprintf("FormFile: %v", err))
|
||||
ctx.ServerError("FormFile", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
@@ -56,7 +57,7 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
|
||||
ctx.HTTPError(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
ctx.HTTPError(http.StatusInternalServerError, fmt.Sprintf("NewAttachment: %v", err))
|
||||
ctx.ServerError("UploadAttachmentGeneralSizeLimit", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,13 +75,44 @@ func DeleteAttachment(ctx *context.Context) {
|
||||
ctx.HTTPError(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != attach.UploaderID) {
|
||||
|
||||
if !ctx.IsSigned {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if attach.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.HTTPError(http.StatusBadRequest, "attachment does not belong to this repository")
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Doer.ID != attach.UploaderID {
|
||||
if attach.IssueID > 0 {
|
||||
issue, err := issues_model.GetIssueByID(ctx, attach.IssueID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetIssueByID", err)
|
||||
return
|
||||
}
|
||||
if !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
} else if attach.ReleaseID > 0 {
|
||||
if !ctx.Repo.Permission.CanWrite(unit.TypeReleases) {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !ctx.Repo.Permission.IsAdmin() && !ctx.Repo.Permission.IsOwner() {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = repo_model.DeleteAttachment(ctx, attach, true)
|
||||
if err != nil {
|
||||
ctx.HTTPError(http.StatusInternalServerError, fmt.Sprintf("DeleteAttachment: %v", err))
|
||||
ctx.ServerError("DeleteAttachment", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]string{
|
||||
@@ -114,7 +146,7 @@ func ServeAttachment(ctx *context.Context, uuid string) {
|
||||
} else { // If we have the repository we check access
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repository, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.HTTPError(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
if !perm.CanRead(unitType) {
|
||||
|
||||
@@ -166,16 +166,6 @@ func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) {
|
||||
return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`, expected 5 space delimited values got %d)", line, len(fields))
|
||||
}
|
||||
|
||||
baseMode, err := git.ParseEntryMode(fields[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headMode, err := git.ParseEntryMode(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseBlobID := fields[2]
|
||||
headBlobID := fields[3]
|
||||
|
||||
@@ -201,8 +191,8 @@ func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) {
|
||||
return &DiffTreeRecord{
|
||||
Status: status,
|
||||
Score: score,
|
||||
BaseMode: baseMode,
|
||||
HeadMode: headMode,
|
||||
BaseMode: git.ParseEntryMode(fields[0]),
|
||||
HeadMode: git.ParseEntryMode(fields[1]),
|
||||
BaseBlobID: baseBlobID,
|
||||
HeadBlobID: headBlobID,
|
||||
BasePath: basePath,
|
||||
|
||||
+46
-31
@@ -399,20 +399,20 @@ type DiffFile struct {
|
||||
isAmbiguous bool
|
||||
|
||||
// basic fields (parsed from diff result)
|
||||
Name string
|
||||
NameHash string
|
||||
OldName string
|
||||
Addition int
|
||||
Deletion int
|
||||
Type DiffFileType
|
||||
Mode string
|
||||
OldMode string
|
||||
IsCreated bool
|
||||
IsDeleted bool
|
||||
IsBin bool
|
||||
IsLFSFile bool
|
||||
IsRenamed bool
|
||||
IsSubmodule bool
|
||||
Name string
|
||||
NameHash string
|
||||
OldName string
|
||||
Addition int
|
||||
Deletion int
|
||||
Type DiffFileType
|
||||
EntryMode string
|
||||
OldEntryMode string
|
||||
IsCreated bool
|
||||
IsDeleted bool
|
||||
IsBin bool
|
||||
IsLFSFile bool
|
||||
IsRenamed bool
|
||||
IsSubmodule bool
|
||||
// basic fields but for render purpose only
|
||||
Sections []*DiffSection
|
||||
IsIncomplete bool
|
||||
@@ -501,21 +501,36 @@ func (diffFile *DiffFile) ShouldBeHidden() bool {
|
||||
return diffFile.IsGenerated || diffFile.IsViewed
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) ModeTranslationKey(mode string) string {
|
||||
switch mode {
|
||||
case "040000":
|
||||
return "git.filemode.directory"
|
||||
case "100644":
|
||||
return "git.filemode.normal_file"
|
||||
case "100755":
|
||||
return "git.filemode.executable_file"
|
||||
case "120000":
|
||||
return "git.filemode.symbolic_link"
|
||||
case "160000":
|
||||
return "git.filemode.submodule"
|
||||
default:
|
||||
return mode
|
||||
func (diffFile *DiffFile) TranslateDiffEntryMode(locale translation.Locale) string {
|
||||
entryModeTr := func(mode string) string {
|
||||
entryMode := git.ParseEntryMode(mode)
|
||||
switch {
|
||||
case entryMode.IsDir():
|
||||
return locale.TrString("git.filemode.directory")
|
||||
case entryMode.IsRegular():
|
||||
return locale.TrString("git.filemode.normal_file")
|
||||
case entryMode.IsExecutable():
|
||||
return locale.TrString("git.filemode.executable_file")
|
||||
case entryMode.IsLink():
|
||||
return locale.TrString("git.filemode.symbolic_link")
|
||||
case entryMode.IsSubModule():
|
||||
return locale.TrString("git.filemode.submodule")
|
||||
default:
|
||||
return mode
|
||||
}
|
||||
}
|
||||
|
||||
if diffFile.EntryMode != "" && diffFile.OldEntryMode != "" {
|
||||
oldMode := entryModeTr(diffFile.OldEntryMode)
|
||||
newMode := entryModeTr(diffFile.EntryMode)
|
||||
return locale.TrString("git.filemode.changed_filemode", oldMode, newMode)
|
||||
}
|
||||
if diffFile.EntryMode != "" {
|
||||
if entryMode := git.ParseEntryMode(diffFile.EntryMode); !entryMode.IsRegular() {
|
||||
return entryModeTr(diffFile.EntryMode)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type limitByteWriter struct {
|
||||
@@ -695,10 +710,10 @@ parsingLoop:
|
||||
strings.HasPrefix(line, "new mode "):
|
||||
|
||||
if strings.HasPrefix(line, "old mode ") {
|
||||
curFile.OldMode = prepareValue(line, "old mode ")
|
||||
curFile.OldEntryMode = prepareValue(line, "old mode ")
|
||||
}
|
||||
if strings.HasPrefix(line, "new mode ") {
|
||||
curFile.Mode = prepareValue(line, "new mode ")
|
||||
curFile.EntryMode = prepareValue(line, "new mode ")
|
||||
}
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{}
|
||||
@@ -733,7 +748,7 @@ parsingLoop:
|
||||
curFile.Type = DiffFileAdd
|
||||
curFile.IsCreated = true
|
||||
if strings.HasPrefix(line, "new file mode ") {
|
||||
curFile.Mode = prepareValue(line, "new file mode ")
|
||||
curFile.EntryMode = prepareValue(line, "new file mode ")
|
||||
}
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{}
|
||||
|
||||
@@ -90,7 +90,7 @@ func GetListLockHandler(ctx *context.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
lock, err := git_model.GetLFSLockByID(ctx, v)
|
||||
lock, err := git_model.GetLFSLockByIDAndRepo(ctx, v, repository.ID)
|
||||
if err != nil && !git_model.IsErrLFSLockNotExist(err) {
|
||||
log.Error("Unable to get lock with ID[%s]: Error: %v", v, err)
|
||||
}
|
||||
|
||||
@@ -82,43 +82,42 @@
|
||||
{{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}}
|
||||
{{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.Repository.IsArchived) $.IsShowingAllCommits}}
|
||||
<div class="diff-file-box file-content {{TabSizeClass $.Editorconfig $file.Name}} tw-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
|
||||
<h4 class="diff-file-header sticky-2nd-row ui top attached header">
|
||||
<div class="diff-file-header sticky-2nd-row ui top attached header">
|
||||
<div class="diff-file-name tw-flex tw-flex-1 tw-items-center tw-gap-1 tw-flex-wrap">
|
||||
<button class="fold-file btn interact-bg tw-p-1{{if not $isExpandable}} tw-invisible{{end}}">
|
||||
{{if $file.ShouldBeHidden}}
|
||||
{{svg "octicon-chevron-right" 18}}
|
||||
{{else}}
|
||||
{{svg "octicon-chevron-down" 18}}
|
||||
{{end}}
|
||||
</button>
|
||||
<div class="tw-font-semibold tw-flex tw-items-center tw-font-mono">
|
||||
{{if $file.IsBin}}
|
||||
<span class="tw-ml-0.5 tw-mr-2">
|
||||
{{ctx.Locale.Tr "repo.diff.bin"}}
|
||||
</span>
|
||||
{{else}}
|
||||
{{template "repo/diff/stats" dict "file" . "root" $}}
|
||||
{{end}}
|
||||
<div class="flex-text-block">
|
||||
<button class="fold-file btn interact-bg tw-flex-shrink-0 tw-p-1{{if not $isExpandable}} tw-invisible{{end}}">
|
||||
{{if $file.ShouldBeHidden}}
|
||||
{{svg "octicon-chevron-right" 18}}
|
||||
{{else}}
|
||||
{{svg "octicon-chevron-down" 18}}
|
||||
{{end}}
|
||||
</button>
|
||||
{{$entryModeText := $file.TranslateDiffEntryMode ctx.Locale}}
|
||||
<a class="muted file-link tw-font-mono" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">
|
||||
{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}
|
||||
</a>
|
||||
</div>
|
||||
<span class="file tw-flex tw-items-center tw-font-mono tw-flex-1"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>
|
||||
<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
|
||||
{{if .IsLFSFile}}<span class="ui label">LFS</span>{{end}}
|
||||
{{if $file.IsGenerated}}
|
||||
<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
|
||||
{{end}}
|
||||
{{if $file.IsVendored}}
|
||||
<span class="ui label">{{ctx.Locale.Tr "repo.diff.vendored"}}</span>
|
||||
{{end}}
|
||||
{{if and $file.Mode $file.OldMode}}
|
||||
{{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}}
|
||||
{{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}
|
||||
<span class="tw-mx-2 tw-font-mono tw-whitespace-nowrap">{{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}}</span>
|
||||
{{else if $file.Mode}}
|
||||
<span class="tw-mx-2 tw-font-mono tw-whitespace-nowrap">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
<button class="btn interact-fg tw-p-2 tw-shrink-0" data-clipboard-text="{{$file.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
|
||||
{{if $file.IsLFSFile}}
|
||||
<span class="ui label">LFS</span>
|
||||
{{end}}
|
||||
{{if $file.IsGenerated}}
|
||||
<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
|
||||
{{end}}
|
||||
{{if $file.IsVendored}}
|
||||
<span class="ui label">{{ctx.Locale.Tr "repo.diff.vendored"}}</span>
|
||||
{{end}}
|
||||
{{if $entryModeText}}
|
||||
<span class="ui label">{{$entryModeText}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="diff-file-header-actions tw-flex tw-items-center tw-gap-1 tw-flex-wrap">
|
||||
<div class="diff-file-header-actions flex-text-block tw-justify-end tw-flex-wrap">
|
||||
{{if $file.IsBin}}
|
||||
{{ctx.Locale.Tr "repo.diff.bin"}}
|
||||
{{else}}
|
||||
{{template "repo/diff/stats" dict "Addition" .Addition "Deletion" .Deletion}}
|
||||
{{end}}
|
||||
|
||||
{{if $showFileViewToggle}}
|
||||
<div class="ui compact icon buttons">
|
||||
<button class="ui tiny basic button file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code"}}</button>
|
||||
@@ -157,7 +156,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="diff-file-body ui attached unstackable table segment" {{if and $file.IsViewed $.IsShowingAllCommits}}data-folded="true"{{end}}>
|
||||
<div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} tw-hidden{{end}}">
|
||||
{{if or $file.IsIncomplete $file.IsBin}}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
{{Eval .file.Addition "+" .file.Deletion}}
|
||||
<span class="diff-stats-bar tw-mx-2" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.stats_desc_file" (Eval .file.Addition "+" .file.Deletion) .file.Addition .file.Deletion}}">
|
||||
{{/* if the denominator is zero, then the float result is "width: NaNpx", as before, it just works */}}
|
||||
<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .file.Addition "/" "(" .file.Addition "+" .file.Deletion "+" 0.0 ")"}}%"></div>
|
||||
</span>
|
||||
{{/* Template Attributes:
|
||||
* Addition: Number of additions
|
||||
* Deletion: Number of deletions
|
||||
* Classes: Additional classes for the root element
|
||||
*/}}
|
||||
{{if or .Addition .Deletion}}
|
||||
<div class="flex-text-block tw-flex-shrink-0 tw-text-[13px] {{if .Classes}}{{.Classes}}{{end}}">
|
||||
<span>
|
||||
{{if .Addition}}<span class="tw-text-diff-prompt-add-fg">+{{.Addition}}</span>{{end}}
|
||||
{{if .Deletion}}<span class="tw-text-diff-prompt-del-fg">-{{.Deletion}}</span>{{end}}
|
||||
</span>
|
||||
<span class="diff-stats-bar" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.stats_desc_file" (Eval .Addition "+" .Deletion) .Addition .Deletion}}">
|
||||
{{/* if the denominator is zero, then the float result is "width: NaNpx", as before, it just works */}}
|
||||
<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Addition "/" "(" .Addition "+" .Deletion "+" 0.0 ")"}}%"></div>
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -16,12 +16,7 @@
|
||||
<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
|
||||
</a>
|
||||
{{if or .DiffShortStat.TotalAddition .DiffShortStat.TotalDeletion}}
|
||||
<span class="tw-ml-auto tw-pl-3 tw-whitespace-nowrap tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
|
||||
<span><span class="text green">{{if .DiffShortStat.TotalAddition}}+{{.DiffShortStat.TotalAddition}}{{end}}</span> <span class="text red">{{if .DiffShortStat.TotalDeletion}}-{{.DiffShortStat.TotalDeletion}}{{end}}</span></span>
|
||||
<span class="diff-stats-bar">
|
||||
<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .DiffShortStat.TotalAddition "/" "(" .DiffShortStat.TotalAddition "+" .DiffShortStat.TotalDeletion "+" 0.0 ")"}}%"></div>
|
||||
</span>
|
||||
</span>
|
||||
{{template "repo/diff/stats" dict "Addition" .DiffShortStat.TotalAddition "Deletion" .DiffShortStat.TotalDeletion "Classes" "tw-ml-auto tw-pl-3 tw-font-semibold"}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="ui tabs divider"></div>
|
||||
|
||||
@@ -34,6 +34,14 @@ func testGeneratePngBytes() []byte {
|
||||
}
|
||||
|
||||
func testCreateIssueAttachment(t *testing.T, session *TestSession, repoURL, filename string, content []byte, expectedStatus int) string {
|
||||
return testCreateAttachment(t, session, repoURL, "issues", filename, content, expectedStatus)
|
||||
}
|
||||
|
||||
func testCreateReleaseAttachment(t *testing.T, session *TestSession, repoURL, filename string, content []byte, expectedStatus int) string {
|
||||
return testCreateAttachment(t, session, repoURL, "releases", filename, content, expectedStatus)
|
||||
}
|
||||
|
||||
func testCreateAttachment(t *testing.T, session *TestSession, repoURL, issueOrRelease, filename string, content []byte, expectedStatus int) string {
|
||||
body := &bytes.Buffer{}
|
||||
|
||||
// Setup multi-part
|
||||
@@ -45,7 +53,7 @@ func testCreateIssueAttachment(t *testing.T, session *TestSession, repoURL, file
|
||||
err = writer.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := NewRequestWithBody(t, "POST", repoURL+"/issues/attachments", body)
|
||||
req := NewRequestWithBody(t, "POST", repoURL+"/"+issueOrRelease+"/attachments", body)
|
||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||
resp := session.MakeRequest(t, req, expectedStatus)
|
||||
|
||||
@@ -57,12 +65,23 @@ func testCreateIssueAttachment(t *testing.T, session *TestSession, repoURL, file
|
||||
return obj["uuid"]
|
||||
}
|
||||
|
||||
func testDeleteIssueAttachment(t *testing.T, session *TestSession, repoURL, uuid string, expectedStatus int) {
|
||||
req := NewRequestWithValues(t, "POST", repoURL+"/issues/attachments/remove", map[string]string{"file": uuid})
|
||||
session.MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
func testDeleteReleaseAttachment(t *testing.T, session *TestSession, repoURL, uuid string, expectedStatus int) {
|
||||
req := NewRequestWithValues(t, "POST", repoURL+"/releases/attachments/remove", map[string]string{"file": uuid})
|
||||
session.MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
func TestAttachments(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
t.Run("CreateAnonymousAttachment", testCreateAnonymousAttachment)
|
||||
t.Run("CreateUser2IssueAttachment", testCreateUser2IssueAttachment)
|
||||
t.Run("UploadAttachmentDeleteTemp", testUploadAttachmentDeleteTemp)
|
||||
t.Run("GetAttachment", testGetAttachment)
|
||||
t.Run("DeleteAttachmentPermissions", testDeleteAttachmentPermissions)
|
||||
}
|
||||
|
||||
func testUploadAttachmentDeleteTemp(t *testing.T) {
|
||||
@@ -157,3 +176,28 @@ func testGetAttachment(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testDeleteAttachmentPermissions(t *testing.T) {
|
||||
const repoURL = "user2/repo1"
|
||||
|
||||
ownerSession := loginUser(t, "user2")
|
||||
readonlySession := loginUser(t, "user5")
|
||||
|
||||
issueFromOwner := testCreateIssueAttachment(t, ownerSession, repoURL, "owner-issue.png", testGeneratePngBytes(), http.StatusOK)
|
||||
testDeleteIssueAttachment(t, readonlySession, repoURL, issueFromOwner, http.StatusForbidden)
|
||||
|
||||
issueFromReader := testCreateIssueAttachment(t, readonlySession, repoURL, "reader-issue.png", testGeneratePngBytes(), http.StatusOK)
|
||||
testDeleteIssueAttachment(t, ownerSession, repoURL, issueFromReader, http.StatusOK)
|
||||
|
||||
testCreateReleaseAttachment(t, readonlySession, repoURL, "reader-release.png", testGeneratePngBytes(), http.StatusNotFound)
|
||||
|
||||
crossRepoUUID := testCreateIssueAttachment(t, ownerSession, repoURL, "cross-repo.png", testGeneratePngBytes(), http.StatusOK)
|
||||
testDeleteIssueAttachment(t, ownerSession, "user2/repo2", crossRepoUUID, http.StatusBadRequest)
|
||||
testDeleteIssueAttachment(t, ownerSession, repoURL, crossRepoUUID, http.StatusOK)
|
||||
|
||||
releaseUUID := testCreateReleaseAttachment(t, ownerSession, repoURL, "reader-release.png", testGeneratePngBytes(), http.StatusOK)
|
||||
testDeleteReleaseAttachment(t, ownerSession, repoURL, releaseUUID, http.StatusOK)
|
||||
|
||||
// test deleting release attachment from another repo
|
||||
testDeleteReleaseAttachment(t, ownerSession, "user2/repo2", crossRepoUUID, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
+17
-8
@@ -1293,8 +1293,7 @@ td .commit-summary {
|
||||
filter: drop-shadow(-4px 0 0 var(--color-primary-alpha-30)) !important;
|
||||
}
|
||||
|
||||
.code-comment:target,
|
||||
.diff-file-box:target {
|
||||
.code-comment:target {
|
||||
border-color: var(--color-primary) !important;
|
||||
border-radius: var(--border-radius) !important;
|
||||
box-shadow: 0 0 0 3px var(--color-primary-alpha-30) !important;
|
||||
@@ -1681,18 +1680,27 @@ tbody.commit-list {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.diff-file-box:target {
|
||||
border-color: var(--color-primary) !important;
|
||||
border-radius: var(--border-radius) !important;
|
||||
box-shadow: 0 0 0 3px var(--color-primary-alpha-30) !important;
|
||||
}
|
||||
|
||||
.diff-file-header {
|
||||
padding: 5px 8px !important;
|
||||
box-shadow: 0 -1px 0 1px var(--color-body); /* prevent borders being visible behind top corners when sticky and scrolled */
|
||||
font-weight: var(--font-weight-normal);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
|
||||
/* prevent borders from being visible behind top corners when sticky and scrolled,
|
||||
this "shadow" is used to use body's color to cover the scrolled-up left and right borders at corners */
|
||||
box-shadow: 0 -1px 0 1px var(--color-body);
|
||||
}
|
||||
|
||||
.diff-file-header .file {
|
||||
min-width: 0;
|
||||
.diff-file-box:target .diff-file-header {
|
||||
box-shadow: unset; /* when targeted, still use the parent's box-shadow, remove the patched above */
|
||||
}
|
||||
|
||||
.diff-file-header .file-link {
|
||||
@@ -1715,6 +1723,7 @@ tbody.commit-list {
|
||||
.diff-file-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1743,13 +1752,13 @@ tbody.commit-list {
|
||||
|
||||
.diff-stats-bar {
|
||||
display: inline-block;
|
||||
background-color: var(--color-red);
|
||||
background-color: var(--color-diff-prompt-del-fg); /* the background is used as "text foreground color" */
|
||||
height: 12px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.diff-stats-bar .diff-stats-add-bar {
|
||||
background-color: var(--color-green);
|
||||
background-color: var(--color-diff-prompt-add-fg);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,8 @@ gitea-theme-meta-info {
|
||||
--color-diff-removed-row-bg: #301e1e;
|
||||
--color-diff-removed-row-border: #634343;
|
||||
--color-diff-removed-word-bg: #6f3333;
|
||||
--color-diff-prompt-add-fg: #87ab63;
|
||||
--color-diff-prompt-del-fg: #cc4848;
|
||||
--color-diff-inactive: #22282d;
|
||||
--color-error-border: #a04141;
|
||||
--color-error-bg: #522;
|
||||
|
||||
@@ -158,6 +158,8 @@ gitea-theme-meta-info {
|
||||
--color-diff-removed-row-bg: #ffeef0;
|
||||
--color-diff-removed-row-border: #f1c0c0;
|
||||
--color-diff-removed-word-bg: #fdb8c0;
|
||||
--color-diff-prompt-add-fg: #21ba45;
|
||||
--color-diff-prompt-del-fg: #db2828;
|
||||
--color-diff-inactive: #f0f2f4;
|
||||
--color-error-border: #e0b4b4;
|
||||
--color-error-bg: #fff6f6;
|
||||
|
||||
Reference in New Issue
Block a user