mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-15 08:08:22 +00:00
Merge branch 'go-gitea:main' into main
This commit is contained in:
commit
bcc4ade3e7
@ -63,14 +63,19 @@ RUN_USER = ; git
|
||||
;PROTOCOL = http
|
||||
;;
|
||||
;; Set the domain for the server.
|
||||
;; Most users should set it to the real website domain of their Gitea instance.
|
||||
;DOMAIN = localhost
|
||||
;;
|
||||
;; The AppURL used by Gitea to generate absolute links, defaults to "{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/".
|
||||
;; The AppURL is used to generate public URL links, defaults to "{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/".
|
||||
;; Most users should set it to the real website URL of their Gitea instance when there is a reverse proxy.
|
||||
;; When it is empty, Gitea will use HTTP "Host" header to generate ROOT_URL, and fall back to the default one if no "Host" header.
|
||||
;ROOT_URL =
|
||||
;;
|
||||
;; Controls how to detect the public URL.
|
||||
;; Although it defaults to "legacy" (to avoid breaking existing users), most instances should use the "auto" behavior,
|
||||
;; especially when the Gitea instance needs to be accessed in a container network.
|
||||
;; * legacy: detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL".
|
||||
;; * auto: always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL".
|
||||
;PUBLIC_URL_DETECTION = legacy
|
||||
;;
|
||||
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
|
||||
;; DO NOT USE IT IN PRODUCTION!!!
|
||||
;USE_SUB_URL_PATH = false
|
||||
|
@ -53,30 +53,31 @@ func getRequestScheme(req *http.Request) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GuessCurrentAppURL tries to guess the current full app URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
|
||||
// GuessCurrentAppURL tries to guess the current full public URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
|
||||
// TODO: should rename it to GuessCurrentPublicURL in the future
|
||||
func GuessCurrentAppURL(ctx context.Context) string {
|
||||
return GuessCurrentHostURL(ctx) + setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
// GuessCurrentHostURL tries to guess the current full host URL (no sub-path) by http headers, there is no trailing slash.
|
||||
func GuessCurrentHostURL(ctx context.Context) string {
|
||||
req, ok := ctx.Value(RequestContextKey).(*http.Request)
|
||||
if !ok {
|
||||
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
|
||||
}
|
||||
// If no scheme provided by reverse proxy, then do not guess the AppURL, use the configured one.
|
||||
// Try the best guess to get the current host URL (will be used for public URL) by http headers.
|
||||
// At the moment, if site admin doesn't configure the proxy headers correctly, then Gitea would guess wrong.
|
||||
// There are some cases:
|
||||
// 1. The reverse proxy is configured correctly, it passes "X-Forwarded-Proto/Host" headers. Perfect, Gitea can handle it correctly.
|
||||
// 2. The reverse proxy is not configured correctly, doesn't pass "X-Forwarded-Proto/Host" headers, eg: only one "proxy_pass http://gitea:3000" in Nginx.
|
||||
// 3. There is no reverse proxy.
|
||||
// Without more information, Gitea is impossible to distinguish between case 2 and case 3, then case 2 would result in
|
||||
// wrong guess like guessed AppURL becomes "http://gitea:3000/" behind a "https" reverse proxy, which is not accessible by end users.
|
||||
// So we introduced "UseHostHeader" option, it could be enabled by setting "ROOT_URL" to empty
|
||||
// wrong guess like guessed public URL becomes "http://gitea:3000/" behind a "https" reverse proxy, which is not accessible by end users.
|
||||
// So we introduced "PUBLIC_URL_DETECTION" option, to control the guessing behavior to satisfy different use cases.
|
||||
req, ok := ctx.Value(RequestContextKey).(*http.Request)
|
||||
if !ok {
|
||||
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
|
||||
}
|
||||
reqScheme := getRequestScheme(req)
|
||||
if reqScheme == "" {
|
||||
// if no reverse proxy header, try to use "Host" header for absolute URL
|
||||
if setting.UseHostHeader && req.Host != "" {
|
||||
if setting.PublicURLDetection == setting.PublicURLAuto && req.Host != "" {
|
||||
return util.Iif(req.TLS == nil, "http://", "https://") + req.Host
|
||||
}
|
||||
// fall back to default AppURL
|
||||
@ -93,8 +94,8 @@ func GuessCurrentHostDomain(ctx context.Context) string {
|
||||
return util.IfZero(domain, host)
|
||||
}
|
||||
|
||||
// MakeAbsoluteURL tries to make a link to an absolute URL:
|
||||
// * If link is empty, it returns the current app URL.
|
||||
// MakeAbsoluteURL tries to make a link to an absolute public URL:
|
||||
// * If link is empty, it returns the current public URL.
|
||||
// * If link is absolute, it returns the link.
|
||||
// * Otherwise, it returns the current host URL + link, the link itself should have correct sub-path (AppSubURL) if needed.
|
||||
func MakeAbsoluteURL(ctx context.Context, link string) string {
|
||||
|
@ -43,20 +43,37 @@ func TestIsRelativeURL(t *testing.T) {
|
||||
func TestGuessCurrentHostURL(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppURL, "http://cfg-host/sub/")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
|
||||
defer test.MockVariableValue(&setting.UseHostHeader, false)()
|
||||
headersWithProto := http.Header{"X-Forwarded-Proto": {"https"}}
|
||||
|
||||
ctx := t.Context()
|
||||
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(ctx))
|
||||
t.Run("Legacy", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.PublicURLDetection, setting.PublicURLLegacy)()
|
||||
|
||||
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{Host: "localhost:3000"})
|
||||
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(ctx))
|
||||
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(t.Context()))
|
||||
|
||||
defer test.MockVariableValue(&setting.UseHostHeader, true)()
|
||||
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{Host: "http-host:3000"})
|
||||
assert.Equal(t, "http://http-host:3000", GuessCurrentHostURL(ctx))
|
||||
// legacy: "Host" is not used when there is no "X-Forwarded-Proto" header
|
||||
ctx := context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000"})
|
||||
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(ctx))
|
||||
|
||||
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{Host: "http-host", TLS: &tls.ConnectionState{}})
|
||||
assert.Equal(t, "https://http-host", GuessCurrentHostURL(ctx))
|
||||
// if "X-Forwarded-Proto" exists, then use it and "Host" header
|
||||
ctx = context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000", Header: headersWithProto})
|
||||
assert.Equal(t, "https://req-host:3000", GuessCurrentHostURL(ctx))
|
||||
})
|
||||
|
||||
t.Run("Auto", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.PublicURLDetection, setting.PublicURLAuto)()
|
||||
|
||||
assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(t.Context()))
|
||||
|
||||
// auto: always use "Host" header, the scheme is determined by "X-Forwarded-Proto" header, or TLS config if no "X-Forwarded-Proto" header
|
||||
ctx := context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000"})
|
||||
assert.Equal(t, "http://req-host:3000", GuessCurrentHostURL(ctx))
|
||||
|
||||
ctx = context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host", TLS: &tls.ConnectionState{}})
|
||||
assert.Equal(t, "https://req-host", GuessCurrentHostURL(ctx))
|
||||
|
||||
ctx = context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000", Header: headersWithProto})
|
||||
assert.Equal(t, "https://req-host:3000", GuessCurrentHostURL(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func TestMakeAbsoluteURL(t *testing.T) {
|
||||
|
@ -41,12 +41,20 @@ const (
|
||||
LandingPageLogin LandingPage = "/user/login"
|
||||
)
|
||||
|
||||
const (
|
||||
PublicURLAuto = "auto"
|
||||
PublicURLLegacy = "legacy"
|
||||
)
|
||||
|
||||
// Server settings
|
||||
var (
|
||||
// AppURL is the Application ROOT_URL. It always has a '/' suffix
|
||||
// It maps to ini:"ROOT_URL"
|
||||
AppURL string
|
||||
|
||||
// PublicURLDetection controls how to use the HTTP request headers to detect public URL
|
||||
PublicURLDetection string
|
||||
|
||||
// AppSubURL represents the sub-url mounting point for gitea, parsed from "ROOT_URL"
|
||||
// It is either "" or starts with '/' and ends without '/', such as '/{sub-path}'.
|
||||
// This value is empty if site does not have sub-url.
|
||||
@ -56,9 +64,6 @@ var (
|
||||
// to make it easier to debug sub-path related problems without a reverse proxy.
|
||||
UseSubURLPath bool
|
||||
|
||||
// UseHostHeader makes Gitea prefer to use the "Host" request header for construction of absolute URLs.
|
||||
UseHostHeader bool
|
||||
|
||||
// AppDataPath is the default path for storing data.
|
||||
// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data"
|
||||
AppDataPath string
|
||||
@ -283,10 +288,10 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
||||
PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
|
||||
|
||||
defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort
|
||||
AppURL = sec.Key("ROOT_URL").String()
|
||||
if AppURL == "" {
|
||||
UseHostHeader = true
|
||||
AppURL = defaultAppURL
|
||||
AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
|
||||
PublicURLDetection = sec.Key("PUBLIC_URL_DETECTION").MustString(PublicURLLegacy)
|
||||
if PublicURLDetection != PublicURLAuto && PublicURLDetection != PublicURLLegacy {
|
||||
log.Fatal("Invalid PUBLIC_URL_DETECTION value: %s", PublicURLDetection)
|
||||
}
|
||||
|
||||
// Check validity of AppURL
|
||||
|
@ -1652,7 +1652,6 @@ issues.pin_comment=připnuto %s
|
||||
issues.unpin_comment=odepnul/a tento %s
|
||||
issues.lock=Uzamknout konverzaci
|
||||
issues.unlock=Odemknout konverzaci
|
||||
issues.lock.unknown_reason=Úkol nelze z neznámého důvodu uzamknout.
|
||||
issues.lock_duplicate=Úkol nemůže být uzamčený dvakrát.
|
||||
issues.unlock_error=Nelze odemknout úkol, který je uzamčený.
|
||||
issues.lock_with_reason=uzamkl/a jako <strong>%s</strong> a omezil/a konverzaci na spolupracovníky %s
|
||||
|
@ -1653,7 +1653,6 @@ issues.pin_comment=hat das %s angeheftet
|
||||
issues.unpin_comment=hat das %s abgeheftet
|
||||
issues.lock=Diskussion sperren
|
||||
issues.unlock=Diskussion entsperren
|
||||
issues.lock.unknown_reason=Es ist nicht möglich, einen Issue mit unbekanntem Grund zu sperren.
|
||||
issues.lock_duplicate=Eine Diskussion kann nicht mehrfach gesperrt werden.
|
||||
issues.unlock_error=Es ist nicht möglich, einen nicht gesperrten Issue zu entsperren.
|
||||
issues.lock_with_reason=gesperrt als <strong>%s</strong> und Diskussion auf Mitarbeiter beschränkt %s
|
||||
|
@ -1497,7 +1497,6 @@ issues.pin_comment=διατήρησε αυτό %s
|
||||
issues.unpin_comment=άφησε αυτό %s
|
||||
issues.lock=Κλείδωμα συνομιλίας
|
||||
issues.unlock=Ξεκλείδωμα συνομιλίας
|
||||
issues.lock.unknown_reason=Αδυναμία κλειδώματος ενός ζητήματος με άγνωστο λόγο.
|
||||
issues.lock_duplicate=Ένα ζήτημα δεν μπορεί να κλειδωθεί δύο φορές.
|
||||
issues.unlock_error=Δεν είναι δυνατό να ξεκλειδώσετε ένα ζήτημα που δεν είναι κλειδωμένο.
|
||||
issues.lock_with_reason=κλειδωμένο ως <strong>%s</strong> και περιορισμένη συνομιλία με συνεργάτες %s
|
||||
|
@ -1487,7 +1487,6 @@ issues.pin_comment=anclado este %s
|
||||
issues.unpin_comment=desanclado este %s
|
||||
issues.lock=Bloquear conversación
|
||||
issues.unlock=Desbloquear conversación
|
||||
issues.lock.unknown_reason=No se puede bloquear una incidencia con una razón desconocida.
|
||||
issues.lock_duplicate=Una incidencia no puede ser bloqueada dos veces.
|
||||
issues.unlock_error=No puede desbloquear una incidencia que no esta bloqueada.
|
||||
issues.lock_with_reason=bloqueado como <strong>%s</strong> y conversación limitada a colaboradores %s
|
||||
|
@ -1146,7 +1146,6 @@ issues.subscribe=مشترک شدن
|
||||
issues.unsubscribe=لغو اشتراک
|
||||
issues.lock=قفل کردن مکالمه
|
||||
issues.unlock=بازکردن مکالمه
|
||||
issues.lock.unknown_reason=نمیتوانید این موضوع را بدون دلیل ببندید.
|
||||
issues.lock_duplicate=یک مسئله دومرتبه نمیتواند بسته شود.
|
||||
issues.unlock_error=این مسئله نمیتواند باز شود لذا قفل نشده بود.
|
||||
issues.lock_with_reason=قفل شده با عنوان <strong>%s</strong> و مکالمه همکاران %s محدود شده است
|
||||
|
@ -1680,7 +1680,6 @@ issues.pin_comment=a épinglé ça %s.
|
||||
issues.unpin_comment=a désépinglé ça %s.
|
||||
issues.lock=Verrouiller la conversation
|
||||
issues.unlock=Déverrouiller la conversation
|
||||
issues.lock.unknown_reason=Impossible de verrouiller un ticket avec une raison inconnue.
|
||||
issues.lock_duplicate=Un ticket ne peut pas être verrouillé à deux reprises.
|
||||
issues.unlock_error=Impossible de déverrouiller un ticket qui n'est pas verrouillé.
|
||||
issues.lock_with_reason=a verrouillé en tant que <strong>%s</strong> et limité la conversation aux collaborateurs %s
|
||||
|
@ -1674,7 +1674,6 @@ issues.pin_comment=phionnáil an %s seo
|
||||
issues.unpin_comment=bain pionna an %s seo
|
||||
issues.lock=Cuir glas ar an gcomhrá
|
||||
issues.unlock=Díghlasáil comhrá
|
||||
issues.lock.unknown_reason=Ní féidir fadhb a ghlasáil le cúis anaithnid.
|
||||
issues.lock_duplicate=Ní féidir saincheist a ghlasáil faoi dhó.
|
||||
issues.unlock_error=Ní féidir saincheist nach bhfuil glasáilte a dhíghlasáil.
|
||||
issues.lock_with_reason=curtha ar ceal mar <strong>%s</strong> agus comhrá teoranta do chomhoibrithe %s
|
||||
|
@ -844,7 +844,6 @@ issues.subscribe=Feliratkozás
|
||||
issues.unsubscribe=Leiratkozás
|
||||
issues.lock=Beszélgetés lezárása
|
||||
issues.unlock=Beszélgetés feloldása
|
||||
issues.lock.unknown_reason=Egy hibajegy nem zárolható ismeretlen okból.
|
||||
issues.lock_duplicate=Egy hibajegy nem zárható be kétszer.
|
||||
issues.unlock_error=Nem nyithatsz meg egy hibajegyet ami nincs is lezárva.
|
||||
issues.lock_confirm=Lezárás
|
||||
|
@ -1236,7 +1236,6 @@ issues.subscribe=Iscriviti
|
||||
issues.unsubscribe=Annulla iscrizione
|
||||
issues.lock=Blocca conversazione
|
||||
issues.unlock=Sblocca conversazione
|
||||
issues.lock.unknown_reason=Impossibile bloccare un problema con un motivo sconosciuto.
|
||||
issues.lock_duplicate=Un issue non può essere bloccato due volte.
|
||||
issues.unlock_error=Impossibile sbloccare un problema che non è bloccato.
|
||||
issues.lock_with_reason=ha bloccato come <strong>%s</strong> e limitato la conversazione ai collaboratori %s
|
||||
|
@ -1680,7 +1680,6 @@ issues.pin_comment=がピン留め %s
|
||||
issues.unpin_comment=がピン留めを解除 %s
|
||||
issues.lock=会話をロック
|
||||
issues.unlock=会話をアンロック
|
||||
issues.lock.unknown_reason=未定義の理由ではイシューをロックできません。
|
||||
issues.lock_duplicate=イシューは二重にロックできません。
|
||||
issues.unlock_error=ロックされていないイシューをアンロックできません。
|
||||
issues.lock_with_reason=が<strong>%s</strong>のためロックし会話を共同作業者に限定 %s
|
||||
|
@ -1503,7 +1503,6 @@ issues.pin_comment=piesprauda šo %s
|
||||
issues.unpin_comment=atsprauda šo %s
|
||||
issues.lock=Slēgt komentēšanu
|
||||
issues.unlock=Atļaut komentēšanu
|
||||
issues.lock.unknown_reason=Neizdevās slēgt problēmas komentēšanu.
|
||||
issues.lock_duplicate=Problēmas komentēšanu nevar slēgt vairākas reizes.
|
||||
issues.unlock_error=Nevar atļaut komentēšanu, ja problēmai tā nav slēgta.
|
||||
issues.lock_with_reason=slēdza ar iemeslu <strong>%s</strong> un ierobežoja komentāru pievienošanu tikai līdzstrādniekiem %s
|
||||
|
@ -1233,7 +1233,6 @@ issues.subscribe=Abonneren
|
||||
issues.unsubscribe=Uitschrijven
|
||||
issues.lock=Gesprek vergrendelen
|
||||
issues.unlock=Gesprek ontgrendelen
|
||||
issues.lock.unknown_reason=Kan een probleem niet vergrendelen met een onbekende reden.
|
||||
issues.lock_duplicate=Een issue kan niet twee keer vergrendeld worden.
|
||||
issues.unlock_error=Kan een niet vergrendeld issue niet ontgrendelen.
|
||||
issues.lock_with_reason=vergrendeld als <strong>%s</strong> en beperkt gesprek tot medewerkers %s
|
||||
|
@ -1135,7 +1135,6 @@ issues.subscribe=Subskrybuj
|
||||
issues.unsubscribe=Anuluj subskrypcję
|
||||
issues.lock=Zablokuj konwersację
|
||||
issues.unlock=Odblokuj konwersację
|
||||
issues.lock.unknown_reason=Nie można zablokować zagadnienia bez żadnego powodu.
|
||||
issues.lock_duplicate=Zagadnienie nie może być zablokowane ponownie.
|
||||
issues.unlock_error=Nie można odblokować zagadnienia, które nie jest zablokowane.
|
||||
issues.lock_with_reason=zablokowano jako <strong>%s</strong> i ograniczono konwersację do współtwórców %s
|
||||
|
@ -1496,7 +1496,6 @@ issues.pin_comment=fixou isto %s
|
||||
issues.unpin_comment=desafixou isto %s
|
||||
issues.lock=Bloquear conversação
|
||||
issues.unlock=Desbloquear conversação
|
||||
issues.lock.unknown_reason=Não pode-se bloquear uma issue com um motivo desconhecido.
|
||||
issues.lock_duplicate=Uma issue não pode ser bloqueada duas vezes.
|
||||
issues.unlock_error=Não pode-se desbloquear uma issue que não esteja bloqueada.
|
||||
issues.lock_with_reason=bloqueada como <strong>%s</strong> e conversação limitada para colaboradores %s
|
||||
|
@ -1680,7 +1680,6 @@ issues.pin_comment=fixou isto %s
|
||||
issues.unpin_comment=desafixou isto %s
|
||||
issues.lock=Bloquear diálogo
|
||||
issues.unlock=Desbloquear diálogo
|
||||
issues.lock.unknown_reason=Não é possível bloquear uma questão com um motivo desconhecido.
|
||||
issues.lock_duplicate=Uma questão não pode ser bloqueada duas vezes.
|
||||
issues.unlock_error=Não é possível desbloquear uma questão que não está bloqueada.
|
||||
issues.lock_with_reason=bloqueou o diálogo como sendo <strong>%s</strong> e restringiu-o aos colaboradores %s
|
||||
|
@ -1474,7 +1474,6 @@ issues.pin_comment=закрепил(а) эту задачу %s
|
||||
issues.unpin_comment=открепил(а) эту задачу %s
|
||||
issues.lock=Ограничить обсуждение
|
||||
issues.unlock=Снять ограничение
|
||||
issues.lock.unknown_reason=Для ограничения обсуждения необходимо указать причину.
|
||||
issues.lock_duplicate=Обсуждение задачи уже ограничено.
|
||||
issues.unlock_error=Невозможно снять несуществующее ограничение обсуждения.
|
||||
issues.lock_with_reason=заблокировано как <strong>%s</strong> и ограничено обсуждение для соучастников %s
|
||||
|
@ -1111,7 +1111,6 @@ issues.subscribe=දායක වන්න
|
||||
issues.unsubscribe=දායක වන්න
|
||||
issues.lock=ලොක් සංවාදය
|
||||
issues.unlock=සංවාදය අගුළු ඇරීමට
|
||||
issues.lock.unknown_reason=නොදන්නා හේතුවක් සමඟ ගැටළුවක් අගුලු දැමිය නොහැක.
|
||||
issues.lock_duplicate=ප්රශ්නයක් දෙවරක් අගුලු දැමිය නොහැක.
|
||||
issues.unlock_error=අගුලු දමා නැති බව ප්රශ්නයක් අන්ලොක් කරන්න බැහැ.
|
||||
issues.lock_with_reason=<strong>%s</strong> ලෙස අගුළු දමා ඇති අතර සහයෝගීතාකරුවන්ට සීමිත සංවාදයක් %s
|
||||
|
@ -955,7 +955,6 @@ issues.subscribe=Prenumerera
|
||||
issues.unsubscribe=Avsluta prenumerationen
|
||||
issues.lock=Lås konversation
|
||||
issues.unlock=Lås upp konversation
|
||||
issues.lock.unknown_reason=Kan inte låsa ärende utan angiven anledning.
|
||||
issues.lock_duplicate=Ett ärende kan inte låsas två gånger.
|
||||
issues.unlock_error=Kan inte låsa upp ett olåst ärende.
|
||||
issues.lock_with_reason=låst som <strong>%s</strong> och begränsad konversation till medarbetare %s
|
||||
|
@ -1607,7 +1607,6 @@ issues.pin_comment=%s sabitlendi
|
||||
issues.unpin_comment=%s sabitlenmesi kaldırıldı
|
||||
issues.lock=Konuşmayı kilitle
|
||||
issues.unlock=Konuşmanın kilidini aç
|
||||
issues.lock.unknown_reason=Sebep belirtmeden konuyu kilitleyemezsiniz.
|
||||
issues.lock_duplicate=Bir konu iki kez kilitlenemez.
|
||||
issues.unlock_error=Kilitlenmemiş bir konunun kilidini açamazsınız.
|
||||
issues.lock_with_reason=<strong>%s</strong> olarak kilitlendi ve katkıcılar için sınırlandırıldı %s
|
||||
|
@ -1158,7 +1158,6 @@ issues.subscribe=Підписатися
|
||||
issues.unsubscribe=Відписатися
|
||||
issues.lock=Блокування обговорення
|
||||
issues.unlock=Розблокування обговорення
|
||||
issues.lock.unknown_reason=Неможливо заблокувати задачу з невідомою причиною.
|
||||
issues.lock_duplicate=Задача не може бути заблокованим двічі.
|
||||
issues.unlock_error=Не можливо розблокувати задачу, яка не заблокована.
|
||||
issues.lock_with_reason=заблоковано як <strong>%s</strong> та обмежене обговорення для співавторів %s
|
||||
|
@ -1679,7 +1679,6 @@ issues.pin_comment=于 %s 被置顶
|
||||
issues.unpin_comment=于 %s 取消置顶
|
||||
issues.lock=锁定对话
|
||||
issues.unlock=解锁对话
|
||||
issues.lock.unknown_reason=由于未知原因无法锁定。
|
||||
issues.lock_duplicate=一个工单不能被锁定两次。
|
||||
issues.unlock_error=无法解锁一个未锁定的工单。
|
||||
issues.lock_with_reason=因为 <strong>%s</strong> 而锁定,并将对话限制为协作者 %s
|
||||
|
@ -1640,7 +1640,6 @@ issues.pin_comment=固定於 %s
|
||||
issues.unpin_comment=取消固定於 %s
|
||||
issues.lock=鎖定對話
|
||||
issues.unlock=解鎖對話
|
||||
issues.lock.unknown_reason=由於未知的原因而無法鎖定問題。
|
||||
issues.lock_duplicate=問題無法被鎖定兩次。
|
||||
issues.unlock_error=無法解鎖未被鎖定的問題。
|
||||
issues.lock_with_reason=因為 <strong>%s</strong> 而鎖定,並將對話設為協作者限定 %s
|
||||
|
@ -76,7 +76,6 @@ func TestShadowPassword(t *testing.T) {
|
||||
func TestSelfCheckPost(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppURL, "http://config/sub/")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
|
||||
defer test.MockVariableValue(&setting.UseHostHeader, false)()
|
||||
|
||||
ctx, resp := contexttest.MockContext(t, "GET http://host/sub/admin/self_check?location_origin=http://frontend")
|
||||
SelfCheckPost(ctx)
|
||||
|
@ -32,7 +32,7 @@
|
||||
<a class="tw-mr-2" href="{{$.HeadRepo.Link}}/compare/{{PathEscapeSegments $.HeadBranch}}{{$.CompareSeparator}}{{if not $.PullRequestCtx.SameRepo}}{{PathEscape $.BaseName}}/{{PathEscape $.Repository.Name}}:{{end}}{{PathEscapeSegments $.BaseBranch}}" title="{{ctx.Locale.Tr "repo.pulls.switch_head_and_base"}}">{{svg "octicon-git-compare"}}</a>
|
||||
<div class="ui dropdown jump select-branch">
|
||||
<div class="ui basic small button">
|
||||
<span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_base"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_base"}}{{end}}: {{$BaseCompareName}}:{{$.BaseBranch}}</span>
|
||||
<span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_base"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_base"}}{{end}}: <strong>{{$BaseCompareName}}:{{$.BaseBranch}}</strong></span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</div>
|
||||
<div class="menu">
|
||||
@ -103,7 +103,7 @@
|
||||
|
||||
<div class="ui dropdown jump select-branch">
|
||||
<div class="ui basic small button">
|
||||
<span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_compare"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_head"}}{{end}}: {{$HeadCompareName}}:{{$.HeadBranch}}</span>
|
||||
<span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_compare"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_head"}}{{end}}: <strong>{{$HeadCompareName}}:{{$.HeadBranch}}</strong></span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</div>
|
||||
<div class="menu">
|
||||
|
Loading…
Reference in New Issue
Block a user