Compare commits

...

10 Commits

Author SHA1 Message Date
silverwind
1195d66c15
Prevent SVG shrinking (#25652)
This will prevent the most common cases of SVG shrinking because lack of
space. I evaluated multiple options and this seems to be the one with
the least impact in size and processing cost, so I went with it.

Unfortunately, CSS can not dynamically convert `16` obtained from
`attr()` to `16px`, or else a generic solution for all sizes would have
been possible. But a solution is [in
sight](https://developer.mozilla.org/en-US/docs/Web/CSS/attr#type-or-unit)
with `attr(width px)` but no browser supports it currently.
2023-07-04 02:15:06 +00:00
Lunny Xiao
0403bd989f
Log the real reason when authentication fails (but don't show the user) (#25414) 2023-07-03 18:39:38 -04:00
Earl Warren
ad57be04b8
Add unit test for repository collaboration (#25640)
Add a few extra test cases and test functions for the collaboration
model to get everything covered by tests (except for error handling, as
we cannot suddenly mock errors from the database).


Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/825
Co-authored-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2023-07-03 21:41:14 +00:00
wxiaoguang
eea58a5d55
Fix UI misalignment on user setting page (#25629)
Fix #25628

Diff with ignoring space:
https://github.com/go-gitea/gitea/pull/25629/files?diff=unified&w=1

The "modal" shouldn't appear between "ui attached segment", otherwise
these segments lose margin-top.

After the fix:

<details>


![image](https://github.com/go-gitea/gitea/assets/2114189/ac15e73d-4ca8-416a-950d-ffc912c6ab61)


![image](https://github.com/go-gitea/gitea/assets/2114189/eb431336-4d21-4e44-8beb-8919595a83c8)


![image](https://github.com/go-gitea/gitea/assets/2114189/dffaed88-5ba2-419d-a241-24cb200f757c)


![image](https://github.com/go-gitea/gitea/assets/2114189/e8c5a03e-e16a-4c94-a1a5-7845d4e1a824)

</details>
2023-07-03 20:38:06 +00:00
GiteaBot
deb007ca2b [skip ci] Updated translations via Crowdin 2023-07-03 20:02:34 +00:00
Lunny Xiao
63622f8fb6
Correct translation wrong format (#25643)
partially fix #25291

---------

Co-authored-by: Lauris BH <lauris@nix.lv>
2023-07-03 19:33:45 +00:00
KN4CK3R
c890454769
Add direct serving of package content (#25543)
Fixes #24723

Direct serving of content aka HTTP redirect is not mentioned in any of
the package registry specs but lots of official registries do that so it
should be supported by the usual clients.
2023-07-03 15:33:28 +02:00
Lunny Xiao
f1cb461c1f
Fix bug when change user name (#25637)
Fix #25621

Co-authored-by: Giteabot <teabot@gitea.io>
2023-07-03 12:05:59 +02:00
wxiaoguang
45bc180a15
Make "cancel" buttons have proper type in modal forms (#25618)
Replace #25446, fix #25438

All "cancel" buttons which do not have "type" should not submit the
form, should not be triggered by "Enter".

This is a complete fix for all modal dialogs.

The major change is "modules/aria/modal.js", "devtest" related code is
for demo/test purpose.
2023-07-03 14:04:50 +08:00
wxiaoguang
2aa6a785cf
Make FindBranches have stable result (#25631)
See the comment
2023-07-03 04:11:32 +00:00
63 changed files with 616 additions and 393 deletions

View File

@ -64,11 +64,6 @@ func (branches BranchList) LoadPusher(ctx context.Context) error {
return nil return nil
} }
const (
BranchOrderByNameAsc = "name ASC"
BranchOrderByCommitTimeDesc = "commit_time DESC"
)
type FindBranchOptions struct { type FindBranchOptions struct {
db.ListOptions db.ListOptions
RepoID int64 RepoID int64
@ -102,7 +97,8 @@ func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
} }
if opts.OrderBy == "" { if opts.OrderBy == "" {
opts.OrderBy = BranchOrderByCommitTimeDesc // the commit_time might be the same, so add the "name" to make sure the order is stable
opts.OrderBy = "commit_time DESC, name ASC"
} }
return sess.OrderBy(opts.OrderBy) return sess.OrderBy(opts.OrderBy)
} }

View File

@ -33,6 +33,19 @@ func TestRepository_GetCollaborators(t *testing.T) {
test(2) test(2)
test(3) test(3)
test(4) test(4)
// Test db.ListOptions
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22})
collaborators1, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{PageSize: 1, Page: 1})
assert.NoError(t, err)
assert.Len(t, collaborators1, 1)
collaborators2, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{PageSize: 1, Page: 2})
assert.NoError(t, err)
assert.Len(t, collaborators2, 1)
assert.NotEqualValues(t, collaborators1[0].ID, collaborators2[0].ID)
} }
func TestRepository_IsCollaborator(t *testing.T) { func TestRepository_IsCollaborator(t *testing.T) {
@ -66,5 +79,80 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) {
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin)) assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin))
// Disvard invalid input.
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(unittest.NonexistentID)))
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID}) unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID})
} }
func TestRepository_CountCollaborators(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
count, err := repo_model.CountCollaborators(repo1.ID)
assert.NoError(t, err)
assert.EqualValues(t, 2, count)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22})
count, err = repo_model.CountCollaborators(repo2.ID)
assert.NoError(t, err)
assert.EqualValues(t, 2, count)
// Non-existent repository.
count, err = repo_model.CountCollaborators(unittest.NonexistentID)
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
}
func TestRepository_IsOwnerMemberCollaborator(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
// Organisation owner.
actual, err := repo_model.IsOwnerMemberCollaborator(repo1, 2)
assert.NoError(t, err)
assert.True(t, actual)
// Team member.
actual, err = repo_model.IsOwnerMemberCollaborator(repo1, 4)
assert.NoError(t, err)
assert.True(t, actual)
// Normal user.
actual, err = repo_model.IsOwnerMemberCollaborator(repo1, 1)
assert.NoError(t, err)
assert.False(t, actual)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
// Collaborator.
actual, err = repo_model.IsOwnerMemberCollaborator(repo2, 4)
assert.NoError(t, err)
assert.True(t, actual)
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15})
// Repository owner.
actual, err = repo_model.IsOwnerMemberCollaborator(repo3, 2)
assert.NoError(t, err)
assert.True(t, actual)
}
func TestRepo_GetCollaboration(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
// Existing collaboration.
collab, err := repo_model.GetCollaboration(db.DefaultContext, repo.ID, 4)
assert.NoError(t, err)
assert.NotNil(t, collab)
assert.EqualValues(t, 4, collab.UserID)
assert.EqualValues(t, 4, collab.RepoID)
// Non-existing collaboration.
collab, err = repo_model.GetCollaboration(db.DefaultContext, repo.ID, 1)
assert.NoError(t, err)
assert.Nil(t, collab)
}

View File

@ -64,6 +64,10 @@ func NewUserRedirect(ctx context.Context, ID int64, oldUserName, newUserName str
oldUserName = strings.ToLower(oldUserName) oldUserName = strings.ToLower(oldUserName)
newUserName = strings.ToLower(newUserName) newUserName = strings.ToLower(newUserName)
if err := DeleteUserRedirect(ctx, oldUserName); err != nil {
return err
}
if err := DeleteUserRedirect(ctx, newUserName); err != nil { if err := DeleteUserRedirect(ctx, newUserName); err != nil {
return err return err
} }

View File

@ -5,9 +5,11 @@ package packages
import ( import (
"io" "io"
"net/url"
"path" "path"
"strings" "strings"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
@ -31,6 +33,14 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
return s.store.Open(KeyToRelativePath(key)) return s.store.Open(KeyToRelativePath(key))
} }
func (s *ContentStore) ShouldServeDirect() bool {
return setting.Packages.Storage.MinioConfig.ServeDirect
}
func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string) (*url.URL, error) {
return s.store.URL(KeyToRelativePath(key), filename)
}
// FIXME: Workaround to be removed in v1.20 // FIXME: Workaround to be removed in v1.20
// https://github.com/go-gitea/gitea/issues/19586 // https://github.com/go-gitea/gitea/issues/19586
func (s *ContentStore) Has(key BlobHash256Key) error { func (s *ContentStore) Has(key BlobHash256Key) error {

View File

@ -383,7 +383,7 @@ hi_user_x=Ahoj <b>%s</b>,
activate_account=Prosíme, aktivujte si váš účet activate_account=Prosíme, aktivujte si váš účet
activate_account.title=%s, prosím aktivujte si váš účet activate_account.title=%s, prosím aktivujte si váš účet
activate_account.text_1=Ahoj <b>%[1]s</b>, děkujeme za registraci na %[2]! activate_account.text_1=Ahoj <b>%[1]s</b>, děkujeme za registraci na %[2]s!
activate_account.text_2=Pro aktivaci vašeho účtu do <b>%s</b> klikněte na následující odkaz: activate_account.text_2=Pro aktivaci vašeho účtu do <b>%s</b> klikněte na následující odkaz:
activate_email=Ověřte vaši e-mailovou adresu activate_email=Ověřte vaši e-mailovou adresu
@ -402,8 +402,8 @@ reset_password.text=Klikněte prosím na následující odkaz pro obnovení vaš
register_success=Registrace byla úspěšná register_success=Registrace byla úspěšná
issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]v repozitáři %[3]s. issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]s repozitáři %[3]s.
issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]v repozitáři %[3]s. issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]s repozitáři %[3]s.
issue.x_mentioned_you=<b>@%s</b> vás zmínil/a: issue.x_mentioned_you=<b>@%s</b> vás zmínil/a:
issue.action.force_push=<b>%[1]s</b> vynutil/a nahrání <b>%[2]s</b> z %[3]s do %[4]s. issue.action.force_push=<b>%[1]s</b> vynutil/a nahrání <b>%[2]s</b> z %[3]s do %[4]s.
@ -2310,7 +2310,7 @@ members.member_role=Role člena:
members.owner=Vlastník members.owner=Vlastník
members.member=Člen members.member=Člen
members.remove=Smazat members.remove=Smazat
members.remove.detail=Odstranit %[1]s z %[2]? members.remove.detail=Odstranit %[1]s z %[2]s?
members.leave=Opustit members.leave=Opustit
members.leave.detail=Opustit %s? members.leave.detail=Opustit %s?
members.invite_desc=Přidat nového člena do %s: members.invite_desc=Přidat nového člena do %s:

View File

