Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Bence Santha 2024-09-20 14:15:56 +02:00
commit 35bb7e7ed5
42 changed files with 372 additions and 435 deletions

5
go.mod
View File

@ -2,6 +2,11 @@ module code.gitea.io/gitea
go 1.23
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
// But some CAs use negative serial number, just relax the check. related:
// Default TLS cert uses negative serial number #895 https://github.com/microsoft/mssql-docker/issues/895
godebug x509negativeserial=1
require (
code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/gitea-vet v0.2.3

View File

@ -247,7 +247,7 @@ func (r *Review) TooltipContent() string {
}
return "repo.issues.review.official"
case ReviewTypeComment:
return "repo.issues.review.comment"
return "repo.issues.review.commented"
case ReviewTypeReject:
return "repo.issues.review.rejected"
case ReviewTypeRequest:

View File

@ -234,6 +234,7 @@ type FindReleasesOptions struct {
IsDraft optional.Option[bool]
TagNames []string
HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags
NamePattern optional.Option[string]
}
func (opts FindReleasesOptions) ToConds() builder.Cond {
@ -261,6 +262,11 @@ func (opts FindReleasesOptions) ToConds() builder.Cond {
cond = cond.And(builder.Eq{"sha1": ""})
}
}
if opts.NamePattern.Has() && opts.NamePattern.Value() != "" {
cond = cond.And(builder.Like{"lower_tag_name", strings.ToLower(opts.NamePattern.Value())})
}
return cond
}

View File

