From 491cc06ffe3491242ad9ff6227423d99e673d0c2 Mon Sep 17 00:00:00 2001 From: caicandong <50507092+CaiCandong@users.noreply.github.com> Date: Tue, 11 Jul 2023 10:04:28 +0800 Subject: [PATCH 1/4] Fix the error message when the token is incorrect (#25701) we refactored `userIDFromToken` for the token parsing part into a new function `parseToken`. `parseToken` returns the string `token` from request, and a boolean `ok` representing whether the token exists or not. So we can distinguish between token non-existence and token inconsistency in the `verfity` function, thus solving the problem of no proper error message when the token is inconsistent. close #24439 related #22119 --------- Co-authored-by: Jason Song Co-authored-by: Giteabot --- services/auth/group.go | 15 +++++++-- services/auth/oauth2.go | 54 +++++++++++++++++------------- tests/integration/api_repo_test.go | 11 ++++++ 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/services/auth/group.go b/services/auth/group.go index a1ff65f203..7193dfcf49 100644 --- a/services/auth/group.go +++ b/services/auth/group.go @@ -49,12 +49,22 @@ func (b *Group) Name() string { // Verify extracts and validates func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { // Try to sign in with each of the enabled plugins + var retErr error for _, ssoMethod := range b.methods { user, err := ssoMethod.Verify(req, w, store, sess) if err != nil { - return nil, err + if retErr == nil { + retErr = err + } + // Try other methods if this one failed. + // Some methods may share the same protocol to detect if they are matched. + // For example, OAuth2 and conan.Auth both read token from "Authorization: Bearer " header, + // If OAuth2 returns error, we should give conan.Auth a chance to try. + continue } + // If any method returns a user, we can stop trying. + // Return the user and ignore any error returned by previous methods. if user != nil { if store.GetData()["AuthedMethod"] == nil { if named, ok := ssoMethod.(Named); ok { @@ -65,5 +75,6 @@ func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore } } - return nil, nil + // If no method returns a user, return the error returned by the first method. + return nil, retErr } diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index b70f84da9b..0dd7a12d2c 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -59,31 +59,32 @@ func (o *OAuth2) Name() string { return "oauth2" } +// parseToken returns the token from request, and a boolean value +// representing whether the token exists or not +func parseToken(req *http.Request) (string, bool) { + _ = req.ParseForm() + // Check token. + if token := req.Form.Get("token"); token != "" { + return token, true + } + // Check access token. + if token := req.Form.Get("access_token"); token != "" { + return token, true + } + // check header token + if auHead := req.Header.Get("Authorization"); auHead != "" { + auths := strings.Fields(auHead) + if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { + return auths[1], true + } + } + return "", false +} + // userIDFromToken returns the user id corresponding to the OAuth token. // It will set 'IsApiToken' to true if the token is an API token and // set 'ApiTokenScope' to the scope of the access token -func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { - _ = req.ParseForm() - - // Check access token. - tokenSHA := req.Form.Get("token") - if len(tokenSHA) == 0 { - tokenSHA = req.Form.Get("access_token") - } - if len(tokenSHA) == 0 { - // Well, check with header again. - auHead := req.Header.Get("Authorization") - if len(auHead) > 0 { - auths := strings.Fields(auHead) - if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { - tokenSHA = auths[1] - } - } - } - if len(tokenSHA) == 0 { - return 0 - } - +func (o *OAuth2) userIDFromToken(tokenSHA string, store DataStore) int64 { // Let's see if token is valid. if strings.Contains(tokenSHA, ".") { uid := CheckOAuthAccessToken(tokenSHA) @@ -129,10 +130,15 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor return nil, nil } - id := o.userIDFromToken(req, store) + token, ok := parseToken(req) + if !ok { + return nil, nil + } + + id := o.userIDFromToken(token, store) if id <= 0 && id != -2 { // -2 means actions, so we need to allow it. - return nil, nil + return nil, user_model.ErrUserNotExist{} } log.Trace("OAuth2 Authorization: Found token for user[%d]", id) diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 0f387192eb..fae1415568 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -41,6 +41,17 @@ func TestAPIUserReposNotLogin(t *testing.T) { } } +func TestAPIUserReposWithWrongToken(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + wrongToken := fmt.Sprintf("Bearer %s", "wrong_token") + req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name) + req = addTokenAuthHeader(req, wrongToken) + resp := MakeRequest(t, req, http.StatusUnauthorized) + + assert.Contains(t, resp.Body.String(), "user does not exist") +} + func TestAPISearchRepo(t *testing.T) { defer tests.PrepareTestEnv(t)() const keyword = "test" From 44572e924347420d3e11823b93921e01a5529959 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 12 Jul 2023 03:47:50 +0900 Subject: [PATCH 2/4] Fix incorrect oldest sort in project list (#25806) sort type `oldest` should be `Asc`. Added a test for this. --- models/fixtures/project.yml | 8 +++++++ models/project/project.go | 28 ++++++++++++------------ models/project/project_test.go | 39 ++++++++++++++++++++++++++++++++++ routers/web/org/projects.go | 2 +- routers/web/repo/projects.go | 2 +- 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/models/fixtures/project.yml b/models/fixtures/project.yml index 3fa4286c53..1bf8030f6a 100644 --- a/models/fixtures/project.yml +++ b/models/fixtures/project.yml @@ -7,6 +7,8 @@ creator_id: 2 board_type: 1 type: 2 + created_unix: 1688973030 + updated_unix: 1688973030 - id: 2 @@ -17,6 +19,8 @@ creator_id: 3 board_type: 1 type: 2 + created_unix: 1688973010 + updated_unix: 1688973010 - id: 3 @@ -27,6 +31,8 @@ creator_id: 5 board_type: 1 type: 2 + created_unix: 1688973020 + updated_unix: 1688973020 - id: 4 @@ -37,3 +43,5 @@ creator_id: 2 board_type: 1 type: 2 + created_unix: 1688973000 + updated_unix: 1688973000 diff --git a/models/project/project.go b/models/project/project.go index 44609e60b2..6fa2adf604 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -196,7 +196,7 @@ type SearchOptions struct { RepoID int64 Page int IsClosed util.OptionalBool - SortType string + OrderBy db.SearchOrderBy Type Type } @@ -226,26 +226,28 @@ func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project)) } +func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { + switch sortType { + case "oldest": + return db.SearchOrderByOldest + case "recentupdate": + return db.SearchOrderByRecentUpdated + case "leastupdate": + return db.SearchOrderByLeastUpdated + default: + return db.SearchOrderByNewest + } +} + // FindProjects returns a list of all projects that have been created in the repository func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) + e := db.GetEngine(ctx).Where(opts.toConds()).OrderBy(opts.OrderBy.String()) projects := make([]*Project, 0, setting.UI.IssuePagingNum) if opts.Page > 0 { e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum) } - switch opts.SortType { - case "oldest": - e.Desc("created_unix") - case "recentupdate": - e.Desc("updated_unix") - case "leastupdate": - e.Asc("updated_unix") - default: - e.Asc("created_unix") - } - count, err := e.FindAndCount(&projects) return projects, count, err } diff --git a/models/project/project_test.go b/models/project/project_test.go index 71ceda7aa5..d1a4715653 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -82,3 +82,42 @@ func TestProject(t *testing.T) { assert.True(t, projectFromDB.IsClosed) } + +func TestProjectsSort(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + tests := []struct { + sortType string + wants []int64 + }{ + { + sortType: "default", + wants: []int64{1, 3, 2, 4}, + }, + { + sortType: "oldest", + wants: []int64{4, 2, 3, 1}, + }, + { + sortType: "recentupdate", + wants: []int64{1, 3, 2, 4}, + }, + { + sortType: "leastupdate", + wants: []int64{4, 2, 3, 1}, + }, + } + + for _, tt := range tests { + projects, count, err := FindProjects(db.DefaultContext, SearchOptions{ + OrderBy: GetSearchOrderByBySortType(tt.sortType), + }) + assert.NoError(t, err) + assert.EqualValues(t, int64(4), count) + if assert.Len(t, projects, 4) { + for i := range projects { + assert.EqualValues(t, tt.wants[i], projects[i].ID) + } + } + } +} diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 21cb23000d..60032c777d 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -62,7 +62,7 @@ func Projects(ctx *context.Context) { OwnerID: ctx.ContextUser.ID, Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), - SortType: sortType, + OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: projectType, }) if err != nil { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 066cdbc5fd..6bfb21c134 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -74,7 +74,7 @@ func Projects(ctx *context.Context) { RepoID: repo.ID, Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), - SortType: sortType, + OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: project_model.TypeRepository, }) if err != nil { From cee352bb38517a361cd6513a05dfe18e6a453e90 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 12 Jul 2023 06:09:23 +0800 Subject: [PATCH 3/4] Show correct SSL Mode on "install page" (#25818) --- routers/install/install.go | 1 + templates/install.tmpl | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/routers/install/install.go b/routers/install/install.go index a2e89d3dac..6a8f561271 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -102,6 +102,7 @@ func Install(ctx *context.Context) { form.DbName = setting.Database.Name form.DbPath = setting.Database.Path form.DbSchema = setting.Database.Schema + form.SSLMode = setting.Database.SSLMode curDBType := setting.Database.Type.String() var isCurDBTypeSupported bool diff --git a/templates/install.tmpl b/templates/install.tmpl index b5caab1489..1716d0a5e4 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -28,7 +28,7 @@ -
+
@@ -47,7 +47,7 @@
-
+
-
+
From d1e066f5d6e1ba91f45118de835c3777eee0811f Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Wed, 12 Jul 2023 00:32:23 +0000 Subject: [PATCH 4/4] [skip ci] Updated translations via Crowdin --- options/locale/locale_de-DE.ini | 238 +++++++++++++++++++++++++++++--- 1 file changed, 221 insertions(+), 17 deletions(-) diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 0cffa24938..f478f10c25 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -57,6 +57,7 @@ new_mirror=Neuer Mirror new_fork=Neuer Fork new_org=Neue Organisation new_project=Neues Projekt +new_project_column=Neue Spalte manage_org=Organisationen verwalten admin_panel=Administration account_settings=Kontoeinstellungen @@ -78,11 +79,14 @@ milestones=Meilensteine ok=OK cancel=Abbrechen +rerun=Neu starten +rerun_all=Alle Jobs neu starten save=Speichern add=Hinzufügen add_all=Alle hinzufügen remove=Löschen remove_all=Alle entfernen +remove_label_str=Element "%s " entfernen edit=Bearbeiten enabled=Aktiviert @@ -90,6 +94,7 @@ disabled=Deaktiviert copy=Kopieren copy_url=URL kopieren +copy_content=Inhalt kopieren copy_branch=Branchenname kopieren copy_success=Kopiert! copy_error=Kopieren fehlgeschlagen @@ -110,32 +115,45 @@ unknown=Unbekannt rss_feed=RSS Feed +pin=Anheften +unpin=Loslösen artifacts=Artefakte concept_system_global=Global +concept_user_individual=Individuum concept_code_repository=Repository concept_user_organization=Organisation +show_timestamps=Zeitstempel anzeigen show_log_seconds=Sekunden anzeigen +show_full_screen=Vollbild anzeigen [aria] +navbar=Navigationsleiste +footer=Fußzeile footer.software=Über die Software footer.links=Links [heatmap] +number_of_contributions_in_the_last_12_months=%s Beiträge in den letzten 12 Monaten +no_contributions=Keine Beiträge less=Weniger more=Mehr [editor] buttons.heading.tooltip=Titel hinzufügen +buttons.bold.tooltip=Fettschrift hinzufügen +buttons.italic.tooltip=Kursivschrift hinzufügen buttons.quote.tooltip=Text zitieren buttons.code.tooltip=Code hinzufügen buttons.link.tooltip=Link hinzufügen +buttons.list.unordered.tooltip=Liste 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.switch_to_legacy.tooltip=Legacy-Editor verwenden buttons.enable_monospace_font=Monospace-Schrift aktivieren buttons.disable_monospace_font=Monospace-Schrift deaktivieren @@ -240,6 +258,7 @@ openid_signup_popup=OpenID-basierte Selbstregistrierung aktivieren. enable_captcha=Registrierungs-Captcha aktivieren enable_captcha_popup=Captcha-Eingabe bei der Registrierung erforderlich. require_sign_in_view=Ansehen erfordert Anmeldung +require_sign_in_view_popup=Seitenzugriff auf angemeldete Benutzer beschränken. Besucher sehen nur die Anmelde- und Registrierungsseite. admin_setting_desc=Das Erstellen eines Administrator-Kontos ist optional. Der erste registrierte Benutzer wird automatisch Administrator. admin_title=Administratoreinstellungen admin_name=Administrator-Benutzername @@ -269,6 +288,9 @@ 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. password_algorithm=Passwort Hashing Algorithmus invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus +password_algorithm_helper=Legen Sie einen Passwort-Hashing-Algorithmus fest. Algorithmen haben unterschiedliche Anforderungen und Stärken. Der argon2-Algorithmus ist ziemlich sicher, aber er verbraucht viel Speicher und kann für kleine Systeme ungeeignet sein. +enable_update_checker=Aktualisierungsprüfung aktivieren +enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen. [home] uname_holder=E-Mail-Adresse oder Benutzername @@ -302,15 +324,19 @@ repos=Repositorys users=Benutzer organizations=Organisationen search=Suche +go_to=Gehe zu code=Code search.type.tooltip=Suchmodus search.fuzzy=Ähnlich +search.fuzzy.tooltip=Zeige auch Ergebnisse, die dem Suchbegriff ähneln search.match=Genau +search.match.tooltip=Zeige nur Ergebnisse, die exakt mit dem Suchbegriff übereinstimmen code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. repo_no_results=Keine passenden Repositorys gefunden. user_no_results=Keine passenden Benutzer gefunden. org_no_results=Keine passenden Organisationen gefunden. code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden. +code_search_results=`Suchergebnisse für "%s"` code_last_indexed_at=Zuletzt indexiert %s relevant_repositories_tooltip=Repositorys, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet. relevant_repositories=Es werden nur relevante Repositorys angezeigt, zeigt ungefilterte Ergebnisse an. @@ -343,6 +369,7 @@ email_not_associate=Diese E-Mail-Adresse ist mit keinem Konto verknüpft. send_reset_mail=Wiederherstellungs-E-Mail senden reset_password=Kontowiederherstellung invalid_code=Dein Bestätigungs-Code ist ungültig oder abgelaufen. +invalid_password=Ihr Passwort stimmt nicht mit dem Passwort überein, das zur Erstellung des Kontos verwendet wurde. reset_password_helper=Konto wiederherstellen reset_password_wrong_user=Du bist angemeldet als %s, aber der Link zur Kontowiederherstellung ist für %s password_too_short=Das Passwort muss mindestens %d Zeichen lang sein. @@ -378,10 +405,15 @@ authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung authorize_application_created_by=Diese Anwendung wurde von %s erstellt. authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositorys und Organisationen. authorize_title=`"%s" den Zugriff auf deinen Account gestatten?` +authorization_failed=Autorisierung fehlgeschlagen +authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage festgestellt haben. Bitte kontaktiere den Betreuer der Anwendung, die du gerade autorisieren wolltest. +sspi_auth_failed=SSPI-Authentifizierung fehlgeschlagen +password_pwned=Das von dir gewählte Passwort ist auf einer Liste von gestohlenen Passwörtern die zuvor bei öffentlichen Datenschutzverletzungen aufgedeckt wurden. Bitte versuche es erneut mit einem anderen Passwort. password_pwned_err=Anfrage an HaveIBeenPwned konnte nicht abgeschlossen werden [mail] view_it_on=Auf %s ansehen +reply=oder antworte direkt auf diese E-Mail link_not_working_do_paste=Link funktioniert nicht? Versuche ihn zu kopieren und im Browser einzufügen. hi_user_x=Hallo %s, @@ -440,10 +472,15 @@ repo.transfer.body=Um es anzunehmen oder abzulehnen, öffne %s, oder ignoriere e repo.collaborator.added.subject=%s hat dich zu %s hinzugefügt repo.collaborator.added.text=Du wurdest als Mitarbeiter für folgendes Repository hinzugefügt: +team_invite.subject=%[1]s hat dich eingeladen, der Organisation %[2]s beizutreten +team_invite.text_1=%[1]s hat dich eingeladen, dem Team %[2]s in der Organisation %[3]s beizutreten. +team_invite.text_2=Bitte klicke auf den folgenden Link, um dem Team beizutreten: +team_invite.text_3=Hinweis: Diese Einladung war für %[1]s gedacht. Wenn du diese Einladung nicht erwartet hast, kannst du diese E-Mail ignorieren. [modal] yes=Ja no=Abbrechen +confirm=Bestätigen cancel=Abbrechen modify=Aktualisieren @@ -478,8 +515,12 @@ size_error=` muss die Größe %s haben.` min_size_error=` muss mindestens %s Zeichen enthalten.` max_size_error=` darf höchstens %s Zeichen enthalten.` email_error=` ist keine gültige E-Mail-Adresse.` +url_error=`"%s" ist keine gültige URL.` +include_error=` muss den Text "%s" enthalten.` glob_pattern_error=` Der Glob Pattern ist ungültig: %s.` regex_pattern_error=` regex ist ungültig: %s.` +username_error=` darf nur alphanumerische Zeichen ('0-9','a-z','A-Z'), Bindestriche ('-'), Unterstriche ('_') und Punkte ('.') enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` +invalid_group_team_map_error=` Zuordnung ist ungültig: %s` unknown_error=Unbekannter Fehler: captcha_incorrect=Der eingegebene CAPTCHA-Code ist falsch. password_not_match=Die Passwörter stimmen nicht überein. @@ -487,6 +528,7 @@ lang_select_error=Wähle eine Sprache aus der Liste aus. username_been_taken=Der Benutzername ist bereits vergeben. username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern. +username_has_not_been_changed=Benutzername wurde nicht geändert repo_name_been_taken=Der Repository-Name wird schon verwendet. repository_force_private=Privat erzwingen ist aktiviert: Private Repositorys können nicht veröffentlicht werden. repository_files_already_exist=Dateien für dieses Repository sind bereits vorhanden. Kontaktiere den Systemadministrator. @@ -500,6 +542,7 @@ team_name_been_taken=Der Teamname ist bereits vergeben. team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben. email_been_used=Die E-Mail-Adresse wird bereits verwendet. email_invalid=Die E-Mail-Adresse ist ungültig. +openid_been_used=Die OpenID-Adresse "%s" wird bereits verwendet. username_password_incorrect=Benutzername oder Passwort ist falsch. password_complexity=Das Passwort erfüllt nicht die Komplexitätsanforderungen: password_lowercase_one=Mindestens ein Kleinbuchstabe @@ -514,17 +557,27 @@ user_not_exist=Dieser Benutzer ist nicht vorhanden. team_not_exist=Dieses Team existiert nicht. last_org_owner=Du kannst den letzten Benutzer nicht aus dem 'Besitzer'-Team entfernen. Es muss mindestens einen Besitzer in einer Organisation geben. cannot_add_org_to_team=Eine Organisation kann nicht als Teammitglied hinzugefügt werden. +duplicate_invite_to_team=Der Benutzer wurde bereits als Teammitglied eingeladen. +organization_leave_success=Sie haben die Organisation %s erfolgreich verlassen. invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s invalid_gpg_key=Dein GPG-Key kann nicht überprüft werden: %s invalid_ssh_principal=Ungültige Identität: %s +must_use_public_key=Der von Ihnen bereitgestellte Key ist ein privater Key. Bitte laden Sie Ihren privaten Key nirgendwo hoch. Verwenden Sie stattdessen Ihren öffentlichen Key. +unable_verify_ssh_key=Der SSH-Schlüssel kann nicht verifiziert werden, überprüfe ihn auf Fehler. auth_failed=Authentifizierung fehlgeschlagen: %v +still_own_repo=Ihr Konto besitzt ein oder mehrere Repositories. Diese müssen erst gelöscht oder übertragen werden. +still_has_org=Dein Konto ist Mitglied einer oder mehrerer Organisationen, verlasse diese zuerst. +still_own_packages=Dein Konto besitzt ein oder mehrere Pakete, lösche diese zuerst. +org_still_own_repo=Diese Organisation besitzt noch ein oder mehrere Repositories. Diese müssen zuerst gelöscht oder übertragen werden. +org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst. target_branch_not_exist=Der Ziel-Branch existiert nicht. [user] change_avatar=Profilbild ändern… +joined_on=Beigetreten am %s repositories=Repositorys activity=Öffentliche Aktivität followers=Follower @@ -539,7 +592,12 @@ unfollow=Nicht mehr folgen heatmap.loading=Heatmap wird geladen… user_bio=Biografie disabled_public_activity=Dieser Benutzer hat die öffentliche Sichtbarkeit der Aktivität deaktiviert. +email_visibility.limited=Ihre E-Mail-Adresse ist für alle authentifizierten Benutzer sichtbar +email_visibility.private=Ihre E-Mail-Adresse ist nur für Sie und Administratoren sichtbar +form.name_reserved=Der Benutzername "%s" ist reserviert. +form.name_pattern_not_allowed=Das Muster "%s" ist nicht in einem Benutzernamen erlaubt. +form.name_chars_not_allowed=Benutzername "%s" enthält ungültige Zeichen. [settings] profile=Profil @@ -581,6 +639,9 @@ cancel=Abbrechen language=Sprache ui=Theme hidden_comment_types=Ausgeblendeter Kommentartypen +hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Das Überprüfen von "Label" entfernt beispielsweise alle " hinzugefügt/entfernt