@ -19,6 +19,7 @@ active_stopwatch=Aktive Zeiterfassung
create_new=Erstellen… create_new=Erstellen…
user_profile_and_more=Profil und Einstellungen… user_profile_and_more=Profil und Einstellungen…
signed_in_as=Angemeldet als signed_in_as=Angemeldet als
enable_javascript=Diese Website benötigt JavaScript.
toc=Inhaltsverzeichnis toc=Inhaltsverzeichnis
licenses=Lizenzen licenses=Lizenzen
return_to_gitea=Zurück zu Gitea return_to_gitea=Zurück zu Gitea
@ -92,6 +93,7 @@ copy_url=URL kopieren
copy_branch=Branchenname kopieren copy_branch=Branchenname kopieren
copy_success=Kopiert! copy_success=Kopiert!
copy_error=Kopieren fehlgeschlagen copy_error=Kopieren fehlgeschlagen
copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden
write=Verfassen write=Verfassen
preview=Vorschau preview=Vorschau
@ -104,22 +106,42 @@ error=Fehler
error404=Die Seite, die du gerade versuchst aufzurufen, <strong>existiert entweder nicht</strong> oder <strong>du bist nicht berechtigt</strong>, diese anzusehen. error404=Die Seite, die du gerade versuchst aufzurufen, <strong>existiert entweder nicht</strong> oder <strong>du bist nicht berechtigt</strong>, diese anzusehen.
never=Niemals never=Niemals
unknown=Unbekannt
rss_feed=RSS Feed rss_feed=RSS Feed
artifacts=Artefakte
concept_system_global=Global
concept_code_repository=Repository concept_code_repository=Repository
concept_user_organization=Organisation concept_user_organization=Organisation
show_log_seconds=Sekunden anzeigen
[aria] [aria]
footer.software=Über die Software
footer.links=Links
[heatmap] [heatmap]
less=Weniger
more=Mehr
[editor] [editor]
buttons.heading.tooltip=Titel hinzufügen
buttons.quote.tooltip=Text zitieren
buttons.code.tooltip=Code hinzufügen
buttons.link.tooltip=Link hinzufügen
buttons.list.ordered.tooltip=Nummerierte Liste hinzufügen
buttons.list.task.tooltip=Aufgabenliste hinzufügen
buttons.mention.tooltip=Benutzer oder Team erwähnen
buttons.ref.tooltip=Issue oder Pull-Request referenzieren
buttons.enable_monospace_font=Monospace-Schrift aktivieren
buttons.disable_monospace_font=Monospace-Schrift deaktivieren
[filter] [filter]
string.asc=AZ
string.desc=ZA
[error] [error]
occurred=Ein Fehler ist aufgetreten occurred=Ein Fehler ist aufgetreten
@ -228,6 +250,7 @@ install_btn_confirm=Gitea installieren
test_git_failed=Fehler beim Test des „git“-Befehls: %v test_git_failed=Fehler beim Test des „git“-Befehls: %v
sqlite3_not_available=Diese Gitea-Version unterstützt SQLite3 nicht. Bitte lade die offizielle binäre Version von %s herunter (nicht die „gobuild“-Version). sqlite3_not_available=Diese Gitea-Version unterstützt SQLite3 nicht. Bitte lade die offizielle binäre Version von %s herunter (nicht die „gobuild“-Version).
invalid_db_setting=Datenbankeinstellungen sind ungültig: %v invalid_db_setting=Datenbankeinstellungen sind ungültig: %v
invalid_db_table=Die Datenbanktabelle "%s" ist ungültig: %v
invalid_repo_path=Repository-Verzeichnis ist ungültig: %v invalid_repo_path=Repository-Verzeichnis ist ungültig: %v
invalid_app_data_path=Der App-Daten-Pfad ist ungültig: %v invalid_app_data_path=Der App-Daten-Pfad ist ungültig: %v
run_user_not_match=Der „Ausführen als“-Benutzername ist nicht der aktuelle Benutzername: %s -> %s run_user_not_match=Der „Ausführen als“-Benutzername ist nicht der aktuelle Benutzername: %s -> %s
@ -245,6 +268,7 @@ default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Reposi
no_reply_address=Versteckte E-Mail-Domain no_reply_address=Versteckte E-Mail-Domain
no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist. no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist.
password_algorithm=Passwort Hashing Algorithmus password_algorithm=Passwort Hashing Algorithmus
invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus
[home] [home]
uname_holder=E-Mail-Adresse oder Benutzername uname_holder=E-Mail-Adresse oder Benutzername
@ -279,6 +303,7 @@ users=Benutzer
organizations=Organisationen organizations=Organisationen
search=Suche search=Suche
code=Code code=Code
search.type.tooltip=Suchmodus
search.fuzzy=Ähnlich search.fuzzy=Ähnlich
search.match=Genau search.match=Genau
code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
@ -545,6 +570,7 @@ location=Standort
update_theme=Theme ändern update_theme=Theme ändern
update_profile=Profil aktualisieren update_profile=Profil aktualisieren
update_language=Sprache aktualisieren update_language=Sprache aktualisieren
update_language_not_found=Sprache "%s" ist nicht verfügbar.
update_language_success=Sprache wurde aktualisiert. update_language_success=Sprache wurde aktualisiert.
update_profile_success=Dein Profil wurde aktualisiert. update_profile_success=Dein Profil wurde aktualisiert.
change_username=Dein Benutzername wurde geändert. change_username=Dein Benutzername wurde geändert.
@ -655,6 +681,7 @@ gpg_token_help=Du kannst eine Signatur wie folgt generieren:
gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
gpg_token_signature=GPG Textsignatur (armored signature) gpg_token_signature=GPG Textsignatur (armored signature)
key_signature_gpg_placeholder=Beginnt mit '-----BEGIN PGP SIGNATURE-----' key_signature_gpg_placeholder=Beginnt mit '-----BEGIN PGP SIGNATURE-----'
verify_gpg_key_success=GPG-Schlüssel "%s" wurde verifiziert.
ssh_key_verified=Verifizierter Schlüssel ssh_key_verified=Verifizierter Schlüssel
ssh_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. ssh_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen.
ssh_key_verify=Verifizieren ssh_key_verify=Verifizieren
@ -975,6 +1002,7 @@ download_archive=Repository herunterladen
no_desc=Keine Beschreibung no_desc=Keine Beschreibung
quick_guide=Kurzanleitung quick_guide=Kurzanleitung
clone_this_repo=Dieses Repository klonen clone_this_repo=Dieses Repository klonen
cite_this_repo=Dieses Repository zitieren
create_new_repo_command=Erstelle ein neues Repository von der Kommandozeile aus create_new_repo_command=Erstelle ein neues Repository von der Kommandozeile aus
push_exist_repo=Bestehendes Repository via Kommandozeile pushen push_exist_repo=Bestehendes Repository via Kommandozeile pushen
empty_message=Dieses Repository hat keinen Inhalt. empty_message=Dieses Repository hat keinen Inhalt.
@ -993,6 +1021,7 @@ issues=Issues
pulls=Pull-Requests pulls=Pull-Requests
project_board=Projekte project_board=Projekte
packages=Pakete packages=Pakete
actions=Actions
labels=Label labels=Label
org_labels_desc=Labels der Organisationsebene, die mit <strong>allen Repositories</strong> in dieser Organisation verwendet werden können org_labels_desc=Labels der Organisationsebene, die mit <strong>allen Repositories</strong> in dieser Organisation verwendet werden können
org_labels_desc_manage=verwalten org_labels_desc_manage=verwalten
@ -1012,6 +1041,7 @@ file_view_rendered=Ansicht rendern
file_view_raw=Originalformat anzeigen file_view_raw=Originalformat anzeigen
file_permalink=Permalink file_permalink=Permalink
file_too_large=Die Datei ist zu groß zum Anzeigen. file_too_large=Die Datei ist zu groß zum Anzeigen.
invisible_runes_header=`Diese Datei enthält unsichtbare Unicode-Zeichen!`
ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden` ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden`
escape_control_characters=Escapen escape_control_characters=Escapen
@ -1032,6 +1062,7 @@ download_file=Datei herunterladen
normal_view=Normale Ansicht normal_view=Normale Ansicht
line=zeile line=zeile
lines=Zeilen lines=Zeilen
from_comment=(Kommentar)
editor.add_file=Datei hinzufügen editor.add_file=Datei hinzufügen
editor.new_file=Neue Datei editor.new_file=Neue Datei
@ -1046,6 +1077,7 @@ editor.must_be_on_a_branch=Du musst dich in einem Branch befinden, um Änderunge
editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
editor.delete_this_file=Datei löschen editor.delete_this_file=Datei löschen
editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen.
editor.file_delete_success=Datei "%s" wurde gelöscht.
editor.name_your_file=Dateinamen eingeben… editor.name_your_file=Dateinamen eingeben…
editor.filename_help=Füge einen Ordner hinzu, indem du seinen Namen und anschließend '/' eingibst. Entferne einen Ordner indem du die Zurücktaste am Anfang des Feldes drückst. editor.filename_help=Füge einen Ordner hinzu, indem du seinen Namen und anschließend '/' eingibst. Entferne einen Ordner indem du die Zurücktaste am Anfang des Feldes drückst.
editor.or=oder editor.or=oder
@ -1681,6 +1713,7 @@ activity.git_stats_deletion_n=%d Löschungen
search=Suchen search=Suchen
search.search_repo=Repository durchsuchen search.search_repo=Repository durchsuchen
search.type.tooltip=Suchmodus
search.fuzzy=Ähnlich search.fuzzy=Ähnlich
search.match=Genau search.match=Genau
search.results=Suchergebnisse für „%s“ in <a href="%s"> %s</a> search.results=Suchergebnisse für „%s“ in <a href="%s"> %s</a>
@ -2158,6 +2191,8 @@ branch.delete_html=Branch löschen
branch.delete_desc=Das Löschen eines Branches ist permanent. Es <strong>KANN NICHT</strong> rückgängig gemacht werden. Fortfahren? branch.delete_desc=Das Löschen eines Branches ist permanent. Es <strong>KANN NICHT</strong> rückgängig gemacht werden. Fortfahren?
branch.create_branch=Erstelle Branch <strong>%s</strong> branch.create_branch=Erstelle Branch <strong>%s</strong>
branch.deleted_by=Von %s gelöscht branch.deleted_by=Von %s gelöscht
branch.restore=Branch "%s" wiederherstellen
branch.download=Branch "%s" herunterladen
branch.included_desc=Dieser Branch ist im Standard-Branch enthalten branch.included_desc=Dieser Branch ist im Standard-Branch enthalten
branch.included=Enthalten branch.included=Enthalten
branch.create_new_branch=Branch aus Branch erstellen: branch.create_new_branch=Branch aus Branch erstellen:
@ -2165,12 +2200,15 @@ branch.confirm_create_branch=Branch erstellen
branch.confirm_rename_branch=Branch umbennen branch.confirm_rename_branch=Branch umbennen
branch.create_branch_operation=Branch erstellen branch.create_branch_operation=Branch erstellen
branch.new_branch=Neue Branch erstellen branch.new_branch=Neue Branch erstellen
branch.new_branch_from=Neuen Branch von "%s" erstellen
branch.renamed=Branch %s wurde in %s umbenannt. branch.renamed=Branch %s wurde in %s umbenannt.
tag.create_tag=Tag <strong>%s</strong> erstellen tag.create_tag=Tag <strong>%s</strong> erstellen
tag.create_tag_operation=Tag erstellen tag.create_tag_operation=Tag erstellen
tag.confirm_create_tag=Tag erstellen tag.confirm_create_tag=Tag erstellen
tag.create_tag_from=Neuen Tag von "%s" erstellen
tag.create_success=Tag "%s" wurde erstellt.
topic.manage_topics=Themen verwalten topic.manage_topics=Themen verwalten
topic.done=Fertig topic.done=Fertig
@ -2407,6 +2445,7 @@ users.created=Registriert am
users.last_login=Letzte Anmeldung users.last_login=Letzte Anmeldung
users.never_login=Hat sich noch nie eingeloggt users.never_login=Hat sich noch nie eingeloggt
users.send_register_notify=Benutzer-Registrierungsbenachrichtigung senden users.send_register_notify=Benutzer-Registrierungsbenachrichtigung senden
users.new_success=Der Account "%s" wurde erstellt.
users.edit=Bearbeiten users.edit=Bearbeiten
users.auth_source=Authentifizierungsquelle users.auth_source=Authentifizierungsquelle
users.local=Lokal users.local=Lokal
@ -2770,7 +2809,7 @@ monitor.queue.review=Konfiguration überprüfen
monitor.queue.review_add=Worker hinzufügen/prüfen monitor.queue.review_add=Worker hinzufügen/prüfen
monitor.queue.settings.title=Pool-Einstellungen monitor.queue.settings.title=Pool-Einstellungen
monitor.queue.settings.maxnumberworkers=Maximale Anzahl an Workern monitor.queue.settings.maxnumberworkers=Maximale Anzahl an Workern
monitor.queue.settings.maxnumberworkers.placeholder=Derzeit %[1]v monitor.queue.settings.maxnumberworkers.placeholder=Derzeit %[1]s
monitor.queue.settings.maxnumberworkers.error=Die Anzahl der Worker muss eine Zahl sein monitor.queue.settings.maxnumberworkers.error=Die Anzahl der Worker muss eine Zahl sein
monitor.queue.settings.submit=Einstellungen aktualisieren monitor.queue.settings.submit=Einstellungen aktualisieren
monitor.queue.settings.changed=Einstellungen aktualisiert monitor.queue.settings.changed=Einstellungen aktualisiert
@ -2979,6 +3018,7 @@ settings.delete.description=Das Löschen eines Pakets ist dauerhaft und kann nic
settings.delete.notice=Du bist dabei, %s (%s) zu löschen. Dieser Vorgang ist unwiderruflich. Bist du sicher? settings.delete.notice=Du bist dabei, %s (%s) zu löschen. Dieser Vorgang ist unwiderruflich. Bist du sicher?
settings.delete.success=Das Paket wurde gelöscht. settings.delete.success=Das Paket wurde gelöscht.
settings.delete.error=Löschen des Pakets fehlgeschlagen. settings.delete.error=Löschen des Pakets fehlgeschlagen.
owner.settings.cargo.initialize=Index initialisieren
owner.settings.cleanuprules.enabled=Aktiviert owner.settings.cleanuprules.enabled=Aktiviert
[secrets] [secrets]
@ -2986,8 +3026,10 @@ value=Wert
name=Name name=Name
[actions] [actions]
actions=Actions
status.unknown=Unbekannt
runners.id=ID runners.id=ID
runners.name=Name runners.name=Name
@ -2997,10 +3039,16 @@ runners.labels=Labels
runners.task_list.run=Ausführen runners.task_list.run=Ausführen
runners.task_list.repository=Repository runners.task_list.repository=Repository
runners.task_list.commit=Commit runners.task_list.commit=Commit
runners.status.unspecified=Unbekannt
runners.status.idle=Inaktiv
runners.status.active=Aktiv runners.status.active=Aktiv
runners.status.offline=Offline
runners.version=Version runners.version=Version
runs.all_workflows=Alle Workflows
runs.commit=Commit runs.commit=Commit
runs.pushed_by=Gepushed von
runs.no_matching_runner_helper=Kein passender Runner: %s
[projects] [projects]