@ -34,7 +34,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML {
name = "avatar"
}
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
return template.HTML(`<img loading="lazy" class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
}
// Avatar renders user avatars. args: user, size (int), class (string)

View File

@ -218,8 +218,6 @@ string.desc=Z A
[error]
occurred=Došlo k chybě
missing_csrf=Špatný požadavek: Neexistuje CSRF token
invalid_csrf=Špatný požadavek: Neplatný CSRF token
not_found=Cíl nebyl nalezen.
network_error=Chyba sítě
@ -1701,7 +1699,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba úkoly musí být ve stejném
issues.review.self.approval=Nemůžete schválit svůj pull request.
issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu.
issues.review.approve=schválil tyto změny %s
issues.review.comment=Okomentovat
issues.review.dismissed=zamítl/a posouzení od %s %s
issues.review.dismissed_label=Zamítnuto
issues.review.left_comment=zanechal komentář
@ -1726,6 +1723,7 @@ issues.review.hide_resolved=Skrýt vyřešené
issues.review.resolve_conversation=Vyřešit konverzaci
issues.review.un_resolve_conversation=Nevyřešit konverzaci
issues.review.resolved_by=označil tuto konverzaci jako vyřešenou
issues.review.commented=Okomentovat
issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby.
issues.reference_issue.body=Tělo zprávy
issues.content_history.deleted=vymazáno

View File

@ -213,8 +213,6 @@ string.desc=ZA
[error]
occurred=Ein Fehler ist aufgetreten
missing_csrf=Fehlerhafte Anfrage: Kein CSRF Token verfügbar
invalid_csrf=Fehlerhafte Anfrage: Ungültiger CSRF Token
not_found=Das Ziel konnte nicht gefunden werden.
network_error=Netzwerkfehler
@ -1681,7 +1679,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
issues.review.approve=hat die Änderungen %s genehmigt
issues.review.comment=Kommentieren
issues.review.dismissed=verwarf %ss Review %s
issues.review.dismissed_label=Verworfen
issues.review.left_comment=hat einen Kommentar hinterlassen
@ -1706,6 +1703,7 @@ issues.review.hide_resolved=Gelöste ausblenden
issues.review.resolve_conversation=Diskussion als "erledigt" markieren
issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren
issues.review.resolved_by=markierte diese Unterhaltung als gelöst
issues.review.commented=Kommentieren
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden.
issues.reference_issue.body=Beschreibung
issues.content_history.deleted=gelöscht

View File

@ -184,8 +184,6 @@ string.desc=Z - A
[error]
occurred=Παρουσιάστηκε ένα σφάλμα
missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF
invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF
not_found=Ο προορισμός δεν βρέθηκε.
network_error=Σφάλμα δικτύου
@ -1603,7 +1601,6 @@ issues.dependency.add_error_dep_not_same_repo=Και τα δύο ζητήματ
issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το δικό σας pull request.
issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request.
issues.review.approve=ενέκρινε αυτές τις αλλαγές %s
issues.review.comment=Σχόλιο
issues.review.dismissed=απέρριψε την αξιολόγηση %s %s
issues.review.dismissed_label=Απορρίφθηκε
issues.review.left_comment=άφησε ένα σχόλιο
@ -1628,6 +1625,7 @@ issues.review.hide_resolved=Απόκρυψη επιλυμένων
issues.review.resolve_conversation=Επίλυση συνομιλίας
issues.review.un_resolve_conversation=Ανεπίλυτη συνομιλία
issues.review.resolved_by=σημείωση αυτή την συνομιλία ως επιλυμένη
issues.review.commented=Σχόλιο
issues.assignee.error=Δεν προστέθηκαν όλοι οι παραλήπτες λόγω απροσδόκητου σφάλματος.
issues.reference_issue.body=Σώμα
issues.content_history.deleted=διαγράφηκε

View File

@ -178,6 +178,8 @@ code_search_by_git_grep = Current code search results are provided by "git grep"
package_kind = Search packages...
project_kind = Search projects...
branch_kind = Search branches...
tag_kind = Search tags...
tag_tooltip = Search for matching tags. Use '%' to match any sequence of numbers.
commit_kind = Search commits...
runner_kind = Search runners...
no_results = No matching results found.
@ -220,8 +222,6 @@ string.desc = Z - A
[error]
occurred = An error occurred
report_message = If you believe that this is a Gitea bug, please search for issues on <a href="%s" target="_blank">GitHub</a> or open a new issue if necessary.
missing_csrf = Bad Request: no CSRF token present
invalid_csrf = Bad Request: invalid CSRF token
not_found = The target couldn't be found.
network_error = Network error
@ -1757,7 +1757,7 @@ issues.review.hide_resolved = Hide resolved
issues.review.resolve_conversation = Resolve conversation
issues.review.un_resolve_conversation = Unresolve conversation
issues.review.resolved_by = marked this conversation as resolved
issues.review.comment = Comment
issues.review.commented = Comment
issues.review.official = Approved
issues.review.requested = Review pending
issues.review.rejected = Changes requested

View File

@ -182,8 +182,6 @@ string.desc=Z - A
[error]
occurred=Ha ocurrido un error
missing_csrf=Solicitud incorrecta: sin token CSRF
invalid_csrf=Solicitud incorrecta: el token CSRF no es válido
not_found=El objetivo no pudo ser encontrado.
network_error=Error de red
@ -1593,7 +1591,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas incidencias deben estar en e
issues.review.self.approval=No puede aprobar su propio pull request.
issues.review.self.rejection=No puede sugerir cambios en su propio pull request.
issues.review.approve=aprobado estos cambios %s
issues.review.comment=Comentario
issues.review.dismissed=descartó la revisión de %s %s
issues.review.dismissed_label=Descartado
issues.review.left_comment=dejó un comentario
@ -1618,6 +1615,7 @@ issues.review.hide_resolved=Ocultar resueltos
issues.review.resolve_conversation=Resolver conversación
issues.review.un_resolve_conversation=Marcar conversación sin resolver
issues.review.resolved_by=ha marcado esta conversación como resuelta
issues.review.commented=Comentario
issues.assignee.error=No todos los asignados fueron añadidos debido a un error inesperado.
issues.reference_issue.body=Cuerpo
issues.content_history.deleted=borrado

View File

@ -118,7 +118,6 @@ filter.private=خصوصی
[filter]
[error]
missing_csrf=درخواست بد: بلیط CSRF ندارد
[startpage]
app_desc=یک سرویس گیت بی‌درد سر و راحت
@ -1235,7 +1234,6 @@ issues.dependency.add_error_dep_not_same_repo=هر دو موضوع باید از
issues.review.self.approval=شما نمی‌توانید تقاضای واکشی خود را تایید کنید.
issues.review.self.rejection=شما نمی‌توانید تقاضا تغییرات تقاضای واکشی خود را تغییر دهید.
issues.review.approve=این تغییرات را تایید شدند %s
issues.review.comment=دیدگاه
issues.review.dismissed=بررسی %s %s را رد شده
issues.review.dismissed_label=رها شده
issues.review.left_comment=یک نظر ثبت کرد
@ -1256,6 +1254,7 @@ issues.review.hide_resolved=مخفی کردن حل شده ها
issues.review.resolve_conversation=مکالمه را بعنوان حل شده علامت گذاری کردن
issues.review.un_resolve_conversation=مکالمه را بعنوان حل نشده علامت گذاری کردن
issues.review.resolved_by=علامت گذاری این مکالمه بعنوان حل شده
issues.review.commented=دیدگاه
issues.assignee.error=به دلیل خطای غیرمنتظره همه تکالیف اضافه نشد.
issues.reference_issue.body=Body
issues.content_history.deleted=حذف شده

View File

@ -133,8 +133,6 @@ filter.private=Yksityinen
[error]
occurred=Virhe tapahtui
missing_csrf=Virheellinen pyyntö: CSRF-tunnusta ei ole olemassa
invalid_csrf=Virheellinen pyyntö: Virheellinen CSRF-tunniste
not_found=Kohdetta ei löytynyt.
network_error=Verkkovirhe
@ -955,6 +953,7 @@ issues.review.left_comment=jätti kommentin
issues.review.pending=Odottaa
issues.review.show_resolved=Näytä ratkaisu
issues.review.hide_resolved=Piilota ratkaisu
issues.review.commented=Kommentoi
issues.reference_issue.body=Kuvaus
issues.content_history.deleted=poistettu
issues.content_history.edited=muokattu

View File

@ -217,8 +217,6 @@ string.desc=Z - A
[error]
occurred=Une erreur sest produite
missing_csrf=Requête incorrecte: aucun jeton CSRF présent
invalid_csrf=Requête incorrecte : jeton CSRF invalide
not_found=La cible n'a pu être trouvée.
network_error=Erreur réseau
@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Les deux tickets doivent être dan
issues.review.self.approval=Vous ne pouvez approuver vos propres demandes d'ajout.
issues.review.self.rejection=Vous ne pouvez demander de changements sur vos propres demandes de changement.
issues.review.approve=a approuvé ces modifications %s.
issues.review.comment=Commenter
issues.review.dismissed=a révoqué lévaluation de %s %s.
issues.review.dismissed_label=Révoquée
issues.review.left_comment=laisser un commentaire
@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Réduire
issues.review.resolve_conversation=Clore la conversation
issues.review.un_resolve_conversation=Rouvrir la conversation
issues.review.resolved_by=a marqué cette conversation comme résolue.
issues.review.commented=Commenter
issues.assignee.error=Tous les assignés n'ont pas été ajoutés en raison d'une erreur inattendue.
issues.reference_issue.body=Corps
issues.content_history.deleted=a supprimé

View File

@ -901,13 +901,13 @@ issues.dependency.add_error_dep_issue_not_exist=Függő hibajegy nem létezik.
issues.dependency.add_error_dep_not_exist=A függőség nem létezik.
issues.dependency.add_error_dep_exists=A függőség már létezik.
issues.dependency.add_error_dep_not_same_repo=Mindkét hibajegynek ugyanabban a tárolóban kell lennie.
issues.review.comment=Hozzászólás
issues.review.reject=%s változtatások kérése
issues.review.pending=Függőben
issues.review.review=Értékelés
issues.review.reviewers=Véleményezők
issues.review.show_outdated=Elavultak mutatása
issues.review.hide_outdated=Elavultak elrejtése
issues.review.commented=Hozzászólás
issues.assignee.error=Nem minden megbízott lett hozzáadva egy nem várt hiba miatt.

View File

@ -129,8 +129,6 @@ filter.public=Opinbert
[error]
occurred=Villa kom upp
missing_csrf=Slæm beiðni: enginn CSRF lykill
invalid_csrf=Slæm beiðni: ógildur CSRF lykill
not_found=Markmiðið fannst ekki.
network_error=Netkerfisvilla
@ -850,13 +848,13 @@ issues.dependency.remove_header=Fjarlægja Kröfu
issues.dependency.add_error_dep_not_exist=Krafa er ekki til.
issues.dependency.add_error_dep_exists=Krafa er nú þegar til.
issues.review.approve=samþykkti þessar breytingar %s
issues.review.comment=Senda Ummæli
issues.review.dismissed_label=Hunsað
issues.review.left_comment=gerði ummæli
issues.review.pending=Í bið
issues.review.outdated=Úrelt
issues.review.show_outdated=Sýna úrelt
issues.review.hide_outdated=Fela úreld
issues.review.commented=Senda Ummæli
issues.reference_issue.body=Meginmál
issues.content_history.deleted=eytt
issues.content_history.edited=breytt

View File

@ -135,8 +135,6 @@ filter.private=Privati
[error]
occurred=Si è verificato un errore
missing_csrf=Richiesta errata: nessun token CSRF presente
invalid_csrf=Richiesta errata: token CSRF non valido
not_found=Il bersaglio non è stato trovato.
network_error=Errore di rete
@ -1331,7 +1329,6 @@ issues.dependency.add_error_dep_not_same_repo=Entrambi i problemi devono essere
issues.review.self.approval=Non puoi approvare la tua pull request.
issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request.
issues.review.approve=hanno approvato queste modifiche %s
issues.review.comment=Commentare
issues.review.dismissed=recensione %s di %s respinta
issues.review.dismissed_label=Respinta
issues.review.left_comment=lascia un commento
@ -1352,6 +1349,7 @@ issues.review.hide_resolved=Nascondi risolte
issues.review.resolve_conversation=Risolvi la conversazione
issues.review.un_resolve_conversation=Segnala la conversazione come non risolta
issues.review.resolved_by=ha contrassegnato questa conversazione come risolta
issues.review.commented=Commentare
issues.assignee.error=Non tutte le assegnazioni sono state aggiunte a causa di un errore imprevisto.
issues.reference_issue.body=Corpo
issues.content_history.deleted=eliminato

View File

@ -218,8 +218,6 @@ string.desc=Z - A
[error]
occurred=エラーが発生しました
missing_csrf=不正なリクエスト: CSRFトークンがありません
invalid_csrf=不正なリクエスト: CSRFトークンが無効です
not_found=ターゲットが見つかりませんでした。
network_error=ネットワークエラー
@ -1714,7 +1712,6 @@ issues.dependency.add_error_dep_not_same_repo=両方とも同じリポジトリ
issues.review.self.approval=自分のプルリクエストを承認することはできません。
issues.review.self.rejection=自分のプルリクエストに対して修正を要求することはできません。
issues.review.approve=が変更を承認 %s
issues.review.comment=コメント
issues.review.dismissed=が %s のレビューを棄却 %s
issues.review.dismissed_label=棄却
issues.review.left_comment=がコメント
@ -1739,6 +1736,7 @@ issues.review.hide_resolved=解決済みを隠す
issues.review.resolve_conversation=解決済みにする
issues.review.un_resolve_conversation=未解決にする
issues.review.resolved_by=がこの会話を解決済みにしました
issues.review.commented=コメント
issues.assignee.error=予期しないエラーにより、一部の担当者を追加できませんでした。
issues.reference_issue.body=内容
issues.content_history.deleted=削除しました

View File

@ -818,12 +818,12 @@ issues.dependency.add_error_dep_not_same_repo=두 이슈는 같은 레포지토
issues.review.self.approval=자신의 풀 리퀘스트를 승인할 수 없습니다.
issues.review.self.rejection=자신의 풀 리퀘스트에 대한 변경을 요청할 수 없습니다.
issues.review.approve="이 변경사항을 승인하였습니다. %s"
issues.review.comment=댓글
issues.review.pending=보류
issues.review.review=검토
issues.review.reviewers=리뷰어
issues.review.show_outdated=오래된 내역 보기
issues.review.hide_outdated=오래된 내역 숨기기
issues.review.commented=댓글
pulls.new=새 풀 리퀘스트

View File

@ -187,8 +187,6 @@ string.desc=Z - A
[error]
occurred=Radusies kļūda
missing_csrf=Kļūdains pieprasījums: netika iesūtīta drošības pilnvara
invalid_csrf=Kļūdains pieprasījums: iesūtīta kļūdaina drošības pilnvara
not_found=Pieprasītie dati netika atrasti.
network_error=Tīkla kļūda
@ -1609,7 +1607,6 @@ issues.dependency.add_error_dep_not_same_repo=Abām problēmām ir jābūt no vi
issues.review.self.approval=Nevar apstiprināt savu izmaiņu pieprasījumi.
issues.review.self.rejection=Nevar pieprasīt izmaiņas savam izmaiņu pieprasījumam.
issues.review.approve=apstiprināja izmaiņas %s
issues.review.comment=Komentēt
issues.review.dismissed=atmeta %s recenziju %s
issues.review.dismissed_label=Atmesta
issues.review.left_comment=atstāja komentāru
@ -1634,6 +1631,7 @@ issues.review.hide_resolved=Paslēpt atrisināto
issues.review.resolve_conversation=Atrisināt sarunu
issues.review.un_resolve_conversation=Atcelt sarunas atrisinājumu
issues.review.resolved_by=atzīmēja sarunu kā atrisinātu
issues.review.commented=Komentēt
issues.assignee.error=Ne visi atbildīgie tika pievienoti, jo radās neparedzēta kļūda.
issues.reference_issue.body=Saturs
issues.content_history.deleted=dzēsts

View File

@ -134,8 +134,6 @@ filter.private=Prive
[error]
occurred=Er is een fout opgetreden
missing_csrf=Foutief verzoek: geen CSRF-token aanwezig
invalid_csrf=Verkeerd verzoek: ongeldig CSRF-token
not_found=Het doel kon niet worden gevonden.
network_error=Netwerk fout
@ -1328,7 +1326,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide kwesties moeten in dezelfde
issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren.
issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag.
issues.review.approve=heeft deze veranderingen %s goedgekeurd
issues.review.comment=Opmerking
issues.review.dismissed=%s's beoordeling afgewezen %s
issues.review.dismissed_label=Afgewezen
issues.review.left_comment=heeft een reactie achtergelaten
@ -1349,6 +1346,7 @@ issues.review.hide_resolved=Verbergen afgehandeld
issues.review.resolve_conversation=Gesprek oplossen
issues.review.un_resolve_conversation=Gesprek niet oplossen
issues.review.resolved_by=markeerde dit gesprek als opgelost
issues.review.commented=Opmerking
issues.assignee.error=Niet alle aangewezen personen zijn toegevoegd vanwege een onverwachte fout.
issues.reference_issue.body=Inhoud
issues.content_history.deleted=verwijderd

View File

@ -131,8 +131,6 @@ filter.private=Prywatne
[error]
occurred=Wystąpił błąd
missing_csrf=Błędne żądanie: brak tokenu CSRF
invalid_csrf=Błędne żądanie: nieprawidłowy token CSRF
not_found=Nie można odnaleźć celu.
network_error=Błąd sieci
@ -1220,7 +1218,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba zgłoszenia muszą być w tym
issues.review.self.approval=Nie możesz zatwierdzić swojego własnego Pull Requesta.
issues.review.self.rejection=Nie możesz zażądać zmian w swoim własnym Pull Requeście.
issues.review.approve=zatwierdza te zmiany %s
issues.review.comment=Skomentuj
issues.review.dismissed_label=Odrzucony
issues.review.left_comment=zostawił komentarz
issues.review.content.empty=Musisz pozostawić komentarz o pożądanej zmianie/zmianach.
@ -1240,6 +1237,7 @@ issues.review.hide_resolved=Ukryj rozwiązane
issues.review.resolve_conversation=Rozwiąż dyskusję
issues.review.un_resolve_conversation=Oznacz dyskusję jako nierozstrzygniętą
issues.review.resolved_by=oznaczył(-a) tę rozmowę jako rozwiązaną
issues.review.commented=Skomentuj
issues.assignee.error=Nie udało się dodać wszystkich wybranych osób do przypisanych przez nieoczekiwany błąd.
issues.reference_issue.body=Treść
issues.content_history.edited=edytowano

View File

@ -184,8 +184,6 @@ string.desc=Z - A
[error]
occurred=Ocorreu um erro
missing_csrf=Pedido inválido: não tem token CSRF presente
invalid_csrf=Requisição Inválida: token CSRF inválido
not_found=Não foi possível encontrar o destino.
network_error=Erro de rede
@ -1599,7 +1597,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as issues devem estar no mes
issues.review.self.approval=Você não pode aprovar o seu próprio pull request.
issues.review.self.rejection=Você não pode solicitar alterações em seu próprio pull request.
issues.review.approve=aprovou estas alterações %s
issues.review.comment=Comentar
issues.review.dismissed=rejeitou a revisão de %s %s
issues.review.dismissed_label=Rejeitada
issues.review.left_comment=deixou um comentário
@ -1624,6 +1621,7 @@ issues.review.hide_resolved=Ocultar resolvidas
issues.review.resolve_conversation=Resolver conversa
issues.review.un_resolve_conversation=Conversa não resolvida
issues.review.resolved_by=marcou esta conversa como resolvida
issues.review.commented=Comentar
issues.assignee.error=Nem todos os responsáveis foram adicionados devido a um erro inesperado.
issues.reference_issue.body=Conteúdo
issues.content_history.deleted=excluído

View File

@ -178,6 +178,8 @@ code_search_by_git_grep=Os resultados da pesquisa no código-fonte neste momento
package_kind=Pesquisar pacotes...
project_kind=Pesquisar planeamentos...
branch_kind=Pesquisar ramos...
tag_kind=Pesquisar etiquetas...
tag_tooltip=Pesquisar etiquetas correspondentes. Use '%' para corresponder a qualquer sequência de números.
commit_kind=Pesquisar cometimentos...
runner_kind=Pesquisar executores...
no_results=Não foram encontrados resultados correspondentes.
@ -220,8 +222,6 @@ string.desc=Z - A
[error]
occurred=Ocorreu um erro
report_message=Se acredita tratar-se de um erro do Gitea, procure questões relacionadas no <a href="%s">GitHub</a> ou abra uma nova questão, se necessário.
missing_csrf=Pedido inválido: não há código CSRF
invalid_csrf=Pedido inválido: código CSRF inválido
not_found=Não foi possível encontrar o destino.
network_error=Erro de rede
@ -1731,7 +1731,7 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as questões têm que estar
issues.review.self.approval=Não pode aprovar o seu próprio pedido de integração.
issues.review.self.rejection=Não pode solicitar modificações sobre o seu próprio pedido de integração.
issues.review.approve=aprovou estas modificações %s
issues.review.comment=Comentar
issues.review.comment=reviu %s
issues.review.dismissed=descartou a revisão de %s %s
issues.review.dismissed_label=Descartada
issues.review.left_comment=deixou um comentário
@ -1756,6 +1756,7 @@ issues.review.hide_resolved=Ocultar os concluídos
issues.review.resolve_conversation=Passar diálogo ao estado de resolvido
issues.review.un_resolve_conversation=Passar diálogo ao estado de não resolvido
issues.review.resolved_by=marcou este diálogo como estando concluído
issues.review.commented=Comentar
issues.review.official=Aprovada
issues.review.requested=Revisão pendente
issues.review.rejected=Modificações solicitadas

View File

@ -182,8 +182,6 @@ string.desc=Я - А
[error]
occurred=Произошла ошибка
missing_csrf=Некорректный запрос: отсутствует токен CSRF
invalid_csrf=Некорректный запрос: неверный токен CSRF
not_found=Цель не найдена.
network_error=Ошибка сети
@ -1578,7 +1576,6 @@ issues.dependency.add_error_dep_not_same_repo=Обе задачи должны
issues.review.self.approval=Вы не можете одобрить собственный запрос на слияние.
issues.review.self.rejection=Невозможно запрашивать изменения своего запроса на слияние.
issues.review.approve=одобрил(а) эти изменения %s
issues.review.comment=Комментировать
issues.review.dismissed=отклонен отзыв %s %s
issues.review.dismissed_label=Отклонено
issues.review.left_comment=оставил комментарий
@ -1602,6 +1599,7 @@ issues.review.hide_resolved=Скрыть разрешенные
issues.review.resolve_conversation=Покинуть диалог
issues.review.un_resolve_conversation=Незавершённый разговор
issues.review.resolved_by=пометить этот разговор как разрешённый
issues.review.commented=Комментировать
issues.assignee.error=Не все назначения были добавлены из-за непредвиденной ошибки.
issues.reference_issue.body=Тело
issues.content_history.deleted=удалено

View File

@ -118,7 +118,6 @@ filter.private=පෞද්ගලික
[filter]
[error]
missing_csrf=නරක ඉල්ලීම: CSRF ටෝකන් නොමැත
[startpage]
app_desc=වේදනාකාරී, ස්වයං-සත්කාරක Git සේවාවක්
@ -1200,7 +1199,6 @@ issues.dependency.add_error_dep_not_same_repo=මෙම ගැටළු දෙ
issues.review.self.approval=ඔබ ඔබේ ම අදින්න ඉල්ලීම අනුමත කළ නොහැක.
issues.review.self.rejection=ඔබ ඔබේ ම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා සිටිය නොහැක.
issues.review.approve=මෙම වෙනස්කම් අනුමත %s
issues.review.comment=අදහස
issues.review.dismissed=%sහි සමාලෝචනය %sප්රතික්ෂේප කරන ලද
issues.review.dismissed_label=බැහැර
issues.review.left_comment=අදහසක් හැරගියා
@ -1221,6 +1219,7 @@ issues.review.hide_resolved=විසඳා සඟවන්න
issues.review.resolve_conversation=සංවාදය විසඳන්න
issues.review.un_resolve_conversation=නොවිසඳිය හැකි සංවාදය
issues.review.resolved_by=මෙම සංවාදය විසඳා ඇති පරිදි සලකුණු කර ඇත
issues.review.commented=අදහස
issues.assignee.error=අනපේක්ෂිත දෝෂයක් හේතුවෙන් සියලුම ඇසිග්නස් එකතු නොකළේය.
issues.reference_issue.body=ශරීරය
issues.content_history.deleted=මකා දැමූ

View File

@ -181,8 +181,6 @@ string.desc=Z - A
[error]
occurred=Vyskytla sa chyba
missing_csrf=Nesprávna žiadosť: neprítomný CSFR token
invalid_csrf=Nesprávna žiadosť: nesprávny CSFR token
not_found=Nebolo možné nájsť cieľ.
network_error=Chyba siete

View File

@ -1039,7 +1039,6 @@ issues.dependency.add_error_dep_not_same_repo=Båda ärendena måste vara i samm
issues.review.self.approval=Du kan inte godkänna din egen pull-begäran.
issues.review.self.rejection=Du kan inte begära ändringar för din egna pull-förfrågan.
issues.review.approve=godkände dessa ändringar %s
issues.review.comment=Kommentar
issues.review.left_comment=lämnade en kommentar
issues.review.content.empty=Du måste skriva en kommentar som anger de önskade ändringarna.
issues.review.reject=begärda ändringar %s
@ -1056,6 +1055,7 @@ issues.review.show_resolved=Visa löst
issues.review.hide_resolved=Dölj löst
issues.review.resolve_conversation=Lös konversation
issues.review.resolved_by=markerade denna konversation som löst
issues.review.commented=Kommentar
issues.assignee.error=Inte alla tilldelade har lagts till på grund av ett oväntat fel.
issues.content_history.options=Alternativ

View File

@ -218,8 +218,6 @@ string.desc=Z - A
[error]
occurred=Bir hata oluştu
missing_csrf=Hatalı İstek: CSRF anahtarı yok
invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı
not_found=Hedef bulunamadı.
network_error=Ağ hatası
@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Her iki konu da aynı depoda olmal
issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız.
issues.review.self.rejection=Kendi değişiklik isteğinizde değişiklik isteyemezsiniz.
issues.review.approve=%s bu değişiklikleri onayladı
issues.review.comment=Yorum Yap
issues.review.dismissed=%s incelemesini %s reddetti
issues.review.dismissed_label=Reddedildi
issues.review.left_comment=bir yorum yaptı
@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Çözülenleri gizle
issues.review.resolve_conversation=Konuşmayı çöz
issues.review.un_resolve_conversation=Konuşmayı çözme
issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi
issues.review.commented=Yorum Yap
issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi.
issues.reference_issue.body=Gövde
issues.content_history.deleted=silindi

View File

@ -120,7 +120,6 @@ filter.private=Приватний
[error]
occurred=Сталася помилка
missing_csrf=Некоректний запит: токен CSRF не задано
network_error=Помилка мережі
[startpage]
@ -1245,7 +1244,6 @@ issues.dependency.add_error_dep_not_same_repo=Обидві задачі пови
issues.review.self.approval=Ви не можете схвалити власний пулл-реквест.
issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест.
issues.review.approve=зміни затверджено %s
issues.review.comment=Коментар
issues.review.dismissed=відхилено відгук %s %s
issues.review.dismissed_label=Відхилено
issues.review.left_comment=додав коментар
@ -1266,6 +1264,7 @@ issues.review.hide_resolved=Приховати вирішене
issues.review.resolve_conversation=Завершити обговорення
issues.review.un_resolve_conversation=Поновити обговорення
issues.review.resolved_by=позначив обговорення завершеним
issues.review.commented=Коментар
issues.assignee.error=Додано не всіх виконавців через непередбачену помилку.
issues.reference_issue.body=Тіло
issues.content_history.deleted=видалено

View File

@ -217,8 +217,6 @@ string.desc=Z - A
[error]
occurred=发生了一个错误
missing_csrf=错误的请求:没有 CSRF 令牌
invalid_csrf=错误的请求:无效的 CSRF 令牌
not_found=找不到目标。
network_error=网络错误
@ -1692,7 +1690,6 @@ issues.dependency.add_error_dep_not_same_repo=这两个工单必须在同一仓
issues.review.self.approval=您不能批准您自己的合并请求。
issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。
issues.review.approve=于 %s 批准此合并请求
issues.review.comment=评论
issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审
issues.review.dismissed_label=已取消
issues.review.left_comment=留下了一条评论
@ -1717,6 +1714,7 @@ issues.review.hide_resolved=隐藏已解决的
issues.review.resolve_conversation=已解决问题
issues.review.un_resolve_conversation=未解决问题
issues.review.resolved_by=标记问题为已解决
issues.review.commented=评论
issues.assignee.error=因为未知原因,并非所有的指派都成功。
issues.reference_issue.body=内容
issues.content_history.deleted=删除于

View File

@ -167,8 +167,6 @@ string.desc=Z - A
[error]
occurred=發生錯誤
missing_csrf=錯誤的請求:未提供 CSRF token
invalid_csrf=錯誤的請求:無效的 CSRF token
not_found=找不到目標。
network_error=網路錯誤
@ -1467,7 +1465,6 @@ issues.dependency.add_error_dep_not_same_repo=這兩個問題必須在同一個
issues.review.self.approval=您不能核可自己的合併請求。
issues.review.self.rejection=您不能對自己的合併請求提出請求變更。
issues.review.approve=核可了這些變更 %s
issues.review.comment=留言
issues.review.dismissed=取消 %s 的審核 %s
issues.review.dismissed_label=已取消
issues.review.left_comment=留下了回應
@ -1488,6 +1485,7 @@ issues.review.hide_resolved=隱藏已解決
issues.review.resolve_conversation=解決對話
issues.review.un_resolve_conversation=取消解決對話
issues.review.resolved_by=標記了此對話為已解決
issues.review.commented=留言
issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。
issues.reference_issue.body=內容
issues.content_history.deleted=刪除

View File

@ -214,6 +214,8 @@ func TagsList(ctx *context.Context) {
ctx.Data["HideBranchesInDropdown"] = true
ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived
namePattern := ctx.FormTrim("q")
listOptions := db.ListOptions{
Page: ctx.FormInt("page"),
PageSize: ctx.FormInt("limit"),
@ -233,6 +235,7 @@ func TagsList(ctx *context.Context) {
IncludeTags: true,
HasSha1: optional.Some(true),
RepoID: ctx.Repo.Repository.ID,
NamePattern: optional.Some(namePattern),
}
releases, err := db.Find[repo_model.Release](ctx, opts)
@ -241,14 +244,21 @@ func TagsList(ctx *context.Context) {
return
}
ctx.Data["Releases"] = releases
count, err := db.Count[repo_model.Release](ctx, opts)
if err != nil {
ctx.ServerError("GetReleasesByRepoID", err)
return
}
numTags := ctx.Data["NumTags"].(int64)
pager := context.NewPagination(int(numTags), opts.PageSize, opts.Page, 5)
ctx.Data["Keyword"] = namePattern
ctx.Data["Releases"] = releases
ctx.Data["TagCount"] = count
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager
ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases)
ctx.HTML(http.StatusOK, tplTagsList)
}

View File

@ -129,6 +129,8 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
// ensure the session uid is deleted
_ = ctx.Session.Delete("uid")
}
ctx.Csrf.PrepareForSessionUser(ctx)
}
}

View File

@ -138,10 +138,8 @@ func Contexter() func(next http.Handler) http.Handler {
csrfOpts := CsrfOptions{
Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
Cookie: setting.CSRFCookieName,
SetCookie: true,
Secure: setting.SessionConfig.Secure,
CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
Header: "X-Csrf-Token",
CookieDomain: setting.SessionConfig.Domain,
CookiePath: setting.SessionConfig.CookiePath,
SameSite: setting.SessionConfig.SameSite,
@ -167,7 +165,7 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Base.AppendContextValue(WebContextKey, ctx)
ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
ctx.Csrf = NewCSRFProtector(csrfOpts)
// Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
@ -204,8 +202,6 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["SystemConfig"] = setting.Config()
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations

View File

@ -20,64 +20,42 @@
package context
import (
"encoding/base32"
"fmt"
"html/template"
"net/http"
"strconv"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
)
const (
CsrfHeaderName = "X-Csrf-Token"
CsrfFormName = "_csrf"
)
// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
type CSRFProtector interface {
// GetHeaderName returns HTTP header to search for token.
GetHeaderName() string
// GetFormName returns form value to search for token.
GetFormName() string
// GetToken returns the token.
GetToken() string
// Validate validates the token in http context.
// PrepareForSessionUser prepares the csrf protector for the current session user.
PrepareForSessionUser(ctx *Context)
// Validate validates the csrf token in http context.
Validate(ctx *Context)
// DeleteCookie deletes the cookie
// DeleteCookie deletes the csrf cookie
DeleteCookie(ctx *Context)
}
type csrfProtector struct {
opt CsrfOptions
// Token generated to pass via header, cookie, or hidden form value.
Token string
// This value must be unique per user.
ID string
}
// GetHeaderName returns the name of the HTTP header for csrf token.
func (c *csrfProtector) GetHeaderName() string {
return c.opt.Header
}
// GetFormName returns the name of the form value for csrf token.
func (c *csrfProtector) GetFormName() string {
return c.opt.Form
}
// GetToken returns the current token. This is typically used
// to populate a hidden form in an HTML template.
func (c *csrfProtector) GetToken() string {
return c.Token
// id must be unique per user.
id string
// token is the valid one which wil be used by end user and passed via header, cookie, or hidden form value.
token string
}
// CsrfOptions maintains options to manage behavior of Generate.
type CsrfOptions struct {
// The global secret value used to generate Tokens.
Secret string
// HTTP header used to set and get token.
Header string
// Form value used to set and get token.
Form string
// Cookie value used to set and get token.
Cookie string
// Cookie domain.
@ -87,103 +65,64 @@ type CsrfOptions struct {
CookieHTTPOnly bool
// SameSite set the cookie SameSite type
SameSite http.SameSite
// Key used for getting the unique ID per user.
SessionKey string
// oldSessionKey saves old value corresponding to SessionKey.
oldSessionKey string
// If true, send token via X-Csrf-Token header.
SetHeader bool
// If true, send token via _csrf cookie.
SetCookie bool
// Set the Secure flag to true on the cookie.
Secure bool
// Disallow Origin appear in request header.
Origin bool
// Cookie lifetime. Default is 0
CookieLifeTime int
// sessionKey is the key used for getting the unique ID per user.
sessionKey string
// oldSessionKey saves old value corresponding to sessionKey.
oldSessionKey string
}
func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
if opt.Secret == "" {
randBytes, err := util.CryptoRandomBytes(8)
if err != nil {
// this panic can be handled by the recover() in http handlers
panic(fmt.Errorf("failed to generate random bytes: %w", err))
}
opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
}
if opt.Header == "" {
opt.Header = "X-Csrf-Token"
}
if opt.Form == "" {
opt.Form = "_csrf"
}
if opt.Cookie == "" {
opt.Cookie = "_csrf"
}
if opt.CookiePath == "" {
opt.CookiePath = "/"
}
if opt.SessionKey == "" {
opt.SessionKey = "uid"
}
if opt.CookieLifeTime == 0 {
opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
}
opt.oldSessionKey = "_old_" + opt.SessionKey
return opt
}
func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie {
return &http.Cookie{
Name: c.opt.Cookie,
Name: opt.Cookie,
Value: value,
Path: c.opt.CookiePath,
Domain: c.opt.CookieDomain,
MaxAge: c.opt.CookieLifeTime,
Secure: c.opt.Secure,
HttpOnly: c.opt.CookieHTTPOnly,
SameSite: c.opt.SameSite,
Path: opt.CookiePath,
Domain: opt.CookieDomain,
MaxAge: int(CsrfTokenTimeout.Seconds()),
Secure: opt.Secure,
HttpOnly: opt.CookieHTTPOnly,
SameSite: opt.SameSite,
}
}
// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
opt = prepareDefaultCsrfOptions(opt)
x := &csrfProtector{opt: opt}
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
return x
func NewCSRFProtector(opt CsrfOptions) CSRFProtector {
if opt.Secret == "" {
panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code
}
opt.Cookie = util.IfZero(opt.Cookie, "_csrf")
opt.CookiePath = util.IfZero(opt.CookiePath, "/")
opt.sessionKey = "uid"
opt.oldSessionKey = "_old_" + opt.sessionKey
return &csrfProtector{opt: opt}
}
x.ID = "0"
uidAny := ctx.Session.Get(opt.SessionKey)
if uidAny != nil {
func (c *csrfProtector) PrepareForSessionUser(ctx *Context) {
c.id = "0"
if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil {
switch uidVal := uidAny.(type) {
case string:
x.ID = uidVal
c.id = uidVal
case int64:
x.ID = strconv.FormatInt(uidVal, 10)
c.id = strconv.FormatInt(uidVal, 10)
default:
log.Error("invalid uid type in session: %T", uidAny)
}
}
oldUID := ctx.Session.Get(opt.oldSessionKey)
uidChanged := oldUID == nil || oldUID.(string) != x.ID
cookieToken := ctx.GetSiteCookie(opt.Cookie)
oldUID := ctx.Session.Get(c.opt.oldSessionKey)
uidChanged := oldUID == nil || oldUID.(string) != c.id
cookieToken := ctx.GetSiteCookie(c.opt.Cookie)
needsNew := true
if uidChanged {
_ = ctx.Session.Set(opt.oldSessionKey, x.ID)
_ = ctx.Session.Set(c.opt.oldSessionKey, c.id)
} else if cookieToken != "" {
// If cookie token presents, re-use existing unexpired token, else generate a new one.
if issueTime, ok := ParseCsrfToken(cookieToken); ok {
dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
x.Token = cookieToken
c.token = cookieToken
needsNew = false
}
}
@ -191,42 +130,33 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
if needsNew {
// FIXME: actionId.
x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
if opt.SetCookie {
cookie := newCsrfCookie(x, x.Token)
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now())
cookie := newCsrfCookie(&c.opt, c.token)
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
if opt.SetHeader {
ctx.Resp.Header().Add(opt.Header, x.Token)
}
return x
ctx.Data["CsrfToken"] = c.token
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + template.HTMLEscapeString(c.token) + `">`)
}
func (c *csrfProtector) validateToken(ctx *Context, token string) {
if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) {
c.DeleteCookie(ctx)
if middleware.IsAPIPath(ctx.Req) {
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
} else {
ctx.Flash.Error(ctx.Tr("error.invalid_csrf"))
ctx.Redirect(setting.AppSubURL + "/")
}
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
// FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch)
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
}
}
// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
// If this validation fails, custom Error is sent in the reply.
// If neither a header nor form value is found, http.StatusBadRequest is sent.
// If this validation fails, http.StatusBadRequest is sent.
func (c *csrfProtector) Validate(ctx *Context) {
if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" {
if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" {
c.validateToken(ctx, token)
return
}
if token := ctx.Req.FormValue(c.GetFormName()); token != "" {
if token := ctx.Req.FormValue(CsrfFormName); token != "" {
c.validateToken(ctx, token)
return
}
@ -234,9 +164,7 @@ func (c *csrfProtector) Validate(ctx *Context) {
}
func (c *csrfProtector) DeleteCookie(ctx *Context) {
if c.opt.SetCookie {
cookie := newCsrfCookie(c, "")
cookie.MaxAge = -1
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
cookie := newCsrfCookie(&c.opt, "")
cookie.MaxAge = -1
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}

View File

@ -82,43 +82,40 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
return nil
}
attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled {
for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name,
UploaderID: doer.ID,
RepoID: issue.Repo.ID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
log.Info("Skipping disallowed attachment type: %s", attachment.Name)
continue
}
return err
}
attachmentIDs = append(attachmentIDs, a.UUID)
}
}
if content.Content == "" && len(attachmentIDs) == 0 {
return nil
}
switch r := ref.(type) {
case *issues_model.Issue:
attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled {
for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name,
UploaderID: doer.ID,
RepoID: issue.Repo.ID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
log.Info("Skipping disallowed attachment type: %s", attachment.Name)
continue
}
return err
}
attachmentIDs = append(attachmentIDs, a.UUID)
}
}
if content.Content == "" && len(attachmentIDs) == 0 {
return nil
}
_, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
_, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err)
}
case *issues_model.Comment:
comment := r
if content.Content == "" {
return nil
}
if comment.Type == issues_model.CommentTypeCode {
switch comment.Type {
case issues_model.CommentTypeCode:
_, err := pull_service.CreateCodeComment(
ctx,
doer,
@ -130,11 +127,16 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
false, // not pending review but a single review
comment.ReviewID,
"",
nil,
attachmentIDs,
)
if err != nil {
return fmt.Errorf("CreateCodeComment failed: %w", err)
}
default:
_, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err)
}
}
}
return nil