View File

@ -408,7 +408,7 @@ hi_user_x=Γειά σου <b>%s</b>,
activate_account=Παρακαλώ ενεργοποιήστε το λογαριασμό σας activate_account=Παρακαλώ ενεργοποιήστε το λογαριασμό σας
activate_account.title=%s, παρακαλώ ενεργοποιήστε το λογαριασμό σας activate_account.title=%s, παρακαλώ ενεργοποιήστε το λογαριασμό σας
activate_account.text_1=Γεια σας <b>%[1]s</b>, ευχαριστούμε για την εγγραφή στο %[2]! activate_account.text_1=Γεια σας <b>%[1]s</b>, ευχαριστούμε για την εγγραφή στο %[2]s!
activate_account.text_2=Παρακαλούμε κάντε κλικ στον παρακάτω σύνδεσμο για να ενεργοποιήσετε το λογαριασμό σας μέσα σε <b>%s</b>: activate_account.text_2=Παρακαλούμε κάντε κλικ στον παρακάτω σύνδεσμο για να ενεργοποιήσετε το λογαριασμό σας μέσα σε <b>%s</b>:
activate_email=Επιβεβαιώστε τη διεύθυνση email σας activate_email=Επιβεβαιώστε τη διεύθυνση email σας
@ -462,7 +462,7 @@ repo.collaborator.added.subject=%s σας πρόσθεσε στο %s
repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου: repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου:
team_invite.subject=%[1]s σας προσκάλεσε να συμμετέχετε στον οργανισμό %[2]s team_invite.subject=%[1]s σας προσκάλεσε να συμμετέχετε στον οργανισμό %[2]s
team_invite.text_1=%[1]s σας προσκάλεσε να συμμετέχετε στην ομάδα %[2]s στον οργανισμός %[3]. team_invite.text_1=%[1]s σας προσκάλεσε να συμμετέχετε στην ομάδα %[2]s στον οργανισμός %[3]s.
team_invite.text_2=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να συμμετάσχετε στην ομάδα: team_invite.text_2=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να συμμετάσχετε στην ομάδα:
team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προοριζόταν για %[1]s. Αν δεν περιμένατε αυτή την πρόσκληση, μπορείτε να αγνοήσετε αυτό το email. team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προοριζόταν για %[1]s. Αν δεν περιμένατε αυτή την πρόσκληση, μπορείτε να αγνοήσετε αυτό το email.

View File

@ -2650,7 +2650,7 @@ dashboard.task.finished=Task: %[1]s started by %[2]s has finished
dashboard.task.unknown=Unknown task: %[1]s dashboard.task.unknown=Unknown task: %[1]s
dashboard.cron.started=Started Cron: %[1]s dashboard.cron.started=Started Cron: %[1]s
dashboard.cron.process=Cron: %[1]s dashboard.cron.process=Cron: %[1]s
dashboard.cron.cancelled=Cron: %s cancelled: %[3]s dashboard.cron.cancelled=Cron: %[1]s cancelled: %[3]s
dashboard.cron.error=Error in Cron: %s: %[3]s dashboard.cron.error=Error in Cron: %s: %[3]s
dashboard.cron.finished=Cron: %[1]s has finished dashboard.cron.finished=Cron: %[1]s has finished
dashboard.delete_inactive_accounts = Delete all unactivated accounts dashboard.delete_inactive_accounts = Delete all unactivated accounts

View File

@ -357,7 +357,7 @@ reset_password.text=لطفاً روی پیوند زیر کلیک کنید تا
register_success=ثبت‌نام با موفقیت انجام شد register_success=ثبت‌نام با موفقیت انجام شد
issue_assigned.pull=@%[1] به شما برای درخواست pull %[2] در ریپازیتوری %[3] محول شده. issue_assigned.pull=@%[1]s به شما برای درخواست pull %[2]s در ریپازیتوری %[3]s محول شده.
issue_assigned.issue=@%[1]s به شما واگذار شده است برای صدور %[2]s در انبار %[3]s. issue_assigned.issue=@%[1]s به شما واگذار شده است برای صدور %[2]s در انبار %[3]s.
issue.x_mentioned_you=<b>@%s</b> به شما اشاره کرد: issue.x_mentioned_you=<b>@%s</b> به شما اشاره کرد:

View File

@ -803,7 +803,7 @@ issues.action_milestone=Tímamót
issues.action_milestone_no_select=Ekkert tímamót issues.action_milestone_no_select=Ekkert tímamót
issues.action_assignee=Úthlutað að issues.action_assignee=Úthlutað að
issues.opened_by=opnað %[1]s af <a href="%[2]s">%[3]s</a> issues.opened_by=opnað %[1]s af <a href="%[2]s">%[3]s</a>
issues.opened_by_fake=opnað %[1] af %[2]s issues.opened_by_fake=opnað %[1]s af %[2]s
issues.previous=Fyrri issues.previous=Fyrri
issues.next=Áfram issues.next=Áfram
issues.open_title=Opið issues.open_title=Opið

View File

@ -438,7 +438,7 @@ repo.collaborator.added.subject=%s pievienoja Jūs repozitorijam %s
repo.collaborator.added.text=Jūs tikāt pievienots kā līdzstrādnieks repozitorijam: repo.collaborator.added.text=Jūs tikāt pievienots kā līdzstrādnieks repozitorijam:
team_invite.subject=%[1]s uzaicināja Jūs pievienoties organizācijai %[2]s team_invite.subject=%[1]s uzaicināja Jūs pievienoties organizācijai %[2]s
team_invite.text_1=%[1]s uzaicināja Jūs pievienoties komandai %[2] organizācijā %[3]s. team_invite.text_1=%[1]s uzaicināja Jūs pievienoties komandai %[2]s organizācijā %[3]s.
team_invite.text_2=Uzspiediet uz šīs saites, lai pievienoties komandai: team_invite.text_2=Uzspiediet uz šīs saites, lai pievienoties komandai:
team_invite.text_3=Piezīme: Šis uzaicinājums ir paredzēts %[1]s. Ja uzskatāt, ka tas nav domāts Jums, varat ignorēt šo e-pastu. team_invite.text_3=Piezīme: Šis uzaicinājums ir paredzēts %[1]s. Ja uzskatāt, ka tas nav domāts Jums, varat ignorēt šo e-pastu.

View File

@ -384,7 +384,7 @@ reset_password.text=Klik op de volgende link om je account te herstellen binnen
register_success=Registratie succesvol register_success=Registratie succesvol
issue_assigned.pull=@%[1]s heeft u toegewezen aan de pull request %[2]s in repository %[3]s. issue_assigned.pull=@%[1]s heeft u toegewezen aan de pull request %[2]s in repository %[3]s.
issue_assigned.issue=@%[1]heeft u toegewezen aan issue %[2]s in repository %[3]s. issue_assigned.issue=@%[1]s heeft u toegewezen aan issue %[2]s in repository %[3]s.
issue.x_mentioned_you=<b>@%s</b> heeft u vermeld: issue.x_mentioned_you=<b>@%s</b> heeft u vermeld:
issue.action.force_push=<b>%[1]s</b> heeft een force-push uitgevoerd <b>%[2]s</b> van %[3]s naar %[4]s. issue.action.force_push=<b>%[1]s</b> heeft een force-push uitgevoerd <b>%[2]s</b> van %[3]s naar %[4]s.
@ -392,7 +392,7 @@ issue.action.push_1=<b>@%[1]s</b> heeft %[3]d commits gepusht naar %[2]s
issue.action.push_n=<b>@%[1]s</b> heeft %[3]d commits gepusht naar %[2]s issue.action.push_n=<b>@%[1]s</b> heeft %[3]d commits gepusht naar %[2]s
issue.action.close=<b>@%[1]s</b> sloot #%[2]d. issue.action.close=<b>@%[1]s</b> sloot #%[2]d.
issue.action.reopen=<b>@%[1]s</b> heropend #%[2]d. issue.action.reopen=<b>@%[1]s</b> heropend #%[2]d.
issue.action.merge=<b>@%[1]</b> heeft een merge uitgevoerd van #%[2]d naar %[3]s. issue.action.merge=<b>@%[1]s</b> heeft een merge uitgevoerd van #%[2]d naar %[3]s.
issue.action.approve=<b>@%[1]s</b> heeft deze pull request goedgekeurd. issue.action.approve=<b>@%[1]s</b> heeft deze pull request goedgekeurd.
issue.action.reject=<b>@%[1]s</b> vraagt om wijzigingen op deze pull request. issue.action.reject=<b>@%[1]s</b> vraagt om wijzigingen op deze pull request.
issue.action.review=<b>@%[1]s</b> heeft gereageerd op deze pull request. issue.action.review=<b>@%[1]s</b> heeft gereageerd op deze pull request.

View File

@ -2494,7 +2494,7 @@ monitor.queue.review=Przejrzyj konfigurację
monitor.queue.review_add=Przejrzyj/Dodaj procesy pracujące monitor.queue.review_add=Przejrzyj/Dodaj procesy pracujące
monitor.queue.settings.title=Ustawienia Puli monitor.queue.settings.title=Ustawienia Puli
monitor.queue.settings.maxnumberworkers=Maksymalna liczba procesów pracujących monitor.queue.settings.maxnumberworkers=Maksymalna liczba procesów pracujących
monitor.queue.settings.maxnumberworkers.placeholder=Obecnie %[1]v monitor.queue.settings.maxnumberworkers.placeholder=Obecnie %[1]d
monitor.queue.settings.maxnumberworkers.error=Maksymalna liczba procesów pracujących musi być liczbą monitor.queue.settings.maxnumberworkers.error=Maksymalna liczba procesów pracujących musi być liczbą
monitor.queue.settings.submit=Aktualizuj ustawienia monitor.queue.settings.submit=Aktualizuj ustawienia
monitor.queue.settings.changed=Zaktualizowano ustawienia monitor.queue.settings.changed=Zaktualizowano ustawienia

View File