View File

@ -320,8 +320,9 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
}
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
RepoID: repo.ID,
TagNames: tags,
RepoID: repo.ID,
TagNames: tags,
IncludeTags: true,
})
if err != nil {
return fmt.Errorf("db.Find[repo_model.Release]: %w", err)
@ -382,12 +383,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
rel, has := relMap[lowerTag]
parts := strings.SplitN(tag.Message, "\n", 2)
note := ""
if len(parts) > 1 {
note = parts[1]
}
if !has {
parts := strings.SplitN(tag.Message, "\n", 2)
note := ""
if len(parts) > 1 {
note = parts[1]
}
rel = &repo_model.Release{
RepoID: repo.ID,
Title: parts[0],
@ -408,10 +409,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
newReleases = append(newReleases, rel)
} else {
rel.Title = parts[0]
rel.Note = note
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}

View File

@ -4,14 +4,19 @@
<div class="ui container">
{{template "base/alert" .}}
{{template "repo/release_tag_header" .}}
{{if .Releases}}
<h4 class="ui top attached header">
<div class="five wide column tw-flex tw-items-center">
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.tags"}}
{{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}
</div>
</h4>
{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}}
<div class="ui attached segment">
<form class="ignore-dirty" method="get">
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.tag_kind") "Tooltip" (ctx.Locale.Tr "search.tag_tooltip")}}
</form>
</div>
<div class="ui attached table segment">
{{if .Releases}}
<table class="ui very basic striped fixed table single line" id="tags-table">
<tbody class="tag-list">
{{range $idx, $release := .Releases}}
@ -57,9 +62,12 @@
{{end}}
</tbody>
</table>
{{else}}
{{if .NumTags}}
<p class="tw-p-4">{{ctx.Locale.Tr "no_results_found"}}</p>
{{end}}
{{end}}
</div>
{{end}}
{{template "base/paginate" .}}
</div>
</div>

View File

@ -59,7 +59,8 @@ func createAttachment(t *testing.T, session *TestSession, repoURL, filename stri
func TestCreateAnonymousAttachment(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := emptyTestSession(t)
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusSeeOther)
// this test is not right because it just doesn't pass the CSRF validation
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusBadRequest)
}
func TestCreateIssueAttachment(t *testing.T) {

View File

@ -5,12 +5,10 @@ package integration
import (
"net/http"
"strings"
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -25,28 +23,12 @@ func TestCsrfProtection(t *testing.T) {
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": "fake_csrf",
})
session.MakeRequest(t, req, http.StatusSeeOther)
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
// test web form csrf via header. TODO: should use an UI api to test
req = NewRequest(t, "POST", "/user/settings")
req.Header.Add("X-Csrf-Token", "fake_csrf")
session.MakeRequest(t, req, http.StatusSeeOther)
resp = session.MakeRequest(t, req, http.StatusSeeOther)
loc = resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp = session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}

View File

@ -7,6 +7,7 @@ import (
"io"
"net"
"net/smtp"
"net/url"
"strings"
"testing"
"time"
@ -26,187 +27,190 @@ import (
)
func TestIncomingEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onGiteaRun(t, func(t *testing.T, u *url.URL) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
t.Run("Payload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Payload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
_, err := incoming_payload.CreateReferencePayload(user)
assert.Error(t, err)
_, err := incoming_payload.CreateReferencePayload(user)
assert.Error(t, err)
issuePayload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
issuePayload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
_, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3})
assert.Error(t, err)
_, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3})
assert.Error(t, err)
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
})
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
})
t.Run("Token", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Token", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
payload := []byte{1, 2, 3, 4, 5}
payload := []byte{1, 2, 3, 4, 5}
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NotEmpty(t, token)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NotEmpty(t, token)
ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token)
assert.NoError(t, err)
assert.Equal(t, token_service.ReplyHandlerType, ht)
assert.Equal(t, user.ID, u.ID)
assert.Equal(t, payload, p)
})
ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token)
assert.NoError(t, err)
assert.Equal(t, token_service.ReplyHandlerType, ht)
assert.Equal(t, user.ID, u.ID)
assert.Equal(t, payload, p)
})
t.Run("Handler", func(t *testing.T) {
t.Run("Reply", func(t *testing.T) {
t.Run("Comment", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Handler", func(t *testing.T) {
t.Run("Reply", func(t *testing.T) {
t.Run("Comment", func(t *testing.T) {
handler := &incoming.ReplyHandler{}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
})
t.Run("CodeComment", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
handler := &incoming.ReplyHandler{}
content := &incoming.MailContent{
Content: "code reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
payload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeCode,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment = comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
})
})
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
handler := &incoming.ReplyHandler{}
watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
assert.False(t, watching)
})
})
t.Run("CodeComment", func(t *testing.T) {
if setting.IncomingEmail.Enabled {
// This test connects to the configured email server and is currently only enabled for MySql integration tests.
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails.
t.Run("IMAP", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
handler := &incoming.ReplyHandler{}
content := &incoming.MailContent{
Content: "code reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
payload, err := incoming_payload.CreateReferencePayload(comment)
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeCode,
})
msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
msg.SetHeader("From", user.Email)
msg.SetBody("text/plain", token)
err = gomail.Send(&smtpTestSender{}, msg)
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment = comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Empty(t, comment.Attachments)
assert.Eventually(t, func() bool {
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
return comment.PosterID == user.ID && comment.Content == token
}, 10*time.Second, 1*time.Second)
})
})
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.False(t, watching)
})
}
})
if setting.IncomingEmail.Enabled {
// This test connects to the configured email server and is currently only enabled for MySql integration tests.
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails.
t.Run("IMAP", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
msg.SetHeader("From", user.Email)
msg.SetBody("text/plain", token)
err = gomail.Send(&smtpTestSender{}, msg)
assert.NoError(t, err)
assert.Eventually(t, func() bool {
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
return comment.PosterID == user.ID && comment.Content == token
}, 10*time.Second, 1*time.Second)
})
}
}
// A simple SMTP mail sender used for integration tests.

View File

@ -17,7 +17,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
@ -146,15 +145,8 @@ func TestCreateBranchInvalidCSRF(t *testing.T) {
"_csrf": "fake_csrf",
"new_branch_name": "test",
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t,
"Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}
func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) {

View File

@ -4,6 +4,7 @@
package integration
import (
"net/http"
"net/url"
"testing"
@ -18,6 +19,7 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateNewTagProtected(t *testing.T) {
@ -60,6 +62,40 @@ func TestCreateNewTagProtected(t *testing.T) {
})
})
t.Run("GitTagForce", func(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
httpContext := NewAPITestContext(t, owner.Name, repo.Name)
dstPath := t.TempDir()
u.Path = httpContext.GitPath()
u.User = url.UserPassword(owner.Name, userPassword)
doGitClone(dstPath, u)(t)
_, _, err := git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update v2", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
require.Error(t, err)
assert.Contains(t, err.Error(), "the tag already exists in the remote")
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
req := NewRequestf(t, "GET", "/%s/releases/tag/v-1.1", repo.FullName())
resp := MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
tagsTab := htmlDoc.Find(".release-list-title")
assert.Contains(t, tagsTab.Text(), "force update v2")
})
})
// Cleanup
releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{
IncludeTags: true,