@ -437,7 +437,7 @@ reset_password.text=Por favor clique no link a seguir para recuperar sua conta e
register_success=Cadastro bem-sucedido register_success=Cadastro bem-sucedido
issue_assigned.pull=@%[1]atribuiu a você o pull request %[2]s no repositório %[3]s. issue_assigned.pull=@%[1]s atribuiu a você o pull request %[2]s no repositório %[3]s.
issue_assigned.issue=@%[1]s atribuiu a você a issue %[2]s no repositório %[3]s. issue_assigned.issue=@%[1]s atribuiu a você a issue %[2]s no repositório %[3]s.
issue.x_mentioned_you=<b>@%s</b> mencionou você: issue.x_mentioned_you=<b>@%s</b> mencionou você:
@ -1535,7 +1535,7 @@ issues.due_date_form_remove=Remover
issues.due_date_not_writer=Você deve ter permissão de escrita no repositório para atualizar a data limite de uma issue. issues.due_date_not_writer=Você deve ter permissão de escrita no repositório para atualizar a data limite de uma issue.
issues.due_date_not_set=Data limite não informada. issues.due_date_not_set=Data limite não informada.
issues.due_date_added=adicionou a data limite %s %s issues.due_date_added=adicionou a data limite %s %s
issues.due_date_modified=modificou a data limite de %[2]para %[1]s %[3]s issues.due_date_modified=modificou a data limite de %[2]s para %[1]s %[3]s
issues.due_date_remove=removeu a data limite %s %s issues.due_date_remove=removeu a data limite %s %s
issues.due_date_overdue=Em atraso issues.due_date_overdue=Em atraso
issues.due_date_invalid=A data limite é inválida ou está fora do intervalo. Por favor, use o formato 'dd/mm/aaaa'. issues.due_date_invalid=A data limite é inválida ou está fora do intervalo. Por favor, use o formato 'dd/mm/aaaa'.
@ -2557,7 +2557,7 @@ dashboard.clean_unbind_oauth_success=Todas as conexões de OAuth não vinculadas
dashboard.task.started=Tarefa Iniciada: %[1]s dashboard.task.started=Tarefa Iniciada: %[1]s
dashboard.task.process=Tarefa: %[1]s dashboard.task.process=Tarefa: %[1]s
dashboard.task.cancelled=Tarefa: %[1]s cancelada: %[3]s dashboard.task.cancelled=Tarefa: %[1]s cancelada: %[3]s
dashboard.task.error=Erro na Tarefa: %[1]: %[3]s dashboard.task.error=Erro na Tarefa: %[1]s: %[3]s
dashboard.task.finished=Tarefa: %[1]s iniciada por %[2]s foi finalizada dashboard.task.finished=Tarefa: %[1]s iniciada por %[2]s foi finalizada
dashboard.task.unknown=Tarefa desconhecida: %[1]s dashboard.task.unknown=Tarefa desconhecida: %[1]s
dashboard.cron.started=Cron Iniciado: %[1]s dashboard.cron.started=Cron Iniciado: %[1]s

View File

@ -472,7 +472,7 @@ repo.transfer.body=Для того чтобы принять или отклон
repo.collaborator.added.subject=%s добавил(а) вас в %s repo.collaborator.added.subject=%s добавил(а) вас в %s
repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория: repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория:
team_invite.subject=%[1] приглашает вас присоединиться к организации %[2] team_invite.subject=%[1]s приглашает вас присоединиться к организации %[2]s
team_invite.text_1=%[1]s приглашает вас присоединиться к команде %[2]s в организации %[3]s. team_invite.text_1=%[1]s приглашает вас присоединиться к команде %[2]s в организации %[3]s.
team_invite.text_2=Перейдите по ссылке, чтобы присоединиться к команде: team_invite.text_2=Перейдите по ссылке, чтобы присоединиться к команде:
team_invite.text_3=Примечание: Это приглашение было направлено для %[1]s. Если вы не ожидали этого приглашения, можете проигнорировать это письмо. team_invite.text_3=Примечание: Это приглашение было направлено для %[1]s. Если вы не ожидали этого приглашения, можете проигнорировать это письмо.

View File

@ -353,17 +353,17 @@ issue.action.push_1=<b>@%[1]s</b> pushed%[3]d%[2]s කිරීමට කැප
issue.action.push_n=<b>@%[1]s</b> pushed%[3]d%[2]s දක්වා කැපේ issue.action.push_n=<b>@%[1]s</b> pushed%[3]d%[2]s දක්වා කැපේ
issue.action.close=<b>@%[1]s</b> වසා #%[2]d. issue.action.close=<b>@%[1]s</b> වසා #%[2]d.
issue.action.reopen=<b>@%[1]s</b> නැවත විවෘත කරන ලද #%[2]d. issue.action.reopen=<b>@%[1]s</b> නැවත විවෘත කරන ලද #%[2]d.
issue.action.merge=<b>@%[1]ගේ</b> ඒකාබද්ධ #%[2]ඈ into%[3]s. issue.action.merge=<b>@%[1]s ගේ</b> ඒකාබද්ධ #%[2]d ඈ into%[3]s.
issue.action.approve=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම අනුමත. issue.action.approve=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම අනුමත.
issue.action.reject=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා. issue.action.reject=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා.
issue.action.review=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම පිළිබඳව අදහස් දැක්වීය. issue.action.review=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම පිළිබඳව අදහස් දැක්වීය.
issue.action.review_dismissed=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම සඳහා%[2]s සිට පසුගිය සමාලෝචනය බැහැර. issue.action.review_dismissed=<b>@%[1]s</b> මෙම අදින්න ඉල්ලීම සඳහා%[2]s සිට පසුගිය සමාලෝචනය බැහැර.
issue.action.ready_for_review=<b>@%[1]s</b> සමාලෝචනය සඳහා සූදානම් මෙම අදින්න ඉල්ලීම සලකුණු. issue.action.ready_for_review=<b>@%[1]s</b> සමාලෝචනය සඳහා සූදානම් මෙම අදින්න ඉල්ලීම සලකුණු.
issue.action.new=<b>@%[1]ගේ</b> නිර්මාණය #%[2]ඈ. issue.action.new=<b>@%[1]s ගේ</b> නිර්මාණය #%[2]d ඈ.
issue.in_tree_path=%sදී: issue.in_tree_path=%sදී:
release.new.subject=%s %s නිදහස් release.new.subject=%s %s නිදහස්
release.new.text=<b>@%[1]හි</b> නිකුතුව%[2]හි[3]හි release.new.text=<b>@%[1]s හි</b> නිකුතුව%[2]s හි %[3]s හි
release.title=සිරැසිය: %s release.title=සිරැසිය: %s
release.note=සටහන: release.note=සටහන:
release.downloads=බාගැනීම්: release.downloads=බාගැනීම්:
@ -844,7 +844,7 @@ migrate.permission_denied_blocked=ඔබට අවසර නොලත් ධා
migrate.invalid_lfs_endpoint=මෙම LFS අවසන් ලක්ෂ්යය වලංගු නොවේ. migrate.invalid_lfs_endpoint=මෙම LFS අවසන් ලක්ෂ්යය වලංගු නොවේ.
migrate.failed=සංක්රමණය අසාර්ථකයි: %v migrate.failed=සංක්රමණය අසාර්ථකයි: %v
migrate.migrate_items_options=අමතර අයිතම සංක්රමණය කිරීම සඳහා ප්රවේශ ටෝකනය අවශ්ය වේ migrate.migrate_items_options=අමතර අයිතම සංක්රමණය කිරීම සඳහා ප්රවේශ ටෝකනය අවශ්ය වේ
migrated_from=<a href="%[1]s">%[2]සිට</a>දක්වා සංක්රමණය වී ඇත migrated_from=<a href="%[1]s">%[2]s සිට</a>දක්වා සංක්රමණය වී ඇත
migrated_from_fake=සංක්රමණය වූ ගෙම්%[1]s migrated_from_fake=සංක්රමණය වූ ගෙම්%[1]s
migrate.migrate=%sසිට සංක්රමණය migrate.migrate=%sසිට සංක්රමණය
migrate.migrating=<b>%s</b> සිට සංක්රමණය වීම... migrate.migrating=<b>%s</b> සිට සංක්රමණය වීම...
@ -1071,7 +1071,7 @@ issues.remove_ref_at=`ඉවත් කරන ලද යොමු <b>%s</b> %s`
issues.add_ref_at=`එකතු කරන ලද යොමු <b>%s</b> %s` issues.add_ref_at=`එකතු කරන ලද යොමු <b>%s</b> %s`
issues.delete_branch_at=`මකාදැමූ ශාඛාව <b>%s</b> %s` issues.delete_branch_at=`මකාදැමූ ශාඛාව <b>%s</b> %s`
issues.filter_label=ලේබලය issues.filter_label=ලේබලය
issues.filter_label_exclude=Labels` ඉවත් කිරීමට <code>alt</code> + <code>ක්ලික් කරන්න/ඇතුළු කරන්න</code> issues.filter_label_exclude=ලේබල බැහැර කිරීමට <code>alt</code> + <code>click/enter</code> භාවිත කරන්න
issues.filter_label_no_select=සියලු ලේබල issues.filter_label_no_select=සියලු ලේබල
issues.filter_label_select_no_label=ලේබලයක් නැත issues.filter_label_select_no_label=ලේබලයක් නැත
issues.filter_milestone=සන්ධිස්ථානය issues.filter_milestone=සන්ධිස්ථානය
@ -1127,7 +1127,7 @@ issues.reopen_comment_issue=අදහස් දක්වා විවෘත ක
issues.create_comment=අදහස issues.create_comment=අදහස
issues.closed_at=`මෙම ගැටළුව වසා <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`මෙම ගැටළුව වසා <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`මෙම ගැටළුව නැවත විවෘත කරන ලදි <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`මෙම ගැටළුව නැවත විවෘත කරන ලදි <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=<a href="%[3]s">මෙම නිකුතුව%[4]හි</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> issues.ref_issue_from=<a href="%[3]s">මෙම නිකුතුව %[4]s හි</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>
issues.ref_pull_from=<a href="%[3]s">මෙම අදින්න ඉල්ලීම%[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> issues.ref_pull_from=<a href="%[3]s">මෙම අදින්න ඉල්ලීම%[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>
issues.ref_closing_from=<a href="%[3]s">මෙම ගැටළුව වසා දමනු ඇත%[4]s මෙම ගැටළුව</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> issues.ref_closing_from=<a href="%[3]s">මෙම ගැටළුව වසා දමනු ඇත%[4]s මෙම ගැටළුව</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>
issues.ref_reopening_from=<a href="%[3]s">මෙම ගැටළුව නැවත විවෘත කරනු ඇත%[4]s මෙම ගැටළුව</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> issues.ref_reopening_from=<a href="%[3]s">මෙම ගැටළුව නැවත විවෘත කරනු ඇත%[4]s මෙම ගැටළුව</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>
@ -1214,7 +1214,7 @@ issues.error_modifying_due_date=නියමිත දිනය වෙනස්
issues.error_removing_due_date=නියමිත දිනය ඉවත් කිරීමට අපොහොසත් විය. issues.error_removing_due_date=නියමිත දිනය ඉවත් කිරීමට අපොහොසත් විය.
issues.push_commit_1=එකතු %d කැප %s issues.push_commit_1=එකතු %d කැප %s
issues.push_commits_n=එකතු %d විවරයන් %s issues.push_commits_n=එකතු %d විවරයන් %s
issues.force_push_codes=`බලය-pushed%[1]s සිට <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> <a class="ui sha" href="%[5]s"><code>%[4]ගේ</code></a> %[6]s` issues.force_push_codes=`බලය-pushed%[1]s සිට <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> <a class="ui sha" href="%[5]s"><code>%[4]s ගේ</code></a> %[6]s`
issues.force_push_compare=සසඳන්න issues.force_push_compare=සසඳන්න
issues.due_date_form=Yyy-mm-dd issues.due_date_form=Yyy-mm-dd
issues.due_date_form_add=නියමිත දිනය එකතු කරන්න issues.due_date_form_add=නියමිත දිනය එකතු කරන්න
@ -2039,7 +2039,7 @@ members.member_role=සාමාජික කාර්යභාරය:
members.owner=හිමිකරු members.owner=හිමිකරු
members.member=සාමාජික members.member=සාමාජික
members.remove=ඉවත් කරන්න members.remove=ඉවත් කරන්න
members.remove.detail=%[1]ගේ සිට%[2]ගේ ඉවත්? members.remove.detail=%[1]s ගේ සිට%[2]s ගේ ඉවත්?
members.leave=හැරයන්න members.leave=හැරයන්න
members.leave.detail=%s හැරයනවාද? members.leave.detail=%s හැරයනවාද?
members.invite_desc=%sවෙත නව සාමාජිකයෙකු එක් කරන්න: members.invite_desc=%sවෙත නව සාමාජිකයෙකු එක් කරන්න:
@ -2111,7 +2111,7 @@ dashboard.clean_unbind_oauth=පිරිසිදු නොබැඳි OAUTH
dashboard.clean_unbind_oauth_success=සියලුම නොබැඳි OAUTH සම්බන්ධතා මකා දමා ඇත. dashboard.clean_unbind_oauth_success=සියලුම නොබැඳි OAUTH සම්බන්ධතා මකා දමා ඇත.
dashboard.task.started=ආරම්භ කාර්යය:%[1]s dashboard.task.started=ආරම්භ කාර්යය:%[1]s
dashboard.task.process=කාර්යය:%[1]s dashboard.task.process=කාර්යය:%[1]s
dashboard.task.cancelled=කාර්යය:%[1]ගේ අවලංගු:%[3]ගේ dashboard.task.cancelled=කාර්යය: %[1]s ගේ අවලංගු: %[3]s ගේ
dashboard.task.error=කාර්යයයේ දෝෂය:%[1]s:%[3]s dashboard.task.error=කාර්යයයේ දෝෂය:%[1]s:%[3]s
dashboard.task.finished=කාර්යය:%[1]s[2]s විසින් ආරම්භ කර ඇත dashboard.task.finished=කාර්යය:%[1]s[2]s විසින් ආරම්භ කර ඇත
dashboard.task.unknown=නොදන්නා කාර්යය:%[1]s dashboard.task.unknown=නොදන්නා කාර්යය:%[1]s
@ -2551,16 +2551,16 @@ comment_issue=`නිකුතුව පිළිබඳ අදහස් <a href=
comment_pull=`අදින්න ඉල්ලීම මත අදහස් <a href="%[1]s">%[3]s #%[2]s</a>` comment_pull=`අදින්න ඉල්ලීම මත අදහස් <a href="%[1]s">%[3]s #%[2]s</a>`
merge_pull_request=`ඒකාබද්ධ අදින්න ඉල්ලීම <a href="%[1]s">%[3]s #%[2]s</a>` merge_pull_request=`ඒකාබද්ධ අදින්න ඉල්ලීම <a href="%[1]s">%[3]s #%[2]s</a>`
transfer_repo=මාරු කරන ලද ගබඩාව <code>%s</code> සිට <a href="%s">%s</a> transfer_repo=මාරු කරන ලද ගබඩාව <code>%s</code> සිට <a href="%s">%s</a>
push_tag=තල්ලු ටැගය <a href="%[2]s">%[3]ගේ</a> <a href="%[1]s">%[4]ගේ</a> push_tag=තල්ලු ටැගය <a href="%[2]s">%[3]s ගේ</a> <a href="%[1]s">%[4]s ගේ</a>
delete_tag=මකාදැමුවා ටැගය%[2]සිට <a href="%[1]s">%[3]s</a> delete_tag=මකාදැමුවා ටැගය%[2]s සිට <a href="%[1]s">%[3]s</a>
delete_branch=මකාදැමූ ශාඛාව%[2]සිට <a href="%[1]s">%[3]s</a> delete_branch=මකාදැමූ ශාඛාව %[2]s සිට <a href="%[1]s">%[3]s</a>
compare_branch=සසඳන්න compare_branch=සසඳන්න
compare_commits=%d විවරයන් සසඳා බලන්න compare_commits=%d විවරයන් සසඳා බලන්න
compare_commits_general=විවරයන් සසඳා බලන්න compare_commits_general=විවරයන් සසඳා බලන්න
mirror_sync_push=සමමුහුර්ත <a href="%[2]s">%[3]s</a> ට <a href="%[1]s">%[4]s</a> කැඩපත සිට mirror_sync_push=සමමුහුර්ත <a href="%[2]s">%[3]s</a> ට <a href="%[1]s">%[4]s</a> කැඩපත සිට
mirror_sync_create=සමමුහුර්ත නව යොමු <a href="%[2]s">%[3]s</a> සිට <a href="%[1]s">%[4]s</a> කැඩපත සිට mirror_sync_create=සමමුහුර්ත නව යොමු <a href="%[2]s">%[3]s</a> සිට <a href="%[1]s">%[4]s</a> කැඩපත සිට
mirror_sync_delete=සමමුහුර්ත සහ මකාදැමූ යොමු <code>%[2]s</code> හි <a href="%[1]s">%[3]s</a> කැඩපතෙන් mirror_sync_delete=සමමුහුර්ත සහ මකාදැමූ යොමු <code>%[2]s</code> හි <a href="%[1]s">%[3]s</a> කැඩපතෙන්
approve_pull_request=`අනුමත <a href="%[1]s">%[3]s #%[2]ගේ</a>` approve_pull_request=`අනුමත <a href="%[1]s">%[3]s #%[2]s ගේ</a>`
reject_pull_request=<a href="%[1]s">%[3]s #%[2]s</a>සඳහා යෝජිත වෙනස්කම් reject_pull_request=<a href="%[1]s">%[3]s #%[2]s</a>සඳහා යෝජිත වෙනස්කම්
publish_release=`නිදහස් <a href="%[2]s"> "%[4]s" </a> හි <a href="%[1]s">%[3]s</a>` publish_release=`නිදහස් <a href="%[2]s"> "%[4]s" </a> හි <a href="%[1]s">%[3]s</a>`
review_dismissed_reason=හේතුව: review_dismissed_reason=හේතුව:

View File

@ -382,7 +382,7 @@ reset_password.text=Pre obnovenie vašeho účtu kliknite, prosím, na nasledovn
register_success=Registrácia prebehla úspešne register_success=Registrácia prebehla úspešne
issue_assigned.pull=@%[1]s vám pridelil pull request %[2] v repozitári %[3]s. issue_assigned.pull=@%[1]s vám pridelil pull request %[2]s repozitári %[3]s.
issue_assigned.issue=@%[1]s vám pridelil úkol %[2]s v repozitári %[3]s. issue_assigned.issue=@%[1]s vám pridelil úkol %[2]s v repozitári %[3]s.
issue.x_mentioned_you=<b>@%s</b> vás zmienil: issue.x_mentioned_you=<b>@%s</b> vás zmienil:
@ -397,7 +397,7 @@ issue.action.reject=<b>@%[1]s</b> požadoval zmeny v tomto pull requeste.
issue.action.review=<b>@%[1]s</b> okomentoval tento pull request. issue.action.review=<b>@%[1]s</b> okomentoval tento pull request.
issue.action.review_dismissed=<b>@%[1]s</b> zamietol poslednú recenziu od %[2]s pre tento pull request. issue.action.review_dismissed=<b>@%[1]s</b> zamietol poslednú recenziu od %[2]s pre tento pull request.
issue.action.ready_for_review=<b>@%[1]s</b> označil tento pull request ako pripravený na revíziu. issue.action.ready_for_review=<b>@%[1]s</b> označil tento pull request ako pripravený na revíziu.
issue.action.new=<b>@%[1]</b> vytvoril/a #%[2]d. issue.action.new=<b>@%[1]s</b> vytvoril/a #%[2]d.
issue.in_tree_path=V %s: issue.in_tree_path=V %s:
release.new.subject=%s v %s vydané release.new.subject=%s v %s vydané

View File

@ -461,10 +461,10 @@ repo.transfer.body=Kabul veya reddetmek için %s ziyaret edin veya görmezden ge
repo.collaborator.added.subject=%s sizi %s ekledi repo.collaborator.added.subject=%s sizi %s ekledi
repo.collaborator.added.text=Bu depo için katkıcı olarak eklendiniz: repo.collaborator.added.text=Bu depo için katkıcı olarak eklendiniz:
team_invite.subject=%[1] sizi %[2] organizasyonuna katılmaya davet etti team_invite.subject=%[1]s sizi %[2]s organizasyonuna katılmaya davet etti
team_invite.text_1=%[1] sizi %[3] organizasyonundaki %[2] takımına katılmaya davet etti. team_invite.text_1=%[1]s sizi %[3]s organizasyonundaki %[2]s takımına katılmaya davet etti.
team_invite.text_2=Takıma katılmak lütfen aşağıdaki bağlantıya tıklayın: team_invite.text_2=Takıma katılmak lütfen aşağıdaki bağlantıya tıklayın:
team_invite.text_3=Not: Bu davet %[1] içindi. Bu daveti beklemiyorsanız, e-postayı yok sayabilirsiniz. team_invite.text_3=Not: Bu davet %[1]s içindi. Bu daveti beklemiyorsanız, e-postayı yok sayabilirsiniz.
[modal] [modal]
yes=Evet yes=Evet

View File

@ -1182,10 +1182,10 @@ issues.create_comment=Коментар
issues.closed_at=`закрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`закрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`повторно відкрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`повторно відкрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`згадано цю задачу в коміті <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`згадано цю задачу в коміті <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">посилання на цю задачу %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from=`<a href="%[3]s">посилання на цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">послався на цей запит злиття %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from=`<a href="%[3]s">послався на цей запит злиття %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">згадав запит на злиття %[4]с, які закриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from=`<a href="%[3]s">згадав запит на злиття %[4]s, які закриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">згадав запит на злиття %[4]с, які повторно відкриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from=`<a href="%[3]s">згадав запит на злиття %[4]s, які повторно відкриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closed_from=`<a href="%[3]s">закрив цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">закрив цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">повторно відкрито цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s">повторно відкрито цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`із %[1]s` issues.ref_from=`із %[1]s`
@ -1269,7 +1269,7 @@ issues.error_modifying_due_date=Не вдалося змінити дату за
issues.error_removing_due_date=Не вдалося видалити дату завершення. issues.error_removing_due_date=Не вдалося видалити дату завершення.
issues.push_commit_1=додав %d коміт %s issues.push_commit_1=додав %d коміт %s
issues.push_commits_n=додав %d коміти(-ів) %s issues.push_commits_n=додав %d коміти(-ів) %s
issues.force_push_codes=`примусово залито %[1]s з <a class="ui sha" href="%[3]s"><code>%[2]</code></a> до <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s` issues.force_push_codes=`примусово залито %[1]s з <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> до <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.force_push_compare=Порівняти issues.force_push_compare=Порівняти
issues.due_date_form=рррр-мм-дд issues.due_date_form=рррр-мм-дд
issues.due_date_form_add=Додати дату завершення issues.due_date_form_add=Додати дату завершення
@ -2613,7 +2613,7 @@ comment_issue=`прокоментував задачу <a href="%[1]s">%[3]s#%[2
comment_pull=`прокоментував запит злиття <a href="%[1]s">%[3]s#%[2]s</a>` comment_pull=`прокоментував запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
merge_pull_request=`прийняв запит злиття <a href="%[1]s">%[3]s#%[2]s</a>` merge_pull_request=`прийняв запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
transfer_repo=перенесено репозиторій <code>%s</code> у <a href="%s">%s</a> transfer_repo=перенесено репозиторій <code>%s</code> у <a href="%s">%s</a>
push_tag=створив мітку <a href="%[2]s">%[3]</a> в <a href="%[1]s">%[4]s</a> push_tag=створив мітку <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
delete_tag=видалено мітку %[2]s з <a href="%[1]s">%[3]s</a> delete_tag=видалено мітку %[2]s з <a href="%[1]s">%[3]s</a>
delete_branch=видалено гілку %[2]s з <a href="%[1]s">%[3]s</a> delete_branch=видалено гілку %[2]s з <a href="%[1]s">%[3]s</a>
compare_branch=Порівняти compare_branch=Порівняти
@ -2622,14 +2622,14 @@ compare_commits_general=Порівняти коміти
mirror_sync_push=синхронізував коміти в <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> із дзеркала mirror_sync_push=синхронізував коміти в <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> із дзеркала
mirror_sync_create=синхронізував нове посилання <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> із дзеркала mirror_sync_create=синхронізував нове посилання <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> із дзеркала
mirror_sync_delete=синхронізовано й видалено посилання <code>%[2]s</code> на <a href="%[1]s">%[3]s</a> із дзеркала mirror_sync_delete=синхронізовано й видалено посилання <code>%[2]s</code> на <a href="%[1]s">%[3]s</a> із дзеркала
approve_pull_request=`схвалив <a href="%[1]s">%[3]s#%[2]</a>` approve_pull_request=`схвалив <a href="%[1]s">%[3]s#%[2]s</a>`
reject_pull_request=`запропонував зміни до <a href="%[1]s">%[3]s#%[2]s</a>` reject_pull_request=`запропонував зміни до <a href="%[1]s">%[3]s#%[2]s</a>`
publish_release=`опублікував випуск <a href="%[2]s"> "%[4]s" </a> з <a href="%[1]s">%[3]s</a>` publish_release=`опублікував випуск <a href="%[2]s"> "%[4]s" </a> з <a href="%[1]s">%[3]s</a>`
review_dismissed=`відхилив відгук від <b>%[4]</b> для <a href="%[1]s">%[3]s#%[2]s</a>` review_dismissed=`відхилив відгук від <b>%[4]s</b> для <a href="%[1]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Причина: review_dismissed_reason=Причина:
create_branch=створив гілку <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> create_branch=створив гілку <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
starred_repo=додав <a href="%[1]s">%[2]s</a> у обране starred_repo=додав <a href="%[1]s">%[2]s</a> у обране
watched_repo=почав слідкувати за <a href="%[1]s">%[2]</a> watched_repo=почав слідкувати за <a href="%[1]s">%[2]s</a>
[tool] [tool]
now=зараз now=зараз

View File

@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetFileStreamByPackageVersion( s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx, ctx,
pv, pv,
&packages_service.PackageFileInfo{ &packages_service.PackageFileInfo{
@ -84,12 +84,8 @@ func GetRepositoryFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
func UploadPackageFile(ctx *context.Context) { func UploadPackageFile(ctx *context.Context) {
@ -200,7 +196,7 @@ func DownloadPackageFile(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil { if err != nil {
if errors.Is(err, util.ErrNotExist) { if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err) apiError(ctx, http.StatusNotFound, err)
@ -209,12 +205,8 @@ func DownloadPackageFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
func DeletePackageFile(ctx *context.Context) { func DeletePackageFile(ctx *context.Context) {

View File

@ -165,7 +165,7 @@ func ListOwners(ctx *context.Context) {
// DownloadPackageFile serves the content of a package // DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) { func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -185,12 +185,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// https://doc.rust-lang.org/cargo/reference/registries.html#publish // https://doc.rust-lang.org/cargo/reference/registries.html#publish

View File

@ -341,17 +341,13 @@ func DownloadPackage(ctx *context.Context) {
pf := pd.Files[0].File pf := pd.Files[0].File
s, _, err := packages_service.GetPackageFileStream(ctx, pf) s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// https://github.com/chef/chef/blob/main/knife/lib/chef/knife/supermarket_unshare.rb // https://github.com/chef/chef/blob/main/knife/lib/chef/knife/supermarket_unshare.rb

View File

@ -162,7 +162,7 @@ func PackageMetadata(ctx *context.Context) {
// DownloadPackageFile serves the content of a package // DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) { func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -182,12 +182,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackage creates a new package // UploadPackage creates a new package

View File

@ -453,7 +453,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
return return
} }
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -474,12 +474,8 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// DeleteRecipeV1 deletes the requested recipe(s) // DeleteRecipeV1 deletes the requested recipe(s)

View File

@ -292,15 +292,11 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pfs[0] pf := pfs[0]
s, _, err := packages_service.GetPackageFileStream(ctx, pf) s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }

View File

@ -482,22 +482,7 @@ func GetBlob(ctx *context.Context) {
return return
} }
s, _, err := packages_service.GetPackageFileStream(ctx, blob.File) serveBlob(ctx, blob)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()
setResponseHeaders(ctx.Resp, &containerHeaders{
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
ContentType: blob.Properties.GetByName(container_module.PropertyMediaType),
ContentLength: blob.Blob.Size,
Status: http.StatusOK,
})
if _, err := io.Copy(ctx.Resp, s); err != nil {
log.Error("Error whilst copying content to response: %v", err)
}
} }
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs
@ -636,22 +621,7 @@ func GetManifest(ctx *context.Context) {
return return
} }
s, _, err := packages_service.GetPackageFileStream(ctx, manifest.File) serveBlob(ctx, manifest)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()
setResponseHeaders(ctx.Resp, &containerHeaders{
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
ContentLength: manifest.Blob.Size,
Status: http.StatusOK,
})
if _, err := io.Copy(ctx.Resp, s); err != nil {
log.Error("Error whilst copying content to response: %v", err)
}
} }
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-tags // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-tags
@ -686,6 +656,36 @@ func DeleteManifest(ctx *context.Context) {
}) })
} }
func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) {
s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
headers := &containerHeaders{
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
ContentLength: pfd.Blob.Size,
Status: http.StatusOK,
}
if u != nil {
headers.Status = http.StatusTemporaryRedirect
headers.Location = u.String()
setResponseHeaders(ctx.Resp, headers)
return
}
defer s.Close()
setResponseHeaders(ctx.Resp, headers)
if _, err := io.Copy(ctx.Resp, s); err != nil {
log.Error("Error whilst copying content to response: %v", err)
}
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery
func GetTagList(ctx *context.Context) { func GetTagList(ctx *context.Context) {
image := ctx.Params("image") image := ctx.Params("image")

View File

@ -249,7 +249,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
return return
} }
s, _, err := packages_service.GetPackageFileStream(ctx, pf) s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil { if err != nil {
if errors.Is(err, util.ErrNotExist) { if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err) apiError(ctx, http.StatusNotFound, err)
@ -258,10 +258,6 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }

View File

@ -59,7 +59,7 @@ func GetRepositoryFile(ctx *context.Context) {
key += "|" + component + "|" + architecture key += "|" + component + "|" + architecture
} }
s, pf, err := packages_service.GetFileStreamByPackageVersion( s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx, ctx,
pv, pv,
&packages_service.PackageFileInfo{ &packages_service.PackageFileInfo{
@ -75,12 +75,8 @@ func GetRepositoryFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// https://wiki.debian.org/DebianRepository/Format#indices_acquisition_via_hashsums_.28by-hash.29 // https://wiki.debian.org/DebianRepository/Format#indices_acquisition_via_hashsums_.28by-hash.29
@ -110,7 +106,7 @@ func GetRepositoryFileByHash(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil { if err != nil {
if errors.Is(err, util.ErrNotExist) { if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err) apiError(ctx, http.StatusNotFound, err)
@ -119,12 +115,8 @@ func GetRepositoryFileByHash(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
func UploadPackageFile(ctx *context.Context) { func UploadPackageFile(ctx *context.Context) {
@ -217,7 +209,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.Params("name") name := ctx.Params("name")
version := ctx.Params("version") version := ctx.Params("version")
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -238,9 +230,8 @@ func DownloadPackageFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf, &context.ServeHeaderOptions{
ContentType: "application/vnd.debian.binary-package", ContentType: "application/vnd.debian.binary-package",
Filename: pf.Name, Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(), LastModified: pf.CreatedUnix.AsLocalTime(),

View File

@ -30,7 +30,7 @@ func apiError(ctx *context.Context, status int, obj interface{}) {
// DownloadPackageFile serves the specific generic package. // DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) { func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -50,12 +50,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackage uploads the specific generic package. // UploadPackage uploads the specific generic package.

View File

@ -105,7 +105,7 @@ func DownloadPackageFile(ctx *context.Context) {
return return
} }
s, _, err := packages_service.GetPackageFileStream(ctx, pfs[0]) s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil { if err != nil {
if errors.Is(err, util.ErrNotExist) { if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err) apiError(ctx, http.StatusNotFound, err)
@ -114,12 +114,8 @@ func DownloadPackageFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pfs[0])
Filename: pfs[0].Name,
LastModified: pfs[0].CreatedUnix.AsLocalTime(),
})
} }
func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (*packages_model.PackageVersion, error) { func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (*packages_model.PackageVersion, error) {

View File

@ -121,7 +121,7 @@ func DownloadPackageFile(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetFileStreamByPackageVersion( s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx, ctx,
pvs[0], pvs[0],
&packages_service.PackageFileInfo{ &packages_service.PackageFileInfo{
@ -136,12 +136,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackage creates a new package // UploadPackage creates a new package

View File

@ -5,8 +5,11 @@ package helper
import ( import (
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -35,3 +38,26 @@ func LogAndProcessError(ctx *context.Context, status int, obj interface{}, cb fu
cb(message) cb(message)
} }
} }
// Serves the content of the package file
// If the url is set it will redirect the request, otherwise the content is copied to the response.
func ServePackageFile(ctx *context.Context, s io.ReadSeekCloser, u *url.URL, pf *packages_model.PackageFile, forceOpts ...*context.ServeHeaderOptions) {
if u != nil {
ctx.Redirect(u.String())
return
}
defer s.Close()
var opts *context.ServeHeaderOptions
if len(forceOpts) > 0 {
opts = forceOpts[0]
} else {
opts = &context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
}
}
ctx.ServeContent(s, opts)
}

View File

@ -210,21 +210,15 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool
return return
} }
s, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(pb.HashSHA256)) s, u, _, err := packages_service.GetPackageBlobStream(ctx, pf, pb)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
} return
defer s.Close()
if pf.IsLead {
if err := packages_model.IncrementDownloadCounter(ctx, pv.ID); err != nil {
log.Error("Error incrementing download counter: %v", err)
}
} }
opts.Filename = pf.Name opts.Filename = pf.Name
ctx.ServeContent(s, opts) helper.ServePackageFile(ctx, s, u, pf, opts)
} }
// UploadPackageFile adds a file to the package. If the package does not exist, it gets created. // UploadPackageFile adds a file to the package. If the package does not exist, it gets created.

View File

@ -83,7 +83,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.Params("version") packageVersion := ctx.Params("version")
filename := ctx.Params("filename") filename := ctx.Params("filename")
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -103,12 +103,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// DownloadPackageFileByName finds the version and serves the contents of a package // DownloadPackageFileByName finds the version and serves the contents of a package
@ -134,7 +130,7 @@ func DownloadPackageFileByName(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetFileStreamByPackageVersion( s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx, ctx,
pvs[0], pvs[0],
&packages_service.PackageFileInfo{ &packages_service.PackageFileInfo{
@ -149,12 +145,8 @@ func DownloadPackageFileByName(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackage creates a new package // UploadPackage creates a new package

View File

@ -362,7 +362,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.Params("version") packageVersion := ctx.Params("version")
filename := ctx.Params("filename") filename := ctx.Params("filename")
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -382,12 +382,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackage creates a new package with the metadata contained in the uploaded nupgk file // UploadPackage creates a new package with the metadata contained in the uploaded nupgk file
@ -600,7 +596,7 @@ func DownloadSymbolFile(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil { if err != nil {
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist { if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
apiError(ctx, http.StatusNotFound, err) apiError(ctx, http.StatusNotFound, err)
@ -609,12 +605,8 @@ func DownloadSymbolFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// DeletePackage hard deletes the package // DeletePackage hard deletes the package

View File

@ -273,15 +273,11 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pd.Files[0].File pf := pd.Files[0].File
s, _, err := packages_service.GetPackageFileStream(ctx, pf) s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }

View File

@ -80,7 +80,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.Params("version") packageVersion := ctx.Params("version")
filename := ctx.Params("filename") filename := ctx.Params("filename")
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -100,12 +100,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackageFile adds a file to the package. If the package does not exist, it gets created. // UploadPackageFile adds a file to the package. If the package does not exist, it gets created.

View File

@ -65,7 +65,7 @@ func GetRepositoryFile(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetFileStreamByPackageVersion( s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx, ctx,
pv, pv,
&packages_service.PackageFileInfo{ &packages_service.PackageFileInfo{
@ -80,12 +80,8 @@ func GetRepositoryFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
func UploadPackageFile(ctx *context.Context) { func UploadPackageFile(ctx *context.Context) {
@ -173,7 +169,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.Params("name") name := ctx.Params("name")
version := ctx.Params("version") version := ctx.Params("version")
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -193,13 +189,8 @@ func DownloadPackageFile(ctx *context.Context) {
} }
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
ContentType: "application/x-rpm",
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
func DeletePackageFile(webctx *context.Context) { func DeletePackageFile(webctx *context.Context) {

View File

@ -175,7 +175,7 @@ func DownloadPackageFile(ctx *context.Context) {
return return
} }
s, pf, err := packages_service.GetFileStreamByPackageVersion( s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx, ctx,
pvs[0], pvs[0],
&packages_service.PackageFileInfo{ &packages_service.PackageFileInfo{
@ -190,12 +190,8 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }
// UploadPackageFile adds a file to the package. If the package does not exist, it gets created. // UploadPackageFile adds a file to the package. If the package does not exist, it gets created.

View File

@ -397,18 +397,17 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pd.Files[0].File pf := pd.Files[0].File
s, _, err := packages_service.GetPackageFileStream(ctx, pf) s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
setResponseHeaders(ctx.Resp, &headers{ setResponseHeaders(ctx.Resp, &headers{
Digest: pd.Files[0].Blob.HashSHA256, Digest: pd.Files[0].Blob.HashSHA256,
}) })
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf, &context.ServeHeaderOptions{
Filename: pf.Name, Filename: pf.Name,
ContentType: "application/zip", ContentType: "application/zip",
LastModified: pf.CreatedUnix.AsLocalTime(), LastModified: pf.CreatedUnix.AsLocalTime(),

View File

@ -216,7 +216,7 @@ func UploadPackageFile(ctx *context.Context) {
} }
func DownloadPackageFile(ctx *context.Context) { func DownloadPackageFile(ctx *context.Context) {
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx, ctx,
&packages_service.PackageInfo{ &packages_service.PackageInfo{
Owner: ctx.Package.Owner, Owner: ctx.Package.Owner,
@ -236,10 +236,6 @@ func DownloadPackageFile(ctx *context.Context) {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }

View File

@ -201,7 +201,7 @@ func SignInPost(ctx *context.Context) {
u, source, err := auth_service.UserSignIn(form.UserName, form.Password) u, source, err := auth_service.UserSignIn(form.UserName, form.Password)
if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailAddressNotExist(err) { if errors.Is(err, util.ErrNotExist) || errors.Is(err, util.ErrInvalidArgument) {
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form) ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
} else if user_model.IsErrEmailAlreadyUsed(err) { } else if user_model.IsErrEmailAlreadyUsed(err) {

View File

@ -13,7 +13,9 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
auth_service "code.gitea.io/gitea/services/auth" auth_service "code.gitea.io/gitea/services/auth"
"code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/oauth2"
@ -81,6 +83,32 @@ func LinkAccount(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplLinkAccount) ctx.HTML(http.StatusOK, tplLinkAccount)
} }
func handleSignInError(ctx *context.Context, userName string, ptrForm any, tmpl base.TplName, invoker string, err error) {
if errors.Is(err, util.ErrNotExist) {
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tmpl, ptrForm)
} else if errors.Is(err, util.ErrInvalidArgument) {
ctx.Data["user_exists"] = true
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tmpl, ptrForm)
} else if user_model.IsErrUserProhibitLogin(err) {
ctx.Data["user_exists"] = true
log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err)
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
} else if user_model.IsErrUserInactive(err) {
ctx.Data["user_exists"] = true
if setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
ctx.HTML(http.StatusOK, TplActivate)
} else {
log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err)
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
}
} else {
ctx.ServerError(invoker, err)
}
}
// LinkAccountPostSignIn handle the coupling of external account with another account using signIn // LinkAccountPostSignIn handle the coupling of external account with another account using signIn
func LinkAccountPostSignIn(ctx *context.Context) { func LinkAccountPostSignIn(ctx *context.Context) {
signInForm := web.GetForm(ctx).(*forms.SignInForm) signInForm := web.GetForm(ctx).(*forms.SignInForm)
@ -116,12 +144,7 @@ func LinkAccountPostSignIn(ctx *context.Context) {
u, _, err := auth_service.UserSignIn(signInForm.UserName, signInForm.Password) u, _, err := auth_service.UserSignIn(signInForm.UserName, signInForm.Password)
if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { handleSignInError(ctx, signInForm.UserName, &signInForm, tplLinkAccount, "UserLinkAccount", err)
ctx.Data["user_exists"] = true
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplLinkAccount, &signInForm)
} else {
ctx.ServerError("UserLinkAccount", err)
}
return return
} }

View File

@ -282,11 +282,7 @@ func ConnectOpenIDPost(ctx *context.Context) {
u, _, err := auth.UserSignIn(form.UserName, form.Password) u, _, err := auth.UserSignIn(form.UserName, form.Password)
if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { handleSignInError(ctx, form.UserName, &form, tplConnectOID, "ConnectOpenIDPost", err)
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form)
} else {
ctx.ServerError("ConnectOpenIDPost", err)
}
return return
} }

View File

@ -34,7 +34,7 @@ func List(ctx *context.Context) {
func FetchActionTest(ctx *context.Context) { func FetchActionTest(ctx *context.Context) {
_ = ctx.Req.ParseForm() _ = ctx.Req.ParseForm()
ctx.Flash.Info(ctx.Req.Method + " " + ctx.Req.RequestURI + "<br>" + ctx.Flash.Info("fetch-action: " + ctx.Req.Method + " " + ctx.Req.RequestURI + "<br>" +
"Form: " + ctx.Req.Form.Encode() + "<br>" + "Form: " + ctx.Req.Form.Encode() + "<br>" +
"PostForm: " + ctx.Req.PostForm.Encode(), "PostForm: " + ctx.Req.PostForm.Encode(),
) )
@ -52,5 +52,15 @@ func Tmpl(ctx *context.Context) {
ctx.Data["TimePast1y"] = now.Add(-1 * 366 * 86400 * time.Second) ctx.Data["TimePast1y"] = now.Add(-1 * 366 * 86400 * time.Second)
ctx.Data["TimeFuture1y"] = now.Add(1 * 366 * 86400 * time.Second) ctx.Data["TimeFuture1y"] = now.Add(1 * 366 * 86400 * time.Second)
if ctx.Req.Method == "POST" {
_ = ctx.Req.ParseForm()
ctx.Flash.Info("form: "+ctx.Req.Method+" "+ctx.Req.RequestURI+"<br>"+
"Form: "+ctx.Req.Form.Encode()+"<br>"+
"PostForm: "+ctx.Req.PostForm.Encode(),
true,
)
time.Sleep(2 * time.Second)
}
ctx.HTML(http.StatusOK, base.TplName("devtest"+path.Clean("/"+ctx.Params("sub")))) ctx.HTML(http.StatusOK, base.TplName("devtest"+path.Clean("/"+ctx.Params("sub"))))
} }

View File

@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
packages_helper "code.gitea.io/gitea/routers/api/packages/helper"
shared_user "code.gitea.io/gitea/routers/web/shared/user" shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/forms"
packages_service "code.gitea.io/gitea/services/packages" packages_service "code.gitea.io/gitea/services/packages"
@ -443,18 +444,11 @@ func DownloadPackageFile(ctx *context.Context) {
return return
} }
s, _, err := packages_service.GetPackageFileStream( s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
ctx,
pf,
)
if err != nil { if err != nil {
ctx.ServerError("GetPackageFileStream", err) ctx.ServerError("GetPackageFileStream", err)
return return
} }
defer s.Close()
ctx.ServeContent(s, &context.ServeHeaderOptions{ packages_helper.ServePackageFile(ctx, s, u, pf)
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
} }

View File

@ -4,19 +4,54 @@
package db package db
import ( import (
"fmt"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
) )
// ErrUserPasswordNotSet represents a "ErrUserPasswordNotSet" kind of error.
type ErrUserPasswordNotSet struct {
UID int64
Name string
}
func (err ErrUserPasswordNotSet) Error() string {
return fmt.Sprintf("user's password isn't set [uid: %d, name: %s]", err.UID, err.Name)
}
// Unwrap unwraps this error as a ErrInvalidArgument error
func (err ErrUserPasswordNotSet) Unwrap() error {
return util.ErrInvalidArgument
}
// ErrUserPasswordInvalid represents a "ErrUserPasswordInvalid" kind of error.
type ErrUserPasswordInvalid struct {
UID int64
Name string
}
func (err ErrUserPasswordInvalid) Error() string {
return fmt.Sprintf("user's password is invalid [uid: %d, name: %s]", err.UID, err.Name)
}
// Unwrap unwraps this error as a ErrInvalidArgument error
func (err ErrUserPasswordInvalid) Unwrap() error {
return util.ErrInvalidArgument
}
// Authenticate authenticates the provided user against the DB // Authenticate authenticates the provided user against the DB
func Authenticate(user *user_model.User, login, password string) (*user_model.User, error) { func Authenticate(user *user_model.User, login, password string) (*user_model.User, error) {
if user == nil { if user == nil {
return nil, user_model.ErrUserNotExist{Name: login} return nil, user_model.ErrUserNotExist{Name: login}
} }
if !user.IsPasswordSet() || !user.ValidatePassword(password) { if !user.IsPasswordSet() {
return nil, user_model.ErrUserNotExist{UID: user.ID, Name: user.Name} return nil, ErrUserPasswordNotSet{UID: user.ID, Name: user.Name}
} else if !user.ValidatePassword(password) {
return nil, ErrUserPasswordInvalid{UID: user.ID, Name: user.Name}
} }
// Update password hash if server password hash algorithm have changed // Update password hash if server password hash algorithm have changed

View File

@ -9,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net/url"
"strings" "strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -20,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
packages_module "code.gitea.io/gitea/modules/packages" packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
@ -562,70 +564,62 @@ func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) erro
} }
// GetFileStreamByPackageNameAndVersion returns the content of the specific package file // GetFileStreamByPackageNameAndVersion returns the content of the specific package file
func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) { func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey) log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey)
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version) pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
if err != nil { if err != nil {
if err == packages_model.ErrPackageNotExist { if err == packages_model.ErrPackageNotExist {
return nil, nil, err return nil, nil, nil, err
} }
log.Error("Error getting package: %v", err) log.Error("Error getting package: %v", err)
return nil, nil, err return nil, nil, nil, err
} }
return GetFileStreamByPackageVersion(ctx, pv, pfi) return GetFileStreamByPackageVersion(ctx, pv, pfi)
} }
// GetFileStreamByPackageVersionAndFileID returns the content of the specific package file
func GetFileStreamByPackageVersionAndFileID(ctx context.Context, owner *user_model.User, versionID, fileID int64) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
log.Trace("Getting package file stream: %v, %v, %v", owner.ID, versionID, fileID)
pv, err := packages_model.GetVersionByID(ctx, versionID)
if err != nil {
if err != packages_model.ErrPackageNotExist {
log.Error("Error getting package version: %v", err)
}
return nil, nil, err
}
p, err := packages_model.GetPackageByID(ctx, pv.PackageID)
if err != nil {
log.Error("Error getting package: %v", err)
return nil, nil, err
}
if p.OwnerID != owner.ID {
return nil, nil, packages_model.ErrPackageNotExist
}
pf, err := packages_model.GetFileForVersionByID(ctx, versionID, fileID)
if err != nil {
log.Error("Error getting file: %v", err)
return nil, nil, err
}
return GetPackageFileStream(ctx, pf)
}
// GetFileStreamByPackageVersion returns the content of the specific package file // GetFileStreamByPackageVersion returns the content of the specific package file
func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) { func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey) pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
return GetPackageFileStream(ctx, pf) return GetPackageFileStream(ctx, pf)
} }
// GetPackageFileStream returns the content of the specific package file // GetPackageFileStream returns the content of the specific package file
func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *packages_model.PackageFile, error) { func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
pb, err := packages_model.GetBlobByID(ctx, pf.BlobID) pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
}
return GetPackageBlobStream(ctx, pf, pb)
}
// GetPackageBlobStream returns the content of the specific package blob
// If the storage supports direct serving and it's enabled, only the direct serving url is returned.
func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
key := packages_module.BlobHash256Key(pb.HashSHA256)
cs := packages_module.NewContentStore()
var s io.ReadSeekCloser
var u *url.URL
var err error
if cs.ShouldServeDirect() {
u, err = cs.GetServeDirectURL(key, pf.Name)
if err != nil && !errors.Is(err, storage.ErrURLNotSupported) {
log.Error("Error getting serve direct url: %v", err)
}
}
if u == nil {
s, err = cs.Get(key)
} }
s, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(pb.HashSHA256))
if err == nil { if err == nil {
if pf.IsLead { if pf.IsLead {
if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil { if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil {
@ -633,7 +627,7 @@ func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (
} }
} }
} }
return s, pf, err return s, u, pf, err
} }
// RemoveAllPackages for User // RemoveAllPackages for User

View File

@ -1,5 +1,18 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content devtest ui container">
{{template "base/alert" .}}
<button class="show-modal" data-modal="#test-modal-form">show modal form</button>
<div id="test-modal-form" class="ui mini modal">
<div class="header">Form dialog</div>
<form class="content" method="post">
<div class="ui input"><input name="user_input"></div>
{{template "base/modal_actions_confirm" (dict "locale" $.locale "ModalButtonTypes" "confirm")}}
</form>
</div>
<div class="divider"></div>
<div class="ui g-modal-confirm modal" id="test-modal-default"> <div class="ui g-modal-confirm modal" id="test-modal-default">
<div class="header">{{svg "octicon-file"}} Default dialog <span>title</span></div> <div class="header">{{svg "octicon-file"}} Default dialog <span>title</span></div>
<div class="content"> <div class="content">

View File

@ -39,21 +39,15 @@
{{.locale.Tr "settings.manage_emails"}} {{.locale.Tr "settings.manage_emails"}}
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">
<div class="ui email list"> <div class="ui list">
{{if $.EnableNotifyMail}} {{if $.EnableNotifyMail}}
<div class="item"> <div class="item">
<div class="gt-mb-3">{{.locale.Tr "settings.email_desc"}}</div>
<form action="{{AppSubUrl}}/user/settings/account/email" class="ui form" method="post"> <form action="{{AppSubUrl}}/user/settings/account/email" class="ui form" method="post">
{{.locale.Tr "settings.email_desc"}}
<div class="right floated content">
<div class="field">
<button class="ui green button">{{$.locale.Tr "settings.email_notifications.submit"}}</button>
</div>
</div>
<div class="right floated content">
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}
<input name="_method" type="hidden" value="NOTIFICATION"> <input name="_method" type="hidden" value="NOTIFICATION">
<div class="field"> <div class="gt-df gt-fw gt-gap-3">
<div class="ui selection dropdown" tabindex="0"> <div class="ui selection dropdown">
<input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}"> <input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}">
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="text"></div> <div class="text"></div>
@ -64,7 +58,7 @@
<div data-value="disabled" class="{{if eq .EmailNotificationsPreference "disabled"}}active selected {{end}}item">{{$.locale.Tr "settings.email_notifications.disable"}}</div> <div data-value="disabled" class="{{if eq .EmailNotificationsPreference "disabled"}}active selected {{end}}item">{{$.locale.Tr "settings.email_notifications.disable"}}</div>
</div> </div>
</div> </div>
</div> <button class="ui green button">{{$.locale.Tr "settings.email_notifications.submit"}}</button>
</div> </div>
</form> </form>
</div> </div>
@ -102,7 +96,7 @@
</form> </form>
</div> </div>
{{end}} {{end}}
<div class="content"> <div class="content gt-py-3">
<strong>{{.Email}}</strong> <strong>{{.Email}}</strong>
{{if .IsPrimary}} {{if .IsPrimary}}
<div class="ui primary label">{{$.locale.Tr "settings.primary"}}</div> <div class="ui primary label">{{$.locale.Tr "settings.primary"}}</div>

View File

@ -22,7 +22,19 @@
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="ui g-modal-confirm delete modal" id="remove-gitea-oauth2-application">
<div class="header">
{{svg "octicon-trash"}}
{{.locale.Tr "settings.remove_oauth2_application"}}
</div> </div>
<div class="content">
<p>{{.locale.Tr "settings.oauth2_application_remove_description"}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
</div>
<div class="ui attached bottom segment"> <div class="ui attached bottom segment">
<h5 class="ui top header"> <h5 class="ui top header">
{{.locale.Tr "settings.create_oauth2_application"}} {{.locale.Tr "settings.create_oauth2_application"}}
@ -46,14 +58,3 @@
</button> </button>
</form> </form>
</div> </div>
<div class="ui g-modal-confirm delete modal" id="remove-gitea-oauth2-application">
<div class="header">
{{svg "octicon-trash"}}
{{.locale.Tr "settings.remove_oauth2_application"}}
</div>
<div class="content">
<p>{{.locale.Tr "settings.oauth2_application_remove_description"}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>

View File

@ -26,7 +26,6 @@
</div> </div>
{{end}} {{end}}
</div> </div>
</div>
<div class="ui g-modal-confirm delete modal" id="revoke-gitea-oauth2-grant"> <div class="ui g-modal-confirm delete modal" id="revoke-gitea-oauth2-grant">
<div class="header"> <div class="header">
@ -38,3 +37,4 @@
</div> </div>
{{template "base/modal_actions_confirm" .}} {{template "base/modal_actions_confirm" .}}
</div> </div>
</div>

View File

@ -39,7 +39,6 @@
</div> </div>
{{end}} {{end}}
</div> </div>
</div>
<div class="ui g-modal-confirm delete modal" id="delete-account-link"> <div class="ui g-modal-confirm delete modal" id="delete-account-link">
<div class="header"> <div class="header">
@ -51,4 +50,5 @@
</div> </div>
{{template "base/modal_actions_confirm" .}} {{template "base/modal_actions_confirm" .}}
</div> </div>
</div>
{{end}} {{end}}

View File

@ -48,7 +48,6 @@
{{.locale.Tr "settings.add_openid"}} {{.locale.Tr "settings.add_openid"}}
</button> </button>
</form> </form>
</div>
<div class="ui g-modal-confirm delete modal" id="delete-openid"> <div class="ui g-modal-confirm delete modal" id="delete-openid">
<div class="header"> <div class="header">
@ -60,3 +59,4 @@
</div> </div>
{{template "base/modal_actions_confirm" .}} {{template "base/modal_actions_confirm" .}}
</div> </div>
</div>

View File

@ -21,7 +21,6 @@
<a class="ui green button" href="{{AppSubUrl}}/user/settings/security/two_factor/enroll">{{$.locale.Tr "settings.twofa_enroll"}}</a> <a class="ui green button" href="{{AppSubUrl}}/user/settings/security/two_factor/enroll">{{$.locale.Tr "settings.twofa_enroll"}}</a>
</div> </div>
{{end}} {{end}}
</div>
<div class="ui g-modal-confirm delete modal" id="disable-twofa"> <div class="ui g-modal-confirm delete modal" id="disable-twofa">
<div class="header"> <div class="header">
@ -33,3 +32,4 @@
</div> </div>
{{template "base/modal_actions_confirm" .}} {{template "base/modal_actions_confirm" .}}
</div> </div>
</div>

View File

@ -24,8 +24,6 @@
</div> </div>
<button id="register-webauthn" class="ui green button">{{svg "octicon-key"}} {{.locale.Tr "settings.webauthn_register_key"}}</button> <button id="register-webauthn" class="ui green button">{{svg "octicon-key"}} {{.locale.Tr "settings.webauthn_register_key"}}</button>
</div> </div>
</div>
<div class="ui g-modal-confirm delete modal" id="delete-registration"> <div class="ui g-modal-confirm delete modal" id="delete-registration">
<div class="header"> <div class="header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}
@ -36,3 +34,4 @@
</div> </div>
{{template "base/modal_actions_confirm" .}} {{template "base/modal_actions_confirm" .}}
</div> </div>
</div>

View File

@ -6,6 +6,7 @@ package integration
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"net/http" "net/http"
"testing" "testing"
@ -139,6 +140,42 @@ func TestPackageGeneric(t *testing.T) {
req = NewRequest(t, "GET", url+"/dummy.bin") req = NewRequest(t, "GET", url+"/dummy.bin")
MakeRequest(t, req, http.StatusUnauthorized) MakeRequest(t, req, http.StatusUnauthorized)
}) })
t.Run("ServeDirect", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
if setting.Packages.Storage.Type != setting.MinioStorageType {
t.Skip("Test skipped for non-Minio-storage.")
return
}
if !setting.Packages.Storage.MinioConfig.ServeDirect {
old := setting.Packages.Storage.MinioConfig.ServeDirect
defer func() {
setting.Packages.Storage.MinioConfig.ServeDirect = old
}()
setting.Packages.Storage.MinioConfig.ServeDirect = true
}
req := NewRequest(t, "GET", url+"/"+filename)
resp := MakeRequest(t, req, http.StatusSeeOther)
checkDownloadCount(3)
location := resp.Header().Get("Location")
assert.NotEmpty(t, location)
resp2, err := (&http.Client{}).Get(location)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp2.StatusCode)
body, err := io.ReadAll(resp2.Body)
assert.NoError(t, err)
assert.Equal(t, content, body)
checkDownloadCount(3)
})
}) })
t.Run("Delete", func(t *testing.T) { t.Run("Delete", func(t *testing.T) {

View File

@ -375,16 +375,6 @@ progress::-moz-progress-bar {
opacity: 1 !important; opacity: 1 !important;
} }
.svg {
display: inline-block;
vertical-align: text-top;
fill: currentcolor;
}
.middle .svg {
vertical-align: middle;
}
.unselectable, .unselectable,
.button, .button,
.lines-num, .lines-num,

View File

@ -10,6 +10,7 @@
@import "./modules/navbar.css"; @import "./modules/navbar.css";
@import "./modules/toast.css"; @import "./modules/toast.css";
@import "./modules/divider.css"; @import "./modules/divider.css";
@import "./modules/svg.css";
@import "./shared/issuelist.css"; @import "./shared/issuelist.css";
@import "./shared/milestone.css"; @import "./shared/milestone.css";

View File

@ -0,0 +1,41 @@
.svg {
display: inline-block;
vertical-align: text-top;
fill: currentcolor;
}
.middle .svg {
vertical-align: middle;
}
/* prevent SVGs from shrinking, like in space-starved flexboxes. the sizes
here are cherry-picked for our use cases, feel free to add more. after
https://developer.mozilla.org/en-US/docs/Web/CSS/attr#type-or-unit is
supported in browsers, use `attr(width px)` instead for a generic
solution. */
.svg[height="12"] { min-height: 12px; }
.svg[height="13"] { min-height: 13px; }
.svg[height="14"] { min-height: 14px; }
.svg[height="15"] { min-height: 15px; }
.svg[height="16"] { min-height: 16px; }
.svg[height="18"] { min-height: 18px; }
.svg[height="20"] { min-height: 20px; }
.svg[height="22"] { min-height: 22px; }
.svg[height="24"] { min-height: 24px; }
.svg[height="36"] { min-height: 36px; }
.svg[height="48"] { min-height: 48px; }
.svg[height="56"] { min-height: 56px; }
.svg[width="12"] { min-width: 12px; }
.svg[width="13"] { min-width: 13px; }
.svg[width="14"] { min-width: 14px; }
.svg[width="15"] { min-width: 15px; }
.svg[width="16"] { min-width: 16px; }
.svg[width="18"] { min-width: 18px; }
.svg[width="20"] { min-width: 20px; }
.svg[width="22"] { min-width: 22px; }
.svg[width="24"] { min-width: 24px; }
.svg[width="36"] { min-width: 36px; }
.svg[width="48"] { min-width: 48px; }
.svg[width="56"] { min-width: 56px; }

View File

@ -2358,10 +2358,6 @@
color: var(--color-text-light-2); color: var(--color-text-light-2);
} }
.settings .list.email > .item:not(:first-child) {
min-height: 60px;
}
.settings .list.collaborator > .item { .settings .list.collaborator > .item {
padding: 0; padding: 0;
} }

View File

@ -0,0 +1,26 @@
import $ from 'jquery';
const fomanticModalFn = $.fn.modal;
// use our own `$.fn.modal` to patch Fomantic's modal module
export function initAriaModalPatch() {
if ($.fn.modal === ariaModalFn) throw new Error('initAriaModalPatch could only be called once');
$.fn.modal = ariaModalFn;
ariaModalFn.settings = fomanticModalFn.settings;
}
// the patched `$.fn.modal` modal function
// * it does the one-time attaching on the first call
function ariaModalFn(...args) {
const ret = fomanticModalFn.apply(this, args);
if (args[0] === 'show' || args[0]?.autoShow) {
for (const el of this) {
// If there is a form in the modal, there might be a "cancel" button before "ok" button (all buttons are "type=submit" by default).
// In such case, the "Enter" key will trigger the "cancel" button instead of "ok" button, then the dialog will be closed.
// It breaks the user experience - the "Enter" key should confirm the dialog and submit the form.
// So, all "cancel" buttons without "[type]" must be marked as "type=button".
$(el).find('form button.cancel:not([type])').attr('type', 'button');
}
}
return ret;
}

View File

@ -1,6 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import {initAriaCheckboxPatch} from './aria/checkbox.js'; import {initAriaCheckboxPatch} from './aria/checkbox.js';
import {initAriaDropdownPatch} from './aria/dropdown.js'; import {initAriaDropdownPatch} from './aria/dropdown.js';
import {initAriaModalPatch} from './aria/modal.js';
import {svg} from '../svg.js'; import {svg} from '../svg.js';
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)'); export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
@ -26,6 +27,7 @@ export function initGiteaFomantic() {
// Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future. // Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
initAriaCheckboxPatch(); initAriaCheckboxPatch();
initAriaDropdownPatch(); initAriaDropdownPatch();
initAriaModalPatch();
} }
function initFomanticApiPatch() { function initFomanticApiPatch() {